Qt使用QSqlDatabase連接MySQL實(shí)現(xiàn)增刪改查功能
一、創(chuàng)建數(shù)據(jù)表
數(shù)據(jù)庫(kù)名:
我們先創(chuàng)建一個(gè)數(shù)據(jù)庫(kù),名字叫 game_db:
CREATE DATABASE IF NOT EXISTS game_db DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; USE game_db;
創(chuàng)建玩家表:players
CREATE TABLE players (
player_id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(32) NOT NULL UNIQUE,
password_hash VARCHAR(64) NOT NULL,
nickname VARCHAR(32),
level INT DEFAULT 1,
experience INT DEFAULT 0,
gold INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
插入 20 條玩家信息 SQL 語句:
INSERT INTO players (username, password_hash, nickname, level, experience, gold) VALUES
('user001', MD5('password1'), 'Knight001', 5, 1200, 500),
('user002', MD5('password2'), 'Mage002', 3, 800, 300),
('user003', MD5('password3'), 'Archer003', 7, 1800, 750),
('user004', MD5('password4'), 'Rogue004', 2, 400, 100),
('user005', MD5('password5'), 'Cleric005', 10, 3200, 1200),
('user006', MD5('password6'), 'Paladin006', 4, 950, 400),
('user007', MD5('password7'), 'Hunter007', 6, 1400, 600),
('user008', MD5('password8'), 'Druid008', 8, 2200, 900),
('user009', MD5('password9'), 'Monk009', 9, 2500, 1000),
('user010', MD5('password10'), 'Barbarian010', 1, 100, 50),
('user011', MD5('password11'), 'Warrior011', 5, 1300, 550),
('user012', MD5('password12'), 'Sorcerer012', 3, 700, 300),
('user013', MD5('password13'), 'Assassin013', 6, 1600, 650),
('user014', MD5('password14'), 'Priest014', 7, 1900, 800),
('user015', MD5('password15'), 'Ranger015', 2, 500, 200),
('user016', MD5('password16'), 'Berserker016', 4, 1100, 450),
('user017', MD5('password17'), 'Necromancer017', 8, 2300, 950),
('user018', MD5('password18'), 'Templar018', 9, 2700, 1100),
('user019', MD5('password19'), 'Shaman019', 10, 3000, 1250),
('user020', MD5('password20'), 'Witch020', 1, 200, 100);
表中結(jié)果如下所示:

二、連接MySQL數(shù)據(jù)庫(kù)
database_manager.h
#ifndef DATABASEMANAGER_H
#define DATABASEMANAGER_H
#include <QObject>
#include <QMutex>
#include <QSqlDatabase>
#include <QSqlError>
enum DatabaseType {
MySQL = 0,
SQLite,
PostgreSQL,
// 可擴(kuò)展更多類型
};
typedef struct stConnectParams {
DatabaseType dbType;
QString strHostName;
QString strDbName;
QString strUserName;
QString strPassword;
QString strConnectionName;
int port;
} ConnectParams;
class DatabaseManager : public QObject
{
Q_OBJECT
public:
static DatabaseManager& instance();
void initConnectionParams(const ConnectParams& connectParams);
void initializeConnection();
private:
explicit DatabaseManager(QObject *parent = nullptr);
~DatabaseManager();
DatabaseManager(const DatabaseManager&) = delete;
DatabaseManager& operator =(const DatabaseManager&) = delete;
DatabaseManager(const DatabaseManager&&) = delete;
DatabaseManager& operator =(const DatabaseManager&&) = delete;
signals:
private:
ConnectParams m_connectParams;
};
database_manager.cpp
#include "database_manager.h"
#include <QDebug>
DatabaseManager::DatabaseManager(QObject *parent) : QObject(parent)
{
}
DatabaseManager::~DatabaseManager()
{
}
DatabaseManager& DatabaseManager::instance()
{
static DatabaseManager instance;
return instance;
}
void DatabaseManager::initConnectionParams(const ConnectParams& connectParams)
{
m_connectParams.dbType = connectParams.dbType;
m_connectParams.strHostName = connectParams.strHostName;
m_connectParams.strDbName = connectParams.strDbName;
m_connectParams.strUserName = connectParams.strUserName;
m_connectParams.strPassword = connectParams.strPassword;
m_connectParams.strConnectionName = connectParams.strConnectionName;
m_connectParams.port = connectParams.port;
}
void DatabaseManager::initializeConnection()
{
if (QSqlDatabase::contains(m_connectParams.strConnectionName)) {
qDebug() << "db:" << m_connectParams.strConnectionName << "already connected";
return;
}
QString strDriver;
switch (m_connectParams.dbType) {
case DatabaseType::SQLite:
strDriver = "QSQLITE";
break;
case DatabaseType::MySQL:
strDriver = "QMYSQL";
break;
case DatabaseType::PostgreSQL:
strDriver = "QPSQL";
break;
}
if (strDriver.isEmpty()) {
qDebug() << "can't find driver";
return;
}
QSqlDatabase db = QSqlDatabase::addDatabase(strDriver, m_connectParams.strConnectionName);
if (m_connectParams.dbType == DatabaseType::SQLite) {
db.setDatabaseName(m_connectParams.strDbName); // SQLite只需數(shù)據(jù)庫(kù)文件路徑
} else {
db.setConnectOptions("MYSQL_OPT_CONNECT_TIMEOUT=5;"
"MYSQL_OPT_READ_TIMEOUT=5;"
"MYSQL_OPT_WRITE_TIMEOUT=5;");
db.setHostName(m_connectParams.strHostName);
db.setPort(m_connectParams.port);
db.setDatabaseName(m_connectParams.strDbName);
db.setUserName(m_connectParams.strUserName);
db.setPassword(m_connectParams.strPassword);
}
if (!db.open()) {
qDebug() << "Database connection failed:" << db.lastError().text();
} else {
qDebug() << "Database connected successfully!";
}
}
三、封裝成一個(gè)完整的輕量級(jí) ORM 風(fēng)格類
什么是ORM 模型類的?
ORM(Object-Relational Mapping)模型類的作用,是將數(shù)據(jù)庫(kù)中的表與 C++(或其他語言)中的類進(jìn)行映射和封裝,讓你能像操作普通對(duì)象那樣操作數(shù)據(jù)庫(kù)數(shù)據(jù),避免直接拼接 SQL 字符串,提高代碼的可讀性、可維護(hù)性、安全性和抽象性。
ORM 模型類的作用總結(jié)如下:
3.1 表結(jié)構(gòu) → 類結(jié)構(gòu)
ORM 會(huì)把數(shù)據(jù)庫(kù)表(如 users)映射成一個(gè)類(如 User),表字段對(duì)應(yīng)類成員或字段名:
// 數(shù)據(jù)庫(kù)表字段 // id | name | age // ORM類字段 QVariantMap row; row["id"] = 1; row["name"] = "Alice"; row["age"] = 30;
3.2 簡(jiǎn)化 SQL 操作
讓你寫這樣的代碼:
userModel.insert({{"name", "Tom"}, {"age", 25}});
而不需要寫繁瑣的 SQL:
INSERT INTO users (name, age) VALUES ('Tom', 25);
目標(biāo)功能:
- 封裝成類 OrmModel 或基類 OrmBase
- 支持設(shè)置數(shù)據(jù)庫(kù)連接、表名
- 支持 select / insert / update / delete 等基礎(chǔ)操作
- 使用 QVariantMap 傳入/返回?cái)?shù)據(jù)
- ORM風(fēng)格:調(diào)用 model.select(…) 而不是手寫 SQL 字符串
類定義:OrmBase
#ifndef DBORMBASE_H
#define DBORMBASE_H
#include <QObject>
#include <QSqlDatabase>
#include <QVariantMap>
#include <QVariantList>
class DbOrmBase : public QObject
{
Q_OBJECT
public:
explicit DbOrmBase(const QString &tableName, const QString &connectionName);
~DbOrmBase();
// CRUD
QVariantList select(const QString &strWhereClause = "", const QStringList &columns = {});
bool insert(const QVariantMap &values);
bool update(const QVariantMap &values, const QString &strWhereClause);
bool remove(const QString &strWhereClause);
private:
bool isConnected() const;
private:
QString m_tableName;
QString m_connectionName;
QSqlDatabase m_db; // 保存數(shù)據(jù)庫(kù)實(shí)例
};
#endif // DBORMBASE_H
類實(shí)現(xiàn):OrmBase.cpp
#include "OrmBase.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlRecord>
#include <QDebug>
OrmBase::OrmBase(const QString &tableName, const QString &connectionName)
: m_tableName(tableName), m_connectionName(connectionName)
{}
QVariantList OrmBase::select(const QString &whereClause, const QStringList &columns)
{
QVariantList results;
QSqlDatabase db = QSqlDatabase::database(m_connectionName);
if (!db.isOpen()) {
qDebug() << "Database not open";
return results;
}
QString columnStr = columns.isEmpty() ? "*" : columns.join(", ");
QString sql = QString("SELECT %1 FROM %2").arg(columnStr, m_tableName);
if (!whereClause.trimmed().isEmpty()) {
sql += " WHERE " + whereClause;
}
QSqlQuery query(db);
if (!query.exec(sql)) {
qDebug() << "Select failed:" << query.lastError().text();
return results;
}
while (query.next()) {
QVariantMap row;
QSqlRecord rec = query.record();
for (int i = 0; i < rec.count(); ++i) {
row[rec.fieldName(i)] = query.value(i);
}
results << row;
}
return results;
}
bool OrmBase::insert(const QVariantMap &values)
{
QSqlDatabase db = QSqlDatabase::database(m_connectionName);
if (!db.isOpen()) return false;
QStringList columns, placeholders;
QVariantList bindValues;
for (auto it = values.begin(); it != values.end(); ++it) {
columns << it.key();
placeholders << "?";
bindValues << it.value();
}
QString sql = QString("INSERT INTO %1 (%2) VALUES (%3)")
.arg(m_tableName)
.arg(columns.join(", "))
.arg(placeholders.join(", "));
QSqlQuery query(db);
query.prepare(sql);
for (const QVariant &val : bindValues) {
query.addBindValue(val);
}
if (!query.exec()) {
qDebug() << "Insert failed:" << query.lastError().text();
return false;
}
return true;
}
bool OrmBase::update(const QVariantMap &values, const QString &whereClause)
{
QSqlDatabase db = QSqlDatabase::database(m_connectionName);
if (!db.isOpen()) return false;
QStringList sets;
QVariantList bindValues;
for (auto it = values.begin(); it != values.end(); ++it) {
sets << QString("%1 = ?").arg(it.key());
bindValues << it.value();
}
QString sql = QString("UPDATE %1 SET %2").arg(m_tableName, sets.join(", "));
if (!whereClause.trimmed().isEmpty()) {
sql += " WHERE " + whereClause;
}
QSqlQuery query(db);
query.prepare(sql);
for (const QVariant &val : bindValues) {
query.addBindValue(val);
}
if (!query.exec()) {
qDebug() << "Update failed:" << query.lastError().text();
return false;
}
return true;
}
bool OrmBase::remove(const QString &whereClause)
{
QSqlDatabase db = QSqlDatabase::database(m_connectionName);
if (!db.isOpen()) return false;
QString sql = QString("DELETE FROM %1").arg(m_tableName);
if (!whereClause.trimmed().isEmpty()) {
sql += " WHERE " + whereClause;
}
QSqlQuery query(db);
if (!query.exec(sql)) {
qDebug() << "Delete failed:" << query.lastError().text();
return false;
}
return true;
}
使用示例:
#include <QCoreApplication>
#include <QDebug>
#include <QDateTime>
#include "database_manager.h"
#include "db_ormbase.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ConnectParams connectParams;
connectParams.dbType = DatabaseType::MySQL;
connectParams.strHostName = "localhost";
connectParams.strDbName = "game_db";
connectParams.strUserName = "root";
connectParams.strPassword = "root";
connectParams.strConnectionName = "user_game";
connectParams.port = 3306;
DatabaseManager::instance().initConnectionParams(connectParams);
DatabaseManager::instance().initializeConnection();
DbOrmBase userModel("players", "user_game");
QVariantList playerList = userModel.select("experience > 1000");
for (const QVariant &playerVar : playerList) {
QVariantMap playerMap = playerVar.toMap();
int playerId = playerMap.value("player_id").toInt();
QString username = playerMap.value("username").toString();
QString nickname = playerMap.value("nickname").toString();
int level = playerMap.value("level").toInt();
int experience = playerMap.value("experience").toInt();
int gold = playerMap.value("gold").toInt();
QString passwordHash = playerMap.value("password_hash").toString();
QDateTime createdAt = playerMap.value("created_at").toDateTime();
// 可以進(jìn)行打印、顯示、或保存到類對(duì)象中
qDebug() << "ID:" << playerId
<< "用戶名:" << username
<< "昵稱:" << nickname
<< "等級(jí):" << level
<< "經(jīng)驗(yàn):" << experience
<< "金幣:" << gold
<< "密碼Hash:" << passwordHash
<< "創(chuàng)建時(shí)間:" << createdAt.toString(Qt::ISODate);
}
return a.exec();
}
輸出結(jié)果:

