2011-05-23 4 views
0

Pour expliquer mon problème, j'ai créé ce projet simple avec deux tables: item et order1 (avec l'item ayant orderId comme Foreign Key).Hibernate JPA cascade delete issue

Maintenant, quand j'essaye de supprimer une rangée de la table de commande, j'obtiens l'erreur disant que l'identifiant particulier de commande est lié avec quelques articles dans la table d'article. Donc, ce que je fais est d'itérer sur tous les objets item (en utilisant le chargement 'Eager'), et d'appeler destroy() pour chacun d'entre eux. Et après avoir supprimé tous les éléments, j'appelle order.destroy(). Mais maintenant, je reçois l'erreur suivante:

Exception in thread "main" javax.persistence.RollbackException: Error while commiting the transaction 
     at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71) 
     at  com.sourind.test.testorm.controller.ItemJpaController.destroy(ItemJpaController.java:112) 
     at com.sourind.test.testorm.App.main(App.java:31) 
Caused by: org.hibernate.ObjectDeletedException: deleted entity passed to persist:  [com.sourind.test.testorm.entity.Item#<null>] 

J'utilise Hibernate (JPA 2.0). Toute aide dans ce domaine serait grandement appréciée.

Pour référence, je copie les classes d'entités créées par NetBeans:

@Entity 
@Table(name = "item", catalog = "test", schema = "") 
@NamedQueries({ 
@NamedQuery(name = "Item.findAll", query = "SELECT i FROM Item i"), 
@NamedQuery(name = "Item.findById", query = "SELECT i FROM Item i WHERE i.id = :id"), 
@NamedQuery(name = "Item.findByName", query = "SELECT i FROM Item i WHERE i.name = :name")}) 
public class Item implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Basic(optional = false) 
    @Column(name = "id") 
    private Integer id; 
    @Basic(optional = false) 
    @Column(name = "name") 
    private String name; 
    @JoinColumn(name = "orderId", referencedColumnName = "id") 
    @ManyToOne(optional = false) 
    private Order1 order1; 

    public Item() { 
    } 

    public Item(Integer id) { 
     this.id = id; 
    } 

public Item(Integer id, String name) { 
    this.id = id; 
    this.name = name; 
} 

public Integer getId() { 
    return id; 
} 

public void setId(Integer id) { 
    this.id = id; 
} 

public String getName() { 
    return name; 
} 

public void setName(String name) { 
    this.name = name; 
} 

public Order1 getOrder1() { 
    return order1; 
} 

public void setOrder1(Order1 order1) { 
    this.order1 = order1; 
} 

@Override 
public int hashCode() { 
    int hash = 0; 
    hash += (id != null ? id.hashCode() : 0); 
    return hash; 
} 

@Override 
public boolean equals(Object object) { 
    // TODO: Warning - this method won't work in the case the id fields are not set 
    if (!(object instanceof Item)) { 
     return false; 
    } 
    Item other = (Item) object; 
    if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { 
     return false; 
    } 
    return true; 
} 

@Override 
public String toString() { 
    return "com.sourind.test.testorm.entity.Item[id=" + id + "]"; 
} 

}

Et la classe ORDER1:

@Entity 
@Table(name = "order", catalog = "test", schema = "") 
@NamedQueries({ 
    @NamedQuery(name = "Order1.findAll", query = "SELECT o FROM Order1 o"), 
    @NamedQuery(name = "Order1.findById", query = "SELECT o FROM Order1 o WHERE o.id = :id"), 
    @NamedQuery(name = "Order1.findByOrderDate", query = "SELECT o FROM Order1 o WHERE o.orderDate = :orderDate")}) 
public class Order1 implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Basic(optional = false) 
    @Column(name = "id") 
    private Integer id; 
    @Basic(optional = false) 
    @Column(name = "orderDate") 
    @Temporal(TemporalType.TIMESTAMP) 
private Date orderDate; 
@OneToMany(cascade = CascadeType.ALL, mappedBy = "order1", fetch=FetchType.EAGER) 
private List<Item> itemList; 

public Order1() { 
} 

public Order1(Integer id) { 
    this.id = id; 
} 

public Order1(Integer id, Date orderDate) { 
    this.id = id; 
    this.orderDate = orderDate; 
} 

public Integer getId() { 
    return id; 
} 

public void setId(Integer id) { 
    this.id = id; 
} 

