Related
I have some entities in a one-to-many relationship.
The parent:
#Entity
#Cacheable(false)
#Table(name="form_area")
public class FormArea {
#OneToMany(fetch = FetchType.LAZY, mappedBy="formArea", cascade = { CascadeType.MERGE, CascadeType.PERSIST })
private List<AssignableArea> assignableAreas = new ArrayList<AssignableArea>();
...
}
The child:
#Entity
#Cacheable(false)
#Table(name="assignable_area")
public class AssignableArea {
private #ManyToOne FormArea formArea;
...
}
The child object, AssignableArea also has getter/setter on the parent, FormArea.
When I re-parent using setFormArea(), it seems successful, in that the database contains the correct information. But when I call getAssignableAreas() on FormArea:
public List<AssignableArea> getAssignableAreas() {
return Collections.unmodifiableList(new ArrayList<>(assignableAreas));
}
I see the old list, before re-parenting.
Why do I see the old, stale list, when I expect to see the updated list?
The most probable reason is that you've already loaded the FormArea and its list of assignableAreas, and you only updated one side of the association, so the other side stays as it was loaded: it's your responsibility to maintain both sides of the association if it matters to you.
If you start a new transaction and reload the FormArea, its list will contain the correct elements.
I have two entities A and B. A is parent of B, ie A is having a list of B as child.
The JPA classes corresponding look like below :
The first entity :
#Entity
#Table(name = "A")
public class A {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "refA", orphanRemoval = true)
private List<B> listOfB;
}
The second entity :
public class B {
#JoinColumn(name = "A_REF")
#ManyToOne
private CoreRecord refA;
private int updatedColumn;
#PrePersist
private void prePersit() {
this.updatedColumn = 1;
}
#PreUpdate
private void preUpdate() {
this.updatedColumn = 2;
}
}
My issue now is that when i first persit A whith a list of B, everything goes fine. Each object of B is cascadly persisted and the updatedColumn if filled correcty.
Then juste after persisting A, if i add a new instance of B in the listOfB and call merge on A, the new instance of B will be persisted in the database but the value of updatedColumn not filled correctly.
When i debug the code, i can notice that the prePersit method is being called but the modification done to my updatedColumn field is never persisted in the database. I have also notice that the preUpdate method is never called.
I would like the modification done in one of the method prePersit or preUpdate to the updatedColumn of the new instance of B to be persisted in the database when i call merge on A.
I've been tourning around searching what i'm doing wrong. Since then i have'nt found the problem nor the solution. I need your help please. Thanks in advance.
My JPA provider is eclipselink 2.5.2
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.
I have the following entities:
#Entity
public static class Parent {
#Id
#GeneratedValue
private Long id;
String st;
#OneToMany(mappedBy = "parent")
Set<Child> children = new HashSet<>();
// get,set
}
#Entity
public static class Child {
#Id
#GeneratedValue
private Long id;
String st;
#ManyToOne()
private Parent parent;
//get,set
}
Note, that there is no Cascade on #OneToMany side.
And I want the following:
I have one Parent with one Child in Detached state.
Now I want to remove child by some condition, so I'm accesing all children, find necessary and remove it directly via em.remove(child). + I remove it from Parent's collection.
After that I want to change some property of Parent and save it also.
And I'm getting EntityNotFound exception.
I performed some debug, and found that children collection is PersistentSet which remembered it's state in storedSnapshot. So, when I'm merging Parent to context - Hibernate do something with that stored snapshot and tries to load child it from DB. Of course, there is no such entity and exception is thrown.
So, there are couple of things I could do:
Map collection with #NotFound(action = NotFoundAction.IGNORE)
During removing from children collection - cast to PersistentSet and clear it also.
But it seems like a hack.
So,
1. What I'm doing wrong? It seems, that it's correct to remove child entity directly
2. Is there more elegant way to handle this?
Reproducible example:
#Autowired
PrentCrud parentDao;
#Autowired
ChiildCrud childDao;
#PostConstruct
public void doSomething() {
LogManager.getLogger("org.hibernate.SQL").setLevel(Level.DEBUG);
Parent p = new Parent();
p.setSt("1");
Child e = new Child();
e.setParent(p);
e.setSt("c");
p.getChildren().add(e);
Parent save = parentDao.save(p);
e.setParent(save);
childDao.save(e);
Parent next = parentDao.findAll().iterator().next();
next.setSt("2");
next.getChildren().size();
childDao.deleteAll();
next.getChildren().clear();
if (next.getChildren() instanceof PersistentSet) { // this is hack, not working without
((Map)((PersistentSet) next.getChildren()).getStoredSnapshot()).clear();
}
parentDao.save(next); // exception is thrwn here without hack
System.out.println("Success");
}
have you tried changing fetch type to eager? defaults for relations
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
maybe it gets cached because of fetch method
You can use next.setChildren(new HashSet<>()); instead of next.getChildren().clear(); to get rid of the getStoredSnapshot()).clear()
But it would be more elegant to use cascade and orphanRemoval.
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
Set<Child> children = new HashSet<>();
public void doSomething() {
...
next.setSt("2");
next.setChildren(new HashSet<>());
parentDao.save(next);
System.out.println("Success");
}
I'm having the following issue when trying to update my entity:
"A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance".
I have a parent entity and it has a Set<...> of some children entities. When I try to update it, I get all the references to be set to this collections and set it.
The following code represents my mapping:
#OneToMany(mappedBy = "parentEntity", fetch = FetchType.EAGER)
#Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
public Set<ChildEntity> getChildren() {
return this.children;
}
I've tried to clean the Set<..> only, according to this: How to "possible" solve the problem but it didn't work.
If you have any ideas, please let me know.
Thanks!
Check all of the places where you are assigning something to sonEntities. The link you referenced distinctly points out creating a new HashSet but you can have this error anytime you reassign the set. For example:
public void setChildren(Set<SonEntity> aSet)
{
this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
}
Usually you want to only "new" the set once in a constructor. Any time you want to add or delete something to the list you have to modify the contents of the list instead of assigning a new list.
To add children:
public void addChild(SonEntity aSon)
{
this.sonEntities.add(aSon);
}
To remove children:
public void removeChild(SonEntity aSon)
{
this.sonEntities.remove(aSon);
}
The method:
public void setChildren(Set<SonEntity> aSet) {
this.sonEntities = aSet;
}
works if the parentEntity is detached and again if we update it.
But if the entity is not detached from per context, (i.e. find and update operations are in the same transaction) the below method works.
public void setChildren(Set<SonEntity> aSet) {
//this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
this.sonEntities.clear();
if (aSet != null) {
this.sonEntities.addAll(aSet);
}
}
When I read in various places that hibernate didn't like you to assign to a collection, I assumed that the safest thing to do would obviously be to make it final like this:
class User {
private final Set<Role> roles = new HashSet<>();
public void setRoles(Set<Role> roles) {
this.roles.retainAll(roles);
this.roles.addAll(roles);
}
}
However, this doesn't work, and you get the dreaded "no longer referenced" error, which is actually quite misleading in this case.
It turns out that hibernate calls your setRoles method AND it wants its special collection class installed here, and won't accept your collection class. This had me stumped for a LONG time, despite reading all the warnings about not assigning to your collection in your set method.
So I changed to this:
public class User {
private Set<Role> roles = null;
public void setRoles(Set<Role> roles) {
if (this.roles == null) {
this.roles = roles;
} else {
this.roles.retainAll(roles);
this.roles.addAll(roles);
}
}
}
So that on the first call, hibernate installs its special class, and on subsequent calls you can use the method yourself without wrecking everything. If you want to use your class as a bean, you probably need a working setter, and this at least seems to work.
Actually, my problem was about equals and hashcode of my entities. A legacy code can bring a lot of problems, never forget to check it out. All I've done was just keep delete-orphan strategy and correct equals and hashcode.
I fixed by doing this :
1. clear existing children list so that they are removed from database
parent.getChildren().clear();
2. add the new children list created above to the existing list
parent.getChildren().addAll(children);
Hope this post will help you to resolve the error
I had the same error. The problem for me was, that after saving the entity the mapped collection was still null and when trying to update the entity the exception was thrown. What helped for me: Saving the entity, then make a refresh (collection is no longer null) and then perform the update. Maybe initializing the collection with new ArrayList() or something might help as well.
I ran into this when updating an entity with a JSON post request.
The error occurred when I updated the entity without data about the children, even when there were none.
Adding
"children": [],
to the request body solved the problem.
I used #user2709454 approach with small improvement.
public class User {
private Set<Role> roles;
public void setRoles(Set<Role> roles) {
if (this.roles == null) {
this.roles = roles;
} else if(this.roles != roles) { // not the same instance, in other case we can get ConcurrentModificationException from hibernate AbstractPersistentCollection
this.roles.clear();
if(roles != null){
this.roles.addAll(roles);
}
}
}
}
It might be caused by hibernate-enhance-maven-plugin. When I enabled enableLazyInitialization property this exception started on happening on my lazy collection. I'm using hibernate 5.2.17.Final.
Note this two hibernate issues:
https://hibernate.atlassian.net/browse/HHH-10708
https://hibernate.atlassian.net/browse/HHH-11459
All those answers didnt help me, BUT I found another solution.
I had an Entity A containing a List of Entity B. Entity B contained a List of Entity C.
I was trying to update Entity A and B. It worked. But when updating Entity C, I got the mentioned error. In entity B I had an annotation like this:
#OneToMany(mappedBy = "entity_b", cascade = [CascadeType.ALL] , orphanRemoval = true)
var c: List<EntityC>?,
I simply removed orphanRemoval and the update worked.
HAS RELATION TYPE:
Don't try to instantiate the collection when it's declared in hasMany, just add and remove objects.
class Parent {
static hasMany = [childs:Child]
}
USE RELATION TYPE:
But the collection could be null only when is declared as a property (use relation) and is not initialized in declaration.
class Parent {
List<Child> childs = []
}
The only time I get this error is when I try to pass NULL into the setter for the collection. To prevent this, my setters look like this:
public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
if(submittedForms == null) {
this.submittedForms.clear();
}
else {
this.submittedForms = submittedForms;
}
}
I had this problem when trying to use TreeSet. I did initialize oneToMany with TreeSet which works
#OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
#OrderBy("id")
private Set<WizardAnswer> answers = new TreeSet<WizardAnswer>();
But, this will bring the error described at the question above. So it seems that hibernate supported SortedSet and if one just change the line above to
#OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
#OrderBy("id")
private SortedSet<WizardAnswer> answers;
it works like magic :)
more info on hibernate SortedSet can be here
There is this bug which looks suspiciously similar: https://hibernate.atlassian.net/browse/HHH-9940.
And the code to reproduce it: https://github.com/abenneke/sandbox/tree/master/hibernate-null-collection/src/test
There are 2 possible fixes to this:
the collection is initialized with an empty collection (instead of null)
orphanRemoval is set to false
Example - was:
#OneToMany(cascade = CascadeType.REMOVE,
mappedBy = "jobEntity", orphanRemoval = true)
private List<JobExecutionEntity> jobExecutionEntities;
became:
#OneToMany(cascade = CascadeType.REMOVE,
mappedBy = "jobEntity")
private List<JobExecutionEntity> jobExecutionEntities;
I had the same issue, but it was when the set was null. Only in the Set collection, in List work fine. You can try to the hibernate annotation #LazyCollection(LazyCollectionOption.FALSE) instead of JPA annotation fetch = FetchType.EAGER.
My solution:
This is my configuration and work fine
#OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
private Set<Barcode> barcodes;
#OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
private List<FormatAdditional> additionals;
One other cause may be using lombok.
#Builder - causes to save Collections.emptyList() even if you say .myCollection(new ArrayList());
#Singular - ignores the class level defaults and leaves field null even if the class field was declared as myCollection = new ArrayList()
My 2 cents, just spent 2 hours with the same :)
I was getting A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance when I was setting parent.setChildren(new ArrayList<>()). When I changed to parent.getChildren().clear(), it solved the problem.
Check for more details: HibernateException - A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance.
I am using Spring Boot and had this issue with a collection, in spite of not directly overwriting it, because I am declaring an extra field for the same collection with a custom serializer and deserializer in order to provide a more frontend-friendly representation of the data:
public List<Attribute> getAttributes() {
return attributes;
}
public void setAttributes(List<Attribute> attributes) {
this.attributes = attributes;
}
#JsonSerialize(using = AttributeSerializer.class)
public List<Attribute> getAttributesList() {
return attributes;
}
#JsonDeserialize(using = AttributeDeserializer.class)
public void setAttributesList(List<Attribute> attributes) {
this.attributes = attributes;
}
It seems that even though I am not overwriting the collection myself, the deserialization does it under the hood, triggering this issue all the same. The solution was to change the setter associated with the deserializer so that it would clear the list and add everything, rather than overwrite it:
#JsonDeserialize(using = AttributeDeserializer.class)
public void setAttributesList(List<Attribute> attributes) {
this.attributes.clear();
this.attributes.addAll(attributes);
}
Mine was completely different with Spring Boot!
For me it was not due to setting collection property.
In my tests I was trying to create an entity and was getting this error for another collection that was unused!
After so much trying I just added a #Transactional on the test method and it solved it. Don't no the reason though.
#OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>();
I experienced the same error when I was adding child object to the existing list of Child Objects.
childService.saveOrUpdate(child);
parent.addToChildren(child);
parentService.saveOrUpdate(parent);
What resolved my problem is changing to:
child = childService.saveOrUpdate(child);
Now the child is revive with other details as well and it worked fine.
Had this issue with spring-boot 2.4.1 when running the tests in bulk from [Intellij Idea] version 2020.3. The issue doesn't appear when running only one test at a time from IntelliJ or when running the tests from command line.
Maybe Intellij caching problem?
Follow up:
The problem appears when running tests using the maven-surefire-plugin reuseForks true. Using reuseForks false would provide a quick fix, but the tests running time will increase dramatically. Because we are reusing forks, the database context might become dirty due to other tests that are run - without cleaning the database context afterwards. The obvious solution would be to clean the database context before running a test, but the best one should be to clean up the database context after each test (solving the root cause of the original problem). Using the #Transactional annotation on your test methods will guarantee that your database changes are rolled back at the end of the test methods. See the Spring documentation on transactions: https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testcontext-tx.
I face a similar issue where I was using these annotations in my parent entity :
#Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
Mistakenly, I was trying to save a null parent object in database and properly setting values to my entity object resolved my error. So, do check if you are silly setting wrong values or trying to save a null object in database.
Adding my dumb answer. We're using Spring Data Rest. This was our pretty standard relationship. The pattern was used elsewhere.
//Parent class
#OneToMany(mappedBy = 'parent',
cascade= CascadeType.ALL, orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
List<Child> children = new LinkedList<>()
//Child class
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = 'ParentID', updatable = false)
#JsonBackReference
Parent parent
With the relationship we created, it was always intended that the children would be added through their own repo. I had not yet added the repo. The integration test we had was going through a complete lifecycle of the entity via REST calls so the transactions would close between requests. No repo for the child meant the json had the children as part of the main structure instead of in _embedded. Updates to the parent would then cause problems.
Following solution worked for me
//Parent class
#OneToMany(mappedBy = 'parent',
cascade= CascadeType.ALL, orphanRemoval = true)
#OrderBy(value="ordinal ASC")
List<Child> children = new ArrayList<>()
//Updated setter of children
public void setChildren(List<Children> children) {
this.children.addAll(children);
for (Children child: children)
child.setParent(this);
}
//Child class
#ManyToOne
#JoinColumn(name="Parent_ID")
private Parent parent;
Instead of assigning new collection
public void setChildren(Set<ChildEntity> children) {
this.children = children;
}
Replace all elements with
public void setChildren(Set<ChildEntity> children) {
Collections.replaceAll(this.children,children);
}
be careful with
BeanUtils.copyProperties(newInsum, insumOld,"code");
This method too break the hibernate.
This is in contrast to the previous answers, I had exactly the same error: "A collection with cascade=”all-delete-orphan” was no longer referenced...." when my setter function looked like this:
public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
if( this.taxCalculationRules == null ) {
this.taxCalculationRules = taxCalculationRules_;
} else {
this.taxCalculationRules.retainAll(taxCalculationRules_);
this.taxCalculationRules.addAll(taxCalculationRules_);
}
}
And then it disappeared when I changed it to the simple version:
public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
this.taxCalculationRules = taxCalculationRules_;
}
(hibernate versions - tried both 5.4.10 and 4.3.11. Spent several days trying all sorts of solutions before coming back to the simple assignment in the setter. Confused now as to why this so.)
In my case it was concurrent access to one Hibernate Session from several threads.
I had the Spring Boot Batch and RepositoryItemReader implementation where I fetched entities by page request with size 10.
For example my entities are:
#Entity
class JobEntity {
#ManyToOne(fetch = FetchType.LAZY)
private GroupEntity group;
}
#Entity
class GroupEntity {
#OneToMany(mappedBy = "group", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private Set<Config> configs;
}
Batch process: reader -> processor -> writer in one transaction.
In that entities configuration, GroupEntity can escapes to other threads:
First thread that entered to read section fetches the page of JobEntity with size 10 (RepositoryItemReader#doRead), this items contain one shared GroupEntity object (because all of them pointed to the same group id). Then it takes the first entity. Next threads that come to read section take JobEntity from this page one by one, until this page will be exhausted.
So now threads have access to the same GroupEntity instance thought the JobEntity instances, that is unsafe multi thread access to the one Hibernate Session.
As of 2021 and Spring Boot 2.5, it helped me to initialize the field right away when declaring it:
#OneToMany(mappedBy="target",fetch= FetchType.EAGER,cascade = CascadeType.ALL, orphanRemoval = true)
private List<TargetEntity> targets = new ArrayList<>();
Issue is solved when we make the child as final..
we should not change the reference of the child in constructor as well as setter.