-
Story
-
Resolution: Unresolved
-
Medium
-
None
-
None
Hibernate is currently issuing two SQL operations when saving fragments which is leading to a 2x performance degradation in write operations. This is the behaviour we are seeing - note the updates of parent_id after inserts:
insert into fragment (anchor_id,attributes,xpath,id) values (?,?,?,?) insert into fragment (anchor_id,attributes,xpath,id) values (?,?,?,?) insert into fragment (anchor_id,attributes,xpath,id) values (?,?,?,?) update fragment set parent_id = ? where id = ? update fragment set parent_id = ? where id = ? update fragment set parent_id = ? where id = ?
It is related to our use of a unidirectional @OneToMany mapping for the childFragments in the FragmentEntity class. Hibernate has poor performance in this case. This article https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/ gives a good explanation of the cause:
What’s the purpose of those update statements?
If you take a look at Hibernate flush order, you’ll see that the persist action is executed before the collection elements are handled. This way, Hibernate inserts the child records first without the Foreign Key since the child entity does not store this information. During the collection handling phase, the Foreign Key column is updated accordingly.
The solution is to implement a bidirectional OneToMany/ManyToOne mapping. However, care must be taken to maintain both sides of the mapping. FragmentEntity should be changed to prevent direct changes to the childFragments collection. Instead, helper methods should be used:
public class FragmentEntity implements Serializable { // ... @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parent_id") private FragmentEntity parent; @Setter(AccessLevel.NONE) @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) private Set<FragmentEntity> childFragments; public Set<FragmentEntity> getChildFragments() { return Collections.unmodifiableSet(this.childFragments); } public void addChildFragment(final FragmentEntity newChildFragment) { newChildFragment.parent = this; childFragments.add(newChildFragment); } }
- mentioned in
-
Page Loading...