關(guān)于spring中事務(wù)的傳播機(jī)制
Spring中的事務(wù)
Spring的事務(wù)其實就是數(shù)據(jù)庫的事務(wù)操作,符合ACID標(biāo)準(zhǔn),也具有標(biāo)準(zhǔn)的事務(wù)隔離級別。
Spring中的事務(wù)只是對JDBC事務(wù)進(jìn)行一些封裝與擴(kuò)展,其底層最終還是會使用到JDBC的這套API。但是Spring事務(wù)有自己的特點,也就是事務(wù)傳播機(jī)制。
所謂事務(wù)傳播機(jī)制,也就是在事務(wù)在多個方法的調(diào)用中是如何傳遞的,是重新創(chuàng)建事務(wù)還是使用父方法的事務(wù)?父方法的回滾對子方法的事務(wù)是否有影響?這些都是可以通過事務(wù)傳播機(jī)制來決定的。
準(zhǔn)備工作
實體類
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;}
DAO層
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;}}
Service層
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;/** * 事務(wù)的傳播機(jī)制 */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();}}
傳播機(jī)制
具體選項可以參考枚舉類org.springframework.transaction.annotation.Propagation。
選項 | 說明 |
REQUIRED | 默認(rèn)選項。如果當(dāng)前沒有事務(wù),就新建一個事務(wù),如果已經(jīng)存在一個事務(wù)中,加入到這個事務(wù)中。 |
SUPPORTS | 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。 |
MANDATORY | 使用當(dāng)前的事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。 |
REQUIRES_NEW | 新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。 |
NOT_SUPPORTED | 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。 |
NEVER | 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。 |
NESTED | 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則執(zhí)行與REQUIRED類似的操作。 |
REQUIRED
默認(rèn)選項。如果當(dāng)前沒有事務(wù),就新建一個事務(wù),如果已經(jīng)存在一個事務(wù)中,加入到這個事務(wù)中。
配置如下:
- TransactionService:REQUIRED
- AreaServiceImpl:REQUIRED
- GoodServiceImpl:REQUIRED
運行上面的Demo,運行結(jié)果如下:
// 創(chuàng)建第一個事務(wù) DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // 創(chuàng)建第一個連接 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------- // 在第一個事務(wù)中執(zhí)行 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(?,?)] // 在第一個事務(wù)中執(zhí)行 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(?,?)] // 提交第一個事務(wù) 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
總結(jié):
- 當(dāng)外層沒有事務(wù)的時候,TransactionService.addGoodAndArea()方法執(zhí)行發(fā)現(xiàn)沒有事務(wù)可用,自己新建事務(wù)。
- goodService.addGood()和areaService.addArea()執(zhí)行時發(fā)現(xiàn)已有事務(wù),就使用當(dāng)前事務(wù)執(zhí)行。
REQUIRES_NEW
新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
配置如下:
- TransactionService:REQUIRED
- GoodServiceImpl:REQUIRES
- AreaServiceImpl:REQUIRES_NEW
運行結(jié)果如下:
// 創(chuàng)建第一個事務(wù) DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // 創(chuàng)建第一個連接 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------- // 掛起第一個事務(wù)并創(chuàng)建第二個事務(wù) DEBUG DataSourceTransactionManager:446 - Suspending current transaction, creating new transaction with name [com.morris.spring.service.AreaServiceImpl.addArea] // 創(chuàng)建第二個連接 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(?,?)] // 提交第二個事務(wù) 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 // 恢復(fù)第一個事務(wù) DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction // 在第一個事務(wù)中執(zhí)行 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
總結(jié):areaServiceImpl.addArea()執(zhí)行時發(fā)現(xiàn)已有事務(wù),就把當(dāng)前事務(wù)掛起,執(zhí)行完后再恢復(fù)。
SUPPORTS
支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。
配置如下:
- TransactionService:SUPPORTS
- AreaServiceImpl:REQUIRED
- GoodServiceImpl:SUPPORTS
運行結(jié)果如下:
// TransactionService.addGoodAndArea以非事務(wù)方式運行 ------addGoodAndArea------- // 開啟第一個事務(wù) DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.AreaServiceImpl.addArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // 創(chuàng)建第一個連接 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(?,?)] // 提交第一個事務(wù) 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以非事務(wù)方式運行 DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
總結(jié):當(dāng)前沒有事務(wù),TransactionService.addGoodAndArea()和AreaServiceImpl.addArea()以非事務(wù)方式運行。
修改配置如下:
- TransactionService:REQUIRED
- AreaServiceImpl:SUPPORTS
- GoodServiceImpl:SUPPORTS
運行結(jié)果如下:
// 開啟第一個事務(wù) DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // 創(chuàng)建第一個連接 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------- // 在第一個事務(wù)中執(zhí)行 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(?,?)] // 在第一個事務(wù)中執(zhí)行 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(?,?)] // 提交第一個事務(wù) 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
總結(jié):當(dāng)前有事務(wù),GoodServiceImpl.addGood()和AreaServiceImpl.addArea()以事務(wù)方式運行。
MANDATORY
使用當(dāng)前的事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。
配置如下:
- TransactionService:REQUIRED
- AreaServiceImpl:REQUIRED
- GoodServiceImpl:MANDATORY
運行結(jié)果如下:
// 創(chuàng)建第一個事務(wù) EBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // 創(chuàng)建第一個連接 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------- // 在第一個事務(wù)中執(zhí)行 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(?,?)] // 在第一個事務(wù)中執(zhí)行 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(?,?)] // 提交第一個事務(wù) 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
總結(jié):當(dāng)前有事務(wù),GoodServiceImpl.addGood()以事務(wù)方式運行。
修改配置如下:
- TransactionService:SUPPORTS
- AreaServiceImpl:MANDATORY
- GoodServiceImpl:SUPPORTS
運行結(jié)果如下:
------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)
總結(jié):當(dāng)前沒有有事務(wù),AreaServiceImpl.addArea()會拋出異常。
NOT_SUPPORTED
以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
配置如下:
- TransactionService:REQUIRED
- AreaServiceImpl:NOT_SUPPORTED
- GoodServiceImpl:NOT_SUPPORTED
運行結(jié)果如下:
// 開啟第一個事務(wù) 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------- // 掛起第一個事務(wù) DEBUG DataSourceTransactionManager:436 - Suspending current transaction // 以非事務(wù)方式運行 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] // 恢復(fù)第一個事務(wù) DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction // 掛起第一個事務(wù) DEBUG DataSourceTransactionManager:436 - Suspending current transaction // 以非事務(wù)方式運行 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] // 恢復(fù)第一個事務(wù) DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction // 提交第一個事務(wù) 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
總結(jié):執(zhí)行GoodServiceImpl.addGood()和AreaServiceImpl.addArea()時當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
NEVER
以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
配置如下:
- TransactionService:NEVER
- AreaServiceImpl:NEVER
- GoodServiceImpl:NEVER
運行結(jié)果如下:
// 沒有事務(wù)都以非事務(wù)方式運行 ------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(?,?)]
總結(jié):當(dāng)前沒有事務(wù)都以非事務(wù)方式執(zhí)行。
修改配置如下:
- TransactionService:REQUIRED
- AreaServiceImpl:NEVER
- GoodServiceImpl:NEVER
運行結(jié)果如下:
// 開啟第一個事務(wù) 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)
總結(jié):AreaServiceImpl.addArea()執(zhí)行時存在事務(wù)就會拋出異常。
NESTED
如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則執(zhí)行與REQUIRED類似的操作。
配置如下:
- TransactionService:REQUIRED
- GoodServiceImpl:NESTED
- AreaServiceImpl:NESTED
運行結(jié)果如下:
// 創(chuàng)建第一個事務(wù) EBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // 創(chuàng)建第一個連接 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------- // 創(chuàng)建回滾點 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 // 創(chuàng)建回滾點 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都差不多,區(qū)別在于發(fā)生異常,下面演示REQUIRED與NESTED發(fā)生異常時的區(qū)別:
配置如下:
- 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(); }
運行結(jié)果如下:
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) ... ...
從運行結(jié)果可以發(fā)現(xiàn)事務(wù)全部都回滾了。
將上面的配置修改如下:
- TransactionService:REQUIRED
- AreaServiceImpl:NESTED
- GoodServiceImpl:NESTED
運行結(jié)果如下:
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
從運行結(jié)果可以發(fā)現(xiàn)areaService.addArea()回滾了(本來就沒有提交內(nèi)容),goodService.addGood()的內(nèi)容提交了。
注意只有運行時異常以及rollbakcFor指定的異常才會回滾。
到此這篇關(guān)于關(guān)于spring中事務(wù)的傳播機(jī)制的文章就介紹到這了,更多相關(guān)spring事務(wù)的傳播機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Idea 2019.2 安裝lombok插件失效問題解決
這篇文章主要介紹了詳解Idea 2019.2 安裝lombok插件失效問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10SpringMVC @RequestBody 為null問題的排查及解決
這篇文章主要介紹了SpringMVC @RequestBody 為null問題的排查及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10JavaBean valication驗證實現(xiàn)方法示例
這篇文章主要介紹了JavaBean valication驗證實現(xiàn)方法,結(jié)合實例形式分析了JavaBean valication驗證相關(guān)概念、原理、用法及操作注意事項,需要的朋友可以參考下2020-03-03IDEA使用學(xué)生郵箱無法注冊問題:JetBrains Account connection error: 拒絕連接
這篇文章主要介紹了IDEA使用學(xué)生郵箱無法注冊問題:JetBrains Account connection error: 拒絕連接,文中通過圖文及示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07spring?boot?使用Mybatis-plus查詢方法解析
這篇文章主要介紹了spring?boot?使用Mybatis-plus查詢方法解析,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09springboot項目配置context path失效的問題解決
本文主要介紹了springboot項目配置context path失效的問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04