执行事务
4 分钟阅读
Executing transactions - 执行事务
您可以使用表示事务的sql.Tx来执行数据库事务。除了表示特定于事务语义的Commit
和Rollback
方法之外,sql.Tx
还有所有用来执行普通数据库操作的方法。要获取sql.Tx
,可以调用DB.Begin
或DB.BeginTx
。
一个数据库事务将多个操作作为更大目标的一部分进行分组。所有的操作都必须成功,或者都不能成功,在这两种情况下都要保持数据的完整性。通常,一个事务的工作流程包括:
- 开始事务。
- 执行一系列的数据库操作。
- 如果没有发生错误,提交事务以进行数据库更改。
- 如果发生错误,回滚事务使数据库保持不变。
sql
包提供了开始和结束事务的方法,以及执行中间的数据库操作的方法。这些方法与上述工作流程中的四个步骤相对应。
开始一个事务
DB.Begin或DB.BeginTx开始一个新的数据库事务,返回一个代表它的
sql.Tx
。执行数据库操作。
使用一个
sql.Tx
,您可以在一系列使用单一连接的操作中查询或更新数据库。为了支持这一点,Tx
导出了以下方法:Exec 和 [ExecContext](https://pkg.go.dev/database/sql#Tx.ExecContext https://before80.github.io/go_docs_with_hugo/stdLib/database/sql/#tx-execcontext----go18) ,用于通过SQL语句(如
INSERT
、UPDATE
和DELETE
)进行数据库更改。更多信息请参见Executing statements that don’t return data(执行不返回数据的语句)。
Query,QueryContext,QueryRow ,QueryRowContext 用于返回行的操作。
更多信息,请参见 Querying for data (查询数据)。
Prepare,PrepareContext,Stmt,StmtContext用于预先定义预处理语句。
更多信息,请参见Using prepared statements (使用预处理语句)。
用以下方法之一结束事务:
使用Tx.Commit提交事务。
如果
Commit
成功(返回nil
错误),那么所有的查询结果都被确认为有效,所有执行的更新都作为一个单一的原子变化应用到数据库中。如果Commit
失败,那么Tx
上的所有Query
和Exec
的结果都应该被视为无效而丢弃。使用Tx.Rollback来回滚事务。
即使
Tx.Rollback
失败,该事务也不再有效,也不会被提交到数据库。
最佳实践
遵循下面的最佳实践,可以更好地了解事务有时需要的复杂语义和连接管理。
- 使用本节中描述的API来管理事务。不要直接使用与事务相关的SQL语句,如
BEGIN
和COMMIT
——这样做会使您的数据库处于不可预测的状态,尤其是在并发程序中。 - 当使用事务时,注意不要直接调用非事务的
sql.DB
方法,因为这些方法会在事务之外执行,将会给您的代码提供一个不一致的数据库状态,甚至导致死锁。
示例
下面的例子中的代码使用一个事务来创建新专辑的客户订单。在这一过程中,代码将:
- 开始一个事务。
- 延迟该事务的回滚。如果事务成功,它将在函数退出前被提交,使延迟的回滚调用成为no-op。如果事务失败,它将不会被提交,这意味着回滚将在函数退出时被调用。
- 确认客户订购的专辑有足够的库存。
- 如果有足够的存货,更新存货数量,用订购的专辑数量来减少它。
- 创建一个新的订单,并为客户检索新订单的生成ID。
- 提交事务并返回ID。
这个例子使用了Tx
方法,这些方法需要一个context.Context
实参。这使得函数的执行——包括数据库操作——在其运行时间过长或客户端连接关闭的情况下有可能被取消。更多信息请参见Canceling in-progress operations(取消正在进行的操作)。
|
|