事务
事务概念
数据库事务就是一种 SQL 语句执行的缓存机制,不会单条执行完毕就立即更新数据库数据,而是等所有语句都执行成功以后,触发 commit
来提交事务,从而更新数据。但如果事务内任意一条语句执行失败,即为事务失败,触发 rollback
回滚结束事务,数据回退到事务之前的版本。
事务的特性
- 原子性(Atomicity):指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败;
- 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态;
- 隔离性(Isolation):指一个事务的执行不能被其他事务干扰,即一个事务内的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间互不干扰;
- 持久性(Durability):指一个事务一旦被提交,它对数据库中的改变就是永久性的;
事务的提交方式
- 手动提交:手动开启事务,添加语句,手动提交或手动回滚;
- 自动提交:每条语句自动存储到一个事务中,执行成功自动提交,执行失败自动回滚;
事务案例
创建数据库表
sql
CREATE TABLE t_bank(
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '账号主键',
account VARCHAR(20) NOT NULL UNIQUE COMMENT '账号',
money INT UNSIGNED COMMENT '金额,不能为负值') ;
INSERT INTO t_bank(account,money) VALUES
('zhangsan',1000),('lisi',1000);
修改工具类和BaseDAO代码
开启事务手动提交时,需要特别注意,手动提交事务一定要关闭事务后(也就是将事务改为自动提交)以后,再进行 connection 资源释放。
java
public static void release() throws SQLException {
Connection connection = threadLocal.get();
if (connection != null) {
threadLocal.remove();
// 从 threadLocal 中移除之后,将自动提交置为 true,确保后续获取连接操作时是 自动提交
connection.setAutoCommit(true);
connection.close();
}
}
java
public int executeUpdate(String sql, Object... params) throws SQLException {
// ...
// 释放资源
preparedStatement.close();
// 如果是自动提交的话,可以释放资源,如果是手动提交,则不能释放资源
if (connection.getAutoCommit()) {
JDBCUtilV2.release();
}
return row;
}
public <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params) throws Exception {
// ...
// 释放资源
resultSet.close();
preparedStatement.close();
// 如果是自动提交的话,可以释放资源,如果是手动提交,则不能释放资源
if (connection.getAutoCommit()) {
JDBCUtilV2.release();
}
return list;
}
创建DAO层
警告
当开启事务时,一定要根据执行结果来判断事务是否提交和是否回滚,否则可能会在数据库看到的数据是错误的!
java
public interface BankDao {
int addMoney(Integer id, Integer money);
int subMoney(Integer id, Integer money);
}
java
public class BankDaoImpl extends BaseDAO implements BankDao {
@Override
public int addMoney(Integer id, Integer money) {
try {
String sql = "update t_bank set money = money + ? where id = ?";
return executeUpdate(sql, money, id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public int subMoney(Integer id, Integer money) {
try {
String sql = "update t_bank set money = money - ? where id + ?";
return executeUpdate(sql, money, id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
java
@Test
public void testTransaction() throws SQLException {
BankDao bankDao = new BankDaoImpl();
Connection connection = null;
try {
connection = JDBCUtilV2.getConnection();
connection.setAutoCommit(false);
// 加钱操作
bankDao.addMoney(1, 200);
// 减钱操作
bankDao.subMoney(2, 200);
// 事务提交
connection.commit();
} catch (Exception e) {
if (connection != null) {
// 事务回滚
connection.rollback();
}
} finally {
JDBCUtilV2.release();
}
}