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

时间:2013-12-05 21:14:33

标签: hibernate grails gorm

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

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

2 个答案:

答案 0 :(得分:1)

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

  

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

如果您仔细研究Grails GORM方法逻辑,您将看到当您处于事务中时,GORM会从ThreadLocal类维护的TransactionSynchronizationManager资源映射中获取其会话。如果找不到,则会打开一个新会话并将其绑定到地图 - 重要的区别是,它会明确地打开一个新会话。它不只是致电sessionFactory.getCurrentSession()

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

另一方面,当您致电flush()时,您在sessionFactory.getCurrentSession()会话中调用它,我相信这是一个与您使用的CurrentSessionContext线程绑定的会话通过Hibernate SessionFactoryCurrentSessionContext的实际实现不是重点,因为(除非我缺少Grails特定的实现),它不会返回TransactionSynchronizationManager所持有的相同会话。

答案 1 :(得分:0)

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

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