news 2026/10/15 2:59:40

仓颉 std.database.sql 事务接口 Transaction 完全指南:核心行为、隔离级别与保存点实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
仓颉 std.database.sql 事务接口 Transaction 完全指南:核心行为、隔离级别与保存点实战
  • 人工智能
  • AI 技能
  • 文档
  • 开发工具

【免费下载链接】DocAgent

项目地址:https://gitcode.com/Cangjie-SIG/DocAgent
点击查看免费下载

Transaction是仓颉标准库std.database.sql中定义数据库事务核心行为的接口,它规定了事务从开始、提交、回滚到保存点管理的完整生命周期,并暴露事务访问模式、延迟模式与隔离级别三项可配置属性。本文以该接口文档为主体,结合 database_sql 包接口定义、事务相关枚举定义 以及 事务控制示例,系统讲解Transaction的每个成员函数、三个关键配置属性的取值语义与底层映射,并给出可直接运行的事务与保存点实战代码,帮助读者在仓颉项目中正确落地数据库事务编程。

Transaction 在 database.sql 包中的定位

std.database.sql包为仓颉提供访问数据库的 SQL/CLI 通用接口,配合数据库驱动Driver完成对数据库的各项操作(参见 包功能总览)。在包的完整调用链中,Transaction处于承上启下的位置:

  1. 通过DriverManager.getDriver()获取驱动,Driver.open()打开数据源Datasource;
  2. 通过Datasource.connect()取得连接Connection;
  3. 通过 Connection.createTransaction() 创建事务对象Transaction,该函数在"已经处于事务状态且不支持并行事务"时会抛出 SqlException;
  4. 通过 Statement 在事务内执行 SQL,最后用Transaction的commit()或rollback()结束事务。

Transaction接口声明如下(完整定义见 database_sql_package_interfaces.md):

public interface Transaction { mut prop accessMode: TransactionAccessMode mut prop deferrableMode: TransactionDeferrableMode mut prop isoLevel: TransactionIsoLevel func begin(): Unit func commit(): Unit func release(savePointName: String): Unit func rollback(): Unit func rollback(savePointName: String): Unit func save(savePointName: String): Unit }

接口的全部成员可划分为三组:三项事务属性(访问模式、延迟模式、隔离级别)、三个事务控制方法(begin/commit/rollback)、三个保存点方法(save/rollback(savePointName)/release)。继承该接口的 class、interface、struct 均需遵守成员函数的入参与返回值定义。

事务生命周期:begin、commit 与 rollback

begin():开始数据库事务

func begin(): Unit

begin()用于开始数据库事务。调用后事务进入活动状态,后续通过该连接执行的 SQL 将被纳入事务管理范围。根据接口文档,当提交事务时服务器端发生错误,或事务已提交、已回滚、连接已断开时,会抛出SqlException。

commit():提交数据库事务

func commit(): Unit

commit()将事务中所有未提交的修改持久化到数据库。调用成功后事务结束。与begin()相同,若提交时服务器端发生错误,或事务已提交/回滚、连接已断开,会抛出SqlException。

rollback():从挂起状态回滚事务

func rollback(): Unit

rollback()用于从挂起状态回滚事务,即撤销当前事务中尚未提交的全部修改,使数据库恢复到事务开始前的状态。这是事务异常处理中的兜底手段:当业务逻辑抛出异常时,通常需要调用rollback()放弃整个事务,避免留下"半成品"数据。

接口成员一览:begin、commit、rollback()与rollback(savePointName)的签名与说明,也收录于接口索引页 Transaction_interface.md。

保存点机制:save、rollback(savePointName) 与 release

保存点(Savepoint)允许在事务内部设置多个"检查点",从而在出错时只回滚到某个检查点之后的修改,而不是放弃整个事务。

save(savePointName):创建保存点

func save(savePointName: String): Unit

在事务中创建一个指定名称的保存点,可用于回滚此保存点之后的事务。参数savePointName为保存点名称,类型为String。保存点名称需要在整个事务中保持唯一,以便后续精确回滚。

rollback(savePointName):回滚至指定保存点

func rollback(savePointName: String): Unit

回滚事务至指定保存点名称。调用后,该保存点之后执行的所有修改被撤销,而保存点之前的修改仍然保留,事务继续处于活动状态,可以继续执行后续 SQL 或再次提交。

release(savePointName):销毁保存点

