4
votes

In my environment, i have grails.gorm.failOnError = true on Config.groovy.

package org.example

class Book {

    String title
    String author
    String email

    static constraints = {
        title nullable: false, blank: false
        email nullable: false, blank: false, unique: true //apparently this is the problem..
    }
}

And, on controller, i have:

package org.example

class BookController {

def update() {

        def bookInstance = Book.get(params.id)
        if (!bookInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])
            redirect(action: "list")
            return
        }

        if (params.version) {
            def version = params.version.toLong()
            if (bookInstance.version > version) {
                bookInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
                          [message(code: 'book.label', default: 'Book')] as Object[],
                          "Another user has updated this Book while you were editing")
                render(view: "edit", model: [bookInstance: bookInstance])
                return
            }
        }

        bookInstance.properties = params

        bookInstance.validate()

        if(bookInstance.hasErrors()) {

            render(view: "edit", model: [bookInstance: bookInstance])

        } else {

            bookInstance.save(flush: true)
            flash.message = message(code: 'default.updated.message', args: [message(code: 'book.label', default: 'Book'), bookInstance.id])
            redirect(action: "show", id: bookInstance.id)
        }      
    }
}

To save, it's ok. But, when updating without set the title field, i get:

Message: Validation error whilst flushing entity [org.example.Book]:
- Field error in object 'org.example.Book' on field 'title': rejected value []; codes [org.example.Book.title.blank.error.org.example.Book.title,org.example.Book.title.blank.error.title,org.example.Book.title.blank.error.java.lang.String,org.example.Book.title.blank.error,book.title.blank.error.org.example.Book.title,book.title.blank.error.title,book.title.blank.error.java.lang.String,book.title.blank.error,org.example.Book.title.blank.org.example.Book.title,org.example.Book.title.blank.title,org.example.Book.title.blank.java.lang.String,org.example.Book.title.blank,book.title.blank.org.example.Book.title,book.title.blank.title,book.title.blank.java.lang.String,book.title.blank,blank.org.example.Book.title,blank.title,blank.java.lang.String,blank]; arguments [title,class org.example.Book]; default message [Property [{0}] of class [{1}] cannot be blank]

   Line | Method
->>  46 | onApplicationEvent in org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|   895 | runTask            in java.util.concurrent.ThreadPoolExecutor$Worker
|   918 | run . . . . . . .  in     ''
^   680 | run                in java.lang.Thread

At q I understand it, the problem occurs when the flush hibernate session, hibernate tries to save the object again then the exception is thrown...

When trying to save the object again, is called the book.validate () again, which makes a new query in the database to ensure the uniqueness of the email field. Right now, the Validation Exception is thrown.

But, when i removed the unique validation of email property, the update is performed normally..

My question is: This behavior is correct? Hibernate calls book.save automatically?

This is the sample project, and the steps to simulate the error are:

  • source: https://github.com/roalcantara/grails_app_validation_exception
  • grails run-app
  • navigate to http:// localhost: 8080/ book/book/create
  • create an new instance filling all fields..
  • then edit this instance, in: http:// localhost: 8080/ book/book/edit/1
  • finally, drop the 'Title' field and click on Update, then the exception is thrown..

In my environment, this behavior has occurred on grails version 2.0.3 and 2.2.1

Thanks for any help! And sorry by my poor (and shame) english.. rs..

1

1 Answers

6
votes

You are essentially validating twice, first with:

bookInstance.validate()

and second with:

bookInstance.save(flush: true)

When you call bookInstance.save(flush: true) a boolean is returned. Grails takes advantage of this by default when a controller is generated, but it appears you have changed the controller Grails generated by default for some reason.

Just replace this:

    bookInstance.validate()

    if(bookInstance.hasErrors()) {

        render(view: "edit", model: [bookInstance: bookInstance])

    } else {

        bookInstance.save(flush: true)
        flash.message = message(code: 'default.updated.message', args: [message(code: 'book.label', default: 'Book'), bookInstance.id])
        redirect(action: "show", id: bookInstance.id)
    }  

With this:

    if( !bookInstance.save( flush: true ) ) {
        render(view: "edit", model: [bookInstance: bookInstance])
        return
    }