事务
提交与回滚
事务 是 CoddyKit 上的免费 Java Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
什么是事务
事务会将多个 SQL 操作组合成一个要么全部成功、要么全部失败的单元。
要么每条语句都执行成功,并将更改永久保存(commit);要么某个操作失败,所有更改都会被撤销(rollback)。这样可以保持数据库的一致性。
一分钟了解 ACID
事务提供以下 ACID 保证:
- 原子性——要么全部执行,要么全部不执行。
- 一致性——将数据库从一个有效状态转换到另一个有效状态。
- 隔离性——并发事务不会相互破坏数据。
- 持久性——一旦提交,更改即使发生崩溃也不会丢失。
自动提交模式
默认情况下,JDBC 连接运行在自动提交模式下:每条语句都是一个独立的事务,并会立即提交。
对于单条语句来说这没有问题,但当多次写入必须一起成功时,这种模式就毫无用处。要自行控制事务,请关闭自动提交。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/bank";
try (Connection conn = DriverManager.getConnection(url, "app", "secret")) {
System.out.println("Default auto-commit: " + conn.getAutoCommit());
conn.setAutoCommit(false);
System.out.println("Now manual: " + conn.getAutoCommit());
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}Commit 和 Rollback
关闭自动提交后,由您决定事务的边界:
conn.commit()会永久保存所有待处理的更改。conn.rollback()会丢弃这些更改。
经典示例是转账:从一个账户扣款,并向另一个账户入账。这两个操作必须同时发生,否则就都不能发生。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/bank";
String debit = "UPDATE account SET balance = balance - ? WHERE id = ?";
String credit = "UPDATE account SET balance = balance + ? WHERE id = ?";
try (Connection conn = DriverManager.getConnection(url, "app", "secret")) {
conn.setAutoCommit(false);
try (PreparedStatement d = conn.prepareStatement(debit);
PreparedStatement c = conn.prepareStatement(credit)) {
d.setInt(1, 100); d.setInt(2, 1); d.executeUpdate();
c.setInt(1, 100); c.setInt(2, 2); c.executeUpdate();
conn.commit();
System.out.println("Transfer committed");
} catch (SQLException ex) {
conn.rollback();
System.out.println("Rolled back: " + ex.getMessage());
}
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}失败时 Rollback 模式
请记住以下结构:
- 关闭自动提交。
- 在内部的
try中执行操作。 - 成功时调用
commit()。 - 在
catch中调用rollback(),然后重新抛出异常或记录日志。
如果没有 rollback,操作失败可能会让数据库处于只更新了一半的状态。
模拟原子性
下面使用普通 Java 模拟要么全部成功、要么全部失败的思想,因此可以独立运行。我们对一个快照执行两次更新;如果任意一次失败,就恢复该快照。
public class Main {
public static void main(String[] args) {
int[] balances = {500, 300};
int[] backup = balances.clone();
try {
balances[0] -= 200;
if (balances[0] < 0) throw new IllegalStateException("insufficient funds");
balances[1] += 200;
System.out.println("Commit: " + balances[0] + ", " + balances[1]);
} catch (RuntimeException e) {
balances = backup;
System.out.println("Rollback: " + balances[0] + ", " + balances[1]);
}
}
}保存点
保存点是事务内部的一个标记。您可以回滚到该标记,而不必丢弃整个事务。
Savepoint sp = conn.setSavepoint()conn.rollback(sp)只会撤销该点之后执行的操作。
这对于长事务中的部分恢复非常有用。
隔离级别
隔离性决定一个事务能够看到另一个事务哪些尚未提交的操作。JDBC 通过 conn.setTransactionIsolation(...) 定义以下级别:
TRANSACTION_READ_COMMITTED——常见的默认级别。TRANSACTION_REPEATABLE_READTRANSACTION_SERIALIZABLE——最强,但速度最慢。
更高的隔离级别可以防止异常,但会降低并发性。
读取异常
较低的隔离级别可能导致:
- 脏读——读取到另一个事务尚未提交的更改。
- 不可重复读——同一行数据在两次读取之间发生变化。
- 幻读——出现符合之前查询条件的新行。
请选择能够防止您的逻辑无法容忍的异常的最低级别。
恢复自动提交
如果连接来自连接池,请在归还连接之前恢复其原始状态。
手动执行事务后,再次设置 conn.setAutoCommit(true)(或恢复借用连接时观察到的值),这样下一个使用者就不会因您的设置而遇到意外。
保持事务简短
事务会一直持有锁,直到提交或回滚。长事务会阻塞其他操作,并增加死锁风险。
- 不要在打开的事务中执行缓慢的网络调用或等待用户输入。
- 先收集数据,然后快速打开事务、写入并提交。
简短且专注的事务具有更好的扩展能力。
快速检查
调用 connection.rollback() 会产生什么效果?
回顾
事务为您提供要么全部成功、要么全部失败的安全保障:
- 使用
setAutoCommit(false)关闭自动提交。 - 成功时调用
commit(),失败时调用rollback()。 - 使用保存点执行部分回滚。
- 调整隔离级别,在正确性和并发性之间取得平衡。
- 归还连接池中的连接前恢复自动提交。
常见问题解答
「事务」课时是免费的吗?
是的 — 「事务」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「事务」这节课中我会学到什么?
提交与回滚 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「事务」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。