您好,登錄后才能下訂單哦!
這篇文章主要介紹了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(); } }
在一個事務中可以指定回滾事務到某一個階段,實現精確控制事務。
在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()開啟事務,最外層方法使用傳播屬性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()從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()創建的,只是在提交時會將之前創建的回滾點釋放。
org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit
if (status.hasSavepoint()) { // NESTED的提交 if (status.isDebug()) { logger.debug("Releasing transaction savepoint"); } unexpectedRollback = status.isGlobalRollbackOnly(); // 只是釋放回滾點 status.releaseHeldSavepoint(); }
流程與addGood()一致。
addArea()發生異常,會執行回滾事務的邏輯,并沒有真正的回滾事務,因為事務并不是addArea()創建的,,只是將之前創建的回滾點釋放。 org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback
if (status.hasSavepoint()) { // 用于NESTED傳播機制,發生異常 // 回滾至回滾點 if (status.isDebug()) { logger.debug("Rolling back transaction to savepoint"); } status.rollbackToHeldSavepoint(); }
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的原理介紹的詳細內容了,看完之后是否有所收獲呢?如果想了解更多相關內容,歡迎來億速云行業資訊!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。