4

我在 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() 方法中进行了必要的控制。我在哪里犯错?

4

1 回答 1

4

Neither use_query_cache nor use_second_level_cache can disable the first level cache. The first level cache always is on.

If you really do not want to use the first level cache, then you have to use StatelessSession instead of Session. But StatelessSession has less functionality and some of the methods you need might be missing.

If you only want to remove a few objects from the first level cache, you can use Session.evict().

Nevertheless I wonder why Hibernate presents the old value to you. This should not happen if each database entity only has one instance in the session (i. e. the instance of the object which you update is the same instance as shown in the list). If you use two different instances, then it is normal Hibernate presents the old value (and if you use two Session instances, one for the list and one for the update, then also you get the old value in the list). So perhaps you don't have to evict the object when you fix your application.

EDIT after your update: In your short codelet I can't see why you get errors if you as a single user use the application.

But generally: You use JSF, so it is probably a web project which can be used by many users.

The Hibernate Session object is not thread-save, i. e. it probably won't work if different users use it at the same time. Each user needs its own session object. So you can store the Hibernate session instance in the Http session instance (and even then sometimes you must use 'synchronized' methods (or objects) just for the case the user presses a second time on a button before the answer from the first request arrived).

2nd EDIT: I think you have the same problem as in this question.

Probably you copied your code from anywhere like the DAO of the cited question. I guess your HibernateUtil class, whose code you copied from anywhere, stores the hibernate session in a ThreadLocal object, i. e. one hibernate session is bound to one thread.

But you're doing a web project. There you should bind one Hibernate session to one user (or browser), i. e. to one Http session. But you do not know in which thread the request from one http session is processed. Thus in your solution the same Http session may get different Hibernate sessions or perhaps different Http sessions may get the same Hibernate session. This depends to your Http server.

Solution: Put the Hibernate session into the Http session (and do not use the ThreadLocal object). You get the Http session object with HttpServletRequest.getSession() and with HttpSession.getAttribute()/HttpSession.setAttribute() you can set the Hibernate session and other Http session related data.

于 2012-04-17T15:46:24.180 回答