Why is grails.gorm.autoFlush set to true not working?

654 Views Asked by At

Consider the following code:

class User {
    static constraints = {
        email email: true, unique: true
        token nullable: true
    }

    String email
    String password
    String token
}

@TestFor(User)
@TestMixin(DomainClassUnitTestMixin)
class UserSpec extends Specification {
    def "email should be unique"() {
        when: "twice same email"
        def user = new User(email: "[email protected]", password: "test")
        def user2 = new User(email: "[email protected]", password: "test")

        then: "saving should fail"
        user.save()
        !user2.save(failOnError: false)
    }
}

With the following configuration (part of it), application.yml:

grails:
    gorm:
        failOnError: true
        autoFlush: true

Why is user2.save(failOnError: false) not returning false due to it not being saved into the database?

Output of running: grails test-app *UserSpec:

businesssoftware.UserSpec > email should be unique FAILED org.spockframework.runtime.ConditionNotSatisfiedError at UserSpec.groovy:40

When I replace user.save() with user.save(flush: true) it does work.

However the documentation at https://grails.github.io/grails-doc/latest/guide/conf.html, Section 4.1.3 claims that:

grails.gorm.autoFlush - If set to true, causes the merge, save and delete methods to flush the session, replacing the need to explicitly flush using save(flush: true).

For reference this is the output of grails --version:

| Grails Version: 3.0.2
| Groovy Version: 2.4.3
| JVM Version: 1.8.0_40

And that's exactly what I am doing, what am I missing here?

1

There are 1 best solutions below

0
On

I think the autoFlush documentation is misleading.

grails.gorm.autoFlush - If set to true, causes the merge, save and delete methods to flush the session, replacing the need to explicitly flush using save(flush: true).

Take a look at the save() methods and doSave() of GormInstanceApi. Toward the end of doSave() you'll see the session is only flushed when the flush parameter is true:

if (params?.flush) {
    session.flush()
}

There's nothing in the code suggesting that the flush parameter can come from any other place than what's passed to save().

I could not find the code to prove it, but I think autoFlush comes into play when the Hibernate session is closed, such as when a controller or service method exists/returns.

Also, the GORM implementation used for Spock and JUnit is not the real GORM, so it may not even use the autoFlush configuration.

I'm going to go on a limb here. I haven't tried this, but you may be able to flush the session manually.

@TestFor(User)
@TestMixin(DomainClassUnitTestMixin)
class UserSpec extends Specification {
    def "email should be unique"() {
        when: "twice same email"
        def user = new User(email: "[email protected]", password: "test")
        def user2 = new User(email: "[email protected]", password: "test")
        simpleDatastore.currentSession.flush()

        then: "saving should fail"
        user.save()
        !user2.save(failOnError: false)
    }
}

simpleDataStore comes from DomainClassUnitTestMixin.