2016-11-01 2 views
-1

Ich bin auf der Suche nach einem eloquenten Weg, mehrere Entitäten innerhalb einer Transaktion zu löschen.Delete über HQL Bulk-Operation mit Rollback

Angesichts einer Liste von IDs möchte ich eine Ausnahme auslösen, wenn die Anzahl der betroffenen Zeilen von der Liste Anzahl unterscheidet. Zur Zeit benutze ich das unten Schnipsel, aber es viel vorformulierten beinhaltet:

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

Einige gotchas:

  • ein Vermächtnis app fehlt Abhängigkeit Nettigkeiten Dies wird Injektion, Kommentierung Transaktionen und andere. Antworten wie "Verwenden Sie den Frühling" sind nicht besonders hilfreich.
  • Wir kompilieren zu Java 1.6.
+0

, was ist das Problem mit diesem Code? – developer

+0

Es gibt eine Tonne Boilerplate. Es funktioniert, es ist nur hässlich – Andreas

+0

Anstelle von MyEntity-Objekt erstellen Sie ein DTO mit nur ID-Feld –

Antwort

1

Ich nahm einen Stich darauf. In diesem speziellen Fall müssen Sie Ihre Transaktion nicht in der try-Anweisung starten, denn wenn Sie sie nicht starten können, sind Sie wahrscheinlich nicht in der Lage, sie zurückzurollen, aber selbst wenn Sie könnten, würde es keinen Sinn haben, weil Sie nicht Hab noch nichts damit gemacht. Wenn die Transaktion nicht geöffnet werden konnte, musste nichts geschlossen werden. Mit anderen Worten, es würde keinen verwaisten Thread aus dem Verbindungspool geben.

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

Leider wird es schwierig sein, es "nett" zu machen, ohne ein eigenes benutzerdefiniertes Framework zu schreiben, um so etwas wie annotationsbasierte Transaktionen zu tun. Wenn Sie Zugriff auf eine AOP-Bibliothek haben, können Sie diese verwenden, um eine Menge davon zu verbergen, basierend auf Ihrer Beschreibung, die zweifelhaft erscheint.

+0

Mit dieser Lösung müsste meine Methode signiture sein 'public int deleteMyEntities (endgültige Liste ids) löst Exception '. 'throws Exception' ist unangenehm, um es gelinde auszudrücken. – Andreas

+0

Sie könnten "e" in eine RuntimeException einfügen, wie zB eine neue RuntimeException (e), die die Änderung der Methodensignatur beseitigt, aber nicht die eleganteste Lösung ist. –

0

Die Lösung, mit der ich am Ende war, beinhaltete Alex 'Vorschläge. Ich habe auch eine Menge Logik herausgezogen, um den Code ein wenig DRYer zu halten. Hinweis: die Hibernate-Session in einem Filter und hielt für die Dauer der Anforderung (offene Sitzung in der Ansicht) geöffnet, damit die Sitzung zurückgesetzt in 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."); 
    }