安庆大理运城常德铜陵江西
投稿投诉
江西南阳
嘉兴昆明
铜陵滨州
广东西昌
常德梅州
兰州阳江
运城金华
广西萍乡
大理重庆
诸暨泉州
安庆南充
武汉辽宁

程序员新人周一优化一行代码,周三被劝退?

10月18日 碎骨族投稿
  这周一,公司新来了一个同事,面试的时候表现得非常不错,各种问题对答如流,老板和我都倍感欣慰。
  这么优秀的人,绝不能让他浪费一分一秒,于是很快,我就发他了需求文档、源码,让他先在本地熟悉一下业务和开发流程。
  结果没想到,周三大家一块review代码的时候就发现了问题,新来的同事直接把原来Transactional优化成了这个鬼样子:Transactional(propagationPropagation。REQUIRED,rollbackForException。class)
  就因为这一行代码,老板(当年也是一线互联网大厂的好手)当场就发飙了,马上就要劝退这位新同事,我就赶紧打圆场,毕竟自己面试的人,不看僧面看佛面,是吧?于是老板答应我说再试用一个月看看。
  会议结束后,我就赶紧让新同事复习了一遍事务,以下是他自己做的总结,还是非常详细的,分享出来给大家一点点参考和启发。相信大家看完后就明白为什么不能这样优化Transactional注解了,纯属画蛇添足和乱用。关于事务
  事务在逻辑上是一组操作,要么执行,要不都不执行。主要是针对数据库而言的,比如说MySQL。
  只要记住这一点,理解事务就很容易了。在Java中,我们通常要在业务里面处理多个事件,比如说编程喵有一个保存文章的方法,它除了要保存文章本身之外,还要保存文章对应的标签,标签和文章不在同一个表里,但会通过在文章表里(posts)保存标签主键(tagid)来关联标签表(tags):publicvoidsavePosts(PostsParampostsParam){保存文章save(posts);处理标签insertOrUpdateTag(postsParam,posts);}
  那么此时就需要开启事务,保证文章表和标签表中的数据保持同步,要么都执行,要么都不执行。
  否则就有可能造成,文章保存成功了,但标签保存失败了,或者文章保存失败了,标签保存成功了这些场景都不符合我们的预期。
  为了保证事务是正确可靠的,在数据库进行写入或者更新操作时,就必须得表现出ACID的4个重要特性:原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。事务隔离(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
  其中,事务隔离又分为4种不同的级别,包括:未提交读(Readuncommitted),最低的隔离级别,允许脏读(dirtyreads),事务可以看到其他事务尚未提交的修改。如果另一个事务回滚,那么当前事务读到的数据就是脏数据。提交读(readcommitted),一个事务可能会遇到不可重复读(NonRepeatableRead)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。可重复读(repeatableread),一个事务可能会遇到幻读(PhantomRead)的问题。幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。串行化(Serializable),最严格的隔离级别,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。虽然Serializable隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用Serializable隔离级别。
  需要格外注意的是:事务能否生效,取决于数据库引擎是否支持事务,MySQL的InnoDB引擎是支持事务的,但MyISAM就不支持。关于Spring对事务的支持
  Spring支持两种事务方式,分别是编程式事务和声明式事务,后者最常见,通常情况下只需要一个Transactional就搞定了(代码侵入性降到了最低),就像这样:TransactionalpublicvoidsavePosts(PostsParampostsParam){保存文章save(posts);处理标签insertOrUpdateTag(postsParam,posts);}
  1)编程式事务
  编程式事务是指将事务管理代码嵌入嵌入到业务代码中,来控制事务的提交和回滚。
  你比如说,使用TransactionTemplate来管理事务:AutowiredprivateTransactionTemplatetransactionTpublicvoidtestTransaction(){transactionTemplate。execute(newTransactionCallbackWithoutResult(){OverrideprotectedvoiddoInTransactionWithoutResult(TransactionStatustransactionStatus){try{。。。。业务代码}catch(Exceptione){回滚transactionStatus。setRollbackOnly();}}});}
  再比如说,使用TransactionManager来管理事务:AutowiredprivatePlatformTransactionManagertransactionMpublicvoidtestTransaction(){TransactionStatusstatustransactionManager。getTransaction(newDefaultTransactionDefinition());try{。。。。业务代码transactionManager。commit(status);}catch(Exceptione){transactionManager。rollback(status);}}
  就编程式事务管理而言,Spring更推荐使用TransactionTemplate。
  在编程式事务中,必须在每个业务操作中包含额外的事务管理代码,就导致代码看起来非常的臃肿,但对理解Spring的事务管理模型非常有帮助。
  2)声明式事务
  声明式事务将事务管理代码从业务方法中抽离了出来,以声明式的方式来实现事务管理,对于开发者来说,声明式事务显然比编程式事务更易用、更好用。
  当然了,要想实现事务管理和业务代码的抽离,就必须得用到Spring当中最关键最核心的技术之一,AOP,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
  声明式事务虽然优于编程式事务,但也有不足,声明式事务管理的粒度是方法级别,而编程式事务是可以精确到代码块级别的。事务管理模型
  Spring将事务管理的核心抽象为一个事务管理器(TransactionManager),它的源码只有一个简单的接口定义,属于一个标记接口:publicinterfaceTransactionManager{}
  该接口有两个子接口,分别是编程式事务接口ReactiveTransactionManager和声明式事务接口PlatformTransactionManager。我们来重点说说PlatformTransactionManager,该接口定义了3个接口方法:interfacePlatformTransactionManagerextendsTransactionManager{根据事务定义获取事务状态TransactionStatusgetTransaction(TransactionDefinitiondefinition)throwsTransactionE提交事务voidcommit(TransactionStatusstatus)throwsTransactionE事务回滚voidrollback(TransactionStatusstatus)throwsTransactionE}
  通过PlatformTransactionManager这个接口,Spring为各个平台如JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
  参数TransactionDefinition和Transactional注解是对应的,比如说Transactional注解中定义的事务传播行为、隔离级别、事务超时时间、事务是否只读等属性,在TransactionDefinition都可以找得到。
  返回类型TransactionStatus主要用来存储当前事务的一些状态和数据,比如说事务资源(connection)、回滚状态等。
  TransactionDefinition。java:publicinterfaceTransactionDefinition{事务的传播行为defaultintgetPropagationBehavior(){returnPROPAGATIONREQUIRED;}事务的隔离级别defaultintgetIsolationLevel(){returnISOLATIONDEFAULT;}事务超时时间defaultintgetTimeout(){returnTIMEOUTDEFAULT;}事务是否只读defaultbooleanisReadOnly(){}}
  Transactional。javaTarget({ElementType。TYPE,ElementType。METHOD})Retention(RetentionPolicy。RUNTIME)InheritedDocumentedpublicinterfaceTransactional{Propagationpropagation()defaultPropagation。REQUIRED;Isolationisolation()defaultIsolation。DEFAULT;inttimeout()defaultTransactionDefinition。TIMEOUTDEFAULT;booleanreadOnly()}Transactional注解中的propagation对应TransactionDefinition中的getPropagationBehavior,默认值为Propagation。REQUIRED(TransactionDefinition。PROPAGATIONREQUIRED)。Transactional注解中的isolation对应TransactionDefinition中的getIsolationLevel,默认值为DEFAULT(TransactionDefinition。ISOLATIONDEFAULT)。Transactional注解中的timeout对应TransactionDefinition中的getTimeout,默认值为TransactionDefinition。TIMEOUTDEFAULT。Transactional注解中的readOnly对应TransactionDefinition中的isReadOnly,默认值为false。
  说到这,我们来详细地说明一下Spring事务的传播行为、事务的隔离级别、事务的超时时间、事务的只读属性,以及事务的回滚规则。事务传播行为
  当事务方法被另外一个事务方法调用时,必须指定事务应该如何传播,例如,方法可能继续在当前事务中执行,也可以开启一个新的事务,在自己的事务中执行。
  声明式事务的传播行为可以通过Transactional注解中的propagation属性来定义,比如说:Transactional(propagationPropagation。REQUIRED)publicvoidsavePosts(PostsParampostsParam){}
  TransactionDefinition一共定义了7种事务传播行为:
  01、PROPAGATIONREQUIRED
  这也是Transactional默认的事务传播行为,指的是如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。更确切地意思是:如果外部方法没有开启事务的话,Propagation。REQUIRED修饰的内部方法会开启自己的事务,且开启的事务相互独立,互不干扰。如果外部方法开启事务并且是Propagation。REQUIRED的话,所有Propagation。REQUIRED修饰的内部方法和外部方法均属于同一事务,只要一个方法回滚,整个事务都需要回滚。ClassA{Transactional(propagationPropagation。PROPAGATIONREQUIRED)publicvoidaMethod{dosomethingBbnewB();b。bMethod();}}ClassB{Transactional(propagationPropagation。PROPAGATIONREQUIRED)publicvoidbMethod{dosomething}}
  这个传播行为也最好理解,aMethod调用了bMethod,只要其中一个方法回滚,整个事务均回滚。
  02、PROPAGATIONREQUIRESNEW
  创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation。REQUIRESNEW修饰的内部方法都会开启自己的事务,且开启的事务与外部的事务相互独立,互不干扰。ClassA{Transactional(propagationPropagation。PROPAGATIONREQUIRED)publicvoidaMethod{dosomethingBbnewB();b。bMethod();}}ClassB{Transactional(propagationPropagation。REQUIRESNEW)publicvoidbMethod{dosomething}}
  如果aMethod()发生异常回滚,bMethod()不会跟着回滚,因为bMethod()开启了独立的事务。但是,如果bMethod()抛出了未被捕获的异常并且这个异常满足事务回滚规则的话,aMethod()同样也会回滚。
  03、PROPAGATIONNESTED
  如果当前存在事务,就在当前事务内执行;否则,就执行与PROPAGATIONREQUIRED类似的操作。
  04、PROPAGATIONMANDATORY
  如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  05、PROPAGATIONSUPPORTS
  如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  06、PROPAGATIONNOTSUPPORTED
  以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  07、PROPAGATIONNEVER
  以非事务方式运行,如果当前存在事务,则抛出异常。
  3、4、5、6、7这5种事务传播方式不常用,了解即可。事务隔离级别
  前面我们已经了解了数据库的事务隔离级别,再来理解Spring的事务隔离级别就容易多了。
  TransactionDefinition中一共定义了5种事务隔离级别:ISOLATIONDEFAULT,使用数据库默认的隔离级别,MySql默认采用的是REPEATABLEREAD,也就是可重复读。ISOLATIONREADUNCOMMITTED,最低的隔离级别,可能会出现脏读、幻读或者不可重复读。ISOLATIONREADCOMMITTED,允许读取并发事务提交的数据,可以防止脏读,但幻读和不可重复读仍然有可能发生。ISOLATIONREPEATABLEREAD,对同一字段的多次读取结果都是一致的,除非数据是被自身事务所修改的,可以阻止脏读和不可重复读,但幻读仍有可能发生。ISOLATIONSERIALIZABLE,最高的隔离级别,虽然可以阻止脏读、幻读和不可重复读,但会严重影响程序性能。
  通常情况下,我们采用默认的隔离级别ISOLATIONDEFAULT就可以了,也就是交给数据库来决定,可以通过SELECT命令来查看MySql的默认隔离级别,结果为REPEATABLEREAD,也就是可重复读。
  事务的超时时间
  事务超时,也就是指一个事务所允许执行的最长时间,如果在超时时间内还没有完成的话,就自动回滚。
  假如事务的执行时间格外的长,由于事务涉及到对数据库的锁定,就会导致长时间运行的事务占用数据库资源。事务的只读属性
  如果一个事务只是对数据库执行读操作,那么该数据库就可以利用事务的只读属性,采取优化措施,适用于多条数据库查询操作中。
  为什么一个查询操作还要启用事务支持呢?
  这是因为MySql(innodb)默认对每一个连接都启用了autocommit模式,在该模式下,每一个发送到MySql服务器的SQL语句都会在一个单独的事务中进行处理,执行结束后会自动提交事务。
  那如果我们给方法加上了Transactional注解,那这个方法中所有的SQL都会放在一个事务里。否则,每条SQL都会单独开启一个事务,中间被其他事务修改了数据,都会实时读取到。
  有些情况下,当一次执行多条查询语句时,需要保证数据一致性时,就需要启用事务支持。否则上一条SQL查询后,被其他用户改变了数据,那么下一个SQL查询可能就会出现不一致的状态。事务的回滚策略
  默认情况下,事务只在出现运行时异常(RuntimeException)时回滚,以及Error,出现检查异常(checkedexception,需要主动捕获处理或者向上抛出)时不回滚。
  https:tobebetterjavaer。comexceptiongailan。html
  如果你想要回滚特定的异常类型的话,可以这样设置:Transactional(rollbackForMyException。class)关于SpringBoot对事务的支持
  以前,我们需要通过XML配置Spring来托管事务,有了SpringBoot之后,一切就变得更加简单了,只需要在业务层添加事务注解(Transactional)就可以快速开启事务。
  也就是说,我们只需要把焦点放在Transactional注解上就可以了。Transactional的作用范围类上,表明类中所有public方法都启用事务方法上,最常用的一种接口上,不推荐使用Transactional的常用配置参数
  虽然Transactional注解源码中定义了很多属性,但大多数时候,我都是采用默认配置,当然了,如果需要自定义的话,前面也都说明过了。Transactional的使用注意事项总结
  1)要在public方法上使用,在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断,如果目标方法不是public,则TransactionAttribute返回null,即不支持事务。protectedTransactionAttributecomputeTransactionAttribute(Methodmethod,NullableC?targetClass){Dontallownopublicmethodsasrequired。if(allowPublicMethodsOnly()!Modifier。isPublic(method。getModifiers())){}Themethodmaybeonaninterface,butweneedattributesfromthetargetclass。Ifthetargetclassisnull,themethodwillbeunchanged。MethodspecificMethodAopUtils。getMostSpecificMethod(method,targetClass);Firsttryisthemethodinthetargetclass。TransactionAttributetxAttrfindTransactionAttribute(specificMethod);if(txAttr!null){returntxA}Secondtryisthetransactionattributeonthetargetclass。txAttrfindTransactionAttribute(specificMethod。getDeclaringClass());if(txAttr!nullClassUtils。isUserLevelMethod(method)){returntxA}if(specificMethod!method){Fallbackistolookattheoriginalmethod。txAttrfindTransactionAttribute(method);if(txAttr!null){returntxA}Lastfallbackistheclassoftheoriginalmethod。txAttrfindTransactionAttribute(method。getDeclaringClass());if(txAttr!nullClassUtils。isUserLevelMethod(method)){returntxA}}}
  2)避免同一个类中调用Transactional注解的方法,这样会导致事务失效。
  更多事务失效的场景测试事务是否起效
  在测试之前,我们先把SpringBoot默认的日志级别info调整为debug,在application。yml文件中修改:logging:level:org:hibernate:debugspringframework:web:debug
  然后,来看修改之前查到的数据:
  开搞。在控制器中添加一个update接口,准备修改数据,打算把沉默王二的狗腿子修改为沉默王二的狗腿:RequestMapping(update)publicStringupdate(Modelmodel){UseruseruserService。findById(2);user。setName(沉默王二的狗腿);userService。update(user);}
  在Service中为方法加上Transactional注解并抛出运行时异常:OverrideTransactionalpublicvoidupdate(Useruser){userRepository。save(user);thrownewRuntimeException(啊,出现妖怪了!);}
  按照我们的预期,当执行save保存数据后,因为出现了异常,所以事务要回滚。所以数据不会被修改。
  在浏览器中输入http:localhost:8080userupdate进行测试,注意查看日志,可以确认事务起效了。
  当我们把事务去掉,同样抛出异常:Overridepublicvoidupdate(Useruser){userRepository。save(user);thrownewRuntimeException(啊,出现妖怪了!);}
  再次执行,发现虽然程序报错了,但数据却被更新了。
  这也间接地证明,我们的Transactional事务起效了。
  看到这,是不是就明白为什么新同事的优化纯属画蛇添足卵用了吧?
  原文链接:https:mp。weixin。qq。comsiGwrb8T7L78eHOJij6n8w
