Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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....?
  }
}
like image 315
Dónal Avatar asked Feb 16 '14 00:02

Dónal


1 Answers

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.

like image 103
jrob Avatar answered Nov 13 '22 02:11

jrob