Skip to content

事务

事务概念

数据库事务就是一种 SQL 语句执行的缓存机制,不会单条执行完毕就立即更新数据库数据,而是等所有语句都执行成功以后,触发 commit 来提交事务,从而更新数据。但如果事务内任意一条语句执行失败,即为事务失败,触发 rollback 回滚结束事务,数据回退到事务之前的版本。

事务的特性

  1. 原子性(Atomicity):指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败;
  2. 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态;
  3. 隔离性(Isolation):指一个事务的执行不能被其他事务干扰,即一个事务内的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间互不干扰;
  4. 持久性(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();
  }

}

Released under the MIT License.