事務傳播行為(propagation behavior)指的就是當一個事務方法被另一個事務方法呼叫時,這個事務方法應該如何執行。
例如:methodA方法呼叫methodB方法時,methodB是繼續在呼叫者methodA的事務中執行呢,還是為自己開啟一個新事務執行,這就是由methodB的事務傳播行為決定的。
Spring在TransactionDefinition介面中規定了7種型別的事務傳播行為。
事務傳播行為是Spring框架獨有的事務增強特性,這是Spring為我們提供的強大的工具箱,使用事務傳播行為可以為我們的開發工作提供許多便利。
**說明:**父方法插入表ks_a、子方法插入表ks_b
表結構:
CREATE TABLE `ks_a` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='測試A';
CREATE TABLE `ks_b` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
`age` tinyint(4) DEFAULT NULL COMMENT '年齡',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='測試B';
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:ks_a資料插入成功,ks_b資料回滾
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insert(ksB);
throw new RuntimeException("主方法報錯");
}
@Transactional(propagation = Propagation.REQUIRED)
public void insert(KsB ksB) {
ksBDao.insert(ksB);
}
結果:兩表資料都回滾了
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:兩表資料都回滾了
父方法無事務,子方法開啟新事務
父方法有事務,子方法和父方法共用一個事務(無論父、子方法報錯,整體回滾)
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.SUPPORTS)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:資料都插入成功
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.SUPPORTS)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:兩表資料都回滾了
如果當前不存在事務,就以非事務執行
如果當前存在事務,就加入該事務
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.MANDATORY)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation ‘mandatory’
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.MANDATORY)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:兩表資料都回滾了
如果當前不存在事務,就丟擲異常
如果當前存在事務,就加入該事務
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:ks_a資料插入成功,ks_b資料回滾
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insert(ksB);
throw new RuntimeException("父方法報錯");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insert(KsB ksB) {
ksBDao.insert(ksB);
}
結果:ks_a資料回滾,ks_b資料插入成功
無論當前存不存在事務,都建立新事務
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:資料都插入成功
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:ks_a資料回滾,ks_b資料插入成功
以非事務方式執行,如果當前存在事務,父方法以事務方式執行,子方法以非事務方式執行
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insert(ksB);
}
@Transactional(propagation = Propagation.NEVER)
public void insert(KsB ksB) {
ksBDao.insert(ksB);
}
結果:org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation ‘never’
以非事務方式執行,如果當前存在事務,則丟擲異常
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.NESTED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:ks_a資料插入成功,ks_b資料回滾
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Override
@Transactional(propagation = Propagation.NESTED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:資料都回滾
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insert(ksB);
throw new RuntimeException("主方法報錯");
}
@Transactional(propagation = Propagation.NESTED)
public void insert(KsB ksB) {
ksBDao.insert(ksB);
}
結果:資料都回滾
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
try {
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
} catch (Exception e) {
//dosomething
}
}
@Transactional(propagation = Propagation.NESTED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法報錯");
}
結果:ks_a資料插入成功,ks_b資料回滾
如果當前沒有事務,則新開事務執行
如果當前存在事務,則在巢狀事務內執行
區別在於:如果當前存在事務,子方法拋異常時
NESTED在父方法可以選擇捕獲子方法,父方法資料不會回滾;
REQUIRES無論捕不捕獲,父方法資料都回滾
區別:如果當前存在事務,父方法拋異常時
NESTED資料回滾,REQUIRES也是如此
REQUIRES_NEW資料不回滾
說明:加入該事務,指的是父、子方法共用一個事務(無論父、子方法報錯,整體回滾)
父方法無事務,子方法開啟新事務
父方法有事務,就加入該事務
如果當前不存在事務,就以非事務執行
如果當前存在事務,就加入該事務
如果當前不存在事務,就丟擲異常
如果當前存在事務,就加入該事務
無論當前存不存在事務,都建立新事務
以非事務方式執行,如果當前存在事務,父方法以事務方式執行,子方法以非事務方式執行
以非事務方式執行,如果當前存在事務,則丟擲異常
如果當前沒有事務,則新開事務執行
如果當前存在事務,則在巢狀事務內執行