【问题标题】:hibernate inconsistency: session returns old value休眠不一致:会话返回旧值
【发布时间】:2012-04-17 14:27:06
【问题描述】:

我在 JSF 项目后面使用 Hibernate。

在主页中,我有一个表示对象列表的数据表。

我使用 Hibernate 成功更新了对象的属性(在数据表中找到)。更新后,通过使用“xxx?faces-redirect=true”重定向来刷新页面。我重定向页面以避免“重复提交表单”类问题。

然后,如果我按 F5 几次,更新对象的旧属性值可能会返回到页面。

据我了解,这是一个休眠会话问题。因为如果我在使用后立即关闭每个会话,则不会发生此问题。但是,由于延迟获取策略,我无法在交易后关闭会话。

简而言之,休眠可能会带来对象的旧值,尽管它已成功更新。我怎样才能避免这样的问题?

ps:我怀疑 Hibernate 的缓存机制,我禁用了一级和二级缓存:

<property name="hibernate.cache.use_query_cache">false</property>
<property name="hibernate.use_second_level_cache">false</property>

但它也没有用..

更新:在@Johanna 回复后,我控制了会话实例的 id,并指出我的 HibernateUtil 类在大多数情况下返回不同的会话或打开一个新会话。下面是 getSession() 方法:

public static Session getSession(){
    Session se = HibernateUtil.session.get();
    if(se == null)
    {
        se = sessionFactory.openSession();
        HibernateUtil.session.set(se);
    }
    return se;
}

我假设一旦我有一个会话,我就会在该会话中进行所有内容,例如列表、更新等。因为我在 getSession() 方法中进行了必要的控制。我在哪里犯错了?

【问题讨论】:

    标签: hibernate jsf caching page-refresh


    【解决方案1】:

    use_query_cache 和 use_second_level_cache 都不能禁用一级缓存。一级缓存始终开启。

    如果你真的不想使用一级缓存,那么你必须使用StatelessSession而不是Session。但是 StatelessSession 的功能较少,可能缺少一些您需要的方法。

    如果只想从一级缓存中移除几个对象,可以使用Session.evict()。

    不过,我想知道为什么 Hibernate 会向您展示旧的价值。如果每个数据库实体在会话中只有一个实例(即您更新的对象的实例与列表中显示的实例相同),则不应发生这种情况。如果您使用两个不同的实例,那么 Hibernate 会显示旧值是正常的(如果您使用两个 Session 实例,一个用于列表,一个用于更新,那么您也会在列表中获得旧值)。因此,当您修复应用程序时,也许您不必逐出对象。

    更新后编辑: 在您的简短代码中,如果您作为单个用户使用该应用程序,我看不出为什么会出现错误。

    但一般来说:你使用的是 JSF,所以它可能是一个可供许多用户使用的 Web 项目。

    Hibernate Session 对象不是线程保存的,即。 e.如果不同的用户同时使用它,它可能不起作用。每个用户都需要自己的会话对象。因此,您可以将 Hibernate 会话实例存储在 Http 会话实例中(即使这样,有时您也必须使用“同步”方法(或对象),以防用户在第一个请求的答案到达之前第二次按下按钮)。

    第二次编辑:我认为您遇到的问题与this question 中的问题相同。

    您可能从任何地方复制了您的代码,例如引用问题的 DAO。我猜你的 HibernateUtil 类,你从任何地方复制的代码,将休眠会话存储在 ThreadLocal 对象中,即。 e.一个休眠会话绑定到一个线程。

    但是你正在做一个网络项目。在那里,您应该将一个 Hibernate 会话绑定到一个用户(或浏览器),即。 e.到一个 Http 会话。但是您不知道来自一个 http 会话的请求是在哪个线程中处理的。因此,在您的解决方案中,相同的 Http 会话可能会获得不同的 Hibernate 会话,或者不同的 Http 会话可能会获得相同的 Hibernate 会话。这取决于您的 Http 服务器。

    解决方案:将 Hibernate 会话放入 Http 会话中(并且 不 使用 ThreadLocal 对象)。使用HttpServletRequest.getSession() 获取Http session 对象,使用HttpSession.getAttribute()/HttpSession.setAttribute() 可以设置Hibernate session 和其他Http session 相关数据。

    【讨论】:

    • 我做了一个控制以避免两个不同的会话。因此,如果有一个打开的会话,我不会创建另一个,我会返回打开的会话。因此,不应存在两个不同的会话。在列出、更新等时使用现有会话是否有问题?
    • 它应该只适用于一个会话。我建议您在调试模式下启动您的应用程序,并检查您更新的实例和您获取旧值的实例是否是同一个实例(Eclipse 调试器中的地址或实例 ID 相等)
    • 我检查了你所说的 ids,我需要用我的观察来更新这个问题。等待您的帮助,谢谢..
    • 看完你的回复后,我可以说我现在以单用户身份进行测试。作为单个用户,为什么我会从 getSession() 方法获得不同的会话?那个方法有问题吗?
    • 这个对象HibernateUtil.session 也可能是问题所在。确保始终是同一个实例。调试(至少在你的 getSession() 中设置一个断点)可以帮助你。
    猜你喜欢
    • 2017-02-18
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    • 2018-09-27
    • 2013-09-14
    • 1970-01-01
    相关资源
    最近更新 更多