# 事务

CodeIgniter 的数据库抽象层允许你在支持事务安全表类型的数据库中使用事务。在 MySQL 中，你需要使用 InnoDB 或 BDB 表类型，而不是更常见的 MyISAM。大多数其他数据库平台原生支持事务。

如果你不熟悉事务，我们建议你寻找适合你所用数据库的优质在线资源进行学习。以下内容假定你已经具备事务的基本理解。

## CodeIgniter 的事务处理方式

CodeIgniter 采用的事务处理方法与流行的数据库类库 ADODB 的处理过程非常相似。我们选择这种方法是因为它能极大简化事务的执行流程。在大多数情况下，你只需要两行代码即可完成操作。

传统的事务实现需要大量额外工作，因为需要跟踪所有查询并根据查询的成功与否决定提交或回滚。这在处理嵌套查询时尤为繁琐。相比之下，我们实现了一个智能事务系统，可以自动为你完成所有这些工作（你也可以选择手动管理事务，但实际上这样做没有任何优势）。

备注

自 v4.3.0 起，在事务过程中，即使 `DBDebug` 设为 true，默认也不会抛出异常。

## 运行事务

要通过事务执行查询，需使用 `$this->db->transStart()` 和 `$this->db->transComplete()` 方法，如下所示：

    <?php

    $this->db->transStart();
    $this->db->query('AN SQL QUERY...');
    $this->db->query('ANOTHER QUERY...');
    $this->db->query('AND YET ANOTHER QUERY...');
    $this->db->transComplete();

你可以在 `transStart()`/`transComplete()` 方法之间运行任意数量的查询，系统将根据这些查询的整体成功或失败情况决定提交或回滚。

## 严格模式

默认情况下，CodeIgniter 在严格模式下运行所有事务。

当启用严格模式时，如果运行多个事务组，其中一个组失败会导致所有后续组被回滚。

如果禁用严格模式，每个组将被独立处理，意味着一个组的失败不会影响其他组。

可以通过以下方式禁用严格模式：

    <?php

    $this->db->transStrict(false);

### 重置事务状态

Added in version 4.6.0.

当启用严格模式时，如果某个事务失败，所有后续事务将被回滚。

若要在失败后重新启动事务，可以重置事务状态：

    <?php

    $this->db->resetTransStatus();

## 错误处理

备注

自 v4.3.0 起，在事务过程中，即使 `DBDebug` 设为 true，默认也不会抛出异常。

你可以通过以下方式自行处理错误：

    <?php

    $this->db->transStart();
    $this->db->query('AN SQL QUERY...');
    $this->db->query('ANOTHER QUERY...');
    $this->db->transComplete();

    if ($this->db->transStatus() === false) {
        // generate an error... or use the log_message() function to log your error
    }

## 抛出异常

Added in version 4.3.0.

备注

自 v4.3.0 起，在事务过程中，即使 `DBDebug` 设为 true，默认也不会抛出异常。

若要在查询出错时抛出异常，可以使用 `$this->db->transException(true)`：

    <?php

    // When DBDebug in the Database Config must be true.

    use CodeIgniter\Database\Exceptions\DatabaseException;

    try {
        $this->db->transException(true)->transStart();
        $this->db->query('AN SQL QUERY...');
        $this->db->query('ANOTHER QUERY...');
        $this->db->query('AND YET ANOTHER QUERY...');
        $this->db->transComplete();
    } catch (DatabaseException $e) {
        // Automatically rolled back already.
    }

如果发生查询错误，所有查询将被回滚，并抛出 `DatabaseException` 异常。

## 禁用事务

事务功能默认启用。可以通过 `$this->db->transOff()` 禁用事务：

    <?php

    $this->db->transOff();
    $this->db->transStart();
    $this->db->query('AN SQL QUERY...');
    $this->db->transComplete();

禁用事务后，查询将自动提交，就像在没有使用事务的情况下运行查询一样。

## 测试模式

你可以选择将事务系统置于「测试模式」，此模式下即使查询有效也会被回滚。要启用测试模式，只需将 `$this->db->transStart()` 方法的第一个参数设为 true：

    <?php

    $this->db->transStart(true); // Query will be rolled back
    $this->db->query('AN SQL QUERY...');
    $this->db->transComplete();

## 手动运行事务

当在 app/Config/Database.php 文件中将 `DBDebug` 设为 false 时，可以通过以下方式手动运行事务：

    <?php

    $this->db->transBegin();

    $this->db->query('AN SQL QUERY...');
    $this->db->query('ANOTHER QUERY...');
    $this->db->query('AND YET ANOTHER QUERY...');

    if ($this->db->transStatus() === false) {
        $this->db->transRollback();
    } else {
        $this->db->transCommit();
    }

备注

手动运行事务时请确保使用 `$this->db->transBegin()`，不要 使用 `$this->db->transStart()`。

## 嵌套事务

在 CodeIgniter 中，事务可以嵌套执行，只有最外层（顶级）的事务命令会被真正执行。你可以在事务块中包含任意数量的 `transStart()`/`transComplete()` 或 `transBegin()`/`transCommit()`/`transRollback()` 组合。CodeIgniter 会跟踪事务的「深度」，并仅在最外层（零深度）执行实际操作。

    <?php

    $this->db->transStart(); // actually starts a transaction
    $this->db->query('SOME QUERY 1 ...');
    $this->db->transStart(); // doesn't necessarily start another transaction
    $this->db->query('SOME QUERY 2 ...');
    $this->db->transComplete(); // doesn't necessarily end the transaction, but required to finish the inner transaction
    $this->db->query('SOME QUERY 3 ...');
    $this->db->transComplete(); // actually ends the transaction

备注

如果事务结构非常复杂，你需要确保内部事务能够再次到达最外层，以便数据库完整执行这些操作，从而避免意外的提交/回滚。