Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is flush: true different from manually flushing the currentSession?

I have a GORM object that I'm working with in an integration test. It has a beforeUpdate hook that keeps a history of the previous password hashes. The code looks something like this:

class Credentials {
    List passwordHistory = []
    String username
    String password

    static hasMany = [passwordHistory : String]

    def beforeUpdate() {
        // of course I'm not really storing plain-text passwords, but
        // this is just for illustration.
        if (isDirty('password')) { passwordHistory << password }
    }
}

In the integration test, I'm wondering why:

appUser.credentials.password = newPassword
appUser.save(flush: true)
sessionFactory.currentSession.flush()
AppUser.withNewSession {
    appUser = AppUser.get(appUser.id)
    appUser.credentials.empty // eagerly fetch the list while session is open
}
assert !appUser.credentials.passwordHistory.empty() // contains the previous password

works, but

appUser.credentials.password = newPassword
appUser.save()
sessionFactory.currentSession.flush()
AppUser.withNewSession {
    appUser = AppUser.get(appUser.id)
    appUser.credentials.empty // eagerly fetch the list while session is open
}
assert !appUser.credentials.passwordHistory.empty() // is empty

does not. The difference is the flush: true in the appUser.save() call. I thought the call to save() attached the object to the current session, but flushing the current session does not add the password to the passwordHistory list. What's really going on here?

like image 323
Joe Avatar asked Dec 05 '13 21:12

Joe


People also ask

What does flush method do in hibernate?

Flushing the session forces Hibernate to synchronize the in-memory state of the Session with the database (i.e. to write changes to the database). By default, Hibernate will flush changes automatically for you: before some query executions. when a transaction is committed.

What is flush and clear in hibernate?

Manual, the programmer is informing hibernate that he/she will handle when to pass the data to the database. Under this configuration the session. flush() call will save the object instances to the database. A session. clear() call acutally can be used to clear the persistance context.

What is flush in Grails?

flush (optional) - When set to true flushes the persistence context, persisting the object immediately and updating the version column for optimistic locking.

What is Session commit in hibernate?

As said in org. hibernate. Session docs, Must be called at the end of a unit of work, before commiting the transaction and closing the session (depending on flush-mode, Transaction. commit() calls this method).


1 Answers

If I'm interpreting the Grails code correctly, you're actually dealing with two different sessions. From the documentation:

Integration tests run inside a database transaction by default, which is rolled back at the end of the each test. This means that data saved during a test is not persisted to the database.

If you dig through the Grails GORM method logic, you'll see that when you're within a transaction, GORM grabs its session from a ThreadLocal resources map that's maintained by the TransactionSynchronizationManager class. If it doesn't find one, it opens a new session and binds it to the map - important distinction, it explicitly opens a new session. It doesn't just call sessionFactory.getCurrentSession().

At the end of the save() GORM logic, if you pass in flush:true it will flush the session associated with the transaction - the one it got from the resources map in TransactionSynchronizationManager.

On the other hand when you call flush() you're calling it on the session that you get from sessionFactory.getCurrentSession() which I believe is a session bound to your thread from the CurrentSessionContext used by the Hibernate SessionFactory. The actual implementation of CurrentSessionContext is beside the point because (unless there's a Grails-specific implementation that I'm missing) it wouldn't return the same session that's held by the TransactionSynchronizationManager.

like image 81
proflux Avatar answered Sep 25 '22 00:09

proflux