0Pricing
Java Academy · 课时

事务

提交与回滚

事务 是 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_READ
  • TRANSACTION_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 反馈 — 无需本地设置。

此课程中的所有课时

  1. JDBC 基础
  2. PreparedStatement
  3. 事务
  4. 使用 HikariCP 实现连接池
← 返回 Java Academy