Reputation: 1088
I've been reading posts about orphanRemoval= true
in JPA .
According to documentation :
orphanRemoval is a flag -
Whether to apply the remove operation to entities that have been removed from the relationship and to cascade the remove operation to those entities.
Also I refered to this article for more info , where they have tried to set child entity (address - in their example ) as null.
I currently understand that making orphanRemoval= true
will perform similar operation as cascade=CascadeType.REMOVE
and if I remove my parent entity , it will delete the child entity as well .
What i want to test is the additional functionality that it brings which is removal of entities that are not referenced by their parent entity.
I am trying to create a similar scenario where I am setting the new collection of phones as new ArrayList<>()
where the parent entity is Person .
Following are my entity classes .
@Entity
@Table(name = "person")
@Data
public class Person {
@Id
int pd ;
String fname;
String lname;
@OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="person",orphanRemoval=true)
List<Phone> phones = new ArrayList<>() ;
public boolean addPhone(Phone phone) {
boolean added = this.phones.add(phone);
phone.setPerson(this);
return added;
}
}
@Entity
@Table(name = "phone")
@Data
public class Phone {
private int countryCode;
@Id
private String number ;
@ManyToOne
@JoinColumn(name="fk_person")
Person person ;
}
public void testFlow() {
Person p = fetchById(765);
p.setPhones(new ArrayList<>());
personRepo.save(p); **// exception on this line**
getPersons();
}
public Person fetchById(int id) {
Optional<Person> pe = personRepo.findById(id);
Person person = pe.get();
System.out.println("person is :"+ person.getFname());
System.out.println("cc is :"+ person.getPhones().get(0).getNumber());
return person;
}
public List<Person> getPersons() {
List<Person> persons = personRepo.findAll();
persons.forEach(p -> {
System.out.println("person :"+p.getPd());
System.out.println("person phones :"+p.getPhones().get(0).getNumber());
System.out.println("=================================");
});
return persons;
}
The entry method is testFlow() .
When I execute this code , I get error :
org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.example.entity.Person.phones
Any clue how i can test the working example of orphanRemoval
?
Upvotes: 4
Views: 5624
Reputation: 10716
The problem is caused by the following line:
p.setPhones(new ArrayList<>());
In Hibernate, you cannot overwrite a collection retrieved from the persistence context if the association has orphanRemoval = true
specified. If your goal is to end up with an empty collection, use p.getPhones().clear()
instead.
Upvotes: 5
Reputation: 26046
This is the line the exception should be thrown:
personRepo.save(p);
It happens, because you are trying to save Person
that doesn't reference any Phone
s. I means, that you're dereferencing only Person
but not the Phone
entities. Since it's a bidirectional relationship, you would have to dereference both:
public void testFlow() {
Person p = fetchById(765);
p.getPhones().foreach(ph -> ph.setPerson(null));
p.setPhones(new ArrayList<>());
personRepo.save(p); **// exception on this line**
getPersons();
}
Upvotes: 1