前言
数据存储这块儿不管是B/S还是C/S都是少不了的,而qt中直接有现成的链接数据库的类,当然适配的应该不至于跟Orm一样花里胡哨,常规的Sqllite,SqlServer,Mysql啥的是没问题的。
Qt连接数据库
qt支持的数据库个人常用的驱动名称如下:
| 驱动类别 | 对应数据库 |
|---|
| QSQLITE | SQLLITE3以上 |
| QODBC | SQLSERVER |
| QMYSQL | MYSQL |
配置工程
首先来看下如何配置工程,QCreator的话在pro文件中找到QT += 这句。
vs的话,类似,在工程属性页对应下图配置。

连接
接下来就可以来实现连接了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #pragma once #include <iostream> #include <vector> #include <QString> #include <QtSql/QSqlDatabase> #include <QtSql/QtSql> #include <QtSql/QSqlQuery>
class CSqlLiteManager { public: CSqlLiteManager(); ~CSqlLiteManager();
bool connectToDb(); bool isConnected();
private: QSqlDatabase m_connect; bool m_isConnected = false; };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| bool CSqlLiteManager::connectToDb() { if (QSqlDatabase::contains("qt_sql_default_connection")) { m_connect = QSqlDatabase::database("qt_sql_default_connection"); } else { m_connect = QSqlDatabase::addDatabase("QSQLITE"); } if (!m_connect.isValid()) { std::cout << "connect sqllite failed" << std::endl; return false; } m_connect.setDatabaseName("test.db"); if (!m_connect.open()) { std::cout << "open sqllite failed" << std::endl; return false; } }
|
调用这个类然后运行程序,不出意外的话会有如下提示(当然如果已经配置了环境变量之类的可能会直接成功):

找到qt类库目录,然后从plugins拷走sqldrivers到运行程序目录下,对应层级设置好(个人习惯在exe同层新建plugins然后放置对应qt的插件库)。
在main.cpp中添加读取插件库的方法。
1 2 3 4 5 6 7 8 9 10 11
| int main(int argc, char *argv[]) { QApplication a(argc, argv); QString strLibPath = a.applicationDirPath(); strLibPath += "/plugins/"; a.addLibraryPath(strLibPath); QtDemo w; w.show(); return a.exec(); }
|
再运行程序就可以看到对应目录下有个test.db这个数据库。
建表
新建的数据库里肯定都是空的,所以需要来张表操作下,可以通过数据库管理工具(SQLLite Expert啊,Navicat之类的),这里用的sqllite就直接建个表了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void CSqlLiteManager::createTable() { QSqlQuery query; query.exec(QString("select count(1) from sqlite_master where type='table' and name='person'")); if (query.next()) { if (query.value(0).toInt() == 0) { QString create_sql = "create table person (id integer primary key autoincrement, number varchar(10), name varchar(20), utype int, addtime datetime default (datetime('now', 'localtime')))"; query.prepare(create_sql); if (!query.exec()) { std::cout << "create table failed " << query.lastError().text().toStdString() << endl; } } } else { std::cout << "select table failed " << query.lastError().text().toStdString() << std::endl; } }
|
通过QSqlQuery.lastError() 可以获取到执行的错误信息,方便排查问题。
运行程序,然后我们通过个工具来看下是否建表完成。

增删改查
所有的数据相关离不开增删改查,crud工程师的必备技能。
这部分基本上直接上代码了,对应的扩展可以自行臆想。
新增
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| bool CSqlLiteManager::insertData() { bool ret = false; QSqlQuery query; QString sql = QString("insert into person (%1) values('%2','%3',%4,'%5')") .arg("`number`,`name`,`utype`,`addtime`") .arg("100001") .arg("test01") .arg(1) .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); if (query.exec(sql)) { ret = true; } else { std::cout << "insertData error: " << query.lastError().text().toStdString() << std::endl; } return ret; }
|
修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| bool CSqlLiteManager::updateData() { bool ret = false; QSqlQuery query; QString sql = QString("update person set `name`='test0002' where id=%1") .arg(1); if (query.exec(sql)) { ret = true; } else { std::cout << "updateData error: " << query.lastError().text().toStdString() << std::endl; } return ret; }
|
删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| bool CSqlLiteManager::deleteData() { bool ret = false; QSqlQuery query; QString sql = QString("delete from person where id=%1") .arg(2); if (query.exec(sql)) { ret = true; } else { std::cout << "deleteData error: " << query.lastError().text().toStdString() << std::endl; } return ret; }
|
查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| std::vector<DbViewItem> CSqlLiteManager::getData() { std::vector<DbViewItem> data; QSqlQuery query(m_connect); QString sql = QString("select * from person order by addtime desc"); if (query.exec(sql)) { while (query.next()) { DbViewItem item; item.id = query.value("id").toInt(); item.number = query.value("number").toString(); item.name = query.value("name").toString(); item.utype = query.value("utype").toInt(); item.addtime = query.value("addtime").toDateTime(); data.push_back(item); } } return data; }
|

多线程使用
如果在多线程中,为了保持单连接,要用到单例(设计模式这块儿后续有时间再缕),当然也可以一直做短连接,连接->执行->断开,不过一般不习惯这种操作,直接一个长连接保持就行。
在执行sql时进行锁操作,防止多个线程调用同一资源造成的冲突。
1 2 3 4 5 6
| #include <mutex>
private: std::mutex lock_mutex;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| bool CSqlLiteManager::insertData() { bool ret = false; lock_mutex.lock(); QSqlQuery query; QString sql = QString("insert into person (%1) values('%2','%3',%4,'%5')") .arg("`number`,`name`,`utype`,`addtime`") .arg("100001") .arg("test01") .arg(1) .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); if (query.exec(sql)) { ret = true; } else { std::cout << "insertData error: " << query.lastError().text().toStdString() << std::endl; } lock_mutex.unlock(); return ret; }
|
多个数据库
有些业务比较复杂的,可能会用到多个/多类数据库,如本地sqllite,远程mysql之类的,在一个工程里如果使用多个数据库,要留意几个问题,一个是QSqlQuery的声明,另一个是QSqlDatabase::addDatabase() 。
1 2
| m_connect = QSqlDatabase::addDatabase("QSQLITE","localdb"); QSqlQuery query(m_connect);
|
相当于是谁的执行用谁的连接,不指定就按照默认的连接(也就是第一个连接),如果工程只有一个数据库连接那就不需要再这么麻烦,当然写上最好,毕竟规范嘛。
小结
大体上数据库相关的操作基本上基础的就是这些,像分页啊查询条件啊聚合啊等等都是看看sql教程就行了,程序这块儿没有啥太多其他的操作,不过有些像数据绑定界面列表这种操作,因人而异吧,个人比较习惯自己写个结构体来接收然后自解析做绑定,后续有新的体会了会再更新调整。