你真的会用Spring事务@Transactional吗?-创新互联

你真的会用 Spring事务@Transactional 吗?
  • 经典错误案例
    • 错误的使用try catch
    • 抛出检查异常
    • 错误添加切面
    • @Transactional事务默认只能加在public的方法上
    • 调用本类方法导致传播行为失效
    • Transactional并没有保证原子性

  在Spring项目中,我们经常使用注解@Transactional来标识一个事务方法。但是,有时会发现这个事务并不是按照我们想要的方式执行。通常在以下几种情况下,你以为的事务管理可能并不是你认为的事务管理。

创新互联公司主要为客户提供服务项目涵盖了网页视觉设计、VI标志设计、成都全网营销推广、网站程序开发、HTML5响应式网站建设公司手机网站开发、微商城、网站托管及成都网站改版、WEB系统开发、域名注册、国内外服务器租用、视频、平面设计、SEO优化排名。设计、前端、后端三个建站步骤的完善服务体系。一人跟踪测试的建站服务标准。已经为成都主动防护网行业客户提供了网站维护服务。经典错误案例 错误的使用try catch

  在业务方法中使用try catch来捕获了异常,然后异常出现,会进入到catch块中,但是没有进行抛出,结果事务没有被正确的回滚。这里没有正确回滚的原因就在于,没有正确的理解Spring事务管理的原理。Spring采用了代理的方式来实现事务管理,调用的顺序是开启事务,执行目标方法,提交或回滚事务。所以虽然目标方法中出现了异常,但是却将异常直接处理了,在代理类眼中,目标方法没有抛出异常,所以事务也就正常提交。

@Transactional(rollbackFor = Exception.class)
    public void transfer(int amount) {
        try{
            serviceB.sub(amount);
            serviceA.add(amount);
        } catch (Exception e) {
            LOGGER.error("error occur");
        }
    }

  正确的解决方法有两种,一种是直接在catch中继续抛出异常,第二种就是直接告诉Spring当前事务需要rollback。

// 1
throw new RuntimeException(e);
// 2
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
抛出检查异常

  在方法中若是抛出检查异常,比如fileNotFound这种,事务则是不会回滚的,这其中的原因就在于@Transactional 注解默认的是rollbackFor是运行期异常。这也就是在阿里的开发规范中要求一定要指定rollbackFor的原因。
所以在使用这个注解的时候还是建议写明 rollbackFor 这样才能明确知道出现了什么异常才会回滚的。

@Transactional(rollbackFor = Exception.class)
错误添加切面

  Aop切面顺序导致事务不能正确回滚,原因是事务切面优先级最低,但如果自定义的切面优先级和它一样,则还是自定义切面在内层,这是若自定义切面没有正确抛出异常,在catch中吃掉了异常,此时就会出现和第一种情况类似的情况,代理类得不到异常信息,也就不会回滚。
  解决方案就是在切面中抛出异常,或者就是将自定义的切面的优先级设置为更小。但是建议使用第一种在切面中抛出异常。

@Transactional事务默认只能加在public的方法上

  非public的方法会导致事务失效。Spring为方法创建代理,添加事务通知,前提条件是该方法是public的,这点需要注意的就是要么设置方法为public,要么可以为非public的方法添加事务通知。
建议使用public方法而不是添加配置

@Bean
    public TransactionAttributeSource transactionAttributeSource(){
        return new AnnotationTransactionAttributeSource(false);
    }
调用本类方法导致传播行为失效

  同一个Service的两个方法之间调用,就会出现这个问题,原因还是在代理对象这里,我们期待的调用时一个代理类的调用,但是我们若是直接在方法中内部调用,则被调用的方法的事务失效,没有被Aop增强。

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void a (){
        b();
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void b (){
        
    }

  以上的改进方案就是自己调用自己,自己注入自己。你可能看见过这样的代码,就是为了解决这个问题的。这种解决方案最常见。

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES)
    public void a (){
        service.b();
    }

  还有一种方法可以通过 AopContext 拿到代理对象,然后再调用。这里要注意,使用这种方式需要开启暴露代理。

@EnableAspectJAutoProxy(exposeProxy = true)
public class Application {}

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void a (){
 ((TestService)AopContext.currentProxy()).b();
}
Transactional并没有保证原子性

  这个问题非常常见,我们总会以为加了事务管理,尤其是加了 Propagation.REQUIRES_NEW 之后我们的事务就会在方法执行之后提交事务,或是加了 synchronized 关键字之后,这就是一个原子操作了,这是不对的哈。为什么呢?还要从代理类说起,代理类开启事务,执行目标方法,提交事务。而不管是 REQUIRES_NEW 还是 synchronized 关键字都只是作用于目标方法,即使目标方法执行成功,可是事务还是没有提交呢。

  对于 insert,delete,update,select — for update,语句来说,都是原子性的。但是 select 不是。

  解决方案就是扩大 synchronized 的范围,为整个代理方法加锁,而不是把锁加在目标方法上。也可以通过 SQL 来控制,保证操作的原子性,使用 select — for update

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧

网页名称:你真的会用Spring事务@Transactional吗?-创新互联
网站网址:https://www.cdcxhl.com/article12/dgecdc.html

成都网站建设公司_创新互联,为您提供移动网站建设网站导航网站营销标签优化网页设计公司网站建设

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

成都定制网站网页设计