1.引
前面四節(jié)已經(jīng)簡單介紹了Spring的事物管理嫁赏,當(dāng)然都是基于單個Service單個方法調(diào)用下的绒疗、最簡答的事物管理帝美,還遺留了一些問題,例如事物嵌套處理库快、RollbackOnly屬性說明等等摸袁,接下來的篇幅我們著重介紹Spring中的嵌套事物。在介紹之前先來回顧一下Spring中的事物傳播特性义屏,并通過一個簡單的例子來看感受一下嵌套事物靠汁。
傳播特性名稱 | 說明 |
---|---|
PROPAGATION_REQUIRED | 如果當(dāng)前沒有事物,則新建一個事物湿蛔;如果已經(jīng)存在一個事物膀曾,則加入到這個事物中 |
PROPAGATION_SUPPORTS | 支持當(dāng)前事物,如果當(dāng)前沒有事物阳啥,則以非事物方式執(zhí)行 |
PROPAGATION_MANDATORY | 使用當(dāng)前事物添谊,如果當(dāng)前沒有事物,則拋出異常 |
PROPAGATION_REQUIRES_NEW | 新建事物察迟,如果當(dāng)前已經(jīng)存在事物斩狱,則掛起當(dāng)前事物 |
PROPAGATION_NOT_SUPPORTED | 以非事物方式執(zhí)行,如果當(dāng)前存在事物扎瓶,則掛起當(dāng)前事物 |
PROPAGATION_NEVER | 以非事物方式執(zhí)行所踊,如果當(dāng)前存在事物,則拋出異常 |
PROPAGATION_NESTED | 如果當(dāng)前存在事物概荷,則在嵌套事物內(nèi)執(zhí)行秕岛;如果當(dāng)前沒有事物,則與PROPAGATION_REQUIRED傳播特性相同 |
所謂嵌套事物误证,就是ServiceA調(diào)用了ServiceB的方法继薛,不同的service之間互相進(jìn)行方法調(diào)用,就可能會引發(fā)嵌套事物的問題愈捅。
2.事物嵌套的例子
- Service
package com.lyc.cn.v2.day09;
public interface AccountService {
void save() throws RuntimeException;
void delete() throws RuntimeException;
}
package com.lyc.cn.v2.day09;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class AccountServiceImpl implements AccountService {
private JdbcTemplate jdbcTemplate;
private static String insert_sql = "insert into account(balance) values (100)";
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void save() throws RuntimeException {
System.out.println("==調(diào)用AccountService的save方法\n");
jdbcTemplate.update(insert_sql);
throw new RuntimeException("==AccountService的save方法手動拋出異常");
}
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void delete() throws RuntimeException {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void beforeCommit(boolean readOnly) {
System.out.println("==回調(diào),事物提交之前");
super.beforeCommit(readOnly);
}
@Override
public void afterCommit() {
System.out.println("==回調(diào),事物提交之后");
super.afterCommit();
}
@Override
public void beforeCompletion() {
super.beforeCompletion();
System.out.println("==回調(diào),事物完成之前");
}
@Override
public void afterCompletion(int status) {
super.afterCompletion(status);
System.out.println("==回調(diào),事物完成之后");
}
});
System.out.println("==調(diào)用AccountService的dele方法\n");
jdbcTemplate.update(insert_sql);
throw new RuntimeException("==AccountService的delete方法手動拋出異常");
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
package com.lyc.cn.v2.day09;
public interface BankService {
void save() throws RuntimeException;
}
package com.lyc.cn.v2.day09;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class BankServiceImpl implements BankService {
private PersonService personService;
private AccountService accountService;
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void save() throws RuntimeException {
System.out.println("==調(diào)用BankService的save方法\n");
personService.save();
accountService.save();
}
public void setPersonService(PersonService personService) {
this.personService = personService;
}
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
}
package com.lyc.cn.v2.day09;
public interface PersonService {
void save() throws RuntimeException;
}
package com.lyc.cn.v2.day09;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class PersonServiceImpl implements PersonService {
private JdbcTemplate jdbcTemplate;
private static String insert_sql = "insert into account(balance) values (100)";
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void save() throws RuntimeException {
System.out.println("==調(diào)用PersonService的save方法\n");
jdbcTemplate.update(insert_sql);
//throw new RuntimeException("==PersonService手動拋出異常");
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
新建三個Service接口及其實現(xiàn)類遏考,AccountService、BankService蓝谨、PersonService灌具。然后在BankService的save方法中調(diào)用了AccountService青团、PersonService的方法。
- 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--開啟tx注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--事物管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--數(shù)據(jù)源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/my_test?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="liyanchao1989@"/>
</bean>
<!--jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--業(yè)務(wù)bean-->
<bean id="accountService" class="com.lyc.cn.v2.day09.AccountServiceImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="personService" class="com.lyc.cn.v2.day09.PersonServiceImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="bankService" class="com.lyc.cn.v2.day09.BankServiceImpl">
<property name="personService" ref="personService"/>
<property name="accountService" ref="accountService"/>
</bean>
<bean id="myTransactionService" class="com.lyc.cn.v2.day09.MyTransactionService">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</beans>
- 測試類及結(jié)果
@Test
public void test2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("v2/day09.xml");
BankService service = ctx.getBean("bankService", BankService.class);
service.save();
}
==創(chuàng)建了名為:[com.lyc.cn.v2.day09.BankServiceImpl.save]的事物
==調(diào)用BankService的save方法
==創(chuàng)建了名為:[com.lyc.cn.v2.day09.PersonServiceImpl.save]的事物
==調(diào)用PersonService的save方法
==創(chuàng)建了名為:[com.lyc.cn.v2.day09.AccountServiceImpl.save]的事物
==調(diào)用AccountService的save方法
==準(zhǔn)備回滾com.lyc.cn.v2.day09.AccountServiceImpl.save
==當(dāng)前事物并非獨立事物,且RollbackOnly為true
==準(zhǔn)備回滾com.lyc.cn.v2.day09.BankServiceImpl.save
java.lang.RuntimeException: ==AccountService的save方法手動拋出異常
3.嵌套事物的處理簡介
回顧一下前面講的創(chuàng)建事物的過程咖楣,其中有:
// 2.如果當(dāng)前已經(jīng)存在事物
// 重點:
// 如果當(dāng)前已經(jīng)存在啟動的事物,則根據(jù)本次要新建的事物傳播特性進(jìn)行評估,以決定對新事物的后續(xù)處理
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
這里我們沒有分析督笆,因為從這開始就要涉及到事物的嵌套處理了(在AbstractPlatformTransactionManager類的getTransaction方法中)。
因為我們在在BankService的save方法中調(diào)用了AccountService截歉、PersonService的方法胖腾。那么當(dāng)BankService調(diào)用到另外兩個Servcie方法的時候,因為當(dāng)前已經(jīng)存在事物(BankService)瘪松,那么接下來就要根據(jù)新事物的傳播特性咸作,以決定下一步的處理了。先來感受一下處理過程:
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition,
Object transaction,
boolean debugEnabled) throws TransactionException {
// 1.PROPAGATION_NEVER --> 以非事物方式執(zhí)行宵睦,如果當(dāng)前存在事物记罚,則拋出異常。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");
}
// 2.以非事物方式執(zhí)行壳嚎,如果當(dāng)前存在事物桐智,則掛起當(dāng)前事物。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) { logger.debug("Suspending current transaction");}
// 重點:掛起已有事物
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 創(chuàng)建新事物,注意:transaction參數(shù)為null,所以這里創(chuàng)建的不是一個真正的事物
return prepareTransactionStatus(
definition,
null,
false,
newSynchronization,
debugEnabled,
suspendedResources);
}
//3.新建事物烟馅,如果當(dāng)前已經(jīng)存在事物说庭,則掛起當(dāng)前事物。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]");
}
// 掛起已有事物
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 創(chuàng)建事物
DefaultTransactionStatus status = newTransactionStatus(
definition,
transaction,
true,
newSynchronization,
debugEnabled,
suspendedResources);
// 開啟事物
doBegin(transaction, definition);
// 初始化事物同步屬性
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
// 4.如果當(dāng)前存在事物郑趁,則在嵌套事物內(nèi)執(zhí)行刊驴;如果當(dāng)前沒有事物,則與PROPAGATION_REQUIRED傳播特性相同
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 如果不允許嵌套事物,則拋出異常
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
/**
* 下面對JtaTransactionManager和AbstractPlatformTransactionManager分別進(jìn)行處理
*/
// useSavepointForNestedTransaction(),是否為嵌套事務(wù)使用保存點
// 1.對于JtaTransactionManager-->返回false
// 2.對于AbstractPlatformTransactionManager-->返回true
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
// 創(chuàng)建保存點在現(xiàn)有spring管理事務(wù),通過TransactionStatus SavepointManager API實現(xiàn)寡润。
// 通常使用JDBC 3.0保存點捆憎。永遠(yuǎn)不要激活Spring同步。
DefaultTransactionStatus status = prepareTransactionStatus(definition,transaction,
false,
false,
debugEnabled,
null);
// 創(chuàng)建保存點
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
// 通過嵌套的開始,提交調(diào)用,及回滾調(diào)用進(jìn)行嵌套事務(wù)梭纹。
// 只對JTA有效,如果已經(jīng)存在JTA事務(wù)躲惰,這里可能會激活Spring同步。
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition,
transaction,
true,
newSynchronization,
debugEnabled,
null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
// 處理PROPAGATION_SUPPORTS和PROPAGATION_REQUIRED兩種傳播特性
// PROPAGATION_REQUIRED --> 如果當(dāng)前沒有事物变抽,則新建一個事物础拨;如果已經(jīng)存在一個事物,則加入到這個事物中绍载。
// PROPAGATION_SUPPORTS --> 支持當(dāng)前事物太伊,如果當(dāng)前沒有事物,則以非事物方式執(zhí)行逛钻。
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
// 對于PROPAGATION_SUPPORTS和PROPAGATION_REQUIRED
// 新事物參與已有事物時,是否驗證已有事物.此屬性值默認(rèn)為false;
// 如開啟將驗證新事物和已有事物的隔離級別和事物只讀屬性是否相同
if (isValidateExistingTransaction()) {
// 驗證事物隔離級別
// 如果當(dāng)前事物的隔離級別不為默認(rèn)隔離級別,則比較當(dāng)前事物隔離級別與已有事物隔離級別,
// 如不同,則拋出事物隔離級別不兼容異常
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
// 驗證事物只讀屬性
// 如果當(dāng)前事物可寫,但是已有的事物是只讀,則拋出異常
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
在該方法中,對于Spring提供的傳播特性分別做了不同的處理锰提,那么接下來我們會對這些傳播特性一一分析曙痘,包括事物的后續(xù)處理芳悲、事物的回滾等。