<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          程序員新人周一優(yōu)化一行代碼,周三被勸退?

          共 12030字,需瀏覽 25分鐘

           ·

          2022-05-15 16:27

          周一,公司新來了一個同事,面試的時候表現(xiàn)得非常不錯,各種問題對答如流,老板和我都倍感欣慰。

          這么優(yōu)秀的人,絕不能讓他浪費一分一秒,于是很快,我就發(fā)他了需求文檔、源碼,讓他先在本地熟悉一下業(yè)務(wù)和開發(fā)流程。

          結(jié)果沒想到,周三大家一塊 review 代碼的時候就發(fā)現(xiàn)了問題,新來的同事直接把原來 @Transactional 優(yōu)化成了這個鬼樣子:

          @Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)

          就因為這一行代碼,老板(當(dāng)年也是一線互聯(lián)網(wǎng)大廠的好手)當(dāng)場就發(fā)飆了,馬上就要勸退這位新同事,我就趕緊打圓場,畢竟自己面試的人,不看僧面看佛面,是吧?于是老板答應(yīng)我說再試用一個月看看。

          會議結(jié)束后,我就趕緊讓新同事復(fù)習(xí)了一遍事務(wù),以下是他自己做的總結(jié),還是非常詳細的,分享出來給大家一點點參考和啟發(fā)。相信大家看完后就明白為什么不能這樣優(yōu)化 @Transactional 注解了,純屬畫蛇添足和亂用。


          關(guān)于事務(wù)

          事務(wù)在邏輯上是一組操作,要么執(zhí)行,要不都不執(zhí)行。主要是針對數(shù)據(jù)庫而言的,比如說 MySQL。

          只要記住這一點,理解事務(wù)就很容易了。在 Java 中,我們通常要在業(yè)務(wù)里面處理多個事件,比如說編程喵??有一個保存文章的方法,它除了要保存文章本身之外,還要保存文章對應(yīng)的標簽,標簽和文章不在同一個表里,但會通過在文章表里(posts)保存標簽主鍵(tag_id)來關(guān)聯(lián)標簽表(tags):

          public?void?savePosts(PostsParam?postsParam)?{
          ?//?保存文章
          ?save(posts);
          ?//?處理標簽
          ??insertOrUpdateTag(postsParam,?posts);
          }

          那么此時就需要開啟事務(wù),保證文章表和標簽表中的數(shù)據(jù)保持同步,要么都執(zhí)行,要么都不執(zhí)行。

          否則就有可能造成,文章保存成功了,但標簽保存失敗了,或者文章保存失敗了,標簽保存成功了——這些場景都不符合我們的預(yù)期。

          為了保證事務(wù)是正確可靠的,在數(shù)據(jù)庫進行寫入或者更新操作時,就必須得表現(xiàn)出 ACID 的 4 個重要特性:

          • 原子性(Atomicity):一個事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會結(jié)束在中間某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯誤,會被回滾(Rollback)到事務(wù)開始前的狀態(tài),就像這個事務(wù)從來沒有執(zhí)行過一樣。
          • 一致性(Consistency):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整性沒有被破壞。
          • 事務(wù)隔離(Isolation):數(shù)據(jù)庫允許多個并發(fā)事務(wù)同時對其數(shù)據(jù)進行讀寫和修改,隔離性可以防止多個事務(wù)并發(fā)執(zhí)行時由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。
          • 持久性(Durability):事務(wù)處理結(jié)束后,對數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會丟失。

          其中,事務(wù)隔離又分為 4 種不同的級別,包括:

          • 未提交讀(Read uncommitted),最低的隔離級別,允許“臟讀”(dirty reads),事務(wù)可以看到其他事務(wù)“尚未提交”的修改。如果另一個事務(wù)回滾,那么當(dāng)前事務(wù)讀到的數(shù)據(jù)就是臟數(shù)據(jù)。
          • 提交讀(read committed),一個事務(wù)可能會遇到不可重復(fù)讀(Non Repeatable Read)的問題。不可重復(fù)讀是指,在一個事務(wù)內(nèi),多次讀同一數(shù)據(jù),在這個事務(wù)還沒有結(jié)束時,如果另一個事務(wù)恰好修改了這個數(shù)據(jù),那么,在第一個事務(wù)中,兩次讀取的數(shù)據(jù)就可能不一致。
          • 可重復(fù)讀(repeatable read),一個事務(wù)可能會遇到幻讀(Phantom Read)的問題。幻讀是指,在一個事務(wù)中,第一次查詢某條記錄,發(fā)現(xiàn)沒有,但是,當(dāng)試圖更新這條不存在的記錄時,竟然能成功,并且,再次讀取同一條記錄,它就神奇地出現(xiàn)了。
          • 串行化(Serializable),最嚴格的隔離級別,所有事務(wù)按照次序依次執(zhí)行,因此,臟讀、不可重復(fù)讀、幻讀都不會出現(xiàn)。雖然 Serializable 隔離級別下的事務(wù)具有最高的安全性,但是,由于事務(wù)是串行執(zhí)行,所以效率會大大下降,應(yīng)用程序的性能會急劇降低。如果沒有特別重要的情景,一般都不會使用 Serializable 隔離級別。

          需要格外注意的是:事務(wù)能否生效,取決于數(shù)據(jù)庫引擎是否支持事務(wù),MySQL 的 InnoDB 引擎是支持事務(wù)的,但 MyISAM 就不支持

          關(guān)于 Spring 對事務(wù)的支持

          Spring 支持兩種事務(wù)方式,分別是編程式事務(wù)和聲明式事務(wù),后者最常見,通常情況下只需要一個 @Transactional 就搞定了(代碼侵入性降到了最低),就像這樣:

          @Transactional
          public?void?savePosts(PostsParam?postsParam)?{
          ?//?保存文章
          ?save(posts);
          ?//?處理標簽
          ??insertOrUpdateTag(postsParam,?posts);
          }

          1)編程式事務(wù)

          編程式事務(wù)是指將事務(wù)管理代碼嵌入嵌入到業(yè)務(wù)代碼中,來控制事務(wù)的提交和回滾。

          你比如說,使用 TransactionTemplate 來管理事務(wù):

          @Autowired
          private?TransactionTemplate?transactionTemplate;
          public?void?testTransaction()?{

          ????????transactionTemplate.execute(new?TransactionCallbackWithoutResult()?{
          ????????????@Override
          ????????????protected?void?doInTransactionWithoutResult(TransactionStatus?transactionStatus)?{

          ????????????????try?{

          ????????????????????//?....??業(yè)務(wù)代碼
          ????????????????}?catch?(Exception?e){
          ????????????????????//回滾
          ????????????????????transactionStatus.setRollbackOnly();
          ????????????????}

          ????????????}
          ????????});
          }

          再比如說,使用 TransactionManager 來管理事務(wù):

          @Autowired
          private?PlatformTransactionManager?transactionManager;

          public?void?testTransaction()?{

          ??TransactionStatus?status?=?transactionManager.getTransaction(new?DefaultTransactionDefinition());
          ??????????try?{
          ???????????????//?....??業(yè)務(wù)代碼
          ??????????????transactionManager.commit(status);
          ??????????}?catch?(Exception?e)?{
          ??????????????transactionManager.rollback(status);
          ??????????}
          }

          就編程式事務(wù)管理而言,Spring 更推薦使用 TransactionTemplate。

          在編程式事務(wù)中,必須在每個業(yè)務(wù)操作中包含額外的事務(wù)管理代碼,就導(dǎo)致代碼看起來非常的臃腫,但對理解 Spring 的事務(wù)管理模型非常有幫助。

          2)聲明式事務(wù)

          聲明式事務(wù)將事務(wù)管理代碼從業(yè)務(wù)方法中抽離了出來,以聲明式的方式來實現(xiàn)事務(wù)管理,對于開發(fā)者來說,聲明式事務(wù)顯然比編程式事務(wù)更易用、更好用。

          當(dāng)然了,要想實現(xiàn)事務(wù)管理和業(yè)務(wù)代碼的抽離,就必須得用到 Spring 當(dāng)中最關(guān)鍵最核心的技術(shù)之一,AOP,其本質(zhì)是對方法前后進行攔截,然后在目標方法開始之前創(chuàng)建或者加入一個事務(wù),執(zhí)行完目標方法之后根據(jù)執(zhí)行的情況提交或者回滾。

          聲明式事務(wù)雖然優(yōu)于編程式事務(wù),但也有不足,聲明式事務(wù)管理的粒度是方法級別,而編程式事務(wù)是可以精確到代碼塊級別的

          事務(wù)管理模型

          Spring 將事務(wù)管理的核心抽象為一個事務(wù)管理器(TransactionManager),它的源碼只有一個簡單的接口定義,屬于一個標記接口:

          public?interface?TransactionManager?{

          }

          該接口有兩個子接口,分別是編程式事務(wù)接口 ReactiveTransactionManager 和聲明式事務(wù)接口 PlatformTransactionManager。我們來重點說說 PlatformTransactionManager,該接口定義了 3 個接口方法:

          interface?PlatformTransactionManager?extends?TransactionManager{
          ????//?根據(jù)事務(wù)定義獲取事務(wù)狀態(tài)
          ????TransactionStatus?getTransaction(TransactionDefinition?definition)
          ????????????throws?TransactionException
          ;

          ????//?提交事務(wù)
          ????void?commit(TransactionStatus?status)?throws?TransactionException;

          ????//?事務(wù)回滾
          ????void?rollback(TransactionStatus?status)?throws?TransactionException;
          }

          通過 PlatformTransactionManager 這個接口,Spring 為各個平臺如 JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了對應(yīng)的事務(wù)管理器,但是具體的實現(xiàn)就是各個平臺自己的事情了。

          參數(shù) TransactionDefinition 和 @Transactional 注解是對應(yīng)的,比如說 @Transactional 注解中定義的事務(wù)傳播行為、隔離級別、事務(wù)超時時間、事務(wù)是否只讀等屬性,在 TransactionDefinition 都可以找得到。

          返回類型 TransactionStatus 主要用來存儲當(dāng)前事務(wù)的一些狀態(tài)和數(shù)據(jù),比如說事務(wù)資源(connection)、回滾狀態(tài)等。

          TransactionDefinition.java:

          public?interface?TransactionDefinition?{

          ?//?事務(wù)的傳播行為
          ?default?int?getPropagationBehavior()?{
          ??return?PROPAGATION_REQUIRED;
          ?}

          ?//?事務(wù)的隔離級別
          ?default?int?getIsolationLevel()?{
          ??return?ISOLATION_DEFAULT;
          ?}

          ??//?事務(wù)超時時間
          ??default?int?getTimeout()?{
          ??return?TIMEOUT_DEFAULT;
          ?}

          ??//?事務(wù)是否只讀
          ??default?boolean?isReadOnly()?{
          ??return?false;
          ?}
          }

          Transactional.java

          @Target({ElementType.TYPE,?ElementType.METHOD})
          @Retention(RetentionPolicy.RUNTIME)
          @Inherited
          @Documented
          public?@interface?Transactional?{

          ?Propagation?propagation()?default?Propagation.REQUIRED;
          ?Isolation?isolation()?default?Isolation.DEFAULT;
          ??int?timeout()?default?TransactionDefinition.TIMEOUT_DEFAULT;
          ??boolean?readOnly()?default?false;

          }
          • @Transactional 注解中的 propagation 對應(yīng) TransactionDefinition 中的 getPropagationBehavior,默認值為 Propagation.REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED)
          • @Transactional 注解中的 isolation 對應(yīng) TransactionDefinition 中的 getIsolationLevel,默認值為 DEFAULT(TransactionDefinition.ISOLATION_DEFAULT)
          • @Transactional 注解中的 timeout 對應(yīng) TransactionDefinition 中的 getTimeout,默認值為TransactionDefinition.TIMEOUT_DEFAULT。
          • @Transactional 注解中的 readOnly 對應(yīng) TransactionDefinition 中的 isReadOnly,默認值為 false。

          說到這,我們來詳細地說明一下 Spring 事務(wù)的傳播行為、事務(wù)的隔離級別、事務(wù)的超時時間、事務(wù)的只讀屬性,以及事務(wù)的回滾規(guī)則。

          事務(wù)傳播行為

          當(dāng)事務(wù)方法被另外一個事務(wù)方法調(diào)用時,必須指定事務(wù)應(yīng)該如何傳播,例如,方法可能繼續(xù)在當(dāng)前事務(wù)中執(zhí)行,也可以開啟一個新的事務(wù),在自己的事務(wù)中執(zhí)行。

          聲明式事務(wù)的傳播行為可以通過 @Transactional 注解中的 propagation 屬性來定義,比如說:

          @Transactional(propagation?=?Propagation.REQUIRED)
          public?void?savePosts(PostsParam?postsParam)?{
          }

          TransactionDefinition 一共定義了 7 種事務(wù)傳播行為:

          01、PROPAGATION_REQUIRED

          這也是 @Transactional 默認的事務(wù)傳播行為,指的是如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù)。更確切地意思是:

          • 如果外部方法沒有開啟事務(wù)的話,Propagation.REQUIRED 修飾的內(nèi)部方法會開啟自己的事務(wù),且開啟的事務(wù)相互獨立,互不干擾。
          • 如果外部方法開啟事務(wù)并且是 Propagation.REQUIRED 的話,所有 Propagation.REQUIRED 修飾的內(nèi)部方法和外部方法均屬于同一事務(wù) ,只要一個方法回滾,整個事務(wù)都需要回滾。
          Class?A?{
          ????@Transactional(propagation=Propagation.PROPAGATION_REQUIRED)
          ????public?void?aMethod?{
          ????????//do?something
          ????????B?b?=?new?B();
          ????????b.bMethod();
          ????}
          }

          Class?B?{
          ????@Transactional(propagation=Propagation.PROPAGATION_REQUIRED)
          ????public?void?bMethod?{
          ???????//do?something
          ????}
          }

          這個傳播行為也最好理解,aMethod 調(diào)用了 bMethod,只要其中一個方法回滾,整個事務(wù)均回滾。

          02、PROPAGATION_REQUIRES_NEW

          創(chuàng)建一個新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。也就是說不管外部方法是否開啟事務(wù),Propagation.REQUIRES_NEW 修飾的內(nèi)部方法都會開啟自己的事務(wù),且開啟的事務(wù)與外部的事務(wù)相互獨立,互不干擾。

          Class?A?{
          ????@Transactional(propagation=Propagation.PROPAGATION_REQUIRED)
          ????public?void?aMethod?{
          ????????//do?something
          ????????B?b?=?new?B();
          ????????b.bMethod();
          ????}
          }

          Class?B?{
          ????@Transactional(propagation=Propagation.REQUIRES_NEW)
          ????public?void?bMethod?{
          ???????//do?something
          ????}
          }

          如果 aMethod()發(fā)生異常回滾,bMethod()不會跟著回滾,因為 bMethod()開啟了獨立的事務(wù)。但是,如果 bMethod()拋出了未被捕獲的異常并且這個異常滿足事務(wù)回滾規(guī)則的話,aMethod()同樣也會回滾。

          03、PROPAGATION_NESTED

          如果當(dāng)前存在事務(wù),就在當(dāng)前事務(wù)內(nèi)執(zhí)行;否則,就執(zhí)行與 PROPAGATION_REQUIRED 類似的操作。

          04、PROPAGATION_MANDATORY

          如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。

          05、PROPAGATION_SUPPORTS

          如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。

          06、PROPAGATION_NOT_SUPPORTED

          以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。

          07、PROPAGATION_NEVER

          以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則拋出異常。

          3、4、5、6、7 這 5 種事務(wù)傳播方式不常用,了解即可。

          事務(wù)隔離級別

          前面我們已經(jīng)了解了數(shù)據(jù)庫的事務(wù)隔離級別,再來理解 Spring 的事務(wù)隔離級別就容易多了。

          TransactionDefinition 中一共定義了 5 種事務(wù)隔離級別:

          • ISOLATION_DEFAULT,使用數(shù)據(jù)庫默認的隔離級別,MySql 默認采用的是 REPEATABLE_READ,也就是可重復(fù)讀。
          • ISOLATION_READ_UNCOMMITTED,最低的隔離級別,可能會出現(xiàn)臟讀、幻讀或者不可重復(fù)讀。
          • ISOLATION_READ_COMMITTED,允許讀取并發(fā)事務(wù)提交的數(shù)據(jù),可以防止臟讀,但幻讀和不可重復(fù)讀仍然有可能發(fā)生。
          • ISOLATION_REPEATABLE_READ,對同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被自身事務(wù)所修改的,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。
          • ISOLATION_SERIALIZABLE,最高的隔離級別,雖然可以阻止臟讀、幻讀和不可重復(fù)讀,但會嚴重影響程序性能。

          通常情況下,我們采用默認的隔離級別 ISOLATION_DEFAULT 就可以了,也就是交給數(shù)據(jù)庫來決定,可以通過 SELECT @@transaction_isolation; 命令來查看 MySql 的默認隔離級別,結(jié)果為 REPEATABLE-READ,也就是可重復(fù)讀。

          事務(wù)的超時時間

          事務(wù)超時,也就是指一個事務(wù)所允許執(zhí)行的最長時間,如果在超時時間內(nèi)還沒有完成的話,就自動回滾。

          假如事務(wù)的執(zhí)行時間格外的長,由于事務(wù)涉及到對數(shù)據(jù)庫的鎖定,就會導(dǎo)致長時間運行的事務(wù)占用數(shù)據(jù)庫資源。

          事務(wù)的只讀屬性

          如果一個事務(wù)只是對數(shù)據(jù)庫執(zhí)行讀操作,那么該數(shù)據(jù)庫就可以利用事務(wù)的只讀屬性,采取優(yōu)化措施,適用于多條數(shù)據(jù)庫查詢操作中。

          為什么一個查詢操作還要啟用事務(wù)支持呢?

          這是因為 MySql(innodb)默認對每一個連接都啟用了 autocommit 模式,在該模式下,每一個發(fā)送到 MySql 服務(wù)器的 SQL 語句都會在一個單獨的事務(wù)中進行處理,執(zhí)行結(jié)束后會自動提交事務(wù)。

          那如果我們給方法加上了 @Transactional 注解,那這個方法中所有的 SQL 都會放在一個事務(wù)里。否則,每條 SQL 都會單獨開啟一個事務(wù),中間被其他事務(wù)修改了數(shù)據(jù),都會實時讀取到。

          有些情況下,當(dāng)一次執(zhí)行多條查詢語句時,需要保證數(shù)據(jù)一致性時,就需要啟用事務(wù)支持。否則上一條 SQL 查詢后,被其他用戶改變了數(shù)據(jù),那么下一個 SQL 查詢可能就會出現(xiàn)不一致的狀態(tài)。

          事務(wù)的回滾策略

          默認情況下,事務(wù)只在出現(xiàn)運行時異常(Runtime Exception)時回滾,以及 Error,出現(xiàn)檢查異常(checked exception,需要主動捕獲處理或者向上拋出)時不回滾。

          https://tobebetterjavaer.com/exception/gailan.html

          如果你想要回滾特定的異常類型的話,可以這樣設(shè)置:

          @Transactional(rollbackFor=?MyException.class)

          關(guān)于 Spring Boot 對事務(wù)的支持

          以前,我們需要通過 XML 配置 Spring 來托管事務(wù),有了 Spring Boot 之后,一切就變得更加簡單了,只需要在業(yè)務(wù)層添加事務(wù)注解(@Transactional)就可以快速開啟事務(wù)。

          也就是說,我們只需要把焦點放在 @Transactional 注解上就可以了。

          @Transactional 的作用范圍

          • 類上,表明類中所有 public 方法都啟用事務(wù)
          • 方法上,最常用的一種
          • 接口上,不推薦使用

          @Transactional 的常用配置參數(shù)

          雖然 @Transactional 注解源碼中定義了很多屬性,但大多數(shù)時候,我都是采用默認配置,當(dāng)然了,如果需要自定義的話,前面也都說明過了。

          @Transactional 的使用注意事項總結(jié)

          1)要在 public 方法上使用,在AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法中有個判斷,如果目標方法不是public,則TransactionAttribute返回null,即不支持事務(wù)。

          protected?TransactionAttribute?computeTransactionAttribute(Method?method,?@Nullable?Class?targetClass)?{
          ????//?Don't?allow?no-public?methods?as?required.
          ????if?(allowPublicMethodsOnly()?&&?!Modifier.isPublic(method.getModifiers()))?{
          ??????return?null;
          ????}

          ????//?The?method?may?be?on?an?interface,?but?we?need?attributes?from?the?target?class.
          ????//?If?the?target?class?is?null,?the?method?will?be?unchanged.
          ????Method?specificMethod?=?AopUtils.getMostSpecificMethod(method,?targetClass);

          ????//?First?try?is?the?method?in?the?target?class.
          ????TransactionAttribute?txAttr?=?findTransactionAttribute(specificMethod);
          ????if?(txAttr?!=?null)?{
          ??????return?txAttr;
          ????}

          ????//?Second?try?is?the?transaction?attribute?on?the?target?class.
          ????txAttr?=?findTransactionAttribute(specificMethod.getDeclaringClass());
          ????if?(txAttr?!=?null?&&?ClassUtils.isUserLevelMethod(method))?{
          ??????return?txAttr;
          ????}

          ????if?(specificMethod?!=?method)?{
          ??????//?Fallback?is?to?look?at?the?original?method.
          ??????txAttr?=?findTransactionAttribute(method);
          ??????if?(txAttr?!=?null)?{
          ????????return?txAttr;
          ??????}
          ??????//?Last?fallback?is?the?class?of?the?original?method.
          ??????txAttr?=?findTransactionAttribute(method.getDeclaringClass());
          ??????if?(txAttr?!=?null?&&?ClassUtils.isUserLevelMethod(method))?{
          ????????return?txAttr;
          ??????}
          ????}
          ????return?null;
          ??}

          2)避免同一個類中調(diào)用 @Transactional 注解的方法,這樣會導(dǎo)致事務(wù)失效。

          更多事務(wù)失效的場景

          測試事務(wù)是否起效

          在測試之前,我們先把 Spring Boot 默認的日志級別 info 調(diào)整為 debug,在 application.yml 文件中 修改:

          logging:
          ??level:
          ????org:
          ??????hibernate:?debug
          ??????springframework:
          ????????web:?debug

          然后,來看修改之前查到的數(shù)據(jù):

          開搞。在控制器中添加一個 update 接口,準備修改數(shù)據(jù),打算把沉默王二的狗腿子修改為沉默王二的狗腿:

          @RequestMapping("/update")
          public?String?update(Model?model)?{
          ????User?user?=?userService.findById(2);
          ????user.setName("沉默王二的狗腿");
          ????userService.update(user);
          ????return?"update";
          }

          在 Service 中為方法加上 @Transactional 注解并拋出運行時異常:

          @Override
          @Transactional
          public?void?update(User?user)?{
          ????userRepository.save(user);
          ????throw?new?RuntimeException("啊,出現(xiàn)妖怪了!");
          }

          按照我們的預(yù)期,當(dāng)執(zhí)行 save 保存數(shù)據(jù)后,因為出現(xiàn)了異常,所以事務(wù)要回滾。所以數(shù)據(jù)不會被修改。

          在瀏覽器中輸入 http://localhost:8080/user/update 進行測試,注意查看日志,可以確認事務(wù)起效了。

          當(dāng)我們把事務(wù)去掉,同樣拋出異常:

          @Override
          public?void?update(User?user)?{
          ????userRepository.save(user);
          ????throw?new?RuntimeException("啊,出現(xiàn)妖怪了!");
          }

          再次執(zhí)行,發(fā)現(xiàn)雖然程序報錯了,但數(shù)據(jù)卻被更新了。

          這也間接地證明,我們的 @Transactional 事務(wù)起效了。

          看到這,是不是就明白為什么新同事的優(yōu)化純屬畫蛇添足/卵用了吧?

          項目源碼

          • 編程喵:https://github.com/itwanger/coding-more
          • 本項目源碼:https://github.com/itwanger/codingmore-learning

          參考來源:

          • 維基百科:https://zh.wikipedia.org/wiki/ACID
          • 維基百科:https://zh.wikipedia.org/wiki/事務(wù)隔離
          • 廖雪峰:https://www.liaoxuefeng.com/wiki/1177760294764384/1179611198786848
          • JavaGuide:https://juejin.cn/post/6844903608224333838
          • 全菜工程師小輝:https://aijishu.com/a/1060000000013284
          • 空無:https://segmentfault.com/a/1190000040130617
          • 一只襪子:https://www.jianshu.com/p/380a9d980ca5

          最后,如果你感興趣的話,歡迎加入 二哥的編程知識星球 (點擊了解詳情),和 150 多名 小伙伴一起交流學(xué)習(xí),這是一個 Java 學(xué)習(xí)指南 + 編程實戰(zhàn)的私密圈子,你可以向二哥提問、幫你制定學(xué)習(xí)計劃、跟著二哥一起做實戰(zhàn)項目。

          沒有什么使我停留——除了目的,縱然岸旁有玫瑰、有綠蔭、有寧靜的港灣,我是不系之舟

          推薦閱讀

          瀏覽 53
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕在线视频无码 | 欧美精品成人在线视频 | 小骚屄在线 | 日本 Ⅴ一区二区三区色情 | www.精品在线播放国产区 |