redis结合spring的事务使用

导读:本篇文章讲解 redis结合spring的事务使用,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

最近有一个需求,一个接口需要去包另外的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 属性执行

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

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

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

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/6482.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!