找回密码
 立即注册
收起左侧

关于Qt数据库相关开发的一些经验总结

2
回复
6397
查看
[复制链接]
累计签到:7 天
连续签到:1 天
来源: 2021-7-23 11:53:18 显示全部楼层 |阅读模式
### 一、前言
近期花了两个多月时间,将数据库相关的代码重新封装成了各种轮子(这条路必须打通,打通以后,相关项目只需要引入这个组件pri即可),测试了从Qt4.7到Qt6.1的各种版本,测试了odbc、sqlite、mysql、postgresql、sqlserver、oracle、人大金仓等数据库,测试了本地连接、远程连接、阿里云连接等,测试了windows、linux、mac等系统,将所有项目数据库相关的代码全部更新了一遍。能够兼容这么多Qt版本和数据库插件以及测试验证系统,估计全网也没几个人,全国11W Qter开发者中应该也是最多不超过10人。


### 二、数据库开发经验总结
- 在数据库相关的应用中,如果仅仅是单机版本,没有特别的需要(比如领导指定,或者需要远程存放数据),强烈建议使用sqlite数据库,这是本人经过无数次的对比测试和N个商业项目应用得出的结论。
- Qt天生内置了sqlite数据库,只需要发布的时候带上插件就行(可以看到插件动态库文件比其他几种都要大,那是因为直接将数据库的源码都编译进去了,而其他只编译了中间通信交互的插件源码),其他数据库要么还要带上动态库,要么还需要创建数据源;
速度上,绝对无与伦比的出类拔萃,同样的数据库结构(表结构、索引等完全一致),查询速度和批量更新速度、数据库事务等,速度都是其他几种的至少3倍以上,而且随着数据量的增大对比越发明显;
- 几千万的数据量完全没问题,而且速度和性能都还可以,不要以讹传讹网上部分菜鸡说的不支持百万以上的数据量,本人亲测亿级别,数据量建议千万级别以下,着重注意数据库表和索引的设计;
- 其他数据库还要注意版本的区别,ODBC数据源形式还容易出错和执行失败;
- sqlite数据库也有几个重大缺点:不支持加密,不支持网络访问,不支持部分数据库高级特性,不支持海量数据(亿级别以上),但是对于绝大部分Qt项目还是足够;
- 数据库支持友好度大致是 sqlite > postgresql > mysql > odbc ;
- 以上都是在Qt环境中个人测试得出的结论,结果未必正确,作为参考即可,其他编程环境比如C#、JAVA请忽略,也许差别可能在中间通信的效率造成的;
### 三、数据库连接说明
1. Qt5默认提供的数据库插件包括了QSQLITE、QMYSQL、QPSQL、QODBC四种,后期版本比如5.12开始把mysql也移除了(可能是因为开源协议的问题),其中驱动打印中还有个QMYSQL3是表示mysql3旧版本,现在默认一般都mysql5以上,QPSQL7表示postgres7旧版本,现在默认一般都postgres9以上。
2. 根据字面意思很容易理解QSQLITE用来连接sqlite数据库,QMYSQL用来连接mysql数据库,QPSQL用来连接postgres数据库,QODBC理论上可以用来连接任何支持ODBC数据源的数据库,比如access、sqlserver、mysql、postgres、oracle等。
3. Qt4默认提供的数据库插件只有QSQLITE、QODBC两种,因为QODBC理论上可以用来连接任何支持ODBC数据源的数据库,只是通过了微软的数据源中间件,效率上可能会有损耗,所以在Qt5又新增了其他几个常用数据库的插件比如QMYSQL、QPSQL,而其他数据库由于协议的要求并没有提供对应的插件需要自行编译比如oracle。
4. Qt内置了sqlite数据库,可以观察到qsqlite4.dll文件大小明显比其他数据库插件大很多,理论上光一个插件应该小很多才对,毕竟sqlite属于小型数据库,所以肯定是将sqlite的源码直接编译到插件了,所以我们在使用sqlite数据库的时候无需带一个sqlite.dll,而使用mysql则需要带上libmysql.dll。
5. 使用mysql、postgres等支持远程访问的数据库的时候,并不需要本地安装数据库,只需要发布程序的时候带上对应数据库的动态库即可,比如mysql对应带上libmysql.dll即可,这样程序指定数据库主机地址就可以连接上,比如阿里云的mysql、postgres等云端数据库。
6. mysql、posgrest等支持远程连接的数据库,默认安装以后出于安全性考虑只支持本地访问,需要做设置才能支持远程访问,mysql需要增加用户root@%即主机设置为%,postgres需要打开安装目录下的C:\PostgreSQL\10\data\pg_hba.conf文件,增加一行 host all all 192.168.1.0/24 md5 表示支持192.168.1.1到192.168.1.255的IP访问,同时将C:\PostgreSQL\10\data\postgresql.conf改成listen_addresses = '*'表示支持所有地址,具体这个的含义可以自行搜索。
7. mysql数据库通信的默认端口是3306,postgres的是5432,这些端口都可以在安装的时候或者后期更改。
8. 数据库也有位数的区别,比如你连接的是64位的数据库那就需要用64位的Qt以及64位的数据库插件和对应的动态库文件,位数一定要完全一致才行,否则连不上,很多人会在这个地方摔一跤。除了位数的区别可能还要注意版本的区别,毕竟数据库一直在更新升级换代,有些版本变动比较大,未必Qt发布版本的时候对应就支持最新的数据库,所以一般建议用稍微老一点的数据库版本,比如mysql本人一直用5.6,测试到现在Qt5.13版本都支持。
9. 一般的软件默认都只需要连接一个数据库,所以建议直接在程序启动以后就打开好数据库,然后其他需要用到数据库的地方就执行即可,最后程序关闭的时候关闭数据库。很多初学者每次增删改查都打开数据库执行完成操作以后然后关闭数据库,这样效率极其低下。如果需要连接多个数据库,则以数据库连接名称作为区分,Qt支持同时多个数据库连接的,数据库跨线程不安全,要加锁,所以建议在哪个线程使用到的数据库就在那个线程中打开,而不要主线程打开数据库子线程使用数据库,很可能会出问题。Qt5.10开始增加了数据库跨线程使用的安全性检查,运行时会打印提示。
10. 创建数据库、创建表、创建索引、初始化数据等这些都可以通过执行sql语句来实现,强烈建议在对常用的数据量比较多的表创建表的时候要创建索引,在大量的数据查询更新操作的时候先启动数据库事务,执行完成以后提交数据库事务。


