Reputation: 7498
Given the following two domain classes:
class Book {
String title
static hasMany = [authors: Author]
static belongsTo = Author
static constraints = {
title(nullable: false)
}
}
class Author {
static hasMany = [books: Books]
}
We create and persist domain objects in services and make use of the data binding feature of Grails. Such a method looks like the following one:
def createAndPersistBook(params) throws ValidationException {
log.debug("Attempt to create and persist book")
Book book = new Book(params)
book.save(flush: true, failOnError: true)
log.debug("Created: ${book}")
book
}
When we pass the params
map
params = ["authors": "[2]"]
to the service method (there is no title defined thus validation will fail) the association from the newly created book to the already existing author (and vice-versa) is done by data binding. But since the title is nullable: false
and not defined a ValidationException
is thrown and the transaction is rolled back.
What we expected now is that the book is not being saved, but Book.list().isEmpty()
returns false. We think that this is because of the dirty-check by hibernate, meaning the books
collection of the existing author
has changed and will be persisted and this save gets cascaded to the book instance.
What is the best way to prevent grails from saving the book in this scenario? Or why is the association done by data binding not properly rolled back when validation fails?
Upvotes: 1
Views: 472
Reputation: 116
If you've specified that your service is transactional, any uncaught exception will cause a transaction to rollback within a service method. The only thing that might stand in your way is if your RDBMS does not support true transactions/rollback.
Have you specified whether the service is transactional? You should have a statement such as below to declare the service is transactional.
def transactional = true
Upvotes: 0