## 回顾事务
### 什么是事务
事务是一种数据库机制,如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败,以保证业务的完整操作
### 事务的四大特性
事务满足 ACID 四大特性,即
* 原子性 Atomicity:事务是不可分割的最小操作单位,被事务管理的操作要么同时成功,要么同时失败回滚
* 一致性 Consistency:数据库在事务执行前后都保持一致性状态,在一致性状态下,所有事务对同一个数据的读取结果都是相同的
* 隔离性 Isolation:多个事务之间是相互独立,也就是说,一个事务所做的修改在最终提交以前,对其它事务是不可见的
* 持久性 Durability:当事务提交或回滚后,数据库会持久化的保存数据,即使系统发生错误
### 在使用 Spring 之前如何控制事务
1. JDBC
```java
// 获取连接
Connection connection = JdbcUtils.getConnection();
// 开启事务:禁止自动提交
connection.setAutoCommit(false);
/**
* 增删改操作
*/
// 提交事务
connection.commit();
// or 回滚事务
connection.rollback();
```
2. MyBatis
```java
// 提交事务
sqlSession.commit();
// or 回滚事务
sqlSession.rollback();
```
实际上,MyBatis 的 `SqlSession` 的底层依然是 `Connection` 对象,所以对于事务的控制,本质上就是对 `Connection` 的控制
## Spring 控制事务
Spring 中控制事务有两种实现方式,即声明式事务和编程式事务,首先引入相关依赖
```xml
org.springframework
spring-tx
5.2.6.RELEASE
```
### 声明式事务
声明式事务管理建立在 Spring 的 AOP 之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚
将 Service 层的类以及数据源事务管理器 `DataSourceTransactionManager` 纳入 IOC 容器管理,并且开启事务的注解驱动
```xml
```
声明式事务只需在需要开启事务的方法(通常是进行多个增删改的方法)上添加 `@Transactional` 注解即可
```java
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
// 设置事务的属性,主要是隔离属性和传播属性
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
private transactionTest() {
/**
* 增删改
*/
userDAO.insert();
userDAO.update();
userDAO.delete();
}
}
```
声明式事务属于无侵入式,使用起来非常简单,不会影响业务逻辑的实现,是 Spring 推荐的方式,但是唯一不足就是粒度较粗,是方法级别
### 编程式事务
编程式事务时一种侵入式的编程方式,可以对更细粒度的代码块进行事务管理,Spring 中使用 `TransactionTemplate` 进行编程式事务管理,首先在 IOC 容器中加入 `TransactionTemplate` 类,将注入所使用的事务管理器
```xml
```
在需要进行事务管理的代码块使用 `TransactionTemplate` 对象
```java
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
private TransactionTemplate transactionTemplate;
private transactionTest() {
// 设置事务的属性,主要是隔离属性和传播属性
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// TransactionTemplate 的核心方法,需要传入一个TransactionCallback对象,这里直接使用匿名对象实现该接口
transactionTemplate.execute(new TransactionCallback