Hibernate 允许对关联对象、属性进行延迟加载,但是必须保证延迟加载的操作限于同一个 Hibernate Session 范围之内进行。如果 Service 层返回一个启用了延迟加载功能的领域对象给 Web 层,当 Web 层访问到那些需要延迟加载的数据时,由于加载领域对象的 Hibernate Session 已经关闭,这些导致延迟加载数据的访问异常。而Spring为我们提供的OpenSessionInViewFilter过滤器为我们很好的解决了这个问 题。OpenSessionInViewFilter的主要功能是使每个请求过程绑定一个 Hibernate Session,即使最初的事务已经完成了,也可以在 Web 层进行延迟加载的操作。OpenSessionInViewFilter 过滤器将 Hibernate Session 绑定到请求线程中,它将自动被 Spring 的事务管理器探测到。
看过OpenSessionInViewFilter以后就知道,OpenSessionInViewFilter的getSession方法中会对session的flushMode设定一个默认为NEVER的值。
我在用STRUCT2的时候只要在web.xml中配上下面的配置就可以了,不过我发现我在Spring MVC中这样配发现并没有起作用。
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>flushMode</param-name>
<param-value>AUTO</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这时候解决办法有两个:
1:继承OpenSessionInViewFilter,并覆盖getSession,设施flushMode 为AUTO
2:通过spring的事务管理Hibernate的sessionFactory,对不同的数据操作作事务的隔离及限制
因为反正都要用到spring作事务管理,所以我在项目中采用了第二种方法,然后我在spring的配置文件中这样配
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<aop:config>
<aop:pointcut id="bussinessService"
expression="execution(* com.pyp.workflow.dao.impl.*.*(..))" />
<aop:advisor pointcut-ref="bussinessService" advice-ref="txAdvice" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="false" propagation="NOT_SUPPORTED" />
<tx:method name="find*" propagation="NOT_SUPPORTED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
然后配完之后并没有用,事务没有生产。这就挺奇怪了的,我搞了好久才查到
大家先看下面配置,spring中自动扫包的
<context:component-scan base-package="com">
看过spring的参考手册的朋友都会这样配置,因为省事,一句话可以自动把com包低下的所有带annotation注解的类都实例化并配好了。所以习惯的也在springMVC中也作了这样的配置。
但如果这样简单的配置会导致刚才spring的事务配置失效原因:
实例化@Controller类时,Spring会自动把关联的@Service(此@Service已做了@Transaction事务注解)类实例化,此时事务并未生效,导致@Transaction注解无效,事务未被注册,因此需要把@Controller和其它的@Service,@Components,@Reposity等分开实例化,在事务生效后,并且其它组件都实例化完成后,@Controller最后实例化。所以在SpringMVC的配置文件中
<context:component-scan base-package="com.*.*.*">
这个配置要配到Controller(对应struct2中的action)这一层的根目录下。这样就可以解决这个问题。
还有一种解决办法就是在spring中的配置文件中这样配
<!-- 扫描com及子包,自动实例化带@注释的实例,这里排除@Controller,所有controller的实例化在 mvc-config中完成 -->
<context:component-scan base-package="com">
<context:exclude filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
在spring mvc的配置文件这样配
<!-- 扫描com及子包,自动实例化带@controller注释的实例,
由于实例化controller时会对controller关联的Service类一同实例化,所以这里需要排除@Service
-->
<context:component-scan base-package="com">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan>
这样也可以解决这个问题