1. 从“会用”到“用好”:QSqlQuery到底解决了什么问题
Qt的数据库模块里,QSqlQuery是绕不开的主角。前面两篇咱们聊过Qt SQL模块的整体架构,以及QSqlDatabase怎么建立连接,那篇结尾我留了个钩子——连接建好了,数据怎么读写?答案就是QSqlQuery。
简单说,QSqlQuery就是Qt帮你封装好的SQL执行器。它的定位很朴素:把SQL语句发给数据库,把结果拿回来。你可能会问,这玩意儿跟我直接写个ODBC或者调用MySQL C API有什么区别?区别在于,QSqlQuery屏蔽了不同数据库驱动的细节,你在Windows上写的代码,换到Linux上编译一遍就能跑,底层从ODBC换成MYSQL驱动,上层代码一行不用改。这个跨数据库能力,才是Qt数据库模块真正的价值所在。
这篇文章我不打算照着官方文档念一遍API,那玩意儿你自己也能翻。我重点讲的是实际开发里怎么用、什么时候该用prepare、事务怎么处理、还有那些文档里不会写的坑。内容主要面向已经把QSqlDatabase连接配好、想进一步掌握数据读写操作的开发者。如果你是完全零基础,建议先把前面两篇看了再来,不然有些上下文你接不上。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心细节解析与实操要点:QSqlQuery的查询模式
2.1 exec()、prepare()与bindValue():三种基本操作方式
QSqlQuery的查询模式就三种:直接exec一条完整SQL、prepare预处理后绑定参数执行、以及通过bindValue动态传值。这三种写法各有适用场景,我一个个说。
第一种,直接exec完整SQL语句,最粗暴,也最直观:
cpp复制QSqlQuery query;
query.exec("SELECT * FROM user WHERE age > 18");
这种写法适合SQL语句完全固定、不含任何外部输入变量的场景。比如程序启动时加载常量配置表,或者某个固定条件的统计查询,一行exec就完事,代码最简洁。它的局限性也很明显——SQL语句是写死的,没法根据程序运行状态动态调整条件。
第二种,prepare预处理。这种模式我强烈推荐在实际项目里大面积使用:
cpp复制QSqlQuery query;
query.prepare("SELECT * FROM user WHERE age > ? AND city = ?");
query.addBindValue(18);
query.addBindValue("深圳");
query.exec();
?是占位符,后续通过addBindValue按顺序绑定值。prepare的好处有三个:第一,SQL语句结构固定,数据库可以对它做预编译和缓存,重复执行时效率高;第二,参数和SQL语句分开传递,从根本上杜绝了SQL注入,这个在接收用户输入的场景里是生死攸关的;第三,代码可读性好,语句结构和数据彻底分离。
第三种,bindValue带具名占位符。注意,这里有两种写法:
cpp复制// 写法一:冒号+名字,通过名字绑定
query.prepare("SELECT * FROM user WHERE age > :age AND city = :city");
query.bindValue(":age", 18);
query.bindValue(":city", "深圳");
query.exec();
// 写法二:省略冒号,直接写名字
query.prepare("SELECT * FROM user WHERE age > :age AND city = :city");
query.bindValue("age", 18);
query.bindValue("city", "深圳");
query.exec();
两种写法都合法,bindValue会自动匹配。我的经验是统一用一种风格,别混着来,不然代码review的时候真心累。具名占位符还有个优势,当你需要多次绑定同一个参数时,或者参数顺序经常调整时,代码的健壮性会好很多,不容易出现参数错位。
2.2 查询结果怎么取:size()、next()、value()与记录的遍历
查询执行完,结果就在QSqlQuery对象内部维护的一个结果集里,取数据靠的是那几个经典方法。先说新手最容易犯的错——以为query有size()就能直接拿来当行数用。
cpp复制QSqlQuery query;
query.exec("SELECT * FROM user");
int rowCount = query.size(); // 危险操作
size()这玩意儿,文档里写得明明白白:只在某些数据库里可靠。实测下来,SQLite驱动基本能返回正确结果,但MySQL驱动在某些情况下返回-1,说明它自己都不知道有多少行。所以千万别把size()当成通用API来依赖。
正确的遍历姿势是这样的:
cpp复制QSqlQuery query;
query.exec("SELECT id, name, age FROM user WHERE age > 18");
while (query.next
