首页>>后端>>Spring->关于Spring事务的一些理论和实践

关于Spring事务的一些理论和实践

时间:2023-11-30 本站 点击:0

一、基础知识

1、Transational注释

特性

可以在类(接口)或方法上标注

标注在类上:类中所有方法都进行事务处理

标注在接口、实现类的方法上:方法进行事务处理

优先级:方法注解>类注解

属性

progation:事务传播机制

事务传播机制特性REQUIRED如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务SUPPORTS如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。MANDATORY如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常REQUIRES_NEW总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起NOT_SUPPORTED总是非事务地执行,并挂起任何存在的事务NEVER总是非事务地执行,如果存在一个活动事务,则抛出异常NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行

isolation:事务隔离级别

DEFAULT使用后端数据库默认的隔离级别

其他四个隔离级别对应着数据库的四个隔离

timeout:事务超时时间,单位是秒。是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。默认值是当前数据库默认事务过期时间。

readOnly:事务是否是只读的,默认是 false。对于只读查询,可以指定事务类型为 readonly,即只读事务。由于只读事务不存在数据的修改, 因此数据库将会为只读事务提供一些优化手段

rollbackFor:设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则事务回滚

noRollbackFor:设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚

rollbackForClassName:设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。

noRollbackForClassName:设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。

使用@Transactional需要注意的地方

@Transactional 只能应用到 public 方法才有效

在默认配置中,Spring FrameWork 的事务框架代码只会将出现 runtime, unchecked 异常的事务标记为回滚;也就是说事务中抛出的异常是 RuntimeException 或其子类,这样事务才会回滚(默认情况下 Error 也会导致事务回滚)。但是,在默认配置的情况下,所有的 checked 异常都不会引起事务回滚。

二、实践

前期准备

1、创建spring项目

新建Spring Initializr项目

配置pom.xml文件

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.12</version><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

创建entity包,创建对应数据库表的实体类

@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name="extra_ad")publicclassExtraAd{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="id",nullable=false)privateLongid;@Basic@Column(name="name",nullable=false)privateStringname;publicExtraAd(Stringname){this.name=name;}}

创建dao包,创建操作数据库表的类的接口

publicinterfaceExtraAdDaoextendsJpaRepository<ExtraAd,Long>{}

创建exception包,自定义异常类

publicclassCustomExceptionextendsException{publicCustomException(Stringmessage){super(message);}}

创建service包,定义服务接口

publicinterfaceISpringTransaction{//主动捕获异常,事务不能回滚voidCatchExceptionCanNotRollback();//不是unchecked异常,事务不能回滚voidNotRuntimeExceptionCanNotRollback()throwsCustomException;//uncheck异常,事务可以回滚voidRuntimeExceptionCanRollback();//指定异常,事务可以回滚voidAssignExceptionCanRollback()throwsCustomException;//RollbackOnly,事务可以回滚voidRollbackOnlyCanRollback()throwsCustomException;//同一个类中,一个不标注事务的方法区调用了标注了事务的方法,事务会失效voidNonTransactionalCanNotRollback();}

在service包下创建impl包,实现ISpringTransaction接口

在test下新建测试类

整体项目结构如图:

2、创建测试数据库

CREATEDATABASEIFNOTEXISTS`example`;CREATETABLEIFNOTEXISTS`example`.`extra_ad`(`id`bigint(20)NOTNULLAUTO_INCREMENTCOMMENT'自增主键',`name`varchar(48)NOTNULLCOMMENT'名称',PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=10DEFAULTCHARSET=utf8COMMENT='额外的表';TRUNCATETABLEextra_ad;

对应的application.yml

spring:profiles:active:devjpa:open-in-view:falsedatasource:url:jdbc:mysql://127.0.0.1:3306/example?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=falseusername:****password:****driver-class-name:com.mysql.cj.jdbc.Drivertomcat:max-active:4min-idle:2initial-size:2---spring:profiles:devserver:port:8080---spring:profiles:testserver:port:8081---spring:profiles:prodserver:port:8082

几种实践情况

1、主动捕获异常,事务不能回滚

@Override@TransactionalpublicvoidCatchExceptionCanNotRollback(){try{extraAdDao.save(newExtraAd("test1"));thrownewRuntimeException();}catch(Exceptionex){ex.printStackTrace();}}

执行前,表中无记录

执行方法

@TestpublicvoidNotRuntimeExceptionCanNotRollback()throwsCustomException{springTransaction.NotRuntimeExceptionCanNotRollback();}

并没有回滚,数据插入成功

清空数据库,继续下一个

2、不是unchecked异常,事务不能回滚

@Override@TransactionalpublicvoidNotRuntimeExceptionCanNotRollback()throwsCustomException{try{extraAdDao.save(newExtraAd("test2"));thrownewRuntimeException();}catch(Exceptionex){thrownewCustomException(ex.getMessage());}}

这里强制把RumtimeException转为CustomException,就不是unchecked异常了

执行方法

没有回滚,成功插入

清空数据库,继续下一条

3、uncheck异常,事务可以回滚

@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name="extra_ad")publicclassExtraAd{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="id",nullable=false)privateLongid;@Basic@Column(name="name",nullable=false)privateStringname;publicExtraAd(Stringname){this.name=name;}}0