func release(savePointName: String): Unit

销毁先前在当前事务中定义的保存点,这允许系统在事务结束之前回收一些资源。当一个保存点不再需要时,应及时release,避免保存点过多占用服务器端资源。

保存点使用原则

从接口设计看,保存点遵循"先save、后按需rollback或release"的使用模式:

  • 需要"部分回滚"时,用rollback(savePointName)回退到指定保存点,保留更早的修改;
  • 保存点使命完成后,用release(savePointName)释放其资源;
  • 无论是否使用保存点,事务最终仍以commit()或rollback()收尾。

需要注意的是,保存点是否可用取决于具体数据库驱动的实现,使用前应确认驱动与数据库服务器支持保存点特性(示例文档 db_transactions.md 亦注明"如果数据库事务支持保存点"这一前提)。

三项事务属性与对应枚举详解

Transaction通过三个mut prop暴露事务的配置属性,读取时获取当前事务的配置,写入时设置事务的配置。三者分别对应三个枚举类型,下面逐一展开。

accessMode:事务访问模式

mut prop accessMode: TransactionAccessMode

访问模式决定事务是只读还是可读写。TransactionAccessMode 枚举定义如下:

public enum TransactionAccessMode <: ToString & Hashable & Equatable<TransactionAccessMode> { | ReadOnly | ReadWrite | Unspecified }
枚举值字符串表示语义
ReadOnly"Read Only"只读模式,事务内不允许执行写操作
ReadWrite"Read Write"读 + 写模式,事务可读写数据
Unspecified"Unspecified"未指定访问模式,行为取决于具体的数据库服务器

Unspecified的意义在于:将具体语义决策下放给数据库服务器,便于驱动在不同数据库间保持接口统一。

deferrableMode:事务延迟模式

mut prop deferrableMode: TransactionDeferrableMode

延迟模式与事务约束检查的时机相关。TransactionDeferrableMode 枚举定义如下:

public enum TransactionDeferrableMode <: ToString & Hashable & Equatable<TransactionDeferrableMode> { | Deferrable | NotDeferrable | Unspecified }
枚举值字符串表示语义
Deferrable"Deferrable"可延迟:延迟事务是指在前滚阶段结束时未提交、且遇到了阻止其回滚错误的事务,因无法回滚而被延迟
NotDeferrable"Not Deferrable"不可延迟:约束检查在语句执行时立即进行
Unspecified"Unspecified"未指定延迟模式,行为取决于具体的数据库服务器

理解Deferrable的关键在于"延迟约束检查":可延迟事务允许将部分完整性约束的检查推迟到事务提交时统一进行,从而让事务在过程中临时处于"约束未满足"状态,只要提交前满足即可。

isoLevel:事务隔离级别

mut prop isoLevel: TransactionIsoLevel

隔离级别定义了数据库系统中,一个事务中操作的结果在何时以何种方式对其他并发事务操作可见,是并发控制的核心参数。TransactionIsoLevel 枚举提供了 8 个取值:

public enum TransactionIsoLevel <: ToString & Hashable & Equatable<TransactionIsoLevel> { | Chaos | Linearizable | ReadCommitted | ReadUncommitted | RepeatableRead | Serializable | Snapshot | Unspecified }
枚举值字符串表示语义
Chaos"Chaos"无法覆盖来自隔离级别更高的事务的挂起更改
Linearizable"Linearizable"事务线性化:区别于串行化,线性化强调单个对象(db 行或 nosql 记录)上的一组单个操作严格按真实时间顺序执行;查看单个对象上的操作子集时线性化相关
ReadCommitted"Read Committed"事务等待,直到其他事务写锁定的行被解锁,防止读取任何"脏"数据
ReadUncommitted"Read Uncommitted"事务之间不隔离,允许脏读
RepeatableRead"Repeatable Read"可重复读:对同一字段的多次读取结果一致,除非数据被当前事务自身修改
Serializable"Serializable"可串行化:所有事务顺序执行,脏读、不可重复读、幻读都不会出现
Snapshot"Snapshot"快照隔离:通过行版本控制避免大多数锁定和阻塞
Unspecified"Unspecified"未指定隔离级别,行为取决于具体的数据库服务器

选择建议(基于枚举文档语义):对一致性要求最高的金融类场景可选Serializable;对读多写少、要求并发吞吐的场景可选Snapshot或ReadCommitted;ReadUncommitted允许脏读,仅在可容忍不一致数据的场景使用;Unspecified则交由数据库服务器决定,适合依赖数据库默认行为的场景。

