• 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注解,然后调用才起到异步的作用。

  • 相关阅读:
    Java 分布式系统 实现session共享
    MySQL 大数据量使用limit分页,随着页码的增大,查询效率越低下。
    Linux下安装Zookeeper
    Mysql Window 下安装
    Spring Boot 教程demo
    全文搜索引擎 Elasticsearch (三)logstash-input-jdbc同步数据 到elasticsearch
    全文搜索引擎 Elasticsearch (二) 使用场景
    67.基于nested object实现博客与评论嵌套关系
    66.基于共享锁和排他锁实现悲观锁并发控制
    65.基于document锁实现悲观锁并发控制
  • 原文地址:https://www.cnblogs.com/lqmblog/p/8565115.html
Copyright © 2020-2023  润新知