日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

@Transactional事务的使用和注意事项及其属性

發(fā)布時間:2025/3/21 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 @Transactional事务的使用和注意事项及其属性 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

事務(wù)管理

提示
@Transactional注解只能應(yīng)用到public可見度的方法上,可以被應(yīng)用于接口定義和接口方法,方法會覆蓋類上面聲明的事務(wù)。

示例:

例如用戶新增需要插入用戶表、用戶與崗位關(guān)聯(lián)表、用戶與角色關(guān)聯(lián)表,如果插入成功,那么一起成功,如果中間有一條出現(xiàn)異常,那么回滾之前的所有操作, 這樣可以防止出現(xiàn)臟數(shù)據(jù),就可以使用事務(wù)讓它實現(xiàn)回退。
做法非常簡單,我們只需要在方法或類添加@Transactional注解即可。

@Transactional public int insertUser(User user) {// 新增用戶信息int rows = userMapper.insertUser(user);// 新增用戶崗位關(guān)聯(lián)insertUserPost(user);// 新增用戶與角色管理insertUserRole(user);return rows; }

常見坑點

常見坑點1:遇到檢查異常時,事務(wù)開啟,也無法回滾。 例如下面這段代碼,用戶依舊增加成功,并沒有因為后面遇到檢查異常而回滾!!

@Transactional public int insertUser(User user) throws Exception {// 新增用戶信息int rows = userMapper.insertUser(user);// 新增用戶崗位關(guān)聯(lián)insertUserPost(user);// 新增用戶與角色管理insertUserRole(user);// 模擬拋出SQLException異常boolean flag = true;if (flag){throw new SQLException("發(fā)生異常了..");}return rows; }

原因分析:因為Spring的默認(rèn)的事務(wù)規(guī)則是遇到運行異常(RuntimeException)和程序錯誤(Error)才會回滾。如果想針對檢查異常進(jìn)行事務(wù)回滾,可以在@Transactional注解里使用 rollbackFor屬性明確指定異常。

例如下面這樣,就可以正常回滾:

@Transactional(rollbackFor = Exception.class) public int insertUser(User user) throws Exception {// 新增用戶信息int rows = userMapper.insertUser(user);// 新增用戶崗位關(guān)聯(lián)insertUserPost(user);// 新增用戶與角色管理insertUserRole(user);// 模擬拋出SQLException異常boolean flag = true;if (flag){throw new SQLException("發(fā)生異常了..");}return rows; }

常見坑點2:在業(yè)務(wù)層捕捉異常后,發(fā)現(xiàn)事務(wù)不生效。 這是許多新手都會犯的一個錯誤,在業(yè)務(wù)層手工捕捉并處理了異常,你都把異常“吃”掉了,Spring自然不知道這里有錯,更不會主動去回滾數(shù)據(jù)。

例如:下面這段代碼直接導(dǎo)致用戶新增的事務(wù)回滾沒有生效。

@Transactional public int insertUser(User user) throws Exception {// 新增用戶信息int rows = userMapper.insertUser(user);// 新增用戶崗位關(guān)聯(lián)insertUserPost(user);// 新增用戶與角色管理insertUserRole(user);// 模擬拋出SQLException異常boolean flag = true;if (flag){try{// 謹(jǐn)慎:盡量不要在業(yè)務(wù)層捕捉異常并處理throw new SQLException("發(fā)生異常了..");}catch (Exception e){e.printStackTrace();}}return rows; }

推薦做法:在業(yè)務(wù)層統(tǒng)一拋出異常,然后在控制層統(tǒng)一處理。

@Transactional public int insertUser(User user) throws Exception {// 新增用戶信息int rows = userMapper.insertUser(user);// 新增用戶崗位關(guān)聯(lián)insertUserPost(user);// 新增用戶與角色管理insertUserRole(user);// 模擬拋出SQLException異常boolean flag = true;if (flag){throw new RuntimeException("發(fā)生異常了..");}return rows; }

一、注意事項

  • 不要在接口上聲明@Transactional ,而要在具體類的方法上使用 @Transactional 注解,不然注解可能無效。
  • 不要將@Transactional放置在類級的聲明中,放在類聲明,會使得全部方法都有事務(wù)。所以@Transactional應(yīng)該放在方法級別,不需要使用事務(wù)的方法,就不要放置事務(wù),好比查詢方法。不然對性能是有影響的。
  • 使用了@Transactional的方法,對同一個類里面的方法調(diào)用, @Transactional無效。
    好比有一個類Test,它的一個方法A,A再調(diào)用Test本類的方法B(無論B是否public仍是private),但A沒有聲明注解事務(wù),而B有。則外部調(diào)用A以后,B的事務(wù)是不會起做用的。(常常在這里出錯)
public class Test{public void A(){ B(); }@Transactionalpublic void B(){ } }

二、異常回滾效果

  • 使用了@Transactional的方法,只能是public,@Transactional注解的方法都是被外部其余類調(diào)用才有效,故只能是public。道理和上面的有關(guān)聯(lián)。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會報錯,但事務(wù)無效。

  • 拋出受檢查異常XXXException,事務(wù)會回滾。

  • 拋出運行時異常NullPointerException,事務(wù)會回滾。

  • 在service中加上@Transactional,如果是直接調(diào)該方法,會回滾,如果是間接調(diào)用,不會回滾。(即上文3提到的)

  • 在service中的private加上@Transactional,事務(wù)不會回滾。

  • 注意點

  • Spring默認(rèn)情況下會對(RuntimeException)及其子類來進(jìn)行回滾,在遇見Exception及其子類的時候則不會進(jìn)行回滾操作。
  • @Transactional既可以作用于接口,接口方法上以及類上,類的方法上。但是Spring官方不建議接口或者接口方法上使用該注解,因為這只有在使用基于接口的代理時它才會生效。另外,@Transactional注解應(yīng)該只被應(yīng)用到 public 方法上,這是由 Spring AOP 的本質(zhì)決定的。如果你在protected、private或者默認(rèn)可見性的方法上使用@Transactional 注解,這將被忽略,也不會拋出任何異常。
  • Spring默認(rèn)使用的是jdk自帶的基于接口的代理,而沒有使用基于類的CGLIB代理。
  • 如果在接口、實現(xiàn)類或方法上都指定了@Transactional 注解,則優(yōu)先級順序為:方法>實現(xiàn)類>接口;
  • 建議只在實現(xiàn)類或?qū)崿F(xiàn)類的方法上使用@Transactional,而不要在接口上使用,這是因為如果使用JDK代理機制(基于接口的代理)是沒問題;而使用CGLIB代理(繼承)機制時就會遇到問題,因為其使用基于類的代理而不是接口,這是因為接口上的@Transactional注解是“不能繼承的”;
  • 測試

    @Overridepublic void test(User user){test1(user);}@Transactionalpublic void test1(User user){test2(user);test3(user);test4(user);}public void test2(User user){int i = userMapper.addUser(user);}public void test3(User user){user.setUsername("333");int i = userMapper.updateUser(user);throw new RuntimeException("制造一個異常");//制造一個異常//拋異常按理應(yīng)該會滾,addUser(user)執(zhí)行了,test3的updateUser(user)沒執(zhí)行,//但運行后test2,test3兩個方法都執(zhí)行了,什么鬼?//要用代理對象調(diào)用,事務(wù)才會生效}public void test4(User user){user.setUsername("444");int i = userMapper.updateUser(user);}

    結(jié)果

    以上代碼運行結(jié)果:方法test3()拋出異常,事務(wù)不會進(jìn)行回滾,方法test2和test3都執(zhí)行了,test4不執(zhí)行,即使在test2,test3,test4都加事務(wù)注解@Transactional,也不會回滾

    分析

  • 因為spring是基于接口代理,即JDk動態(tài)代理去處理事務(wù)的,這就說明是基于方法攔截的(記住這點)
  • 那么spring進(jìn)入第一個方法test()時,它沒有開啟事務(wù),就對里面嵌套的其他方法也忽略了,這也是正常的處理邏輯。
  • 試想一下,如果spring不是這樣處理就會出現(xiàn)邏輯混亂。比如此時test()方法里面執(zhí)行了一些DB操作,然后再調(diào)用其他方法test1(),如果發(fā)生異常,此時test()里面的DB操作部分沒有回滾,test1()里面卻回滾了?那豈不是出現(xiàn)了邏輯混亂?
  • 解決

    @Transactional@Overridepublic void test(User user){test1(user);}public void test1(User user){test2(user);test3(user);test4(user);}public void test2(User user){int i = userMapper.addUser(user);}public void test3(User user){user.setUsername("333");int i = userMapper.updateUser(user);throw new RuntimeException("制造一個異常");//制造一個異常}public void test4(User user){user.setUsername("444");int i = userMapper.updateUser(user);}

    結(jié)果

    以上代碼運行結(jié)果,方法test3()拋出異常,事務(wù)進(jìn)行回滾,方法test2,test3,test4都不執(zhí)行。

    try catch

    @Transactional@Overridepublic void test(User user){int i = userMapper.addUser(user);//把test2(user)放在try里面目的:抓住異常,不管test2(user)有沒有異常,都可以正常執(zhí)行addUser(user)方法try {UserService proxy=(UserService) AopContext.currentProxy();//獲取代理對象proxy.test2(user);//用代理對象調(diào)用方法開啟事務(wù)//test2(user);//單獨調(diào)用時} catch (Exception e) {logger.error("修改出現(xiàn)了異常",e.getMessage());}}@Transactional@Overridepublic void test2(User user){user.setUsername("888");int i = userMapper.updateUser(user);int x=1/0;//制造一個異常// throw new RuntimeException("制造一個異常");//制造一個異常}

    總結(jié)

    1.在方法里沒有try catch抓異常時,被調(diào)用的方法入口處如果沒有加事務(wù)注解,那么方法內(nèi)調(diào)用其他的方法(不管其他方法前面有沒有加事務(wù)注解),其他的方法的事務(wù)都不會生效;被調(diào)用的方法入口處如果加了事務(wù)注解,那么方法內(nèi)調(diào)用其他的方法(不管其他方法前面有沒有加事務(wù)注解),其他的方法的事務(wù)都會生效。

    2.在方法里有try catch抓異常時,(如下代碼)都加了事務(wù)注解,當(dāng)拋出異常后,兩個方法都不會回滾,并且數(shù)據(jù)庫記錄都發(fā)生改變(事務(wù)失效)事務(wù)失效解決:
    獲取當(dāng)前對象的動態(tài)代理對象即可。

    三、@Transactional()添加屬性

    Exception異常

  • 讓Exception異常也進(jìn)行回滾操作,在調(diào)用該方法前加上:
  • @Transactional(rollbackFor = Exception.class)
  • 讓RuntimeException不進(jìn)行回滾操作,在調(diào)用該方法前加上:
  • @Transactional(noRollbackFor=RunTimeException.class)

    只讀事務(wù)

  • 在整個方法運行前就不會開啟事務(wù):
    這樣就做成一個只讀事務(wù),可以提高效率
  • @Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

    更多

    查看方法的事務(wù)是否已經(jīng)被執(zhí)行

    TransactionSynchronizationManager.isActualTransactionActive()

    返回true:該方法中存在事務(wù),false則不存在

    事務(wù)的傳播及其屬性的意義:

    //如果有事務(wù),那么加入事務(wù),沒有的話新創(chuàng)建一個 @Transactional(propagation=Propagation.REQUIRED)//這個方法不開啟事務(wù) @Transactional(propagation=Propagation.NOT_SUPPORTED)//不管是否存在事務(wù),都創(chuàng)建一個新的事務(wù),原來的掛起,新的執(zhí)行完畢,繼續(xù)執(zhí)行老的事務(wù) @Transactional(propagation=Propagation.REQUIREDS_NEW)//必須在一個已有的事務(wù)中執(zhí)行,否則拋出異常 @Transactional(propagation=Propagation.MANDATORY)//不能在一個事務(wù)中執(zhí)行,就是當(dāng)前必須沒有事務(wù),否則拋出異常 @Transactional(propagation=Propagation.NEVER)//其他bean調(diào)用這個方法,如果在其他bean中聲明了事務(wù),就是用事務(wù)。沒有聲明,就不用事務(wù)。 @Transactional(propagation=Propagation.SUPPORTS)//如果一個活動的事務(wù)存在,則運行在一個嵌套的事務(wù)中,如果沒有活動的事務(wù),則按照REQUIRED屬性執(zhí)行,它使用一個單獨的事務(wù)。這個書屋擁有多個回滾的保存點,內(nèi)部事務(wù)的回滾不會對外部事務(wù)造成影響,它只對DataSource TransactionManager事務(wù)管理器起效。 @Transactional(propagation=Propagation.NESTED)//只讀,不能更新,刪除 @Transactional(propagation=Propagation.REQUIRED,readOnly=true)//超時30秒 @Transactional(propagation=Propagation.REQUIRED,timeout=30)//數(shù)據(jù)庫隔離級別 @Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT) 《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結(jié)

    以上是生活随笔為你收集整理的@Transactional事务的使用和注意事项及其属性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。