How do I stop Hibernate from deleting entities from DB which are not in list which I working with?

32 Views Asked by At

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.

1

There are 1 best solutions below

0
Malvin Lok On BEST ANSWER

Hibernate is removing the PathCord entities from the database because it treats the association between Operation and PathCord as the "owner" of the relationship. When you filter the list, Hibernate thinks that you've removed the entities from the Operation object, and it cascades the removal operation to these associated entities.

The simplest solution is to update the relationship annotation configuration in your Operation class:

  1. You need to change the direction of the relationship owner. Make the PathCord entities the owner of the relationship instead of the Operation. This means changing the @OneToMany annotation in your Operation class to a @OneToMany(mappedBy = "operation").

  2. Use the @OneToMany(cascade = CascadeType.ALL, orphanRemoval = false) annotation on the path list inside your Operation entity. This will ensure that even if a PathCord is removed from an Operation's path list, it will not be deleted from your database.

  3. Add @ManyToOne annotation inside your PathCord class. This way you indicate PathCord is the owner of the relationship.

Here's how the code would look:

In your Operation class:

@Entity
public class Operation{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // some fields

    private Status status;

    @OneToMany(mappedBy = "operation", cascade = CascadeType.ALL, orphanRemoval = false)
    private List<PathCord> path;

    private String name;

    // some fields
}

In your PathCord class:

@Entity
public class PathCord {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private double x;
    private double y;
    boolean beenThere;

    @ManyToOne
    @JoinColumn(name = "operation_id")
    private Operation operation;
}

After making this change, if you want to remove a PathCord from an Operation, you'll have to do so explicitly using something like pathCordRepository.delete(pathCord), or by setting its Operation value to null. Hibernate will no longer automatically remove PathCords that are removed from an Operation's path list.