偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Spring事務(wù)為什么會(huì)失效?

開發(fā) 前端
當(dāng)我們換一種框架來實(shí)現(xiàn)時(shí),里面對(duì)事務(wù)控制的代碼就要推倒重寫,并不一定能保證替換后的api和之前的api有相同的行為。

不用Spring管理事務(wù)?

讓我們先來看一下不用spring管理事務(wù)時(shí),各種框架是如何管理事務(wù)的。

使用JDBC來管理事務(wù):

使用Hibernate來管理事務(wù):

業(yè)務(wù)邏輯和事務(wù)代碼是耦合到一塊的,并且和框架的具體api綁定了。當(dāng)我們換一種框架來實(shí)現(xiàn)時(shí),里面對(duì)事務(wù)控制的代碼就要推倒重寫,并不一定能保證替換后的api和之前的api有相同的行為。

「統(tǒng)一的事務(wù)抽象」

基于這些問題,Spring抽象了一些事務(wù)相關(guān)的頂層接口。無論是全局事務(wù)還是本地事務(wù),JTA,JDBC還是Hibernate,Spring都使用統(tǒng)一的編程模型。使得應(yīng)用程序可以很容易的在全局事務(wù)與本地事務(wù),或者不同事物框架之間進(jìn)行切換。

「下圖為Spring事物抽象的核心類」

常用api

接口

PlatformTransactionManager

對(duì)事務(wù)進(jìn)行管理

TransactionDefinition

定義事務(wù)的相關(guān)屬性,例如隔離級(jí)別,傳播行為

TransactionStatus

保存事務(wù)狀態(tài)

針對(duì)不同的數(shù)據(jù)訪問技術(shù),使用不用的PlatformTransactionManager類即可。

數(shù)據(jù)訪問技術(shù)

PlatformTransactionManager實(shí)現(xiàn)類

JDBC/Mybatis

DataSourceTransactionManager

Hibernate

HibernateTransactionManager

Jpa

JpaTransactionManager

Jms

JmsTransactionManager

編程式事務(wù)管理

當(dāng)我們使用Spring的事務(wù)時(shí),可以使用編程式事務(wù)或者聲明式事務(wù)。

當(dāng)使用編程式事務(wù)的時(shí)候,可以直接使用事務(wù)的頂層接口,也可以使用模版類TransactionTemplate。

使用PlatformTransactionManager

使用TransactionTemplate

當(dāng)我們直接使用PlatformTransactionManager來管理事務(wù)時(shí),有很多模版代碼。例如業(yè)務(wù)代碼正常執(zhí)行,提交事務(wù),否則回滾事務(wù)。我們可以把這部分模版代碼封裝成一個(gè)模版類,這樣使用起來就很方便了,如下所示:

如下圖所示,TransactionTemplate#execute方法就是一個(gè)典型的模版方法:

我們可以傳入如下2個(gè)接口的實(shí)現(xiàn)類來執(zhí)行業(yè)務(wù)邏輯,TransactionCallback(需要返回執(zhí)行結(jié)果)或TransactionCallbackWithoutResult(不需要返回結(jié)果)。

聲明式事務(wù)管理

為了讓使用更加簡潔,Spring直接把事務(wù)代碼的執(zhí)行放到切面中了,我們只需要在業(yè)務(wù)代碼方法上加上一個(gè)@Transactional注解即可,這種方式我們最常用哈。

使用@Transactional注解

此時(shí)事務(wù)相關(guān)的定義我們就可以通過@Transactional注解來設(shè)置了。

屬性名

類型

描述

默認(rèn)值

value(和transactionManager互為別名)

String

當(dāng)在配置文件中有多個(gè)PlatformTransactionManager ,用該屬性指定選擇哪個(gè)事務(wù)管理器

空字符串""

propagation

枚舉:Propagation

事務(wù)的傳播行為

REQUIRED

isolation

枚舉:Isolation

事務(wù)的隔離度

DEFAULT

timeout

int

事務(wù)的超時(shí)時(shí)間。如果超過該時(shí)間限制但事務(wù)還沒有完成,則自動(dòng)回滾事務(wù)

-1

readOnly

boolean

指定事務(wù)是否為只讀事務(wù)

false

rollbackFor

Class[]

需要回滾的異常

空數(shù)組{}

rollbackForClassName

String[]

需要回滾的異常類名

空數(shù)組{}

noRollbackFor

Class[]

不需要回滾的異常

空數(shù)組{}

noRollbackForClassName

String[]

不需要回滾的異常類名

空數(shù)組{}

源碼解析

我們需要在配置類上加上@EnableTransactionManagement注解,來開啟spring事務(wù)管理功能。

「TransactionManagementConfigurationSelector#selectImports」

往容器中注入AutoProxyRegistrar和ProxyTransactionManagementConfiguration這2個(gè)類,那這2個(gè)類有啥作用呢?(源碼太多了,我就不貼代碼一步一步分析了,主要是理清思路)。

AutoProxyRegistrar主要就是往容器中注入一個(gè)類InfrastructureAdvisorAutoProxyCreator,這個(gè)類有什么作用呢?

