视图中的休眠打开会话:每个请求的事务?

Posted

技术标签:

【中文标题】视图中的休眠打开会话:每个请求的事务?【英文标题】:Hibernate Open Session in View: Transaction per Request? 【发布时间】:2012-05-26 14:16:06 【问题描述】:

我在 Tomcat 上使用 Hibernate 和 Spring。我一直在阅读和重新阅读指向JBoss wiki page 的关于该主题的内容,这很有帮助。但它给我留下了一些问题。

    为每个请求启动事务的想法困扰着我。我想我可以将过滤器限制为某些控制器——也许将我所有需要事务的控制器放在伪“tx”路径或其他东西下。但是,如果您不知道是否需要交易,那么使用交易不是一个坏主意吗?而且,如果我只是在某些请求中进行读取——很可能来自缓存的读取——没有事务我不是更好吗?

    我读过一些帖子,提到他们如何在服务层处理事务,我想用 Spring 来做这件事。但是过滤器代码是什么样的呢?我仍然希望会话在我的视图中可用以进行一些延迟加载。

    如果我只需在我的过滤器中调用sessionFactory.getCurrentSession(),它如何“释放”回会话工厂以供重复使用? (我希望看到 session.close() 或其他东西,即使在使用事务时也是如此。)谁告诉会话工厂该会话可以重用?

    也许是beginTransaction() 调用在请求期间将给定的数据库连接绑定到给定的会话?否则,会话会根据需要从池中提取数据库连接,对吧?

感谢您耐心解答我的所有问题。

(如果你的答案是指向 Spring 文档的链接,你只会让我哭泣。你不想要这样,是吗?如果人们不再回答 Spring,我会付真金白银-相关的问题。)

【问题讨论】:

【参考方案1】:

您的担忧是有道理的,wiki 页面上提供的解决方案过于简单。事务不应该在 Web 层进行管理 - 它应该在服务层进行处理。

正确的实现会打开一个会话并将其绑定到过滤器中的一个线程。没有交易开始。会话被置于刷新模式从不 - 只读模式。服务调用会将会话设置为自动刷新模式并启动/提交事务。一旦服务方法完成,会话刷新模式将恢复为从不。

还有一个选项可以在过滤器中不打开会话。每个服务层调用都会打开一个单独的会话和事务 - 服务调用完成后,会话不会关闭,而是注册为延迟关闭。 Web 请求处理完成后,会话将关闭。

Spring 提供了OpensessionInViewFilter,其工作方式如上所述。所以忽略 jboss wiki 文章,只配置 OpensessionInViewFilter - 一切都会好起来的。

SessionFactory.getCurrentSession() - 在内部创建会话并将其分配给本地线程。每个请求/线程都有自己的会话。 Web 请求处理完成后,会话将关闭。在您的代码中,您只需要使用 SessionFactory.getCurrentSession() 而不必关闭它。 jboss wiki 页面上的代码示例是错误的——它应该在 finally 块中有一个 SessionFactory.getCurrentSession().close()。或者他们可能正在使用 JTA 事务并将休眠配置为与 JTA 事务一起打开/关闭会话。

【讨论】:

我从这里到那里再到那里,在这个主题上跳过了一个星期的网络......这是我第一次读到 Spring 有一个 OpenSessionInView 过滤器。谢谢。 我一直认为它可以作为交易。但实际上事务受到spring的@Transactional的限制。谢谢你的解释。【参考方案2】:

如果过滤器为每个请求创建一个会话,这不是问题,因为会话来自会话池,并且它们将被重用。从操作系统的角度来看,什么都没有发生。

事实上,休眠会话是与数据库服务器的 tcp(或套接字/管道)连接。创建 db conn 的成本很大程度上取决于 sql 类型(postgresql 在这方面非常糟糕,尽管它在任何方面都非常好)。但这并不意味着什么,因为 hibernate 重用了数据库连接。

简单的休眠过滤器解决方案也会为每个请求在会话上启动一个新事务。从 SQL 的角度来看,它是事务:它是一个“BEGIN”和“COMMIT”查询。它总是很昂贵,应该减少。

恕我直言,如果事务仅在当前请求的第一个查询时启动,则可能的解决方案是。也许春天有一些有用的东西。

【讨论】:

我们得到的是将 OpenSessionInView 过滤器(见上文)与 Spring @Transactional 注释结合使用。到目前为止,效果很好。 @Marvo 我没有那么好的经验。你无法控制,究竟发生了什么以及为什么。在我的上一个项目中,我有多个数据库连接(一些实体连接第一个,一些连接到第二个)和交叉定向弹簧层,其中一些进程来自 Quartz,一些来自网络。情况很复杂,主要问题在于,在基于注释或/和 AOP 的事物中,您可以非常控制地检查失败的原因或问题出在哪里。它是否有效,但您看不到它们。在清晰的程序环境中,这是微不足道的。

以上是关于视图中的休眠打开会话:每个请求的事务?的主要内容,如果未能解决你的问题,请参考以下文章

休眠中的多租户数据库

单个休眠会话中的多个事务(使用 Spring)

无法为事务打开休眠会话/无法打开连接 [关闭]

JSF 中的本地化,如何记住每个会话而不是每个请求/视图选择的语言环境

在每个请求使用会话时如何让 NHibernate 重试死锁事务?

休眠“在视图中打开会话”和异步任务