执行方法

RuntimeException是uncheck异常,发生回滚,数据并没有插入成功

4、指定异常,事务可以回滚

@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name="extra_ad")publicclassExtraAd{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="id",nullable=false)privateLongid;@Basic@Column(name="name",nullable=false)privateStringname;publicExtraAd(Stringname){this.name=name;}}1

执行方法

参数rollbackFor指定了CustomException异常类型,并且抛出了该异常,事务回滚,数据插入失败

5、Rollback Only,事务处理失败

@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name="extra_ad")publicclassExtraAd{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="id",nullable=false)privateLongid;@Basic@Column(name="name",nullable=false)privateStringname;publicExtraAd(Stringname){this.name=name;}}2

运行方法

抛出异常

@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name="extra_ad")publicclassExtraAd{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="id",nullable=false)privateLongid;@Basic@Column(name="name",nullable=false)privateStringname;publicExtraAd(Stringname){this.name=name;}}3

由于字段name要求不为空,extraAdDao.save(new ExtraAd())运行出错

@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name="extra_ad")publicclassExtraAd{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="id",nullable=false)privateLongid;@Basic@Column(name="name",nullable=false)privateStringname;publicExtraAd(Stringname){this.name=name;}}4

本方法中调用了标记为事务的方法oneSaveMethod(),多个事务合为一个事务,只有全部正确执行完成才会提交事务,由于extraAdDao.save(new ExtraAd())运行出错,事务被标记为rollback-only,导致事务回滚,数据插入失败。

6、同一个类中,一个不标注事务的方法调用了标注了事务的方法,事务会失效

@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name="extra_ad")publicclassExtraAd{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="id",nullable=false)privateLongid;@Basic@Column(name="name",nullable=false)privateStringname;publicExtraAd(Stringname){this.name=name;}}5

可以看到,事务并没有回滚,数据成功插入

运行中抛出的异常

@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name="extra_ad")publicclassExtraAd{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="id",nullable=false)privateLongid;@Basic@Column(name="name",nullable=false)privateStringname;publicExtraAd(Stringname){this.name=name;}}6

从抛出的异常可以看到,首先,我们拿到的是代理对象,调用 NonTransactionalCanNotRollback 方法。但是 NonTransactionalCanNotRollback 方法在调用 anotherOneSaveMethod 的时候却是原始对象的 anotherOneSaveMethod。所以,这里的调用根本就没有事务的存在,导致事务失效,就更加不存在回滚了.

7、不同类中,一个不标注事务的方法调用了标注了事务的方法,事务生效

可以看到,由于异常是uncheck类型,事务发生了回滚,数据并没有插入成功。

@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name="extra_ad")publicclassExtraAd{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="id",nullable=false)privateLongid;@Basic@Column(name="name",nullable=false)privateStringname;publicExtraAd(Stringname){this.name=name;}}7

从抛出的异常可以看到,首先,我们拿到的是代理对象,再去调用anotherOneSaveMethod。所以,这就有事务了,即事务不会失效。



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Spring/4436.html