我在休眠和版本控制方面遇到问题。我正在使用 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 抛出异常,在这种情况下,将重试操作(可能,但高度不太可能,在一个循环中) - 基本上,这将引入保存用户的序列化,但基于乐观锁定。如前所述,这不起作用。
有人可以帮我解决这个问题吗?这是一个好主意吗?也许有更好的方法?为什么休眠代码段不起作用?