今天小編給大家分享的是一文解析spring中事務的傳播機制,相信很多人都不太了解,為了讓大家更加了解,所以給大家總結了以下內容,一起往下看吧。一定會有所收獲的哦。
Spring的事務其實就是數據庫的事務操作,符合ACID標準,也具有標準的事務隔離級別。
Spring中的事務只是對JDBC事務進行一些封裝與擴展,其底層最終還是會使用到JDBC的這套API。但是Spring事務有自己的特點,也就是事務傳播機制。
所謂事務傳播機制,也就是在事務在多個方法的調用中是如何傳遞的,是重新創建事務還是使用父方法的事務?父方法的回滾對子方法的事務是否有影響?這些都是可以通過事務傳播機制來決定的。
Area
package com.morris.spring.entity;import lombok.Data;import java.io.Serializable;@Datapublic class Area implements Serializable {private Integer id;private String areaName;private Integer areaCode;}
Good
package com.morris.spring.entity;import lombok.Data;import java.io.Serializable;import java.math.BigDecimal;@Datapublic class Good implements Serializable {private Integer id;private String goodName;private BigDecimal price;}
AreaDao
package com.morris.spring.dao;import com.morris.spring.entity.Area;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;public class AreaDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public boolean insert(Area area) {String sql = "insert into t_area(area_name, area_code) values(?,?)";return jdbcTemplate.update(sql, area.getAreaName(), area.getAreaCode()) > 0;}}
GoodDao
package com.morris.spring.dao;import com.morris.spring.entity.Good;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;public class GoodDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public boolean insert(Good good) {String sql = "insert into t_good(good_name, price) values(?,?)";return jdbcTemplate.update(sql, good.getGoodName(), good.getPrice()) > 0;}}
AreaServiceImpl
package com.morris.spring.service;import com.morris.spring.dao.AreaDao;import com.morris.spring.entity.Area;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;public class AreaServiceImpl implements AreaService {@Autowiredprivate AreaDao areaDao;@Transactional(propagation = Propagation.REQUIRED)@Overridepublic boolean addArea(int i) {int y = 1000000 / i;Area area = new Area();area.setAreaCode(y);area.setAreaName("shenzhen");return areaDao.insert(area);}}
GoodServiceImpl
package com.morris.spring.service;import com.morris.spring.dao.GoodDao;import com.morris.spring.entity.Good;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import java.math.BigDecimal;public class GoodServiceImpl implements GoodService {@Autowiredprivate GoodDao goodDao;@Transactional(propagation = Propagation.REQUIRED)@Overridepublic boolean addGood() {Good good = new Good();good.setGoodName("iphone");good.setPrice(BigDecimal.valueOf(99999));return goodDao.insert(good);}}
TransactionService
package com.morris.spring.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;@Componentpublic class TransactionService {@Autowiredprivate GoodService goodService;@Autowiredprivate AreaService areaService;@Transactional(propagation = Propagation.REQUIRED)public void addGoodAndArea() {System.out.println("------addGoodAndArea-------");areaService.addArea(10);goodService.addGood();}}
TransactionPropagationDemo
package com.morris.spring.demo.jdbc;import com.morris.spring.config.JdbcConfig;import com.morris.spring.dao.AreaDao;import com.morris.spring.dao.GoodDao;import com.morris.spring.service.AreaServiceImpl;import com.morris.spring.service.GoodServiceImpl;import com.morris.spring.service.TransactionService;import org.junit.jupiter.api.Test;import org.springframework.context.annotation.AnnotationConfigApplicationContext;/** * 事務的傳播機制 */public class TransactionPropagationDemo {@Testpublic void test() {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();applicationContext.register(GoodDao.class);applicationContext.register(AreaDao.class);applicationContext.register(GoodServiceImpl.class);applicationContext.register(AreaServiceImpl.class);applicationContext.register(JdbcConfig.class);applicationContext.register(TransactionService.class);applicationContext.refresh();TransactionService transactionService = applicationContext.getBean(TransactionService.class);transactionService.addGoodAndArea();}}
具體選項可以參考枚舉類org.springframework.transaction.annotation.Propagation。
選項 | 說明 |
REQUIRED | 默認選項。如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。 |
SUPPORTS | 支持當前事務,如果當前沒有事務,就以非事務方式執行。 |
MANDATORY | 使用當前的事務,如果當前沒有事務,就拋出異常。 |
REQUIRES_NEW | 新建事務,如果當前存在事務,把當前事務掛起。 |
NOT_SUPPORTED | 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。 |
NEVER | 以非事務方式執行,如果當前存在事務,則拋出異常。 |
NESTED | 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與REQUIRED類似的操作。 |
默認選項。如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。
配置如下:
TransactionService:REQUIRED
AreaServiceImpl:REQUIRED
GoodServiceImpl:REQUIRED
運行上面的Demo,運行結果如下:
// 創建第一個事務 DEBUG DataSourceTransactionManager:381 - 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit ------addGoodAndArea------- // 在第一個事務中執行 DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] // 在第一個事務中執行 DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // 提交第一個事務 DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction
總結:
當外層沒有事務的時候,TransactionService.addGoodAndArea()方法執行發現沒有事務可用,自己新建事務。
goodService.addGood()和areaService.addArea()執行時發現已有事務,就使用當前事務執行。
新建事務,如果當前存在事務,把當前事務掛起。
配置如下:
TransactionService:REQUIRED
GoodServiceImpl:REQUIRES
AreaServiceImpl:REQUIRES_NEW
運行結果如下:
// 創建第一個事務 DEBUG DataSourceTransactionManager:381 - 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] to manual commit ------addGoodAndArea------- // 掛起第一個事務并創建第二個事務 DEBUG DataSourceTransactionManager:446 - Suspending current transaction, creating new transaction with name [com.morris.spring.service.AreaServiceImpl.addArea] // 創建第二個連接 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] to manual commit DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // 提交第二個事務 DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] after transaction // 恢復第一個事務 DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction // 在第一個事務中執行 DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] after transaction
總結:areaServiceImpl.addArea()執行時發現已有事務,就把當前事務掛起,執行完后再恢復。
支持當前事務,如果當前沒有事務,就以非事務方式執行。
配置如下:
TransactionService:SUPPORTS
AreaServiceImpl:REQUIRED
GoodServiceImpl:SUPPORTS
運行結果如下:
// TransactionService.addGoodAndArea以非事務方式運行 ------addGoodAndArea------- // 開啟第一個事務 DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.AreaServiceImpl.addArea]: 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] to manual commit DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // 提交第一個事務 DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] after transaction DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction // AreaServiceImpl.addArea以非事務方式運行 DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
總結:當前沒有事務,TransactionService.addGoodAndArea()和AreaServiceImpl.addArea()以非事務方式運行。
修改配置如下:
TransactionService:REQUIRED
AreaServiceImpl:SUPPORTS
GoodServiceImpl:SUPPORTS
運行結果如下:
// 開啟第一個事務 DEBUG DataSourceTransactionManager:381 - 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] to manual commit ------addGoodAndArea------- // 在第一個事務中執行 DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // 在第一個事務中執行 DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] // 提交第一個事務 DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] after transaction
總結:當前有事務,GoodServiceImpl.addGood()和AreaServiceImpl.addArea()以事務方式運行。
使用當前的事務,如果當前沒有事務,就拋出異常。
配置如下:
TransactionService:REQUIRED
AreaServiceImpl:REQUIRED
GoodServiceImpl:MANDATORY
運行結果如下:
// 創建第一個事務 EBUG DataSourceTransactionManager:381 - 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] to manual commit ------addGoodAndArea------- // 在第一個事務中執行 DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // 在第一個事務中執行 DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] // 提交第一個事務 DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] after transaction
總結:當前有事務,GoodServiceImpl.addGood()以事務方式運行。
修改配置如下:
TransactionService:SUPPORTS
AreaServiceImpl:MANDATORY
GoodServiceImpl:SUPPORTS
運行結果如下:
------addGoodAndArea------- DEBUG DataSourceTransactionManager:888 - Should roll back transaction but cannot - no transaction available org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory' at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:372) at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:374) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:205) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:219) at com.sun.proxy.$Proxy23.addArea(Unknown Source) at com.morris.spring.service.TransactionService.addGoodAndArea(TransactionService.java:20)
總結:當前沒有有事務,AreaServiceImpl.addArea()會拋出異常。
以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
配置如下:
TransactionService:REQUIRED
AreaServiceImpl:NOT_SUPPORTED
GoodServiceImpl:NOT_SUPPORTED
運行結果如下:
// 開啟第一個事務 DEBUG DataSourceTransactionManager:381 - 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit ------addGoodAndArea------- // 掛起第一個事務 DEBUG DataSourceTransactionManager:436 - Suspending current transaction // 以非事務方式運行 DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] DEBUG DataSourceUtils:115 - Fetching JDBC Connection from DataSource 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:1043 - Resuming suspended transaction after completion of inner transaction // 掛起第一個事務 DEBUG DataSourceTransactionManager:436 - Suspending current transaction // 以非事務方式運行 DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] DEBUG DataSourceUtils:115 - Fetching JDBC Connection from DataSource 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:1043 - Resuming suspended transaction after completion of inner transaction // 提交第一個事務 DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction
總結:執行GoodServiceImpl.addGood()和AreaServiceImpl.addArea()時當前存在事務,就把當前事務掛起。
以非事務方式執行,如果當前存在事務,則拋出異常。
配置如下:
TransactionService:NEVER
AreaServiceImpl:NEVER
GoodServiceImpl:NEVER
運行結果如下:
// 沒有事務都以非事務方式運行 ------addGoodAndArea------- DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] DEBUG DataSourceUtils:115 - Fetching JDBC Connection from DataSource 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 JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
總結:當前沒有事務都以非事務方式執行。
修改配置如下:
TransactionService:REQUIRED
AreaServiceImpl:NEVER
GoodServiceImpl:NEVER
運行結果如下:
// 開啟第一個事務 DEBUG DataSourceTransactionManager:381 - 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit ------addGoodAndArea------- DEBUG DataSourceTransactionManager:864 - Initiating transaction rollback DEBUG DataSourceTransactionManager:345 - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never' at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:430) at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:362) at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:374) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:205) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:219) at com.sun.proxy.$Proxy23.addArea(Unknown Source) at com.morris.spring.service.TransactionService.addGoodAndArea(TransactionService.java:20)
總結:AreaServiceImpl.addArea()執行時存在事務就會拋出異常。
如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與REQUIRED類似的操作。
配置如下:
TransactionService:REQUIRED
GoodServiceImpl:NESTED
AreaServiceImpl:NESTED
運行結果如下:
// 創建第一個事務 EBUG DataSourceTransactionManager:381 - 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit ------addGoodAndArea------- // 創建回滾點 DEBUG DataSourceTransactionManager:466 - Creating nested transaction with name [com.morris.spring.service.AreaServiceImpl.addArea] DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // 釋放回滾點 DEBUG DataSourceTransactionManager:754 - Releasing transaction savepoint // 創建回滾點 DEBUG DataSourceTransactionManager:466 - 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:754 - Releasing transaction savepoint DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction
如果不拋出異常,使用REQUIRED與NESTED都差不多,區別在于發生異常,下面演示REQUIRED與NESTED發生異常時的區別:
配置如下:
TransactionService:REQUIRED
AreaServiceImpl:REQUIRED
GoodServiceImpl:REQUIRED
TransactionService.addGoodAndArea修改如下:
@Transactional(propagation = Propagation.REQUIRED) public void addGoodAndArea() { System.out.println("------addGoodAndArea-------"); try { areaService.addArea(0); } catch (Exception e) { e.printStackTrace(); } goodService.addGood(); }
運行結果如下:
DEBUG DataSourceTransactionManager:381 - 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] to manual commit ------addGoodAndArea------- DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG DataSourceTransactionManager:877 - Participating transaction failed - marking existing transaction as rollback-only DEBUG DataSourceTransactionManager:360 - Setting JDBC transaction [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] rollback-only java.lang.ArithmeticException: / by zero at com.morris.spring.service.AreaServiceImpl.addArea(AreaServiceImpl.java:18) ... ... DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] DEBUG DataSourceTransactionManager:723 - Global transaction is marked as rollback-only but transactional code requested commit DEBUG DataSourceTransactionManager:877 - Participating transaction failed - marking existing transaction as rollback-only DEBUG DataSourceTransactionManager:360 - Setting JDBC transaction [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] rollback-only DEBUG DataSourceTransactionManager:723 - Global transaction is marked as rollback-only but transactional code requested commit DEBUG DataSourceTransactionManager:864 - Initiating transaction rollback DEBUG DataSourceTransactionManager:345 - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] after transaction org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:905) ... ...
從運行結果可以發現事務全部都回滾了。
將上面的配置修改如下:
TransactionService:REQUIRED
AreaServiceImpl:NESTED
GoodServiceImpl:NESTED
運行結果如下:
DEBUG DataSourceTransactionManager:381 - 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:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit ------addGoodAndArea------- DEBUG DataSourceTransactionManager:466 - Creating nested transaction with name [com.morris.spring.service.AreaServiceImpl.addArea] DEBUG DataSourceTransactionManager:857 - Rolling back transaction to savepoint java.lang.ArithmeticException: / by zero at com.morris.spring.service.AreaServiceImpl.addArea(AreaServiceImpl.java:18) ... ... DEBUG DataSourceTransactionManager:466 - 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:754 - Releasing transaction savepoint DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction
從運行結果可以發現areaService.addArea()回滾了(本來就沒有提交內容),goodService.addGood()的內容提交了。
注意只有運行時異常以及rollbakcFor指定的異常才會回滾。
關于一文解析spring中事務的傳播機制就分享到這里了,希望以上內容可以對大家有一定的參考價值,可以學以致用。如果喜歡本篇文章,不妨把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。