一、事务简介
事务是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。
事务具有ACID特性:
- 原子性(Atomicity) —— 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性(Consistency) —— 事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。
- 隔离性(Isolation) —— 一个事务的执行不能被其他事务干扰,多个并发事务之间要相互隔离。
- 持久性(Durability) —— 一个事务一旦提交,它对数据库中数据的改变就应该是永久的。接下来的其他操作或故障不应该对其执行结果有任何影响。
关于事务的基本理论在许多书籍资料中都能找到,这里就不赘诉了。
二、JDBC中事务的提交与回滚
如果在JDBC中不明确指定开启事务,那么所有的代码操作都在一个默认的事务中进行,默认的事务会自动开启,自动提交。因此如果我们需要在JDBC中手动操作事务,就首先需要关闭事务的自动提交。
JDBC中关于事务操作的API:
- void setAutoCommit(boolean autoCommit) —— 设置事务是否自动提交,如果设置为false,表示手动提交事务。
- void commit() —— 手动提交事务
- void rollback() —— 手动回滚事务(出现异常时候,所有已经执行成功的代码需要回退到事务开始前的状态)。
- Savepoint setSavepoint() —— 设置事务回滚到哪个位置。
可以使用一个转账的示例来演示事务的提交与回滚:
假设基于上面这个表完成一次转账操作:张三转账100元给李四。
// 参数:
// jdbc协议:postgresql子协议://主机地址:数据库端口号/要连接的数据库名
String url = "jdbc:postgresql://localhost:5432/test2";
// 数据库用户名
String user = "postgres";
// 数据库密码
String password = "123456";// 1. 加载Driver类,Driver类对象将自动被注册到DriverManager类中
Class.forName("org.postgresql.Driver");// 2. 连接数据库,返回连接对象
Connection conn = DriverManager.getConnection(url, user, password);PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {// 3. 关闭事务的自动提交机制conn.setAutoCommit(false);// 4. 预编译SQL// 从张三账户中减去100元String sql1 = "UPDATE bank SET money = money - 100 WHERE name = ?"; pstmt1 = conn.prepareStatement(sql1);pstmt1.setString(1, "张三");// 往李四账户中增加100元String sql2 = "UPDATE bank SET money = money + 100 WHERE name = ?";pstmt2 = conn.prepareStatement(sql2);pstmt2.setString(1, "李四");// 5. 执行SQL语句pstmt1.executeUpdate();pstmt2.executeUpdate();
} catch (Exception e) {// 6. 如果发生异常,则回滚事务conn.rollback();
} finally {// 6. 如果没有发生异常,则提交事务conn.commit();// 7. 关闭资源pstmt1.close();pstmt2.close();conn.close();
}
这样把转账操作放置在一个事务中执行,保证了事务的隔离性。即使转账操作发生异常,事务也能正确回滚,保证数据库中总金额仍然是2000。
三、JDBC中事务回滚到特定位置
调用setSavepoint()就能指定事务发生异常时回滚到特定位置:
// 省略参数代码// 1. 加载Driver类,Driver类对象将自动被注册到DriverManager类中
Class.forName("org.postgresql.Driver");// 2. 连接数据库,返回连接对象
Connection conn = DriverManager.getConnection(url, user, password);PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
Savepoint sp = null;
try {// 3. 关闭事务的自动提交机制,并指定事务回滚到这个位置conn.setAutoCommit(false);sp = conn.setSavepoint();// 4. 预编译SQL// 从张三账户中减去100元String sql1 = "UPDATE bank SET money = money - 100 WHERE name = ?"; pstmt1 = conn.prepareStatement(sql1);pstmt1.setString(1, "张三");// 往李四账户中增加100元String sql2 = "UPDATE bank SET money = money + 100 WHERE name = ?";pstmt2 = conn.prepareStatement(sql2);pstmt2.setString(1, "李四");// 5. 执行SQL语句pstmt1.executeUpdate();pstmt2.executeUpdate();
} catch (Exception e) {// 6. 如果发生异常,则回滚事务到指定位置conn.rollback(sp);
} finally {// 6. 如果没有发生异常,则提交事务conn.commit();// 7. 关闭资源pstmt1.close();pstmt2.close();conn.close();
}