0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

分享一下Spring事務(wù)的玩法

OSC開源社區(qū) ? 來源:江南一點(diǎn)雨 ? 2022-12-30 11:41 ? 次閱讀

事務(wù)的重要性不言而喻,Spring 對(duì)事務(wù)也提供了非常豐富的支持,各種支持的屬性應(yīng)有盡有。

然而很多小伙伴知道,這里有兩個(gè)屬性特別繞:

隔離性

傳播性

有多繞呢?松哥都一直懶得寫文章去總結(jié)。不過最近有小伙伴問到這個(gè)問題,剛好有空,就抽空總結(jié)一下,我不會(huì)干巴巴的和大家講概念,接下來的所有內(nèi)容,松哥都會(huì)通過具體的案例來和大家演示。

好啦,不廢話啦,請(qǐng)看大屏幕。

1. 什么是事務(wù)

數(shù)據(jù)庫事務(wù)是指作為單個(gè)邏輯工作單元執(zhí)行的一系列操作,這些操作要么一起成功,要么一起失敗,是一個(gè)不可分割的工作單元。

在我們?nèi)粘9ぷ髦?,涉及到事?wù)的場景非常多,一個(gè) service 中往往需要調(diào)用不同的 dao 層方法,這些方法要么同時(shí)成功要么同時(shí)失敗,我們需要在 service 層確保這一點(diǎn)。

說到事務(wù)最典型的案例就是轉(zhuǎn)賬了:

?

張三要給李四轉(zhuǎn)賬 500 塊錢,這里涉及到兩個(gè)操作,從張三的賬戶上減去 500 塊錢,給李四的賬戶添加 500 塊錢,這兩個(gè)操作要么同時(shí)成功要么同時(shí)失敗,如何確保他們同時(shí)成功或者同時(shí)失敗呢?答案就是事務(wù)。

事務(wù)有四大特性(ACID):

c15977e4-7619-11ed-8abf-dac502259ad0.png

原子性(Atomicity): 一個(gè)事務(wù)(transaction)中的所有操作,要么全部完成,要么全部不完成,不會(huì)結(jié)束在中間某個(gè)環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯(cuò)誤,會(huì)被回滾(Rollback)到事務(wù)開始前的狀態(tài),就像這個(gè)事務(wù)從來沒有執(zhí)行過一樣。即,事務(wù)不可分割、不可約簡。

一致性(Consistency): 在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預(yù)設(shè)約束、觸發(fā)器、級(jí)聯(lián)回滾等。

隔離性(Isolation): 數(shù)據(jù)庫允許多個(gè)并發(fā)事務(wù)同時(shí)對(duì)其數(shù)據(jù)進(jìn)行讀寫和修改,隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。事務(wù)隔離分為不同級(jí)別,包括未提交讀(Read Uncommitted)、提交讀(Read Committed)、可重復(fù)讀(Repeatable Read)和串行化(Serializable)。

持久性(Durability): 事務(wù)處理結(jié)束后,對(duì)數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會(huì)丟失。

這就是事務(wù)的四大特性。

2. Spring 中的事務(wù)

2.1 兩種用法

Spring 作為 Java 開發(fā)中的基礎(chǔ)設(shè)施,對(duì)于事務(wù)也提供了很好的支持,總體上來說,Spring 支持兩種類型的事務(wù),聲明式事務(wù)和編程式事務(wù)。

編程式事務(wù)類似于 Jdbc 事務(wù)的寫法,需要將事務(wù)的代碼嵌入到業(yè)務(wù)邏輯中,這樣代碼的耦合度較高,而聲明式事務(wù)通過 AOP 的思想能夠有效的將事務(wù)和業(yè)務(wù)邏輯代碼解耦,因此在實(shí)際開發(fā)中,聲明式事務(wù)得到了廣泛的應(yīng)用,而編程式事務(wù)則較少使用,考慮到文章內(nèi)容的完整,本文對(duì)兩種事務(wù)方式都會(huì)介紹。

2.2 三大基礎(chǔ)設(shè)施

Spring 中對(duì)事務(wù)的支持提供了三大基礎(chǔ)設(shè)施,我們先來了解下。

PlatformTransactionManager

TransactionDefinition

TransactionStatus

這三個(gè)核心類是 Spring 處理事務(wù)的核心類。

2.2.1 PlatformTransactionManager

PlatformTransactionManager 是事務(wù)處理的核心,它有諸多的實(shí)現(xiàn)類,如下:

c180036e-7619-11ed-8abf-dac502259ad0.png

PlatformTransactionManager 的定義如下:

publicinterfacePlatformTransactionManager{
TransactionStatusgetTransaction(@NullableTransactionDefinitiondefinition);
voidcommit(TransactionStatusstatus)throwsTransactionException;
voidrollback(TransactionStatusstatus)throwsTransactionException;
}

可以看到 PlatformTransactionManager 中定義了基本的事務(wù)操作方法,這些事務(wù)操作方法都是平臺(tái)無關(guān)的,具體的實(shí)現(xiàn)都是由不同的子類來實(shí)現(xiàn)的。

這就像 JDBC 一樣,SUN 公司制定標(biāo)準(zhǔn),其他數(shù)據(jù)庫廠商提供具體的實(shí)現(xiàn)。

這么做的好處就是我們 Java 程序員只需要掌握好這套標(biāo)準(zhǔn)即可,不用去管接口的具體實(shí)現(xiàn)。以 PlatformTransactionManager 為例,它有眾多實(shí)現(xiàn),如果你使用的是 JDBC 那么可以將 DataSourceTransactionManager 作為事務(wù)管理器;如果你使用的是 Hibernate,那么可以將 HibernateTransactionManager 作為事務(wù)管理器;如果你使用的是 JPA,那么可以將 JpaTransactionManager 作為事務(wù)管理器。

DataSourceTransactionManager、HibernateTransactionManager 以及 JpaTransactionManager 都是 PlatformTransactionManager 的具體實(shí)現(xiàn),但是我們并不需要掌握這些具體實(shí)現(xiàn)類的用法,我們只需要掌握好 PlatformTransactionManager 的用法即可。

