2016-11-01 8 views
-1

Я ищу красноречивый способ удаления нескольких объектов внутри транзакции.Удалить с помощью операции HQL с откатом

Учитывая список идентификаторов, я хотел бы создать исключение, если количество затронутых строк отличается от количества списков. В настоящее время я использую ниже фрагмент кода, но она включает в себя много шаблонных:

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

несколько подводных камней:

  • Это наследие приложение не хватают инъекций зависимостей, аннотаций привода сделок и других тонкостей. Ответы, подобные «Использовать весну», не являются исключительно полезными.
  • Скомпилируем java 1.6.
+0

чем проблема с этим кодом? – developer

+0

Существует тонна шаблона. Это работает, это просто уродливо – Andreas

+0

Вместо объекта MyEntity создайте DTO с только полем id –

ответ

1

Я принял удар. В этом конкретном случае вам не нужно начинать транзакцию в операторе try, потому что, если вы не можете его запустить, вы, скорее всего, не сможете его отбросить, но даже если бы вы не могли, Он ничего не сделал с этим. Если транзакция не может быть открыта, закрыть ее нечем будет. Другими словами, в пуле соединений не было бы осиротевшей нити.

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

К сожалению, будет сложно сделать его «приятным», не создавая собственные пользовательские рамки, чтобы делать что-то вроде транзакций на основе аннотаций. Если у вас есть доступ к библиотеке AOP, вы можете использовать это, чтобы скрыть много этого, хотя на основе вашего описания, которое кажется сомнительным.

+0

С помощью этого решения мой метод signiture должен быть «public int deleteMyEntities» (окончательный список ids) выбрасывает Exception'. 'throws Exception' неприятно, мягко говоря. – Andreas

+0

Вы можете обернуть «e» в RuntimeException, например, выбросить новое RuntimeException (e), которое устраняет изменение подписи метода, но не является самым изящным решением. –

0

Решение, в которое я попал, включал предложения Алекса. Я также вытащил много логики, чтобы сохранить код немного DRYer. Примечание: спящий режим сеанс открыт в фильтре и в течение срока действия запроса (открытое заседание в связи), таким образом, сброс сеанса в 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."); 
    }