什么是事务
事务是数据库管理系统中的一个关键概念,用于确保数据的完整性和可靠性。在Spring框架中,事务通常指的是一段代码,这段代码要么完全执行,要么完全不执行,不会出现部分执行的情况。 Spring提供了事务管理的机制,允许开发者以声明式或编程式的方式来控制事务的边界和行为。
事务具有ACID属性,这确保了事务的可靠性:
- 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成,不会结束在中间某个点。
- 一致性(Consistency):事务必须使数据库从一个一致的状态转移到另一个一致的状态。
- 隔离性(Isolation):并发执行的事务之间不会互相影响,每个事务都像是独立执行。
- 持久性(Durability):一旦事务提交,则其结果永久保存在数据库中,即使系统发生故障也不会丢失。
Spring的事务管理主要有两种方式:
事务管理
1.声明式事务管理:
通过配置文件或注解来声明事务的边界和特性,利用AOP(面向切面编程)在方法执行前后添加事务管理的逻辑。
在Spring Boot中实现声明式事务管理,涉及到自动配置、自定义配置、事务属性的设置等多个方面。以下是详细的步骤和配置方法:
1. 添加依赖
确保项目中包含了Spring Boot的事务管理和数据库相关依赖。对于JPA,依赖如下:
Maven (pom.xml
):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 数据库驱动依赖,例如MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 其他业务依赖 -->
</dependencies>
Gradle (build.gradle
):
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'mysql:mysql-connector-java'
// 其他业务依赖
}
2. 配置数据源
在application.properties
或application.yml
中配置数据库连接信息:
# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/your_db?useSSL=false
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
3. 配置实体扫描
创建一个配置类,用于指定实体类的扫描路径,以及配置事务管理器:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager",
basePackages = {"com.example.domain"} // 实体类的包
)
public class DatabaseConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder, DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.example.domain") // 实体类的包
.persistenceUnit("default")
.build();
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
4. 使用@Transactional
注解
在需要事务管理的类或方法上使用@Transactional
注解:
@Service
public class SomeService {
@Autowired
private SomeRepository someRepository;
@Transactional
public void someServiceMethod() {
// 业务逻辑,涉及数据库操作
someRepository.doSomething();
}
}
5. 配置事务属性
通过@Transactional
注解的属性来配置传播行为、隔离级别、超时时间、只读属性和回滚规则:
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30, // 超时时间,单位为秒
readOnly = false,
rollbackFor = Exception.class // 指定哪些异常会导致事务回滚
)
public void someServiceMethod() {
// 业务逻辑
}
6. 自定义事务管理器
如果需要自定义事务管理器,可以在配置类中添加相应的@Bean
方法:
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
// 可以在这里设置其他属性,如超时时间
return transactionManager;
}
7. 配置事务管理器属性
通过配置文件设置事务管理器的属性,例如JPA的事务超时时间:
# application.properties
spring.jpa.properties.hibernate.transaction.manager_lookup_class=org.hibernate.hql.internal.TransactionManagerLookup
spring.transaction.default_timeout=5 # 以秒为单位设置默认的事务超时时间
8. 多数据源事务管理
如果应用中使用了多个数据源,需要为每个数据源配置独立的事务管理器,并在@EnableJpaRepositories
注解中指定事务管理器和实体管理器工厂:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactorySecondary",
transactionManagerRef = "transactionManagerSecondary",
basePackages = {"com.example.domain.secondary"}
)
public class SecondaryDatabaseConfig {
@Bean(name = "entityManagerFactorySecondary")
public LocalContainerEntityManagerFactoryBean getEntityManagerFactorySecondary() {
// 配置第二个数据源的EntityManagerFactoryBean
}
@Bean(name = "transactionManagerSecondary")
public PlatformTransactionManager getTransactionManagerSecondary(EntityManagerFactory entityManagerFactory) {
// 配置第二个数据源的JpaTransactionManager
}
}
通过上述步骤,就可以在Spring Boot应用中实现声明式事务管理了,并且可以根据业务需求配置不同的事务属性。Spring Boot的自动配置功能会根据你添加的依赖和配置来自动配置Spring的事务管理器,从而简化了事务管理的复杂性。
2.编程式事务管理:
通过代码直接管理事务的生命周期,如获取和释放事务资源,提交或回滚事务等。
在Spring Boot中实现编程式事务管理,你需要使用TransactionTemplate
类。Spring Boot的自动配置机制会为你配置好事务管理器,但编程式事务管理不会自动应用,你需要显式地在你的代码中使用TransactionTemplate
。
以下是在Spring Boot中实现编程式事务管理的步骤:
1. 配置事务管理器
Spring Boot通常会自动配置事务管理器,但如果你想自定义配置,可以在配置类中添加一个PlatformTransactionManager
的Bean。例如,如果你使用的是JPA,Spring Boot会默认配置一个JpaTransactionManager
。
Java配置示例:
@Configuration
public class DatabaseConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.example.domain") // 指定实体类所在的包
.persistenceUnit("default")
.build();
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
2. 配置TransactionTemplate
在你的配置类或服务类中,创建一个TransactionTemplate
的Bean,并注入你之前配置的PlatformTransactionManager
。
Java配置示例:
@Service
public class SomeService {
@Autowired
private TransactionTemplate transactionTemplate; // Spring Boot会自动注入
public void someServiceMethod() {
// 使用TransactionTemplate执行事务
transactionTemplate.execute(status -> {
// 这里编写业务逻辑
// 这些操作将被事务管理
return null;
});
}
}
3. 设置事务属性
通过TransactionTemplate
的execute
方法,你可以传递一个TransactionStatus
的回调。虽然你不能直接设置传播行为和隔离级别,但你可以在事务管理器的配置中设置这些属性。
例如,对于DataSourceTransactionManager
,你可以设置默认的隔离级别:
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
transactionManager.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
return transactionManager;
}
4. 适应不同的业务场景
- 读取操作:对于读取操作,如果不需要事务管理,可以使用
PROPAGATION_NOT_SUPPORTED
或PROPAGATION_NEVER
。 - 写入操作:对于写入操作,默认使用
PROPAGATION_REQUIRED
。 - 嵌套事务:对于需要嵌套事务的场景,可以使用
PROPAGATION_NESTED
。
由于编程式事务管理不使用注解,因此事务的传播行为和隔离级别通常在事务管理器的配置中设置。如果你需要更细粒度的控制,可能需要考虑使用声明式事务管理。
在Spring Boot中,推荐使用声明式事务管理,因为它更符合Spring的编程模型,并且易于使用。编程式事务管理通常用于那些不能使用声明式事务管理的场景。
3.事务的属性
在Spring框架中,事务属性定义了事务的特定行为,包括其传播行为、隔离级别、超时设置、只读标志以及回滚规则。以下是Spring支持的主要事务属性:
1. 传播行为(Propagation Behavior)
REQUIRED
:如果存在一个事务,则加入该事务;如果不存在事务,则创建一个新的事务。SUPPORTS
:支持当前事务,如果没有事务则不创建。MANDATORY
:必须在一个事务中,否则抛出异常。REQUIRES_NEW
:总是创建一个新的事务。NOT_SUPPORTED
:不能在事务中执行,如果存在一个活动的事务,则将其挂起。NESTED
:如果支持嵌套事务,就创建一个嵌套事务。如果当前事务存在,则嵌套事务是当前事务的子事务。
2. 隔离级别(Isolation Level)
ISOLATION_DEFAULT
:使用后端数据库的默认隔离级别。ISOLATION_READ_UNCOMMITTED
:读未提交。ISOLATION_READ_COMMITTED
:读已提交。ISOLATION_REPEATABLE_READ
:重复读。ISOLATION_SERIALIZABLE
:串行化。
3. 超时属性(Timeout)
- 以秒为单位设置事务的超时时间。超过这个时间,事务将被回滚。
4. 只读属性(Read-Only)
- 标记事务为只读事务,这可以给某些事务管理器提供优化的机会。
5. 回滚规则(Rollback Rules)
- 定义哪些异常会导致事务回滚。可以使用
rollbackFor
和noRollbackFor
属性指定。
6. Phase(事务的阶段)
- 指定事务应该在方法的执行阶段(例如,方法调用之前、调用之后、方法返回时或抛出异常时)。
7. 异步执行(Asynchronous Execution)
- 在某些事务管理器中,可以配置事务的异步执行。
这些属性可以通过编程式事务管理或声明式事务管理来设置。在声明式事务管理中,通常使用@Transactional
注解来设置这些属性,而在编程式事务管理中,则通过TransactionDefinition
对象来设置。正确配置事务属性对于确保数据的一致性、隔离性和整体事务管理策略至关重要。理解这些属性及其对事务行为的影响是进行事务管理的关键。
工作原理
Spring中@Transactional
注解的工作原理可以概括为以下几个步骤:
1. 注解驱动的事务管理启用
在Spring配置中通过@EnableTransactionManagement
注解启用事务管理,这将使得Spring扫描并处理@Transactional
注解。
2. 事务属性的配置
使用@Transactional
注解标记方法或类,通过注解的参数定义事务的属性,如传播行为、隔离级别、超时时间、只读标志、回滚规则等。
3. 创建代理
Spring容器在创建Bean时,会检测到@Transactional
注解。对于被注解的方法,Spring会通过AOP创建一个代理对象(可能是JDK动态代理或CGLIB代理),原始方法的调用会被这个代理对象所包装。
4. 代理对象的调用
当调用一个被@Transactional
注解的方法时,实际上是调用了代理对象中的方法。在代理对象内部,Spring框架会在方法执行前后添加事务逻辑。
5. 事务的开始
在代理对象的方法调用之前,Spring会根据定义的事务属性开始一个新的事务或加入到已存在的事务中。
6. 业务逻辑的执行
执行被@Transactional
注解的方法中的业务逻辑。
7. 事务的提交或回滚
- 如果方法正常执行完成,Spring将提交事务。
- 如果方法执行过程中抛出异常,并且该异常没有被注解的
rollbackFor
属性排除在外,Spring将回滚事务。
8. 清理
事务完成后,无论是提交还是回滚,Spring都会完成事务的清理工作,包括释放数据库连接等资源。
9. 事务管理器
在整个过程中,PlatformTransactionManager
是一个关键的组件,它负责实际的事务管理操作,如获取TransactionStatus
,以及根据运行时情况提交或回滚事务。
通过这个流程,@Transactional
注解使得Spring能够以声明式的方式管理事务,让开发者可以专注于业务逻辑,而不必显式地处理事务的创建、提交和回滚。