### 四、数据库操作流程代码
```c++
void MainWindow::testDb()
{
    //打印当前Qt对应支持的数据库驱动名称
    qDebug() << QSqlDatabase::drivers();


    //创建数据库对象,驱动名称根据打印的填写,"QSQLITE", "QMYSQL", "QMYSQL3", "QODBC", "QODBC3", "QPSQL", "QPSQL7"
    QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
    //设置数据库参数,要查看Qt文档是否支持该数据库,一般建议默认的就好不用设置
    //db.setConnectOptions("MYSQL_OPT_RECONNECT=1;MYSQL_OPT_CONNECT_TIMEOUT=1;");
    //设置数据库的主机地址
    db.setHostName("127.0.0.1");
    //设置数据库通信端口,默认值  mysql:3306  postgres:5432  sqlserver:1433
    db.setPort(5433);
    //设置数据库名称,默认值  mysql:mysql  postgres:postgres  sqlserver:master
    //如果是sqlite数据库只需要设置这个参数即可,其余参数都不用设置,因为sqlite不需要主机端口和用户
    //参数内容为数据库文件的路径 db.setDatabaseName("c:/test.db");
    db.setDatabaseName("postgres");
    //设置登录用户名称,默认值  mysql:root  postgres:postgres  sqlserver:sa
    db.setUserName("postgres");
    //设置登录用户密码
    db.setPassword("admin");
    //打开数据库,如果失败打印错误信息
    if (!db.open()) {
        qDebug() << db.lastError();
        return;
    }


    //执行增删改查
    //常规查询语句
    QString sql = "select UserName,UserPwd from UserInfo";
    //带条件+排序+分组的查询语句
    sql = "select UserName,UserPwd from UserInfo where UserName='admin' order by UserName asc group by UserGroup";


    //构建查询对象,传入sql语句查询,可以先判断执行成功与否再来取值
    QSqlQuery query;
    if (query.exec(sql)) {
        //循环取出所有查询结果,对应结果是QVariant类型可以自行to到其他类型
        while(query.next()) {
            qDebug() << query.value(0).toString() << query.value(1).toString();
        }
    }


    //添加数据,拼接字符串的形式比较通用,还有占位符的形式
    sql = "insert into UserInfo(UserName,UserPwd) values('ceshi', '12345')";
    //删除数据,如果不加where条件则表示删除整个表的数据
    sql = "delete from UserInfo where UserName='ceshi'";
    //更新数据,如果不加where条件则表示更新整个表的数据
    sql = "update UserInfo set UserPwd='admin123' where UserName='ceshi'";


    //可以复用上面的QSqlQuery对象,也可以重新new,复用的话需要先调用clear
    query.clear();
    //添加+删除+更新 数据只需要知道执行成功与否就行
    if (!query.exec(sql)) {
        qDebug() << "执行sql语句失败";
    }


    //关闭数据库,程序自动关闭的时候也会关闭,所以只是用一个数据库的情况下无需手动关闭
    db.close();
}
```
### 五、数据库综合应用组件
#### (一)功能特点
1. 同时支持多种数据库比如odbc、sqlite、mysql、postgresql、sqlserver、oracle、人大金仓等。
2. 一个数据库类即可管理本地数据库通信,也支持远程数据库通信等。
3. 数据库线程支持执行各种sql语句,包括单条和批量。
4. 组件中的所有类打印信息、错误信息、执行结果都信号发出去。
5. 集成数据库通用翻页类(负责具体处理逻辑),搭配分页导航控件(负责外观),形成超级牛逼的翻页控件。
6. 集成数据库自动清理类,设定最大记录数后台自动清理早期数据。
7. 集成自定义委托类,支持复选框、文本框、下拉框、日期框、微调框、进度条等。
8. 同时支持Qt4-Qt6,亲测Qt4.6到Qt6.1任意版本,任意系统和编译器。
9. 本组件无故障 360天7乘24小时 运行在至少上万个现场,商业级别品质保证。
10. 每个类都对应完整详细的使用示例,注释详细,非常适合阅读学习。
11. 可以作为独立的程序运行,比如自动清理早期数据,同步数据到云端。
12. 全部线程处理,不卡界面,自动重连数据库。
13. 普通测试情况,sqlite数据库,数据库发生器每秒钟插入1000条记录约0.003秒钟,同时自动清理数据类每秒钟删除1000条记录约0.13秒,不同线程互不干扰。