PlatformTransactionManager 中主要有如下三個(gè)方法:

1.getTransaction()

getTransaction() 是根據(jù)傳入的 TransactionDefinition 獲取一個(gè)事務(wù)對(duì)象,TransactionDefinition 中定義了一些事務(wù)的基本規(guī)則,例如傳播性、隔離級(jí)別等。

2.commit()

commit() 方法用來提交事務(wù)。

3.rollback()

rollback() 方法用來回滾事務(wù)。

2.2.2 TransactionDefinition

TransactionDefinition 用來描述事務(wù)的具體規(guī)則,也稱作事務(wù)的屬性。事務(wù)有哪些屬性呢?看下圖:

c1a5ce1e-7619-11ed-8abf-dac502259ad0.png

可以看到,主要是五種屬性:

隔離性

傳播性

回滾規(guī)則

超時(shí)時(shí)間

是否只讀

這五種屬性接下來松哥會(huì)和大家詳細(xì)介紹。

TransactionDefinition 類中的方法如下:

c1ccffe8-7619-11ed-8abf-dac502259ad0.png

可以看到一共有五個(gè)方法:

getIsolationLevel(),獲取事務(wù)的隔離級(jí)別

getName(),獲取事務(wù)的名稱

getPropagationBehavior(),獲取事務(wù)的傳播性

getTimeout(),獲取事務(wù)的超時(shí)時(shí)間

isReadOnly(),獲取事務(wù)是否是只讀事務(wù)

TransactionDefinition 也有諸多的實(shí)現(xiàn)類,如下:

c1ecccce-7619-11ed-8abf-dac502259ad0.png

如果開發(fā)者使用了編程式事務(wù)的話,直接使用 DefaultTransactionDefinition 即可。

2.2.3 TransactionStatus

TransactionStatus 可以直接理解為事務(wù)本身,該接口源碼如下:

publicinterfaceTransactionStatusextendsSavepointManager,Flushable{
booleanisNewTransaction();
booleanhasSavepoint();
voidsetRollbackOnly();
booleanisRollbackOnly();
voidflush();
booleanisCompleted();
}

isNewTransaction() 方法獲取當(dāng)前事務(wù)是否是一個(gè)新事務(wù)。

hasSavepoint() 方法判斷是否存在 savePoint()。

setRollbackOnly() 方法設(shè)置事務(wù)必須回滾。

isRollbackOnly() 方法獲取事務(wù)只能回滾。

flush() 方法將底層會(huì)話中的修改刷新到數(shù)據(jù)庫,一般用于 Hibernate/JPA 的會(huì)話,對(duì)如 JDBC 類型的事務(wù)無任何影響。

isCompleted() 方法用來獲取是一個(gè)事務(wù)是否結(jié)束。

這就是 Spring 中支持事務(wù)的三大基礎(chǔ)設(shè)施。

3. 編程式事務(wù)

我們先來看看編程式事務(wù)怎么玩。

通過 PlatformTransactionManager 或者 TransactionTemplate 可以實(shí)現(xiàn)編程式事務(wù)。如果是在 Spring Boot 項(xiàng)目中,這兩個(gè)對(duì)象 Spring Boot 會(huì)自動(dòng)提供,我們直接使用即可。

但是如果是在傳統(tǒng)的 SSM 項(xiàng)目中,則需要我們通過配置來提供這兩個(gè)對(duì)象,松哥給一個(gè)簡單的配置參考,如下(簡單起見,數(shù)據(jù)庫操作我們使用 JdbcTemplate):

有了這兩個(gè)對(duì)象,接下來的代碼就簡單了:

@Service
publicclassTransferService{
@Autowired
JdbcTemplatejdbcTemplate;
@Autowired
PlatformTransactionManagertxManager;

publicvoidtransfer(){
DefaultTransactionDefinitiondefinition=newDefaultTransactionDefinition();
TransactionStatusstatus=txManager.getTransaction(definition);
try{
jdbcTemplate.update("updateusersetaccount=account+100whereusername='zhangsan'");
inti=1/0;
jdbcTemplate.update("updateusersetaccount=account-100whereusername='lisi'");
txManager.commit(status);
}catch(DataAccessExceptione){
e.printStackTrace();
txManager.rollback(status);
}
}
}

這段代碼很簡單,沒啥好解釋的,在 try...catch... 中進(jìn)行業(yè)務(wù)操作,沒問題就 commit,有問題就 rollback。

如果我們需要配置事務(wù)的隔離性、傳播性等,可以在 DefaultTransactionDefinition 對(duì)象中進(jìn)行配置。

上面的代碼是通過 PlatformTransactionManager 實(shí)現(xiàn)的編程式事務(wù),我們也可以通過 TransactionTemplate 來實(shí)現(xiàn)編程式事務(wù),如下:

@Service
publicclassTransferService{
@Autowired
JdbcTemplatejdbcTemplate;
@Autowired
TransactionTemplatetranTemplate;
publicvoidtransfer(){
tranTemplate.execute(newTransactionCallbackWithoutResult(){
@Override
protectedvoiddoInTransactionWithoutResult(TransactionStatusstatus){
try{
jdbcTemplate.update("updateusersetaccount=account+100whereusername='zhangsan'");
inti=1/0;
jdbcTemplate.update("updateusersetaccount=account-100whereusername='lisi'");
}catch(DataAccessExceptione){
status.setRollbackOnly();
e.printStackTrace();
}
}
});
}
}

直接注入 TransactionTemplate,然后在 execute 方法中添加回調(diào)寫核心的業(yè)務(wù)即可,當(dāng)拋出異常時(shí),將當(dāng)前事務(wù)標(biāo)注為只能回滾即可。

注意,execute 方法中,如果不需要獲取事務(wù)執(zhí)行的結(jié)果,則直接使用 TransactionCallbackWithoutResult 類即可,如果要獲取事務(wù)執(zhí)行結(jié)果,則使用 TransactionCallback 即可。

這就是兩種編程式事務(wù)的玩法。

