I receive following error when I save the object using Hibernate
object references an unsaved transient instance - save the transient instance before flushi
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);
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();
.
.
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);
As I explained in this article 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).
To move an entity from one state to the other, you can use the persist
, remove
or merge
methods.
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, as I explained in this article, you can fix this by adding cascade to the entity association that triggered this failure, as follows:
@OneToOne
association@OneToOne(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private PostDetails details;
Notice the
CascadeType.ALL
value we added for thecascade
attribute.
@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.
Check out this article for more details about what is the best way to achieve this goal.
@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
.
For more details about the best way to map a
@ManyToMany
association, check out this article as well.
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
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.