#### (二)数据库通信管理线程类
1. 可设置数据库类型,支持多种数据库类型。
2. 数据库类型包括但不限于odbc、sqlite、mysql、postgresql、sqlserver、oracle、人大金仓等。
3. 可设置数据库连接信息包括主机地址、用户信息等。
4. 具有自动重连机制,可设置是否检查连接以及检查间隔。
5. 支持单条sql语句队列,一般用于查询返回数据,每次插入一条执行一条。
6. 支持多条sql语句队列,一般用于远程提交数据,每次插入一条执行多条。
7. 支持批量sql语句队列,一般用于批量更新数据,每次插入多条执行多条。
8. 可设置队列最大数量,限定排队处理的sql语句集合。
9. 通过信号发出 打印信息、错误信息、查询结果。


#### (三)数据库通用翻页类
1. 可设置每页多少行记录,自动按照设定的值进行分页。
2. 可设置要查询的表名、字段集合、条件语句、排序语句。
3. 可设置第一页、上一页、下一页、末一页、翻页按钮。
4. 可设置当前页、总页数、总记录数、每页记录数、查询用时标签页。
5. 多线程查询总记录数,数据量巨大时候不会卡主界面。
6. 建议条件字段用整型类型的主键,速度极快。
7. 提供查询结果返回信号,包括当前页、总页数、总记录数、查询用时等信息。
8. 可设置所有列或者某一列对齐样式例如居中或者右对齐。
9. 可增加列用于标识该条记录,设定列的位置、标题、宽度。
10. 提供函数直接执行第一页、上一页、下一页、末一页。
11. 提供函数直接跳转到指定页。
12. 根据是否第一页、末一页自动禁用对应的按钮。
13. 本控件是翻页功能类,和翻页控件navpage完美搭配,形成超级牛逼的翻页控件。


