I working on a project where sometimes I need to get complex entity from DB with list of other entities inside:
@Entity
public class Operation{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// some fields
private Status status;
@OneToMany
@JoinColumn(name = "operation_id")
private List<PathCord> path;
private String name;
// some fields
}
The entity from list:
@Entity
public class PathCord {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private double x;
private double y;
boolean beenThere;
}
And sometimes I need to work with "Operation" entity with only few filtered PathCord's. Like this:
Operation operation = getLastOperationFromDB();
//some logic
operation.setPath(new ArrayList<>(operation.getPath().stream()
.filter(pathCord -> !pathCord.isBeenThere())
.toList()));
// do something
operation.setStatus(Status.COMPLETED);
operationRepository.save(operation);
The problem is that when repository updating the "Operation" it also deletes ALL the PathCord's which wasn't included in filtered list. But, I need just to update these PathCord's if they were changed or just leave the table unchanged if PathCord's remained untouched. Making different query for this operationt isn't an option. I need to get full list first.
I have already tried different variants of @OnDelete and @Cascade annotations, but I think I don't get how it works properly.
Hibernate is removing the
PathCordentities from the database because it treats the association betweenOperationandPathCordas the "owner" of the relationship. When you filter the list, Hibernate thinks that you've removed the entities from theOperationobject, and it cascades the removal operation to these associated entities.The simplest solution is to update the relationship annotation configuration in your
Operationclass:You need to change the direction of the relationship owner. Make the
PathCordentities the owner of the relationship instead of theOperation. This means changing the@OneToManyannotation in yourOperationclass to a@OneToMany(mappedBy = "operation").Use the
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = false)annotation on thepathlist inside yourOperationentity. This will ensure that even if aPathCordis removed from anOperation'spathlist, it will not be deleted from your database.Add
@ManyToOneannotation inside yourPathCordclass. This way you indicatePathCordis the owner of the relationship.Here's how the code would look:
In your
Operationclass:In your
PathCordclass:After making this change, if you want to remove a
PathCordfrom anOperation, you'll have to do so explicitly using something likepathCordRepository.delete(pathCord), or by setting itsOperationvalue tonull. Hibernate will no longer automatically removePathCordsthat are removed from anOperation'spathlist.