spring注解事务使用总结

在使用spring的注解事务的时候,需要考虑到事务的传播行为、遇到什么类型的异常时,事务才起作用、事务方法之间的嵌套调用时,怎么样才生效等等诸多问题。网上搜到很多的主要还是一堆理论文字描述,我这里给出亲测的代码,是借助公司真实的系统来做测试。

 

系统之间调用图如下:

 

事务和异步处理都在server模块里面。 

 

 接口如下:

Java代码
  1. /** 
  2.  * 测试事务行为接口 
  3.  *  
  4.  * @author plg 
  5.  * 
  6.  */  
  7. public interface TestService {  
  8.   
  9.     public void methodA();  
  10.   
  11.     public void methodB();  
  12.   
  13.     public void methodC();  
  14.       
  15.     .  
  16.     .  
  17.     .  
  18. }  

 

 使用两个表:

Sql代码
  1. CREATE TABLE `mall_order_statistics` (  
  2.   `id` bigint(20) NOT NULL AUTO_INCREMENT,  
  3.   `shop_id` bigint(20) NOT NULL,  
  4.   `order_num` bigint(20) NOT NULL,  
  5.   `order_amount` bigint(20) NOT NULL,  
  6.   `avg_order_amount` bigint(20) NOT NULL,  
  7.   `pay_type` int(11) NOT NULL,  
  8.   `order_type` int(11) NOT NULL',  
  9.   `order_date` date NOT NULL,  
  10.   `create_time` datetime NOT NULL,  
  11.   PRIMARY KEY (`id`),  
  12.   KEY `idx_shop_id` (`shop_id`)  
  13. ) ENGINE=InnoDB  
  14.   
  15.   
  16. CREATE TABLE `mall_goods_ranking` (  
  17.   `id` bigint(20) NOT NULL AUTO_INCREMENT,  
  18.   `shop_id` bigint(20) NOT NULL,  
  19.   `order_date` date NOT NULL,  
  20.   `mall_goods_id` bigint(20) NOT NULL,  
  21.   `goods_name` varchar(20) NOT NULL,  
  22.   `sales_volume` bigint(20) NOT NULL,  
  23.   `sales_amount` bigint(20) NOT NULL,  
  24.   `create_time` datetime NOT NULL,  
  25.   PRIMARY KEY (`id`),  
  26.   KEY `idx_shop_id` (`shop_id`)  
  27. ) ENGINE=InnoDB  

 

1.事务与异常类型

这里用的是注解式事务@Transactional。

1.1 正常的处理

        先来一个正常的处理,事先已把数据库表清空,代码调用如下:

 

