Search code examples
hibernategrailstransactionsgrails-orm

Handling Grails transactions programmatically


When I need to save a list of objects, and each object should be saved in it's own transaction (so that if one fails they don't all fail), I do it like this:

List<Book> books = createSomeBooks()
books.each { book ->
  Book.withNewSession {
    Book.withTransaction {TransactionStatus status ->
      try {
        book.save(failOnError: true)
      } catch (ex) {
        status.setRollbackOnly()
      }
    }
  }
} 

I use Book.withNewSession because if one book fails to save and the transaction is rolled back, the session will be invalid which will prevent subsequent books from saving. However, there are a couple of problems with this approach:

  1. It's a bit verbose
  2. A new session will always be created for each book, even if the previous book succeeded

Is there a better way? One possibility that occurred to me is to dependency-inject the Hibernate SessionFactory and do this instead

List<Book> books = createSomeBooks()
books.each { book ->
  try {
    Book.withTransaction {
      book.save(failOnError: true)
    }
  } catch (ex) {
    // use the sessionFactory to create a new session, but how....?
  }
}

Solution

  • This should do it:

    List<Book> books = createSomeBooks()
    books.each { book ->
      Book.withNewTransaction {TransactionStatus status ->
        try {
          book.save(failOnError: true)
        } catch (ex) {
          status.setRollbackOnly()
        }
      }
    } 
    

    The session isn't invalid if you rollback, it is just cleared. So any attempts to access entities read from the DB would fail, but writes of not-yet-persisted entities will be just fine. But, you do need to use separate transactions to keep one failure from rolling back everything, hence the withNewTransaction.