Search code examples
javahibernatehqljava-6

Delete via HQL bulk operation with rollback


I'm looking for an eloquent way to delete multiple entities inside a transaction.

Given a list of ids, I would like to throw an exception if the count of affected rows differs from the list count. Currently I use the below snippet, but it involves a lot of boilerplate:

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

Some gotchas:

  • This is a legacy app lacking dependency injection, annotation driven transactions and other niceties. Answers akin to "Use spring" aren't exceptionally helpful.
  • We compile to java 1.6.

Solution

  • I took a stab at it. In this specific case you don't need to start your transaction in the try statement, because if you're unable to start it then you are likely unable to roll it back, but even if you could there would be no point because you haven't done anything with it yet. If the transaction couldn't open, there would be nothing to close. In other words, there would not be an orphaned thread from the connection pool.

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

    Unfortunately, it will be difficult to make it "nice" without writing your own custom framework to do something like annotation based transactions. If you have access to an AOP library you could use that to hide a lot of this though based on your description that seems doubtful.