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();
    }

}

提交一个,回滚一个。

posted @ 2022-06-29 01:41  雩娄的木子  阅读(84)  评论(0编辑  收藏  举报