#### (四)分页导航控件
1. 可设置页码按钮的个数。
2. 可设置字体大小。
3. 可设置边框圆角角度、大小、颜色。
4. 可设置正常状态背景颜色、文字颜色。
5. 可识别悬停状态背景颜色、文字颜色。
6. 可设置按下状态背景颜色、文字颜色。
7. 可设置选中状态背景颜色、文字颜色。
8. 可设置导航位置居中对齐、左对齐、右对齐。
9. 可设置是否显示提示标签控件。
10. 自动计算总页码数显示隐藏多余按钮。
11. 自动计算切换页码导航。
12. 和分页导航功能类无缝对接完美融合。


#### (五)自动清理数据线程类
1. 可设置要清理的对应数据库连接名称和表名。
2. 可设置条件字段。
3. 可设置排序字段。
4. 可设置最大保留的记录数。
5. 可设置执行自动清理的间隔。
6. 后期支持多个数据库和多个表。
7. 建议条件字段用数字类型的主键,速度极快。
8. 增加统计用字段名称设置。
9. 增加自动清理文件夹,超过大小自动删除文件夹中早期文件。


#### (六)自定义委托全家桶
1. 可设置多种委托类型,例如复选框、文本框、下拉框、日期框、微调框、进度条等。
2. 可设置是否密文显示,一般用于文本框。
3. 可设置是否允许编辑,一般用于下拉框。
4. 可设置是否禁用,一般用来禁用某列。
5. 可设置数据集合,比如下拉框数据集合。
6. 提供值变化信号,比方说下拉框值改动触发。
7. 可设置数据校验自动产生不同的图标。
8. 支持设置校验列、校验规则、校验值、校验成功图标、校验失败图标、图标大小。
9. 可设置校验数据产生不同的背景颜色和文字颜色。
10. 校验规则支持 == > >= < <= != contains,非常丰富。
11. 复选框自动居中而不是左侧,切换选中状态发送对应的信号。
12. 可设置颜色委托,自动根据颜色值绘制背景颜色,自动设置最佳文本颜色。
13. 可设置按钮委托,自动根据值生成多个按钮,按钮按下发送对应的信号。
14. 当设置了委托列时自动绘制选中背景色和文字颜色。
15. 可设置关键字对照表绘制关键字比如原始数据是 0-禁用 1-启用。
16. 可设置复选框对应的映射选中不选中关键字。
17. 根据不同的委托类型绘制,可以依葫芦画瓢自行增加自己的委托。
18. 所有功能封装成1个类,核心代码不到500行,使用极其方便友好。


#### (七)效果图



#### (八)体验地址
1. 体验地址:[https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A](https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A) 提取码:o05q  文件名:bin_dbtool.zip
2. 国内站点:[https://gitee.com/feiyangqingyun](https://gitee.com/feiyangqingyun)
3. 国际站点:[https://github.com/feiyangqingyun](https://github.com/feiyangqingyun)
4. 个人主页:[https://blog.csdn.net/feiyangqingyun](https://blog.csdn.net/feiyangqingyun)
5. 知乎主页:[https://www.zhihu.com/people/feiyangqingyun/](https://www.zhihu.com/people/feiyangqingyun/)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

累计签到:129 天
连续签到:42 天
2021-7-24 13:38:27 显示全部楼层
产品用了sqlite3,后期如果表结构变化了,软件升级后,怎么优雅的升级一下用户的表结构呢~
回复 支持 反对

使用道具 举报

累计签到:27 天
连续签到:1 天
2021-7-30 16:09:46 显示全部楼层
UncleV 发表于 2021-7-24 13:38
产品用了sqlite3,后期如果表结构变化了,软件升级后,怎么优雅的升级一下用户的表结构呢~  ...

修改表结构SQL加到程序里,启动执行,或者版本号不一样更新
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了