日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
一個(gè)99%的人都說(shuō)不清楚知識(shí)點(diǎn)—Spring事務(wù)傳播行為

面試過(guò)很多人,大部分都能把事務(wù)的四個(gè)特性及隔離級(jí)別說(shuō)得七七八八,但當(dāng)問(wèn)到 Spring 的傳播行為時(shí),就基本上沒(méi)人能說(shuō)出個(gè)一二三了。

創(chuàng)新互聯(lián)成立于2013年,我們提供高端重慶網(wǎng)站建設(shè)公司成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)、網(wǎng)站定制、成都營(yíng)銷(xiāo)網(wǎng)站建設(shè)微信平臺(tái)小程序開(kāi)發(fā)、微信公眾號(hào)開(kāi)發(fā)、成都網(wǎng)站推廣服務(wù),提供專(zhuān)業(yè)營(yíng)銷(xiāo)思路、內(nèi)容策劃、視覺(jué)設(shè)計(jì)、程序開(kāi)發(fā)來(lái)完成項(xiàng)目落地,為廣告設(shè)計(jì)企業(yè)提供源源不斷的流量和訂單咨詢。

我們都知道,一個(gè)事務(wù)要么成功,要么失敗。但當(dāng)若干個(gè)事務(wù)配合完成一個(gè)復(fù)雜任務(wù)時(shí),就不能簡(jiǎn)單的這樣一刀切了。我們需要根據(jù)任務(wù)之間的親疏關(guān)系來(lái)指定哪些任務(wù)需要聯(lián)動(dòng)回滾,哪些任務(wù)即使失敗也不會(huì)影響其他任務(wù)。要解決這個(gè)問(wèn)題,就需要了解事務(wù)的傳播行為了。Spring 中有七種事務(wù)的傳播行為,如下表所示:

事務(wù)傳播行為類(lèi)型

說(shuō)明

PROPAGATION_REQUIRED

如果當(dāng)前沒(méi)有事務(wù),就新建一個(gè)事務(wù),如果已經(jīng)存在一個(gè)事務(wù)中,加入到這個(gè)事務(wù)中。這是最常見(jiàn)的選擇。

PROPAGATION_SUPPORTS

支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就以非事務(wù)方式執(zhí)行。

PROPAGATION_MANDATORY

使用當(dāng)前的事務(wù),如果當(dāng)前沒(méi)有事務(wù),就拋出異常。

PROPAGATION_REQUIRES_NEW

新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。

PROPAGATION_NOT_SUPPORTED

以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。

PROPAGATION_NEVER

以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。

PROPAGATION_NESTED

如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒(méi)有事務(wù),則執(zhí)行與 PROPAGATION_REQUIRED 類(lèi)似的操作。

Spring 可以通過(guò) @Transactional 注解的 propagation 屬性來(lái)設(shè)置不同的傳播行為策略。Spring 為此提供了一個(gè)枚舉類(lèi) Propagation,源碼如下:

