使用回滚通过HQL批量操作删除

时间:2016-11-01 21:35:10

标签: java hibernate hql java-6

我正在寻找一种有效的方法来删除事务中的多个实体。

给定一个id列表,如果受影响的行数与列表数不同,我想抛出异常。目前我使用下面的代码片段,但它涉及很多样板:

  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。

2 个答案:

答案 0 :(得分: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库,你可以使用它来隐藏很多这个,虽然你的描述看起来很可疑。

答案 1 :(得分:0)

我最终采用的解决方案包括Alex的建议。我还提出了很多逻辑来保持代码有点干。注意:hibernate会话在过滤器中打开并在请求期间保持(在视图中打开会话),因此会话重置在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.");
  }