问题 带有@Transactional注释的Spring OpenSessionInViewFilter


这是关于春天 OpenSessionInViewFilter 使用 @Transactional 服务层的注释。

我经历了这么多的堆栈溢出帖子,但仍然困惑我是否应该使用 OpenSessionInViewFilter 还是不要避免 LazyInitializationException 如果有人帮我找到以下查询的答案,那将是很有帮助的。

  • 这是不好的做法 OpenSessionInViewFilter 在申请中 有复杂的架构。
  • 使用此过滤器可能会导致 N+1 问题
  • 如果我们使用 OpenSessionInViewFilter 这是什么意思 @Transactional 不是必需的?

下面是我的Spring配置文件

<context:component-scan base-package="com.test"/>
<context:annotation-config/>
 <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="resources/messages" />
        <property name="defaultEncoding" value="UTF-8" />
    </bean>
 <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
        p:location="/WEB-INF/jdbc.properties" />
 <bean id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
        p:driverClassName="${jdbc.driverClassName}"
        p:url="${jdbc.databaseurl}" p:username="${jdbc.username}"
        p:password="${jdbc.password}" />
       <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />     
        <property name="configLocation">
            <value>classpath:hibernate.cfg.xml</value>
        </property>
        <property name="configurationClass">
            <value>org.hibernate.cfg.AnnotationConfiguration</value>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${jdbc.dialect}</prop>
                <prop key="hibernate.show_sql">true</prop>
                <!--
                <prop key="hibernate.hbm2ddl.auto">create</prop>
                 -->
            </props>
        </property>
    </bean>
 <tx:annotation-driven /> 
 <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />

  </bean>

12026
2018-05-26 21:23


起源



答案:


OpenSessionInView 是一个servlet过滤器,而不仅仅是打开一个hibernate会话并将其存储在 SessionHolder 为服务请求的线程。打开此会话后,当您在请求的呈现阶段使用它时,hibernate可以读取Lazy初始化集合和对象。调用时可以访问此会话 SessionFactory.getCurrentSession()

但是,OpenSessionInView只是打开会话,它不会开始任何事务。打开会话后,您可以从数据库中读取对象,但是,如果您想在事务中执行某些操作,则需要执行此操作 @Transactional 在需要时划分事务开始和结束的注释或其他机制。

然后回答问题:

在具有复杂模式的应用程序中使用OpenSessionInViewFilter是不好的做法。

如果你需要避免使用LazyInitializationException并且重载只是打开新的Hibernate Session并在每个请求的请求结束时关闭它,这是一个很好的做法。

使用此过滤器可能会导致N + 1问题

我在许多项目中使用此过滤器,并没有造成任何问题。

如果我们使用OpenSessionInViewFilter,是否意味着不需要@Transactional?

不可以。你只在线程的SessionHolder中打开了一个Hibernate Session,但是如果你需要需要放置的事务 @Transactional


10
2018-05-27 00:34



+1感谢您的详细解答。我忘了在我的排队中再提一件事了。我还需要更改会话模式吗?我看到人们在使用OpenSessionInView过滤器时遇到更新问题并保存 - Gautam
会话模式是什么意思? - Fernando Rincon
@Fernanado感谢回复我检查了几个帖子并注意到这段代码session.setFlushMode(FlushMode.AUTO);为什么我们明确需要设置会话刷新模式?对不起,这是会话刷新模式。 - Gautam
默认情况下,当OpenSessionInView打开会话时,flushMode将建立到MANUAL,因为它的目的只是读取对象而不是写入数据库。如果您使用HibernateTransactionManager并且所有修改都包含在事务中,则无需更改刷新模式,因为HibernateTransactionManager在启动新事务时更改此设置。但是,如果更改事务外的对象(这不是一个好习惯),则需要将刷新模式设置为AUTO。 - Fernando Rincon
非常感谢 :)) - Gautam


答案:


OpenSessionInView 是一个servlet过滤器,而不仅仅是打开一个hibernate会话并将其存储在 SessionHolder 为服务请求的线程。打开此会话后,当您在请求的呈现阶段使用它时,hibernate可以读取Lazy初始化集合和对象。调用时可以访问此会话 SessionFactory.getCurrentSession()

但是,OpenSessionInView只是打开会话,它不会开始任何事务。打开会话后,您可以从数据库中读取对象,但是,如果您想在事务中执行某些操作,则需要执行此操作 @Transactional 在需要时划分事务开始和结束的注释或其他机制。

然后回答问题:

在具有复杂模式的应用程序中使用OpenSessionInViewFilter是不好的做法。

