Deletes not cascading for self-referencing entities - java

I have the following (simplified) Hibernate entities:
#Entity
#Table(name = "package")
public class Package {
protected Content content;
#OneToOne(cascade = {javax.persistence.CascadeType.ALL})
#JoinColumn(name = "content_id")
#Fetch(value = FetchMode.JOIN)
public Content getContent() {
return content;
}
public void setContent(Content content) {
this.content = content;
}
}
#Entity
#Table(name = "content")
public class Content {
private Set<Content> subContents = new HashSet<Content>();
private ArchivalInformationPackage parentPackage;
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(name = "subcontents", joinColumns = {#JoinColumn(name = "content_id")}, inverseJoinColumns = {#JoinColumn(name = "elt")})
#Cascade(value = {org.hibernate.annotations.CascadeType.DELETE, org.hibernate.annotations.CascadeType.REPLICATE})
#Fetch(value = FetchMode.SUBSELECT)
public Set<Content> getSubContents() {
return subContents;
}
public void setSubContents(Set<Content> subContents) {
this.subContents = subContents;
}
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "parent_package_id")
public Package getParentPackage() {
return parentPackage;
}
public void setParentPackage(Package parentPackage) {
this.parentPackage = parentPackage;
}
}
So there is one Package, which has one "top" Content. The top Content links back to the Package, with cascade set to ALL. The top Content may have many "sub" Contents, and each sub-Content may have many sub-Contents of its own. Each sub-Content has a parent Package, which may or may not be the same Package as the top Content (ie a many-to-one relationship for Content to Package).
The relationships are required to be ManyToOne (Package to Content) and ManyToMany (Content to sub-Contents) but for the case I am currently testing each sub-Content only relates to one Package or Content.
The problem is that when I delete a Package and flush the session, I get a Hibernate error stating that I'm violating a foreign key constraint on table subcontents, with a particular content_id still referenced from table subcontents.
I've tried specifically (recursively) deleting the Contents before deleting the Package but I get the same error.
Is there a reason why this entity tree is not being deleted properly?
EDIT: After reading answers/comments I realised that a Content cannot have multiple Packages, and a sub-Content cannot have multiple parent-Contents, so I have modified the annotations from ManyToOne and ManyToMany to OneToOne and OneToMany. Unfortunately that did not fix the problem.
I have also added the bi-directional link from Content back to the parent Package which I left out of the simplified code.

If I understand correctly, based on the ManyToOne mapping, one Content has many Packages, and I assume you removed the "packages" collection field from your Content class in your simplified code above?
So, for your "packages" collection field, do you have a cascade delete on it (just like what you have on your subcontents)? If you do, then I think it should work. When you delete the root Content, it should perform cascade delete on each subcontent, and each content will then perform cascade delete on the package.
Does that work?

The problem turned out to be caused by the fact that I was flushing and clearing the session after deleting each Package, and due to the circular dependencies in the model not everything was being deleted. The flush and clear are required because very large data sets are involved. In the end I changed it so that a set of all entities dependent on the current Package is constructed (which may include other Packages) and then all deleted before calling flush and clear.

Related

Spring JPA - One to Many - Re-parenting

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.

JPA many-to-many relationship causing infinite recursion and stack overflow error

I'm working on an EclipseLink project in which one user can "follow" another as can be done on social media sites. I have this set up with a User entity (referencing a table called users) which has a list of "followers" (users who follow that user) and another list of "following" (users that user is following). The relationship is defined in a separate table called followers which contains columns for the followed user's ID (user_id) and the following user's ID (follower_id).
My users model looks like this:
#Entity
#Table(name = "users")
#NamedQuery(name = "User.findAll", query = "SELECT u FROM USER u")
public class User {
// other attributes
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "follower", joinColumns = #JoinColumn(
name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "follower_id", referencedColumnName = "id"))
private List<User> followers;
#ManyToMany(mappedBy = "followers")
private List<User> following;
// other getters and setters
public List<User> getFollowers() {
return this.followers;
}
public List<User> getFollowing() {
return this.following;
}
}
The getFollowers() method seems to work fine, but when getFollowing() is called I get a bunch of console spam that culminates in a StackOverflowException:
com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion
(StackOverflowError) (through reference chain:
org.eclipse.persistence.indirection.IndirectList[0]-
>org.myproject.model.User["followers"]-
>org.eclipse.persistence.indirection.IndirectList[0]-
>org.myproject.model.User["following"]-
...
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase
.serializeFields(BeanSerializerBase.java:518)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize
(BeanSerializer.java:117)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer
.serializeContents(IndexedListSerializer.java:94)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer
.serializeContents(IndexedListSerializer.java:21)
...
Please let me know if I should provide more of the stack trace. Any hints?
Every time you have #OneToMany (a collection) you need to add #JsonIgnore to it or else it will cause an infinite loop which results in a stack overflow exception because it keeps looking up between the parent(the one side) and the child (the many side)
For more info on dealing with this kind of problems check this excellent article http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
public class A {
private String name;
#JsonIgnoreProperties(value = "linkToA") // remove field to avoid loop
private B linkToB;
}
public class B {
private String name;
#JsonIgnoreProperties(value = "linkToB") // remove field to avoid loop
private A linkToA;
}
I tried everything what I found in the web and it didn't work. None of any annotation.
But I found a solution after a huge fight with this issue.
First point you need to add to both entities (not the relational one) this annotation:
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "relationClass"})
public class YourClass { ...
}
Where "relationClass" is the name of the List/Set of your class for the relations:
For example:
#OneToMany(mappedBy = "yourClass", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private Set<RelationClass> relationClass;
You also need to specify "hibernateLazyInitializer", "handler" in the annotation or it will cause serialization problem.
After it if you see the table that define relation table and you will see rows in there, and in your JSON response will not be any loop anymore. So as I think the best solution is also create a repository for the relation tablee and access the data from there.
Hope it will help someone!
I think the problem in previous answers is the package of the annotation. In the article http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
#JsonManagedReference and #JsonBackReference works fine.
But the package should be com.fasterxml.jackson.annotation .
Sometimes another packages may be imported and does not solve the problem.
In addition adding
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
before the class definition of the model works fine.
You can also use lombok's EqualsAndHashcode annotation like below.
#EqualsAndHashcode(exclude ={"following"})
public class User{}

