Hibernate doesn't add records to association - java

I have a m:n relation beetwen objects (Meeting, Person) as many persons can be participant of many meetings.
I've set it like this
Meeting
#ManyToMany(mappedBy = "meetings")
protected Set<Person> participants = new HashSet<Person>();
Person
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "person_meeting",
joinColumns = {#JoinColumn(name = "person_id")},
inverseJoinColumns = {#JoinColumn(name = "meeting_id")}
)
protected Set<Meeting> meetings = new HashSet<Meeting>();
Id DB hibernate created me table meeting_participants with two fields: meeting_id, person_id. Cool, just as I wanted.
Now problematic case. I've create Meeting object and I saved it to DB. Than I create set of users, I add it to meeting
this.saveMeeting.setParticipants(set);
Hibernate displays:
Hibernate: update Meeting set duration=?, meetingDate=?, room=? where meeting_id=?
Nothing added to association. What do I need to change ?
// EDIT
I've changed in Meeting definition of the field
#ManyToMany(mappedBy = "meetings", cascade = {CascadeType.ALL})
protected Set<Person> participants = new HashSet<Person>();
Now I get error
org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
It is in this method
public static Long add(Meeting meeting) {
SessionFactory sf = null;
Session session = null;
sf = HibernateUtil.getSessionFactory();
session = sf.openSession();
session.beginTransaction();
try{
session.save(meeting);
session.getTransaction().commit();
session.flush();
} catch(HibernateException e){
session.getTransaction().rollback();
e.printStackTrace();
return new Long(-1);
}
session.close();
return meeting.getId();
}
The line that is causing the problem is:
session.save(meeting);
EDIT
Ok I've closed session properly. Everything works find BUT only when I'm creating new objects. When I want to update association it doesn not work. So the question is. How to update association ??

This question is asked every two days. You just initialized one side of the association, and you chose the one that Hibernate ignores. A bidirectional association has an owner side, and an inverse side. Hibernate only considers the owner side. And the owner side is the one which doesn't have the mappedBy attribute.
So you need to initialize the other side of the association:
for (Participant p : set) {
p.getMeetings().add(saveMeeting);
}

I've solved the problem. I didn't think about that so I didn not wrote it. Person was a top class and it was extended by two others. I've changed update action so that it took an instance of Object class. That way I'm able to update association

Related

JPA bidirectional relation, JoinColumn not populated [duplicate]

I have one to many relationships between person class and car class. A person can own many cars and vice versa. I am using restful API to post data. My annotations and Get service is working fine but my post service throws " java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL" error whenever I try to insert new data. Child table foreign key is being inserted as null.
Here is part of my code.
Person.java
private List<Car> cars = new ArrayList<Car>();
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
#JsonManagedReference
public List<Car> getCars() {
return cars;
}
Car.java
private Person person;
#ManyToOne
#JoinColumn(name = "PERSON_ID", nullable = false, updatable = true, insertable = true)
#JsonBackReference
public Person getPerson() {
return person;
}
My service class:
#POST
#Path("/PersonRegistration")
#Consumes(MediaType.APPLICATION_JSON)
public Response postPersonCars(Person person) throws Exception{
Session session = null;
ObjectMapper mapper = new ObjectMapper();
//Person per = new Person();
//Car cars = new Car();
try{
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
//per.setCars(person.getCars());
session.save(person);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
throw e;
}finally{
if(null != session){
session.close();
}
}
return Response.status(201).entity(mapper.writeValueAsString(person)).build();
}
This annotation:
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
has two consequences:
mappedBy implies that Car is the owning side of the relationship. This means that whenever you want to establish a relationship between Car and Person, you need to do it by setting the Car.person property to the appropriate value. Changes to Person.cars will be ignored by Hibernate.
cascade=CascadeType.ALL means that whenever you save a Person, Hibernate will also invoke the save operation on all entities contained in Person.cars
Result: you are calling Session.save() on a bunch of Car entities that do not have the Car.person property set properly.
Solution: either change the owning side of the relationship (be aware that you will also need a #JoinColumn on Person.cars if you do not want an extra database table to be created) or loop through Person.cars and set the Car.person property properly in each of them.
cascade=CascadeType.ALL suggests the first solution fits your use case better.

Spring Boot object references an unsaved transient instance error from method [duplicate]

I receive following error when I save the object using Hibernate
object references an unsaved transient instance - save the transient instance before flushing
You should include cascade="all" (if using xml) or cascade=CascadeType.ALL (if using annotations) on your collection mapping.
This happens because you have a collection in your entity, and that collection has one or more items which are not present in the database. By specifying the above options you tell hibernate to save them to the database when saving their parent.
I believe this might be just repeat answer, but just to clarify, I got this on a #OneToOne mapping as well as a #OneToMany. In both cases, it was the fact that the Child object I was adding to the Parent wasn't saved in the database yet. So when I added the Child to the Parent, then saved the Parent, Hibernate would toss the "object references an unsaved transient instance - save the transient instance before flushing" message when saving the Parent.
Adding in the cascade = {CascadeType.ALL} on the Parent's reference to the Child solved the problem in both cases. This saved the Child and the Parent.
Sorry for any repeat answers, just wanted to further clarify for folks.
#OneToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
return performanceLog;
}
Introduction
When using JPA and Hibernate, an entity can be in one of the following 4 states:
New - A newly created object that hasn’t ever been associated with a Hibernate Session (a.k.a Persistence Context) and is not mapped to any database table row is considered to be in the New or Transient state.
To become persisted we need to either explicitly call the persist method or make use of the transitive persistence mechanism.
Persistent - A persistent entity has been associated with a database table row and it’s being managed by the currently running Persistence Context.
Any change made to such an entity is going to be detected and propagated to the database (during the Session flush-time).
Detached - Once the currently running Persistence Context is closed all the previously managed entities become detached. Successive changes will no longer be tracked and no automatic database synchronization is going to happen.
Removed - Although JPA demands that managed entities only are allowed to be removed, Hibernate can also delete detached entities (but only through a remove method call).
Entity state transitions
To move an entity from one state to the other, you can use the persist, remove or merge methods.
Fixing the problem
The issue you are describing in your question:
object references an unsaved transient instance - save the transient instance before flushing
is caused by associating an entity in the state of New to an entity that's in the state of Managed.
This can happen when you are associating a child entity to a one-to-many collection in the parent entity, and the collection does not cascade the entity state transitions.
So, you can fix this by adding cascade to the entity association that triggered this failure, as follows:
The #OneToOne association
#OneToOne(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private PostDetails details;
Notice the CascadeType.ALL value we added for the cascade attribute.
The #OneToMany association
#OneToMany(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
Again, the CascadeType.ALL is suitable for the bidirectional #OneToMany associations.
Now, in order for the cascade to work properly in a bidirectional, you also need to make sure that the parent and child associations are in sync.
The #ManyToMany association
#ManyToMany(
mappedBy = "authors",
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}
)
private List<Book> books = new ArrayList<>();
In a #ManyToMany association, you cannot use CascadeType.ALL or orphanRemoval as this will propagate the delete entity state transition from one parent to another parent entity.
Therefore, for #ManyToMany associations, you usually cascade the CascadeType.PERSIST or CascadeType.MERGE operations. Alternatively, you can expand that to DETACH or REFRESH.
This happens when saving an object when Hibernate thinks it needs to save an object that is associated with the one you are saving.
I had this problem and did not want to save changes to the referenced object so I wanted the cascade type to be NONE.
The trick is to ensure that the ID and VERSION in the referenced object is set so that Hibernate does not think that the referenced object is a new object that needs saving. This worked for me.
Look through all of the relationships in the class you are saving to work out the associated objects (and the associated objects of the associated objects) and ensure that the ID and VERSION is set in all objects of the object tree.
Or, if you want to use minimal "powers" (e.g. if you don't want a cascade delete) to achieve what you want, use
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
...
#Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
In my case it was caused by not having CascadeType on the #ManyToOne side of the bidirectional relationship. To be more precise, I had CascadeType.ALL on #OneToMany side and did not have it on #ManyToOne. Adding CascadeType.ALL to #ManyToOne resolved the issue.
One-to-many side:
#OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true)
private Set<GlobalConfigScope>gcScopeSet;
Many-to-one side (caused the problem)
#ManyToOne
#JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Many-to-one (fixed by adding CascadeType.PERSIST)
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
This occurred for me when persisting an entity in which the existing record in the database had a NULL value for the field annotated with #Version (for optimistic locking). Updating the NULL value to 0 in the database corrected this.
This isn't the only reason for the error. I encountered it just now for a typo error in my coding, which I believe, set a value of an entity which was already saved.
X x2 = new X();
x.setXid(memberid); // Error happened here - x was a previous global entity I created earlier
Y.setX(x2);
I spotted the error by finding exactly which variable caused the error (in this case String xid). I used a catch around the whole block of code that saved the entity and printed the traces.
{
code block that performed the operation
} catch (Exception e) {
e.printStackTrace(); // put a break-point here and inspect the 'e'
return ERROR;
}
Don't use Cascade.All until you really have to. Role and Permission have bidirectional manyToMany relation. Then the following code would work fine
Permission p = new Permission();
p.setName("help");
Permission p2 = new Permission();
p2.setName("self_info");
p = (Permission)crudRepository.save(p); // returned p has id filled in.
p2 = (Permission)crudRepository.save(p2); // so does p2.
Role role = new Role();
role.setAvailable(true);
role.setDescription("a test role");
role.setRole("admin");
List<Permission> pList = new ArrayList<Permission>();
pList.add(p);
pList.add(p2);
role.setPermissions(pList);
crudRepository.save(role);
while if the object is just a "new" one, then it would throw the same error.
beside all other good answers, this could happen if you use merge to persist an object and accidentally forget to use merged reference of the object in the parent class. consider the following example
merge(A);
B.setA(A);
persist(B);
In this case, you merge A but forget to use merged object of A. to solve the problem you must rewrite the code like this.
A=merge(A);//difference is here
B.setA(A);
persist(B);
If your collection is nullable just try: object.SetYouColection(null);
This issue happened to me when I created a new entity and an associated entity in a method marked as #Transactional, then performed a query before saving. Ex
#Transactional
public someService() {
Entity someEntity = new Entity();
AssocaiatedEntity associatedEntity = new AssocaitedEntity();
someEntity.setAssociatedEntity(associatedEntity);
associatedEntity.setEntity(someEntity);
// Performing any query was causing hibernate to attempt to persist the new entity. It would then throw an exception
someDao.getSomething();
entityDao.create(someEntity);
}
To fix, I performed the query before creating the new entity.
To add my 2 cents, I got this same issue when I m accidentally sending null as the ID. Below code depicts my scenario (and OP didn't mention any specific scenario).
Employee emp = new Employee();
emp.setDept(new Dept(deptId)); // --> when deptId PKID is null, same error will be thrown
// calls to other setters...
em.persist(emp);
Here I m setting the existing department id to a new employee instance without actually getting the department entity first, as I don't want to another select query to fire.
In some scenarios, deptId PKID is coming as null from calling method and I m getting the same error.
So, watch for null values for PK ID
It can also happen when you are having OneToMany relation and you try to add the child entity to the list in parent entity, then retrieve this list through parent entity (before saving this parent entity), without saving child entity itself, e.g.:
Child childEntity = new Child();
parentEntity.addChild(childEntity);
parentEntity.getChildren(); // I needed the retrieval for logging, but one may need it for other reasons.
parentRepository.save(parentEntity);
The error was thrown when I saved the parent entity. If I removed the retrieval in the previous row, then the error was not thrown, but of course that's not the solution.
The solution was saving the childEntity and adding that saved child entity to the parent entity, like this:
Child childEntity = new Child();
Child savedChildEntity = childRepository.save(childEntity);
parentEntity.addChild(savedChildEntity);
parentEntity.getChildren();
parentRepository.save(parentEntity);
If you're using Spring Data JPA then addition #Transactional annotation to your service implementation would solve the issue.
I also faced the same situation. By setting following annotation above the property made it solve the exception prompted.
The Exception I faced.
Exception in thread "main" java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.model.Car_OneToMany
To overcome, the annotation I used.
#OneToMany(cascade = {CascadeType.ALL})
#Column(name = "ListOfCarsDrivenByDriver")
private List<Car_OneToMany> listOfCarsBeingDriven = new ArrayList<Car_OneToMany>();
What made Hibernate throw the exception:
This exception is thrown at your console because the child object I attach to the parent object is not present in the database at that moment.
By providing #OneToMany(cascade = {CascadeType.ALL}) , it tells Hibernate to save them to the database while saving the parent object.
i get this error when i use
getSession().save(object)
but it works with no problem when I use
getSession().saveOrUpdate(object)
For the sake of completeness: A
org.hibernate.TransientPropertyValueException
with message
object references an unsaved transient instance - save the transient instance before flushing
will also occur when you try to persist / merge an entity with a reference to another entity which happens to be detached.
One other possible reason: in my case, I was attempting to save the child before saving the parent, on a brand new entity.
The code was something like this in a User.java model:
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();
The setNewPassword() method creates a PasswordHistory record and adds it to the history collection in User. Since the create() statement hadn't been executed yet for the parent, it was trying to save to a collection of an entity that hadn't yet been created. All I had to do to fix it was to move the setNewPassword() call after the call to create().
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);
There is another possibility that can cause this error in hibernate. You may set an unsaved reference of your object A to an attached entity B and want to persist object C. Even in this case, you will get the aforementioned error.
There are so many possibilities of this error some other possibilities are also on add page or edit page. In my case I was trying to save a object AdvanceSalary. The problem is that in edit the AdvanceSalary employee.employee_id is null Because on edit I was not set the employee.employee_id. I have make a hidden field and set it. my code working absolutely fine.
#Entity(name = "ic_advance_salary")
#Table(name = "ic_advance_salary")
public class AdvanceSalary extends BaseDO{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
#Column(name = "employee_id", insertable=false, updatable=false)
#NotNull(message="Please enter employee Id")
private Long employee_id;
#Column(name = "advance_date")
#DateTimeFormat(pattern = "dd-MMM-yyyy")
#NotNull(message="Please enter advance date")
private Date advance_date;
#Column(name = "amount")
#NotNull(message="Please enter Paid Amount")
private Double amount;
#Column(name = "cheque_date")
#DateTimeFormat(pattern = "dd-MMM-yyyy")
private Date cheque_date;
#Column(name = "cheque_no")
private String cheque_no;
#Column(name = "remarks")
private String remarks;
public AdvanceSalary() {
}
public AdvanceSalary(Integer advance_salary_id) {
this.id = advance_salary_id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public Long getEmployee_id() {
return employee_id;
}
public void setEmployee_id(Long employee_id) {
this.employee_id = employee_id;
}
}
I think is because you have try to persist an object that have a reference to another object that is not persist yet, and so it try in the "DB side" to put a reference to a row that not exists
Case 1:
I was getting this exception when I was trying to create a parent and saving that parent reference to its child and then some other DELETE/UPDATE query(JPQL). So I just flush() the newly created entity after creating parent and after creating child using same parent reference. It Worked for me.
Case 2:
Parent class
public class Reference implements Serializable {
#Id
#Column(precision=20, scale=0)
private BigInteger id;
#Temporal(TemporalType.TIMESTAMP)
private Date modifiedOn;
#OneToOne(mappedBy="reference")
private ReferenceAdditionalDetails refAddDetails;
.
.
.
}
Child Class:
public class ReferenceAdditionalDetails implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#OneToOne
#JoinColumn(name="reference",referencedColumnName="id")
private Reference reference;
private String preferedSector1;
private String preferedSector2;
.
.
}
In the above case where parent(Reference) and child(ReferenceAdditionalDetails) having OneToOne relationship and when you try to create Reference entity and then its child(ReferenceAdditionalDetails), it will give you the same exception. So to avoid the exception you have to set null for child class and then create the parent.(Sample Code)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
In my case , issue was completely different. I have two classes let's say c1 & c2. Between C1 & C2 dependency is OneToMany. Now if i am saving C1 in DB it was throwing above error.
Resolution of this problem was to get first C2's id from consumer request and find C2 via repository call.Afterwards save c2 into C1 object .Now if i am saving C1, it's working fine.
I was facing the same error for all PUT HTTP transactions, after introducing optimistic locking (#Version)
At the time of updating an entity it is mandatory to send id and version of that entity. If any of the entity fields are related to other entities then for that field also we should provide id and version values, without that the JPA try to persist that related entity first as a new entity
Example: we have two entities --> Vehicle(id,Car,version) ; Car(id, version, brand); to update/persist Vehicle entity make sure the Car field in vehicle entity has id and version fields provided
Simple way of solving this issue is save the both entity.
first save the child entity and then save the parent entity.
Because parent entity is depend on child entity for the foreign key value.
Below simple exam of one to one relationship
insert into Department (name, numOfemp, Depno) values (?, ?, ?)
Hibernate: insert into Employee (SSN, dep_Depno, firstName, lastName, middleName, empno) values (?, ?, ?, ?, ?, ?)
Session session=sf.openSession();
session.beginTransaction();
session.save(dep);
session.save(emp);
One possible cause of the error is the inexistence of the setting of the value of the parent entity ; for example for a department-employees relationship you have to write this in order to fix the error :
Department dept = (Department)session.load(Department.class, dept_code); // dept_code is from the jsp form which you get in the controller with #RequestParam String department
employee.setDepartment(dept);
I faced this exception when I did not persist parent object but I was saving the child. To resolve the issue, with in the same session I persisted both the child and parent objects and used CascadeType.ALL on the parent.
My problem was related to #BeforeEach of JUnit. And even if I saved the related entities (in my case #ManyToOne), I got the same error.
The problem is somehow related to the sequence that I have in my parent.
If I assign the value to that attribute, the problem is solved.
Ex.
If I have the entity Question that can have some categories (one or more) and entity Question has a sequence:
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
#Id
private Long id;
I have to assign the value question.setId(1L);
Just make Constructor of your mapping in your base class.
Like if you want One-To-One relation in Entity A, Entity B.
if your are taking A as base class, then A must have a Constructor have B as a argument.

Spring, Hibernate, JPA - Why am i not using entity manager, why does casacdeType.Persist only work with entity manager

I was testing out how cascade works, and ran into some stuff that confuses me. I have two simple entities:
#Entity
public class Child {
#Id
Long id;
#OneToOne()
#JoinColumn(name = "JOINCOLMN", referencedColumnName = "ID")
Person person;
}
#Entity
public class Person {
#Id
Long id;
#OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
Child child;
}
I was testing out the cascade type persist. So i wrote this piece of code:
Person person = new Person();
person.setId(100L);
person.setName("SomeName");
Child child = new Child();
child.setId(60L);
child.setPerson(person);
personRepository.save(person);
However, even though both of them were suppose to get persisted, only the person gets persist. So the first question is :
Why is this not working?
I searched around a bit, and found people using the Hibernate entity manager. Here is an example where the persist cascade type works:
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Author a = new Author();
a.setFirstName(“John”);
a.setLastName(“Doe”);
Book b1 = new Book();
b1.setTitle(“John’s first book”);
a.getBooks().add(b1);
Book b2 = new Book();
b2.setTitle(“John’s second book”);
a.getBooks().add(b2);
em.persist(a);
em.getTransaction().commit();
em.close();
My second question is:
I can see that entity manager is used to manage the transaction, and manage the entities. But i never use it, so what happens in my piece of the code? Who manages the transaction? Who persist the entity?
Spring JPA can help you manage your transaction by using repositories, or the #Transactional annotation. It basically wraps any method so the core of the method is performed in a transaction.
In your case, the call personRepository.save(person) opens a transaction and commits the changes to the database.
Regarding your first question, the problem comes from your #OneToOne relationship and its setters implementation. Calling child.setPerson(person); does not set the person's child. Therefore, when calling personRepository.save(person) , as the person's child is null, there is no Child object to persist.
You want to make sure to keep the object state coherent:
Person person = new Person();
person.setId(100L);
person.setName("SomeName");
Child child = new Child();
child.setId(60L);
child.setPerson(person);
person.setChild(child);
personRepository.save(person);

Hibernate save object (one to many relationship) foreign key is null

I have one to many relationships between person class and car class. A person can own many cars and vice versa. I am using restful API to post data. My annotations and Get service is working fine but my post service throws " java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL" error whenever I try to insert new data. Child table foreign key is being inserted as null.
Here is part of my code.
Person.java
private List<Car> cars = new ArrayList<Car>();
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
#JsonManagedReference
public List<Car> getCars() {
return cars;
}
Car.java
private Person person;
#ManyToOne
#JoinColumn(name = "PERSON_ID", nullable = false, updatable = true, insertable = true)
#JsonBackReference
public Person getPerson() {
return person;
}
My service class:
#POST
#Path("/PersonRegistration")
#Consumes(MediaType.APPLICATION_JSON)
public Response postPersonCars(Person person) throws Exception{
Session session = null;
ObjectMapper mapper = new ObjectMapper();
//Person per = new Person();
//Car cars = new Car();
try{
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
//per.setCars(person.getCars());
session.save(person);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
throw e;
}finally{
if(null != session){
session.close();
}
}
return Response.status(201).entity(mapper.writeValueAsString(person)).build();
}
This annotation:
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
has two consequences:
mappedBy implies that Car is the owning side of the relationship. This means that whenever you want to establish a relationship between Car and Person, you need to do it by setting the Car.person property to the appropriate value. Changes to Person.cars will be ignored by Hibernate.
cascade=CascadeType.ALL means that whenever you save a Person, Hibernate will also invoke the save operation on all entities contained in Person.cars
Result: you are calling Session.save() on a bunch of Car entities that do not have the Car.person property set properly.
Solution: either change the owning side of the relationship (be aware that you will also need a #JoinColumn on Person.cars if you do not want an extra database table to be created) or loop through Person.cars and set the Car.person property properly in each of them.
cascade=CascadeType.ALL suggests the first solution fits your use case better.

Jpa several #ManyToOne with Cascade

I have got three entities, Session, Order and User (part of my online movie tickets project). In my domain model, Order keeps fk of both User and Session. As you can see in my code:
#Table(name="Orders")
#Entity
public class Order {
#ManyToOne
#JoinColumn(nullable = false)
private User user;
#ManyToOne
private Session session;
...
}
#Entity
#Table(name="Session")
public class Session {
#OneToMany(fetch=FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "session")
private List<Order> orders = new ArrayList<Order>();
...
}
#Table(name="User")
#Entity
public class User {
#OneToMany(cascade = { CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REMOVE },
mappedBy = "user")
private #Getter Set<Order> orders = new HashSet<>();
...
}
My question is, Can I use CascadeType.ALL in both Session and User?
Are there potential conflicts when update Order with both Session and User?
As you can see, I use fetchType.Lazy, Can it guarantee that orders in both Session and User are up-to-date?
Question 1: It's a good question, but in order to answer it you need to understand the concept of the owning entity. The Entity with the #ManyToOne annotation is the owner of the relationship. This is important for the developer because no relationship will be persisted unless it's done on the owning side, in this case that means setting Order.user. However, since you have the cascade annotation on the non-owning User, you have to do extra work to use the cascade functionality:
// create Order
Order order = new Order();
// create User and Set of orders
User user = new User();
Set<Order> userOrders = new HashSet<Order>();
user.setOrders(userOrders);
userOrders.add(order);
// and set Order.user
order.setUser(user);
// persist with cascade
em.persist(user);
Notice that you must create a Set of orders as well as set Order.user to persist with cascade. However, if you put the cascade annotation on the owning entity Order, then your job becomes much simpler:
// create User
User user = new User();
// create Order
Order order = new Order();
// and set Order.user
order.setUser(user);
// persist with cascade
em.persist(order);
Now just persisting order will persist the new User and the Order with one call. Without the cascade annotation on the Order entity, persisting Order before User will give you an exception.
References: What is the “owning side” in an ORM mapping?, In a bidirectional JPA OneToMany/ManyToOne association, what is meant by “the inverse side of the association”?
Question 2: FetchType.LAZY means you have to get the children by specific query, so if I understand your question, the answer is no, it doesn't guarantee anything. With FetchType.LAZY when you get a Session you will not have access to the Session.orders when the entity becomes detached, typically after you have left your Session Bean or Service Layer. If you need access to orders, you will need to get them in the select query:
"select distinct s from Session s join fetch s.orders"
EDIT: As noted, by default this query does a sql "inner join", which will return nothing if there are no orders. Instead, do
"select distinct s from Session s left join fetch s.orders"
so that you always get the sessions that are in the database.
Reference: Difference between FetchType LAZY and EAGER in Java Persistence API?

Categories

Resources