web-dev-qa-db-fra.com

JPA/Hibernate supprime parfois l'entité qui ne fonctionne pas

J'ai le code suivant qui fonctionne généralement bien:

public void delete(T object)
{
  EntityManager em = getPersistence().createEntityManager();
  EntityTransaction et = em.getTransaction();
  try
  {
    et.begin();
    object = em.find(object.getClass(), object.getId());
    em.remove(object);
    em.flush();
    et.commit();
  }
  catch(Exception e)
  {
    error("Unable to delete " + object.toString() + ": there are references to it.");
  }
  finally
  {
    if (et.isActive()) et.rollback();
    em.close();
  }
}

Pour beaucoup de mes classes d'entités, cela fonctionne. Cependant, pour deux d'entre elles, cela ne fait rien, cela ne génère aucune exception et ne supprime pas l'objet. Le journal de hibernate montre que hibernate exécute un certain nombre de requêtes de sélection, mais ne tente même pas d’exécuter une suppression.

J'ai déjà essayé des suggestions trouvées dans d'autres questions similaires ici et ici , mais en vain (eh bien, ce dernier suggère @Transactional que je ne peux pas utiliser, mais je viens de joindre les déclarations entre begin() et commit() à la place).

Je n'arrive pas à trouver ce que ces deux classes ont plus (ou moins) que les autres. Ils utilisent @PrimaryKeyJoinColumn comme presque toutes les autres entités que j'ai, ils ont @OneToMany et @ManyToOne comme ohters. Pour être honnête, ils ont un champ @OneToOne(optional = false) qui fait référence à une autre classe et que d’autres entités n’en ont pas, mais je ne voudrais pas changer cela (et donc changer le schéma de la base de données) à moins que vous ne me disiez qu'il pourrait raison pour cela.

Est-ce que @OneToOne est responsable? Ou est-ce que mon code de suppression est buggé?

30
Lucio Crusca

Avez-vous des associations dans ce graphique qui répercutent en cascade la suppression de la chose? Si tel est le cas, la spécification JPA indique clairement que le fournisseur doit annuler la suppression dans un tel cas. Si tel est le cas, Hibernate écrit une instruction de journal indiquant "suppression de la suppression d'entité de planification [...]". Vous pouvez le voir en activant la journalisation de suivi sur le consignateur org.hibernate.event.internal.DefaultPersistEventListener.

Si tel est le cas, vous devrez nettoyer ces associations comme requis par la spécification JPA.

74
Steve Ebersole

Remplacer le cascade = CascadeType.ALL par orphanRemoval = true dans l'association @OneToMany donne le résultat attendu: les enregistrements enfants sont correctement supprimés sans qu'il soit nécessaire de supprimer l'enregistrement parent.

Dommage que l'erreur ne soit pas enregistrée plus clairement. 

26
Arthur Nederlof

J'ai eu le même problème en suivant les choses rassemblées.

  1. Hibernate 3.6.10.Final
  2. Entité mère avec non référence à l'enfant.
  3. Une entité Child en référence au parent. C'est un code hérité.

    class ChildEntity {
      @ManyToOne(cascade = CascadeType.ALL)
      private ParentEntity parent;
    }
    
  4. L'entité enfant est chargée dans le contexte de session, puis supprimée de la table sans notification JPA dans la transaction (procédure stockée, SQL natif).

  5. Alors le parent ne peut pas être supprimé. Pas de suppression, pas d'exception, juste un message 'suppression de planification' dans le journal TRACE.

Solution: j'ai supprimé l'attribut en cascade. Il était un peu difficile pour moi de trouver quelle entité enfant bloque la suppression de parent. En outre, je ne comprends pas pourquoi la cascade de l'enfant affecte si je supprime l'entité Parent.

4
Andriy Slobodyanyk

J'ai eu le même problème avec une entité qui a de nombreuses relations. Ce que j'ai fait était de mettre à zéro les entités parent, de faire une fusion, puis de faire un flush et d'effacer et enfin, obtenir à nouveau l'entité en utilisant find et remove. Par exemple:

objAgreement.setParent(null);
em.merge(objAgreement);
em.flush();
em.clear();

objAgreement= em.find(Agreement.class, objAgreement.getId())
em.remove(objAgreement);
0
jmoran

En plus de tout cela, une fois que vous avez déjà corrigé Cascade et Orphan, dans mon cas, j’ai trouvé une autre raison pour laquelle le DELETE ne pouvait pas être créé même lorsque tout semble aller bien et même lorsque le message DELETE est dans le journal mais que la base de données n’affiche aucun changement .

Il pourrait également s'agir d'un problème transactionnel, mais dans mon cas, la clé (dans Oracle Database) de la requête de suppression était un type de données CHAR (8) dans la base de données, comme dans la documentation Oracle. CHAR ou VARCHAR2 si vos données ne sont pas d'une longueur, le reste est recouvert d'un blanc et un JPA DELETE fait la distinction entre "0001" et "0001".

J'écris cette réponse car je ne vois pas une telle chose dans toutes mes recherches et quelqu'un d'autre pourrait passer à travers ce post et avoir le même problème.

À votre santé.

0
Eduardo