中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

spring的事務傳播屬性REQUIRED_NESTED的原理介紹

發布時間:2023-06-28 11:22:20 來源:億速云 閱讀:111 作者:栢白 欄目:開發技術

這篇文章主要介紹了spring的事務傳播屬性REQUIRED_NESTED的原理介紹,具有一定借鑒價值,需要的朋友可以參考下。下面就和我一起來看看吧。

傳統事務中回滾點的使用

package com.morris.spring.demo.jdbc;
import java.sql.*;
/**
 * 傳統JDBC中回滾點的使用
 */
public class TraditionSavePointDemo {
	public static void main(String[] args) throws SQLException {
		String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull";
		String username = "user";
		String password = "user";
		Connection connection = DriverManager.getConnection(url, username, password);
		connection.setAutoCommit(false); // 不自動提交
		Savepoint one = connection.setSavepoint("one");
		Savepoint two = null;
		try {
			Statement statement = connection.createStatement();
			statement.execute("insert into t_good(good_name, price) values('iphone14', 9999)");
			statement.close();
			two = connection.setSavepoint("two");
		} catch (Exception e) {
			e.printStackTrace();
			connection.rollback(one); // 回滾事務
		}
		try {
			Statement statement = connection.createStatement();
			statement.execute("insert into t_good(good_name, price) values('iphone15', 9999)");
			statement.close();
			boolean flag = true;
			if(flag) {
				throw new RuntimeException("xxxx");
			}
		} catch (Exception e) {
			e.printStackTrace();
			connection.rollback(two); // 回滾事務
		}
		connection.commit();
	}
}

在一個事務中可以指定回滾事務到某一個階段,實現精確控制事務。

事務的傳播屬性NESTED

在spring中,要想使用事務中的回滾點,可以使用傳播屬性NESTED。

com.morris.spring.service.TransactionService#addGoodAndArea

@Transactional(propagation = Propagation.REQUIRED)
public void addGoodAndArea() {
	System.out.println("------addGoodAndArea-------");
	goodService.addGood();
	areaService.addArea(0);
}

com.morris.spring.service.AreaServiceImpl#addArea

@Transactional(propagation = Propagation.NESTED)
@Override
public boolean addArea(int i) {
	int y = 1000000 / i;
	Area area = new Area();
	area.setAreaCode(y);
	area.setAreaName("shenzhen");
	return areaDao.insert(area);
}

com.morris.spring.service.GoodServiceImpl#addGood

@Transactional(propagation = Propagation.NESTED)
@Override
public boolean addGood() {
	Good good = new Good();
	good.setGoodName("iphone");
	good.setPrice(BigDecimal.valueOf(99999));
	return goodDao.insert(good);
}

運行結果如下:

DEBUG DataSourceTransactionManager:384 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:267 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction
DEBUG DataSourceTransactionManager:285 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit
------addGoodAndArea-------
DEBUG DataSourceTransactionManager:477 - Creating nested transaction with name [com.morris.spring.service.GoodServiceImpl.addGood]
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
DEBUG DataSourceTransactionManager:767 - Releasing transaction savepoint
DEBUG DataSourceTransactionManager:477 - Creating nested transaction with name [com.morris.spring.service.AreaServiceImpl.addArea]
DEBUG DataSourceTransactionManager:870 - Rolling back transaction to savepoint
DEBUG DataSourceTransactionManager:877 - Initiating transaction rollback
DEBUG DataSourceTransactionManager:347 - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162]
DEBUG DataSourceTransactionManager:392 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction
java.lang.ArithmeticException: / by zero
... ...

發現整個事務都已經回滾了,按照回滾點的邏輯,addArea()方法拋出異常,不是應該只回滾到addArea()前嗎,也就是addGood()應該被提交,這是為什么呢?

如果我們將addArea()方法try catch起來,就能得到我們想要的結果,addGood()被提交,而addArea()回滾,這又是為什么呢?我們帶著這幾個問題來分析源碼。

addAreaAndGood()開啟事務

addAreaAndGood()開啟事務,最外層方法使用傳播屬性PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED效果都一樣,都是開啟一個新的事務。

org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction

else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
		def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
		def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
	// 第一次進來
	SuspendedResourcesHolder suspendedResources = suspend(null);
	if (debugEnabled) {
		logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
	}
	try {
		// 開啟新事務
		return startTransaction(def, transaction, debugEnabled, suspendedResources);
	}
	catch (RuntimeException | Error ex) {
		resume(null, suspendedResources);
		throw ex;
	}
}

addGood()獲得事務并創建回滾點

addGood()從ThreadLocal中獲得addAreaAndGood()創建的事務,然后發現自己的傳播屬性為PROPAGATION_NESTED,就創建了一個回滾點。

org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction

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() + "]");
	}
	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.
		DefaultTransactionStatus status =
				prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
		// 創建回滾點
		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.
		return startTransaction(definition, transaction, debugEnabled, null);
	}
}

addGood()提交事務時釋放回滾點

addGood()并不會真正的提交事務,因為事務并不是addGood()創建的,只是在提交時會將之前創建的回滾點釋放。

org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit

if (status.hasSavepoint()) {
	// NESTED的提交
	if (status.isDebug()) {
		logger.debug("Releasing transaction savepoint");
	}
	unexpectedRollback = status.isGlobalRollbackOnly();
	// 只是釋放回滾點
	status.releaseHeldSavepoint();
}

addArea()獲得事務并創建回滾點

流程與addGood()一致。

addArea()回滾事務釋放回滾點

addArea()發生異常,會執行回滾事務的邏輯,并沒有真正的回滾事務,因為事務并不是addArea()創建的,,只是將之前創建的回滾點釋放。 org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback

if (status.hasSavepoint()) {
	// 用于NESTED傳播機制,發生異常
	// 回滾至回滾點
	if (status.isDebug()) {
		logger.debug("Rolling back transaction to savepoint");
	}
	status.rollbackToHeldSavepoint();
}

addAreaAndGood()回滾這個事務

addArea()發生異常后繼續往外拋,addAreaAndGood()也會捕獲到異常,然后執行回滾邏輯,這樣整個事務都回滾了。 org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback

else if (status.isNewTransaction()) {
	// 只有最外層的事務newTransaction=true
	if (status.isDebug()) {
		logger.debug("Initiating transaction rollback");
	}
	// 事務的回滾
	/**
	 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doRollback(org.springframework.transaction.support.DefaultTransactionStatus)
	 */
	doRollback(status);
}

為什么將addArea()方法try catch起來,整個事務就不會回滾了呢?

因為將addArea()方法try catch起來后,addAreaAndGood()就會執行提交事務的邏輯,這樣addGood()就被提交了。

以上就是spring的事務傳播屬性REQUIRED_NESTED的原理介紹的詳細內容了,看完之后是否有所收獲呢?如果想了解更多相關內容,歡迎來億速云行業資訊!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

喀喇| 道孚县| 萝北县| 江门市| 开远市| 梁平县| 晋宁县| 浦县| 明溪县| 灵武市| 德江县| 瓮安县| 衡阳市| 闻喜县| 尚志市| 玛沁县| 涞水县| 石首市| 米易县| 泊头市| 扶余县| 皮山县| 鹤岗市| 佛学| 昌平区| 治多县| 镇坪县| 阿克陶县| 兰州市| 迭部县| 威信县| 昌邑市| 佳木斯市| 玉环县| 长岭县| 积石山| 大渡口区| 孝感市| 兰溪市| 大足县| 玛多县|