Hibernate Session刷新行为[和Spring @Transactional]

时间:2010-05-20 18:14:06

标签: java hibernate spring session

我在网络应用程序中使用Spring和Hibernate,

将SessionFactory注入到DAO bean中,然后通过webservicecontext在Servlet中使用此DAO。

DAO方法是事务性的,在我使用的方法之一... getCurrentSession()。save(myObject);

一个servlet使用传递的对象调用此方法。

更新似乎不会立即刷新,查看数据库中的更改大约需要5秒钟。调用DAO更新方法的servlet方法只需几分之一秒即可完成。

DAO的@Transactional方法完成后,可能不会发生冲洗?它似乎不是一个规则 [我已经看到了]。

然后问题是:在每个DAO方法之后如何强制会话刷新? 这可能不是一件好事,但谈到Service层,一些方法必须以立即刷新结束,而Hibernate Session行为是不可预测的。

那么如何保证我的@Transactional方法在该方法代码的最后一行之后保留所有更改?

 getCurrentSession().flush() is the only solution?

P.S。我在某处看到@Transactional与DB Transaction相关联。方法返回,必须提交事务。 我没有看到这种情况发生。

6 个答案:

答案 0 :(得分:5)

一旦“顶级”@Transactional方法完成(即从你的servlet调用的方法),那么应该提交事务,并且默认的Hibernate行为是在提交时刷新。

听起来好像发生了奇怪的事情,你应该做一些调查。

调查您的日志,特别是我会在事务管理器上将日志记录级别设置为DEBUG,以确切了解事务管理器正在执行的操作。

另外,为hibernate设置登录(将show_sql设置为true):当发生刷新时,这将输出到System.out,这可能会给你一些线索。

如果您发现任何有趣的事情,请回报。

答案 1 :(得分:3)

Amaze我认为这是一个可以解决这种确切情况的确切答案,每当你需要在每次交易后完全刷新会话限制时,你可以使用FlushMode.ALWAYS来表示特定的DAO。

@Autowired
private SessionFactory sessionFactory;

@Before
public void myInitMethod(){
  sessionFactory.getCurrentSession().setFlushMode(FlushMode.ALWAYS);
}

ALWAYS flushmode不是优选的,因为它很昂贵,但是根据您的要求,您可以根据不同的要求使用不同的flushmode进行多个会话工厂。

答案 2 :(得分:2)

我也有这个问题! 我正在使用Spring注释事务(@Transactional),我正在使用一些方法实际执行session.flush(),而不是其他方法!

从我的调试日志中我有:

transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!

必须从Spring TransationManager调用session.close(),但我不确定发生了什么:我仔细检查了我的Spring配置,我无法争论为什么会发生这种情况。我进一步调查,我将重新发布任何提示,但如果有人有一些有用的建议,我将非常感激:o)

更新:问题似乎与hibernate session flush mode = MANUAL有关:它在Spring事务结束时没有刷新对象。

您可以使用以下内容验证当前的刷新模式:

SessionFactoryUtils.getSession(mySessionFactory(), false).getCurrentSession().getFlushMode()


更新[为我解决]:

我在我的问题中挖了很多东西,找到了问题的根源和解决方法。

如果我理解的话,我有一个间歇性问题,其中一些交易被正确关闭(即休眠会话被刷新)而其他一些事务没有。

我发现:

  1. 问题是由于我在进入事务时将flushMode设置为FlushMode.MANUAL(在事务提交时不触发刷新操作);

  2. 这反过来是由于Spring和ZK之间发生了一些令人讨厌的事情(糟糕的交易全部来自ZK的作曲家 - 如果你不了解ZK,他们是从servlet调用的,大致相当于Struts的行动);

  3. 准确的问题在于Spring的OpenSessionInViewFilter没有正确地与ZK的过滤器通信:OpenSessionInViewFilter提供的Hibernate会话没有正确设置其刷新模式。单个ZK编写器运行良好(我已经从一些JUnit测试中测试过它),以及单独的OpenSessionInViewFilter(我使用WebApplicationContextUtils从一个普通的Servlet测试过它。)

  4. 结论:我在作曲家的每个@Transactional方法结束时调用session.flush(),我将迁移到Vaadin(看起来要简化得多)。

答案 3 :(得分:0)

根据您的特殊需要(在每个DAO方法调用时刷新),您可以将会话刷新模式设置为FlushMode.ALWAYS。来自hibernate文档:

The Session is flushed before every query.

答案 4 :(得分:0)

设置" hibernate.transaction.flush_before_completion"属性为真可能有帮助。

<prop key="hibernate.transaction.flush_before_completion">true</prop>

https://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/
&#34;如果启用,会话将在事务完成前阶段自动刷新。内置和自动会话上下文管理是首选,请参见第2.5节“上下文会话”&#34;。

答案 5 :(得分:0)

出于性能原因,我不建议全局使用 hibernate.transaction.flush_before_completion

或者,您可以自己管理交易:

@PersistenceContext
private EntityManager em;
....
public void save(){
    try
    {         
        em.getTransaction().begin();                

        <update database>

        em.getTransaction().commit();                        
    }
    catch(Throwable th) {
        em.getTransaction().rollback();
        //log, rethrow
    }        
}