Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。
1、JDBC事务
JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口( java.sql.Connection )提供了两种事务模式:自动提交和手工提交。 java.sql.Connection 提供了以下控制事务的方法:
public void setAutoCommit(boolean)
public boolean getAutoCommit()
public void commit()
public void rollback()
使用 JDBC 事务界定时,您可以将多个 SQL 语句结合到一个事务中。JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。
三种Java事务差异
JDBC事务控制的局限性在一个数据库连接内,但是其使用简单。
JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂。
容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。
spring boot对事务的支持主要是两个注解:
@EnableTransactionManagement
会自动扫注解 @Transactional 的方法和类(表示该类中所有public的方法都需要开启事务的)
在spring boot中这个注解会在系统启动时自动加载,具体过程见下面的加载过程解析
@Transactional
使用注解来选择需要使用事务的方法(切入点),声明该方法需要事务支持,这是一个基于 AOP 的实现操作
@Transactional来修饰一个方法时,将mysql的底层事务进行了抽象,总结起来就做了3件事:
1. 将autocommit=off,为回滚做准备
2. 根据配置的isoliation,set tx_isolation,默认是RC级别,所以即便你的mysql的tx_isolation是RR级别,但通过spring transaction会重新制定tx_isolation,优先级要高于mysql设置的tx_isolation
3. 在方法执行过程中如果报错,执行rollback,否则执行commit
在spring boot中,在spring.factories中默认加载了
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
@AutoConfigureAfter({ JtaAutoConfiguration.class, JpaBaseConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })
public class TransactionAutoConfiguration {
在TransactionAutoConfiguration加载之前要加载
JpaBaseConfiguration以及DataSourceTransactionManagerAutoConfiguration类
spring boot启动的时候,执行application.run方法后,执行到JpaBaseConfiguration类
执行到JpaBaseConfiguration类
@EnableConfigurationProperties(JpaProperties.class)
@Import(DataSourceInitializedPublisher.Registrar.class)
public abstract class JpaBaseConfiguration implements BeanFactoryAware {
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
JpaProperties类
@ConfigurationProperties(prefix = "spring.jpa")
public class JpaProperties {
这也是我们在application.properties中设置所能生效的原因
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.type=trace
在这里初始化transactionManager,即使用JPA作为数据库访问技术时,spring boot使用的是JpaTransactionManager,而使用JDBC作为数据库访问技术时,spring boot使用的是DataSourceTransactionManager
这里就初始化了transactionManager
之后会执行到ConfigurationClassPostProcessor的
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof ImportAware) {
ImportRegistry importRegistry = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
AnnotationMetadata importingClass = importRegistry.getImportingClassFor(bean.getClass().getSuperclass().getName());
if (importingClass != null) {
((ImportAware) bean).setImportMetadata(importingClass);
}
}
return bean;
}
其中关键代码
((ImportAware) bean).setImportMetadata(importingClass);
在AbstractTransactionManagementConfiguration implement ImportAware中
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableTx = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
if (this.enableTx == null) {
throw new IllegalArgumentException(
"@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
}
}
加载了EnableTransactionManagement注解(即在spring boot中已经自动加载了@EnableTransactionManagement注解)
AdviceMode mode() default AdviceMode.PROXY;
mode标识表示采用哪种事务通知,默认JDK proxy_based,另外一种是AspectJ weaving-based
public enum AdviceMode {
PROXY,
ASPECTJ
}
可以看到
其在EnableAsync,EnableCaching中都有使用,说明异步以及缓存功能与事务功能一样,都是基于jdk proxy_based 拦截器实现的
@EnableTransactionManager与@Transactional配合使用
org.springframework.transaction.interceptor.TransactionAttribute
org.springframework.transaction.interceptor.DefaultTransactionAttribute
org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
@EnableAsync与@Async配合使用,使用
AnnotationAsyncExecutionInterceptor
AsyncAnnotationAdvisor
@EnableCaching与@Cacheable配合使用
org.springframework.cache.aspectj.AspectJCachingConfiguration
之后加载到DataSourceTransactionManagerAutoConfiguration中,初始化datasource
再之后终于加载到TransactionAutoConfiguration(autoconfigure包下的spring.factories中配置的事务管理配置类)
看到初始化后的transactionManager和datasource
加载流程图:
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository
implements JpaRepository, JpaSpecificationExecutor {
@Transactional
public void delete(T entity) {
Spring Data JPA 对所有的默认方法都开启了事务支持,且查询类事务默认启用 readOnly=true 属性,其增删改的每个方法都使用@Transactional来覆盖readOnly属性的
事务的传播行为是指:如果在开始当前事务之前,一个事务上下文已经存在,此时可以有若干选项来指定一个事务性方法的执行行为
1. REQUIERD:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
2. SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
3. MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
4. REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起
5. NOT_SUPPORTED:以非事务方式运行,如果存在当前事务,则把当前事务挂起
6. NEVER:以非事务方式运行,如果存在当前事务,则抛出异常
7. NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行,如果当前没有事务,则该取值等价于REQUIRED
Isolation isolation() default Isolation.DEFAULT;
1. DEFAULT:表示使用底层数据库的默认隔离级别。对大部分数据库而言,READ_COMMITED
2. READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别
3. READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值
4. REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读
5. SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下不会用到该级别
1. spring boot这种默认机制,只需要在我们用事务时,在方法或者此方法的类上加上@Transactional注解即可。在实际工作中,我们一般都要在Service层的某些方法上加事务
@Transactional(rollbackOn= Exception.class)
public void saveUserInfo() throws Exception{
//此方法有多个repository的调用
userCustomerRepository.save(…);
userRepository.save(…);
//模拟异常,事务回滚
throw New Exception(“test”);
}
2. @Transactional只能被应用到public方法上,对于其他非public的方法,如果标记了也不会出错,但方法没有事务功能
3. 用spring事务管理器,由spring来负责数据库的打开、提交、回滚。默认遇到运行期异常(throw new RuntimeException)会回滚;而遇到需要捕获的异常(throw new Exception)不会回滚。
要想所有异常都回滚,要加上
@Transactional(rollbackFor={Exception.class,其他异常})
如果让运行期异常不回滚,要加上
@Transactional(notRollbackFor=RuntimeException.class)
4. spring的建议是你在具体的类(或类的方法上)使用@Transactional注解,而不要使用在类所要实现的任何接口上。原因就是事务是基于代理的,所以以下两种情况即便添加了@Transactional注解也是无事务支持的
1)@Transactional注解使用在接口上,但是事务创建的是基于类的代理,那么事务的设置将不能被基于类的代理所识别,对象也将不会被事务代理所包装
2)如spring cache中的情况,当调用标识有@Cacheable的方法与@Cacheable标识的方法在同一个类中时,spring cache无效。原因也是@Cacheable会创建基于类的代理,但由于是同一个类的方法调用,所以不会被拦截到,所以缓存无效。
5. 事务有两种配置方式,一种是使用@Transactional显式的注解式事务。
还有一种是隐式事务,ASPECTJ的思路配置方法,所以不是没有加@Transactional注解就一定没有事务
参考:spring中使用aspectj为service package下的所有增删改方法添加事务