編程式事務(wù)由于代碼入侵太嚴(yán)重了,因?yàn)樵趯?shí)際開發(fā)中使用的很少,我們?cè)陧?xiàng)目中更多的是使用聲明式事務(wù)。

4. 聲明式事務(wù)

聲明式事務(wù)如果使用 XML 配置,可以做到無侵入;如果使用 Java 配置,也只有一個(gè) @Transactional 注解侵入而已,相對(duì)來說非常容易。

以下配置針對(duì)傳統(tǒng) SSM 項(xiàng)目(因?yàn)樵?Spring Boot 項(xiàng)目中,事務(wù)相關(guān)的組件已經(jīng)配置好了):

4.1 XML 配置

XML 配置聲明式事務(wù)大致上可以分為三個(gè)步驟,如下:

配置事務(wù)管理器

配置事務(wù)通知

配置 AOP

第二步和第三步中定義出來的方法交集,就是我們要添加事務(wù)的方法。

配置完成后,如下一些方法就自動(dòng)具備事務(wù)了:

publicclassUserService{
publicvoidm3(){
jdbcTemplate.update("updateusersetmoney=997whereusername=?","zhangsan");
}
}

4.2 Java 配置

我們也可以使用 Java 配置來實(shí)現(xiàn)聲明式事務(wù):

@Configuration
@ComponentScan
//開啟事務(wù)注解支持
@EnableTransactionManagement
publicclassJavaConfig{
@Bean
DataSourcedataSource(){
DriverManagerDataSourceds=newDriverManagerDataSource();
ds.setPassword("123");
ds.setUsername("root");
ds.setUrl("jdbc///test01?serverTimezone=Asia/Shanghai");
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
returnds;
}

@Bean
JdbcTemplatejdbcTemplate(DataSourcedataSource){
returnnewJdbcTemplate(dataSource);
}

@Bean
PlatformTransactionManagertransactionManager(){
returnnewDataSourceTransactionManager(dataSource());
}
}

這里要配置的東西其實(shí)和 XML 中配置的都差不多,最最關(guān)鍵的就兩個(gè):

事務(wù)管理器 PlatformTransactionManager。

@EnableTransactionManagement 注解開啟事務(wù)支持。

配置完成后,接下來,哪個(gè)方法需要事務(wù)就在哪個(gè)方法上添加 @Transactional 注解即可,向下面這樣:

@Transactional(noRollbackFor=ArithmeticException.class)
publicvoidupdate4(){
jdbcTemplate.update("updateaccountsetmoney=?whereusername=?;",998,"lisi");
inti=1/0;
}

當(dāng)然這個(gè)稍微有點(diǎn)代碼入侵,不過問題不大,日常開發(fā)中這種方式使用較多。

當(dāng)@Transactional 注解加在類上面的時(shí)候,表示該類的所有方法都有事務(wù),該注解加在方法上面的時(shí)候,表示該方法有事務(wù)。

4.3 混合配置

也可以 Java 代碼和 XML 混合配置來實(shí)現(xiàn)聲明式事務(wù),就是一部分配置用 XML 來實(shí)現(xiàn),一部分配置用 Java 代碼來實(shí)現(xiàn):

假設(shè) XML 配置如下:

那么 Java 代碼中的配置如下:

@Configuration
@ComponentScan
@ImportResource(locations="classpath:applicationContext3.xml")
publicclassJavaConfig{
@Bean
DataSourcedataSource(){
DriverManagerDataSourceds=newDriverManagerDataSource();
ds.setPassword("123");
ds.setUsername("root");
ds.setUrl("jdbc///test01?serverTimezone=Asia/Shanghai");
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
returnds;
}

@Bean
JdbcTemplatejdbcTemplate(DataSourcedataSource){
returnnewJdbcTemplate(dataSource);
}

@Bean
PlatformTransactionManagertransactionManager(){
returnnewDataSourceTransactionManager(dataSource());
}
}

Java 配置中通過 @ImportResource 注解導(dǎo)入了 XML 配置,XML 配置中的內(nèi)容就是開啟 @Transactional 注解的支持,所以 Java 配置中省略了 @EnableTransactionManagement 注解。

這就是聲明式事務(wù)的幾種配置方式。好玩吧!

5. 事務(wù)屬性

在前面的配置中,我們只是簡單說了事務(wù)的用法,并沒有和大家詳細(xì)聊一聊事務(wù)的一些屬性細(xì)節(jié),那么接下來我們就來仔細(xì)捋一捋事務(wù)中的五大屬性。

5.1 隔離性

首先就是事務(wù)的隔離性,也就是事務(wù)的隔離級(jí)別。

MySQL 中有四種不同的隔離級(jí)別,這四種不同的隔離級(jí)別在 Spring 中都得到了很好的支持。

Spring 中默認(rèn)的事務(wù)隔離級(jí)別是 default,即數(shù)據(jù)庫本身的隔離級(jí)別是啥就是啥,default 就能滿足我們?nèi)粘i_發(fā)中的大部分場景。

不過如果項(xiàng)目有需要,我們也可以調(diào)整事務(wù)的隔離級(jí)別。

調(diào)整方式如下:

5.1.1 編程式事務(wù)隔離級(jí)別

如果是編程式事務(wù),通過如下方式修改事務(wù)的隔離級(jí)別:

TransactionTemplate

transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);

TransactionDefinition 中定義了各種隔離級(jí)別。

PlatformTransactionManager

publicvoidupdate2(){
//創(chuàng)建事務(wù)的默認(rèn)配置
DefaultTransactionDefinitiondefinition=newDefaultTransactionDefinition();
definition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
TransactionStatusstatus=platformTransactionManager.getTransaction(definition);
try{
jdbcTemplate.update("updateaccountsetmoney=?whereusername=?;",999,"zhangsan");
inti=1/0;
//提交事務(wù)
platformTransactionManager.commit(status);
}catch(DataAccessExceptione){
e.printStackTrace();
//回滾
platformTransactionManager.rollback(status);
}
}

這里是在 DefaultTransactionDefinition 對(duì)象中設(shè)置事務(wù)的隔離級(jí)別。

5.1.2 聲明式事務(wù)隔離級(jí)別

