1.无事务案例
1.1. 成功添加数据
在DemoController下添加方法insertSucessTest 用于成功添加测试数据
@ResponseBody
@PostMapping(value = "insertSucessTest")
public String insertSucessTest(@RequestBody DemoEntity demoEntity){
return demoService.insertSucessTest(demoEntity);
}
Service
String insertSucessTest(DemoEntity demoEntity);
ServiceImpl
@Override
public String insertSucessTest(DemoEntity demoEntity) {
demoMapper.insert(demoEntity);
return "success";
}
1.2. 异常添加数据
在DemoController下添加方法insertNoTransactional 用于异常添加测试数据
@ResponseBody
@PostMapping(value = "/insertNoTransactional")
public String insertNoTransactional(@RequestBody DemoEntity demoEntity) throws Exception {
return demoService.insertNoTransactional(demoEntity);
}
Service
public interface DemoService {
String insertNoTransactional(DemoEntity demoEntity) throws Exception;
}
impl
public String insertNoTransactional(DemoEntity demoEntity) throws Exception {
demoMapper.insertSelective(demoEntity);
throw new Exception("测试事务回滚");
}
我们通过postman进行接口测试下,在测试之前我们先清空下数据库信息方便可以准确的验证
先正常的添加一条测试数据
添加一条异常测试数据
2.事务是什么
通过以上案例我们不难发现,当我们的程序存在异常的时候所存放到数据库的信息并没有被回滚,依旧存在数据库中这样就会造成数据库中存在很多脏数据,为了避免这种情况的发生,为此我们需要在程序异常的时候把已存入到数据库的内容给擦出掉 保证我们数据的干净和准确,为此我们就需要用到"事务"这个神奇的魔法工具
事务是一种用于管理数据库操作的机制,通常用于确保一组数据库操作要么都成功执行,要么都不执行。事务的目的是维护数据的一致性和完整性,同时提供隔离性和持久性。
3.事务的特性(ACID)
属性 | 描述 |
---|---|
原子性(Atomicity) | 事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。 |
一致性(Consistency) | 在事务开始之前和事务结束以后,数据库的完整性没有被破坏 |
事务隔离(Isolation) | 数据库允许多个并发事务同时对其数据进行读写和修改,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致 |
持久性(Durability) | 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。 |
4.事务的传播机制
//事务传播机制枚举类摘要
public enum Propagation {
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
NEVER(TransactionDefinition.PROPAGATION_NEVER),
NESTED(TransactionDefinition.PROPAGATION_NESTED);
}
属性 | 描述 |
---|---|
REQUIRED(默认) | 支持当前事务,如果不存在则创建一个新事务 |
SUPPORTS | 支持当前事务,如果不存在,则以非事务方式执行方法调用 |
MANDATORY | 强制必须有事务,如果没有事务就抛出异常 |
REQUIRES_NEW | 每次执行都创建一个新的事务,不影响调用其他有事务方法的异常而导致回滚 |
NOT_SUPPORTED | 以非事务方式执行,如果存在,则挂起当前事务 |
NEVER | 以非事务方式执行,如果存在事务则抛出异常 |
NESTED | 如果存在当前事务,则在嵌套事务中执行, 类似REQUIRED,这仅适用于处理JDBC 3.0驱动程序时的JDBC DataSourceTransactionManager |
5.事务的隔离级别
//事务隔离级别枚举类摘要
public enum Isolation {
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
}
属性 | 描述 |
---|---|
DEFAULT(默认的) | 使用底层数据存储的默认隔离级别。所有其他级别都对应JDBC隔离级别 |
READ_UNCOMMITTED (读未提交) | 可能发生脏读取、不可重复读取和幻读。此级别允许由一个事务更改的行在提交该行中的任何更改之前由另一个事务读取(“脏读取”)。如果回滚任何更改,则第二个事务将检索到无效行.例如A事务修改记录但没提交,B事务可读取到修改后的值此时可导致脏读、不可重复读、幻读。 |
READ_COMMITTED (读已提交) | 可以防止脏读;可能发生不可重复的读取和幻读。此级别仅禁止事务读取包含未提交更改的行 例如 A事务修改并提交后,B事务才能读取到修改后的值,此时阻止了脏读,但可能导致不可重复读和幻读。 |
REPEATABLE_READ (可重复读) | 指示防止脏读取和不可重复读取;可能会出现幻影读取。该级别禁止事务读取其中有未提交更改的行,还禁止一个事务读取一行,第二个事务更改该行,第一个事务重新读取该行,从而在第二次获得不同值(“不可重复读取”)的情况。例如 A事务读取了一条记录,B事务将不能修改这条记录,阻止脏读和不可重复读,但是可能出现幻读。 |
SERIALIZABLE(串行化) | 最严格的隔离级别,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用 Serializable 隔离级别 |
6.spring对事务的支持方式
spring对事务有2种支持方式 编程式和声明式
编程式代码量大,对代码入侵较深,可控粒度较强.可以根据自己业务按需执行
声明式代码量相对较少,不用自己在业务中实现事务控制,对代码入侵较少,但是粒度相对编程式较低只能控制到类或方法级别,无法进行细粒度控制
6.1.编程式事务
6.1.1. 什么是编程式事务
编程式事务管理是一种手动管理事务的方式,与声明式事务管理(使用注解或XML进行配置)相对应。在编程式事务管理中,开发人员需要显式地编写代码来启动、提交或回滚事务。
6.1.2. 编程式事务优点
编程式事务管理的优点是灵活性高,可以在代码中精确控制事务的边界和行为。但同时,也需要开发人员编写更多的代码,并且容易出现遗漏或错误。
6.1.3. 编程式事务管理的主要步骤
获取事务管理器 >> 开始事务 >> 执行事务操作 >> 提交事务或回滚事务
-
获取事务管理器:首先,需要获取一个事务管理器的实例,该实例负责管理事务的生命周期。
-
开始事务:在需要应用事务的方法或代码块中,通过事务管理器的方法,显式开始一个事务。
-
执行事务操作:在事务范围内,执行数据库操作或其他需要进行事务管理的操作。如果在执行过程中发生异常,可以选择手动回滚事务。
-
提交事务或回滚事务:根据执行结果,如果全部正常,可以手动提交事务。如果发生异常或出现错误情况,可以手动回滚事务,以保证数据库的一致性。
6.1.4. 常用的编程式事务管理API及其用法
编程式事务管理通常是通过事务管理器提供的API来实现的,需要在代码中手动管理事务的开启、提交和回滚等操作,下面举例几个常用的编程式事务管理API及其用法的介绍
-
PlatformTransactionManager:
-
开启事务:
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
-
提交事务:
transactionManager.commit(transactionStatus);
-
回滚事务:
transactionManager.rollback(transactionStatus);
-
-
TransactionStatus:
-
判断事务是否已完成:
transactionStatus.isCompleted();
-
判断事务是否为新事务:
transactionStatus.isNewTransaction();
-
判断事务是否已回滚:
transactionStatus.isRollbackOnly();
-
标记事务为只回滚:
transactionStatus.setRollbackOnly();
-
-
TransactionDefinition:
-
设置事务传播行为:
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
-
设置事务隔离级别:
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
-
设置事务超时时间(秒):
transactionDefinition.setTimeout(30);
-
6.1.5. 编程式事务实现的几种方式
6.1.5.1. 基于PlatformTransactionManager管理事务
6.1.5.1.1 案例描述
@Autowired
private PlatformTransactionManager transactionManager;
public void demo() throws Exception {
// 获取事务定义和状态
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 执行事务操作
// 操作1
// 操作2
// ...
// 提交事务
transactionManager.commit(transactionStatus);
} catch (Exception e) {
//出现异常,事务回滚
transactionManager.rollback(transactionStatus);
}
}
6.1.5.1.2. 案例改造
在DemoController下添加方法insertErrorManager 用于异常添加测试数据
@ResponseBody
@PostMapping(value = "insertErrorManager")
public String insertErrorManager(@RequestBody DemoEntity demoEntity){
return demoService.insertErrorManager(demoEntity);
}
Service
String insertErrorManager(DemoEntity demoEntity);
ServiceImpl
public String insertErrorManager(DemoEntity demoEntity) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus transactionStatus = transactionManager.getTransaction(def);
try {
//第一次添加
demoMapper.insertSelective(demoEntity);
//第二次添加会因为主键冲突而异常
demoMapper.insertSelective(demoEntity);
transactionManager.commit(transactionStatus);
} catch (Exception e) {
log.error("异常了 e:{},需进行事务回滚",e.getMessage());
transactionManager.rollback(transactionStatus);
return "fail";
}
log.info("一切正常");
return "success";
}
6.1.5.1.3 测试结果
6.1.5.2. 基于TransactionTemplate管理事务
6.1.5.2.1. 案例描述
@Autowired
private TransactionTemplate transactionTemplate;
public void demo() {
// 定义事务的属性
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 执行事务操作
transactionTemplate.execute(new TransactionCallback<Void>() {
@Override
public Void doInTransaction(TransactionStatus status) {
try {
// 执行事务的业务逻辑
// ...
// 如果发生异常,事务将自动回滚
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
return null;
}
});
}
6.1.5.2.2. 案例改造
在DemoController下添加方法insertErrorTemplate 用于异常添加测试数据
@ResponseBody
@PostMapping(value = "insertErrorTemplate")
public String insertErrorTemplate(@RequestBody DemoEntity demoEntity){
return demoService.insertErrorTemplate(demoEntity);
}
Service
String insertErrorTemplate(DemoEntity demoEntity);
ServiceImpl
public String insertErrorTemplate(DemoEntity demoEntity) {
// 定义事务的属性
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
try{
// 执行事务操作
transactionTemplate.execute(new TransactionCallback<Void>() {
@Override
public Void doInTransaction(TransactionStatus status) {
demoMapper.insertSelective(demoEntity);
demoMapper.insertSelective(demoEntity);
// 执行事务的业务逻辑
try {
throw new Exception("主动异常");
} catch (Exception e) {
throw new RuntimeException(e);
}
// ...
// 如果发生异常,事务将自动回滚
}
});
}catch (RuntimeException e){
return "fail";
}
return "success";
}
6.1.5.2.3. 测试结果
6.2.声明式事务
6.2.1. 什么是声明式事务
声明式事务管理是Spring框架提供的一种用于管理事务的机制,通过使用注解(如@Transactional
)或XML配置的方式,将事务的定义和管理与业务逻辑代码分离。
在声明式事务管理中,开发人员可以通过简单的注解或XML配置来定义事务的属性和行为,而不需要在业务逻辑代码中显式地编写事务管理的代码。Spring框架会通过AOP(面向切面编程)技术,动态地为带有事务注解或配置的方法创建代理对象,在方法执行前后自动应用事务的管理。
6.2.2. 声明式事务优点
-
分离关注点:将事务管理与业务逻辑代码分离,使业务逻辑代码更加清晰和简洁,只需关注业务本身而无需关心事务的具体实现。
-
降低耦合性:事务管理将从业务逻辑中解耦出来,使得业务逻辑代码可以独立于事务管理的具体实现。这样,在需要更改事务管理策略或切换到其他事务管理方式时,可以更加灵活地进行更改,而无需修改业务逻辑代码。
-
提高可维护性和可重用性:通过声明式事务管理,不同的方法可以共享相同的事务属性,提高了代码的可维护性和可重用性。一旦事务属性需要修改,只需在事务定义上进行调整,而不需要修改每个方法的代码。
-
减少重复性的事务管理代码:声明式事务管理通过自动应用事务管理逻辑,减少了编写重复性的事务管理代码的工作量,同时提高了代码的可读性和可维护性。
6.2.3. 声明式事务管理的步骤
-
配置事务管理器:需要配置一个合适的事务管理器,以与底层数据源(如数据库)进行交互。
-
声明事务属性:使用注解或XML配置方式,在需要应用事务管理的方法上添加相应的事务注解或配置,如
@Transactional
注解。 -
启用事务管理:在Spring配置文件中启用事务管理,可以使用
<tx:annotation-driven>
来启用注解驱动的事务管理。
通过以上步骤,声明式事务管理就可以生效了。当调用被注解或配置的方法时,Spring会拦截方法的调用,根据事务定义和配置的事务属性来管理事务的生命周期,如开始、提交或回滚事务。
6.2.4. 声明式事务的几种实现方式
6.2.4.1. @Transactional
@Transactional注解可以应用于类或方法上。当应用于类上时,注解表示该类的所有方法都将受到事务管理。当应用于方法上时,注解表示该方法将受到事务管理。
6.2.4.1.1. 注解的作用
-
启动事务:使用@Transactional注解的方法将自动启动事务。当方法被调用时,Spring将为该方法创建一个事务,并将其绑定到当前线程。
-
提交事务:如果方法执行成功并未发生任何异常,则事务将被提交,对数据库所做的更改将永久保存。
-
回滚事务:如果方法执行期间发生异常,则事务将被回滚,数据库中的更改将被撤销,以保持数据的一致性。
-
事务传播行为:@Transactional注解还可以指定事务的传播行为。例如,可以将一个方法标记为一个事务性方法,并且在调用另一个被@Transactional注解标记的方法时,可以选择是加入已存在的事务,还是创建一个新的事务。
-
异常处理:默认情况下,只有遇到未检查异常(RuntimeException及其子类)时,事务才会回滚。但可以利用@Transactional注解的rollbackFor和noRollbackFor属性来定义其他异常类型时是否回滚事务。
6.2.4.1.2. Transactional代码明细
public @interface Transactional {
//配置事务管理器 如果未配置则默认使用spring自带的
@AliasFor("transactionManager")
String value() default "";
//配置事务管理器 如果未配置则默认使用spring自带的
@AliasFor("value")
String transactionManager() default "";
//事务的传播机制
Propagation propagation() default Propagation.REQUIRED;
//事务的隔离级别
Isolation isolation() default Isolation.DEFAULT;
//事务过期时间,默认是当前数据库默认事务过期时间
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
//是否为只读事务,默认是false
boolean readOnly() default false;
//指定哪些异常可以导致事务回滚,默认是Throwable的子类
Class<? extends Throwable>[] rollbackFor() default {};
//指定哪些异常类型可以导致事务回滚,默认是Throwable的子类
String[] rollbackForClassName() default {};
//指定哪些异常不会执行事务回滚,默认是Throwable的子类
Class<? extends Throwable>[] noRollbackFor() default {};
//指定哪些异常不会执行事务回滚,默认是Throwable的子类
String[] noRollbackForClassName() default {};
}
6.2.4.1.3. 代码改造
在DemoController下添加方法insertErrorTransactional 用于异常添加测试数据
@ResponseBody
@PostMapping(value = "insertErrorTransactional")
public String insertErrorTransactional(@RequestBody DemoEntity demoEntity) throws Exception {
return demoService.insertErrorTransactional(demoEntity);
}
Service
String insertErrorTransactional(DemoEntity demoEntity) throws Exception;
ServiceImpl
@Override
@Transactional(rollbackFor = Exception.class)
public String insertErrorTransactional(DemoEntity demoEntity) throws Exception {
demoMapper.insertSelective(demoEntity);
throw new Exception("测试事务回滚");
}
6.2.4.1.4 测试结果
6.2.4.2. xml配置方式
在早期未采用springboot进行项目实现的时候,更多的基于springmvc的时候我们实现事务很多时候都是通过配置xml来实现的.我们再springboot中就不进行测试了
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务通知-->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<!--配置事务属性,即哪些方法要执行事务-->
<tx:attributes>
<!--配置回滚事务,需对数据进行回滚 -->
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<!-- 配置只读事务,无需对数据进行回滚-->
<tx:method name="get*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--配置事务切面-->
<aop:config>
<!--要执行的方法在哪个包-->
<aop:pointcut id="AdviceAop" expression="execution(* com.zhuhuo.modual.service.*.*(..))"/>
<!-- 配置为AdviceAop执行哪个事务通知 -->
<aop:advisor advice-ref="txadvice" pointcut-ref="AdviceAop"/>
</aop:config>
</beans>
6.2.4.3 拦截器方式(我们在烛火博客项目中所采用的方式)
我们还可以通过自己实现PlatformTransactionManager来进行事务处理,此处需要用到一个拦截器 TransactionInterceptor
6.2.4.3.1.TransactionInterceptor是什么
TransactionInterceptor 是基于spring aop的一种方式,用于使用公共Spring事务基础设施(PlatformTransactionManager)进行声明性事务管理。派生自TransactionAspectSupport类,该类包含与Spring底层事务API的集成。TransactionInterceptor只是按照正确的顺序调用相关的超类方法,如invokeWithinTransaction。TransactionInterceptors是线程安全的。
6.2.4.3.2.TransactionInterceptor简要源码分析
核心一:属性配置
通过查看源码可知,在事务拦截器中主要做了2件事情,设置用于驱动事务的默认事务管理器,设置事务方法名称为键的属性和事务属性
public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
setTransactionManager(ptm);
setTransactionAttributeSource(tas);
}
public void setTransactionManager(@Nullable PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
//允许按注册名称匹配属性的简单TransactionAttributeSource实现 说白了就是给事务属性做个处理注册啥的
public void setTransactionAttributes(Properties transactionAttributes) {
NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
tas.setProperties(transactionAttributes);
this.transactionAttributeSource = tas;
}
//设置属性
public void setProperties(Properties transactionAttributes) {
TransactionAttributeEditor tae = new TransactionAttributeEditor();
Enumeration<?> propNames = transactionAttributes.propertyNames();
while (propNames.hasMoreElements()) {
String methodName = (String) propNames.nextElement();
String value = transactionAttributes.getProperty(methodName);
tae.setAsText(value);
TransactionAttribute attr = (TransactionAttribute) tae.getValue();
addTransactionalMethod(methodName, attr);
}
}
public void addTransactionalMethod(String methodName, TransactionAttribute attr) {
if (logger.isDebugEnabled()) {
logger.debug("Adding transactional method [" + methodName + "] with attribute [" + attr + "]");
}
this.nameMap.put(methodName, attr);
}
所有的事务属性都存放在这里,前面的key就是我们要传递的必入insert,save等等,后面是对应的相关属性,是PROPAGATION_REQUIRED还是readOnly等等
private Map<String, TransactionAttribute> nameMap = new HashMap<>();
核心二 invokeWithinTransaction 我们在本章节就不对此方法的源码具体过程做过多的讲述,有兴趣的可以自己私下看下。
6.2.4.3.3.实现TransactionInterceptor
先引入依赖
<!-- 支持面向方面的编程即AOP,包括spring-aop和AspectJ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建TransactionalConfiguration并定义TransactionInterceptor注册bean为transactions
@Configuration
public class TransactionalConfiguration {
@Bean(name = "transactions")
public TransactionInterceptor transactionInterceptor(){
}
}
前面讲到我们在通过TransactionInterceptor实现事务的时候主要目的就是做2件事情,设置用于驱动事务的默认事务管理器和配置事务方法名称为键的属性和事务属性,因此我们需要先定义一下默认事务管理器,并在TransactionInterceptor定义TransactionInterceptor方法并设置事务管理器
@Configuration
public class TransactionalConfiguration {
@Bean(name = "transactions")
public TransactionInterceptor transactionInterceptor(PlatformTransactionManager platformTransactionManager){
//定义事物拦截器
TransactionInterceptor interceptor = new TransactionInterceptor();
//设置事物管理器
interceptor.setTransactionManager(platformTransactionManager);
}
}
之后我们需要对我们所要定义的事务进行相关属性设置,通过前面所介绍的事务的传播机制可知默认的事务传播机制是PROPAGATION_REQUIRED,因此我们需要先定义下PROPAGATION_REQUIRED的类型
/**
* 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
* 这是最常见的选择 适用于save、update、delete等操作
*/
private static final String PROPAGATION_REQUIRED = "PROPAGATION_REQUIRED";
之后我们要对哪些方法给PROPAGATION_REQUIRED属性进行定义,如果不满足可以自行添加即可
/**
* 需要加入事物的规则,可以把需要添加事务的方法自行补充
*/
private static final String[] REQUIRED_RULE_TRANSACTION = {"create*", "insert*", "save*", "add*", "update*", "modify*", "change*", "del*", "edit*"};
以上是需要进行事务回滚的属性以及方法定义.那对于只读的属性同理我们也需要进行定义一下
对于只读事务我们要在PROPAGATION_REQUIRED基础上添加一个readOnly即可.
/**
* 只读 不需要加入事物的元素
*/
private static final String PROPAGATION_REQUIRED_READ = "PROPAGATION_REQUIRED,readOnly";
定义下不需要进行数据回滚的方法
/**
* 不需要加入事物的规则,即只读
*/
private static final String[] READ_RULE_TRANSACTION = {"select*", "get*", "count*", "find*"};
我们在定义完相关属性对照关系后我们接下来就需要把这些属性信息进行解析处理.在前面我们又知道TransactionInterceptor除了设置事务管理器外还对需要对事务的属性进行设置,因此我们需要对Properties进行赋值
@Bean(name = "transactions")
public TransactionInterceptor transactionInterceptor(PlatformTransactionManager platformTransactionManager){
//定义事物拦截器
TransactionInterceptor interceptor = new TransactionInterceptor();
//设置事物管理器
interceptor.setTransactionManager(platformTransactionManager);
//定义属性信息
Properties properties = new Properties();
//遍历需要加入事物的属性信息
for (String key : REQUIRED_RULE_TRANSACTION) {
properties.setProperty(key, PROPAGATION_REQUIRED);
}
//遍历不需要加入事物的属性
for (String key : READ_RULE_TRANSACTION) {
properties.setProperty(key, PROPAGATION_REQUIRED_READ);
}
//设置事务属性
interceptor.setTransactionAttributes(properties);
}
对事物管理器和事务属性都配置好后我们就可以直接返回我们所定义的拦截器了
return interceptor;
那上面是一些基础配置信息,如果我们在启动的时候怎么验证我们所定义的事务拦截器是否正常的被spring加载呢,我们可以通过日志的方式去打印出来,并在出现问题的时候把异常给打印出来,为此我们的改造如下
@Slf4j
@Configuration
public class TransactionalConfiguration {
/**
* 引入日志
*/
private static Logger logger = LoggerFactory.getLogger(TransactionalConfiguration.class);
/**
* 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择 适用于save、update、delete等操作
*/
private static final String PROPAGATION_REQUIRED = "PROPAGATION_REQUIRED";
/**
* 只读 不需要加入事物的元素
*/
private static final String PROPAGATION_REQUIRED_READ = "PROPAGATION_REQUIRED,readOnly";
/**
* 需要加入事物的规则,可以把需要添加事务的方法自行补充
*/
private static final String[] REQUIRED_RULE_TRANSACTION = {"create*", "insert*", "save*", "add*", "update*", "modify*", "change*", "del*", "edit*"};
/**
* 不需要加入事物的规则,即只读
*/
private static final String[] READ_RULE_TRANSACTION = {"select*", "get*", "count*", "find*"};
/**
* 事物拦截器
*
* @param platformTransactionManager 自动注入 无需手动
* @return
*/
@Bean(name = "transactions")
public TransactionInterceptor transactionInterceptor(PlatformTransactionManager platformTransactionManager) throws Exception {
try {
log.info(" ********** 配置事物拦截器 开始 **********");
//定义事物拦截器
TransactionInterceptor interceptor = new TransactionInterceptor();
//定义属性信息
Properties properties = new Properties();
//遍历需要加入事物的属性信息
for (String key : REQUIRED_RULE_TRANSACTION) {
properties.setProperty(key, PROPAGATION_REQUIRED);
}
//遍历不需要加入事物的属性
for (String key : READ_RULE_TRANSACTION) {
properties.setProperty(key, PROPAGATION_REQUIRED_READ);
}
//设置事物管理器
interceptor.setTransactionManager(platformTransactionManager);
//设置事务属性
interceptor.setTransactionAttributes(properties);
log.info(" ********** 配置事物拦截器 成功 **********");
//返回拦截器信息
return interceptor;
} catch (Exception e) {
log.error(" ********** 配置事物拦截器 失败 **********e:{}",e);
throw new Exception();
}
}
}
我们光配置拦截器但是并没有说明我们在哪里去监听,因此我们还需要通过 BeanNameAutoProxyCreator 来创建事务代理
/**
* 配置声明式事务 利用BeanNameAutoProxyCreator自动创建事务代理
* setBeanNames 监听serviceImpl 如果需要监听多处存在事物则用","进行分割
*/
@Bean
public BeanNameAutoProxyCreator getBeanNameAutoProxyCreator() {
BeanNameAutoProxyCreator proxyCreator = new BeanNameAutoProxyCreator();
// 此属性指定目标类本省是否是代理的对象,如果目标类没有实现任何类,就设为true代表自己
proxyCreator.setProxyTargetClass(true);
proxyCreator.setBeanNames("*Service");
proxyCreator.setInterceptorNames("transactions");
return proxyCreator;
}
完整配置如下
@Slf4j
@Configuration
public class TransactionalConfiguration {
/**
* 引入日志
*/
private static Logger logger = LoggerFactory.getLogger(TransactionalConfiguration.class);
/**
* 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择 适用于save、update、delete等操作
*/
private static final String PROPAGATION_REQUIRED = "PROPAGATION_REQUIRED";
/**
* 只读 不需要加入事物的元素
*/
private static final String PROPAGATION_REQUIRED_READ = "PROPAGATION_REQUIRED,readOnly";
/**
* 需要加入事物的规则,可以把需要添加事务的方法自行补充
*/
private static final String[] REQUIRED_RULE_TRANSACTION = {"create*", "insert*", "save*", "add*", "update*", "modify*", "change*", "del*", "edit*"};
/**
* 不需要加入事物的规则,即只读
*/
private static final String[] READ_RULE_TRANSACTION = {"select*", "get*", "count*", "find*"};
/**
* 事物拦截器
*
* @param platformTransactionManager 自动注入 无需手动
* @return
*/
@Bean(name = "transactions")
public TransactionInterceptor transactionInterceptor(PlatformTransactionManager platformTransactionManager) throws Exception {
try {
log.info(" ***************************** 配置事物拦截器 开始 *****************************");
//定义事物拦截器
TransactionInterceptor interceptor = new TransactionInterceptor();
//定义属性信息
Properties properties = new Properties();
//遍历需要加入事物的属性信息
for (String key : REQUIRED_RULE_TRANSACTION) {
properties.setProperty(key, PROPAGATION_REQUIRED);
}
//遍历不需要加入事物的属性
for (String key : READ_RULE_TRANSACTION) {
properties.setProperty(key, PROPAGATION_REQUIRED_READ);
}
//设置事物管理器
interceptor.setTransactionManager(platformTransactionManager);
//设置事务属性
interceptor.setTransactionAttributes(properties);
log.info(" ***************************** 配置事物拦截器 成功 *****************************");
//返回拦截器信息
return interceptor;
} catch (Exception e) {
log.error("***************************** 配置事物拦截器 失败 ***************************** e:{}", e);
throw new Exception();
}
}
/**
* 配置声明式事务 利用BeanNameAutoProxyCreator自动创建事务代理
* setBeanNames 监听serviceImpl 如果需要监听多处存在事物则用","进行分割
*
* @return
*/
@Bean
public BeanNameAutoProxyCreator getBeanNameAutoProxyCreator() {
BeanNameAutoProxyCreator proxyCreator = new BeanNameAutoProxyCreator();
// 此属性指定目标类本省是否是代理的对象,如果目标类没有实现任何类,就设为true代表自己
proxyCreator.setProxyTargetClass(true);
proxyCreator.setBeanNames("*Service");
proxyCreator.setInterceptorNames("transactions");
return proxyCreator;
}
}
6.2.4.3.4. 代码改造
在DemoController下添加方法insertErrorInterceptor 用于异常添加测试数据
@ResponseBody
@PostMapping(value = "insertErrorInterceptor")
public String insertErrorInterceptor(@RequestBody DemoEntity demoEntity) throws Exception {
return demoService.insertErrorInterceptor(demoEntity);
}
Service
String insertErrorInterceptor(DemoEntity demoEntity) throws Exception;
ServiceImpl
@Override
public String insertErrorInterceptor(DemoEntity demoEntity) throws Exception {
demoMapper.insertSelective(demoEntity);
throw new Exception("测试拦截器事务回滚");
}
6.2.4.3.4. 测试结果
7.总结
在本章节我们简单的介绍了下事务的相关信息,例如事务的隔离级别,事务的传播机制,事务的实现方式(编程式、声明式[注解,xml,自定义实现拦截器]),自定义通过拦截器方式实现声明式事务等等,在工作中我们对于编程式事务管理还是声明式事务管理取决于具体的应用场景和开发需求。对于复杂的业务逻辑或需要更细粒度控制的情况,编程式事务管理可能更合适。而对于简单的事务场景或希望减少开发工作量的情况,声明式事务管理可能更为方便。实际上,许多项目中也可能同时使用两种方式的混合模式,根据具体需求选择最合适的方式。