事务

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);

重置事务状态

在 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
}

抛出异常

在 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

备注

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