四、實(shí)現(xiàn)派生具體模型類
為什么要派生?
雖然 OrmBase 已支持通用 CRUD,但派生后的 UserModel 能實(shí)現(xiàn):
- 指定固定表名(如 players)
- 添加和 players 表相關(guān)的專屬函數(shù)(如 getAdultUsers()、findByName())
- 讓調(diào)用更具語義:UserModel user; user.insert(…)
創(chuàng)建 UserModel 類
UserModel.h
#ifndef USERMODEL_H
#define USERMODEL_H
#include "OrmBase.h"
class UserModel : public OrmBase
{
public:
explicit UserModel(const QString &connectionName = "default");
// 自定義業(yè)務(wù)函數(shù)
QVariantList getAdultUsers();
QVariantMap findById(int id);
QVariantList findByName(const QString &name);
};
#endif // USERMODEL_H
UserModel.cpp
#include "UserModel.h"
UserModel::UserModel(const QString &connectionName)
: OrmBase("players", connectionName) // 固定表名為 users
{}
// 查詢年齡 >= 18 的用戶
QVariantList UserModel::getAdultUsers()
{
return select("experience >= 1000", {"player_id", "username", "experience"});
}
// 查詢某個(gè) ID 的用戶(返回一行)
QVariantMap UserModel::findById(int id)
{
auto results = select(QString("player_id = %1").arg(id));
return results.isEmpty() ? QVariantMap() : results.first().toMap();
}
// 查詢某個(gè)名字的所有用戶
QVariantList UserModel::findByName(const QString &name)
{
QString clause = QString("username = '%1'").arg(name.replace("'", "''")); // 簡(jiǎn)單防注入
return select(clause);
}
五、支持多線程連接池 + ORM + 事務(wù)封裝
將構(gòu)建以下組件:
+-------------------------+
| DBConnectionPool | 線程安全連接池類(單例)
+-------------------------+
| OrmBase | ORM 基類(封裝表操作 + 自動(dòng)獲取線程連接)
+-------------------------+
| OrmManager | 管理多個(gè)模型,支持事務(wù)封裝
+-------------------------+
| XxxModel : OrmBase | 表模型(如 UserModel)
+-------------------------+
5.1 線程安全數(shù)據(jù)庫(kù)連接池:DBConnectionPool
DBConnectionPool.h
#ifndef DBCONNECTIONPOOL_H
#define DBCONNECTIONPOOL_H
#include <QSqlDatabase>
#include <QMutex>
#include <QMap>
#include <QThreadStorage>
class DBConnectionPool
{
public:
static DBConnectionPool &instance();
void init(const QString &driver,
const QString &host,
int port,
const QString &dbName,
const QString &user,
const QString &password,
int maxConn = 10);
QSqlDatabase getConnection();
void closeAll();
private:
DBConnectionPool() = default;
Q_DISABLE_COPY(DBConnectionPool)
QString m_driver, m_host, m_dbName, m_user, m_password;
int m_port = 3306;
int m_maxConn = 10;
QMutex m_mutex;
int m_connIndex = 0;
QThreadStorage<QString> m_threadConnName;
};
#endif // DBCONNECTIONPOOL_H
DBConnectionPool.cpp
#include "DBConnectionPool.h"
#include <QSqlDatabase>
#include <QSqlError>
#include <QDebug>
DBConnectionPool &DBConnectionPool::instance()
{
static DBConnectionPool pool;
return pool;
}
void DBConnectionPool::init(const QString &driver,
const QString &host,
int port,
const QString &dbName,
const QString &user,
const QString &password,
int maxConn)
{
m_driver = driver;
m_host = host;
m_port = port;
m_dbName = dbName;
m_user = user;
m_password = password;
m_maxConn = maxConn;
}
QSqlDatabase DBConnectionPool::getConnection()
{
if (!m_threadConnName.hasLocalData()) {
QMutexLocker locker(&m_mutex);
QString connName = QString("conn_%1").arg(++m_connIndex);
m_threadConnName.setLocalData(connName);
QSqlDatabase db = QSqlDatabase::addDatabase(m_driver, connName);
db.setHostName(m_host);
db.setPort(m_port);
db.setDatabaseName(m_dbName);
db.setUserName(m_user);
db.setPassword(m_password);
db.setConnectOptions("MYSQL_OPT_CONNECT_TIMEOUT=5;"
"MYSQL_OPT_RECONNECT=1");
if (!db.open()) {
qCritical() << "Failed to open DB:" << db.lastError().text();
}
}
return QSqlDatabase::database(m_threadConnName.localData());
}
void DBConnectionPool::closeAll()
{
QMutexLocker locker(&m_mutex);
for (int i = 1; i <= m_connIndex; ++i) {
QString name = QString("conn_%1").arg(i);
if (QSqlDatabase::contains(name)) {
QSqlDatabase::removeDatabase(name);
}
}
m_connIndex = 0;
}
5.2 ORM 基類 OrmBase(使用連接池)
class OrmBase
{
public:
OrmBase(const QString &tableName);
virtual ~OrmBase() = default;
bool insert(const QVariantMap &values);
QVariantList select(const QString &whereClause = "", const QStringList &columns = {});
bool update(const QVariantMap &values, const QString &whereClause);
bool remove(const QString &whereClause);
QSqlDatabase db() const;
protected:
QString m_tableName;
};
關(guān)鍵點(diǎn):OrmBase 中不再保存 QSqlDatabase 實(shí)例,而是每次從線程池中獲?。?/p>
QSqlDatabase OrmBase::db() const
{
return DBConnectionPool::instance().getConnection();
}
5.3 事務(wù)封裝類 OrmManager
用于封裝如下邏輯:
- beginTransaction()
- commit()
- rollback()
class OrmManager
{
public:
static bool beginTransaction();
static bool commit();
static bool rollback();
};
實(shí)現(xiàn):
bool OrmManager::beginTransaction()
{
QSqlDatabase db = DBConnectionPool::instance().getConnection();
return db.transaction();
}
bool OrmManager::commit()
{
QSqlDatabase db = DBConnectionPool::instance().getConnection();
return db.commit();
}
bool OrmManager::rollback()
{
QSqlDatabase db = DBConnectionPool::instance().getConnection();
return db.rollback();
}
5.4 使用示例:線程內(nèi)事務(wù) + 多表操作
DBConnectionPool::instance().init("QMYSQL", "127.0.0.1", 3306, "testdb", "root", "123456");
UserModel user;
OrderModel order;
OrmManager::beginTransaction();
bool ok1 = user.insert({{"name", "Tom"}});
bool ok2 = order.insert({{"user_id", 1}, {"amount", 200.0}});
if (ok1 && ok2)
OrmManager::commit();
else
OrmManager::rollback();
5.5 線程使用示例(多線程安全)
QtConcurrent::run([](){
UserModel u;
u.insert({{"name", "ThreadUser"}});
});
以上就是Qt使用QSqlDatabase連接MySQL實(shí)現(xiàn)增刪改查功能的詳細(xì)內(nèi)容,更多關(guān)于QSqlDatabase連接MySQL實(shí)現(xiàn)增刪改查的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SublimeText編譯C開發(fā)環(huán)境設(shè)置
這篇文章主要介紹了使用SublimeText編譯C代碼的開發(fā)環(huán)境設(shè)置,大家參考使用2013-11-11
C++類和對(duì)象之類的6個(gè)默認(rèn)成員函數(shù)詳解
類是對(duì)某一事物的抽象描述,具體地講類是C++中的一種構(gòu)造的數(shù)據(jù)類型,下面這篇文章主要給大家介紹了關(guān)于C++類和對(duì)象之類的6個(gè)默認(rèn)成員函數(shù)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
基于Matlab實(shí)現(xiàn)多目標(biāo)粘液霉菌算法的示例代碼
多目標(biāo)粘液霉菌算法(MOSMA),這是最近開發(fā)的粘液霉菌算法(SMA)的多目標(biāo)變體,用于處理工業(yè)中的多目標(biāo)優(yōu)化問題。本文將用Matlab實(shí)現(xiàn)這一算法,需要的可以參考一下2022-05-05
C++中Overload,Override,Hide之間的區(qū)別
重載overload,這個(gè)概念是大家熟知的。在同一可訪問區(qū)內(nèi)被聲名的幾個(gè)具有不同參數(shù)列的(參數(shù)的類型、個(gè)數(shù)、順序不同)同名函數(shù),程序會(huì)根據(jù)不同的參數(shù)列來確定具體調(diào)用哪個(gè)函數(shù),這種機(jī)制就是重載2013-09-09
QT網(wǎng)絡(luò)編程UDP下C/S架構(gòu)廣播通信(實(shí)例講解)
下面小編就為大家?guī)硪黄猀T網(wǎng)絡(luò)編程UDP下C/S架構(gòu)廣播通信(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07

