2016-11-01 2 views
-1

Je suis à la recherche d'une méthode éloquente pour supprimer plusieurs entités dans une transaction.Suppression via une opération en bloc HQL avec annulation

Étant donné une liste d'identifiants, j'aimerais lancer une exception si le nombre de lignes affectées diffère du nombre de listes. Actuellement, j'utilise l'extrait ci-dessous, mais il faut beaucoup de passe-partout:

private int deleteMyEntities(final List<Integer> ids) { 
    final Session session = SomeHelper.getOpenSession(); 
    Transaction tx = null; 
    try { 
     tx = session.beginTransaction(); 
     final int affectedCount = session.createQuery("delete MyEntity where id in (:ids)") 
      .setParameterList("ids", ids) 
      .executeUpdate(); 
     if (affectedCount == ids.size()) { 
     tx.commit(); 
     tx = null; 
     return affectedCount; 
     } else { 
     throw new HibernateException("Delete count does not match target count."); 
     } 
    } finally { 
     if (tx != null) { 
     tx.rollback(); 
     } 
    } 
    } 

Quelques pièges:

  • Ceci est une application héritée manque injection de dépendance, les transactions conduit d'annotation et d'autres raffinements. Les réponses s'apparentant à «Utiliser le ressort» ne sont pas exceptionnellement utiles.
  • Nous compilons en Java 1.6.
+0

quel est le problème avec ce code? – developer

+0

Il y a une tonne de passe-partout. Cela fonctionne, c'est juste moche – Andreas

+0

Au lieu de l'objet MyEntity créer un DTO avec seulement le champ d'identification –

Répondre

1

J'ai pris un coup de couteau. Dans ce cas précis, vous n'avez pas besoin de démarrer votre transaction dans l'instruction try, car si vous ne parvenez pas à la démarrer, vous ne pourrez probablement pas la redémarrer, mais même si vous le pouvez, cela ne sert à rien car vous n'avez pas de compte. Je n'ai rien fait pour le moment. Si la transaction ne pouvait pas ouvrir, il n'y aurait rien à fermer. En d'autres termes, il n'y aurait pas de thread orphelin à partir du pool de connexions. Malheureusement, il sera difficile de le rendre "agréable" sans écrire votre propre cadre personnalisé pour faire quelque chose comme des transactions basées sur des annotations. Si vous avez accès à une bibliothèque AOP, vous pouvez l'utiliser pour masquer une grande partie de cela, en fonction de votre description qui semble douteuse.

+0

Avec cette solution ma méthode de construction devrait être 'public int deleteMyEntities (liste finale ids) throws Exception'. 'throws Exception' est désagréable, c'est le moins qu'on puisse dire. – Andreas

+0

Vous pouvez inclure 'e' dans une exception RuntimeException comme lancer une nouvelle RuntimeException (e) qui élimine la modification de la signature de méthode, mais n'est pas la solution la plus élégante. –

0

La solution avec laquelle j'ai fini par aller comprenait les suggestions d'Alex. J'ai aussi sorti beaucoup de logique pour garder le code un peu plus sec. Remarque: la session de mise en veille prolongée est ouvert dans un filtre et maintenu pendant toute la durée de la demande (session ouverte en vue), ainsi la remise à zéro de la session dans recoverFromFailedTransaction

public int deleteMyEntity(final List<Integer> ids) { 
    return deleteEntities("MyEntity", ids); 
    } 

    private int deleteEntities(final String entityName, final List<Integer> ids) { 
    final Session session = SomeHelper.getOpenSession(); 
    final Query query = session.createQuery("delete " + entityName + " where id in (:ids)") 
     .setParameterList("ids", ids); 
    return performBatchOperation(query, ids.size()); 
    } 

    private int performBatchOperation(final Query query, final int expectedAffectedCount) { 
    final Session session = SomeHelper.getOpenSession(); 
    final Transaction tx = session.beginTransaction(); 
    try { 
     final int affectedCount = query.executeUpdate(); 
     if (affectedCount == expectedAffectedCount) { 
     tx.commit(); 
     return affectedCount; 
     } else { 
     throw new HibernateException(String.format(
      "Affected count [%d] does not match expected count [%d].", 
      affectedCount, 
      expectedAffectedCount)); 
     } 
    } catch (RuntimeException e) { 
     logger.error(e); 
     recoverFromFailedTransaction(tx); 
     throw e; 
    } 
    } 

private void recoverFromFailedTransaction(final Transaction tx) { 
    try { 
     if (tx != null) { 
     tx.rollback(); 
     } 
    } catch (HibernateException e) { 
     logger.error("Exception when rolling back failed transaction. ", e); 
    } 
    try { 
     SomeHelper.getOpenSession().close(); 
    } catch (HibernateException e) { 
     logger.error("Exception when closing session . ", e); 
    } 
    SomeHelper.resetOpenSession(); 
    logger.warn("Session discarded."); 
    }