我正在使用一个JSF项目的Hibernate。
在主页面中,我有一个表示对象列表的数据表。
我使用Hibernate成功更新了对象的属性(在数据表中找到)。更新后,通过使用“xxx?faces-redirect = true”重定向来刷新页面。我重定向页面,以避免“重复表单提交”类问题。
然后,如果我多次点击F5,更新对象的旧属性值可能会返回到页面。
据我所知,这是一个Hibernate会话问题。因为如果我在使用它们后立即关闭每个会话,则不会发生此问题。但是,由于延迟抓取策略,我无法在事务后关闭会话。
简而言之,虽然hibernate已成功更新,但它可能会带来旧对象的价值。我怎样才能避免这样的问题?
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()方法中进行了必要的控制。我在哪里犯了错误?
答案 0 :(得分:4)
use_query_cache和use_second_level_cache都不能禁用第一级缓存。第一级缓存始终打开。
如果您确实不想使用第一级缓存,则必须使用StatelessSession而不是Session。但StatelessSession功能较少,您需要的一些方法可能会丢失。
如果您只想从第一级缓存中删除一些对象,可以使用Session.evict()。
尽管如此,我想知道为什么Hibernate会向您展示旧的价值。如果每个数据库实体在会话中只有一个实例(即,您更新的对象的实例与列表中显示的实例相同),则不应发生这种情况。如果你使用两个不同的实例,那么它是正常的Hibernate呈现旧值(如果你使用两个Session实例,一个用于列表,一个用于更新,那么你也可以在列表中获得旧值)。因此,在修复应用程序时,可能不必逐出该对象。
更新后编辑: 在您的短代码中,如果您作为单个用户使用该应用程序,我无法理解为什么会出现错误。
但一般情况下:您使用JSF,因此它可能是一个可供许多用户使用的Web项目。
Hibernate Session对象不是线程保存的,i。即如果不同的用户同时使用它,它可能无法工作。每个用户都需要自己的会话对象。因此,你可以将Hibernate会话实例存储在Http会话实例中(有时你甚至必须使用'synchronized'方法(或对象),因为用户在第一个请求的答案到达之前第二次按下按钮)。
第二次编辑:我认为你遇到与this question相同的问题。
可能您从任何地方复制了代码,例如引用问题的DAO。我想你的HibernateUtil类,你从任何地方复制的代码,都将hibernate会话存储在ThreadLocal对象中,i。即一个hibernate会话绑定到一个线程。
但是你正在做一个网络项目。你应该将一个Hibernate会话绑定到一个用户(或浏览器),i。即到一个Http会话。但是您不知道在哪个线程中处理来自一个http会话的请求。因此,在您的解决方案中,相同的Http会话可能会获得不同的Hibernate会话,或者可能不同的Http会话可能会获得相同的Hibernate会话。这取决于您的Http服务器。
解决方案:将Hibernate会话放入Http会话(并且不使用ThreadLocal对象)。您可以使用HttpServletRequest.getSession()
和HttpSession.getAttribute()
/ HttpSession.setAttribute()
获取Http会话对象,您可以设置Hibernate会话和其他与Http会话相关的数据。