如果你需要避免使用LazyInitializationException并且重载只是打开新的Hibernate Session并在每个请求的请求结束时关闭它,这是一个很好的做法。

使用此过滤器可能会导致N + 1问题

我在许多项目中使用此过滤器,并没有造成任何问题。

如果我们使用OpenSessionInViewFilter,是否意味着不需要@Transactional?

不可以。你只在线程的SessionHolder中打开了一个Hibernate Session,但是如果你需要需要放置的事务 @Transactional


10
2018-05-27 00:34



+1感谢您的详细解答。我忘了在我的排队中再提一件事了。我还需要更改会话模式吗?我看到人们在使用OpenSessionInView过滤器时遇到更新问题并保存 - Gautam
会话模式是什么意思? - Fernando Rincon
@Fernanado感谢回复我检查了几个帖子并注意到这段代码session.setFlushMode(FlushMode.AUTO);为什么我们明确需要设置会话刷新模式?对不起,这是会话刷新模式。 - Gautam
默认情况下,当OpenSessionInView打开会话时,flushMode将建立到MANUAL,因为它的目的只是读取对象而不是写入数据库。如果您使用HibernateTransactionManager并且所有修改都包含在事务中,则无需更改刷新模式,因为HibernateTransactionManager在启动新事务时更改此设置。但是,如果更改事务外的对象(这不是一个好习惯),则需要将刷新模式设置为AUTO。 - Fernando Rincon
非常感谢 :)) - Gautam


在这里投掷我的0.02c(并继续扩大 Fernando Rincon的出色答案):

你不应该使用 OpenSessionInView 过滤 只是 因为你需要绕过一个 LazyInitializationException。它只会给你的系统增加另一层混乱和复杂性。您应该从系统设计中确切地知道您需要在前端访问集合的位置。从那里开始,很容易(根据我的经验)构建一个控制器方法来调用服务方法来检索你的集合更合乎逻辑。

但是,如果你有另一个问题,使用 OpenSessionInView 过滤器解决了,作为一个愉快的副作用,然后你打开一个会话,然后我没有看到使用它来访问你的集合的危害。但是,如果你使用的话,我会说 OpenSessionInView 要在一个地方获取集合对象,您应该在其他地方重构代码以执行相同的操作,以便在应用程序中标准化用于获取集合的策略。

权衡此重构的成本与编写控制器和服务方法的成本,以确定您是否应该使用 OpenSessionInView过滤。


3
2018-05-27 04:16



+1用于解释OpenSessionInViewfiletr的副作用:) - Gautam


OpenSessionInViewFilter是一个servlet过滤器,它将hibernate会话绑定到http请求,对于所有数据库操作,事务性和非事务性,同一个hibernate会话用于给定的http请求。这会将db层暴露给Web层,使其成为反模式。

我的经验是,当我们想要对java对象进行更改并且不希望它们反映在数据库中时,这会使代码难以调试。由于hibernate会话始终处于打开状态,因此它希望刷新数据库中的数据。

仅当JS基本休息服务存在且其间没有服务层时,才应使用此方法。


2
2018-02-13 03:26



谢谢你的建议。真的很感激。 - Gautam


OpenSessionInViewFilter的典型使用模式是某些实体被延迟加载,但在视图呈现阶段,视图需要此实体的一些属性,这些属性最初未加载,因此需要从数据库中获取此数据。现在,通常在Web应用程序的服务层中进行事务划分,因此在视图呈现发生时,视图正在与分离的实体一起工作,从而导致 LazyInitializationException 访问卸载的属性时。

从这个网址 https://developer.jboss.org/wiki/OpenSessionInView :

问题
  典型的Web应用程序中的一个常见问题是在操作的主要逻辑完成之后呈现视图,因此,Hibernate会话已经关闭并且数据库事务已经结束。如果访问已在JSP(或任何其他视图呈现机制)内的Session中加载的分离对象,则可能会遇到未加载的集合或未初始化的代理。您得到的异常是:LazyInitializationException:会话已关闭(或非常类似的消息)。当然,这是可以预料的,毕竟你已经结束了你的工作单元。

第一种解决方案是打开另一个工作单元来渲染视图。这很容易做到,但通常不是正确的方法。渲染已完成操作的视图应该在第一个工作单元内,而不是单独的工作单元。在双层系统中,具有动作执行,通过会话进行数据访问以及在同一虚拟机中呈现视图的解决方案是在会话呈现之前保持会话处于打开状态。

作为替代方案,请考虑使用视图所需的适当数据量加载实体。这可以通过使用DTO投影来完成。本文列出了使用Open Session In View模式的一些缺点: https://vladmihalcea.com/the-open-session-in-view-anti-pattern/


1
2018-05-09 22:38