@Transactional事务的使用和注意事项及其属性
事務(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注解即可。
常見坑點
常見坑點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ù)是不會起做用的。(常常在這里出錯)
二、異常回滾效果
使用了@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ù)不會回滾。
注意點
測試
@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,也不會回滾
分析
解決
@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異常
只讀事務(wù)
這樣就做成一個只讀事務(wù),可以提高效率
更多
查看方法的事務(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)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java版的防抖(debounce)和节
- 下一篇: cron表达式语法