package org.springframework.transaction.annotation;
import org.springframework.transaction.TransactionDefinition;
public enum Propagation {
/**
* 需要事務(wù),它是默認(rèn)傳播行為,如果當(dāng)前存在事務(wù),就沿用當(dāng)前事務(wù),
* 否則新建一個(gè)事務(wù)運(yùn)行內(nèi)部方法
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
/**
* 支持事務(wù),如果當(dāng)前存在事務(wù),就沿用當(dāng)前事務(wù),
* 如果不存在,則繼續(xù)采用無(wú)事務(wù)的方式運(yùn)行內(nèi)部方法
*/
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
/**
* 必須使用事務(wù),如果當(dāng)前沒(méi)有事務(wù),則會(huì)拋出異常,
* 如果存在當(dāng)前事務(wù),則沿用當(dāng)前事務(wù)
*/
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
/**
* 無(wú)論當(dāng)前事務(wù)是否存在,都會(huì)創(chuàng)建新事務(wù)運(yùn)行方法,
* 這樣新事務(wù)就可以擁有新的鎖和隔離級(jí)別等特性,與當(dāng)前事務(wù)相互獨(dú)立
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
/**
* 不支持事務(wù),當(dāng)前存在事務(wù)時(shí),將掛起事務(wù),運(yùn)行方法
*/
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
/**
* 不支持事務(wù),如果當(dāng)前方法存在事務(wù),則拋出異常,否則繼續(xù)使用無(wú)事務(wù)機(jī)制運(yùn)行
*/
NEVER(TransactionDefinition.PROPAGATION_NEVER),
/**
* 在當(dāng)前方法調(diào)用內(nèi)部方法時(shí),如果內(nèi)部方法發(fā)生異常,
* 只回滾內(nèi)部方法執(zhí)行過(guò)的 SQL ,而不回滾當(dāng)前方法的事務(wù)
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
......
}

接下來(lái)我們通過(guò)對(duì)其中三種最常用的(REQUIRED、REQUIRES_NEW、NESTED)策略進(jìn)行對(duì)比來(lái)更深入的理解。以下測(cè)試均在外部方法開(kāi)啟事務(wù)的情況下進(jìn)行,因?yàn)樵谕獠繘](méi)有事務(wù)的情況下,三者都會(huì)新建事務(wù),效果一樣。

REQUIRED

當(dāng)內(nèi)部方法的事務(wù)傳播行為設(shè)置為 REQUIRED 時(shí),內(nèi)部方法會(huì)加入外部方法的事務(wù)。我們?cè)?UserServiceImpl 中添加如下方法:

@Service
public class UserServiceImpl extends ServiceImpl implements UserService {
@Autowired
private UserMapper mapper;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addWithRequired(User user) {
mapper.insert(user);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addWithRequiredAndException(User user) {
mapper.insert(user);
throw new RuntimeException();
}
}

創(chuàng)建 TransactionServiceImpl 類(lèi),并添加如下方法:

@Slf4j
@Service
public class TransactionServiceImpl implements TransactionService {
@Autowired
private UserService userService;
@Override
public void noTransaction_required_required_externalException() {
User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
userService.addWithRequired(xiaoShui);
userService.addWithRequired(xiaoJing);
throw new RuntimeException();
}
@Override
public void noTransaction_required_requiredException() {
User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
userService.addWithRequired(xiaoShui);
userService.addWithRequiredAndException(xiaoJing);
}
@Override
@Transactional
public void transaction_required_required_externalException() {

User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
userService.addWithRequired(xiaoShui);
userService.addWithRequired(xiaoJing);
throw new RuntimeException();
}
@Override
@Transactional
public void transaction_required_requiredException() {
User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
userService.addWithRequired(xiaoShui);
userService.addWithRequiredAndException(xiaoJing);
}
@Override
@Transactional
public void transaction_required_requiredException_try() {
User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
userService.addWithRequired(xiaoShui);
try {
userService.addWithRequiredAndException(xiaoJing);
} catch (Exception e) {
log.error("發(fā)生異常,事務(wù)回滾!");
}
}
}

結(jié)果分析如下表所示:

方法

結(jié)果

分析

noTransaction_required_required_externalException

小水和小鏡均成功入庫(kù)

外部方法未開(kāi)啟事務(wù),所以所有插入操作均未受到外部異常影響

noTransaction_required_requiredException

小水入庫(kù),小鏡未入庫(kù)

外部方法未開(kāi)啟事務(wù),內(nèi)部方法事務(wù)各自獨(dú)立,互不影響,「小鏡」的插入方法發(fā)生異常回滾,但「小水」的插入方法不受影響

transaction_required_required_externalException

小水和小鏡均未入庫(kù)

外部方法開(kāi)啟事務(wù),所有內(nèi)部方法均加入外部方法的事務(wù)中。而外部方法發(fā)生異常,所以導(dǎo)致所有操作都發(fā)生回滾

transaction_required_requiredException

小水和小鏡均未入庫(kù)

外部方法開(kāi)啟事務(wù),所有內(nèi)部方法均加入外部方法的事務(wù)中。由于「小鏡」的插入方法發(fā)生異常,此時(shí)所有方法都處于同一個(gè)事務(wù)中,所以導(dǎo)致所有操作都發(fā)生回滾

transaction_required_requiredException_try

小水和小鏡均未入庫(kù)

外部方法開(kāi)啟事務(wù),所有內(nèi)部方法均加入外部方法的事務(wù)中。由于「小鏡」的插入方法發(fā)生異常,此時(shí)所有方法都處于同一個(gè)事務(wù)中,即使發(fā)生異常的部分被 try-catch 住,所有操作仍然會(huì)回滾

前面四種情況都比較好理解,很多人不能理解最后一種情況:我都 try-catch 了你還想怎樣?這里的關(guān)鍵點(diǎn)在于所有方法都處于同一個(gè)事務(wù)中,此時(shí)「小鏡」的插入方法發(fā)生異常,那么這個(gè)方法所在的事務(wù)就會(huì)被 Spring 設(shè)置為 rollback 狀態(tài)。因?yàn)楫惓1?catch 了,所以外部方法執(zhí)行完要進(jìn)行 commit 操作,這時(shí)卻發(fā)現(xiàn)當(dāng)前事務(wù)已經(jīng)處于 rollback 狀態(tài)了,雖然它不知道哪里出了問(wèn)題,但也只能聽(tīng)從指揮,回滾所有操作了。

PS:由于外部方法不開(kāi)啟事務(wù)的情況,在每種傳播行為下結(jié)果都是類(lèi)似的,所以后面不再給出示例。

REQUIRES_NEW

當(dāng)內(nèi)部方法的傳播行為設(shè)置為 REQUIRES_NEW 時(shí),內(nèi)部方法會(huì)先將外部方法的事務(wù)掛起,然后開(kāi)啟一個(gè)新的事務(wù) 。在 UserServiceImpl 中添加如下方法:

@Service
public class UserServiceImpl extends ServiceImpl implements UserService {
...
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addWithRequiredNew(User user) {
mapper.insert(user);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addWithRequiredNewAndException(User user) {
mapper.insert(user);
throw new RuntimeException();
}
}

在 TransactionServiceImpl 中添加如下方法:

@Slf4j
@Service
public class TransactionServiceImpl implements TransactionService {
...
@Override
@Transactional
public void transaction_required_requiredNew_externalException() {
User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
userService.addWithRequired(xiaoShui);
userService.addWithRequiredNew(xiaoJing);
throw new RuntimeException();
}
@Override
@Transactional
public void transaction_required_requiredNew_requiredNewException() {
User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
User shuiJing = new User().setName("水鏡");
userService.addWithRequired(xiaoShui);
userService.addWithRequiredNew(xiaoJing);
userService.addWithRequiredNewAndException(shuiJing);
}
@Override
@Transactional
public void transaction_required_requiredNewException_try() {
User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
User shuiJing = new User().setName("水鏡");
userService.addWithRequired(xiaoShui);
userService.addWithRequiredNew(xiaoJing);
try {
userService.addWithRequiredNewAndException(shuiJing);
} catch (Exception e) {
log.error("發(fā)生異常,事務(wù)回滾!");
}

}
}

結(jié)果分析如下表所示:

方法

結(jié)果

分析

transaction_required_requiredNew_externalException

小水未入庫(kù),小鏡入庫(kù)

外部方法開(kāi)啟事務(wù),「小水」的插入方法和外部方法在同一個(gè)事務(wù)中,跟隨外部方法發(fā)生回滾;「小鏡」的插入方法開(kāi)啟一個(gè)獨(dú)立的新事務(wù),不受外部方法異常的影響

transaction_required_requiredNew_requiredNewException

小水未入庫(kù),小鏡入庫(kù),水鏡未入庫(kù)

外部方法開(kāi)啟事務(wù),「水鏡」的插入方法開(kāi)啟一個(gè)獨(dú)立的新事務(wù),因?yàn)榘l(fā)生異常,所以自己回滾了;「水鏡」的異常沒(méi)有做處理,因此會(huì)被外部方法感知到,「小水」的插入方法和外部方法在同一個(gè)事務(wù)中,跟隨外部方法發(fā)生回滾;「小鏡」的插入方法也會(huì)開(kāi)啟一個(gè)獨(dú)立的新事務(wù),因此不會(huì)受到任何方法的影響,成功入庫(kù)

transaction_required_requiredNewException_try

小水和小鏡入庫(kù),水鏡未入庫(kù)

外部方法開(kāi)啟事務(wù),「水鏡」的插入方法開(kāi)啟一個(gè)獨(dú)立的新事務(wù),因?yàn)榘l(fā)生異常,所以自己回滾了;「水鏡」的異常被 try-catch 處理了,其他方法正常提交「小水」和「小鏡」成功入庫(kù)

NESTED

當(dāng)內(nèi)部方法的傳播行為設(shè)置為 NESTED 時(shí),內(nèi)部方法會(huì)開(kāi)啟一個(gè)新的嵌套事務(wù)(子事務(wù))。在 UserServiceImpl 中添加如下方法:

@Service
public class UserServiceImpl extends ServiceImpl implements UserService {
...
@Override
@Transactional(propagation = Propagation.NESTED)
public void addWithNested(User user) {
mapper.insert(user);
}
@Override
@Transactional(propagation = Propagation.NESTED)
public void addWithNestedAndException(User user) {
mapper.insert(user);
throw new RuntimeException();
}
}

在 TransactionServiceImpl 中添加如下方法:

@Slf4j
@Service
public class TransactionServiceImpl implements TransactionService {
...
@Override
@Transactional
public void transaction_nested_nested_externalException() {
User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
userService.addWithNested(xiaoShui);
userService.addWithNested(xiaoJing);
throw new RuntimeException();
}
@Override
@Transactional
public void transaction_nested_nestedException() {
User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
userService.addWithNested(xiaoShui);
userService.addWithNestedAndException(xiaoJing);
}
@Override
@Transactional
public void transaction_nested_nestedException_try() {
User xiaoShui = new User().setName("小水");
User xiaoJing = new User().setName("小鏡");
User shuiJing = new User().setName("水鏡");
userService.addWithRequired(xiaoShui);
userService.addWithNested(xiaoJing);
try {
userService.addWithNestedAndException(shuiJing);
} catch (Exception e) {
log.error("發(fā)生異常,事務(wù)回滾!",e);
}
}
}

結(jié)果分析如下表所示:

方法

結(jié)果

分析

transaction_nested_nested_externalException

小水和小鏡均未入庫(kù)

外部方法開(kāi)啟事務(wù),內(nèi)部方法開(kāi)啟各自的子事務(wù),外部方法發(fā)生異常,主事務(wù)回滾,子事務(wù)跟隨主事務(wù)回滾

transaction_nested_nestedException

小水和小鏡均未入庫(kù)

外部方法開(kāi)啟事務(wù),內(nèi)部方法開(kāi)啟各自的子事務(wù),「小鏡」的插入方法發(fā)生異?;貪L自己的子事務(wù);「小鏡」的異常沒(méi)有做處理,因此會(huì)被外部方法感知到,「小水」的插入方法在外部方法的子事務(wù)中,所以跟隨主事務(wù)回滾

transaction_nested_nestedException_try

小水和小鏡入庫(kù),水鏡未入庫(kù)

外部方法開(kāi)啟事務(wù),「小鏡」和「水鏡」開(kāi)啟各自的子事務(wù),「小水」加入外部方法的事務(wù)?!杆R」的插入方法發(fā)生異?;貪L自己的子事務(wù);「水鏡」的異常被 try-catch 處理了,其他方法正常提交「小水」和「小鏡」成功入庫(kù)

每個(gè) NESTED 事務(wù)執(zhí)行前會(huì)將當(dāng)前操作保存下來(lái),叫做 savepoint (保存點(diǎn)),如果當(dāng)前 NESTED 事務(wù)執(zhí)行失敗,則回滾到之前的保存點(diǎn),保存點(diǎn)使得子事務(wù)的回滾不對(duì)主事務(wù)造成影響。NESTED 事務(wù)在外部事務(wù)提交以后自己才會(huì)提交。

總結(jié)

REQUIRES_NEW 最為簡(jiǎn)單,不管當(dāng)前有無(wú)事務(wù),它都會(huì)開(kāi)啟一個(gè)全新事務(wù),既不影響外部事務(wù),也不會(huì)影響其他內(nèi)部事務(wù),真正的井水不犯河水,堅(jiān)定而獨(dú)立。

REQUIRED 在沒(méi)有外部事務(wù)的情況下,會(huì)開(kāi)啟一個(gè)獨(dú)立的新事務(wù),且不會(huì)對(duì)其他同級(jí)事務(wù)造成影響;而當(dāng)存在外部事務(wù)的情況下,則會(huì)與外部事務(wù)同生共死。

NESTED 在沒(méi)有外部事務(wù)的情況下與 REQUIRED 效果相同;而當(dāng)存在外部事務(wù)的情況下,當(dāng)外部事務(wù)回滾時(shí),它會(huì)創(chuàng)建一個(gè)嵌套事務(wù)(子事務(wù))。外部事務(wù)回滾時(shí),子事務(wù)會(huì)跟著回滾;但子事務(wù)的回滾不會(huì)對(duì)外部事務(wù)和其他同級(jí)事務(wù)造成影響。


網(wǎng)頁(yè)題目:一個(gè)99%的人都說(shuō)不清楚知識(shí)點(diǎn)—Spring事務(wù)傳播行為
新聞來(lái)源:http://www.dlmjj.cn/article/codicjp.html