如果是聲明式事務(wù)通過如下方式修改隔離級(jí)別:

XML:

Java:

@Transactional(isolation=Isolation.SERIALIZABLE)
publicvoidupdate4(){
jdbcTemplate.update("updateaccountsetmoney=?whereusername=?;",998,"lisi");
inti=1/0;
}

關(guān)于事務(wù)的隔離級(jí)別,如果大家還不熟悉,可以參考松哥之前的文章:四個(gè)案例看懂 MySQL 事務(wù)隔離級(jí)別。

5.2 傳播性

先來說說何謂事務(wù)的傳播性:

?

事務(wù)傳播行為是為了解決業(yè)務(wù)層方法之間互相調(diào)用的事務(wù)問題,當(dāng)一個(gè)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),事務(wù)該以何種狀態(tài)存在?例如新方法可能繼續(xù)在現(xiàn)有事務(wù)中運(yùn)行,也可能開啟一個(gè)新事務(wù),并在自己的事務(wù)中運(yùn)行,等等,這些規(guī)則就涉及到事務(wù)的傳播性。

關(guān)于事務(wù)的傳播性,Spring 主要定義了如下幾種:

publicenumPropagation{
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
NEVER(TransactionDefinition.PROPAGATION_NEVER),
NESTED(TransactionDefinition.PROPAGATION_NESTED);
privatefinalintvalue;
Propagation(intvalue){this.value=value;}
publicintvalue(){returnthis.value;}
}

具體含義如下:

傳播性 描述
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ù),則拋出異常
REQUIRES_NEW 創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起
NOT_SUPPORTED 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起
NEVER 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常
NESTED 如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行;如果當(dāng)前沒有事務(wù),則該取值等價(jià)于 TransactionDefinition.PROPAGATION_REQUIRED

一共是七種傳播性,具體配置也簡單:

TransactionTemplate中的配置

transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

PlatformTransactionManager中的配置

publicvoidupdate2(){
//創(chuàng)建事務(wù)的默認(rèn)配置
DefaultTransactionDefinitiondefinition=newDefaultTransactionDefinition();
definition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatusstatus=platformTransactionManager.getTransaction(definition);
try{
jdbcTemplate.update("updateaccountsetmoney=?whereusername=?;",999,"zhangsan");
inti=1/0;
//提交事務(wù)
platformTransactionManager.commit(status);
}catch(DataAccessExceptione){
e.printStackTrace();
//回滾
platformTransactionManager.rollback(status);
}
}

聲明式事務(wù)的配置(XML)

聲明式事務(wù)的配置(Java)

@Transactional(noRollbackFor=ArithmeticException.class,propagation=Propagation.REQUIRED)
publicvoidupdate4(){
jdbcTemplate.update("updateaccountsetmoney=?whereusername=?;",998,"lisi");
inti=1/0;
}

用就是這么來用,至于七種傳播的具體含義,松哥來和大家一個(gè)一個(gè)說。

5.2.1 REQUIRED

REQUIRED 表示如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。

例如我有如下一段代碼:

@Service
publicclassAccountService{
@Autowired
JdbcTemplatejdbcTemplate;
@Transactional
publicvoidhandle1(){
jdbcTemplate.update("updateusersetmoney=?whereid=?;",1,2);
}
}
@Service
publicclassAccountService2{
@Autowired
JdbcTemplatejdbcTemplate;
@Autowired
AccountServiceaccountService;
publicvoidhandle2(){
jdbcTemplate.update("updateusersetmoney=?whereusername=?;",1,"zhangsan");
accountService.handle1();
}
}

我在 handle2 方法中調(diào)用 handle1。

那么:

如果 handle2 方法本身是有事務(wù)的,則 handle1 方法就會(huì)加入到 handle2 方法所在的事務(wù)中,這樣兩個(gè)方法將處于同一個(gè)事務(wù)中,一起成功或者一起失?。ú还苁?handle2 還是 handle1 誰拋異常,都會(huì)導(dǎo)致整體回滾)。

如果 handle2 方法本身是沒有事務(wù)的,則 handle1 方法就會(huì)自己開啟一個(gè)新的事務(wù),自己玩。

舉一個(gè)簡單的例子:handle2 方法有事務(wù),handle1 方法也有事務(wù)(小伙伴們根據(jù)前面的講解自行配置事務(wù)),項(xiàng)目打印出來的事務(wù)日志如下:

o.s.jdbc.support.JdbcTransactionManager:Creatingnewtransactionwithname[org.javaboy.spring_tran02.AccountService2.handle2]:PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.jdbc.support.JdbcTransactionManager:AcquiredConnection[HikariProxyConnection@875256468wrappingcom.mysql.cj.jdbc.ConnectionImpl@9753d50]forJDBCtransaction
o.s.jdbc.support.JdbcTransactionManager:SwitchingJDBCConnection[HikariProxyConnection@875256468wrappingcom.mysql.cj.jdbc.ConnectionImpl@9753d50]tomanualcommit
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereusername=?;]
o.s.jdbc.support.JdbcTransactionManager:Participatinginexistingtransaction
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereid=?;]
o.s.jdbc.support.JdbcTransactionManager:Initiatingtransactioncommit
o.s.jdbc.support.JdbcTransactionManager:CommittingJDBCtransactiononConnection[HikariProxyConnection@875256468wrappingcom.mysql.cj.jdbc.ConnectionImpl@9753d50]
o.s.jdbc.support.JdbcTransactionManager:ReleasingJDBCConnection[HikariProxyConnection@875256468wrappingcom.mysql.cj.jdbc.ConnectionImpl@9753d50]aftertransaction

從日志中可以看到,前前后后一共就開啟了一個(gè)事務(wù),日志中有這么一句:

Participatinginexistingtransaction

這個(gè)就說明 handle1 方法沒有自己開啟事務(wù),而是加入到 handle2 方法的事務(wù)中了。

5.2.2 REQUIRES_NEW

REQUIRES_NEW 表示創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。換言之,不管外部方法是否有事務(wù),REQUIRES_NEW 都會(huì)開啟自己的事務(wù)。