「看一下繼承關(guān)系,原來是繼承自AbstractAutoProxyCreator,用來實(shí)現(xiàn)自動(dòng)代理沒跑了!」

BeanFactoryTransactionAttributeSourceAdvisor主要就是往容器中注入了一個(gè)Advisor類,用來保存Pointcut和Advice。

對(duì)應(yīng)的Pointcut為TransactionAttributeSourcePointcut的實(shí)現(xiàn)類,是一個(gè)匿名內(nèi)部類,即篩選的邏輯是通過TransactionAttributeSourcePointcut類來實(shí)現(xiàn)的。

BeanFactoryTransactionAttributeSourceAdvisor

對(duì)應(yīng)的Advice的實(shí)現(xiàn)類為TransactionInterceptor,即針對(duì)事務(wù)增強(qiáng)的邏輯都在這個(gè)類中。

篩選的邏輯我們就先不分析了,后面會(huì)再簡單提一下。

我們來看針對(duì)事務(wù)增強(qiáng)的邏輯,當(dāng)執(zhí)行被@Transactional標(biāo)記的方法時(shí),會(huì)調(diào)用到如下方法(TransactionInterceptor#invoke有點(diǎn)類似我們的@Around)。

TransactionInterceptor#invoke

TransactionAspectSupport#invokeWithinTransaction

我挑出這個(gè)方法比較重要的幾個(gè)部分來分析吧(上圖圈出來的部分)。

  • 如果需要的話開啟事務(wù)(和傳播屬性相關(guān),我們后面會(huì)提到)。
  • 執(zhí)行業(yè)務(wù)邏輯。
  • 如果發(fā)生異常則會(huì)滾事務(wù)。
  • 如果正常執(zhí)行則提交事務(wù)。

「所以當(dāng)發(fā)生異常需要會(huì)滾的時(shí)候,我們一定不要自己把異常try catch掉,不然事務(wù)會(huì)正常提交」。

TransactionAspectSupport#createTransactionIfNecessary

當(dāng)開啟事務(wù)的時(shí)候,可以看到各種傳播屬性的行為(即@Transactional方法調(diào)用@Transactional方法會(huì)發(fā)生什么?)

AbstractPlatformTransactionManager#getTransaction

Spring事務(wù)的傳播行為在Propagation枚舉類中定義了如下幾種選擇。

「支持當(dāng)前事務(wù)」

  • REQUIRED :如果當(dāng)前存在事務(wù),則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。
  • SUPPORTS:如果當(dāng)前存在事務(wù),則加入該事務(wù) 。如果當(dāng)前沒有事務(wù), 則以非事務(wù)的方式繼續(xù)運(yùn)行。
  • MANDATORY :如果當(dāng)前存在事務(wù),則加入該事務(wù) 。如果當(dāng)前沒有事務(wù),則拋出異常

「不支持當(dāng)前事務(wù)」

  • REQUIRES_NEW :如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起,創(chuàng)建一個(gè)新事務(wù)。
  • NOT_SUPPORTED :如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起,以非事務(wù)方式運(yùn)行,
  • NEVER :如果當(dāng)前存在事務(wù),則拋出異常。

「其他情況」

NESTED :如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來執(zhí)行 。如果當(dāng)前沒有事務(wù),則該取值等價(jià)于REQUIRED。

以NESTED啟動(dòng)的事務(wù)內(nèi)嵌于外部事務(wù)中 (如果存在外部事務(wù)的話),此時(shí)內(nèi)嵌事務(wù)并不是一個(gè)獨(dú)立的事務(wù),它依賴于外部事務(wù)。只有通過外部事務(wù)的提交,才能引起內(nèi)部事務(wù)的提交,嵌套的子事務(wù)不能單獨(dú)提交。

事務(wù)失效的場景有哪些?

因?yàn)槲覀兘?jīng)常使用聲明式事務(wù),如果一步消息就會(huì)導(dǎo)致事務(wù)失效,所以我們就從源碼角度來盤一下事務(wù)為什么失效。

異常被你try catch了

首先就是我們上面剛提到的,「異常被你try catch了」。因?yàn)槁暶魇绞挛锸峭ㄟ^目標(biāo)方法是否拋出異常來決定是提交事物還是會(huì)滾事物的。

自調(diào)用

當(dāng)自調(diào)用時(shí),方法執(zhí)行不會(huì)經(jīng)過代理對(duì)象,所以會(huì)導(dǎo)致事務(wù)失效。

// 事務(wù)失效
@Service
public class UserServiceV2Impl implements UserService {

@Autowired
private JdbcTemplate jdbcTemplate;

@Override
public void addUser(String name, String location) {
doAdd(name);
}

@Transactional
public void doAdd(String name) {
String sql = "insert into user (`name`) values (?)";
jdbcTemplate.update(sql, new Object[]{name});
throw new RuntimeException("保存用戶失敗");
}
}

我們可以通過如下三種方式來解決自調(diào)用失效的場景。

「1.@Autowired注入代理對(duì)象,然后調(diào)用方法」