三个枚举均实现了ToString、Hashable、Equatable<自身>及==/!=运算符,可参与哈希、比较与字符串输出,toString()的映射关系即上文表格中的"字符串表示"列。

实战:完整事务控制示例

以下示例来自 数据库事务示例文档,演示了Transaction接口在真实仓颉程序中的完整用法,包含连接建立、参数化插入、提交与回滚。

普通数据库事务:commit 与 rollback

import std.database.sql.* import std.time.* main() { let SQL_INSERT = "INSERT INTO EMPLOYEE (NAME, SALARY, CREATED_DATE) VALUES (?, ?, ?)" let drv = DriverManager.getDriver("opengauss") ?? return let db = drv.open("opengauss://localhost:5432/testdb") try(cn = db.connect()) { let psInsert = cn.prepareStatement(SQL_INSERT) // 创建事务对象 let tx = cn.createTransaction() try { // 插入第一条数据 psInsert.set<String>(0, "mkyong") psInsert.set<Array<Byte>>(1, Array<Byte>(1, repeat: 10)) psInsert.set<DateTime>(2, DateTime.now()) psInsert.update() // 插入第二条数据 psInsert.set<String>(0, "kungfu") psInsert.set<Array<Byte>>(1, Array<Byte>(1, repeat: 20)) psInsert.set<DateTime>(2, DateTime.now()) psInsert.update() // 如果连接到数据库,测试回滚 SQLException:未为参数3指定值。 psInsert.set<String>(0, "mkyong") psInsert.set<Array<Byte>>(1, Array<Byte>(5, {i => UInt8(i + 1)})) psInsert.update() // 提交事务 tx.commit() } catch (e1: SqlException) { e1.printStackTrace() try { // 发生异常,回滚所有事务 tx.rollback() } catch (e2: SqlException) { // 如果回滚失败 e2.printStackTrace() } } } catch (e: SqlException) { // 如果连接失败 e.printStackTrace() } }

要点拆解:

  • 通过DriverManager.getDriver("opengauss")获取驱动(若未注册返回None),drv.open()打开数据源,db.connect()建立连接,相关驱动管理能力见 DriverManager_class.md;
  • cn.createTransaction()创建事务对象,随后所有psInsert.update()均在事务作用域内执行;
  • 第三条插入语句缺少参数 3 的取值,会触发SqlException,此时tx.commit()不会执行,控制流转入catch分支调用tx.rollback()撤销前两条插入——这正是"要么全部成功、要么全部撤销"的事务原子性体现;
  • 内层catch再次捕获SqlException是因为rollback()本身也可能因连接断开等场景失败,需要单独处理;
  • 事务相关的 SQL 均通过 Statement 接口 的set<T>/update完成参数绑定与执行。

事务保存点:部分回滚

import std.database.sql.* import std.time.* main() { let SQL_INSERT = "INSERT INTO EMPLOYEE (NAME, SALARY, CREATED_DATE) VALUES (?, ?, ?)" let drv = DriverManager.getDriver("opengauss") ?? return let db = drv.open("opengauss://localhost:5432/testdb") try(cn = db.connect()) { let psInsert = cn.prepareStatement(SQL_INSERT) let tx = cn.createTransaction() try { // 创建保存点 1 tx.save("save1") psInsert.set<String>(0, "mkyong") psInsert.set<Array<Byte>>(1, Array<Byte>(1, repeat: 10)) psInsert.set<DateTime>(2, DateTime.now()) psInsert.update() // 创建保存点 2 tx.save("save2") psInsert.set<String>(0, "kungfu") psInsert.set<Array<Byte>>(1, Array<Byte>(1, repeat: 20)) psInsert.set<DateTime>(2, DateTime.now()) psInsert.update() // 创建保存点 3 tx.save("save3") psInsert.set<String>(0, "mkyong") psInsert.set<Array<Byte>>(1, Array<Byte>(5, {i => UInt8(i + 1)})) psInsert.update() // 回滚到保存点 2 tx.rollback("save2") // 提交事务 tx.commit() } catch (e1: SqlException) { e1.printStackTrace() try { // 发生异常,回滚所有事务 tx.rollback() } catch (e2: SqlException) { e2.printStackTrace() } } } catch (e: SqlException) { e.printStackTrace() } }

要点拆解:

  • 依次创建save1、save2、save3三个保存点,每插入一条数据前先建一个保存点,形成"分段提交"的结构;
  • tx.rollback("save2")撤销save2之后(即第三条插入)的修改,而save2之前的两条插入保留;
  • 最终tx.commit()提交,此时数据库只包含保留的两条记录——保存点让"部分成功"成为可能,避免因单条失败而丢弃全部工作;
  • 若中途抛出SqlException,外层catch仍以tx.rollback()全量回滚兜底。

异常处理:SqlException

Transaction的全部方法在失败时统一抛出 SqlException。该类继承自Exception,提供三种构造函数与三项诊断属性:

public open class SqlException <: Exception { public init() public init(message: String) public init(message: String, sqlState: String, errorCode: Int64) }
属性类型含义
messageString异常信息字符串
sqlStateString长度为五个字符的字符串,是数据库系统返回的最后执行的 sql 语句状态
errorCodeInt64数据库供应商返回的整数错误代码

在事务编程中,SqlException可能出现的典型时机包括:begin()/commit()/rollback()时服务器端发生错误、事务已提交或回滚、连接已断开,以及createTransaction()时已处于事务状态且驱动不支持并行事务。建议按示例中的分层捕获方式处理:外层捕获连接与事务异常,内层捕获rollback()自身失败,并利用sqlState与errorCode辅助定位数据库端错误。

小结

Transaction接口为仓颉数据库编程提供了统一且完备的事务抽象:

  • 生命周期控制:begin()开始、commit()提交、rollback()回滚,构成事务的完整闭环;
  • 保存点精细化控制:save()创建检查点、rollback(savePointName)部分回滚、release()释放资源,实现事务内"局部撤销";
  • 三项可配置属性:accessMode(读写模式)、deferrableMode(延迟模式)、isoLevel(隔离级别),分别由TransactionAccessMode、TransactionDeferrableMode、TransactionIsoLevel三个枚举提供取值,且均支持toString()字符串输出与相等性比较;
  • 统一的异常模型:所有方法失败时抛出SqlException,携带sqlState与errorCode便于诊断。

配合 database_sql 包 readme 中的完整 API 索引与 事务示例,开发者可以快速在仓颉项目中搭建具备事务能力的数据访问层。使用时请以实际数据库驱动支持的特性为准,尤其是保存点与延迟模式是否可用。

  • 人工智能
  • AI 技能
  • 文档
  • 开发工具

【免费下载链接】DocAgent

项目地址:https://gitcode.com/Cangjie-SIG/DocAgent
点击查看免费下载

相关推荐

上一篇:抖音视频下载终极指南:从零开始掌握无水印批量下载技巧
下一篇:如何快速部署抖音无水印下载器:面向新手的完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 2:58:08

HttpAsyncClient连接池配置详解:三个核心参数决定并发上限

做Java后端的人应该都有过这种经历&#xff1a;明明用上了HttpAsyncClient这种基于NIO的异步客户端&#xff0c;按理说并发能力应该很强&#xff0c;结果压测时连接池却先顶不住了。日志里一行ConnectionPoolTimeoutException: Timeout waiting for connection from pool&#…

作者头像 李华
网站建设 2026/10/15 2:56:53

2026年薪酬设计标杆企业,靠谱选择看这几点!

引言&#xff1a;薪酬设计的困局与破局在2026年的商业环境中&#xff0c;企业的人力资源管理已从辅助职能跃升为战略核心。薪酬设计&#xff0c;作为吸引、激励与保留人才的关键杠杆&#xff0c;其科学性与精准性直接关系到企业竞争力。然而&#xff0c;众多企业在实践中仍面临…

作者头像 李华
网站建设 2026/10/15 2:56:18

为什么大部分「PDF 转 Word」都不免费?拆开这条流水线你就懂了

PDF 转 Word 的难点&#xff0c;一句话说清&#xff1a;PDF 是一张「画出来的纸」&#xff0c;Word 是一份「有结构的文档」。前者只有坐标、字形、线条&#xff0c;后者有段落、表格、标题、脚注。把前者变成后者&#xff0c;任何靠谱的工具都在跑同一条流水线。这条流水线真不…

作者头像 李华
网站建设 2026/10/15 2:56:10

诺瓦笔试全解析:C/C++基础、编程题与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华