這塊松哥要多說兩句,有的小伙伴可能覺得 REQUIRES_NEW 和 REQUIRED 太像了,似乎沒啥區(qū)別。

其實(shí)你要是單純看最終回滾效果,可能確實(shí)看不到啥區(qū)別。

但是,大家注意松哥上面的加粗,在 REQUIRES_NEW 中可能會(huì)同時(shí)存在兩個(gè)事務(wù),外部方法的事務(wù)被掛起,內(nèi)部方法的事務(wù)獨(dú)自運(yùn)行,而在 REQUIRED 中則不會(huì)出現(xiàn)這種情況,如果內(nèi)外部方法傳播性都是 REQUIRED,那么最終也只是一個(gè)事務(wù)。

還是上面那個(gè)例子,假設(shè) handle1 和 handle2 方法都有事務(wù),handle2 方法的事務(wù)傳播性是 REQUIRED,而 handle1 方法的事務(wù)傳播性是 REQUIRES_NEW,那么最終打印出來的事務(wù)日志如下:

o.s.jdbc.support.JdbcTransactionManager:Creatingnewtransactionwithname[org.javaboy.spring_tran02.AccountService2.handle2]:PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.jdbc.support.JdbcTransactionManager:AcquiredConnection[HikariProxyConnection@422278016wrappingcom.mysql.cj.jdbc.ConnectionImpl@732405c2]forJDBCtransaction
o.s.jdbc.support.JdbcTransactionManager:SwitchingJDBCConnection[HikariProxyConnection@422278016wrappingcom.mysql.cj.jdbc.ConnectionImpl@732405c2]tomanualcommit
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereusername=?;]
o.s.jdbc.support.JdbcTransactionManager:Suspendingcurrenttransaction,creatingnewtransactionwithname[org.javaboy.spring_tran02.AccountService.handle1]
o.s.jdbc.support.JdbcTransactionManager:AcquiredConnection[HikariProxyConnection@247691344wrappingcom.mysql.cj.jdbc.ConnectionImpl@14ad4b95]forJDBCtransaction
com.zaxxer.hikari.pool.HikariPool:HikariPool-1-Addedconnectioncom.mysql.cj.jdbc.ConnectionImpl@14ad4b95
o.s.jdbc.support.JdbcTransactionManager:SwitchingJDBCConnection[HikariProxyConnection@247691344wrappingcom.mysql.cj.jdbc.ConnectionImpl@14ad4b95]tomanualcommit
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereid=?;]
o.s.jdbc.support.JdbcTransactionManager:Initiatingtransactioncommit
o.s.jdbc.support.JdbcTransactionManager:CommittingJDBCtransactiononConnection[HikariProxyConnection@247691344wrappingcom.mysql.cj.jdbc.ConnectionImpl@14ad4b95]
o.s.jdbc.support.JdbcTransactionManager:ReleasingJDBCConnection[HikariProxyConnection@247691344wrappingcom.mysql.cj.jdbc.ConnectionImpl@14ad4b95]aftertransaction
o.s.jdbc.support.JdbcTransactionManager:Resumingsuspendedtransactionaftercompletionofinnertransaction
o.s.jdbc.support.JdbcTransactionManager:Initiatingtransactioncommit
o.s.jdbc.support.JdbcTransactionManager:CommittingJDBCtransactiononConnection[HikariProxyConnection@422278016wrappingcom.mysql.cj.jdbc.ConnectionImpl@732405c2]
o.s.jdbc.support.JdbcTransactionManager:ReleasingJDBCConnection[HikariProxyConnection@422278016wrappingcom.mysql.cj.jdbc.ConnectionImpl@732405c2]aftertransaction

分析這段日志我們可以看到:

首先為 handle2 方法開啟了一個(gè)事務(wù)。

執(zhí)行完 handle2 方法的 SQL 之后,事務(wù)被刮起(Suspending)。

為 handle1 方法開啟了一個(gè)新的事務(wù)。

執(zhí)行 handle1 方法的 SQL。

提交 handle1 方法的事務(wù)。

恢復(fù)被掛起的事務(wù)(Resuming)。

提交 handle2 方法的事務(wù)。

從這段日志中大家可以非常明確的看到 REQUIRES_NEW 和 REQUIRED 的區(qū)別。

松哥再來簡單總結(jié)下(假設(shè) handle1 方法的事務(wù)傳播性是 REQUIRES_NEW):

如果 handle2 方法沒有事務(wù),handle1 方法自己開啟一個(gè)事務(wù)自己玩。

如果 handle2 方法有事務(wù),handle1 方法還是會(huì)開啟一個(gè)事務(wù)。此時(shí),如果 handle2 發(fā)生了異常進(jìn)行回滾,并不會(huì)導(dǎo)致 handle1 方法回滾,因?yàn)?handle1 方法是獨(dú)立的事務(wù);如果 handle1 方法發(fā)生了異常導(dǎo)致回滾,并且 handle1 方法的異常沒有被捕獲處理傳到了 handle2 方法中,那么也會(huì)導(dǎo)致 handle2 方法回滾。

?

這個(gè)地方小伙伴們要稍微注意一下,我們測試的時(shí)候,由于是兩個(gè)更新 SQL,如果更新的查詢字段不是索引字段,那么 InnoDB 將使用表鎖,這樣就會(huì)發(fā)生死鎖(handle2 方法執(zhí)行時(shí)開啟表鎖,導(dǎo)致 handle1 方法陷入等待中,而必須 handle1 方法執(zhí)行完,handle2 才能釋放鎖)。所以,在上面的測試中,我們要將 username 字段設(shè)置為索引字段,這樣默認(rèn)就使用行鎖了。

5.2.3 NESTED

NESTED 表示如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行;如果當(dāng)前沒有事務(wù),則該取值等價(jià)于 TransactionDefinition.PROPAGATION_REQUIRED。

假設(shè) handle2 方法有事務(wù),handle1 方法也有事務(wù)且傳播性為 NESTED,那么最終執(zhí)行的事務(wù)日志如下:

o.s.jdbc.support.JdbcTransactionManager:Creatingnewtransactionwithname[org.javaboy.demo.AccountService2.handle2]:PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.jdbc.support.JdbcTransactionManager:AcquiredConnection[HikariProxyConnection@2025689131wrappingcom.mysql.cj.jdbc.ConnectionImpl@2ed3628e]forJDBCtransaction
o.s.jdbc.support.JdbcTransactionManager:SwitchingJDBCConnection[HikariProxyConnection@2025689131wrappingcom.mysql.cj.jdbc.ConnectionImpl@2ed3628e]tomanualcommit
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereusername=?;]
o.s.jdbc.support.JdbcTransactionManager:Creatingnestedtransactionwithname[org.javaboy.demo.AccountService.handle1]
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereid=?;]
o.s.jdbc.support.JdbcTransactionManager:Releasingtransactionsavepoint
o.s.jdbc.support.JdbcTransactionManager:Initiatingtransactioncommit
o.s.jdbc.support.JdbcTransactionManager:CommittingJDBCtransactiononConnection[HikariProxyConnection@2025689131wrappingcom.mysql.cj.jdbc.ConnectionImpl@2ed3628e]
o.s.jdbc.support.JdbcTransactionManager:ReleasingJDBCConnection[HikariProxyConnection@2025689131wrappingcom.mysql.cj.jdbc.ConnectionImpl@2ed3628e]aftertransaction

關(guān)鍵一句在 Creating nested transaction。

此時(shí),NESTED 修飾的內(nèi)部方法(handle1)屬于外部事務(wù)的子事務(wù),外部主事務(wù)回滾的話,子事務(wù)也會(huì)回滾,而內(nèi)部子事務(wù)可以單獨(dú)回滾而不影響外部主事務(wù)和其他子事務(wù)(需要處理掉內(nèi)部子事務(wù)的異常)。

5.2.4 MANDATORY

MANDATORY 表示如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。

這個(gè)好理解,我舉兩個(gè)例子:

假設(shè) handle2 方法有事務(wù),handle1 方法也有事務(wù)且傳播性為 MANDATORY,那么最終執(zhí)行的事務(wù)日志如下:

o.s.jdbc.support.JdbcTransactionManager:Creatingnewtransactionwithname[org.javaboy.demo.AccountService2.handle2]:PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.jdbc.support.JdbcTransactionManager:AcquiredConnection[HikariProxyConnection@768820610wrappingcom.mysql.cj.jdbc.ConnectionImpl@14840df2]forJDBCtransaction
o.s.jdbc.support.JdbcTransactionManager:SwitchingJDBCConnection[HikariProxyConnection@768820610wrappingcom.mysql.cj.jdbc.ConnectionImpl@14840df2]tomanualcommit
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereusername=?;]
o.s.jdbc.support.JdbcTransactionManager:Participatinginexistingtransaction
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereid=?;]
o.s.jdbc.support.JdbcTransactionManager:Initiatingtransactioncommit
o.s.jdbc.support.JdbcTransactionManager:CommittingJDBCtransactiononConnection[HikariProxyConnection@768820610wrappingcom.mysql.cj.jdbc.ConnectionImpl@14840df2]
o.s.jdbc.support.JdbcTransactionManager:ReleasingJDBCConnection[HikariProxyConnection@768820610wrappingcom.mysql.cj.jdbc.ConnectionImpl@14840df2]aftertransaction

從這段日志可以看出:

首先給 handle2 方法開啟事務(wù)。

執(zhí)行 handle2 方法的 SQL。

handle1 方法加入到已經(jīng)存在的事務(wù)中。

執(zhí)行 handle1 方法的 SQL。

提交事務(wù)。

假設(shè) handle2 方法無事務(wù),handle1 方法有事務(wù)且傳播性為 MANDATORY,那么最終執(zhí)行時(shí)會(huì)拋出如下異常:

Noexistingtransactionfoundfortransactionmarkedwithpropagation'mandatory'

由于沒有已經(jīng)存在的事務(wù),所以出錯(cuò)了。

5.2.5 SUPPORTS

SUPPORTS 表示如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。

這個(gè)也簡單,舉兩個(gè)例子大家就明白了。

假設(shè) handle2 方法有事務(wù),handle1 方法也有事務(wù)且傳播性為 SUPPORTS,那么最終事務(wù)執(zhí)行日志如下:

o.s.jdbc.support.JdbcTransactionManager:Creatingnewtransactionwithname[org.javaboy.demo.AccountService2.handle2]:PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.jdbc.support.JdbcTransactionManager:AcquiredConnection[HikariProxyConnection@1780573324wrappingcom.mysql.cj.jdbc.ConnectionImpl@44eafcbc]forJDBCtransaction
o.s.jdbc.support.JdbcTransactionManager:SwitchingJDBCConnection[HikariProxyConnection@1780573324wrappingcom.mysql.cj.jdbc.ConnectionImpl@44eafcbc]tomanualcommit
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereusername=?;]
o.s.jdbc.support.JdbcTransactionManager:Participatinginexistingtransaction
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereid=?;]
o.s.jdbc.support.JdbcTransactionManager:Initiatingtransactioncommit
o.s.jdbc.support.JdbcTransactionManager:CommittingJDBCtransactiononConnection[HikariProxyConnection@1780573324wrappingcom.mysql.cj.jdbc.ConnectionImpl@44eafcbc]
o.s.jdbc.support.JdbcTransactionManager:ReleasingJDBCConnection[HikariProxyConnection@1780573324wrappingcom.mysql.cj.jdbc.ConnectionImpl@44eafcbc]aftertransaction

這段日志很簡單,沒啥好說的,認(rèn)準(zhǔn) Participating in existing transaction 表示加入到已經(jīng)存在的事務(wù)中即可。

假設(shè) handle2 方法無事務(wù),handle1 方法有事務(wù)且傳播性為 SUPPORTS,這個(gè)最終就不會(huì)開啟事務(wù)了,也沒有相關(guān)日志。

5.2.6 NOT_SUPPORTED

NOT_SUPPORTED 表示以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。

