Как удалить сиротские сущности, используя спящий режим и JPA для отношений "многие ко многим"?
Я хочу удалить сиротские сущности, используя hibernate и JPA для отношений "многие ко многим", но все, что я нашел, было атрибутом атрибута. org.hibernate.annotations.CascadeType.DELETE_ORPHAN (т.е. @Cascade (value = {org.hibernate.annotations.CascadeType.DELETE_ORPHAN)), который работает только для отношений "один ко многим".
Я хочу знать, могу ли я удалить сироты из моих отношений "многие ко многим".
Ответы
Ответ 1
Из книги "Pro JPA 2":
Только отношения с одиночной мощностью на стороне источника могут включить удаление сирот, поэтому опция orphanRemoval определенные в аннотации отношений @OneToOne и @OneToMany, но ни о каких-либо аннотациях @ManyToOne или @ManyToMany.
Это облом, но для ManyToMany нет автоматического удаления сирот JPA.
Ответ 2
Собственно, я сделал тест со следующими объектами:
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
@ManyToMany
@Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Role> roles = new HashSet<Role>();
//...
}
@Entity
public class Role {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToMany(mappedBy = "roles")
private Set<Person> persons = new HashSet<Person>();
//...
}
И со следующим набором данных:
<dataset>
<PERSON id="1" firstname="john" lastname="doe"/>
<PERSON id="2" firstname="clark" lastname="kent"/>
<PERSON id="3" firstname="james" lastname="bond"/>
<ROLE id="1" name="foo"/>
<ROLE id="2" name="bar"/>
<ROLE id="3" name="boo"/>
<ROLE id="4" name="baz"/>
<PERSON_ROLE persons_id="1" roles_id="1"/>
<PERSON_ROLE persons_id="1" roles_id="2"/>
<PERSON_ROLE persons_id="2" roles_id="2"/>
<PERSON_ROLE persons_id="2" roles_id="3"/>
<PERSON_ROLE persons_id="3" roles_id="1"/>
<PERSON_ROLE persons_id="3" roles_id="4"/>
</dataset>
Следующий метод тестирования:
@Test
public void testCascadeDeleteOrphanOnDelete() {
Person person = entityManager.find(Person.class, 1L);
entityManager.remove(person);
ReflectionAssert.assertPropertyLenientEquals("id", Arrays.asList(2, 3), findAllPersons());
ReflectionAssert.assertPropertyLenientEquals("id", Arrays.asList(3, 4), findAllRoles());
}
private List<Person> findAllPersons() {
return entityManager.createQuery("from Person").getResultList();
}
private List<Role> findAllRoles() {
return entityManager.createQuery("from Role").getResultList();
}
Просто проходит. Ниже произведенного выхода:
Hibernate: select personx0_.id as id17_0_, personx0_.firstName as firstName17_0_, personx0_.lastName as lastName17_0_ from Person personx0_ where personx0_.id=?
Hibernate: select roles0_.persons_id as persons1_1_, roles0_.roles_id as roles2_1_, rolex1_.id as id18_0_, rolex1_.name as name18_0_ from Person_Role roles0_ left outer join Role rolex1_ on roles0_.roles_id=rolex1_.id where roles0_.persons_id=?
Hibernate: delete from Person_Role where persons_id=?
Hibernate: delete from Role where id=?
Hibernate: delete from Role where id=?
Hibernate: delete from Person where id=?
Hibernate: select personx0_.id as id17_, personx0_.firstName as firstName17_, personx0_.lastName as lastName17_ from Person personx0_
Hibernate: select rolex0_.id as id18_, rolex0_.name as name18_ from Role rolex0_
Ответ 3
До сих пор не существует атрибута orphanRemoval для аннотации ManyToMany. У меня такая же проблема. И предложение реализовать его в базе данных не является ответом на проблему. Вся философия JPA противоречит внедрению логики в базе данных, но через сопоставления.
Ответ 4
Не используйте спящий режим для этого, напишите SQl-код и запустите его непосредственно в базе данных с помощью набора. И затем исправить структурную проблему базы данных, которая позволит вам получить осиротевшие записи в первую очередь. Это признак очень плохо разработанной базы данных, если у вас даже есть осиротевшие записи. Этот shouod должен быть встроенным исправлением, а не тем, что работает от кода приложения. Вам стыдно за то, что вы не настроили правильные отношения PK/FK.
Ответ 5
попробуйте следующее:
@ManyToMany(cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.LAZY, mappedBy = "yourObject")