When I am working with association in JPA with Hibernate, Error during managed flush [org.hibernate.TransientObjectException: object references an unsaved transient instance – save the transient instance beforeQuery flushing …] is one of the common error often I have seen.
Lets’s see when this Error occur and how to fix this issue. First let’s reproduce the issue.
1. Reproducing Issue
Entity Mappings:
@Entity public class Student implements Serializable { @Id private int id; @Column(name="CONTACT_NO") private String contactNo; private String fname; private String lname; // ... omitted setters and getters }
@Entity public class Branch implements Serializable { @Id @Column(name="BRANCH_ID") private int branchId; @Column(name="BRANCH_NAME") private String branchName; @Column(name="BRANCH_SHORT_NAME") private String branchShortName; private String description; //uni-directional one-to-many association to Student @OneToMany(orphanRemoval = true) @JoinColumn(name="BRANCH_ID") private List<Student> students; // .. omitted setters getters etc. }
Let’s save Branch entity.
Branch branch = new Branch(); branch.setBranchShortName("CSE"); branch.setBranchName("Computer Science and Engineering"); branch.setDescription("CSE department offers courses under ambitious curriculum in computer science .."); List<Student> students = new ArrayList<Student>(); students.add(getStudent1()); students.add(getStudent2()); branch.setStudents(students); entityManager.persist(branch); transaction.commit();
Output:
We will get ERROR: HHH000346: Error during managed flush [org.hibernate.TransientObjectException: object references an unsaved transient instance – save the transient instance beforeQuery flushing: com.javabydeveloper.domain.Student]
Hibernate: insert into Branch (BRANCH_NAME, BRANCH_SHORT_NAME, description) values (?, ?, ?) Hibernate: update Student set BRANCH_ID=? where id=? Nov 19, 2020 10:55:19 AM org.hibernate.internal.ExceptionMapperStandardImpl mapManagedFlushFailure ERROR: HHH000346: Error during managed flush [org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: com.javabydeveloper.domain.Student] javax.persistence.RollbackException: Error while committing the transaction at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:75) at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:71) at com.javabydeveloper.App.main(App.java:43) Caused by: java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: com.javabydeveloper.domain.Student at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:144) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162) at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1434) at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:484) at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3190) at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2404) at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:467) at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:146) at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38) at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:220) at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:68) ... 1 more Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: com.javabydeveloper.domain.Student at org.hibernate.engine.internal.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:279) at org.hibernate.type.EntityType.getIdentifier(EntityType.java:462) at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:151) at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:894) at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1317) at org.hibernate.persister.collection.OneToManyPersister.recreate(OneToManyPersister.java:186) at org.hibernate.action.internal.CollectionRecreateAction.execute(CollectionRecreateAction.java:50) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:586) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:460) at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1428) ... 9 more
2. Understanding Root Cause
Hibernate first inserted Branch record in database and trying to update foreign key BRANCH_ID in Student table. But Student record not yet inserted in DB. Which means, the Student entity state is transient in Persistence Context.
So, Branch object has reference (Student), which is an unsaved transient instance. We need to save Student (transient instance) before Hibernate flushing Branch.
3. Solution
In Hibernate, In Parent and Child table association mappings, this is a common issue. To fix the issue, we need to apply proper Cascading operation to the reference in Branch entity using CascadeType.
CascadeType propagates entity transitions from Parent to Child. For example, if you persist parent entity the referenced entities also get persisted before flushing. Let’s update the Branch entity using CascadeType.ALL.
@Entity public class Branch implements Serializable { @Id @Column(name="BRANCH_ID") private int branchId; @Column(name="BRANCH_NAME") private String branchName; @Column(name="BRANCH_SHORT_NAME") private String branchShortName; private String description; //uni-directional one-to-many association to Student @OneToMany(cascade = CascadeType.ALL , orphanRemoval = true) @JoinColumn(name="BRANCH_ID") private List<Student> students; // .. omitted setters getters etc. }
Let’s run the program again and verify results.
Hibernate: insert into Branch (BRANCH_NAME, BRANCH_SHORT_NAME, description) values (?, ?, ?) Hibernate: insert into Student (CONTACT_NO, fname, lname) values (?, ?, ?) Hibernate: insert into Student (CONTACT_NO, fname, lname) values (?, ?, ?) Hibernate: update Student set BRANCH_ID=? where id=? Hibernate: update Student set BRANCH_ID=? where id=? Nov 19, 2020 11:21:43 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
Now, we can notice Student records get updated before updating BRANCH_ID foreign key in Student table.
4. Conclusion
We have seen the root cause of issue “object references an unsaved transient instance – save the transient instance beforeQuery flushing” in JPA with Hibernate and how to fix the issue.