• 1725阅读
  • 2回复

Qt数据库应用10-通用数据库设置 [复制链接]

上一主题 下一主题
离线liudianwu
 

图酷模式  只看楼主 倒序阅读 楼主  发表于: 2022-02-11

## 一、前言
做过了众多的项目,只要是用了数据的项目,基本上还有一个需求就是,提供一个界面用来初始化数据库,相当于恢复出厂设置的数据,一般恢复出厂设置需要复位的是配置文件、数据库文件、布局文件这几种,其中配置文件和布局文件都可以通过先删除原来的文件,然后重新生成新的默认文件来实现,而数据库文件只有sqlite才是文件型的数据库,也可以采用类似方式,比如将出厂设置的数据库文件放在资源文件,需要恢复的时候,先删除原来的文件,然后从资源文件拷贝出来对应的文件即可,同时还要赋值可读写权限。

数据库恢复出厂,还有一种更好的办法就是通过执行sql语句去实现,这种办法通用各种数据库,比如sqlite、mysql、posggres等,都可以通过执行sql语句的方式,新建数据库以及重置数据库表,基本上的流程都是:删除数据库(有些不能通过删除数据库形式的则删除数据库表)、新建数据库、新建数据库表、添加初始数据。经过这几个步骤以后,就会生成一个全新的干干净净的按照自己要求的数据库。有些时候,我们不想去挨个界面中删除清空数据,直接来个恢复出厂,清清爽爽干干净净的一步到位。

既然是通用的数据库设置,那当然需要支持多种数据库了,整个轮子组件还是花了点心思的,完善了很多年,在各种项目中验证和迭代,形成现在的规范。数据库的相关参数就几个:数据库类型、数据库地址、数据库端口、数据库名称、数据库用户名称、数据库用户密码、其中数据库地址既可以填网址也可以填IP地址。这些参数可以直接在界面上选择,选择好以后存储到配置文件,同时还提供检测连接功能,测试下填入的信息是否正确,默认还可以自动填入数据库的端口,比如mysql是3306,sqlserver是1433。

## 二、功能特点
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. 体验地址:[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/)

## 四、效果图