Java代码
  1. web端代码如下:  
  2. try {  
  3.     testService.methodA();  
  4. catch (Exception e) {  
  5.     logger.error("========================= " + e);  
  6. }  
  7.   
  8. server端代码如下:  
  9. @Transactional  
  10. @Override  
  11. public void methodA() {  
  12.     MallGoodsRanking mallGoodsRanking = new MallGoodsRanking();  
  13.     mallGoodsRanking.setShopId(1L);  
  14.     mallGoodsRanking.setOrderDate(new Date());  
  15.     mallGoodsRanking.setMallGoodsId(1L);  
  16.     mallGoodsRanking.setGoodsName("测试");  
  17.     mallGoodsRanking.setSalesVolume(1L);  
  18.     mallGoodsRanking.setSalesAmount(1L);  
  19.     mallGoodsRanking.setCreateTime(new Date());  
  20.     mallGoodsRankingMapper.insert(mallGoodsRanking);  
  21.   
  22.     MallOrderStatistics mallOrderStatistics = new MallOrderStatistics();  
  23.     mallOrderStatistics.setShopId(1L);  
  24.     mallOrderStatistics.setOrderNum(1L);  
  25.     mallOrderStatistics.setAvgOrderAmount(1L);  
  26.     mallOrderStatistics.setOrderAmount(1L);  
  27.     mallOrderStatistics.setPayType(1);  
  28.     mallOrderStatistics.setOrderType(1);  
  29.     mallOrderStatistics.setOrderDate(new Date());  
  30.     mallOrderStatistics.setCreateTime(new Date());  
  31.     mallOrderStatisticsMapper.insert(mallOrderStatistics);  
  32. }  

 执行结果:



 

 

1.2异常处理-不插入必填字段,抛出RuntimeException

 

Java代码  
  1. @Transactional  
  2. @Override  
  3. public void methodA() {  
  4.     MallGoodsRanking mallGoodsRanking = new MallGoodsRanking();  
  5.     mallGoodsRanking.setShopId(1L);  
  6.     mallGoodsRanking.setOrderDate(new Date());  
  7.     mallGoodsRanking.setMallGoodsId(1L);  
  8.     mallGoodsRanking.setGoodsName("测试");  
  9.     mallGoodsRanking.setSalesVolume(1L);  
  10.     mallGoodsRanking.setSalesAmount(1L);  
  11.     mallGoodsRanking.setCreateTime(new Date());  
  12.     mallGoodsRankingMapper.insert(mallGoodsRanking);  
  13.   
  14.     MallOrderStatistics mallOrderStatistics = new MallOrderStatistics();  
  15.     // mallOrderStatistics.setShopId(1L);必填字段  
  16.     mallOrderStatistics.setOrderNum(1L);  
  17.     mallOrderStatistics.setAvgOrderAmount(1L);  
  18.     mallOrderStatistics.setOrderAmount(1L);  
  19.     mallOrderStatistics.setPayType(1);  
  20.     mallOrderStatistics.setOrderType(1);  
  21.     mallOrderStatistics.setOrderDate(new Date());  
  22.     mallOrderStatistics.setCreateTime(new Date());  
  23.     mallOrderStatisticsMapper.insert(mallOrderStatistics);  
  24. }  

 如上面的代码,把第二条插入语句的一个必填字段注释掉,这样执行之后,抛出了异常:

Java代码 
  1. ; SQL []; Column 'shop_id' cannot be null; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'shop_id' cannot be null, dubbo version: 2.8.4, current host: 192.168.9.141  
  2. org.springframework.dao.DataIntegrityViolationException:  

 抛出了DataIntegrityViolationException类型的异常,这个异常是RuntimeException类型的异常,spring的@Transactional默认就是捕捉RuntimeException异常,遇到RuntimeException异常才会处理事务回滚。

  既然抛出了RuntimeException异常,那数据库自然没有插入成功了。接下来演示抛出Exception异常,看事务能否生效。

 

1.3异常处理-不插入必填字段,抛出Exception

 

Java代码
  1. @Transactional  
  2. @Override  
  3. public void methodA() throws Exception {  
  4.     try {  
  5.         MallGoodsRanking mallGoodsRanking = new MallGoodsRanking();  
  6.         mallGoodsRanking.setShopId(1L);  
  7.         mallGoodsRanking.setOrderDate(new Date());  
  8.         mallGoodsRanking.setMallGoodsId(1L);  
  9.         mallGoodsRanking.setGoodsName("测试");  
  10.         mallGoodsRanking.setSalesVolume(1L);  
  11.         mallGoodsRanking.setSalesAmount(1L);  
  12.         mallGoodsRanking.setCreateTime(new Date());  
  13.         mallGoodsRankingMapper.insert(mallGoodsRanking);  
  14.   
  15.         MallOrderStatistics mallOrderStatistics = new MallOrderStatistics();  
  16.         // mallOrderStatistics.setShopId(1L);必填字段  
  17.         mallOrderStatistics.setOrderNum(1L);  
  18.         mallOrderStatistics.setAvgOrderAmount(1L);  
  19.         mallOrderStatistics.setOrderAmount(1L);  
  20.         mallOrderStatistics.setPayType(1);  
  21.         mallOrderStatistics.setOrderType(1);  
  22.         mallOrderStatistics.setOrderDate(new Date());  
  23.         mallOrderStatistics.setCreateTime(new Date());  
  24.         mallOrderStatisticsMapper.insert(mallOrderStatistics);  
  25.     } catch (Exception e) {  
  26.                 logger.error(" ================== " + e);  
  27.                 throw new Exception("异常");  
  28.     }  
  29. }  
捕捉异常后,然后 throw new Exception。执行后,抛出异常:

 

Java代码 
  1. java.lang.Exception: 异常  
  2.     at yunnex.saofu.mall.service.impl.TestServiceImpl.methodA(TestServiceImpl.java:55)  
  3.     at yunnex.saofu.mall.service.impl.TestServiceImpl$$FastClassBySpringCGLIB$$8fcf06b.invoke(<generated>)  
  4.     at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)  
  5.     at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717)  
