范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

踩坑Transactional,你能发现问题在哪么?

  前阵子接手了一段代码,里面用到了@transaction注解,了解Spring的小伙伴肯定知道,@Transactional是Spring提供的一种控制事务管理的快捷手段。但是我这段程序在运行的时候,经常出现莫名其妙的问题,连夜研究了好久才搞清楚,在这里记录一下, 避免大家入坑。1. 大家来找茬
  在介绍具体问题之前,我把问题代码简化了一下,看大家能找到其中的问题吗?问题代码1
  下面的这段代码主要是想利用MySQL里面的行锁select for update,来实现简单的分布式锁。但是在实践过程中,发现这个锁好像并没有生效,而且在数据库的里面也没有查找对应transaction连接的信息。@Component @EnableScheduling public class someService {      @Scheduled(...)   public doSomeWork() {     // find some id by logic          // process the related info     doOtherWork(id);   }      @Transactional(isolation = Isolation.READ_COMMITTED)   public void doOtherWork(id) {     Info info = requestMapper.selectByPrimaryKeyForUpdate(id);     doSomeFollowingProcess(info);     ...   } }问题代码2
  下面代码分两个步骤,第一步会检查相关信息,第二步调用了一个transactional修饰的方法,完成一些基本工作;但在实践中,发现一个非常诡异的问题,在MainWork中,doSomeCheck执行时会抛出nullPointException,debug发现所有autowired进来的service均为空,注释掉doSomeCheck里面的内容后,继续往下执行,却发现doWork能够正常执行,所有的注入均没有问题。@Component public class MainWork {   @AutoWired   DetailWork detailWork        public void workflow() {     detailWork.doSomeCheck();     detailWork.doWork();   } }  @Component public class DetailWork {        @AutoWired     UsefulService usefulService;        @AutoWired     InfoService infoService;        @Transactional(isolation = Isolation.READ_COMMITTED)     public void doWork() {       usefulService.doSomeWork();     }        void doSomeCheck() {       infoService.getInfo();     } }
  大伙看看能发现什么问题吗?2. 关于@Transactional注解
  Spring支持编程式事务管理和声明式事务管理两种方式。编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后,根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需基于@Transactional注解的方式,便可以将事务规则应用到业务逻辑中。
  下图是调用@Transactional注解的方法时,Spring内部的时序图。简单来讲就是IOC容器初始化时,会生成@Transactional注解所在类的代理对象,然后实际执行中会通过AOP执行代理对象的方法,TransactionAdvisor会在方法调用前判断是否开启事务,在调用结束后,会判断是否提交或回滚事务。
  深入研究代码,我们会发现TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。protected TransactionAttribute computeTransactionAttribute(Method method,     Class<?> targetClass) {         // Don"t allow no-public methods as required.         if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {         return null; }
  此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。也就是说protected、private 修饰的方法上使用 @Transactional 注解会导致事务无效。
  了解了@Transactional的原理之后,我们在回头看看之前的问题,会不会是使用方法不对导致的呢?3. 拨云见日问题代码1解析
  下面的代码中,我们在同一个类里面调用了@Transactional修饰的方法,其实这样调用的话并没有用到Spring AOP生成的代理对象。从上面的时序图也可以看到,只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。@Component @EnableScheduling public class someService {      @Scheduled(...)   public doSomeWork() {     // find some id by logic          // process the related info     doOtherWork(id);   }      @Transactional(isolation = Isolation.READ_COMMITTED)   public void doOtherWork(id) {     Info info = requestMapper.selectByPrimaryKeyForUpdate(id);     doSomeFollowingProcess(info);     ...   } }
  那如何解决这种类内调用的问题呢? 很简单,可以使用applicationContext直接从IOC容器中将someService类取出来,然后再调用doOtherWork方法即可,这样就能用上Spring AOP生成的代理对象了。
  下面是更改之后的代码,更改之后发现事务生效了,问题解决!@Component @EnableScheduling public class someService {      @Autowired   private ApplicationContext applicationContext;      @Scheduled(...)   public doSomeWork() {     // find some id by logic          // process the related info     SomeService someService = applicationContext.getBean(someService.class);     someService.doOtherWork(id);   }      @Transactional(isolation = Isolation.READ_COMMITTED)   public void doOtherWork(id) {     Info info = requestMapper.selectByPrimaryKeyForUpdate(id);     doSomeFollowingProcess(info);     ...   } }问题代码2解析
  下面的代码中,MainWork调用doSomeCheck的时候,会出现null的情况,原因是由于该方法不是public方法,会导致@Transactional调用失败。你可能会说这就是普通方法,跟@Transactional有什么关系?
  需要注意的是,无论transactional注解在类上还是在方法上,IOC容器都会生成对应类的代理对象,然后使用代理对象去访问对应的方法。在这个例子里面, 调用doWork时一切正常,事务也会生效;但是调用doSomeCheck时,从之前的分析可以看到,由于方法不是public,此时事务管理器不会起作用,直接导致所有的autowired未完成注入。修改的方法也很简单,把doSomeCheck改成public就行了。
  这个问题隐藏比较深一些,不清楚原理很难发现这个问题。@Component public class MainWork {   @AutoWired   DetailWork detailWork        public void workflow() {     detailWork.doSomeCheck();     detailWork.doWork();   } }   @Component public class DetailWork {        @AutoWired     UsefulService usefulService;        @AutoWired     InfoService infoService;        @Transactional(isolation = Isolation.READ_COMMITTED)     public void doWork() {       usefulService.doSomeWork();     }        public void doSomeCheck() {       infoService.getInfo();     } }4. 相关拓展几种事务失效的场景
  上面说到的两个问题,其实就是@Transactional注解使用不当,导致失效的两种情形;除此之外,以下几种情况也会导致事务失效:业务代码中存在异常时,使用try…catch…语句块捕获,而catch语句块没有throw new RuntimeExecption异常;(最难被排查到问题且容易忽略)注解@Transactional中Propagation属性值设置错误即Propagation.NOT_SUPPORTED(一般不会设置此种传播机制)mysql关系型数据库,且存储引擎是MyISAM而非InnoDB,则事务会不起作用(比较少见);业务代码抛出异常类型非RuntimeException,事务失效;Spring默认抛出未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor属性。
  事务的传播行为
  事务的传播行为也会影响到事务与事务之间的关系,一定要搞清楚,否则经常会出现很奇怪的问题。
  具体来讲有以下几种属性:propagation 代表事务的传播行为,默认值为 Propagation.REQUIRED,其他的属性信息如下:Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。( 也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 )Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。( 当类A中的 a 方法用默Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为Propagation.REQUIRES_NEW会暂停 a方法的事务 )Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。Propagation.NESTED :和 Propagation.REQUIRED 效果一样。事务的隔离级别
  SQL标准定义了4种事务隔离级别来避免3种数据不一致的问题。事务等级从高到低,分别为:
  1.Serializable(序列化)
  系统中所有的事务以串行地方式逐个执行,所以能避免所有数据不一致情况。
  但是这种以排他方式来控制并发事务,串行化执行方式会导致事务排队,系统的并发量大幅下降,使用的时候要绝对慎重。
  2.Repeatable read(可重复读)
  一个事务一旦开始,事务过程中所读取的所有数据不允许被其他事务修改。
  一个隔离级别没有办法解决"幻影读"的问题。
  因为它只"保护"了它读取的数据不被修改,但是其他数据会被修改。如果其他数据被修改后恰好满足了当前事务的过滤条件(where语句),那么就会发生"幻影读"的情况。
  其他两种事务隔离等级为:
  3.Read Committed(已提交读)
  一个事务能读取到其他事务提交过(Committed)的数据。
  一个事务在处理过程中如果重复读取某一个数据,而且这个数据恰好被其他事务修改并提交了,那么当前重复读取数据的事务就会出现同一个数据前后不同的情况。
  在这个隔离级别会发生"不可重复读"的场景。
  4.Read Uncommitted(未提交读)
  一个事务能读取到其他事务修改过,但是还没有提交的(Uncommitted)的数据。
  数据被其他事务修改过,但还没有提交,就存在着回滚的可能性,这时候读取这些"未提交"数据的情况就是"脏读"。
  在这个隔离级别会发生"脏读"场景。

小胖30杜凯琹,王楚钦30横扫黄镇廷王浩冷暂停今晚最后一场对决是勒布伦弟弟对阵一号小胖樊振东,年龄相差10岁啊,法国又出天才,小胖118119115连下3局淘汰勒布伦兄弟。第一局33开局,横直大战,84小胖领先,118小胖先下太幽默了!王治郅我14岁穿52码球鞋,梦幻脚步是和巩晓彬学的头条创作挑战赛太幽默了!王治郅我14岁穿52码球鞋,梦幻脚步是和巩晓彬学的北京时间2023年4月10日星期一消息。许久没有王治郅的消息了。自从大郅不再执教八一,同时八一退出CBA,假如你穿越回古代,还能吃到生鲜食品吗?看看古人的物流运物流小课堂穿越到古代还能吃生鲜食品吗?假如你穿越回古代,答案是可以。如果你穿越到周王朝时期,要靠朝廷中主管物流的官员负责实现。但速度是别想了,当时的物流体系基本都是靠走跑步慢的,在ChatGPT遭多国围堵4月3日,联合国教科文组织号召各国立即执行其人工智能伦理问题建议书。这一规范性全球框架由教科文组织193个成员国一致通过,旨在提供所有必要保障。图片来源视觉中国中青报中青网见习记者把钱花在刀刃上!一加手机换新攻略,24K预算这么选就对了虽然说如今的手机市场,安卓苹果鸿蒙等不同阵营都拥有众多手机品牌和各种机型,但是在换新机的时候,考虑到使用习惯等因素,有不少人都喜欢认准一个品牌,像是3月底发布的魅族20系列,就有许Q2产品储备腾讯网易米哈游发力,重磅产品集中开测更多积极信号来了。文灰信鸽近日,葡萄君统计了国内游戏厂商2023年Q2产品储备情况,最终汇总到25家厂商43款新品。以下为Q2储备情况本次产品储备敲定了多款大作,它们是近期版号恢复Java故障诊断使用日志调查问题像其他调查技术一样,使用日志在某些情况下有意义,而在其他情况下则没有意义。在本文中,我们将研究使用日志可以帮助你更容易理解软件行为的各种情况。我们将首先讨论日志信息的几个关键点,然哪吒UU秀版对上AIONYyounger,谁才是15万纯电SUV卷王?在如今电动化时代下,电动车可以说成为了越来越多消费者在买车上的首选,那么,15万预算,该选择哪款纯电SUV呢?不得不说,当前15万级纯电SUV市场的竞争还是非常卷的,尤其是国产纯电全国多个城市优化公积金政策多地房地产市场企稳回升优化公积金政策是各城市支持住房消费的重要举措之一。近期,全国多个城市出台公积金贷款带押过户支持商转公贷款允许提取公积金支付首付款等政策,加大公积金对购房者的支持力度。对于购房者来说请注意!iPhone15将由印度代工,网友和小米11一样的售后就买之前一直有消息称苹果将生产线撤出了中国,有没有完全撤出还不确定,但可以确定的是苹果已经在东南几个国家建设了代工厂,去年和今年的部分产品,比如AirPodsPro,是由它们生产的,现绍兴首家亚运场馆青年之家正式启用4月8日,绍兴市首家亚运场馆青年之家在绍兴棒(垒)球体育文化中心正式启用。启用仪式上,30余名绍兴市青联委员和青年代表身着不同颜色的球服,在专业教练的带领下,开展了疯狂躲避球击球赛
支付宝突然宣布,花呗借呗将大规模关闭,网友欠的钱怎么还?如今移动支付已经成为了主流的支付方式,也就是我们常见的支付宝和微信支付。支付宝发展至今已不仅仅是一个支付软件,还衍生出了诸多功能和服务,其中花呗和借呗就是支付宝上两颗耀眼的星星。支成品油零售限价今夜迎来年内最大幅上调!95号汽油全面进入8元时代金融界4月17日消息今夜24时,本次成品油零售限价将迎来年内最大幅上调。据国家发改委今日消息,根据近期国际市场油价变化情况,按照现行成品油价格形成机制,汽柴油零售限价每吨分别上调5外媒苹果WWDC23预示后iPhone时代到来环球网科技综合报道据外媒4月17日报道,彭博社记者马克古尔曼在其社交平台上指出,苹果公司将于6月举办的全球开发者大会将是该公司历史上最盛大的产品发布活动之一。除了苹果混合现实头显是科学家早就找到了免费能源,却被禁止研究和使用?是真的吗?科学家早就找到了免费能源,却被禁止研究和使用?是真的吗?这个问题在一些科幻小说和电影中被提出过,但是,我们是否能够把这个问题和现实联系起来呢?对于这个问题,我们需要做深入探讨和分析业绩变脸信披违规,一上市公司及董事长再遭公开谴责大河财立方消息4月17日,深交所对广东文化长城集团股份有限公司(股票简称ST文化)及相关当事人给予公开谴责处分。据深交所处分决定,经查明,ST文化等当事人存在以下违规行为2023年从2799元跌至1980元,16GB256GB5000mAh,高配旗舰降至千元档潮流总在变化,这是因为人们对于产品的需求经常会发生改变,例如以前人们对手机的需求只是打电话,但后续开始重视它的游戏性能拍照效果,而手机厂商们想要让旗下机型卖出去,那么必须投其所好,想换OPPO手机这5款性价比高,骁龙8上代旗舰降价至2679元如果您喜欢,可以点击上面的关注二字。后续会为您提供更多有价值的内容。每日分享想换OPPO手机这5款性价比高,骁龙8旗舰降价至2679元。第一款真我GTNeo2参考价格1899元(1首次利用柔性基COF作为主体材料提升锂硫电池性能肖丹教授NanoEnergy观点首次利用柔性基COF作为主体材料提升锂硫电池性能文章信息调控柔性共价有机框架(COF)结构特性制备具有高性能的锂硫电池主体材料第一作者葛云晨通讯作者ED高温固态合成BaScAlO化合物的热发射性能二钡钪铝氧化物(Ba2ScAlO5)是大功率真空器件热离子发射的重要化合物。本文对高温固态合成机理进行了广泛的研究,以开发单相Ba2ScAlO5并评估其发射能力。采用碳酸钡氧化钪和逐鹿争雄,南京钢联花落谁家?编者按4月,一场罕见的百亿争夺战在钢铁行业悄然打响,随着南京钢联60股权收购案出现大反转,一系列组合拳之下,此前拟收购方沙钢集团几近出局,一时间引发行业热议。中信的此次入局是中国钢三部门广交会展期内销售的进口展品享税收优惠央视网消息(新闻联播)财政部海关总署税务总局日前发布通知,明确对2023年举办的广交会在商务部确定的展期内销售的免税额度内的进口展品免征进口关税进口环节增值税和消费税,涉及机械器具