這篇文章主要介紹了Spring事務(wù)失效問題分析及解決方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
成都創(chuàng)新互聯(lián)長期為上千多家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為土默特左旗企業(yè)提供專業(yè)的成都網(wǎng)站建設(shè)、成都做網(wǎng)站,土默特左旗網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
隔離級別
在 TransactionDefinition.java 接口中,定義了“四種”的隔離級別枚舉:
/** * 【Spring 獨有】使用后端數(shù)據(jù)庫默認的隔離級別 * * MySQL 默認采用的 REPEATABLE_READ隔離級別 * Oracle 默認采用的 READ_COMMITTED隔離級別 */ int ISOLATION_DEFAULT = -1; /** * 最低的隔離級別,允許讀取尚未提交的數(shù)據(jù)變更,可能會導(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; /** * 對同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。 */ int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; /** * 最高的隔離級別,完全服從ACID的隔離級別。所有的事務(wù)依次逐個執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說,該級別可以防止臟讀、不可重復(fù)讀以及幻讀。 * * 但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。 */ int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
事務(wù)的傳播級別
事務(wù)的傳播行為,指的是當(dāng)前帶有事務(wù)配置的方法,需要怎么處理事務(wù);例如:方法可能繼續(xù)在現(xiàn)有事務(wù)中運行,也可能開啟一個新事務(wù),并在自己的事務(wù)中運行;
需要注意,事務(wù)的傳播級別,并不是數(shù)據(jù)庫事務(wù)規(guī)范中的名詞,而是 Spring 自身所定義的。通過事務(wù)的傳播級別,Spring 才知道如何處理事務(wù),是創(chuàng)建一個新事務(wù)呢,還是繼續(xù)使用當(dāng)前的事務(wù);
在 TransactionDefinition.java 接口中,定義了三類七種傳播級別:
// ========== 支持當(dāng)前事務(wù)的情況 ========== /** * 如果當(dāng)前存在事務(wù),則使用該事務(wù)。 * 如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù)。 */ int PROPAGATION_REQUIRED = 0; /** * 如果當(dāng)前存在事務(wù),則使用該事務(wù)。 * 如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。 */ int PROPAGATION_SUPPORTS = 1; /** * 如果當(dāng)前存在事務(wù),則使用該事務(wù)。 * 如果當(dāng)前沒有事務(wù),則拋出異常。 */ int PROPAGATION_MANDATORY = 2; // ========== 不支持當(dāng)前事務(wù)的情況 ========== /** * 創(chuàng)建一個新的事務(wù)。 * 如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。 */ int PROPAGATION_REQUIRES_NEW = 3; /** * 以非事務(wù)方式運行。 * 如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。 */ int PROPAGATION_NOT_SUPPORTED = 4; /** * 以非事務(wù)方式運行。 * 如果當(dāng)前存在事務(wù),則拋出異常。 */ int PROPAGATION_NEVER = 5; // ========== 其他情況 ========== /** * 如果當(dāng)前存在事務(wù),則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運行。 * 如果當(dāng)前沒有事務(wù),則等價于 {@link TransactionDefinition#PROPAGATION_REQUIRED} */ int PROPAGATION_NESTED = 6;
@Transaction
@Transaction 注解是Spring的tx模塊提供的,使用AOP實現(xiàn)的事務(wù)控制,即底層為動態(tài)代理;
@Transaction 可以作用于接口、接口方法、類以及類方法上;當(dāng)作用于類上時,該類的所有 public 方法將都具有該類型的事務(wù)屬性,也可以在方法級別使用該標(biāo)注來覆蓋類級別的定義;
事務(wù)失效
1.異常類型錯誤,默認是RuntimException才會回滾
2.異常被catch后沒有拋出,需要拋異常才能回滾
3.是否發(fā)生自身調(diào)用的問題
4.注解所在位置是否為public修飾
5.數(shù)據(jù)源沒有配置事務(wù)管理器
6.不支持事務(wù)的引擎,如MyIsam
下面是一個事務(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方法時,會調(diào)用child方法,但執(zhí)行完成后插入的記錄有兩條,一條是parent的,一條是child的;
這是因為上面的parent方法調(diào)用的child方法出現(xiàn)問題,@Transaction 是基于AOP的方式進行事務(wù)控制的(CglibAopProxy.java進行方法攔截,TransactionInterceptor.java進行代理對象調(diào)用執(zhí)行方法),需要使用的是代理對象調(diào)用方法,上面的代碼使用的還是this,即當(dāng)前實例化對象,因此執(zhí)行this.child(),方法不能被攔截增強;
將上面的parent方法修改如下
@Autowired private ApplicationContext context; private OrderService orderService; @PostConstruct public void init() { orderService = context.getBean(OrderService.class); } @Transactional @Override public void parent() { try { //獲取代理對象,通過代理對象調(diào)用child() orderService.child(); //獲取代理對象 //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)異常的時候,事務(wù)會進行回滾;
如果想實現(xiàn)當(dāng)調(diào)用parent方法時,調(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ù)并開啟一個新事務(wù)執(zhí)行,新事務(wù)執(zhí)行完畢后,喚醒之前的掛起的事務(wù),則繼續(xù)執(zhí)行;如果當(dāng)前不存在事務(wù),則新建一個事務(wù);
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
文章標(biāo)題:Spring事務(wù)失效問題分析及解決方案
當(dāng)前鏈接:http://m.rwnh.cn/article8/jepeip.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、標(biāo)簽優(yōu)化、網(wǎng)站策劃、外貿(mào)建站、商城網(wǎng)站、網(wǎng)站設(shè)計公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)