Spring事务

Spring事务

1.简介

Spring提供两种事务管理方式,分为编程式和声明式。

  • 编程式:通过编码的方式手动启用、提交或回滚事务,粒度更细,但更麻烦。
  • 声明式:通过在方法或类或接口上添加注解进行包装,无侵入地实现事务,更方便,但粒度更大。

需要注意的是,使用的数据库需要支持事务,否则事务将不起作用。如MySql的MyIsam引擎就不支持事务。

2.实例

业务代码

在下面的实例中,我要先创建申请,再创建申请处理。如果创建申请处理失败,应该回滚。这里是没加事务的。为了演示,我们先注掉第7行,模拟出错。

@Override
  public boolean saveTransaction(CertApply certApply) {
    certApply.setState(1); //初审中
    save(certApply);
    ApplyDeal applyDeal = new ApplyDeal();
    applyDeal.setState(1); //初审
    applyDeal.setApplyId(certApply.getId());
    applyDealService.save(applyDeal);
    return true;
  }
运行测试

运行截图
显然出现了我们要看到的问题
接下来开始使用事务进行管理

3.解决问题

依赖(Spring)
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-tx</artifactId>
		<version>${spring.version}</version>
	</dependency>
开启事务管理

@SpringBootApplication
@EnableAspectJAutoProxy
@EnableTransactionManagement
@MapperScan("com.example.course_system.mapper")
public class CourseSystemApplication {

  public static void main(String[] args) {
    SpringApplication.run(CourseSystemApplication.class, args);
  }

}
在方法上加上@Transaction注解
 @Override
  @Transactional
  public boolean saveTransaction(CertApply certApply) {
    certApply.setState(1); //初审中
    save(certApply);
    ApplyDeal applyDeal = new ApplyDeal();
    applyDeal.setState(1); //初审
//    applyDeal.setApplyId(certApply.getId());
    applyDealService.save(applyDeal);
    return true;
  }
测试

在这里插入图片描述

没有新的记录加入。结果正确!

4.SpringBoot实现声明式事务的几种方式

  1. 基于动态代理支持@Transaction。

    就是上面的那种方法。但这种方法是有局限性的。在有些情况下会失效。

    • 同一个类中,方法A实现了事务,方法B中没实现事务,B中调用A。事务失效(没有经过代理类,俗称自调用问题)
    • 数据库引擎不支持事务
    • 没有被 Spring 管理
    • 方法不是 public 的
    • 异常被吃了 (高频)异常类型错误(一定要抛出RuntimeException,或者配置识别的事务类型)
  2. 基于AspectJ编译期织入来支持@Transactional

    • 用来解决上面的方法不是public的时候动态代理失效的问题

5.基于AspectJ编译期织入来支持@Transactional

我们可以采用“编译期织入”或“类加载期织入”的方式,在运行代码前,将我们的目标类的方法增强,无需管用“动态代理”实现时的种种限制。本文就接下来就讲下,如何使用AspectJ来实现在编译期对@Transactional注解的方法进行织入。

5.1.导包
   <plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>aspectj-maven-plugin</artifactId>
				<version>1.11</version>
				<configuration>
					<aspectLibraries>
						<aspectLibrary>
							<groupId>org.springframework</groupId>
							<artifactId>spring-aspects</artifactId>
						</aspectLibrary>
					</aspectLibraries>
					<complianceLevel>1.8</complianceLevel>
					<source>1.8</source>
					<target>1.8</target>
					<showWeaveInfo>true</showWeaveInfo>
				</configuration>
				<executions>
					<execution>
						<goals>
							<goal>compile</goal>
						</goals>
					</execution>
				</executions>
                </plugin>

5.2.配置

将@EnableTransactionManagement中的mode设置为AdviceMode.ASPECTJ(默认为AdviceMode.PROXY,也就是我们的动态代理~)

然后就可以啦。这里就不测试,用兴趣的同学可以去实验下。

6.一些补充知识

6.1Transaction注解的源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	/**
	 * Alias for {@link #transactionManager}.
	 * @see #transactionManager
	 */
	@AliasFor("transactionManager")
	String value() default "";

	/**
	 * 用于指定事务管理器。
	 */
	@AliasFor("value")
	String transactionManager() default "";

	/**
	 * 事务传播级别
	* 可选的值有:
	* Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
	* 可以理解为和外部事物融为一体,共存亡。即使外层事务trycatch了内层事务的异常,两个事务还是一起回滚
	* Propagation.SUPPORTS如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
	* 简单理解:有事务就加入,没有就算了。
	* Propagation.MANDATORY如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
	* 简单理解:需要一个有事务的大哥来带他走,他自己就报错。很符合mandatory的意思 托管
	* Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,挂起当前的事务。
	* 简单理解:外层出事不影响该事务。内层出事了,异常会抛给外层
	* Propagation.NOT_SUPPORTED以非事务的方式运行,如果当前存在事务,暂停当前的事务。
	* 简单理解:机动车道上的非机动车,自己出问题不会回滚,但会害和他一道的事务回滚
	* Propagation.NEVER以非事务的方式运行,如果当前存在事务,则抛出异常。
	* Propagation.NESTED和 Propagation.REQUIRED 有细微差别。如果当前存在事务,则作为该事务的子事务,如果当前不存在事	 * 务,则创建一个新的事务。外层事务trycatch了内层事务的异常,只有内存事务会回滚
	 */
	Propagation propagation() default Propagation.REQUIRED;

	/**
	 * 事务隔离级别
	 * Isolation.DEFAULT:使用底层数据库默认的隔离级别。
	* Isolation.READ_UNCOMMITTED
	* Isolation.READ_COMMITTED
	* Isolation.REPEATABLE_READ
	* Isolation.SERIALIZABLE
	 */
	Isolation isolation() default Isolation.DEFAULT;

	/**
	 * 事务超时时间。超过时间没完成,自动回滚。默认-1
	 */
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	/**
	 * 是否为只读事务。可以帮助事务优化。只是一个暗示,对最终事务的结果没有影响。
	 */
	boolean readOnly() default false;

	/**
	 * 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
	 */
	Class<? extends Throwable>[] rollbackFor() default {};

	/**
	 * 如上
	 */
	String[] rollbackForClassName() default {};

	/**
	* 不回滚的异常类型
	 */
	Class<? extends Throwable>[] noRollbackFor() default {};

	/**
	 * 如上
	 */
	String[] noRollbackForClassName() default {};

}

参考:事务传播机制 https://blog.csdn.net/qq_35493807/article/details/105756761

/**
* 不回滚的异常类型
 */
Class<? extends Throwable>[] noRollbackFor() default {};

/**
 * 如上
 */
String[] noRollbackForClassName() default {};

}


参考:事务传播机制 https://blog.csdn.net/qq_35493807/article/details/105756761

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值