Spring事务机制
Spring事务
1、Spring事务概述
Spring中的事务是真的难,代码少,但是全部串联起来了,画个完整的流程图画不出来,让人很难受。
所以从简单的代码入手,然后一步一步的窥探其全貌。
2、@EnableTransactionManagement注解
首先使用spring提供的事务必须要在配置类上加一个注解@EnableTransactionManagement,而这个注解在背后也做了一些隐秘的事情。
2.1、TransactionManagementConfigurationSelector
因为这个类实现了ImportSelector接口,所以会来实现selectImports方法,来注册对应的bean
这里来注册了两个bean,一个是AutoProxyRegistrar,另外一个是ProxyTransactionManagementConfiguration。
众所周知,spring中的事务是利用Spring的AOP来进行实现的。那么就少不了Advisor和PointCut。PointCut肯定是判断类或者是方法上有无@Transactional注解,如果有,那么则生成对应的代理对象。
那么这两个类就是来实现上面的功能的。那么来具体说明一下
2.2、AutoProxyRegistrar
这个类从名字上看是自动代理注册,那么从这里可以明白,自动注册代理的是什么。应该是加了@Transactional注解之后的代理类,这里心中有个大概的概念即可。那么看下具体的实现:
导入了InfrastructureAdvisorAutoProxyCreator,而这个类继承了AbstractAutoProxyCreator,说明了这个是BeanPostProcessor,那么肯定就是在bean初始化阶段中产生对应的代理类的BeanPostProcessor的。
那么对Advisor进行筛选的时候就应该由InfrastructureAdvisorAutoProxyCreator这个类来进行决定。
2.3、ProxyTransactionManagementConfiguration
从名字上代理事务管理配置,那么应该要做什么?继承上面是对Advisor来进行筛选,那么这里肯定是来生成Advisor的。
而点进源码一看,验证了上面的猜测:
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
// AnnotationTransactionAttributeSource中定义了一个Pointcut
// 并且AnnotationTransactionAttributeSource可以用来解析@Transactional注解,并得到一个RuleBasedTransactionAttribute对象
return new AnnotationTransactionAttributeSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
2.3.1、BeanFactoryTransactionAttributeSourceAdvisor
从名字上看就是一个Advisor,而对应的Advice和PointCut是从容器中获取得到的,恰好又是下面配置的。
那么就分别来看下
2.3.2、TransactionInterceptor
为什么要先说TransactionInterceptor,因为Advice最终就会封装称为一个Interceptor。那么这里只需要看看这里的实现即可。
TransactionInterceptor实现了MethodInterceptor接口,那么就应该有对应的拦截方法。那么代理对象最终就会调用到里面的invoke方法中来。
2.3.3、TransactionAttributeSource
既然上面是Advice,那么这里就应该是PointCut,有了PointCut,那么就又有ClassFilter和对应的方法匹配器。
那么深入的了解一下,点击到对应的set方法中去:
public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
this.transactionAttributeSource = transactionAttributeSource;
}
而这里传入的是一个SpringTransactionAnnotationParser,spring事务注解解析器,那么看看解析的是什么
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
看到了@Transactional就明白了这里的秘密。将@Transactional注解形成TransactionAttribute对象,然后又解析成RuleBasedTransactionAttribute保存得到对应的属性,所以@Transactional最终的信息都存在了RuleBasedTransactionAttribute中。
看到源码到此为止,我们可以认为是如果类上或者是方法上加了@Transactional注解,那么就匹配。匹配就应该结合Advice来生成对应的Advisor,而对应的生成Advisor的地方在哪里?那么肯定是在bean的初始化后方法中。
2.4、解析成Advisor
AnnotationTransactionAttributeSource就是用来判断某个类上是否存在@Transactional注解, 或者判断某个方法上是否存在@Transactional注解的。匹配了对应的PointCut就会结合Advice进行生成对应的Advisor,放入到容器中去。
3、事务操作
那么当代理对象调用对应的代理方法的时候,则是会执行到TransactionInterceptor的invoke方法中来。
这里的代码真的是可读性太差,所以这里需要结合案例和对应的图来进行分析。我在这里结合一幅图
下面的可以不用看了。
看下对应的方法:
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
@Override
@Nullable
public Object proceedWithInvocation() throws Throwable {
// 执行后续的Interceptor,以及被代理的方法
return invocation.proceed(); // test() sql
}
@Override
public Object getTarget() {
return invocation.getThis();
}
@Override
public Object[] getArguments() {
return invocation.getArguments();
}
});
}
3.1、invokeWithinTransaction
那么这里最重要的代码就是invokeWithinTransaction中的代码,那么进去看下:
这里做一些筛检,看重点代码即可:
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
// TransactionAttribute就是@Transactional中的配置
TransactionAttributeSource tas = getTransactionAttributeSource();
// 获取@Transactional注解中的属性值
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 返回Spring容器中类型为TransactionManager的Bean对象
// 事务提交、回滚、创建数据库连接都是由这个对象来做到的
// get容器中的事务管理器
final TransactionManager tm = determineTransactionManager(txAttr);
...................
// 把tm强制转换为PlatformTransactionManager,所以我们在定义时得定义PlatformTransactionManager类型
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// joinpoint的唯一标识,就是当前在执行的方法名字
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// CallbackPreferringPlatformTransactionManager表示拥有回调功能的PlatformTransactionManager,也不常用
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 如果有必要就创建事务,这里就涉及到事务传播机制的实现了
// TransactionInfo表示一个逻辑事务,比如两个逻辑事务属于同一个物理事务
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 执行下一个Interceptor或被代理对象中的方法
retVal = invocation.proceedWithInvocation(); //test
}
catch (Throwable ex) {
// target invocation exception
// 抛异常了,则回滚事务,或者
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
..........
}
从上面删减的代码上来看就变得简单了,先开启事务,然后执行方法,最终决定提交或者是回滚。
那么从这里来看的话,最终要的代码就在于下面这行代码,根据需要来创建事务,需要注意的时候事务信息都封装在了这个对象中,所以这个对象也很重要。
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
那么看下如何根据事务管理器、方法名称以及@Transactional中的属性来进行配置的。
3.2、createTransactionIfNecessary
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
// 每个逻辑事务都会创建一个TransactionStatus,但是TransactionStatus中有一个属性代表当前逻辑事务底层的物理事务是不是新的
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 开启事务
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
// 返回一个TransactionInfo对象,表示得到了一个事务,可能是新创建的一个事务,也可能是拿到的已有的事务
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
3.3、getTransaction方法
看下开启事务方法,代码很长,可读性太差。进入到AbstractPlatformTransactionManager类中的getTransaction方法,这里将代码分解一下,看重要代码:
Object transaction = doGetTransaction();
来到DataSourceTransactionManager中的doGetTransaction方法中来:
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
// FALSE表示的是连接不是新建的
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
首先来创建一个对象,然后将ConnectionHolder存入进去之后,设置一下不是一个新建的连接。
而TransactionSynchronizationManager.getResource这里是通过ThreadLocal来进行实现的,针对的是每个线程,看下结构:
// key为DataSource对象,value为ConnectionHolder对象
ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");
如果是第一次开启事务,那么ThreadLocal根据key(key为DataSource对象)获取得到的时候肯定是没有值的,那么对应的ConnectionHolder就为null,如果时候第N次来进行获取得到的时候,这里就是上一个事务的数据库连接。
所以第一次获取得到的时候DataSourceTransactionObject对象中的ConnectionHolder为null,而对应的false表示的不是新建的数据库连接;如果是第N次来进行获取得到的时候,表示获取得到的连接不是当前事务新建的。
得到了一个DataSourceTransactionObject之后,那么来判断,是否已经有了事务,判断的逻辑肯定是看数据库连接是否有对应的数据库连接:
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(def, transaction, debugEnabled);
}
如果有的话,就会进入到已有的环节。那么假设当前是没有的。
// 在当前Thread中没有事务的前提下,以下三个是等价的
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 没有事务需要挂起,不过TransactionSynchronization有可能需要挂起
// suspendedResources表示当前线程被挂起的资源持有对象(数据库连接、TransactionSynchronization)
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
// 开启事务后,transaction中就会有数据库连接了,并且isTransactionActive为true
// 并返回TransactionStatus对象,该对象保存了很多信息,包括被挂起的资源
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
那么第一步,检查@Transactonal中配置的属性信息,一般来说都是PROPAGATION_REQUIRED
那么下面将会来开启事务startTransaction:
/**
* Start a new transaction.
*/
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
// 是否开启一个新的TransactionSynchronization
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 开启的这个事务的状态信息:
// 事务的定义、用来保存数据库连接的对象、是否是新事务,是否是新的TransactionSynchronization
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 开启事务
doBegin(transaction, definition);
// 如果需要新开一个TransactionSynchronization,就把新创建的事务的一些状态信息设置到TransactionSynchronizationManager中
prepareSynchronization(status, definition);
return status;
}
首先开启事务之前,保存一下当前要开启的事务的状态信息,将新否是新事务、新的同步器和当前事务的配置信息存入进去。创建一个对象之后开启事务。看下DataSourceTransactionManager中的doBegin方法:
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 如果当前线程中所使用的DataSource还没有创建过数据库连接,就获取一个新的数据库连接
// 如果创建过并且事务的传播机制是以上几种
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
// 这里才会将真正的连接设置进来,并且设置true,表示是新建的数据库连接
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 当前数据库连接同步器是新建的(synchronizedWithTransaction=true)
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
// 根据@Transactional注解中的设置,设置Connection的readOnly与隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// 设置autocommit为false
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
// 表示的当前数据库连接已经开启了事务
txObject.getConnectionHolder().setTransactionActive(true);
// 设置数据库连接的过期时间
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
// 把新建的数据库连接设置到resources中,resources就是一个ThreadLocal<Map<Object, Object>>,事务管理器中的设 // 置的DataSource对象为key,数据库连接对象为value,对应的是前面的ThreadLocal
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
}
这里是将当前是否是新建的连接存入到数据库连接中来。然后会将事务状态信息存入起来,来到下面的代码:
prepareSynchronization(status, definition);
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
// 当前事务是否获取
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
// 当前事务隔离级别
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
// 当前事务是否只读
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
// 当前事务的名字(默认为方法名称)
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
// 当前同步器初始化
TransactionSynchronizationManager.initSynchronization();
}
}
也就是说将当前的属性状态信息,都存入到ThreadLocal中去。那么对于一个事务来说,可能不止这些信息,比如说当前事务的超时时间、数据库都需要另外存放。
所以还需要来进行封装
// 返回一个TransactionInfo对象,表示得到了一个事务,可能是新创建的一个事务,也可能是拿到的已有的事务
prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
那么这里才是一个事务的完整信息
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
// The transaction manager will flag an error if an incompatible tx already exists.
txInfo.newTransactionStatus(status);
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No need to create transaction for [" + joinpointIdentification +
"]: This method is not transactional.");
}
}
// 把txInfo设置到ThreadLocal中
txInfo.bindToThread();
return txInfo;
}
上面做的是将一个事务封装起来,绑定到线程上去。如果事务发生了回滚或者其他情况,那么需要来进行恢复。
4、代码演示
首先搭建环境:
4.1、依赖
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!--druid连接池-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.11</version>
</dependency>
<!--Spring的jdbc支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.10</version>
</dependency>
</dependencies>
4.2、配置类
因为通过配置的方式来进行操作的,所以使用对应的配置类即可:
@Configuration
@ComponentScan(basePackages = "com.guang.spring.transaction")
// 加入注解!!!!!
@EnableTransactionManagement
public class MyConfig {
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setUrl("jdbc:mysql://localhost:3306/jdbc");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(){
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
@Bean
public PlatformTransactionManager dataSourceTransactionManager(){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource());
return dataSourceTransactionManager;
}
}
4.3、启动类
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
UserService userService = context.getBean(UserService.class);
userService.inert();
}
}
下面将会分别结合不同的情况来进行分析
代码演示中,还是看着下面这张图来进行分析:
4.1、只有一个@Transactional
4.1.1、正常情况
@Service
public class UserService0 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void inert() {
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
}
}
正常情况就是:
- 1、创建数据库连接,设置autocommit为false,开启事务
- 2、执行目标方法;
- 3、事务提交,归还数据库连接池前设置autocommit为ture
但是事实情况比这更加复杂,那么画出对应的流程图来进行分析:
4.1.2、异常情况
4.2、两个@Transactional
4.2.1、正常情况
@Service
public class UserService1 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private UserService1 userService;
@Transactional
public void inert() {
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
userService.update();
}
@Transactional
public void update() {
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
}
}
4.2.2、异常情况1
通常是第二个执行失败。因为如果是第一个执行失败的情况下,第二个方法就不会来执行。所以多数情况下是第二种情况
@Service
public class UserService2 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private UserService2 userService;
@Transactional
public void inert() {
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
try {
userService.update();
} catch (Exception e) {
System.out.println("update执行失败");
}
}
/**
* newTransaction
*/
// @Transactional(propagation = Propagation.REQUIRES_NEW)
@Transactional
public void update() {
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
throw new NullPointerException();
}
}
4.2.3、异常情况2
@Service
public class UserService3 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private UserService3 userService;
@Transactional
public void inert() {
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
userService.update();
try {
System.out.println("尽管SQL没有问题,但是这里依然可以让事务回滚");
throw new NullPointerException("事务回滚");
}catch (Exception e){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
System.out.println(e.getMessage());
}
}
/**
* savepointcut
*/
// @Transactional(propagation = Propagation.NESTED)
@Transactional
public void update() {
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
throw new NullPointerException();
}
}
4.2.4、异常情况3
@Service
public class UserService4 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private UserService4 userService;
/**
* self control
*/
@Transactional(rollbackFor = {Throwable.class},noRollbackFor = {RuntimeException.class})
public void inert() {
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
}
/**
* savepointcut
*/
@Transactional(propagation = Propagation.NESTED)
public void update() {
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
throw new NullPointerException();
}
}
这个是不使用当前的数据库连接,而是使用mybatis或者是jdbcTemplate自带的。
4.2.5、异常情况4
@Service
public class UserService5 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private UserService5 userService;
@Transactional
public void inert() {
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
try {
userService.update();
} catch (Exception e) {
// e.printStackTrace();
System.out.println("start here");
}
}
/**
* newTransaction
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void update() {
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
throw new NullPointerException();
}
}
提交一个,回滚一个。