Qt 5.15.4中通过QSqlDatabase与QOCI插件在Oracle 19c启动只读事务可行吗?
Qt QOCI 实现只读事务(无需修改Qt源码)
问题背景
环境:Qt 5.15.4、基于OCI 12.2构建的QOCI插件、Oracle 19c数据库,基础数据库连接功能正常。
已尝试两种方式均未达到预期:
- 直接执行
SET TRANSACTION READ ONLY:无报错,但后续执行INSERT语句未触发预期的ORA-01456错误; - 先调用
db.transaction()再执行SET TRANSACTION READ ONLY:触发ORA-01453错误(提示SET TRANSACTION必须是事务的第一条语句)。
已知修改QOCIDriver::beginTransaction()中OCITransStart()的参数为OCI_TRANS_READONLY可生效,但需无需修改Qt源码的实现方案。
可行解决方案
核心思路
不依赖Qt内置的transaction()方法,直接通过Qt提供的接口获取原生OCI句柄,手动启动只读事务,同时保持Qt数据库连接的上下文一致性。
具体实现代码
#include <QtSql/QSqlDatabase> #include <QtSql/QSqlDriver> #include <oci.h> // 获取目标数据库连接 QSqlDatabase db = QSqlDatabase::database("your_connection_name"); if (!db.isOpen()) { // 处理连接未打开逻辑 return; } // 获取原生OCI环境句柄和连接句柄 OCIEnv* ociEnv = reinterpret_cast<OCIEnv*>(db.driver()->handle()); OCIError* ociErr = nullptr; // 初始化错误句柄 OCIHandleAlloc(ociEnv, reinterpret_cast<void**>(&ociErr), OCI_HTYPE_ERROR, 0, nullptr); OCISvcCtx* ociConn = reinterpret_cast<OCISvcCtx*>(db.driver()->handle(QSqlDriver::ConnectionHandle)); // 启动只读事务 sword transStatus = OCITransStart(ociEnv, ociErr, ociConn, 0, OCI_TRANS_READONLY); if (transStatus != OCI_SUCCESS && transStatus != OCI_SUCCESS_WITH_INFO) { // 处理事务启动失败逻辑 OCIHandleFree(ociErr, OCI_HTYPE_ERROR); return; } // 执行测试INSERT语句(此时会触发ORA-01456) QSqlQuery query(db); if (!query.exec("INSERT INTO your_table (col) VALUES ('test')")) { qDebug() << query.lastError().text(); // 应输出ORA-01456错误 } // 结束事务 db.rollback(); OCIHandleFree(ociErr, OCI_HTYPE_ERROR);
关键说明
- 直接执行
SET TRANSACTION READ ONLY无效的原因:Qt QOCI插件默认会在执行第一条DML/DDL语句时自动启动读写事务,覆盖手动设置的只读属性; - 此方案通过原生OCI接口启动事务,绕过了Qt默认的事务启动逻辑,确保事务属性为只读;
- 事务结束时仍可使用Qt的
commit()或rollback()方法,Qt会正确调用OCI的事务结束接口,无需额外处理。
内容的提问来源于stack exchange,提问作者Lars
相关产品推荐
相关产品推荐