public Date getOrderDate() { 
    return orderDate; 
} 

public void setOrderDate(Date orderDate) { 
    this.orderDate = orderDate; 
} 

public List<Item> getItemList() { 
    return itemList; 
} 

public void setItemList(List<Item> itemList) { 
    this.itemList = itemList; 
} 

@Override 
public int hashCode() { 
    int hash = 0; 
    hash += (id != null ? id.hashCode() : 0); 
    return hash; 
} 

@Override 
public boolean equals(Object object) { 
    // TODO: Warning - this method won't work in the case the id fields are not set 
    if (!(object instanceof Order1)) { 
     return false; 
    } 
    Order1 other = (Order1) object; 
    if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { 
     return false; 
    } 
    return true; 
} 

@Override 
public String toString() { 
    return "com.sourind.test.testorm.entity.Order1[id=" + id + "]"; 
} 

}

Merci à l'avance, Souri

+0

Pouvez-vous s'il vous plaît poster le code de la méthode ItemJpaController # destroy()? –

Répondre

0

Vous devriez simplement pouvoir supprimer l'enregistrement de commande et lui faire supprimer tous les dossiers d'article associés. Pour ce faire, vous devez ajouter un autre type de cascade à l'association entre l'ordre et les éléments.

@OneToMany(cascade=CascadeType.ALL, mappedBy = "order1", fetch=FetchType.EAGER, orphanRemoval=true) 
private List<Item> itemList; 

Ceci indique à hibernate que lorsque vous supprimez l'ordre parent, il doit supprimer les enregistrements d'élément enfant. Cette suppression descendra en cascade dans les enregistrements d'élément enfant et les supprimera.

+0

Avoir un problème avec la même chose, essayer de supprimer une entité parente et avoir la cascade de suppression sur les entités possédées. Quoi qu'il en soit, j'ai inclus l'annotation @Cascade (org.hibernate.annotations.CascadeType.DELETE_ORPHAN) et j'obtiens un avertissement de NetBeans que le type de cascade DELETE_ORPHAN est obsolète. J'utilise Hibernate 3.6, et cela ne semble pas résoudre le problème de toute façon. Supprimer dans JPA est assez douloureux. J'ai beaucoup de cas où cela fonctionne comme prévu, et d'autres me baffle sans fin. – kpenrose

+0

Vraiment, vous devriez utiliser le orphanRemoval = true dans le OneToMany pour éviter les problèmes de dépréciation. J'ai édité ma réponse pour résoudre ce problème. –

0

Voici le @ Alex code pour ItemJpaController#destroy()

public void destroy(Integer id) throws NonexistentEntityException { 
    EntityManager em = null; 
    try { 
     em = getEntityManager(); 
     em.getTransaction().begin(); 
     Item item; 
     try { 
      item = em.getReference(Item.class, id); 
      item.getId(); 
     } catch (EntityNotFoundException enfe) { 
      throw new NonexistentEntityException("The item with id " + id + " no longer exists.", enfe); 
     } 
     Order1 order1 = item.getOrder1(); 
     if (order1 != null) { 
      order1.getItemList().remove(item); 
      order1 = em.merge(order1); 
     } 
     em.remove(item); 
     em.getTransaction().commit(); 
    } finally { 
     if (em != null) { 
      em.close(); 
     } 
    } 
} 

Et aussi, la version de mise en veille prolongée J'utilise ne pas DELETE_ORPHAN en cascade.

De plus, comment puis-je obtenir une session? et dois-je l'obtenir dans la classe JpaController à l'intérieur de la méthode destroy() ou dans la méthode de messagerie à partir de laquelle je crée/supprime des objets.

+0

Cela se lit comme il devrait être un edit à la question originale. Si oui, pouvez-vous modifier/signaler un modérateur pour le faire pour vous. – forsvarir

+0

Ahh je vois. Je suppose que cette annotation n'est disponible que si vous utilisez le noyau Hibernate.Vous pouvez obtenir la session à partir du gestionnaire d'entités comme suit: Session session = entityManager.unwrap (Session.class); –

+0

Pouvez-vous utiliser supprimer orphelin comme ceci: @OneToMany (deleteOrphan = true) Je pense que cela fonctionne pour JPA. Désolé de ne pas avoir remarqué que vous utilisiez EntityManager tout de suite. –