// @Service
public class UserServiceV3Impl implements UserService {

@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private UserService userService;

@Override
public void addUser(String name, String location) {
userService.doAdd(name);
}

@Override
@Transactional
public void doAdd(String name) {
String sql = "insert into user (`name`) values (?)";
jdbcTemplate.update(sql, new Object[]{name});
throw new RuntimeException("保存用戶失敗");
}
}

「2.從ApplicationContext獲取代理對(duì)象,然后調(diào)用方法」

@Service
public class UserServiceV4Impl implements UserService {

@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private ApplicationContext applicationContext;

@Override
public void addUser(String name, String location) {
UserService userService = applicationContext.getBean(UserService.class);
userService.doAdd(name);
}

@Override
@Transactional
public void doAdd(String name) {
String sql = "insert into user (`name`) values (?)";
jdbcTemplate.update(sql, new Object[]{name});
throw new RuntimeException("保存用戶失敗");
}
}

「3.進(jìn)行如下設(shè)置@EnableAspectJAutoProxy(exposeProxy = true),從AopContext中獲取代理對(duì)象,然后調(diào)用方法」

@Service
public class UserServiceV5Impl implements UserService {

@Autowired
private JdbcTemplate jdbcTemplate;

@Override
public void addUser(String name, String location) {
UserService userService = (UserService) AopContext.currentProxy();
userService.doAdd(name);
}

@Override
@Transactional
public void doAdd(String name) {
String sql = "insert into user (`name`) values (?)";
jdbcTemplate.update(sql, new Object[]{name});
throw new RuntimeException("保存用戶失敗");
}
}

非public方法導(dǎo)致事務(wù)失效

我們先來猜一下為什么非public方法會(huì)導(dǎo)致事務(wù)失效?

「難道是因?yàn)榉莗ublic方法不會(huì)生成代理對(duì)象?」

我們給一個(gè)非public方法加上@Transactional,debug到如下代碼看一下是否會(huì)生成代理對(duì)象。

AbstractAutoProxyCreator#wrapIfNecessary

「結(jié)論是不會(huì)生成代理對(duì)象,那為什么不會(huì)生成代理對(duì)象呢?」

應(yīng)該就是不符合Pointcut的要求了唄,我們?cè)谇懊嬉呀?jīng)提到了事務(wù)對(duì)應(yīng)的Pointcut為TransactionAttributeSourcePointcut。

TransactionAttributeSourcePointcut#matches

matches方法返回false,為什么會(huì)返回false呢?

一直debug發(fā)現(xiàn)是如下代碼導(dǎo)致的。

AbstractFallbackTransactionAttributeSource#computeTransactionAttribute

即public方法能正常生成代理對(duì)象,而非public方法因?yàn)椴环螾ointcut的要求,根本就不會(huì)生成代理對(duì)象。

異常類型不正確,默認(rèn)只支持RuntimeException和Error,不支持檢查異常

「為什么不支持檢查異常呢?」

拿出我們上面分析過的代碼:

當(dāng)執(zhí)行業(yè)務(wù)邏輯發(fā)生異常的時(shí)候,會(huì)調(diào)用到TransactionAspectSupport#completeTransactionAfterThrowing方法。

可以看到對(duì)異常類型做了判斷,根據(jù)返回的結(jié)果來決定是否會(huì)滾事務(wù),會(huì)調(diào)用到如下方法進(jìn)行判斷。

RuleBasedTransactionAttribute#rollbackOn

如果用戶指定了回滾的異常類型,則根據(jù)用戶指定的規(guī)則來判斷,否則用默認(rèn)的規(guī)則。

DefaultTransactionAttribute

默認(rèn)的規(guī)則為只支持RuntimeException和Error。

我們可以通過@Transactional屬性指定回滾的類型,一般為Exception即可。

@Transactional(rollbackFor = Exception.class)

責(zé)任編輯:武曉燕 來源: Java識(shí)堂
相關(guān)推薦

2022-09-20 22:27:08

事務(wù)失效public 修飾

2024-05-14 08:37:34

2022-09-14 19:50:22

事務(wù)場景流程

2022-02-14 16:53:57

Spring項(xiàng)目數(shù)據(jù)庫

2021-12-13 11:12:41

Spring事務(wù)失效

2021-04-15 08:01:27

Spring聲明式事務(wù)

2020-12-11 08:02:16

索引MySQL存儲(chǔ)

2022-07-05 14:19:30

Spring接口CGLIB

2022-09-22 09:57:20

Spring事務(wù)失效

2022-12-06 10:39:43

Spring事務(wù)失效

2015-07-10 09:28:09

事務(wù)性能

2024-01-29 08:28:01

Spring事務(wù)失效

2024-09-09 08:29:25

2012-03-26 10:26:43

openstackeucalyptus

2012-05-02 10:08:51

桌面Linux微軟

2012-08-17 10:01:07

云計(jì)算

2021-07-09 09:24:06

NanoID UUID軟件開發(fā)

2020-03-30 15:05:46

Kafka消息數(shù)據(jù)

2021-01-25 07:14:53

Cloud DevOps云計(jì)算

2023-03-22 09:10:18

IT文檔語言
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)