views:

143

answers:

2

I want to delete orphan entities using hibernate and JPA on a many-to-many relationship but all that I found was this atibute the attribute. org.hibernate.annotations.CascadeType.DELETE_ORPHAN ( i.e. @Cascade(value={org.hibernate.annotations.CascadeType.DELETE_ORPHAN) ), which works only for one-to-many relationships.

I want to know if I can delete the orphan ones on my many-to-many relationship.

I´d be happy if anyone could help me...

Thanks in advance !

A: 

Don't use hibernate for that, write SQl code and run it directly on the database in a set-based manner. And then fix the database structural problem that allowd you to get orphaned records in the first place. It's a sign of a very poorly designed database if you even have orphaned records. This shouod be a onetime fix not something that runs from the application code. Shame on you for not setting up proper PK/FK relationships to begin with.

HLGEM
You totally missed the point (as you already did when you downvoted my answer about optimistic locking).
Pascal Thivent
@Pascal Thivent - you missed the point. This is not something that should be done EVER from the application interface. This is a database problem and should be handled by a correctly designed database. And if I down voted one of your answers (which I honestly don't know if I did, it doesn't strike a bell), then mentioning that in your comment when it was irrelevant seems to me that you are on a vendetta more than trying to do the right thing.
HLGEM
My comment is about the imaginary "structural problem" you are referring too (you missed the point, I insist, the OP has proper PK/FK, you are just not thinking at the object level). Then, believe it or not, I didn't downvote your above answer (feel free to contact a diamond moderators) even if I think this answer is irrelevant (like the downvote on [my answer](http://stackoverflow.com/questions/2949039/concurency-problem-with-isolation-read-committed/2949534#2949534)). So no, I'm not on a vendetta (but feedback would have been appreciated).
Pascal Thivent
+1  A: 

Actually, I did a test with the following entities:

@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>();

    //...
}

And with the following dataset:

<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>

The following test method:

@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();
}

Just passes. Below the produced output:

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_
Pascal Thivent
Yes ! I´ve tested this too. It really deletes all the depedent roles (in this case) but I can´t do that. I need to delete only the orphan ones...Thanks for you help !
Alucard
@user368453: Indeed, it's like Hibernate cascaded the DELETE (and I understand now that this is not what you want). I wonder if Hibernate can do better.
Pascal Thivent
Yes ! I wish it could... but It seems I´ll have to create a trigger on the database for doing that =/
Alucard