1

I have two entities like this with joins,

Entity Address:

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@JsonBackReference
@EqaulsAndHashCode.Exclude
@ToString.Exclude
@JoinColumn(name="PERSON_ID", referencedColumns = "PERSON_ID")
private Person personDetails;

Entity Person:

@OneToMany(mappedBy = "personDetails", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@ToString.Exclude
@EqualsAndHashCode.Exclude
@JsonManagedReference
List<Address> addressList = new ArrayList<>();

I am deleting Address using AddressRespository.deleteAll(//list of address) inside a @Transactional method. This method then calls another method from a different service which is also annotated with @Transactional and updates one of the property of Person and performs the save operation.

As this stage it returns an error deleted instance passed to merge.

I tried to use flush after deleteAll which resolves this error but it did not delete anything in the database.

How can I achieve the desired outcome here?

cs55
  • 7
  • 4
  • You probably still have the reference to the deleted address(es) in your person object. Remove them from the addressList. – grekier May 16 '23 at 12:53
  • How though? Thought deleteAll would take care of it automatically. – cs55 May 16 '23 at 13:13
  • You shouldn't try this, as JPA does caching by the entity ID - delete then persist is pretty much just an undo operation internally so I don't know why you'd want delete then insert statements. That said, your issue is not likely with the delete then persist, but with your transactional context; the same issue you have in your other question. Put another way, are any changes you make committed and visible through your DB client? @Transactional is a Spring thing, so you'd have to look at the contracts and how your methods chain together to understand why this might not work the way you want – Chris May 16 '23 at 13:56
  • If your issue is solely with the delete not working, that you are trying to delete something and seeing an issue because you want it gone in the end - you are, as the error suggest, passing in an entity that you've marked to delete to the save/merge operation. Look at your model again, you've got references between entities marked as cascade merge. So if they aren't null when one of those entities is saved, the save is likely cascading to the entity you want to delete, causing it to undo the delete. Clear and manage your references and the problem will go away. – Chris May 16 '23 at 13:59

2 Answers2

0

The issue is that you have reference in your addressList to some of the Address instances that were delete. If you debug and set a breakpoint before saving the Person, you will see that this list holds the IDs of the deleted addresses. Deleting the Address will not result in it being removed from the other (Person) object as long as it was loaded in memory before delete.

I see 2 way to fix this:

  1. You fetch the Person from JPA after deletion.
  2. You manually remove the deleted addresses from the list

As you have a list of addresses that you pass to the deleteAll, I guess you have everything you need to use solution 2. Just do a person.addressList.removeAll(//list of addresses) before saving.

This is assuming that you want all addresses removed from DB. If you want to recreated them, you could remove the IDs of the addresses in the list.

grekier
  • 2,322
  • 1
  • 16
  • 23
0

For your mapping calling AddressRespository.deleteAll(Iterable) is not enough, because it removes Address items from the repo, but not from the Person.addressList(). To fix your issue you need to modify your code as

person.getAddressList().removeAll(addressesToBeRemoved);
AddressRespository.deleteAll(addressesToBeRemoved);

P.S. As soon as you have orphanRemoval = true in your @OneToMany declaration if the addresses you are removing are not referenced from another Person instance it can be enough just to remove addresses from addressList() to make Hibernate remove them from the address table as well:

person.getAddressList().removeAll(addressesToBeRemoved);

See this answer: https://stackoverflow.com/a/60838256/12473843

Sergey Tsypanov
  • 3,265
  • 3
  • 8
  • 34