2

我在休眠和版本控制方面遇到问题。我正在使用 Hibernate 3.6.7-Final。这是来自我的 DAO 类的代码片段(它由使用 @Transactional 注释的 Spring Beans 调用,因此它本身不是事务性的,但为了强制 StaleObjectStateException,使用了刷新):

@Override
public void save(User user) {
    boolean saved = false;
    while (!saved) {
        try {
            sessionFactory.getCurrentSession().saveOrUpdate(user);
            sessionFactory.getCurrentSession().flush();
            saved = true;
        } catch (StaleObjectStateException exc) {
            sessionFactory.getCurrentSession().refresh(user);
        }
    }
}

版本是一个长值映射,如下所示:

<version name="version" access="field" column="version" type="long" unsaved-value="null" />

问题是,用户永远不会被保存,代码只会永远循环。我调试了一下,结果发现hibernate有一个ActionQueue的概念,并且在内部,它在不同的集合中有插入、更新等。对于上面代码的每个循环,更新集合都会增长 1。它失败了,因为它总是尝试在该集合中的索引 0 处执行更新,这是“陈旧”的,并且在该点失败,并且没有尝试使用刷新的版本执行更新。有没有办法让这个工作?

也许一些关于我想要做什么的背景,所以也许更聪明的人将能够提出一个更好的解决方案:假设没有版本控制,并且用户有一个包含无效登录计数的列。每次失败,此计数都会增加一,并在成功登录时重置。(我们用它来设置用户在 x 次不成功尝试后何时可以登录的超时,以防止对帐户的暴力破解,但这在这里无关紧要。)现在,假设攻击者使用攻击,其中服务器上的多个线程可以访问同一个用户帐户,当 2 个线程读取失败计数为 2 的用户时,线程 1 将其更新为 3,保存,然后线程 2 将其更新为 3,并保存 -我们刚刚失去了一次不成功的尝试,这是一个错误。

所以,为了解决这个问题,我想引入版本控制列,在这种情况下,它不会导致上面的线程 2 抛出异常,在这种情况下,将重试操作(可能,但高度不太可能,在一个循环中) - 基本上,这将引入保存用户的序列化,但基于乐观锁定。如前所述,这不起作用。

有人可以帮我解决这个问题吗?这是一个好主意吗?也许有更好的方法?为什么休眠代码段不起作用?

4

1 回答 1

0

您可能需要一个新会话来执行更新。作为一般规则,您不应在引发异常后使用会话。

您可以阅读线程以获取更多信息。TL;DR:hibernate 抛出的异常是不可恢复的。您必须回滚,关闭会话并重新开始。

于 2013-01-07T00:54:58.163 回答