带有@Transactional注释的Spring OpenSessionInViewFilter

Gau*_*tam 11 java spring hibernate spring-transactions

这是关于Spring 在服务层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>
Run Code Online (Sandbox Code Playgroud)

Fer*_*con 12

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

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

那么问题的答案是:

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

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

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

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

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

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

  • 默认情况下,当OpenSessionInView打开会话时,flushMode将建立为MANUAL,因为它的目的只是读取对象而不是写入数据库.如果您使用HibernateTransactionManager并且所有修改都包含在事务中,则无需更改刷新模式,因为HibernateTransactionManager在启动新事务时更改此设置.但是,如果更改事务外的对象(这不是一个好习惯),则需要将刷新模式设置为AUTO. (2认同)

Jam*_*ENL 5

在这里加入我的 0.02c(并扩展Fernando Rincon 的出色答案):

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

但是,如果您有使用过滤器解决的另一个问题OpenSessionInView,并且作为一个令人高兴的副作用,您随后打开了一个会话,那么我不认为使用它来访问您的集合有什么坏处。但是,我想说,如果您使用OpenSessionInView在一个地方获取集合对象,则应该在其他地方重构代码以执行相同的操作,以便用于获取集合的策略在您的应用程序中实现标准化。

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