假設(shè) handle2 方法有事務(wù),handle1 方法也有事務(wù)且傳播性為 NOT_SUPPORTED,那么最終事務(wù)執(zhí)行日志如下:

o.s.jdbc.support.JdbcTransactionManager:Creatingnewtransactionwithname[org.javaboy.demo.AccountService2.handle2]:PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.jdbc.support.JdbcTransactionManager:AcquiredConnection[HikariProxyConnection@1365886554wrappingcom.mysql.cj.jdbc.ConnectionImpl@3198938b]forJDBCtransaction
o.s.jdbc.support.JdbcTransactionManager:SwitchingJDBCConnection[HikariProxyConnection@1365886554wrappingcom.mysql.cj.jdbc.ConnectionImpl@3198938b]tomanualcommit
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereusername=?;]
o.s.jdbc.support.JdbcTransactionManager:Suspendingcurrenttransaction
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLupdate
o.s.jdbc.core.JdbcTemplate:ExecutingpreparedSQLstatement[updateusersetmoney=?whereid=?;]
o.s.jdbc.datasource.DataSourceUtils:FetchingJDBCConnectionfromDataSource
o.s.jdbc.support.JdbcTransactionManager:Resumingsuspendedtransactionaftercompletionofinnertransaction
o.s.jdbc.support.JdbcTransactionManager:Initiatingtransactioncommit
o.s.jdbc.support.JdbcTransactionManager:CommittingJDBCtransactiononConnection[HikariProxyConnection@1365886554wrappingcom.mysql.cj.jdbc.ConnectionImpl@3198938b]
o.s.jdbc.support.JdbcTransactionManager:ReleasingJDBCConnection[HikariProxyConnection@1365886554wrappingcom.mysql.cj.jdbc.ConnectionImpl@3198938b]aftertransaction

這段日志大家認(rèn)準(zhǔn)這兩句就行了 :Suspending current transaction 表示掛起當(dāng)前事務(wù);Resuming suspended transaction 表示恢復(fù)掛起的事務(wù)。

5.2.7 NEVER

NEVER 表示以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。

假設(shè) handle2 方法有事務(wù),handle1 方法也有事務(wù)且傳播性為 NEVER,那么最終會(huì)拋出如下異常:

Existingtransactionfoundfortransactionmarkedwithpropagation'never'

5.3 回滾規(guī)則

默認(rèn)情況下,事務(wù)只有遇到運(yùn)行期異常(RuntimeException 的子類)以及 Error 時(shí)才會(huì)回滾,在遇到檢查型(Checked Exception)異常時(shí)不會(huì)回滾。

像 1/0,空指針這些是 RuntimeException,而 IOException 則算是 Checked Exception,換言之,默認(rèn)情況下,如果發(fā)生 IOException 并不會(huì)導(dǎo)致事務(wù)回滾。

如果我們希望發(fā)生 IOException 時(shí)也能觸發(fā)事務(wù)回滾,那么可以按照如下方式配置:

Java 配置:

@Transactional(rollbackFor=IOException.class)
publicvoidhandle2(){
jdbcTemplate.update("updateusersetmoney=?whereusername=?;",1,"zhangsan");
accountService.handle1();
}

XML 配置:

另外,我們也可以指定在發(fā)生某些異常時(shí)不回滾,例如當(dāng)系統(tǒng)拋出 ArithmeticException 異常并不要觸發(fā)事務(wù)回滾,配置方式如下:

Java 配置:

@Transactional(noRollbackFor=ArithmeticException.class)
publicvoidhandle2(){
jdbcTemplate.update("updateusersetmoney=?whereusername=?;",1,"zhangsan");
accountService.handle1();
}

XML 配置:

5.4 是否只讀

只讀事務(wù)一般設(shè)置在查詢方法上,但不是所有的查詢方法都需要只讀事務(wù),要看具體情況。

一般來說,如果這個(gè)業(yè)務(wù)方法只有一個(gè)查詢 SQL,那么就沒必要添加事務(wù),強(qiáng)行添加最終效果適得其反。

但是如果一個(gè)業(yè)務(wù)方法中有多個(gè)查詢 SQL,情況就不一樣了:多個(gè)查詢 SQL,默認(rèn)情況下,每個(gè)查詢 SQL 都會(huì)開啟一個(gè)獨(dú)立的事務(wù),這樣,如果有并發(fā)操作修改了數(shù)據(jù),那么多個(gè)查詢 SQL 就會(huì)查到不一樣的數(shù)據(jù)。此時(shí),如果我們開啟事務(wù),并設(shè)置為只讀事務(wù),那么多個(gè)查詢 SQL 將被置于同一個(gè)事務(wù)中,多條相同的 SQL 在該事務(wù)中執(zhí)行將會(huì)獲取到相同的查詢結(jié)果。

設(shè)置事務(wù)只讀的方式如下:

Java 配置:

@Transactional(readOnly=true)

XML 配置:

5.5 超時(shí)時(shí)間

超時(shí)時(shí)間是說一個(gè)事務(wù)允許執(zhí)行的最長時(shí)間,如果超過該時(shí)間限制但事務(wù)還沒有完成,則自動(dòng)回滾事務(wù)。

事務(wù)超時(shí)時(shí)間配置方式如下(單位為秒):

Java 配置:

@Transactional(timeout=10)

XML 配置:

在 TransactionDefinition 中以 int 的值來表示超時(shí)時(shí)間,其單位是秒,默認(rèn)值為-1。

6. 注意事項(xiàng)

事務(wù)只能應(yīng)用到 public 方法上才會(huì)有效。

事務(wù)需要從外部調(diào)用,Spring 自調(diào)事務(wù)用會(huì)失效。即相同類里邊,A 方法沒有事務(wù),B 方法有事務(wù),A 方法調(diào)用 B 方法,則 B 方法的事務(wù)會(huì)失效,這點(diǎn)尤其要注意,因?yàn)榇砟J街粩r截通過代理傳入的外部方法調(diào)用,所以自調(diào)用事務(wù)是不生效的。

