为了做这个通用的数据库组件,专门安装了虚拟机来安装各种版本的不同类型的数据库做测试,包括编译对应的数据库插件,我一直坚信的是一切从实际出发+有实际采用发言权,包括不同Qt版本编译mysql、oracle数据库插件,以及不同数据库在查询数据、分页显示数据、清空数据、批量插入数据、批量删除数据等操作,经历过至少半个月的疯狂实战,光mysql就安装了5.7和8.0版本,postgresql安装了9.0、10.0、13.0三个版本,oracle安装了11.0,国产数据库安装了kingbaseV8,反复将物联网管理平台、视频监控系统、可视化大屏电子看板系统,在各种数据库下进行测试,优化了大大小小上百个细节,而且同时支持直连数据库和ODBC数据源方式连接数据库两种形式,比如通过oci库搭配qoci插件连接oracle数据库、通过odbc数据源搭配qodbc插件连接oracle数据库,才形成现在的版本。
通用的数据库组件基本功能:






void frmConfigDb::on_btnConnect_clicked()
{
{
//初始化数据库连接信息结构体数据
DbInfo dbInfo;
initDbInfo(dbInfo, connName);
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()
{
if (QUIHelper::showMessageBoxQuestion("确定要初始化数据库吗? 会全部清空数据并且不可还原!") != QMessageBox::Yes) {
return;
}
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;
initDbInfo(dbInfo, connName);
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);
}