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

本文介绍了在SpringMVC项目中正确配置事务管理的方法,解决了事务无法正常回滚的问题。通过调整applicationContext.xml和Springmvc.dispatcher.xml中的配置,实现了对@Service注解的事务增强。

最新换了框架,新项目用SpringMVC + spring JdbcTemplate。搭框架时,发现了一个事务无法正常回滚的问题,记录如下:

首先展示问题:

Spring applicationContext.xml配置:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.          
  2. <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">  
  3.     <property name="jndiName">  
  4.         <value>java:comp/env/jdbc/will</value>  
  5.     </property>  
  6. </bean>   
  7.       
  8. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  
  9.     <property name="dataSource" ref="dataSource" />  
  10. </bean>  
  11.   
  12. <bean id="txManager"  
  13.     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  14.     <property name="dataSource" ref="dataSource" />  
  15. </bean>  
  16.   
  17. <!-- 事务控制   -->  
  18. <tx:annotation-driven transaction-manager="txManager" />  

Spring mvc.dispatcher.xml配置:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <!-- 自动扫描的包名 -->    
  2. <context:component-scan base-package="com.will" >   
  3. </context:component-scan>  
  4.   
  5. <!-- 默认的注解映射的支持 -->  
  6. <mvc:annotation-driven />  
  7.   
  8. <!-- 对静态资源文件的访问  -->    
  9. <mvc:default-servlet-handler/>    
  10.     
  11.       
  12. <!-- 拦截器    
  13. <mvc:interceptors>    
  14.     <bean class="com.will.mvc.MyInteceptor" />    
  15. </mvc:interceptors>   
  16. -->   
  17.   
  18. <!-- 视图解释类 -->   
  19. <bean id="viewResolver"    
  20.     class="org.springframework.web.servlet.view.UrlBasedViewResolver">    
  21.     <property name="viewClass"  value="org.springframework.web.servlet.view.JstlView" />    
  22.     <property name="prefix" value="/WEB-INF/pages/" />    
  23.     <property name="suffix" value=".jsp" />    
  24. </bean>     

然后在Service层模拟了一个事务回滚的method case:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Transactional  
  2. public boolean save(Person person)  
  3. {  
  4.    for(int id: new int[]{2,3})  
  5.     {  
  6.         personDao.del(id);  
  7.         int j = 1/0;  
  8.     }                  
  9.      
  10.     return false;  
  11. }  

本以为大功告成,在运行save方法时,由于1/0 抛出 java.lang.ArithmeticException: / by zero  RuntimeException,导致事务回归。However,no way! So crazy~

查了下,发现Spring MVC对于事务配置比较讲究,需要额外的配置。解决办法如下:

需要在 applicationContext.xml增加:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <context:component-scan base-package="com.will">   
  2.     <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />   
  3. </context:component-scan>  

在 Spring mvc.dispatcher.xml增加:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <context:component-scan base-package="com.will" >   
  2.     <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />   
  3.     <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />    
  4. </context:component-scan>  


由于web.xml中配置:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <context-param>  
  2.     <param-name>contextConfigLocation</param-name>  
  3.     <param-value>  
  4.              classpath:applicationContext.xml  
  5.     </param-value>  
  6. </context-param>  
  7. <listener>  
  8.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  9. </listener>  
  10. <servlet>  
  11.     <servlet-name>dispatcher</servlet-name>  
  12.     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  13.     <init-param>  
  14.          <param-name>contextConfigLocation</param-name>  
  15.          <param-value>classpath*:/mvc_dispatcher_servlet.xml</param-value>  
  16.     </init-param>  
  17.     <load-on-startup>1</load-on-startup>  
  18. </servlet>  
  19. <servlet-mapping>  
  20.     <servlet-name>dispatcher</servlet-name>  
  21.     <url-pattern>*.do</url-pattern>  
  22. </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 plain copy 在CODE上查看代码片 派生到我的代码片
  1. <context:component-scan base-package="com.will">   
  2. </context:component-scan>  

在 Spring mvc.dispatcher.xml增加:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <context:component-scan base-package="com.will" >      
  2.     <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />    
  3. </context:component-scan>  

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

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 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; ''  
  2. 2013-09-25 09:53:13,037 [http-8080-2] DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'txManager'  
  3. 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; ''  
  4. 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  
  5. 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  
  6. 2013-09-25 09:53:13,327 [http-8080-2] DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL update  
  7. 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=?]  
  8. 2013-09-25 09:53:13,348 [http-8080-2] DEBUG [org.springframework.jdbc.core.JdbcTemplate] - SQL update affected 1 rows  
  9. 2013-09-25 09:53:13,363 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Initiating transaction rollback  
  10. 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]  
  11. 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  
  12. 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。
