2010-12-01 19 views
3

Je voudrais mapper une arborescence de "chapitres". Chaque chapitre a une référence à son parent, et une liste ordonnée (par "position") de sous-chapitres. Avec JPA 2.0, Hibernate 3.5, l'entité "chapitre" se présente comme suit:Pourquoi hibernate/jpa définit-il le champ @OrderColumn sur null pour un élément supprimé dans une liste mappée avant de le supprimer?

@Entity 
public class Chapter { 

    @Id 
    @GeneratedValue 
    private long id; 

    @Column(name="position", nullable=false) 
    private int position; 

    @ManyToOne 
    @JoinColumn(name="parentChapter_id", updatable=false, insertable=false) 
    private Chapter parentChapter; 

    @OneToMany(cascade=CascadeType.ALL, orphanRemoval=true) 
    @OrderColumn(name="position") 
    @JoinColumn(name="parentChapter_id") 
    private List<Chapter> subChapters = new ArrayList<Chapter>(); 

    public List<Chapter> getSubChapters() { 
      return subChapters; 
    } 
} 

Le problème est, si l'un des éléments de subChapters est enlevé

// EntityManager em 
Chapter parent = em.find(Chapter.class, 1); 
subChapters = parent.getSubChapters(); 
subChapters.remove(1); 
EntityTransaction tx = em.getTransaction(); 
tx.begin(); 
em.persist(parent); 
tx.commit(); 

Hibernate tente d'exécuter cette déclaration

update 
    Chapter 
set 
    parentChapter_id=null, 
    position=null 
where 
    parentChapter_id=? 
    and id=? 

qui échoue à cause de la contrainte NOT NULL de position. Si @OrderColumn(name="position") est supprimé, Hibernate ne met pas à jour position (et par conséquent cela fonctionne) et supprime le (sous) Chapter par la suite. Quelles sont les causes Hibernate à d'abord mettre à jour le futur orphelin et puis l'enlever?

Répondre

2

Le problème est conceptuel, spécialement à propos de la propriété position :-) C'est une propriété de la liste elle-même, pas pour l'élément list (Chapitre). Donc, je le ferais différemment:

@Entity 
public class Chapter { 
    @Id @GeneratedValue 
    private long id; 

    @ManyToOne 
    private Chapter parent; 

    @OneToMany(mappedBy="parent") 
    @OrderColumn(name="position") 
    private List<Chapter> children; 
} 

Notez que vous avez deux structures ici: un chapitre et une liste. Le point est que l'état de la liste doit également être persistant, mais il n'est pas lié au struct chapitre. Donc, il mérite une table pour lui-même (et c'est ce que Hibernate essaiera de faire si vous activez le "generate ddl").

Vous pouvez voir cet exemple de leur suite de tests: https://github.com/hibernate/hibernate-core/tree/master/hibernate-core/src/test/java/org/hibernate/test/collection/list/

Vous verrez qu'ils n'utilisent pas annotations, mais vous pouvez facilement traduire :-) Merci

+0

partenon, mais comment accéder à la position d'un chapitre alors? – Zeemee

+0

Eh bien, la position est un détail d'implémentation pour la structure List. Si vous avez besoin de manipuler cette propriété hors de la portée de la liste, je dirais que vous avez besoin d'une autre structure et que vous la mappez en utilisant une méthode non standard. – jpkrohling