事务使用中常见的坑

目录

一、事务的特性

二、常见使用

三、常见的注意问题

1. 事务操作中存在查询或者循环等耗时操作

2. 事务中发送消息

3. 事务注解在非public方法上 

4. 调用同一个类中的方法事务会失效


一、事务的特性

        事务简单来说是一串连续的原子性操作,事务具有以下特点:

        ●原子性(A),事务中的操作要么全部完成,要不就全部失败。

        ●隔离性(I),数据库允许事务并发访问数据库,隔离是为了防止多个事务对同一行数据的交叉修改引起的不一致。根据可忍受不一致的范围,目前的隔离级别有:

        ○未提交读,有脏读风险。

        ○已提交读,有不可重复读的风险。

        ○可重复读,有幻读的风险。

        ○串行化,最严格,但是会大大降低性能。

        ● 一致性(C),事务开始前,待变更行都是老数据,事务结束之后,变更行都是新数据,不会存在新老数据混杂的情况。

        ● 持久性(D),事务结束后,修改对于数据库来说是永久的,如果事务执行途中遇到数据库 crash,重启后数据也是正确的。

二、常见使用

        通常我们在系统开发时会遇见很多事务场景,而且Spring框架也提供了很好事务管理机制,主要分为编程式事务和声明式事务两种。

        编程式事务:是指在代码中手动的管理事务的提交、回滚等操作,代码侵入性比较强,且会存在较多的重复代码,如下示例:

try {    
    //TODO something     
    transactionManager.commit(status);
} catch (Exception e) {    
    transactionManager.rollback(status);    
    throw new InvoiceApplyException("异常失败");
}

        声明式事务:基于AOP面向切面开发,它将具体业务与事务处理部分解耦,代码侵入性低,所以在实际开发中声明式事务用的比较多。声明式事务也有两种实现方式,一是基于TX和AOP的xml配置文件方式,二种就是基于@Transactional注解了,通常都是使用注解进行事务管理。如下所示:

//可在注解属性中指定回滚的异常类,超时时间,是否为只读事务,事务隔离级别以及具体某个事务管理器
@Transactional(rollbackFor = Exception.class,timeout = 3000,transactionManager = "dynamicManager", propagation = Propagation.SUPPORTS, readOnly=false)
public String test() {            
    int insert = cityInfoDictMapper.insert(cityInfoDict);   
}

        @Transactional 可以作用在接口、类、类方法:   

  • 作用于类:当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息。
  • 作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。
  • 作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效
     

三、常见的注意问题

1. 事务操作中存在查询或者循环等耗时操作

        事务其实类似于多线程中的加锁,在事务开始时会将部分资源进行冻结锁定,应该尽量将事务操作的执行时间缩短,像循环耗时或者查询这种与事务无关的操作都应该放在事务外进行;

2. 事务中发送消息

        事务中发送消息其实是一个危险行为,抛开耗时问题,当消息发送成功后若事务提交失败则会造成脏消息,因为一般事务操作才是主业务,MQ消息是可人工补偿的分支业务,所以推荐做法是利用spring提供的事务同步管理器,在事务提交后再去进行消息发送:

@Transactional
public void testMQ(String var1){
    //insert
    //update
    mqHelper.sendMsg();
}

public void sendMsg(){
  //判断是否开启了事务
  if (TransactionSynchronizationManager.isSynchronizationActive()){
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    //sendMsg
                }
            });
        }else {
            //sendMsg
        }

    }

3. 事务注解在非public方法上 

        如果Transactional注解应用在非public 修饰的方法上,Transactional将会失效。之所以会失效是因为在Spring AOP 代理时,底层 TransactionInterceptor (事务拦截器)会在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用AbstractFallbackTransactionAttributeSource的
computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息,该方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

        private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
            if (this.allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
                return null;

4. 调用同一个类中的方法事务会失效

         在日常开发中我们经常会遇到方法嵌套的情况,比如一个service类中包含methodA方法和methodB方法,methodA调用了methodB方法,这个时候methodB方法上就算添加了事务注解也没用,因为Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理,而调用同一个类中的方法是使用的this指针调用,this是原有对象,所以事务会失效。通用的解决方案是分裂编写,将methodB抽离至其他类,然后注入调用。

  private Integer A() throws Exception {
        this.insertB();
        /**
         * 插入数据
         */
        insert = xxxDao.insert(Object);
        return insert;
    }
 
    @Transactional()
    public Integer insertB() throws Exception {
        xxxDao.insert(Object);
    }

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值