I have a JPA Entity that has an attribute declared in the following way
#OneToOne(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinColumn(name = "userId", referencedColumnName = "id", insertable = false, updatable = false)
private UserBare user;
(UserBare is a dummy entity I've created which is a dumbed down version of a User obj as I needed some date, not all of User obj be made available to this entity. Is there a better way to approach this?
This is a convenience attr that I use just make the userBare object available to this entity when reading this entity. But when I actually write this entity, it seems to create new entries of 'user' in the database (instead of updating). I already have insertable=false,updatable=false but it still writes to the database. I tried removing the CascadeType declaration but that is throwing an error.
Here is the database snapshot after the unwanted rows are added (last 3). Also I've noticed that deleting the original entity did not delete these three unwanted rows. So I guess JPA is storing them but the references are intact to the original entity.
My souspicioun would be that "OneToOne" means "OneToOne" excluding the possibility of "OneToZero" if you don't set Optional to true like this:
#OneToOne(optional=true)
But just a guess.. I'm not sure
Related
I am mapping some Java classes using hibernate on a database that I have no write access for. One of the fields used as a FK between ClientEpisodes and Physician's has 6 rows that contain a -1 for the FK. THis is obviously an invalid FK, but I need to still join and simply ignore these Physicians and have a null physician.
I am joining the class using
#JoinColumn(name = "epi_phid1", referencedColumnName = "ph_id", foreignKey = #ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Physician physician;
I tried specifying NO_CONSTRAINT to see if it would ignore it then, but even still I get the following error:
javax.persistence.EntityNotFoundException: Unable to find private.package.info.Physician with id -1
Is there any way to tell Hibernate to ignore values that aren't valid such as the -1?
The #ForeignKey annotation with NO_CONSTRAINT value tells hibernate to not generate foreign keys, if schema generation is used. It doesn't have any effect on the internal entity validation in the JPA framework.
There is a hibernate specific #NotFound annotation which you could use:
#JoinColumn(name = "epi_phid1", referencedColumnName = "ph_id")
#NotFound(action = NotFoundAction.IGNORE)
private Physician physician;
However be advised that if you use this in a #OneToMany or #ManyToMany annotation which maps a Collection, hibernate will fill the invalid/missing entities with null (so the list will contain null value(s)).
I am using Hibernate as my JPA provider, and I want one of the fields in an entity to be ignored when calling save(). However, I do have a matching column in the corresponding database table and I want my entity field to be populated with the database value when I fetch the entity. So, I want the field to be ignored when saving the entity, but not when fetching it.
If I use #Transient, the field is completely ignored, which is not what I want. Is there any way to do this?
From the excellent book Pro JPA 2 :
JPA defines options to set individual mappings to be read-only using
the insertable and updatable elements of the #Column and #JoinColumn
annotations. These two settings default to true but can be set to
false if we want to ensure that the provider will not insert or update
information in the table in response to changes in the entity
instance. If the data in the mapped table already exists and we want
to ensure that it will not be modified at runtime, then the
insertable and updatable elements can be set to false, effectively
preventing the provider from doing anything other than reading the
entity from the database.
#Column(insertable = false, updatable = false)
private String readOnlyField;
Basically my question is why if I have an Hibernate relationship like this one.
#OneToMany(cascade = {CascadeType.ALL})
#JoinColumn(name = "candidacy_id", nullable = false)
#XmlElement
#JsonIgnore
#Getter
#Setter
private List<EvaluationSelectionCriteria> evaluationSelectionCriterias = new ArrayList<>();
#ManyToOne
#JoinColumn(name = "candidacy_id", nullable = false, insertable = false, updatable = false)
#XmlTransient
#Getter
#Setter
private Candidacy candidacy;
Why if I do this candidacy.setEvaluationSelectionCriteria(list) automatically this list is persisted in database?
I would like to use the EvaluationSelectionCriteria as a repository to render a list of "future" EvaluationSelectionCriteria
Could be because is not Lazy?
More detail explanation
So would be like I call method a, there I´m get from database entity A then I set a list into A and then I return A in the method but I´m not saving A, when I see the value of the list already have ids!!!
If you do not want the list to be saved when the parent entity is saved/merged, you should remove or restrict the cascade setting for the relationship:
#OneToMany
private List<EvaluationSelectionCriteria> evaluationSelectionCriterias
or
#OneToMany(cascade = CascadeType.REMOVE) // or other values from the enum
private List<EvaluationSelectionCriteria> evaluationSelectionCriterias
EDIT: If you want to fetch an entity in a transactional method and modify it, you can restrict the scope of the transaction to the fetching only. Then modify the entity outside the transactional method. Later, you can merge the detached entity if needed.
Since collection attributes are lazy per default you will either need to
access their content while still inside the transactional method - so the collection can be fetched from the DB. Please note that you will have to call a method on the collection that actualy requires it's content to be loaded, like getCriterias().size().
use LEFT JOIN FETCH to load the collection as a side effect of the query.
I would not modify the FlushMode for the session - while this would probably work, it feels like a kludge - it does not communicate your intent very well. Explicitly fetching the collection and modifying it outside the transaction expresses your intent better IMO.
I found the solution, I forgot to say that I´m using Spring, so finally I add the #Transactional(readOnly=true) into my method instead in the service class level.
I have an entity named 'Department' and another entity named 'student'. I know the department will have many students and there shoulld be relation between these two tables in database. But in my project, the DB tables are already there and there is no relation (foreign key) between department and student tables.
In entity class, student.java , there is a relation written as,
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = 'DeptId', nullable = false, insertable = false, updatable = false)
Department department
I am confused about this existing code.
When I wrote a test, I am fetching the department from DB by deptId and set the student entity as,
student.setDepartment(department);
This doesn't populate the DB column 'DEPTID' in student table.
Since there's no student collection in Department, I cannot set the student as,
department.addStudents(student);
I am not sure whether we can insist a #ManyToOne relation without a relation between the tables in DB.
Please let me know how I can fix this issue so that the 'DEPTID' column in student table is populated with the correct data.
Thanks in advance,
BS
you r having
#JoinColumn(name = 'DeptId', nullable = false, insertable = false, updatable = false)
instead why dont you try
#JoinColumn(name = 'DeptId', nullable = false)
Hibernate wont check whether the mapping constraints that you are putting are valid at db level. It just assumes it is valid and executes queries based on that assumption.
Hi sorry for responding to your question so late but I think the reply could equally help another person. Now you said the tables existed already in the database, if they haven't yet got some data then I suggest you drop them, activate your Table Generation Strategy in your persistence.xml file to Create, in that case, it will recreate those tables with your desired relationship columns. Do not also forget to use the #OneToMany annotation on the Department.java class to indicate its capabilities of reception of many students. It is used together with the #ManyToOne
I have two tables: t_promo_program and t_promo_program_param.
They are represented by the following JPA entities:
#Entity
#Table(name = "t_promo_program")
public class PromoProgram {
#Id
#Column(name = "promo_program_id")
private Long id;
#OneToMany(cascade = {CascadeType.REMOVE})
#JoinColumn(name = "promo_program_id")
private List<PromoProgramParam> params;
}
#Entity
#Table(name = "t_promo_program_param")
public class PromoProgramParam {
#Id
#Column(name = "promo_program_param_id")
private Long id;
//#NotNull // This is a Hibernate annotation so that my test db gets created with the NOT NULL attribute, I'm not married to this annotation.
#ManyToOne
#JoinColumn(name = "PROMO_PROGRAM_ID", referencedColumnName = "promo_program_id")
private PromoProgram promoProgram;
}
When I delete a PromoProgram, Hibernate hits my database with:
update
T_PROMO_PROGRAM_PARAM
set
promo_program_id=null
where
promo_program_id=?
delete
from
t_promo_program
where
promo_program_id=?
and last_change=?
I'm at a loss for where to start looking for the source of the problem.
Oh crud, it was a missing "mappedBy" field in PromoProgram.
Double-check whether you're maintaining bidirectional association consistency. That is; make sure that all PromoProgramParam entities that link to a PromoProgram as its parent are also contained in said parent's params list. It's a good idea to make sure this happens regardless of which side "initiates" the association if you will; if setPromoProgram is called on a PromoProgramParam, have the setter automatically add itself to the PromoProgram's params list. Vice versa, when calling addPromoProgramParam on a PromoProgram, have it set itself as the param's parent.
I've encountered this problem before as well, and it was due to not maintaining bidirectional consistency. I debugged around into Hibernate and found that it was unable to cascade the delete operation to the children because they weren't in the list. However, they most certainly were present in the database, and caused FK exceptions as Hibernate tried to delete only the parent without first deleting its children (which you've likely also encountered with the #NonNull in place).
FYI, I believe the proper "EJB 3.0"-way of making the PromoProgramParam.promoProgram field (say that a 100 times) non-nullable is to set the optional=false attribute on the #ManyToOne annotation.