前言
Spring在TransactionDefinition接口中規定了7種類型的事務傳播行爲。事務傳播行爲是Spring框架獨有的事務增強特性,它不屬於的事務實際提供方數據庫行爲。這是Spring爲我們提供的強大的工具箱,使用事務傳播行可以爲我們的開發工作提供許多便利。但是人們對他的誤解也頗多,你一定也聽過“service方法事務最好不要嵌套”的傳言。要想正確的使用工具首先需要了解工具。本文對七種事務傳播行爲做詳細介紹,內容主要代碼示例的方式呈現。
基本概念
1.什麼是事務傳播行爲?
即然是傳播,那麼至少有兩個東西,纔可以發生傳播。單體不存在傳播這個行爲。事務傳播行爲(propagation behavior)指的就是當一個事務方法被另一個事務方法調用時,這個事務方法應該如何進行。
例如:methodA事務方法調用methodB事務方法時,methodB是繼續在調用者methodA的事務中運行呢,還是爲自己開啓一個新事務運行,這就是由methodB的事務傳播行爲決定的。
用僞代碼說明:
public void methodA(){
methodB();
//doSomething
}
@Transaction(Propagation=XXX)
public void methodB(){
//doSomething
}
代碼中methodA()
方法嵌套調用了methodB()
方法,methodB()
的事務傳播行爲由@Transaction(Propagation=XXX)
設置決定。這裏需要注意的是methodA()
並沒有開啓事務,某一個事務傳播行爲修飾的方法並不是必須要在開啓事務的外圍方法中調用。
2.Spring中七種事務傳播行爲
事務傳播行爲類型 | 說明 |
PROPAGATION_REQUIRED |
如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇。 解釋: 如果 如果 |
PROPAGATION_SUPPORTS |
支持當前事務,如果當前沒有事務,就以非事務方式執行。 解釋: 如果 如果
|
PROPAGATION_MANDATORY | 如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常 |
PROPAGATION_REQUIRES_NEW | 新建事務,如果當前存在事務,把當前事務掛起。 |
PROPAGATION_NOT_SUPPORTED | 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。 |
PROPAGATION_NEVER | 以非事務方式執行,如果當前存在事務,則拋出異常。 |
PROPAGATION_NESTED | 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。 |
2.1 PROPAGATION_REQUIRED
場景1:同一個Service中,methodA()沒有事務,那麼methodB()開啓事務
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
}
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景2:同一個Service中,methodA()有事務,那麼methodB()不開啓事務
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景3:ServiceA中的methodA()方法沒有事務,methodA()中調用ServiceB中methodB()開啓了事務法方法
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
場景4:ServiceA中的methodA()方法有事務,methodA()中調用ServiceB中methodB()不開啓事務法方法
//PersonServiceA類
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
數據庫結果結果分析
場景序號 | 數據庫結果 | 結果分析 |
1 |
“張三”插入數據庫 李四”未插入數據庫。 |
methodA() 方法沒有事務,則methodB() 開啓一個事務 |
2 |
“張三”插入數據庫 李四”未插入數據庫 |
methodA() 方法開啓了事務,則methodB() 加入methodA() 的事務中去不需要再開啓事務 |
3 |
“張三”插入數據庫 李四”未插入數據庫。 |
methodA() 方法沒有事務,則methodB() 開啓一個事務 |
4 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,則methodB() 無需再開啓一個事務 |
2.2 PROPAGATION_SUPPORTS
場景1:同一個Service中,methodA()沒有事務,methodB()總是非事務地執行
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景2:同一個Service中,methodA()有事務,methodB()事務執行
//注意:methodA()方法事務傳播行爲是Propagation.REQUIRED
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
//注意:單獨調用menthodB()方法,事務不起作用,不會回滾
@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景3:ServiceA中的methodA()方法沒有事務,methodA()中調用ServiceB中methodB()非事務執行
//PersonServiceA類的方法
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
場景4:ServiceA中的methodA()方法有事務,methodA()中調用ServiceB中methodB()事務執行
//PersonServiceA類
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
數據庫結果結果分析
場景序號 | 數據庫結果 | 結果分析 |
1 | “張三”和“李四”均插入數據庫 | methodA() 方法沒有事務,methodB() 總是非事務執行,那麼methodB() 方法出現異常也不回滾 |
2 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,那麼methodB() 跟methodA() 方法回滾 |
3 | “張三”和“李四”均插入數據庫 | methodA() 方法沒有事務,methodB() 總是非事務執行,那麼methodB() 方法出現異常也不回滾 |
4 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,那麼methodB() 跟methodA() 方法回滾 |
2.3 PROPAGATION_MANDATORY
場景1:同一個Service中,methodA()沒有事務,methodB()非事務執行
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景2:同一個Service中,methodA()有事務,methodB()事務執行
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景3:ServiceA中的methodA()方法沒有事務,methodA()中調用ServiceB中methodB()方法非事務執行,且出現異常
//PersonServiceA類
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景4:ServiceA中的methodA()方法有事務,methodA()中調用ServiceB中methodB()有事務法方法
//PersonServiceA類
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
數據庫結果結果分析
場景序號 | 數據庫結果 | 結果分析 |
1 | “張三”和“李四”均插入數據庫 | methodA() 方法沒有事務,methodB() 有事務,但無效,那麼methodB() 方法出現異常也不回滾 |
2 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,methodB() 有事務,那麼methodB() 跟methodA() 方法事務回滾 |
3 |
“張三”插入數據庫 “李四”未插入數據庫 |
methodA() 方法沒有事務,調用methodB() 出現IllegalTransactionStateException |
4 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,methodB() 有事務,那麼methodB() 跟methodA() 方法回滾 |
2.4 PROPAGATION_REQUIRED_NEW
場景1:同一個Service中,methodA()沒有事務,methodB()非事務執行
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景2:同一個Service中,methodA()有事務,methodB()事務執行
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景3:ServiceA中的methodA()方法沒有事務,methodA()中調用ServiceB中methodB()事務執行
//PersonServiceA類
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景4:ServiceA中的methodA()方法有事務,methodA()中調用ServiceB中methodB()有事務法方法
數據庫結果結果分析
場景序號 | 數據庫結果 | 結果分析 |
1 | “張三”和“李四”均插入數據庫 | methodA() 方法沒有事務,methodB() 有事務但無效,那麼methodB() 方法出現異常也不回滾 |
2 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,methodB() 有事務,那麼methodB() 跟methodA() 方法回滾 |
3 |
“張三”插入數據庫 “李四”未插入數據庫 |
methodA() 方法沒有事務,methodB() 有事務,只對methodB() 事務回滾 |
4 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,methodB() 有事務,那麼methodB() 跟methodA() 方法回滾 |
2.5 PROPAGATION_NOT_SUPPORTED
場景1:同一個Service中,methodA()方法沒有事務,methodB()方法總是非事務地執行
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景2:同一個Service中,methodA()方法有事務,methodB()方法總是非事務地執行
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景3:ServiceA中的methodA()方法沒有事務,methodA()中調用ServiceB中methodB()方法總是非事務地執行
//PersonServiceA類
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
@Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景4:ServiceA中的methodA()方法有事務,methodA()中調用ServiceB中methodB()方法總是非事務地執行
//PersonServiceA類
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
@Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
數據庫結果結果分析
場景序號 | 數據庫結果 | 結果分析 |
1 | “張三”和“李四”均插入數據庫 | methodA() 方法沒有事務,methodB() 總是非事務地執行,那麼methodB() 方法出現異常也不回滾 |
2 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,methodB() 總是非事務地執行,那麼methodB() 跟methodA() 方法事務回滾 |
3 | “張三”和“李四”均插入數據庫 | methodA() 方法沒有事務,methodB() 總是非事務地執行,那麼methodB() 方法出現異常也不回滾 |
4 |
“張三”未插入數據庫 “李四”插入數據庫 |
methodA() 方法有事務掛起的不提交,methodB() 總是非事務地執行,執行完methodB() 後,methodA() 方法有事務才提交 |
2.6 PROPAGATION_NERVER
場景1:同一個Service中,methodA()方法沒有事務,methodB()總是非事務地執行
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景2:同一個Service中,methodA()方法有事務,methodB()方法總是非事務地執行
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景3:ServiceA中的methodA()方法沒有事務,methodA()中調用ServiceB中methodB()方法總是非事務地執行
//PersonServiceA類
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
@Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景4:ServiceA中的methodA()方法有事務,methodA()中調用ServiceB中methodB()方法總是非事務地執行
//PersonServiceA類
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
@Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
數據庫結果結果分析
場景序號 | 數據庫結果 | 結果分析 |
1 | “張三”和“李四”均插入數據庫 | methodA() 方法沒有事務,methodB() 總是非事務地執行,那麼methodB() 方法出現異常也不回滾 |
2 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,methodB() 總是非事務地執行,那麼methodB() 跟methodA() 方法事務回滾 |
3 | “張三”和“李四”均插入數據庫 | methodA() 方法沒有事務,methodB() 總是非事務地執行,那麼methodB() 方法出現異常也不回滾 |
4 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,methodB() 總是非事務地執行,那麼methodB() 跟methodA() 方法事務回滾 |
2.7 PROPAGATION_NESTED
場景1:同一個Service中,methodA()方法沒有事務,methodB()非事務執行
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.NESTED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景2:同一個Service中,methodA()方法有事務,methodB()方法總是非事務地執行
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
this.methodB();
}
@Transactional(propagation = Propagation.NESTED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景3:ServiceA中的methodA()方法沒有事務,methodA()中調用ServiceB中methodB()方法事務地執行
//PersonServiceA類
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
//@Transactional(propagation = Propagation.NESTED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
場景4:ServiceA中的methodA()方法有事務,methodA()中調用ServiceB中methodB()方法總是非事務地執行
//PersonServiceA類
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("張三");
personPO.setAge(22);
personDao.insert(personPO);
personServiceB.methodB();
}
//PersonServiceB類
//@Transactional(propagation = Propagation.NESTED,rollbackFor = Exception.class)
public void methodB(){
TbPersonPO personPO=new TbPersonPO();
personPO.setName("李四");
personPO.setAge(24);
personDao.insert(personPO);
throw new RuntimeException();
}
數據庫結果結果分析
場景序號 | 數據庫結果 | 結果分析 |
1 | “張三”和“李四”均插入數據庫 | methodA() 方法沒有事務,methodB() 總是非事務地執行,那麼methodB() 方法出現異常也不回滾 |
2 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,methodB() 總是非事務地執行,那麼methodB() 跟methodA() 方法事務回滾 |
3 |
“張三”插入數據庫 “李四”未插入數據庫 |
methodA() 方法沒有事務,methodB() 總事務執行,methodB() 中的事務回滾 |
4 | “張三”和“李四”均未插入數據庫 | methodA() 方法有事務,那麼methodB() 跟methodA() 方法事務回滾 |
參考:https://blog.csdn.net/weixin_39625809/article/details/80707695