Spring MVC @Transactional注解方式事务失效的解决办法
2016-03-11 16:09
741 查看
前文提到,最新换了框架,新项目用SpringMVC + Spring JdbcTemplate。搭框架时,发现了一个事务无法正常回滚的问题,记录如下:
首先展示问题:
Spring applicationContext.xml配置:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/will</value>
</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务控制 -->
<tx:annotation-driven transaction-manager="txManager" />Spring mvc.dispatcher.xml配置:
!-- 自动扫描的包名 -->
<context:component-scan base-package="com.will" >
</context:component-scan>
<!-- 默认的注解映射的支持 -->
<mvc:annotation-driven />
<!-- 对静态资源文件的访问 -->
<mvc:default-servlet-handler/>
<!-- 拦截器
<mvc:interceptors>
<bean class="com.will.mvc.MyInteceptor" />
</mvc:interceptors>
-->
<!-- 视图解释类 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
然后在Service层模拟了一个事务回滚的method case:
@Transactional
public boolean save(Person person)
{
for(int id: new int[]{2,3})
{
personDao.del(id);
int j = 1/0;
}
return false;
}
本以为大功告成,在运行save方法时,由于1/0 抛出 java.lang.ArithmeticException: / by zero
需要在 applicationContext.xml增加:
<context:component-scan base-package="com.will">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
在 Spring mvc.dispatcher.xml增加:
<context:component-scan base-package="com.will" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
由于web.xml中配置:
Spring容器优先加载由ServletContextListener(对应applicationContext.xml)产生的父容器,而SpringMVC(对应mvc_dispatcher_servlet.xml)产生的是子容器。子容器Controller进行扫描装配时装配的@Service注解的实例是没有经过事务加强处理,即没有事务处理能力的Service,而父容器进行初始化的Service是保证事务的增强处理能力的。如果不在子容器中将Service
exclude掉,此时得到的将是原样的无事务处理能力的Service,因为在多上下文的情况下,如果同一个bean被定义两次,后面一个优先。
( update 2014.05.27 今天看见一种说法:key word =双亲上下文。不使用ContextLoaderListener监听器来加载spring的配置,改用DispatcherServlet来加载spring的配置,不要双亲上下文,只使用一个DispatcherServlet就不会出现上述问题。笔者这里未测过这个办法,因为我自己的业务需要一个extends ContextLoaderListener的selfListener,有兴趣的朋友可以自己测试下这个说法,并欢迎把测试的结果与我交流
:) )
( update 2015.03.20 对于因为双亲上下文引起初始化二次,关键字:springmvc 初始化两次 )
经过以上分析,故可以优化上述配置:
在 applicationContext.xml增加:
<context:component-scan base-package="com.will">
</context:component-scan>
在 Spring mvc.dispatcher.xml增加:
<context:component-scan base-package="com.will" >
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
首先展示问题:
Spring applicationContext.xml配置:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/will</value>
</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务控制 -->
<tx:annotation-driven transaction-manager="txManager" />Spring mvc.dispatcher.xml配置:
!-- 自动扫描的包名 -->
<context:component-scan base-package="com.will" >
</context:component-scan>
<!-- 默认的注解映射的支持 -->
<mvc:annotation-driven />
<!-- 对静态资源文件的访问 -->
<mvc:default-servlet-handler/>
<!-- 拦截器
<mvc:interceptors>
<bean class="com.will.mvc.MyInteceptor" />
</mvc:interceptors>
-->
<!-- 视图解释类 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
然后在Service层模拟了一个事务回滚的method case:
@Transactional
public boolean save(Person person)
{
for(int id: new int[]{2,3})
{
personDao.del(id);
int j = 1/0;
}
return false;
}
本以为大功告成,在运行save方法时,由于1/0 抛出 java.lang.ArithmeticException: / by zero RuntimeException,导致事务回归。However,no
way! So crazy~
查了下,发现Spring MVC对于事务配置比较讲究,需要额外的配置。解决办法如下:
需要在 applicationContext.xml增加:
<context:component-scan base-package="com.will">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
在 Spring mvc.dispatcher.xml增加:
<context:component-scan base-package="com.will" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
由于web.xml中配置:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/mvc_dispatcher_servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
Spring容器优先加载由ServletContextListener(对应applicationContext.xml)产生的父容器,而SpringMVC(对应mvc_dispatcher_servlet.xml)产生的是子容器。子容器Controller进行扫描装配时装配的@Service注解的实例是没有经过事务加强处理,即没有事务处理能力的Service,而父容器进行初始化的Service是保证事务的增强处理能力的。如果不在子容器中将Service
exclude掉,此时得到的将是原样的无事务处理能力的Service,因为在多上下文的情况下,如果同一个bean被定义两次,后面一个优先。
( update 2014.05.27 今天看见一种说法:key word =双亲上下文。不使用ContextLoaderListener监听器来加载spring的配置,改用DispatcherServlet来加载spring的配置,不要双亲上下文,只使用一个DispatcherServlet就不会出现上述问题。笔者这里未测过这个办法,因为我自己的业务需要一个extends ContextLoaderListener的selfListener,有兴趣的朋友可以自己测试下这个说法,并欢迎把测试的结果与我交流
:) )
( update 2015.03.20 对于因为双亲上下文引起初始化二次,关键字:springmvc 初始化两次 )
经过以上分析,故可以优化上述配置:
在 applicationContext.xml增加:
<context:component-scan base-package="com.will">
</context:component-scan>
在 Spring mvc.dispatcher.xml增加:
<context:component-scan base-package="com.will" >
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
相关文章推荐
- SQL Server误区30日谈 第1天 正在运行的事务在服务器故障转移后继续执行
- 浅析SQL Server中包含事务的存储过程
- Mysql中的事务是什么如何使用
- MySql的事务使用与示例详解
- C#分布式事务的超时处理实例分析
- C#中的事务用法实例分析
- SQL Server的事务操作隔离模式介绍
- MySQL中事务概念的简洁学习教程
- C#处理Access中事务的方法
- oracle 合并查询 事务 sql函数小知识学习
- sql不常用函数总结以及事务,增加,删除触发器
- mysql的XA事务恢复过程详解
- 在Mysql存储过程中使用事务实例
- 解析php file_exists无效的解决办法
- mysql存储过程事务管理简析
- php+mysql事务rollback&commit示例
- PHP中的事务使用实例
- SQLServer存储过程中事务的使用方法
- 在Java的JDBC使用中设置事务回滚的保存点的方法
- PHP使用Mysql事务实例解析