Spring事務(wù)失效問(wèn)題分析及解決方案
這篇文章主要介紹了Spring事務(wù)失效問(wèn)題分析及解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
隔離級(jí)別
在 TransactionDefinition.java 接口中,定義了“四種”的隔離級(jí)別枚舉:
/** * 【Spring 獨(dú)有】使用后端數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別 * * MySQL 默認(rèn)采用的 REPEATABLE_READ隔離級(jí)別 * Oracle 默認(rèn)采用的 READ_COMMITTED隔離級(jí)別 */ int ISOLATION_DEFAULT = -1; /** * 最低的隔離級(jí)別,允許讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、幻讀或不可重復(fù)讀 */ int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; /** * 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生 */ int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; /** * 對(duì)同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。 */ int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; /** * 最高的隔離級(jí)別,完全服從ACID的隔離級(jí)別。所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說(shuō),該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。 * * 但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級(jí)別。 */ int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
事務(wù)的傳播級(jí)別
事務(wù)的傳播行為,指的是當(dāng)前帶有事務(wù)配置的方法,需要怎么處理事務(wù);例如:方法可能繼續(xù)在現(xiàn)有事務(wù)中運(yùn)行,也可能開啟一個(gè)新事務(wù),并在自己的事務(wù)中運(yùn)行;
需要注意,事務(wù)的傳播級(jí)別,并不是數(shù)據(jù)庫(kù)事務(wù)規(guī)范中的名詞,而是 Spring 自身所定義的。通過(guò)事務(wù)的傳播級(jí)別,Spring 才知道如何處理事務(wù),是創(chuàng)建一個(gè)新事務(wù)呢,還是繼續(xù)使用當(dāng)前的事務(wù);
在 TransactionDefinition.java 接口中,定義了三類七種傳播級(jí)別:
// ========== 支持當(dāng)前事務(wù)的情況 ==========
/**
* 如果當(dāng)前存在事務(wù),則使用該事務(wù)。
* 如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。
*/
int PROPAGATION_REQUIRED = 0;
/**
* 如果當(dāng)前存在事務(wù),則使用該事務(wù)。
* 如果當(dāng)前沒(méi)有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。
*/
int PROPAGATION_SUPPORTS = 1;
/**
* 如果當(dāng)前存在事務(wù),則使用該事務(wù)。
* 如果當(dāng)前沒(méi)有事務(wù),則拋出異常。
*/
int PROPAGATION_MANDATORY = 2;
// ========== 不支持當(dāng)前事務(wù)的情況 ==========
/**
* 創(chuàng)建一個(gè)新的事務(wù)。
* 如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* 以非事務(wù)方式運(yùn)行。
* 如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* 以非事務(wù)方式運(yùn)行。
* 如果當(dāng)前存在事務(wù),則拋出異常。
*/
int PROPAGATION_NEVER = 5;
// ========== 其他情況 ==========
/**
* 如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行。
* 如果當(dāng)前沒(méi)有事務(wù),則等價(jià)于 {@link TransactionDefinition#PROPAGATION_REQUIRED}
*/
int PROPAGATION_NESTED = 6;
@Transaction
@Transaction 注解是Spring的tx模塊提供的,使用AOP實(shí)現(xiàn)的事務(wù)控制,即底層為動(dòng)態(tài)代理;
@Transaction 可以作用于接口、接口方法、類以及類方法上;當(dāng)作用于類上時(shí),該類的所有 public 方法將都具有該類型的事務(wù)屬性,也可以在方法級(jí)別使用該標(biāo)注來(lái)覆蓋類級(jí)別的定義;
事務(wù)失效
1.異常類型錯(cuò)誤,默認(rèn)是RuntimException才會(huì)回滾
2.異常被catch后沒(méi)有拋出,需要拋異常才能回滾
3.是否發(fā)生自身調(diào)用的問(wèn)題
4.注解所在位置是否為public修飾
5.數(shù)據(jù)源沒(méi)有配置事務(wù)管理器
6.不支持事務(wù)的引擎,如MyIsam
下面是一個(gè)事務(wù)失效的例子
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Transactional
@Override
public void parent() {
try {
this.child();
} catch (Exception e) {
log.error("插入異常", e);
}
Order order = new Order();
order.setOrderNo("parent");
order.setStatus("0");
order.setTitle("parent");
order.setAmount("1000");
orderMapper.insert(order);
}
@Transactional
@Override
public void child() {
Order order = new Order();
order.setOrderNo("child");
order.setStatus("0");
order.setTitle("child");
order.setAmount("2000");
orderMapper.insert(order);
throw new RuntimeException();
}
}
當(dāng)調(diào)用parent方法時(shí),會(huì)調(diào)用child方法,但執(zhí)行完成后插入的記錄有兩條,一條是parent的,一條是child的;
這是因?yàn)樯厦娴膒arent方法調(diào)用的child方法出現(xiàn)問(wèn)題,@Transaction 是基于AOP的方式進(jìn)行事務(wù)控制的(CglibAopProxy.java進(jìn)行方法攔截,TransactionInterceptor.java進(jìn)行代理對(duì)象調(diào)用執(zhí)行方法),需要使用的是代理對(duì)象調(diào)用方法,上面的代碼使用的還是this,即當(dāng)前實(shí)例化對(duì)象,因此執(zhí)行this.child(),方法不能被攔截增強(qiáng);
將上面的parent方法修改如下
@Autowired
private ApplicationContext context;
private OrderService orderService;
@PostConstruct
public void init() {
orderService = context.getBean(OrderService.class);
}
@Transactional
@Override
public void parent() {
try {
//獲取代理對(duì)象,通過(guò)代理對(duì)象調(diào)用child()
orderService.child();
//獲取代理對(duì)象
//OrderService orderService = (OrderService) AopContext.currentProxy();
//orderService.child();
} catch (Exception e) {
log.error("插入異常", e);
}
Order order = new Order();
order.setOrderNo("parent");
order.setStatus("0");
order.setTitle("parent");
order.setAmount("1000");
orderMapper.insert(order);
}
執(zhí)行parent方法,當(dāng)出現(xiàn)異常的時(shí)候,事務(wù)會(huì)進(jìn)行回滾;
如果想實(shí)現(xiàn)當(dāng)調(diào)用parent方法時(shí),調(diào)用child方法發(fā)生異常,只回滾child方法插入的數(shù)據(jù),parent方法插入的數(shù)據(jù)不回滾,修改如下
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void child() {
Order order = new Order();
order.setOrderNo("child");
order.setStatus("0");
order.setTitle("child");
order.setAmount("2000");
orderMapper.insert(order);
throw new RuntimeException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
如果當(dāng)前存在事務(wù),則掛起事務(wù)并開啟一個(gè)新事務(wù)執(zhí)行,新事務(wù)執(zhí)行完畢后,喚醒之前的掛起的事務(wù),則繼續(xù)執(zhí)行;如果當(dāng)前不存在事務(wù),則新建一個(gè)事務(wù);
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- SpringMVC中事務(wù)是否可以加在Controller層的問(wèn)題
- Spring事務(wù)失效的一種原因關(guān)于this調(diào)用的問(wèn)題
- JAVA Spring中讓人頭痛的JAVA大事務(wù)問(wèn)題要如何解決你知道嗎
- 解決Spring或SpringBoot開啟事務(wù)以后無(wú)法返回自增主鍵的問(wèn)題
- 解決try-catch捕獲異常信息后Spring事務(wù)失效的問(wèn)題
- SpringBoot內(nèi)部調(diào)用事務(wù)不起作用問(wèn)題的解決方案
- 詳解Spring Boot微服務(wù)如何集成fescar解決分布式事務(wù)問(wèn)題
- Spring聲明式事務(wù)和@Aspect的攔截順序問(wèn)題的解決
- 解決spring mvc 多數(shù)據(jù)源切換,不支持事務(wù)控制的問(wèn)題
- Spring中事務(wù)幾個(gè)常見(jiàn)的問(wèn)題解決
相關(guān)文章
Java Swing實(shí)現(xiàn)簡(jiǎn)單的體重指數(shù)(BMI)計(jì)算器功能示例
這篇文章主要介紹了Java Swing實(shí)現(xiàn)簡(jiǎn)單的體重指數(shù)(BMI)計(jì)算器功能,涉及Java Swing窗口組件布局、響應(yīng)及數(shù)值運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2017-12-12
Java日期工具類時(shí)間校驗(yàn)實(shí)現(xiàn)
一般項(xiàng)目中需要對(duì)入?yún)⑦M(jìn)行校驗(yàn),比如必須是一個(gè)合法的日期,本文就來(lái)介紹一下Java日期工具類時(shí)間校驗(yàn)實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
SpringBoot實(shí)現(xiàn)自定義配置文件提示的方法
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)自定義配置文件提示的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
如何利用grep-console插件使Intellij idea顯示多顏色調(diào)試日志
這篇文章主要介紹了利用grep-console插件使Intellij idea顯示多顏色調(diào)試日志,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
簡(jiǎn)單了解Mybatis如何實(shí)現(xiàn)SQL防注入
這篇文章主要介紹了簡(jiǎn)單了解Mybatis如何實(shí)現(xiàn)SQL防注入,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
Java后臺(tái)實(shí)現(xiàn)瀏覽器一鍵導(dǎo)出下載zip壓縮包
這篇文章主要為大家詳細(xì)介紹了Java后臺(tái)實(shí)現(xiàn)瀏覽器一鍵導(dǎo)出下載zip壓縮包,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07

