Qt使用QSqlDatabase连接MySQL实现增删改查功能

本文主要是介绍Qt使用QSqlDatabase连接MySQL实现增删改查功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴...

一、创建数据表

数据库名:

我们先创建一个数据库,名字叫 game_db:

CREATE DATABASE IF NOT EXISTS game_db DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
USE game_db;

创建玩家表:players

CREATE TABLE players PnCNA(
    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);

表中结果如下所示:

Qt使用QSqlDatabase连接MySQL实现增删改查功能

二、连接MySQL数据库

database_manager.h

#ifndef DATABASEMANAGER_H
#define DATABASEMANAGER_H

#include <QObject>
#include <QMutex>
#include <QSqlDatabase>
#include <QSqlError>

enum DatabaseType {
    MySQL = 0,
    SQLite,
    PostgreSQL,
    // 可扩展更多类型
};

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()
{

}

DapythontabaseManager& 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.PnCNAstrDbName); // SQLite只需数据库文件路径
    } 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!";
    }
}

三、封装成一个完整的轻量级 ORM 风格类

什么是ORM 模型类的?

ORM(Object-Relational Mapping)模型类的作用,是将数据库中的表与 C++(或其他语言)中的类进行映射和封装,让你能像操作普通对象那样操作数据库数据,避免直接拼接 SQL 字符串,提高代码的可读性、可维护性、安全性和抽象性。

ORM 模型类的作用总结如下:

3.1 表结构 → 类结构

ORM 会把数据库表(如 users)映射成一个类(如 User),表字段对应类成员或字段名:

// 数据库表字段
// id | name | age

// ORM类字段
QVariantMap row;
row["id"] = 1;
row["name"] = "Alice";
row["age"] = 30;

3.2 简化 SQL 操作

让你写这样的代码:

userModel.insert({{"name", "Tom"}, {"age", 25}});

而不需要写繁琐的 SQL:

INSERT INTO users (name, age) VALUES ('Tom', 25);

目标功能:

  • 封装成类 OrmModel 或基类 OrmBase
  • 支持设置数据库连接、表名
  • 支持 select / insert / update / delete 等基础操作
  • 使用 QVariantMap 传入/返回数据
  • ORM风格:调用 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; // 保存数据库实例
};

#endif // DBORMBASE_H

类实现: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("passwChina编程ord_hash").toString();
        QDateTime createdAt = playerMap.value("created_at").toDateTime();

        // 可以进行打印、显示、或保存到类对象中
        qDebug() << "ID:" << playerId
                 << "用户名:" << username
                 << "昵称:" << nickname
                 << "等级:" << level
                 << "经验:" << experience
                 << "金币:" << gold
                 << "密码Hash:" << passwordHash
                 << "创建时间:" << createdAt.toString(Qt::ISODate);
    }

    return a.exec();
}

输出结果:

Qt使用QSqlDatabase连接MySQL实现增删改查功能

四、实现派生具体模型类

为什么要派生?

虽然 OrmBase 已支持通用 CRUD,但派生后的 UserModel 能实现:

  • 指定固定表名(如 players)
  • 添加和 players 表相关的专属函数(如 getAdultUsers()、findByName())
  • 让调用更具语义:UserModel user; user.insert(…)

创建 UserModel 类

UserModel.h

#ifndef USERMODEL_H
#define USERMODEL_H

#include "OrmBase.h"

class UserModel : public OrmBase
{
public:
    explicit UserModel(const QString &connectionName = "default");

    // 自定义业务函数
    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",python "experience"});
}

// 查询某个 ID 的用户(返回一行)
QVariantMap UserModel::findById(int id)
{
    auto results = select(QString("player_id = %1").arg(id));
    return results.isEmpty() ? QVariantMap() : results.first().toMap();
}

// 查询某个名字的所有用户
QVariantList UserModel::findByName(const QString &name)
{
    QString clause = QString("username = '%1'").arg(name.replace("'", "''")); // 简单防注入
    return select(clause);
}

五、支持多线程连接池 + ORM + 事务封装

将构建以下组件:

+-------------------------+
| DBConnectionPool       | 线程安全连接池类(单例)
+-------------------------+
| OrmBase                | ORM 基类(封装表操作 + 自动获取线程连接)
+-------------------------+
| OrmManager             | 管理多个模型,支持事务封装
+-------------------------+
| XxxModel : OrmBase     | 表模型(如 UserModel)
+-------------------------+

5.1 线程安全数据库连接池: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;
};

关键点:OrmBase 中不再保存 QSqlDatabase 实例,而是每次从线程池中获取:

QSqlDatabase OrmBase::db() const
{
    return DBConnectionPool::instance().getConnection();
}

5.3 事务封装类 OrmManager

用于封装如下逻辑:

  • beginTransaction()
  • commit()
  • rollback()
class OrmManager
{
public:
    static bool beginTransaction();
    static bool commit();
    static bool rollback();
};

实现:

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 使用示例:线程内事务 + 多表操作

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实现增删改查功能的详细内容,更多关于QSqlDatabase连接MySQL实现增删改查的资料请关注China编程(www.chinasem.cn)其它相关文章!

这篇关于Qt使用QSqlDatabase连接MySQL实现增删改查功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155317

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关