@Transactional事务的使用和注意事项及其属性
事務管理
提示
@Transactional注解只能應用到public可見度的方法上,可以被應用于接口定義和接口方法,方法會覆蓋類上面聲明的事務。
示例:
例如用戶新增需要插入用戶表、用戶與崗位關聯表、用戶與角色關聯表,如果插入成功,那么一起成功,如果中間有一條出現異常,那么回滾之前的所有操作, 這樣可以防止出現臟數據,就可以使用事務讓它實現回退。
做法非常簡單,我們只需要在方法或類添加@Transactional注解即可。
常見坑點
常見坑點1:遇到檢查異常時,事務開啟,也無法回滾。 例如下面這段代碼,用戶依舊增加成功,并沒有因為后面遇到檢查異常而回滾!!
@Transactional public int insertUser(User user) throws Exception {// 新增用戶信息int rows = userMapper.insertUser(user);// 新增用戶崗位關聯insertUserPost(user);// 新增用戶與角色管理insertUserRole(user);// 模擬拋出SQLException異常boolean flag = true;if (flag){throw new SQLException("發生異常了..");}return rows; }原因分析:因為Spring的默認的事務規則是遇到運行異常(RuntimeException)和程序錯誤(Error)才會回滾。如果想針對檢查異常進行事務回滾,可以在@Transactional注解里使用 rollbackFor屬性明確指定異常。
例如下面這樣,就可以正常回滾:
@Transactional(rollbackFor = Exception.class) public int insertUser(User user) throws Exception {// 新增用戶信息int rows = userMapper.insertUser(user);// 新增用戶崗位關聯insertUserPost(user);// 新增用戶與角色管理insertUserRole(user);// 模擬拋出SQLException異常boolean flag = true;if (flag){throw new SQLException("發生異常了..");}return rows; }常見坑點2:在業務層捕捉異常后,發現事務不生效。 這是許多新手都會犯的一個錯誤,在業務層手工捕捉并處理了異常,你都把異常“吃”掉了,Spring自然不知道這里有錯,更不會主動去回滾數據。
例如:下面這段代碼直接導致用戶新增的事務回滾沒有生效。
@Transactional public int insertUser(User user) throws Exception {// 新增用戶信息int rows = userMapper.insertUser(user);// 新增用戶崗位關聯insertUserPost(user);// 新增用戶與角色管理insertUserRole(user);// 模擬拋出SQLException異常boolean flag = true;if (flag){try{// 謹慎:盡量不要在業務層捕捉異常并處理throw new SQLException("發生異常了..");}catch (Exception e){e.printStackTrace();}}return rows; }推薦做法:在業務層統一拋出異常,然后在控制層統一處理。
@Transactional public int insertUser(User user) throws Exception {// 新增用戶信息int rows = userMapper.insertUser(user);// 新增用戶崗位關聯insertUserPost(user);// 新增用戶與角色管理insertUserRole(user);// 模擬拋出SQLException異常boolean flag = true;if (flag){throw new RuntimeException("發生異常了..");}return rows; }一、注意事項
- 不要在接口上聲明@Transactional ,而要在具體類的方法上使用 @Transactional 注解,不然注解可能無效。
- 不要將@Transactional放置在類級的聲明中,放在類聲明,會使得全部方法都有事務。所以@Transactional應該放在方法級別,不需要使用事務的方法,就不要放置事務,好比查詢方法。不然對性能是有影響的。
- 使用了@Transactional的方法,對同一個類里面的方法調用, @Transactional無效。
好比有一個類Test,它的一個方法A,A再調用Test本類的方法B(無論B是否public仍是private),但A沒有聲明注解事務,而B有。則外部調用A以后,B的事務是不會起做用的。(常常在這里出錯)
二、異常回滾效果
使用了@Transactional的方法,只能是public,@Transactional注解的方法都是被外部其余類調用才有效,故只能是public。道理和上面的有關聯。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會報錯,但事務無效。
拋出受檢查異常XXXException,事務會回滾。
拋出運行時異常NullPointerException,事務會回滾。
在service中加上@Transactional,如果是直接調該方法,會回滾,如果是間接調用,不會回滾。(即上文3提到的)
在service中的private加上@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("制造一個異常");//制造一個異常//拋異常按理應該會滾,addUser(user)執行了,test3的updateUser(user)沒執行,//但運行后test2,test3兩個方法都執行了,什么鬼?//要用代理對象調用,事務才會生效}public void test4(User user){user.setUsername("444");int i = userMapper.updateUser(user);}結果
以上代碼運行結果:方法test3()拋出異常,事務不會進行回滾,方法test2和test3都執行了,test4不執行,即使在test2,test3,test4都加事務注解@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);}結果
以上代碼運行結果,方法test3()拋出異常,事務進行回滾,方法test2,test3,test4都不執行。
try catch
@Transactional@Overridepublic void test(User user){int i = userMapper.addUser(user);//把test2(user)放在try里面目的:抓住異常,不管test2(user)有沒有異常,都可以正常執行addUser(user)方法try {UserService proxy=(UserService) AopContext.currentProxy();//獲取代理對象proxy.test2(user);//用代理對象調用方法開啟事務//test2(user);//單獨調用時} catch (Exception e) {logger.error("修改出現了異常",e.getMessage());}}@Transactional@Overridepublic void test2(User user){user.setUsername("888");int i = userMapper.updateUser(user);int x=1/0;//制造一個異常// throw new RuntimeException("制造一個異常");//制造一個異常}總結
1.在方法里沒有try catch抓異常時,被調用的方法入口處如果沒有加事務注解,那么方法內調用其他的方法(不管其他方法前面有沒有加事務注解),其他的方法的事務都不會生效;被調用的方法入口處如果加了事務注解,那么方法內調用其他的方法(不管其他方法前面有沒有加事務注解),其他的方法的事務都會生效。
2.在方法里有try catch抓異常時,(如下代碼)都加了事務注解,當拋出異常后,兩個方法都不會回滾,并且數據庫記錄都發生改變(事務失效)事務失效解決:
獲取當前對象的動態代理對象即可。
三、@Transactional()添加屬性
Exception異常
只讀事務
這樣就做成一個只讀事務,可以提高效率
更多
查看方法的事務是否已經被執行
TransactionSynchronizationManager.isActualTransactionActive()返回true:該方法中存在事務,false則不存在
事務的傳播及其屬性的意義:
//如果有事務,那么加入事務,沒有的話新創建一個 @Transactional(propagation=Propagation.REQUIRED)//這個方法不開啟事務 @Transactional(propagation=Propagation.NOT_SUPPORTED)//不管是否存在事務,都創建一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務 @Transactional(propagation=Propagation.REQUIREDS_NEW)//必須在一個已有的事務中執行,否則拋出異常 @Transactional(propagation=Propagation.MANDATORY)//不能在一個事務中執行,就是當前必須沒有事務,否則拋出異常 @Transactional(propagation=Propagation.NEVER)//其他bean調用這個方法,如果在其他bean中聲明了事務,就是用事務。沒有聲明,就不用事務。 @Transactional(propagation=Propagation.SUPPORTS)//如果一個活動的事務存在,則運行在一個嵌套的事務中,如果沒有活動的事務,則按照REQUIRED屬性執行,它使用一個單獨的事務。這個書屋擁有多個回滾的保存點,內部事務的回滾不會對外部事務造成影響,它只對DataSource TransactionManager事務管理器起效。 @Transactional(propagation=Propagation.NESTED)//只讀,不能更新,刪除 @Transactional(propagation=Propagation.REQUIRED,readOnly=true)//超時30秒 @Transactional(propagation=Propagation.REQUIRED,timeout=30)//數據庫隔離級別 @Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT) 《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的@Transactional事务的使用和注意事项及其属性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java版的防抖(debounce)和节
- 下一篇: cron表达式语法