执行结果:

 

 看到了吗,第一条SQL还是插入成功了,第二条语句插入出错,抛出Exception异常,但是事务并没有回滚。 接下来测试一下@Transactional(rollbackFor=Exception.class)这种情况。
 

1.4异常处理-不插入必填字段,抛出Exception,@Transactional指定异常类型

             由于篇幅问题,这里补贴代码了,和1.3不同的就是事务注解改成:@Transactional(rollbackFor = Exception.class),运行之后,事务生效:


 

 数据库表没有插入数据。
        对于service方法中使用注解事务,要么service中的方法中不做异常捕获,要么捕捉异常后throw new RuntimeException(),这样出现异常时,都会使事务回滚。如果需要捕捉特定类型的异常来回滚事务,则需要用@Transactional(rollbackFor=特定类型异常.class)来指定。
 =================================================================================================================================
 

2.事务方法嵌套使用

         这里在引入一个表(mall_config),这个表原先是空的。

 

 2.1事务方法调用私有方法,私有方法有数据库操作,私有方法抛出异常

 

Java代码
  1. @Transactional  
  2. @Override  
  3. public void methodA() throws Exception {  
  4.   
  5.     MallConfig mallConfig = new MallConfig();  
  6.     mallConfig.setCanFetch(true);  
  7.     mallConfig.setCanDeliver(true);  
  8.     mallConfigMapper.insert(mallConfig);  
  9.   
  10.     this.insert();  
  11. }  
  12.   
  13. private void insert() {  
  14.     MallGoodsRanking mallGoodsRanking = new MallGoodsRanking();  
  15.     mallGoodsRanking.setShopId(1L);  
  16.     mallGoodsRanking.setOrderDate(new Date());  
  17.     mallGoodsRanking.setMallGoodsId(1L);  
  18.     mallGoodsRanking.setGoodsName("测试");  
  19.     mallGoodsRanking.setSalesVolume(1L);  
  20.     mallGoodsRanking.setSalesAmount(1L);  
  21.     mallGoodsRanking.setCreateTime(new Date());  
  22.     mallGoodsRankingMapper.insert(mallGoodsRanking);  
  23.   
  24.     MallOrderStatistics mallOrderStatistics = new MallOrderStatistics();  
  25.     // mallOrderStatistics.setShopId(1L);必填字段  
  26.     mallOrderStatistics.setOrderNum(1L);  
  27.     mallOrderStatistics.setAvgOrderAmount(1L);  
  28.     mallOrderStatistics.setOrderAmount(1L);  
  29.     mallOrderStatistics.setPayType(1);  
  30.     mallOrderStatistics.setOrderType(1);  
  31.     mallOrderStatistics.setOrderDate(new Date());  
  32.     mallOrderStatistics.setCreateTime(new Date());  
  33.     mallOrderStatisticsMapper.insert(mallOrderStatistics);//这里抛出异常  
  34. }  

 

执行结果:

 

 

 

 

 

 数据没有插入,事务生效!在同一个类中,一个事务方法调用私用方法,私用方法里有对数据库的操作,私有方法里有异常(RuntimeException)产生,这种情况事务是起效果的。

 

 

 2.2 事务方法调用私有方法,私有方法有数据库操作,事务方法抛出异常

 

