Java Hibernate 会话刷新行为 [ 和 Spring @Transactional ]

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2876617/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 13:56:49  来源:igfitidea点击:

Hibernate Session flush behaviour [ and Spring @Transactional ]

javahibernatespringsession

提问by EugeneP

I use Spring and Hibernate in a web-app,

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

SessionFactory is injected into a DAO bean, and then this DAO is used in a Servlet through webservicecontext.

SessionFactory 被注入到一个 DAO bean 中,然后这个 DAO 通过 webservicecontext 在一个 Servlet 中使用。

DAO methods are transactional, inside one of the methods I use ... getCurrentSession().save(myObject);

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

One servlet calls this method with an object passed.

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

The update seems to not be flushed at once, it takes about 5 seconds to see the changes in the database. The servlet's method in which that DAO's update method is called, takes a fraction of second to complete.

更新好像不是一下子刷新的,大概需要5秒才能看到数据库的变化。调用该 DAO 的更新方法的 servlet 方法需要几分之一秒才能完成。

After the @Transactional method of DAO is completed, flushing may NOT happen ? It does not seem to be a rule[ I already see it ].

DAO 的@Transactional 方法完成后,可能不会发生刷新?这似乎不是规则[我已经看到了]。

Then the question is this: what to do to force the session to flush after every DAO method? It may not be a good thing to do, but talking about a Service layer, some methods must end with immediate flush, and Hibernate Session behavior is not predictable.

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

So what to do to guarantee that my @Transactional method persists all the changes after the last line of that method code?

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

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

p.s. I read somewhere that @Transactional IS ASSOCIATED with a DB Transaction. Method returns, transaction must be committed. I do not see this happens.

ps 我在某处读到@Transactional 与数据库事务相关联。方法返回,事务必须提交。我没有看到这种情况发生。

回答by Dick Chesterwood

Once the "top level" @Transactional method has completed (ie the method called from your servlet), then the transaction should be committed, and the default Hibernate behaviour is to flush on the commit.

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

It sounds like something odd is happening and you should do a bit of investigating.

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

Investigate your logs, in particular I would set the logging level to DEBUG on your Transaction Manager to see exactly what the transaction manager is doing.

调查您的日志,特别是我会在您的事务管理器上将日志记录级别设置为 DEBUG,以准确查看事务管理器在做什么。

Also, set the logging on for hibernate (set show_sql to true): this will output to System.out when flushing is happening and this might give you some clues.

此外,将登录设置为休眠(将 show_sql 设置为 true):这将在发生刷新时输出到 System.out,这可能会给您一些线索。

Do report back if you find anything interesting.

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

回答by think01

I had this problem too! I'm using Spring annotation transaction (@Transactional) and I'm getting some method actually doing the session.flush(), and other not!

我也有这个问题!我正在使用 Spring 注释事务 (@Transactional) 并且我得到了一些实际执行 session.flush() 的方法,而其他方法则没有!

From my debug log I have:

从我的调试日志中,我有:

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

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

The session.close() has to be called from Spring TransationManager, but I'm not sure what's going on: I double-checked my Spring configuration and I cannot argue why this is happening. I further investigate and I'll repost any hint but if someone has some useful advice I'ld be very grateful :o)

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

Update: The problem seems related to hibernate session flush mode = MANUAL: it didn't flush objects at the end of Spring's transaction.

更新:问题似乎与休眠会话刷新模式 = MANUAL 相关:它没有在 Spring 的事务结束时刷新对象。

You can verify your current flush mode with something like:

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

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



UPDATE [SOLVED FOR ME]:

更新 [为我解决]:

I dug a lot into my problem and found the origin of the problem and a workaround.

我深入研究了我的问题,找到了问题的根源和解决方法。

I had, as you if I understand well, an intermittent problem where some transactions were closed correctly (i.e. hibernate sessions were flushed) and some others not.

我有一个间歇性的问题,就像你一样,如果我理解得很好,一些事务被正确关闭(即休眠会话被刷新),而另一些则没有。

I discovered that:

我发现:

  1. the problem was due to flushModeset to FlushMode.MANUALwhen I entered the transaction (that not trigger the flush operation when transaction commits);

  2. this in turn was due to something nasty going on between Spring and ZK (the bad transactions were all from ZK's composers - if you don't know ZK, they are called from servlets, roughly equivalent to Struts' actions);

  3. the precise problem lies in the Spring's OpenSessionInViewFilternot correctly communicating with ZK's filters: the Hibernate session provided by OpenSessionInViewFilteris not getting its flush mode correctly set. The single ZK composer works well (I've tested it from some JUnit tests), as well as the OpenSessionInViewFilteralone (I've tested it from a plain Servlet using WebApplicationContextUtils).

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

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

  3. 确切的问题在于 SpringOpenSessionInViewFilter没有正确地与 ZK 的过滤器通信:提供的 Hibernate 会话OpenSessionInViewFilter没有正确设置其刷新模式。单个 ZK composer 运行良好(我已经通过一些 JUnit 测试对其进行了测试),以及OpenSessionInViewFilter单独运行(我已经使用WebApplicationContextUtils.

Conclusion: I call session.flush()at the end of every @Transactional method in my composers, and I'll migrate to Vaadin (much much simpler to integrate, it seems).

结论:我session.flush()在我的作曲家中的每个 @Transactional 方法结束时调用,我将迁移到 Vaadin(似乎更容易集成)。

回答by think01

For your particular need (flushing at every DAO method call), you can set the session flush mode to FlushMode.ALWAYS. From the hibernate documentation:

对于您的特殊需要(在每次 DAO 方法调用时刷新),您可以将会话刷新模式设置为 FlushMode.ALWAYS。从休眠文档:

The Session is flushed before every query.

回答by commit

Amaze I think this one be exact answer which can solve such exact situation, you can use FlushMode.ALWAYSfor particular DAO whenever you require to flush session restrict fully after every transaction.

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

@Autowired
private SessionFactory sessionFactory;

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

ALWAYSflushmode is not preferable because it is costly, but as you are require you can multiple session factory with different flushmode for different requirement.

ALWAYS冲洗模式不是可取的,因为它很昂贵,但是根据您的需要,您可以针对不同的要求使用不同的冲洗模式多个会话工厂。

回答by flavio.donze

setting the "hibernate.transaction.flush_before_completion" property to true might help.

将“hibernate.transaction.flush_before_completion”属性设置为 true 可能会有所帮助。

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

https://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/
"If enabled, the session will be automatically flushed during the before completion phase of the transaction. Built-in and automatic session context management is preferred, see Section 2.5, “Contextual sessions”".

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

回答by adlerer

I wouldn't recommend using hibernate.transaction.flush_before_completionglobally for performance reasons.

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

Alternatively, you can manage transactions yourself:

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

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

        <update database>

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