使用 flush: true 与手动刷新 currentSession 有什么不同?

7

我有一个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 }
    }
}

在集成测试中,我想知道为什么:
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

运作正常,但是

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列表中。这里实际发生了什么?


只是一个想法,如果你把 get() 改成 findById() 会发生什么? - user800014
快速想法:passwordHistory 的默认初始化器(... = [])可能会让事情变得混乱。尝试将其删除。当然,您还需要更改向列表添加元素的方式。 - Andrew
2
还有一个想法:如果您使用addToPasswordHistory(...)而不是直接附加,行为是否会改变(尽管我立即不确定原始集合是否支持addTo...) - Andrew
@SérgioMichels 我今晚会检查。你知道为什么会有所不同吗? - Joe
@Joe 我曾考虑过获取缓存,但安德鲁的评论似乎更准确地解决了你的问题。 - user800014
显示剩余2条评论
2个回答

1
如果我正确解释了Grails代码,你实际上正在处理两个不同的会话。从文档中可以看到,集成测试默认在数据库事务中运行,该事务在每次测试结束时都将回滚。这意味着在测试期间保存的数据不会持久保存到数据库中。如果您深入研究Grails GORM方法逻辑,您会发现当您处于事务内部时,GORM会从TransactionSynchronizationManager类维护的ThreadLocal资源映射中获取其会话。如果找不到,则会打开一个新的会话并将其绑定到地图上-重要的区别是,它明确地打开一个新的会话。它不只是调用sessionFactory.getCurrentSession()。在save()GORM逻辑的末尾,如果您传递flush:true,它将刷新与事务相关联的会话-从TransactionSynchronizationManager中获取的会话。
另一方面,当您调用flush()时,您是在从sessionFactory.getCurrentSession()获取的会话上调用它,我相信这是一个与Hibernate SessionFactory使用的CurrentSessionContext中的线程绑定的会话。实际的CurrentSessionContext实现并不重要,因为(除非我漏掉了Grails特定的实现),它不会返回由TransactionSynchronizationManager持有的同一会话。

0

在第一种情况下,您刷新了会话,因此对象变得“脏”。 当您使用newSession闭包打开新会话时,可以修改对象,但是如果要使修改“脏”,则必须在新会话中显式调用save()方法。 如果这样做,在闭包关闭后,您可以看到在闭包中对对象所做的修改。

在第二种情况下,在withNewSession之前对象不是脏的,因此不需要进行非显式调用以修改对象,这就是为什么在第二种情况下您看到列表为空的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接