建議事務(wù)注解 @Transactional 一般添加在實(shí)現(xiàn)類上,而不要定義在接口上,如果加在接口類或接口方法上時(shí),只有配置基于接口的代理這個(gè)注解才會(huì)生效。

7. 小結(jié)

好啦,這就是和大家分享的 Spring 事務(wù)的玩法,不知道小伙伴們搞明白沒有?






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2952

    瀏覽量

    104495
  • XML
    XML
    +關(guān)注

    關(guān)注

    0

    文章

    187

    瀏覽量

    33024
  • Boot
    +關(guān)注

    關(guān)注

    0

    文章

    149

    瀏覽量

    35753

原文標(biāo)題:長文捋明白Spring事務(wù)!隔離性?傳播性?一網(wǎng)打盡!

文章出處:【微信號(hào):OSC開源社區(qū),微信公眾號(hào):OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Spring事務(wù)失效的十種常見場景

    Spring針對(duì)Java Transaction API (JTA)、JDBC、Hibernate和Java Persistence API(JPA)等事務(wù) API,實(shí)現(xiàn)了致的編程模型,而
    的頭像 發(fā)表于 12-11 15:03 ?842次閱讀

    Spring事務(wù)實(shí)現(xiàn)原理

    作者:京東零售 范錫軍 1、引言 springspring-tx模塊提供了對(duì)事務(wù)管理支持,使用spring事務(wù)可以讓我們從復(fù)雜的
    的頭像 發(fā)表于 11-08 10:10 ?629次閱讀
    <b class='flag-5'>Spring</b><b class='flag-5'>事務(wù)</b>實(shí)現(xiàn)原理

    什么是java spring

    水平,學(xué)習(xí)和研究Spring源碼將會(huì)使你收到意想不到的效果。Spring框架的好處在我們進(jìn)入細(xì)節(jié)以前,讓我們看一下Spring可以給個(gè)工程
    發(fā)表于 09-11 11:16

    Spring的兩種方式事務(wù)管理和API接口介紹

    Spring事務(wù)管理
    發(fā)表于 03-21 06:52

    Spring事務(wù)分析的實(shí)現(xiàn)方式

    Spring事務(wù)原理分析
    發(fā)表于 07-02 15:19

    詳解Spring事務(wù)管理

    在學(xué)習(xí)spring事務(wù)管理時(shí),我忍不住要問,spring為什么進(jìn)行事務(wù)管理,spring怎么進(jìn)行的事務(wù)
    發(fā)表于 07-12 06:54

    Spring事務(wù)管理詳解說明

    Spring事務(wù)管理詳解
    發(fā)表于 05-20 13:46

    spring中聲明式事務(wù)實(shí)現(xiàn)原理猜想

    ? @Transactional注解簡介 @Transactional 是spring中聲明式事務(wù)管理的注解配置方式,相信這個(gè)注解的作用大家都很清楚。 @Transactional 注解可以幫助
    的頭像 發(fā)表于 10-13 09:20 ?1606次閱讀

    淺談Spring事務(wù)的那些坑

    對(duì)于從事java開發(fā)工作的同學(xué)來說,spring事務(wù)肯定再熟悉不過了。在某些業(yè)務(wù)場景,如果同時(shí)有多張表的寫入操作,為了保證操作的原子性(要么同時(shí)成功,要么同時(shí)失?。┍苊鈹?shù)據(jù)不致的
    的頭像 發(fā)表于 10-11 10:31 ?714次閱讀

    發(fā)現(xiàn)個(gè)Spring事務(wù)的巨坑bug 你必須要小心了

    不正確 9.多線程調(diào)用 10.嵌套事務(wù)多回滾了 對(duì)于從事java開發(fā)工作的同學(xué)來說,spring事務(wù)肯定再熟悉不過了。在某些業(yè)務(wù)場景,如果同時(shí)有多張表的寫入操作,為了保證操作的原子
    的頭像 發(fā)表于 10-11 18:17 ?831次閱讀

    淺談Spring事務(wù)底層原理

    開啟Spring事務(wù)本質(zhì)上就是增加了個(gè)Advisor,但我們使用@EnableTransactionManagement注解來開啟Spring事務(wù)
    的頭像 發(fā)表于 12-06 09:56 ?668次閱讀

    Spring事務(wù)在哪幾種情況會(huì)不生效?

    日常開發(fā)中,我們經(jīng)常使用到spring事務(wù)。最近星球位還有去美團(tuán)面試,被問了這么道面試題: Spring
    的頭像 發(fā)表于 05-10 17:53 ?882次閱讀
    <b class='flag-5'>Spring</b><b class='flag-5'>事務(wù)</b>在哪幾種情況<b class='flag-5'>下</b>會(huì)不生效?

    8個(gè)Spring事務(wù)失效的場景介紹

    事故。今天,我們就簡單來說下Spring事務(wù)的原理,然后總結(jié)一下spring事務(wù)失敗的場景,并提出對(duì)應(yīng)的解決方案。
    的頭像 發(fā)表于 05-11 10:41 ?588次閱讀
    8個(gè)<b class='flag-5'>Spring</b><b class='flag-5'>事務(wù)</b>失效的場景介紹

    spring事務(wù)失效的些場景

    對(duì)于從事java開發(fā)工作的同學(xué)來說,spring事務(wù)肯定再熟悉不過了。 在某些業(yè)務(wù)場景,如果個(gè)請(qǐng)求中,需要同時(shí)寫入多張表的數(shù)據(jù)。為了保證操作的原子性(要么同時(shí)成功,要么同時(shí)失?。?/div>
    的頭像 發(fā)表于 10-08 14:27 ?417次閱讀
    <b class='flag-5'>spring</b><b class='flag-5'>事務(wù)</b>失效的<b class='flag-5'>一</b>些場景

    Spring事務(wù)傳播性的相關(guān)知識(shí)

    本文主要介紹了Spring事務(wù)傳播性的相關(guān)知識(shí)。
    的頭像 發(fā)表于 01-10 09:29 ?391次閱讀
    <b class='flag-5'>Spring</b><b class='flag-5'>事務(wù)</b>傳播性的相關(guān)知識(shí)