Hibernate flush:true与手动刷新当前会话有何不同?
我有一个GORM对象,我正在进行集成测试。它有一个Hibernate flush:true与手动刷新当前会话有何不同?,hibernate,grails,gorm,Hibernate,Grails,Gorm,我有一个GORM对象,我正在进行集成测试。它有一个beforeUpdate钩子,用于保存以前密码哈希的历史记录。代码如下所示: class Credentials { List passwordHistory = [] String username String password static hasMany = [passwordHistory : String] def beforeUpdate() { // of course
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 }
}
}
有效,但是
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
没有。区别在于
appUser.save()
调用中的flush:true
。我认为调用save()
会将对象附加到当前会话,但刷新当前会话不会将密码添加到passwordHistory
列表中。这里到底发生了什么?如果我正确解释了Grails代码,那么实际上您正在处理两个不同的会话。从文件中:
默认情况下,集成测试在数据库事务内部运行,这
在每个测试结束时回滚。这意味着数据已保存
在测试期间,数据不会持久化到数据库
如果深入研究Grails GORM方法逻辑,您将看到当您在事务中时,GORM会从ThreadLocal
资源映射中获取其会话,该映射由TransactionSynchronizationManager
类维护。如果找不到,它将打开一个新会话,并将其绑定到map(重要的区别)上,它将显式打开一个新会话。它不仅仅调用sessionFactory.getCurrentSession()
在save()
GORM逻辑的末尾,如果您传入flush:true
它将刷新与事务相关联的会话-它从TransactionSynchronizationManager中的资源映射中获得的会话
另一方面,当您调用flush()
时,您是在从sessionFactory.getCurrentSession()
获得的会话上调用它,我相信这是一个从HibernatesessionFactory
使用的CurrentSessionContext
绑定到线程的会话。CurrentSessionContext
的实际实现并不重要,因为(除非我缺少一个特定于Grails的实现),在第一次刷新会话时,它不会返回由TransactionSynchronizationManager
持有的相同会话,因此对象变为“脏”。
使用newSession闭包打开新会话时,可以修改对象,但如果要使修改变为“脏”,则必须在新会话中显式调用save()方法。
如果这样做,在关闭闭包后,可以看到在闭包中对对象所做的修改
在withNewSession之前的第二种情况下,对象不是脏的,因此修改对象需要非显式调用,这就是为什么在第二种情况下列表为空的原因。只是想一想,如果将get()
更改为findById()
,会发生什么?快速思考:默认初始值设定项(…=[]
)对于密码历史记录
可能会让人困惑。尝试删除此。当然,您还需要更改附加到列表中的方式。还有一个问题:如果使用addToPasswordHistory(…)
而不是直接附加,行为是否会改变(尽管我不能立即确定原始集合是否支持addTo
)@AndrewaddToPasswordHistory(…)
似乎为我初始化了此列表,但我没有将其初始化为[]
,但我注意到会话刷新的相同行为。换句话说,我尝试了你提到的两种情况,但似乎都没有效果。@SérgioMichels我今晚会检查。你知道为什么会有不同吗?
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