spring的事务

今天学习了以下spring boot的事务,发现真他娘的方便阿。我可能就是典型的那种打死也不吃,最后来一句真香的那种人了。越看spring boot越觉得好用。

在spring boot使用事物非常简单。

前提:你的数据引擎是支持事务的。

首先在启动类要加上

@EnableTransactionManagement,从而支持事物。

然后在Service层使用

@Transactional

该注解可以使用在类上,这样就是所有方法都是使用事务。同时也可以使用在方法上。

只是有一点要注意,使用了事务注解,就不要在使用try.....catch捕获异常了,否则事物是不起作用的。

@Transactional
public boolean insert2Db(Order order){

 insert1;
 insert2;
 update3;
 ......
}

如果其中一个失败,就会回滚。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。分割线。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

今天在测试事务的时候,遇到了几个坑,当我显示添加一个错误的时候,事务并没有回滚。看例子:

@Service
public class SAPServiceImpl implements SAPService {


    public static Logger logger = LoggerFactory.getLogger(SAPServiceImpl.class.getName());

    public static final String post_url = "http://test.com";


    @Autowired
    FinSAPBatchMapper finSAPBatchMapper;

    @Autowired
    FinSAPMainMapper finSAPMainMapper;

    @Autowired
    FinSAPDetailMapper finSAPDetailMapper;



  public boolean dealRespFromSAP(SAPData sapData,Map<String,String> response){
        logger.info("reponse from miora",response);
        System.out.println(Arrays.toString(response.entrySet().toArray()));
        if (response == null){
            System.out.print("Response from miora is null");
            return  false;
        }
        switch (response.get("code")){
            case "":
                System.out.print("Response from mioracle is empty");
                return false;

            case  "200":
                //TODO 处理响应,更新数据库状态
                Integer status = 1;
                try {
//                    if (this.updateDbStatus(sapData, status)) {
                    if (this.updateDbStatus(sapData, status)) {
                        System.out.print("Update Ok!");
                        return true;
                    } else {
                        System.out.print("Updat 失败");
                        return false;
                    }
                }catch (Exception e){
                    logger.error(e.getMessage() + "testtransction");
                }

            default:
                String code = response.get("code");
                String desc = response.get("desc");
                System.out.print("Code:" + code + " desc:"+desc);
                return false;
        }
    }


 @Transactional
 public boolean updateDbStatus(SAPData sapData,Integer status) {
        System.out.println("Start to update status in database");
        String cur_date_time = "";

//        try {
//            finSAPBatchMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
//            finSAPMainMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
//            return true;
//        }catch (Exception e){
//            System.out.println("update error :" + e.getMessage());
//            return  false;
//        }
    }

这是第一版本的代码,我发现当我第二个数据库操作错误的时候,事务并不起作用。后来查阅代码首先发现了一个错误:

1、不能用catch捕获异常,否则检测不到异常,事务也不会回滚。

然后我就把代码改了。

@Transactional(rollbackFor=Exception.class)    
public boolean updateDbStatus(SAPData sapData,Integer status) {
        System.out.println("Start to update status in database");
        String cur_date_time = "";
        finSAPBatchMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
        finSAPMainMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
        return true;

但时,发现还是不起作用,接着查资料,发现了不能在同一个service类中一个非事务方法调用事务方法,这样不起作用。具体原理可见: service内部类的调用 。 另一篇好的总结: spring boot的坑

在网上有很多办法,但我试了一下都不好使,比如在service标签上添加Transactional,这样就会每调用一个,都会开启一个事务,但不知道怎么回事,就是不管用啊。

后来,查到一个方法:不要使用实例调用,应该直接使用接口调用。即不要用this,methoA,应该用 service.methodA(),当然,前提是必须是接口方法啊。你得首先定义好接口方法。

首先自动注入一个service.

  @Autowired
    private SAPService sapService;

然后,在调用的时候是这样:

 try {
                    if (sapService.updateDbStatus(sapData, status)) {
                        System.out.print("Update Ok!");
                        return true;
                    } else {
                        System.out.print("Updat 失败");
                        return false;
                    }
                }catch (Exception e){
                    logger.error(e.getMessage() + "testtransction");
                }

改好之后,再测试,事务生效了。

如果想在事务方法中catch异常,一定要手动rollback:

    @Transactional(rollbackFor = Exception.class)
    public boolean updateDbStatus(SAPData sapData,Integer status) {
        System.out.println("Start to update status in database");
        String cur_date_time = "";
        try {
            finSAPBatchMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
            finSAPMainMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
            return true;
        }catch (Exception e){
            System.out.println("update error :" + e.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return  false;
        }
    }

此外,有一些还要注意:比如,使用事务的方法必须是public。

其实主要原因是spring的事务通过AOP代理来实现的,使用Transaction注解的,就会进行事务拦截。

如果在类内部调用不会走代理,也就导致代理失效,事务无法使用。

我去,这一下来,这个框架限制还挺多,使用php的laravel的框架时候,事务随便用啊!静态语言适合大型项目,更系统化,但灵活度和php,python等动态语言比,还是有劣势的。所以说,语言只是工具,工具就注定了每一种语言都不是万能的,都会有各自的优势和劣势。

参考资料:

深入理解spring的transaction

--------EOF---------
微信分享/微信扫码阅读