How to deal with unique field generated by hibernate?

Getting right to the point, I'm trying to build the following logic to hibernate relationships.
A Resource has many read groups.
A Resource has many write groups.
Both groups are Groups class.
What I did until now:
ResourcePage.class
public class ResourcePage {
/*
useless code
*/
private Set read;
private Set write;
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
targetEntity = Groups.class)
#JoinTable(name = "resourcepage_read_permissions")
public Set getRead() {
return read;
}
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
targetEntity = Groups.class)
#JoinTable(name = "resourcepage_write_permissions")
public Set getWrite() {
return write;
}
/*
useless code
*/
}
The tables is created as expected.
However, hibernate is generating an unique constraint to id of group and this is giving me a big problem because sometimes two different resources can be same group as read group.
How do you guys deal with it?
How can I make hibernate not generate this unique constraint?
Thanks a lot.
You need to use #ManyToMany instead of #OneToMany.

Why does not FetchType.Lazy work?

I have model Category. It may have parent Category and list of sub Category. I wrote this questions because could not find case where entity relate to himself.
I tried to implement it like this:
#Entity
public class Category {
#Id
private String id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parent_id")
private Category parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
private List<Category> subcategories = Lists.newArrayList();
}
I save entities like:
Category parent = new Category();
parent.setName("parent");
Category child1 = new Category();
child1.setName("child1");
child1.setParent(parent);
parent.getSubcategories().add(child1);
categoryPersistence.save(parent);
I expected to see something like:
parent {
id
parent: null
childs {
child {
id
parent: ...lazy loading exception...
childs: null
}
}
}
But in child model on filed parent i have recursive loop.
How to prevent it?
Yeah, i also used #JsonIgnore. But i was not sure is it good practice.
But if i have a case when i need one Category and i realy need to send it to UI with parent. Is #JsonIgnore can produce this?
#damienMiheev I have also suffered from same problem, but debugging the issue 1 or half hour i figured out
That even if your parent field loaded lazily, while the automatic JSON generation getter of your field is getting called which is fetching the values, which is creating some cyclic execution.
Because it is fetching parent then fetching children and again fetching parent for every child in children collection and continuing this process until StackOverflows.
Here are the solution for your problem
You should exclude the "parent" the from json generation e.g. you can mark that field as #JsonIgnore.
you should also not include parent and children in your hashCode(), equals() and toString() method.

Hibernate - A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance

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.

Categories