如何刷新:真的不同于手动刷新currentSession?

Joe*_*Joe 7 grails hibernate grails-orm

我有一个GORM对象,我正在进行集成测试.它有一个beforeUpdate钩子,可以保存以前密码哈希的历史记录.代码看起来像这样:

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 }
    }
}
Run Code Online (Sandbox Code Playgroud)

在集成测试中,我想知道为什么:

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
Run Code Online (Sandbox Code Playgroud)

有效,但是

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
Run Code Online (Sandbox Code Playgroud)

才不是.不同之处flush: true在于appUser.save()通话中.我认为调用save()将对象附加到当前会话,但刷新当前会话不会将密码添加到passwordHistory列表中.这里到底发生了什么?

pro*_*lux 1

如果我正确解释 Grails 代码,那么您实际上正在处理两个不同的会话。从文档中:

默认情况下,集成测试在数据库事务内运行,该事务在每次测试结束时回滚。这意味着测试期间保存的数据不会持久保存到数据库中。

如果您深入研究 Grails GORM 方法逻辑,您会发现当您处于事务中时,GORM 从类ThreadLocal维护的资源映射中获取其会话TransactionSynchronizationManager。如果找不到,它会打开一个新会话并将其绑定到地图 - 重要的区别是,它显式打开一个新会话。它不只是调用sessionFactory.getCurrentSession()

在 GORM 逻辑的末尾save(),如果您传入flush:true它将刷新与事务关联的会话 - 它从 中的资源映射中获取的会话TransactionSynchronizationManager

另一方面,当您调用时,flush()您是在您获得的会话上调用它,我相信该会话是从Hibernate 使用的sessionFactory.getCurrentSession()绑定到您的线程的会话。的实际实现不是重点,因为(除非我缺少特定于 Grails 的实现)它不会返回.CurrentSessionContextSessionFactoryCurrentSessionContextTransactionSynchronizationManager