我們在使用Spring框架進(jìn)行開發(fā)時,經(jīng)常在service
層寫很多方法,而且這些方法都是帶事務(wù)
的,那么Spring的事務(wù)怎么在多個方法之間傳播呢?今天我們就仔細(xì)聊一聊。
Spring的事務(wù)傳播機(jī)制主要解決在多個方法之間,事務(wù)如何傳遞的問題,通常有7種傳播類型:
REQUIRED
SUPPORTS
MANDATORY
REQUIRES_NEW
NOT_SUPPORTED
NEVER
NESTED
下面我們就一一演示這7種類型是如何工作的。
基礎(chǔ)代碼
在講解7種傳播類型之前,我們先看看基礎(chǔ)代碼,代碼很簡單,大家先熟悉一下:
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
//拋出異常
int i = 1 / 0 ;
}
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
//拋出異常
int i = 1 / 0 ;
}
方法outerTransaction()
向表中插入文本"outerTransaction",然后調(diào)用innerTransaction()
方法,最后通過計算1 / 0
拋出異常。
方法innerTransaction()
向表中插入文本"innerTransaction",通過計算1 / 0
拋出異常。
這里我們在調(diào)用innerTransaction()
方法時,先獲取當(dāng)前的AOP代理,再通過代理調(diào)用。這是因為兩個方法在同一個類中,如果不通過代理,直接調(diào)用,會脫離Spring事務(wù)AOP的管理,導(dǎo)致事務(wù)失效。
我們在這兩個方法上使用注解,并配置不同的傳播機(jī)制,通過查看數(shù)據(jù)庫是否插入數(shù)據(jù)成功來演示不同傳播機(jī)制的效果。
REQUIRED
REQUIRED
是Spring默認(rèn)的傳播機(jī)制,含義:如果當(dāng)前存在事務(wù),則加入該事務(wù),如果不存在事務(wù),則創(chuàng)建一個事務(wù)。下面我們分別演示一下:
- 如果不存在事務(wù),則創(chuàng)建一個事務(wù)。具體代碼如下:
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
}
@Transactional(propagation = Propagation.REQUIRED)
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
//拋出異常
int i = 1 / 0 ;
}
outerTransaction()方法沒有事務(wù)注解,雖然調(diào)用innerTransaction()方法時有異常拋出,插入數(shù)據(jù)也應(yīng)該成功。innerTransaction()方法有事務(wù)注解,傳播方式為:REQUIRED
,由于outerTransaction()沒有事務(wù),所以會新創(chuàng)建一個事務(wù),后面有異常拋出,所以數(shù)據(jù)不會插入成功,我們測試一下,看看結(jié)果如何?
和我們的預(yù)期是一致的,innerTransaction()
創(chuàng)建了新的事務(wù),由于拋出異常,所以數(shù)據(jù)沒有插入成功。
- 如果當(dāng)前存在事務(wù),則加入該事務(wù),代碼如下:
@Transactional(propagation = Propagation.REQUIRED)
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
//拋出異常
int i = 1 / 0 ;
}
@Transactional(propagation = Propagation.REQUIRED)
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
}
outerTransaction()增加了事務(wù)注解,傳播類型為REQUIRED
,由于之前沒有事務(wù),所以新創(chuàng)建了一個事務(wù),然后調(diào)用innerTransaction(),innerTransaction()的傳播類型也為REQUIRED
,由于前面有事務(wù),所以加入事務(wù),最后outerTransaction()拋出異常,由于兩個方法在同一個事務(wù)中,所以兩個數(shù)據(jù)都不會插入成功。我們測試一下,
和我們的預(yù)期是一致的,innerTransaction()加入了outerTransaction()的事務(wù),拋出異常后,兩條數(shù)據(jù)都不會插入成功。
SUPPORTS
如果當(dāng)前存在事務(wù),則加入該事務(wù),如果不存在事務(wù),則以非事務(wù)的方式執(zhí)行。同樣我們分別演示一下。
- 如果當(dāng)前存在事務(wù),則加入該事務(wù),代碼如下:
@Transactional(propagation = Propagation.REQUIRED)
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
//拋出異常
int i = 1 / 0 ;
}
@Transactional(propagation = Propagation.SUPPORTS)
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
}
outerTransaction()是有事務(wù)的,innerTransaction()的傳播類型為:SUPPORTS
,則會加入到事務(wù)中,由于兩個方法在同一個事務(wù)中,拋出異常后,兩條數(shù)據(jù)都不會插入成功,我們測試一下,
和預(yù)期一致,沒有問題。
- 如果不存在事務(wù),則以非事務(wù)的方式執(zhí)行,具體代碼如下:
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
//拋出異常
int i = 1 / 0 ;
}
我們將outerTransaction()方法的事務(wù)注解去掉,拋出異常的位置挪到innerTransaction()中,由于innerTransaction()的傳播類型是SUPPORTS
,外層是沒有事務(wù)的,所以innerTransaction()也是沒有事務(wù)的,雖然拋出了異常,但是不會回滾,兩條數(shù)據(jù)都應(yīng)該插入成功,我們測試一下,
和預(yù)期一致,沒有問題。
MANDATORY
如果當(dāng)前存在事務(wù),則加入到事務(wù)當(dāng)中;如果當(dāng)前沒有事務(wù),則拋出異常。我們分別演示一下,
- 如果當(dāng)前存在事務(wù),則加入到事務(wù)當(dāng)中,代碼如下:
@Transactional(propagation = Propagation.REQUIRED)
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
//拋出異常
int i = 1 / 0 ;
}
@Transactional(propagation = Propagation.MANDATORY)
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
}
outerTransaction()方法是有事務(wù)的,innerTransaction()方法的傳播類型是MANDATORY
,會加入到事務(wù)中,由于outerTransaction()方法拋出了異常,所以兩條數(shù)據(jù)都不會成功,我們測試一下,
和預(yù)期一致,都沒有成功。
- 如果當(dāng)前沒有事務(wù),則拋出異常,代碼如下:
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
//拋出異常
int i = 1 / 0 ;
}
@Transactional(propagation = Propagation.MANDATORY)
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
}
我們只是去掉了outerTransaction()上的事務(wù)注解,我們看一下會不會拋出異常,測試一下,
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
確實(shí)是拋出了異常,我們再查看一下數(shù)據(jù)庫的數(shù)據(jù),
outerTransaction()方法的數(shù)據(jù)插入成功了,因為outerTransaction()方法沒有事務(wù),雖然后面的方法拋出了異常,但數(shù)據(jù)還是會插入成功。
REQUIRES_NEW
總是創(chuàng)建一個新的事務(wù),如果當(dāng)前存在事務(wù),則掛起當(dāng)前事務(wù)。這句話怎么理解呢?我們看看下面的代碼,
@Transactional(propagation = Propagation.REQUIRED)
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
//拋出異常
int i = 1 / 0 ;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
}
outerTransaction()是有事務(wù)的,innerTransaction()方法的傳播類型是REQUIRES_NEW
,會創(chuàng)建一個新的事務(wù),雖然outerTransaction()最后拋出了異常,由于兩個方法是兩個事務(wù),所以異常只會對外層事務(wù)回滾,我們測試一下,
innerTransaction()插入數(shù)據(jù)成功,outerTransaction()方法由于有異常,所以進(jìn)行了回滾。如果將異常從外層挪到內(nèi)層,也就是外層不拋出異常,而內(nèi)層拋出異常,執(zhí)行結(jié)果會是什么樣子呢?小伙伴們自己思考一下吧。
NOT_SUPPORTED
以非事務(wù)的方式執(zhí)行操作,如果當(dāng)前存在事務(wù),則掛起當(dāng)前事務(wù)。這種傳播類型說明方法都是非事務(wù)的,不管外層有沒有事務(wù),我們先看看代碼,
@Transactional(propagation = Propagation.REQUIRED)
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
//拋出異常
int i = 1 / 0 ;
}
outerTransaction()是有事務(wù)的,innerTransaction()的傳播類型是NOT_SUPPORTED
,說明innerTransaction()以非事務(wù)執(zhí)行,數(shù)據(jù)插入后,拋出異常,由于外層是有事務(wù)的,所以外層事務(wù)回滾,我們測試一下,
和預(yù)期是一致的,內(nèi)層以非事務(wù)執(zhí)行,插入數(shù)據(jù)成功,外層有事務(wù),而且有異常,所以事務(wù)回滾。
NEVER
以非事務(wù)的方式執(zhí)行操作,如果當(dāng)前存在事務(wù),則拋出異常。我們具體看一下代碼,
@Transactional(propagation = Propagation.REQUIRED)
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
}
@Transactional(propagation = Propagation.NEVER)
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
//拋出異常
int i = 1 / 0 ;
}
outerTransaction()有事務(wù),innerTransaction() 的傳播類型是NEVER
,由于外層方法有事務(wù),所以要拋異常,外層方法也要回滾,所以兩條數(shù)據(jù)都不會插入成功,我們測試一下,
拋出的異常是:
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
再看看數(shù)據(jù)庫中的數(shù)據(jù),
和預(yù)期是一致的。
NESTED
如果當(dāng)前存在事務(wù),則在當(dāng)前事務(wù)中創(chuàng)建一個新的嵌套事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的任務(wù)。我們分別看一下是什么意思。
- 如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的任務(wù)。這個感覺和REQUIRED是一樣的,我們先看看代碼,
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
currentProxy.innerTransaction();
}
@Transactional(propagation = Propagation.NESTED)
public void innerTransaction() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction");
transactionPropagationMapper.insert(tp);
//拋出異常
int i = 1 / 0 ;
}
outerTransaction()沒有事務(wù),innerTransaction()的傳播類型是NESTED
,會創(chuàng)建一個新的事務(wù),由于拋出了異常所以內(nèi)層會回滾,外層沒有事務(wù),會插入數(shù)據(jù)成功,我們測試一下,
和預(yù)期一致。
- 如果當(dāng)前存在事務(wù),則在當(dāng)前事務(wù)中創(chuàng)建一個新的嵌套事務(wù)。我們再看看代碼,
@Transactional(propagation = Propagation.REQUIRED)
public void outerTransaction() {
//向表中插入文本“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("outerTransaction");
transactionPropagationMapper.insert(tp);
//調(diào)用innerTransaction方法
TransactionPropagationService currentProxy = (TransactionPropagationService)AopContext.currentProxy();
try {
currentProxy.innerTransaction1();
currentProxy.innerTransaction2();
} catch (Exception e) {
e.printStackTrace();
}
}
@Transactional(propagation = Propagation.NESTED)
public void innerTransaction1() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction1");
transactionPropagationMapper.insert(tp);
}
@Transactional(propagation = Propagation.NESTED)
public void innerTransaction2() {
//向表中插入文本“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
tp.setMethodName("innerTransaction2");
transactionPropagationMapper.insert(tp);
//拋出異常
int i = 1 / 0 ;
}
outerTransaction()有事務(wù),分別調(diào)用innerTransaction1()和innerTransaction2() ,并catch異常,innerTransaction1()和innerTransaction2()的傳播機(jī)制都是NESTED
,會分別創(chuàng)建一個內(nèi)嵌事務(wù),innerTransaction1()正常結(jié)束,沒有異常,innerTransaction2()拋出異常事務(wù)回滾,而外層由于catch了異常,方法也可以正常結(jié)束,所以不會回滾。我們預(yù)測的是:outerTransaction()插入成功,innerTransaction1()插入成功,innerTransaction2()回滾。我們測試一下,
和我們的預(yù)測是一致的。
總結(jié)
到此,Spring的7種傳播機(jī)制就介紹完了。這里邊的內(nèi)容很多,是不好記憶的,其實(shí)我們也不必死記硬背,看看源碼中的注釋就可以了。如果再不行,就翻翻我的博客多看看吧~~