Spring 事务
数据库的事务
概念和特性
事务是数据库操作的一个基本单元,它包含一组操作,这些操作要么全部成功执行,要么全部不执行,保持数据的一致性
- 原子性 (Atomicity):一个事务中的多个操作要么全部发生,要不都不发生
- 一致性 (Consistency):事务执行前后,数据从一个合法状态转为另一个合法状态(不能数据虽然改了,但是是不合法的数据)
比如修改前后都要满足索引的约束,主键、外检、唯一、余额不能为负(业务自定义)等 - 隔离性 (Isolation):多个事务并发执行时,一个事务的执行不影响其他事务(并发时要保证数据是安全的)
- 持久性 (Durability):事务完成后,对数据的影响是永久性的(不能隔一段时间数据变化了或又恢复了)
隔离级别
| 隔离级别 | 底层实现 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|---|
| READ UNCOMMITTED(读未提交) | 不加锁 | ✅ | ✅ | ✅ |
| READ COMMITTED(读已提交) | 写加锁,读不加锁(行锁) | ✅ | ✅ | |
| REPEATABLE READ(可重复度) | 对行读写都加锁(行锁) | ✅ | ||
| SERIALIZABLE(串行化) | 对表加锁(表锁) |
脏读:一个事务读取了另一个事务修改过(还未提交)的数据。读到了无效的数据
不可重复度:同一个事务内多次读取同一行数据,读取到的结果不一致,因为期间其他事务修改数据并提交。同一事务多次单行读取结果不一致
幻读:和不可重复读有点类似,只不是过幻读指的是范围查询是多行的。同一事务多次范围读取结果不一致
- Mysql 默认是 RR,Oracle 和 PG 默认是 RC,Oracle 不支持 RU
- 虽然都不是 SERIALIZABLE,但是也是数据安全的,比如使用 MVCC,使用了扩展的隔离级别等
JDBC 事务
配置数据源
DriverManager 是 JDBC1.0 提供的(不支持连接池),DataSource 是 JDBC2.0 提供的(支持连接池,意味着连接可复用)
DriverManager 获取连接示例
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "username";
String password = "password";
Connection conn = DriverManager.getConnection(url, user, password);
DataSource 获取连接示例(实现有多个,C3P0、DPCB、Hikari、Druid)
// 不带连接池
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setURL("jdbc:mysql://localhost:3306/mydb");
dataSource.setUser("username");
dataSource.setPassword("password");
// Hikari
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("username");
dataSource.setPassword("password");
dataSource.setMaximumPoolSize(10); // 连接池大小
// DBCP
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("username");
dataSource.setPassword("password");
dataSource.setInitialSize(5); // 初始连接数
dataSource.setMaxTotal(10); // 最大连接数
使用示例
手动控制事务,也叫编程式事务
Connection conn = null;
try {
// 获取数据库连接
conn = dataSource.getConnection();
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
// 执行SQL操作1
PreparedStatement stmt1 = conn.prepareStatement("UPDATE account SET balance = balance - ? WHERE id = ?");
stmt1.setInt(1, 100);
stmt1.setInt(2, 1);
stmt1.executeUpdate();
// 执行SQL操作2
PreparedStatement stmt2 = conn.prepareStatement("UPDATE account SET balance = balance + ? WHERE id = ?");
stmt2.setInt(1, 100);
stmt2.setInt(2, 2);
stmt2.executeUpdate();
// 提交事务
conn.commit();
} catch (SQLException e) {
// 回滚事务
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
// 恢复自动提交并关闭连接
if (conn != null) {
try {
conn.setAutoCommit(true);
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Spring 事务
编程式事务:手动控制事务的回滚和提交,比如 JDBC 的事务就叫编程式事务
声明式事务:不需要手动控制事务的提交和回滚,使用注解自动完成,Spring 的 @Transactional 就是声明式事务
事务管理器
PlatformTransactionManager 是 Spring 事务的核心接口,定义了事务的基本操作
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
DataSourceTransactionManager:用于 JDBC 和 MyBatisHibernateTransactionManager:用于 HibernateJpaTransactionManager:用于 JPAJtaTransactionManager:用于分布式事务
TransactionTemplate 是对 PlatformTransactionManager 进一步的封装,用的确实不多,了解一下就行了
Spring 中配置 PlatformTransactionManager
@Configuration
@EnableTransactionManagement // 启用注解事务
public class AppConfig {
// 1. 配置数据源
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setMaximumPoolSize(10);
return dataSource;
}
// 2. 配置事务管理器(事务管理需要数据源)
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 3. 配置 JdbcTemplate(jdbc 也需要数据源)
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
Spring 中使用编程式事务
@Service
public class AccountService {
@Autowired
private PlatformTransactionManager transactionManager;
public void transfer(int fromId, int toId, int amount) {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
try {
// 业务代码
jdbcTemplate.update("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, fromId);
jdbcTemplate.update("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, toId);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
Spring 中使用声明式事务
@Configuration
@EnableTransactionManagement
public class AppConfig {
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setMaximumPoolSize(10);
return dataSource;
}
@Bean // Spring 中需要手动配置事务管理器(SpringBoot中会自动配置,SpringBoot 中只要配置好了数据数据源,DataSource 也会自动配置)
public TransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource());
}
}
@Service
public class AccountService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional // 加上这个注解就好了,外部调用这个方法时,如果方法出现异常会自动回滚
public void transfer(int fromId, int toId, int amount) {
// 扣钱
jdbcTemplate.update("UPDATE account SET balance = balance - ? WHERE id = ?", amount, fromId);
// 加钱
jdbcTemplate.update("UPDATE account SET balance = balance + ? WHERE id = ?", amount, toId);
}
}
@Transactional
Spring 提供 @Transactional 注解实现声明式事务注解
可以用在类上,可以用在方法上
可以配置隔离级别和传播行为
还有一些使用限制,比如不能标注在 private 方法上,不能同类中调用等
详细看这里asdfasdf

浙公网安备 33010602011771号