一種通用的Qt數據庫接口操作方法
自己開發了一個股票軟件,功能很強大,需要的點擊下面的鏈接獲取:
https://www.cnblogs.com/bclshuai/p/11380657.html
目錄
1 前言... 1
2 QT環境下數據庫配置... 2
2.1 開發環境... 2
2.2 配置工作... 2
2.3 數據庫驅動配置... 2
2.4 創建數據庫增加數據庫表格... 3
2.5 包含頭文件和庫文件... 3
2.6 初始化數據庫... 4
2.7 打開數據庫... 5
3 數據庫增刪改查操作... 5
3.1 常規增刪改查操作... 5
3.1.1 插入數據... 5
3.1.2 更新數據... 6
3.1.3 查詢數據... 7
3.1.4 刪除數據... 10
3.2 基於模板的數據庫操作... 11
3.2.1 增刪改查接口說明... 11
3.2.2 增刪改查接口實現... 13
3.3 對比總結... 19
1 前言
通常對數據庫的操作都是根據具體的表格和具體的條件編輯SQL語句,然後調用SQL語句去操作數據庫。對於固定的條件編寫SQL語句很方便,但是對於數據查詢,通常根據用戶動態輸入的條件去組織SQL,這就要制定一定的策略規則去組織SQL語句,實現一個通用的數據查詢接口,只要將條件按照一定的格式輸入,就可以實現自動化的SQL語句編寫。本文主要講解QT環境下如何使用數據庫,介紹了常用的數據庫增刪改查操作方法。然後又介紹了一種通用的參數模板方法,定義一些規範和參數模板,使用者只需要按照要求輸入參數條件,就可以實現對不同表格的相同的操作。
2 QT環境下數據庫配置
2.1 開發環境
Qt5.9.6 vs2015,使用QSQLITE保存數據到本地數據庫文件。實現增刪改查的功能。
2.2 配置工作
2.3 數據庫驅動配置
將Qt安裝目錄下D:\QT\Qt5.9.6\5.9.6\msvc2015_64\plugins\的數據庫驅動文件夾sqldrivers複製到你的開發程序exe路徑下,如下圖所示。
2.4 創建數據庫增加數據庫表格
使用Sqlite工具DB Browser for SQLite.exe創建數據庫,並新建數據庫的表格。
2.5 包含頭文件和庫文件
2.6 初始化數據庫
db_ = QSqlDatabase::addDatabase("QSQLITE");
QString dbpath = QCoreApplication::applicationDirPath() + "/localSqliteDb";
db_.setDatabaseName(dbpath);
2.7 打開數據庫
if (!db_.open())
{
msg = tr("open local database fail");
break;
}
3 數據庫增刪改查操作
3.1 常規增刪改查操作
通常情況下我們操作數據庫時,會根據數據庫的字段名稱,數據庫表格名稱,數據庫字段值,篩選條件去組織SQL語句,如下所示的四種數據庫常規操作,動態的組織SQL語句,組織條件,需要做很多的條件判斷和篩選才能組織,非常麻煩。
3.1.1 插入數據
Query是數據庫請求,可以執行SQL語句,可以將數據組織成一個字符串,符合SQL的語法,就可以插入數據,也可以通過bindValue函數來綁定數值與SQL模板中的別名,例如綁定task中的數值name和SQL模板中的標籤:name,執行SQL語句時,就會用task中的數值name替換:name,通過exec函數來執行SQL語句。
int LocalDb::onSaveTask(QVariantMap & task,QString& strMsg)
{
int errorCode = -1;
do
{
QSqlQuery query;
query.prepare("INSERT INTO alarmTask (id, name,type,target,similarity,time,createtime) "
"VALUES ( :id,:name,:type,:target,:similarity,:time,:createtime)");
query.bindValue(":id", QUuid::createUuid());
query.bindValue(":name", task.value("name"));
query.bindValue(":type", task.value("type"));
query.bindValue(":target", task.value("target"));
query.bindValue(":time", task.value("time"));
query.bindValue(":similarity", task.value("similarity"));
query.bindValue(":createtime", task.value("createtime"));
if (!query.exec())
{
QSqlError error = query.lastError();
errorCode = error.type();
strMsg = error.text();
LOG_ERROR("insert task failed %s", strMsg.toStdString().c_str());
break;
}
errorCode = 0;
//創建圖片解析任務
} while (0);
return errorCode;
}
3.1.2 更新數據
更新數據採用update語句。同樣採用QSqlQuery方法
int LocalDb::onUpdateTask(QVariantMap & task, QString & strMsg)
{
int errorCode = -1;
do
{
QSqlQuery query;
query.prepare("update alarmTask set name=:name,type=:type,target=:target,similarity=:similarity,time=:time,createtime=:createtime where id=:id");
query.bindValue(":id", task.value("id"));
query.bindValue(":name", task.value("name"));
query.bindValue(":type", task.value("type"));
query.bindValue(":target", task.value("target"));
query.bindValue(":time", task.value("time"));
query.bindValue(":similarity", task.value("similarity"));
query.bindValue(":createtime", task.value("createtime"));
if (!query.exec())
{
QSqlError error = query.lastError();
strMsg = error.text();
errorCode = error.type();
LOG_ERROR("UpdateTask failed %s", strMsg.toStdString().c_str());
break;
}
errorCode = 0;
//創建圖片解析任務
} while (0);
return errorCode;
}
3.1.3 查詢數據
在使用查詢時,如果是多個條件,例如select * from vehicle where vehicletype in (: vehiclecolor),如果使用query.bindValue(":vehiclecolor ", task.value("vehiclecolor "));當vehiclecolor值爲多個時,例如‘紅’,‘黃’。如果用query.bindValue(":vehiclecolor", task.value("vehiclecolor"));綁定值,會發現執行語句時無效。只有用將顏色組織成字符串後纔有效,strCondition += QString(" vehiclecolor in (%1) AND").arg(strtemp);
void LocalDb::SlotQueryVehicleByCondition(int pageno, int pagesize, QMap<QString, QMap<QString, QString>> cond)
{
int errorCode = 0;
QString msg;
QString sqlcount = "SELECT count(*) from vehicle ";
QVariantMap bindvalue;
QString sqlserchpage = "SELECT * from vehicle ";//ORDER BY time DESC LIMIT :limit OFFSET :offset";
QString strCondition = "";
QString strtemp = "";
if (cond.size() > 0)
{
strCondition = "where";
//車輛類型
if (cond.contains("vehicletype"))
{
strtemp = GetStringCondition(cond["vehicletype"].keys());
strCondition += QString(" vehicletype in (%1) AND").arg(strtemp);
}
//車牌號碼
if (cond.contains("vehiclenum"))
{
strtemp = cond["vehiclenum"]["vehiclenum"];
strCondition += QString(" vehiclenum in ('%1') AND").arg(strtemp);
}
//特徵標識
if (cond.contains("mark"))
{
if (cond["mark"].contains("副駕駛"))
{
strCondition += QString(" visepilot in ('是') AND");
}
if (cond["mark"].contains("掛件"))
{
strCondition += QString(" hangthing in ('是') AND");
}
if (cond["mark"].contains("遮陽板"))
{
strCondition += QString(" hidesunplate in ('是') AND");
}
if (cond["mark"].contains("危險品"))
{
strCondition += QString(" danger in ('是') AND");
}
}
//車牌顏色
if (cond.contains("platecolor"))
{
strtemp = GetStringCondition(cond["platecolor"].keys());
strCondition += QString(" platecolor in (%1) AND").arg(strtemp);
}
//車身顏色
if (cond.contains("vehiclecolor"))
{
strtemp = GetStringCondition(cond["vehiclecolor"].keys());
strCondition += QString(" vehiclecolor in (%1) AND").arg(strtemp);
}
if (cond.contains("time"))
{
if (cond["time"].contains("starttime"))
{
strCondition += QString(" time >='%1' AND").arg(cond["time"]["starttime"]);
}
if (cond["time"].contains("endtime"))
{
strCondition += QString(" time <='%1' AND").arg(cond["time"]["endtime"]);
}
}
if (strCondition.right(4) == " AND")//去除最後的 AND
{
strCondition = strCondition.left(strCondition.length() - 4);
}
if (strCondition == "where")//無條件
{
strCondition = "";
}
}
QVariantMap replyData;
do
{
//先查出符合條件的總數量
QSqlQuery countquery;
//先刪除數據庫中的數據
sqlcount += strCondition;
countquery.prepare(sqlcount);
if (!countquery.exec()
|| !countquery.next())
{
QSqlError error = countquery.lastError();
errorCode = error.type();
msg = error.text();
LOG_ERROR("query vehicle count failed,msg:%s", msg.toStdString().c_str());
break;
}
quint64 totalCount = countquery.record().value(0).toULongLong();
if (0 >= totalCount)
{
break;
}
QVariantList dataList;
QSqlQuery query;
sqlserchpage += strCondition;
sqlserchpage += QString(" ORDER BY time DESC LIMIT %1 OFFSET %2").arg(pagesize).arg((pageno)* pagesize);
query.prepare(sqlserchpage);
if (!query.exec())
{
QSqlError error = query.lastError();
errorCode = error.type();
msg = error.text();
break;
}
while (query.next())
{
QSqlRecord record = query.record();
int column = record.count();
QVariantList recorditem;
for (int i = 0; i < column; i++)
{
QVariantMap data;
data.insert("N", record.fieldName(i));
data.insert("V", record.value(i));
recorditem.append(data);
}
QVariantMap item;
item.insert("data", recorditem);
dataList.append(item);
}
replyData.insert("totalCount", totalCount);
replyData.insert("data", dataList);
} while (0);
singalQueryVehicle(errorCode, msg, replyData);
}
3.1.4 刪除數據
刪除數據用delete,如果清空表格,則不帶where條件。同樣如果是一次刪除多條記錄,不能用bindValue去綁定值,計時綁定的值是拼接好的字符串,並且加上了引號都沒用,需直接組織一個字符串。將多個taskId的值添加到字符串中。
int LocalDb::deleteAlarmTask(QString id,QString& strMsg)
{
if (id=="")
{
return -1;
}
int errorCode = 0;
QString msg;
do
{
QSqlQuery query;
//先刪除數據庫中的數據
query.prepare("delete from alarmTask where id =:taskId");
query.bindValue(":taskId",id);
if (!query.exec())
{
QSqlError error = query.lastError();
errorCode = error.type();
msg = error.text();
LOG_ERROR("delete %s failed,msg:%s", id.toStdString().c_str(), msg.toStdString().c_str());
return -1;
}
} while (0);
return 0;
}
3.2 基於模板的數據庫操作
數據庫的增刪改查之間有很多相似之處,按照上面的方法去操作數據庫,針對不同的表格的增刪改查,都要自定義一個函數去操作具體的數據表格,並組織具體的sql語句,擴展性和通用性比較差,現在試圖用四個函數實現所有表格的增刪改查,需要定義一些規範和參數模板,使用者只需要按照要求輸入參數條件,就可以實現對不同表格的相同的操作。
3.2.1 增刪改查接口說明
/*新增記錄
QString table, 表格名稱,
QVariantMap& info, 插入表格的數據,名稱值鍵值對
QString& strMsg,返回錯誤消息
*/
int insertTableRecord(QString table, QVariantMap& info, QString& strMsg);
/*
//cond
/*刪除記錄
QString table, 表格名稱,
QVariantList& cond, 判斷條件,QVariantMap保存的鍵值對
{
"name":"target",
"relation":"=", 關係運算符
"value":"C://picture/1.jpg",
"logical":"AND" 邏輯運算符,第一個沒有邏輯運算符
}
QString& strMsg,返回錯誤消息
*/
int deleteTableRecord(QString table, QVariantList& cond, QString& strMsg);
/*修改數據記錄
QString table, 表格名稱,
QVariantMap info 跟新數據的鍵值對
QVariantList& cond, 判斷條件,QVariantMap保存的鍵值對
{
"name":"target",
"relation":"=", 關係運算符
"value":"C://picture/1.jpg",
"logical":"AND" 邏輯運算符,第一個沒有邏輯運算符
}
QString& strMsg,返回錯誤消息
*/
int updateTableRecord(QString table, QVariantMap info, QVariantList& cond, QString& strMsg);
/*查詢數據記錄
QString table, 表格名稱,
QVariantList& cond, 判斷條件,QVariantMap保存的鍵值對
{
"name":"target",
"relation":"=", 關係運算符
"value":"C://picture/1.jpg",
"logical":"AND" 邏輯運算符,第一個沒有邏輯運算符
}
int pageno, 分頁查詢的頁碼,0開始
int pagesize,分頁查詢每頁數量
QString strOrderKey,用於排序的鍵名稱
QString strOrder,正序還是逆序排列,逆序:DESC或者正序:ASC
QString& strMsg,返回錯誤消息
*/
int queryTableRecordByPage(QString table, QVariantList& cond, int pageno, int pagesize,QString strOrderKey,QString strOrder, QString& strMsg, QVariantMap& replyData);
3.2.2 增刪改查接口實現
具體實現代碼如下:
int LocalDb::deleteTableRecord(QString table, QVariantList& cond, QString& strMsg)
{
//cond
/*
{
"name":"target",
"relation":"==", 關係運算符
"value":"C://picture/1.jpg",
"logical":"AND" 邏輯運算符,第一個沒有邏輯運算符
}*/
if (table == "")
{
strMsg = "表格爲空";
return -1;
}
do
{
QString strQuery = "delete from "+ table;
if (cond.size()!=0)
{
strQuery += " ";
strQuery += "where ";
for (int i = 0; i < cond.size();i++)
{
if (cond[i].toMap().contains("logical"))
{
strQuery += cond[i].toMap().value("logical").toString();
strQuery += " ";
}
if (cond[i].toMap().contains("name"))
{
strQuery += cond[i].toMap().value("name").toString();
strQuery += " ";
}
if (cond[i].toMap().contains("relation"))
{
strQuery += cond[i].toMap().value("relation").toString();
strQuery += " ";
}
if (cond[i].toMap().contains("value"))
{
strQuery += cond[i].toMap().value("value").toString();
strQuery += " ";
}
}
}
QSqlQuery query;
//先刪除數據庫中的數據
query.prepare(strQuery);
if (!query.exec())
{
QSqlError error = query.lastError();
strMsg = error.text();
LOG_ERROR("delete table:%s failed,msg:%s", table.toStdString().c_str(), strMsg.toStdString().c_str());
return -1;
}
} while (0);
return 0;
}
int LocalDb::updateTableRecord(QString table, QVariantMap info, QVariantList & cond, QString & strMsg)
{
int errorCode = -1;
do
{
QSqlQuery query;
QString strQuery = "update " + table + " set ";
QVariantMap::iterator it;
for (it = info.begin();it!= info.end();it++)
{
strQuery += it.key();
strQuery += "=";
strQuery += it.value().toString();
}
if (cond.size() != 0)
{
strQuery += " ";
strQuery += "where ";
for (int i = 0; i < cond.size(); i++)
{
if (cond[i].toMap().contains("logical"))
{
strQuery += cond[i].toMap().value("logical").toString();
strQuery += " ";
}
if (cond[i].toMap().contains("name"))
{
strQuery += cond[i].toMap().value("name").toString();
strQuery += " ";
}
if (cond[i].toMap().contains("relation"))
{
strQuery += cond[i].toMap().value("relation").toString();
strQuery += " ";
}
if (cond[i].toMap().contains("value"))
{
strQuery += cond[i].toMap().value("value").toString();
strQuery += " ";
}
}
}
query.prepare(strQuery);
if (!query.exec())
{
QSqlError error = query.lastError();
strMsg = error.text();
errorCode = error.type();
LOG_ERROR("Update table %s failed %s", table.toStdString().c_str(), strMsg.toStdString().c_str());
break;
}
errorCode = 0;
//創建圖片解析任務
} while (0);
return errorCode;
}
int LocalDb::insertTableRecord(QString table, QVariantMap& info,QString& strMsg)
{
int errorCode = 0;
QString msg;
QVariantMap data;
if (info.size() == 0)
{
return -1;
}
QString strName = "";
QString strValue = "";
for (QVariantMap::iterator it = info.begin(); it != info.end(); it++)
{
strName += it.key();
strName += ",";
strValue += it.value().toString();
strValue += ",";
}
//去除末尾的逗號
strName = strName.left(strName.length() - 1);
strValue = strValue.left(strValue.length() - 1);
QString strSql = "INSERT INTO "+ table+" (" + strName + ") VALUES(" + strValue + ")";
do
{
QSqlQuery query;
query.prepare(strSql);
if (!query.exec())
{
QSqlError error = query.lastError();
errorCode = error.type();
strMsg = error.text();
break;
}
errorCode = 0;
} while (0);
return errorCode;
}
int LocalDb::queryTableRecordByPage(QString table, QVariantList & cond, int pageno, int pagesize, QString strOrderKey, QString strOrder, QString & strMsg, QVariantMap& replyData)
{
if (table=="")
{
return -1;
}
int errorCode = -1;
QString msg;
QString sqlcount = "SELECT count(*) from "+ table;
QString strQuery = "SELECT * from "+ table;//ORDER BY time DESC LIMIT :limit OFFSET :offset";
if (cond.size() > 0)
{
QString strCondition = " where ";
for (int i = 0; i < cond.size(); i++)
{
if (cond[i].toMap().contains("logical"))
{
strCondition += cond[i].toMap().value("logical").toString();
strCondition += " ";
}
if (cond[i].toMap().contains("name"))
{
strCondition += cond[i].toMap().value("name").toString();
strCondition += " ";
}
if (cond[i].toMap().contains("relation"))
{
strCondition += cond[i].toMap().value("relation").toString();
strCondition += " ";
}
if (cond[i].toMap().contains("value"))
{
strCondition += cond[i].toMap().value("value").toString();
strCondition += " ";
}
}
sqlcount += strCondition;
strQuery += strCondition;
}
//分頁排序查詢
if (strOrderKey!="" &&strOrder!="")
{
strQuery += QString("ORDER BY %1 %2 LIMIT %3 OFFSET %4").arg(strOrderKey).arg(strOrder).arg(pagesize).arg(pagesize*pageno);
}
do
{
//先查出符合條件的總數量
QSqlQuery countquery;
//先刪除數據庫中的數據
countquery.prepare(sqlcount);
if (!countquery.exec()
|| !countquery.next())
{
QSqlError error = countquery.lastError();
errorCode = error.type();
msg = error.text();
LOG_ERROR("query alarmInfo count failed,msg:%s", msg.toStdString().c_str());
break;
}
quint64 totalCount = countquery.record().value(0).toULongLong();
if (0 >= totalCount)
{
errorCode = 0;
break;
}
QSqlQuery query;
query.prepare(strQuery);
if (!query.exec())
{
QSqlError error = query.lastError();
errorCode = error.type();
msg = error.text();
break;
}
QVariantList dataList;
while (query.next())
{
QSqlRecord record = query.record();
int column = record.count();
QVariantMap data;
for (int i = 0; i < column; i++)
{
data.insert(record.fieldName(i), record.value(i));
}
dataList.append(data);
}
//QVariantMap replyData;
replyData.insert("totalCount", totalCount);
replyData.insert("data", dataList);
errorCode = 0;
} while (0);
return errorCode;
}
3.3 對比總結
對比項 |
常規增刪改查操作 |
基於模板的操作方法 |
擴展性 |
差 |
好 |
代碼量 |
多 |
少 |
開發效率 |
低 |
高 |
靈活性 |
低 |
高 |
通過對比可以知道,常規的增刪改查操作需要針對具體的條件組織SQL語句,擴展性差,代碼量大,而且對於不同的表格需要編寫不同的函數接口,開發效率低下,靈活性差。而基於模板的操作方法,對操作方法進行了抽象,確定了統一的規範和方法,不同的表格可以使用相同的接口進行數據庫操作,代碼的靈活性和擴展性強,開發效率高,只需要按照規範輸入條件參數,函數就可以自動的組織SQL語句,進行數據庫操作。