## 五、相关代码
```cpp
void frmConfigDb::on_btnConnect_clicked()
{
    {
        DbInfo dbInfo;
        dbInfo.connName = connName;
        dbInfo.dbName = AppConfig::LocalDbName;
        dbInfo.hostName = AppConfig::LocalHostName;
        dbInfo.hostPort = AppConfig::LocalHostPort;
        dbInfo.userName = AppConfig::LocalUserName;
        dbInfo.userPwd = AppConfig::LocalUserPwd;

        QString dbType = AppConfig::LocalDbType.toUpper();
        if (dbType == "SQLITE") {
            dbInfo.dbName = DbHelper::getDbDefaultFile(connFlag);
            if (QFile(dbInfo.dbName).size() <= 4) {
                QUIHelper::showMessageBoxError("数据库文件不存在!", 5);
                return;
            }
        }

        QSqlDatabase database;
        if (DbHelper::initDatabase(true, dbType, database, dbInfo)) {
            if (database.open()) {
                database.close();
                QUIHelper::showMessageBoxInfo("打开数据库成功!", 3);
            } else {
                QString error = database.lastError().text();
                QUIHelper::showMessageBoxError("打开数据库失败!\n" + error, 3);
            }
        } else {            
            QString error = database.lastError().text();
            QUIHelper::showMessageBoxError("连接数据库失败!\n" + error, 3);
        }
    }

    QSqlDatabase::removeDatabase(connName);
}

void frmConfigDb::on_btnInit_clicked()
{
    QString sqlName = QString("%1/db/%2.sql").arg(QUIHelper::appPath()).arg(connFlag);
    QFile file(sqlName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QUIHelper::showMessageBoxError("数据库脚本文件打开失败!", 3);
        return;
    }

    QElapsedTimer time;
    time.start();
    {
        DbInfo dbInfo;
        dbInfo.connName = connName;
        dbInfo.dbName = AppConfig::LocalDbName;
        dbInfo.hostName = AppConfig::LocalHostName;
        dbInfo.hostPort = AppConfig::LocalHostPort;
        dbInfo.userName = AppConfig::LocalUserName;
        dbInfo.userPwd = AppConfig::LocalUserPwd;

        QString dbType = AppConfig::LocalDbType.toUpper();
        if (dbType == "SQLITE") {
            dbInfo.dbName = DbHelper::getDbDefaultFile(connFlag);
            //如果文件存在则先删除原来的数据库文件,貌似win上不行
            QFile file(dbInfo.dbName);
            if (file.exists()) {
                bool ok = file.remove();
                if (!ok) {
                    qDebug() << TIMEMS << "remove error" << dbInfo.dbName;
                }

                //清空所有表
                QStringList tables = QSqlDatabase::database().tables();
                foreach (QString table, tables) {
                    DbHelper::clearTable(table, dbType);
                    qDebug() << TIMEMS << "clearTable" << table;
                }

                //关闭默认数据库连接
                QSqlDatabase::database().close();
            }
        }

        //初始化数据库连接并打开数据库
        QSqlDatabase database;
        if (!DbHelper::initDatabase(true, dbType, database, dbInfo)) {
            QString error = database.lastError().text();
            QUIHelper::showMessageBoxError("连接数据库失败!\n" + error, 3);
            return;
        }
        if (!database.open()) {
            QString error = database.lastError().text();
            QUIHelper::showMessageBoxError("打开数据库失败!\n" + error, 3);
            return;
        }

        QSqlQuery query(QSqlDatabase::database(connName));

        //第一步:删除原有数据库
        QString sql = QString("DROP DATABASE %1;").arg(dbInfo.dbName);
        qDebug() << TIMEMS << "sql:" << sql << "result:" << query.exec(sql);

        //第二步:新建数据库
        sql = QString("CREATE DATABASE %1;").arg(dbInfo.dbName);
        qDebug() << TIMEMS << "sql:" << sql << "result:" << query.exec(sql);

        //第三步:切换到新建的数据库库并执行建表语句
        database.close();
        if (!DbHelper::initDatabase(false, dbType, database, dbInfo)) {
            QString error = database.lastError().text();
            QUIHelper::showMessageBoxError("连接数据库失败!\n" + error, 3);
            return;
        }
        if (!database.open()) {
            QString error = database.lastError().text();
            QUIHelper::showMessageBoxError("打开数据库失败!\n" + error, 3);
            return;
        }

        //将执行出错的sql语句输出到文件方便查看
        QString fileName2 = QString("%1/db/error.sql").arg(QUIHelper::appPath());
        QFile file2(fileName2);

        QSqlQuery query2(QSqlDatabase::database(connName));

        sql = "BEGIN;";
        qDebug() << TIMEMS << "sql:" << sql << "result:" << query2.exec(sql);

        while (!file.atEnd()) {
            sql = QString::fromUtf8(file.readLine());
            sql.replace("\n", "");

            //有些数据库不支持的语句跳过去
            if (DbHelper::existNoSupportSql(sql)) {
                continue;
            }

            //重新纠正sql语句
            DbHelper::checkSql(dbType, sql);

            if (!query2.exec(sql)) {
                //打印及输出错误信息
                QString error = query2.lastError().text();
                qDebug() << TIMEMS << "sql:" << sql << error;

                //没打开则先打开
                if (!file2.isOpen()) {
                    file2.open(QFile::WriteOnly | QFile::Append);
                }

                QString msg = QString("时间[%1]  语句: %2  错误: %3\n").arg(DATETIME).arg(sql).arg(error);
                file2.write(msg.toUtf8());
            }
        }

        sql = "COMMIT;";
        qDebug() << TIMEMS << "sql:" << sql << "result:" << query2.exec(sql);
        database.close();

        //sqlite数据库的话再执行下压缩减少体积
        if (dbType == "SQLITE") {
            DbHelper::execSql("VACUUM;");
        }
    }

    QSqlDatabase::removeDatabase(connName);
    double ms = time.elapsed();
    QString info = QString("数据库脚本执行成功,总共用时 %1 秒!\n记得重新启动程序!").arg(QString::number(ms / 1000, 'f', 1));
    QUIHelper::showMessageBoxInfo(info, 3);
}
```
4条评分好评度+1贡献值+1金钱+10威望+1
20091001753 好评度 +1 - 2022-02-11
20091001753 贡献值 +1 - 2022-02-11
20091001753 威望 +1 - 2022-02-11
20091001753 金钱 +10 - 2022-02-11
欢迎关注微信公众号:Qt实战/Qt入门和进阶(各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发) QQ:517216493  WX:feiyangqingyun  QQ群:751439350
离线uidab

只看该作者 1楼 发表于: 2022-02-11
    
有时候为了工作直接获得答案,而我却失去了思考的乐趣!


飘啊飘,何时能安居!
离线shawking

只看该作者 2楼 发表于: 2022-02-11
为这个程序必须给刘大师点个赞啊!!!
快速回复
限100 字节
 
上一个 下一个