Java代码 
  1. @Transactional  
  2. @Override  
  3. public void methodA() throws Exception {  
  4.   
  5.     MallConfig mallConfig = new MallConfig();  
  6.     mallConfig.setCanFetch(true);  
  7.     mallConfig.setCanDeliver(true);  
  8.     mallConfigMapper.insert(mallConfig);  
  9.   
  10.     this.insert();  
  11.   
  12.         //第三部分  
  13.     MallOrderStatistics mallOrderStatistics = new MallOrderStatistics();  
  14.     // mallOrderStatistics.setShopId(1L);必填字段  
  15.     mallOrderStatistics.setOrderNum(1L);  
  16.     mallOrderStatistics.setAvgOrderAmount(1L);  
  17.     mallOrderStatistics.setOrderAmount(1L);  
  18.     mallOrderStatistics.setPayType(1);  
  19.     mallOrderStatistics.setOrderType(1);  
  20.     mallOrderStatistics.setOrderDate(new Date());  
  21.     mallOrderStatistics.setCreateTime(new Date());  
  22.     mallOrderStatisticsMapper.insert(mallOrderStatistics);  
  23. }  
  24.   
  25.   
  26. private void insert() {  
  27.     MallGoodsRanking mallGoodsRanking = new MallGoodsRanking();  
  28.     mallGoodsRanking.setShopId(1L);  
  29.     mallGoodsRanking.setOrderDate(new Date());  
  30.     mallGoodsRanking.setMallGoodsId(1L);  
  31.     mallGoodsRanking.setGoodsName("测试");  
  32.     mallGoodsRanking.setSalesVolume(1L);  
  33.     mallGoodsRanking.setSalesAmount(1L);  
  34.     mallGoodsRanking.setCreateTime(new Date());  
  35.     mallGoodsRankingMapper.insert(mallGoodsRanking);  

 

 看以上代码,只有第三部分那里抛出异常,执行结果和2.1一样如下。在同一个类里面,一个事务方法里调用私有方法,相当于代码都写在事务方法里。

 

2.3 接口方法调用私有方法,私有方法添加事务注解

 

Java代码
  1. @Override  
  2. public void methodA() throws Exception {  
  3.   
  4.     MallConfig mallConfig = new MallConfig();  
  5.     mallConfig.setCanFetch(true);  
  6.     mallConfig.setCanDeliver(true);  
  7.     mallConfigMapper.insert(mallConfig);  
  8.   
  9.     this.insert();  
  10. }  
  11.   
  12.   
  13. @Transactional  
  14. private void insert() {  
  15.     MallGoodsRanking mallGoodsRanking = new MallGoodsRanking();  
  16.     mallGoodsRanking.setShopId(1L);  
  17.     mallGoodsRanking.setOrderDate(new Date());  
  18.     mallGoodsRanking.setMallGoodsId(1L);  
  19.     mallGoodsRanking.setGoodsName("测试");  
  20.     mallGoodsRanking.setSalesVolume(1L);  
  21.     mallGoodsRanking.setSalesAmount(1L);  
  22.     mallGoodsRanking.setCreateTime(new Date());  
  23.     mallGoodsRankingMapper.insert(mallGoodsRanking);  
  24.   
  25.     MallOrderStatistics mallOrderStatistics = new MallOrderStatistics();  
  26.     // mallOrderStatistics.setShopId(1L);必填字段  
  27.     mallOrderStatistics.setOrderNum(1L);  
  28.     mallOrderStatistics.setAvgOrderAmount(1L);  
  29.     mallOrderStatistics.setOrderAmount(1L);  
  30.     mallOrderStatistics.setPayType(1);  
  31.     mallOrderStatistics.setOrderType(1);  
  32.     mallOrderStatistics.setOrderDate(new Date());  
  33.     mallOrderStatistics.setCreateTime(new Date());  
  34.     mallOrderStatisticsMapper.insert(mallOrderStatistics);  
  35. }  

 

 执行结果如下:

 

 

 

 出现异常后,前面两个表还是插入成功了。@Transactional放在private方法上是不起效果的,并且也不报错,spring官网也说明了这一点。实际上,像这种情况,内部调用带有事务注解的public方法,事务也不生效,也就是

 

Java代码 
  1. @Transactional  
  2. public void insert() {  
  3.   
  4. 改成  
  5.   
  6. @Transactional  
  7. private void insert() {  

 具体原因请查看:http://blog.csdn.net/seelye/article/details/40144817。

 

 方法内部调用带有事务注解的方法(无论是private还是public),事务是不生效的。

spring异步(@Async)处理也是一样,有时候在进行业务处理的过程,有些业务可以作为异步来处理,这时候就有一些同学就在同一类中新建一个方法,使用@Async注解,然后调用这个异步方法,其实这是不起作用的,一定要新建另外一个类,这个类新建公共方法,用@Async注解,然后调用才起到异步的作用。

posted @   倔强的蒲公英  阅读(650)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示