<think>好的,我现在需要回答用户关于Spring MVC中@Repository注解的使用说明、示例和功能的问题。首先,我应该回顾一下用户提供的引用资料,看看里面有没有相关的信息。 用户提供的引用有5个,其中引用[1]提到@Component、@Repository、@Service、@Controller的关系,指出@Component是通用的注解,而@Repository用于持久层。引用[4]也提到这四个注解位于同一个包下,说明它们都是用来标注不同层次的组件。引用[5]中有具体的代码示例,展示@Service和@Repository的使用,可能可以从中提取示例。 接下来,我需要明确@Repository在Spring MVC中的作用。根据Spring框架的设计,@Repository通常用于数据访问层(DAO层),用来标记类作为数据访问对象。它的主要功能包括组件扫描和异常转换。Spring会自动扫描带有@Repository的类,并将其注册为Bean。此外,@Repository还能将特定于持久层的异常(如JDBC异常)转换为Spring的统一DataAccessException,方便异常处理。 在示例部分,引用[5]中的代码展示了@Service的使用,但用户需要的是@Repository的示例。我可以构造一个简单的DAO实现类,使用@Repository注解,并注入JdbcTemplate来操作数据库。例如: ```java @Repository public class UserDaoImpl implements UserDao { @Autowired private JdbcTemplate jdbcTemplate; public User getUserById(Long id) { String sql = "SELECT * FROM users WHERE id = ?"; return jdbcTemplate.queryForObject(sql, new Object[]{id}, User.class); } } ``` 这样既展示了注解的使用,也说明了如何与数据库交互。同时需要提到,为了确保组件扫描生效,需要在Spring配置中添加包扫描路径,例如使用<context:component-scan>或者在Java配置类中使用@ComponentScan。 关于功能部分,需要强调@Repository除了作为组件注册外,还提供的异常转换特性。这点可能用户不太清楚,需要详细说明,比如在数据访问过程中抛出的原生异常会被Spring的异常转换机制处理,避免代码中捕获特定异常,提高可维护性。 用户可能还会混淆@Repository与其他注解的区别,比如@Service和@Controller,所以可以简要提到它们的适用场景:@Service用于服务层,@Controller用于控制层,而@Repository专用于数据访问层。不过根据用户的问题,这部分可能不需要展开,但可以作为相关问题提出。 最后,按照用户要求生成相关问题,需要围绕@Repository的用途、与其他注解的区别、配置方式以及异常处理机制来设计问题。例如: 1. @Repository和其他注解如@Service的主要区别是什么? 2. 如何配置Spring以正确扫描@Repository注解的类? 3. @Repository如何处理数据访问层的异常? 需要确保回答结构清晰,分点说明,并正确引用提供的资料中的信息,比如引用[1]和[4]提到的注解关系,引用[5]中的代码结构作为示例参考。同时,注意使用中文回答,数学公式部分这里可能用不到,但需要遵循系统指令的其他要求,如LaTeX格式等,但用户的问题不涉及数学内容,所以可能不需要处理这部分。</think>### Spring MVC中@Repository注解使用说明 #### 一、功能概述 1. **组件注册** 被`@Repository`注解的类会被Spring自动扫描并注册为Bean,通常用于**数据访问层(DAO层)**[^1][^4]。 2. **异常转换** 自动将数据访问层抛出的原生异常(如JDBC异常)转换为Spring的统一异常`DataAccessException`,简化异常处理[^4]。 --- #### 二、使用示例 ```java // 定义DAO接口 public interface UserDao { User getUserById(Long id); } // 实现类使用@Repository注解 @Repository public class UserDaoImpl implements UserDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public User getUserById(Long id) { String sql = "SELECT * FROM users WHERE id = ?"; return jdbcTemplate.queryForObject(sql, new Object[]{id}, User.class); } } ``` ##### 关键点: 1. **配置组件扫描** 在Spring配置文件中启用包扫描: ```xml <context:component-scan base-package="com.example.dao" /> ``` 或通过Java配置类: ```java @Configuration @ComponentScan("com.example.dao") public class AppConfig {} ``` 2. **依赖注入** 在其他组件(如`@Service`类)中通过`@Autowired`注入DAO层Bean: ```java @Service public class UserService { @Autowired private UserDao userDao; } ``` --- #### 三、与其他注解的区别 | 注解 | 适用层 | 主要功能 | |--------------|-------------|-----------------------------------| | `@Repository`| 数据访问层 | 数据操作、异常转换 | | `@Service` | 业务逻辑层 | 定义服务类(如事务管理)[^2][^5] | | `@Controller`| 控制层 | 处理HTTP请求[^2] | | `@Component` | 通用组件 | 非特定层时使用[^4] | --- #### 四、典型应用场景 - 数据库操作(如JdbcTemplate、MyBatis Mapper) - NoSQL数据访问(如MongoDB、Redis) - 事务管理(结合`@Transactional`注解---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值