一、简介
Spring Boot 提供声明式的事务管理机制,使用@Transactional注解。
Spring Boot 也提供编程式事务管理机制。
Spring Boot 使用事务非常简单,首先在启动类使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可。如果注解在类上,则整个类的所有方法都默认支持事务。
事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。
Spring内置事务管理器:
事务管理器 | 使用场景 |
---|---|
DataSourceTransactionManager | 数据源事务管理器,提供对单个javax.sql.DataSource 事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理 |
JdoTransactionManager | 提供对单个javax.jdo.PersistenceManagerFactor y事务管理,用于集成JDO框架时的事务管理 |
JpaTransactionManager | 提供对单个javax.persistence.EntityManagerFactory 事务支持,用于集成JPA实现框架时的事务管理 |
HibernateTransactionManager | 提供对单个org.hibernate.SessionFactory 事务支持,用于集成Hibernate框架时的事务管理;该事务管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate 3.2+版本 |
JtaTransactionManager | 提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器 |
二、@Transactional注解的参数介绍:
属性名 | 说明 |
value | 指定使用哪个事务管理器 如果Spring容器中存在多个 PlatformTransactionManager事务管理器 实例, 并且没有实现接口 TransactionManagementConfigurer 指定默认值, 则在方法上使用注解 @Transactional 的时候,就必须要用value指定,如果不指定,则会抛出异常。 示例: @Transactional(value="txManager1") |
propagation | 事务传播行为1.REQUIRED:支持事务。如果业务方法执行时已经在一个事务中,则加入当前事务,否则重新开启一个事务。默认事务传播行为。
内层事务结束,等着外层一起提交。 外层回滚,内层也回滚。 内层回滚,外层也回滚,即使外层有try-catch,该事务也会回滚。
2.REQUIRES_NEW:支持事务。每次都是创建一个新事物,如果当前已经在事务中了,会挂起当前事务。
内层事务结束,内层就提交了,不用等着外层一起提交。 外层报错回滚,不影响内层。 内层报错回滚,外层try-catch内层的异常,外层不会回滚。 内层报错回滚,外层没有捕获处理异常,外层也回滚。
3.NESTED:支持事务。如果当前已经在一个事务中了,则嵌套在已有的事务中作为一个子事务。如果当前没在事务中则开启一个事务。
内层事务结束,等着外层一起提交。 外层回滚,内层也回滚。 内层回滚,外层try-catch内层的异常,不影响外层。 内层报错回滚,外层没有捕获处理异常,外层也回滚。
使用前提: JDK版本要在1.4以上,有java.sql.Savepoint。 事务管理器的nestedTransactionAllowed属性为true。 外层try-catch内层的异常。
4.SUPPORTS:支持事务。如果业务方法执行时已经在一个事务中就加入当前事务。否则就算了,不会开启一个事物。 5.MANDATORY:支持事务,如果业务方法执行时已经在一个事务中,则加入当前事务。否则抛出异常。
6.NOT_SUPPORTED:不支持事务,如果业务方法执行时已经在一个事务中,则挂起当前事务,等方法执行完毕后,事务恢复进行。 7.NEVER:不支持事务。如果当前已经在一个事务中了,抛出异常。
示例: @Transactional(propagation = Propagation.REQUIRED) |
isolation | 事务隔离级别1.DEFAULT ,这是默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应. 2.READ_UNCOMMITTED 这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。 3.READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻读。 4.REPEATABLE_READ这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻读。 5.SERIALIZABLE 事务被处理为顺序执行。防止脏读,不可重复读,防止幻读。 示例: @Transactional(isolation=Isolation.REPEATABLE_READ) Mysql innodb默认提供的是REPEATABLE_READ |
readOnly | 读写或只读事务,默认读写。 示例: @Transactional(readOnly=true) |
timeout | 事务超时时间 示例: @Transactional(timeout=10) 单位秒 |
rollbackFor | 导致事务回滚的异常类数组。必须继承自Throwable。 示例: @Transactional(rollbackFor=Exception.class) |
rollbackForClassName | 导致事务回滚的异常类名字数组。必须继承自Throwable |
noRollbackFor | 不会导致事务回滚的异常类数组。必须继承自Throwable |
noRollbackForClassName | 不会导致事务回滚的异常类名字数组。必须继承自Throwable |
关于事务的隔离机制和传播机制,详情请看另一篇博客
【十六】Spring Boot之事务(事务传播机制、嵌套事务、事务隔离机制详解)
三、自定义多个事务管理,和指定默认事务管理器的代码示例
package com.sid.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import javax.annotation.Resource;
import javax.sql.DataSource;
/**
* @program: springboot
* @description: 自定义多个事务管理器并且指定默认事务管理器
* @author: Sid
* @date: 2018-11-21 15:50
* @since: 1.0
**/
//实现TransactionManagementConfigurer接口指定默认事务管理器
@Component
public class TransactionManagerConfig implements TransactionManagementConfigurer {
@Resource(name="txManager1")
private PlatformTransactionManager txManager1;
// 创建事务管理器1
@Bean(name = "txManager1")
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
/** 没有引用spring-boot-starter-data-jpa 依赖
// 创建事务管理器2
@Bean(name = "txManager2")
public PlatformTransactionManager txManager2(EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
*/
//其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager1;
}
}
使用
@Transactional(value="txManager1")
@Override
public User addUser(User user) {
userMapper.insert(user);
int i = 1 / 0;
return user;
}
四、用@EnableAspectJAutoProxy (exposeProxy = true)方式
在同一个类中有两个方法,A和B
B是使用@Transactional事务注解,在外部调用B方法的时候事务会生效
但是如果A方法中调用B方法,B的事务不会生效。
因为A方法调同一个类的B方法时,没有走代理。
解决这个问题就用到@EnableAspectJAutoProxy (exposeProxy = true)注解用来支持aop方式的自动事务配置
AOP切入同类调用方法-AopContext.currentProxy()。
代码示例:
@Override
public User addUser(User user) {
UserService proxy = (UserService) AopContext.currentProxy();
proxy.addUserB(user);
return user;
}
@Transactional(value="txManager1")
public void addUserB(User user) {
userMapper.insert(user);
int i = 1 / 0;
}
启动类
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan("com.sid.mapper")//将项目中对应的mapper类的路径加进来就可以了
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
五、编程式事务管理机制
不用@Transactional注解
得到事务管理器(代码参见上述:三、自定义多个事务管理,和指定默认事务管理器的代码示例)
用事务管理器来开启、提交、回滚事务
// 得到事务管理器
@Resource(name="txManager1")
private PlatformTransactionManager txManager1;
public void addUserA(User user) {
DefaultTransactionDefinition transDefinition = new DefaultTransactionDefinition();
//设置事务的传播机制
transDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW);
//得到事务。根据事务的传播机制判断是新建事务还是用当前已有的事务
TransactionStatus transStatus = txManager1.getTransaction(transDefinition);
try {
userMapper.insert(user);
// 提交事务
txManager1.commit(transStatus);
} catch (Exception e) {
// 回滚事务
txManager1.rollback(transStatus);
}
}