您的位置:首页 > 编程语言 > Java开发

Spring MVC @Transactional注解方式事务失效的解决办法

2016-05-23 13:30 841 查看
首先展示问题:

Spring applicationContext.xml配置:

[html] view
plaincopy





         

<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配置:

[html] view
plaincopy





<!-- 自动扫描的包名 -->    

<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:

[java] view
plaincopy





@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增加:

[html] view
plaincopy





<context:component-scan base-package="com.will">   

    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />   

</context:component-scan>  

在 Spring mvc.dispatcher.xml增加:

[html] view
plaincopy





<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中配置:

[html] view
plaincopy





<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。

( update 2014.05.27  今天看见一种说法:key word =双亲上下文。不使用ContextLoaderListener监听器来加载spring的配置,改用DispatcherServlet来加载spring的配置,不要双亲上下文,只使用一个DispatcherServlet就不会出现上述问题。笔者这里未测过这个办法,因为我自己的业务需要一个extends ContextLoaderListener的selfListener,有兴趣的朋友可以自己测试下这个说法,并欢迎把测试的结果与我交流
:) )

经过以上分析,故可以优化上述配置

在 applicationContext.xml增加:

[html] view
plaincopy





<context:component-scan base-package="com.will">   

</context:component-scan>  

在 Spring mvc.dispatcher.xml增加:

[html] view
plaincopy





<context:component-scan base-package="com.will" >      

    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />    

</context:component-scan>  

经过如上配置,可以发现事务控制部分的日志如下:

[html] view
plaincopy





2013-09-25 09:53:13,031 [http-8080-2] DEBUG [org.springframework.transaction.annotation.AnnotationTransactionAttributeSource] - Adding transactional method 'save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''  

2013-09-25 09:53:13,037 [http-8080-2] DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'txManager'  

2013-09-25 09:53:13,050 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Creating new transaction with name [com.will.service.impl.PersonServiceImpl.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''  

2013-09-25 09:53:13,313 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Acquired Connection [jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction  

2013-09-25 09:53:13,323 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Switching JDBC Connection [jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL-AB JDBC Driver] to manual commit  

2013-09-25 09:53:13,327 [http-8080-2] DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL update  

2013-09-25 09:53:13,328 [http-8080-2] DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [delete from person where id=?]  

2013-09-25 09:53:13,348 [http-8080-2] DEBUG [org.springframework.jdbc.core.JdbcTemplate] - SQL update affected 1 rows  

2013-09-25 09:53:13,363 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Initiating transaction rollback  

2013-09-25 09:53:13,364 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL-AB JDBC Driver]  

2013-09-25 09:53:13,377 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Releasing JDBC Connection [jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL-AB JDBC Driver] after transaction  

2013-09-25 09:53:13,378 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Returning JDBC Connection to DataSource  

在2013-09-25 09:53:13,363处进行了rollback。

转载的转载,不知原处。感谢原作者分享!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: