Hello,
I would like to know the recommended approach for CRUD operations on @OneToMany relationships.
– TLDR –
I want to efficiently update a parent-child relationship, both in normal and bulk/batch operations (1-50K parents with up to 2x30 children), for changes to the entire parent/child complex as well as individual children. I want to minimize the amount of code/CRUD logic in the entities but also ensure that the parent and child are always in sync. If possible, I prefer not to rely heavily on annotation magic.
Here’s the full story:
In simplified terms, I have two entities:
@Table(name = "parent")
@Entity
public class ParentEntity{
@Id
@JdbcTypeCode(SqlTypes.*CHAR* )
@Column(length = 36)
@GeneratedValue
public UUID id;
@OneToMany(mappedBy = "parentEntity", cascade = CascadeType.*ALL* , orphanRemoval = true)
public List<ChildEntity> children = new ArrayList<>();
}
@Table(name = "child")
@Entity
public class ChildEntity{
@Id
@JdbcTypeCode(SqlTypes.*CHAR* )
@Column(length = 36)
@GeneratedValue
public UUID id;
@ManyToOne(fetch = FetchType.*LAZY* )
@JoinColumn(name = "parent _id")
public ParentEntity parentEntity;
}
So far, I have only dealt with the case where the entire parent entity needed to be updated (CRUD).
For that purpose, I created a ParentRepository
. It first calculates (quite complex) which children need to be removed, updated, or added during an update. Then it calls parent.update()
, where the changes are applied directly to the children in an operation on the parent. This ensures that the parent and children are always synchronized, and I never need to call persistAndFlush
separately (Which I think is a big issue?). However, the downside is that the ParentEntity
is bloated with CRUD operations, which I believe shouldn’t be there. Currently, the ParentEntity
has around 170 lines, well beyond my “pain threshold” of 60-80 lines.
Now, the additional requirement is that I need to be notified of CRUD changes to the children at another part of the application. One quick solution would be to add an @EntityListeners
annotation to the ChildEntity
. However, I’m not a big fan of annotations because they can quickly become magical and difficult to understand. Therefore, I’m considering controlling the CRUD operations through a separate ORM service and then directly notifying a service injected there with a clear method call.
However, when I operate directly on the children, it seems that the parent entity doesn’t become aware of the changes, and I have to reload it from the database, resulting in unnecessary SELECT queries. This is particularly problematic for bulk operations involving 50K parent entities, each with up to 2x30 child entities.
Additionally, now there is a new case where I need to update only specific fields of the parent (CRUD). Loading the entire parent entity and processing all the fields appears to be too expensive. Therefore, I’m currently handling this in the ChildRepository
. But now I have two separate places where a parent and its children can be updated, which makes it impossible to have a central place for making changes to the parent/child bundle and notifying various services about these changes.
Last but not least, if the bulk update operations become to resource hungry, actually there could even be up to 1 million parents in - EXTREME - cases - I would like to have the possibility to skip informing listeners about the changes, or send a consolidated “Bulk update info” to them only - that would be easy when calling a service from a dedicated, central ORM CRUD Service, but impossible - or I just have no idea how to - with an annotation based @EntityListeners solution.
Sorry, this is so complex that it’s challenging for me to explain concisely but clearly. I did my best. I hope you understand my problem.
Thank you very much for your time and thoughts on this.