投诉 评论 转载

足球转会信息8月20日1、根据媒体人杜立言的消息,上海海港后卫贺惯即将加盟大连人,而上海海港已经已找好替身。杜立言在社媒中写道:最新消息,贺惯已经打包行李。上海海港已经为他找好替身。本赛季贺惯在海港……人以群分马克龙和布丽吉特生活奢侈,760万买口罩,450万买2020年5月,67岁的法国第一夫人布丽吉特亮相,成了很多人所关注的焦点。民众们发现:布丽吉特脸上的这一只口罩,价值1。9万人民币,是高奢品牌LV为她独家定制的。口罩的皮……2022年男篮U18亚青赛即将开打,谁会成为中国男篮的明日之2022年男篮U18亚青赛将于今年8月21至8月28日在伊朗举行。国际篮联已经公布了本届U18亚青赛的比赛赛程。与中国队同分在一个小组的是印度队和韩国队。8月21日,中国队将首……电视行业未来之光,海信领跑激光电视产业,份额高达49根据洛图科技(RUNTO)数据,从2015到2021年中国激光电视市场年复合增长率高达155。8。这个数据强有力的说明了,与其他新型显示技术相比,激光显示技术具有明显的差异化技……现役贵为天选之子的十大球星,欧文恃才傲物,维金斯自暴自弃天才免不了有障碍,因为障碍会创造天才。这句罗曼罗兰的名言同样适用于NBA的舞台。在星光璀璨的NBA舞台,出道即被寄予厚望,贵为天选之子的超级球星屡见不鲜。但,能够兑现天赋……程序员新人周一优化一行代码,周三被劝退?这周一,公司新来了一个同事,面试的时候表现得非常不错,各种问题对答如流,老板和我都倍感欣慰。这么优秀的人,绝不能让他浪费一分一秒,于是很快,我就发他了需求文档、源码,让他……原发性肝硬化应该怎么调理?原发性胆汁性肝硬化的治疗主要用药物进行治疗,如熊去氧胆酸类药物,本药可以促进胆汁分泌以及抑制免疫反应的发生,从而可以保护肝脏以及胆管,是治疗本病的首选药物。如果使用熊去氧胆酸类……中国田径女将入籍日本成女排主力,赢球大喊哟西,称中国是她的根中国田径女将入籍日本成女排主力,赢球大喊哟西,称中国是她的根当今世界体坛,归化者并不少见,有些人是为了谋生,有些人则是为了自己的理想。日本是日本最受欢迎的国家之一,而王娇……一年四季经常性出汗的人,要小心了通常临近夏季,气温升高天气炎热,人们一旦在室外呆的比较久,就会出汗,这是正常现象。但有一类人,无论是夏季还是冬季,一年四季都会经常性出汗,可能是局部出汗,也可能是全身出汗。……凯文卢尼每支伟大球队里,都会有这样一位老实人12分7个篮板2次助攻3次抢断,这是凯文卢尼在总决赛第二场交出的数据。场上正负值24,和斯蒂芬库里并列全队第一。什么时候开始,凯文卢尼成为了勇士不可缺少的一员了?……5分钟2球!梅西造四大纪录,小蜘蛛独闯龙潭,阿根廷静待卫冕冠2022世界杯北京时间12月14日,卡塔尔世界杯首场半决赛开打,梅西领军的潘帕斯雄鹰阿根廷迎战莫德里奇带领的格子军团克罗地亚,此役阿根廷的锋线和表现神勇的克罗地亚门神利瓦科维奇……2022年微型车市场盘点奇瑞新能源表现出色,五菱还是独角兽2022年已经离我们而去,汽车市场给人印象最深的莫过于微型车,因为纯电动车已经让微型车市场彻底告别内燃机,提前进入电气化时代。今天我们就来一起回顾一下,2022年微型车销量哪家……
体验了小米MIXFold2后,我把主力机换成了折叠屏荨麻疹高发季,家长重视防治方法,这3种水果孩子要少吃罗马塑造冠军之师任重而道远,穆里尼奥又得在荆棘中采摘鲜花,苦8小时11笔签约达成!2人获顶薪,太阳囤积内线,勇士又被挖墙骁龙8新机买不起?可以考虑这5款旧旗舰,降幅都在2000元以孩子爱吃的5款零食,好吃营养又解馋,遇见囤一些,别舍不得花钱7月1日重磅开票7月17日和王晰一起唱出这个世界的一切图景在美国几乎无人问津,在我国却成了轻奢品?你是否买过?女生做哪种化妆师会更有前途好的教育,从家长改变思维做起三句话让吃饭玩手机的孩子变了同样是台偶剧的女主,把王心凌张韶涵杨丞琳放一起,差距有了建议中老年人如果不差钱,多吃4种鱼,补钙补蛋白,越吃越健康学习逻辑与思维第三十四题消失的尸体投放了百度竞价广告后也没有想象的那样效果黄芪白粉病如何防治?科普下清明上河图描绘的是哪个城市吴敏霞婆婆罕见出镜!哭诉享受不到天伦之乐,对儿媳的称呼显疏离孩子得了抑郁症怎么办?新帮手来了股票龙头股怎么找如何运用好“回档拉升”短线战法小板凳成儿童意外伤害的“杀手”跳绳比赛关于林生斌,前一段我也写了五六篇小文章,可以重读一下熟牛肉要放冰箱冷冻吗熟牛肉要不要放冰箱冷冻

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找七猫云易事利