redis结合spring的事务使用

最近有一个需求,一个接口需要去包另外的3个接口,接口之间的数据相互联系相互依赖,如果有一个接口发生异常或者在主逻辑之中发生异常,那么所有此次产生的数据变化都需要回滚。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

因为考虑到异常时候需要数据回滚,所以第一时间肯定是想到事务。

但是在实际操作中会用到3张表。比如A,B,C。

@Transactional(propagation = Propagation.REQUIRED)
public void all(){
    A();   //操作A表
    B();   //操作B表,B表会用到A表的主键
    C();   //操作C表, C表会用到B表和A表的主键
}

1。更改数据库隔离级别(否定的做法)

当有数据插入到A表中,B可能会用到A中刚插入的新数据,但是由于各事务之间虽然是独立的,但是包在了一个大的事务中,因此各事务之间又是一致性的,所以读不到其他事务的未提交数据,如果把隔壁级别设置为读未提交的数据的话,会造成脏读,不可重复读和幻像读。因此这种做法就被排除了。

2。用redis进行insert的数据存储。

由于spring在执行方法时,虽然事务执行完成还未commit,但是你如果这时候去数据库查询主键你会发现,虽然数据没有,但是主键已经被改变(比如原始的A表主键为10。这时候运行了A的方法以后,会对A表进行insert操作,断点断在B的方法上,当运行到B方法时,我们去A表查看记录,由于spring还未commit,因此A表肯定没有记录,但是A表的主键一定是大于10的。在事务未提交时,数据应该是存储在内存中(未提交数据存储的地方只是别人说的,未验证,有心的朋友可以自己查下相关资料))。如果发生异常进行数据回滚时,主键会变回操作之前的主键。

在总结了上述2个方法之后,最终的定论肯定是用redis的方法。

但是spring的事务支持回滚,redis的数据不支持回滚,如果数据越来越多,那么redis岂不是存储的数据量越来越大了。

针对redis无法进行事务回滚的情况,需要自己去设定逻辑实现回滚。

@Transactional
    public void executeAllVoid(){
        List<String> A = null;
        List<String> B = null;
        try {
            A= voidA();
            B= voidB();
            voidC();
        }catch (Exception e){
            e.printStackTrace();
            logger.error("系统错误:",e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }finally {
            if(A != null) {
                redisTemplate.opsForHash().delete(keyA, A.toArray());
            }
            if(B!= null){
                redisTemplate.opsForHash().delete(keyB, B.toArray());
            }
        }
    }

在操作A方法和B方法时,如果有异常对异常进行向上抛出,到达最外层的方法时,进行try catch捕获异常。当捕获当异常时,由于redis设置的key我们是可知的,可以存到本地session,可以以参数的形式返回等等。所以我们在catch到异常的时候,进行手动redis的删除操作来以此做到redis的假回滚状态。因为spring事务中,如果catch到异常以后spring的事务是无法生效的,所以我们需要进行手动回滚或者抛出运行时异常来告诉spring回滚刚才的操作。

try{
}catch(Exception e){
    throw new RuntimeException();
}

try{
}catch(Exception e){
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}

上述代码方法二选其一就可以。

 

关于spring事务的延申,我们都知道spring的事务有7种传播特性,这里就不一一举例了。主要有3种会造成混淆。

1。PROPAGATION_REQUIRED: 如果存在一个事务,则加入当前事务。如果没有事务则开启 。

2。PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。 

3.PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行

第一种事务时默认的事务传播特性,本文中的所有事务都是基于这个,大白话说就是   有一个父类的方法,子类的方法要在父类的方法中执行,但是子类和父类之间不能相互独立,父类或者任何一个子类发生异常了那么都需要回滚。

第二种事务和第一种事务恰恰相反。各事务之间时相互独立的,   父类的方法中有许多的子类方法,父类或者任何一个子类的方法发生异常,只会对发生异常的方法进行回滚,其他的方法不受到他的影响。

第三种事务是一个级别性的关系。     如果父类的方法发生异常了,那么嵌套在父类方法的所有子方法都会发生回滚,但是如果是一个子方法发生了异常,父类和其他的子类方法都不会受到异常的影响去回滚数据信息。

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值