Qt 数据库QSqlDatabase使用记录

记录一些在QT中使用QSqlDatabase操作数据库时,需要注意的地方

创建数据库

cpp 复制代码
bool CDBOperatorAbstract::_openDBConn(CDatabaseConfig config)
{
    QWriteLocker locker(&m_locker);
    QSqlDatabase db;
    if(QSqlDatabase::contains(m_connectionName))
    {
        db = QSqlDatabase::database(m_connectionName);
        QStringList tables = db.tables();
        if(db.isOpen() && tables.contains(config.m_databaseName))
        {
            m_isConnected = true;
            return true;
        }
    }
    else
    {
        // 此时目标数据库不一定存在,不指定数据库名,会默认连接到mysql的主数据库(mysql数据库)
        db = QSqlDatabase::addDatabase("QMYSQL",m_connectionName);
        db.setHostName(config.m_hostAddress);   // 数据库服务器IP
        db.setPort(config.m_port);              // 端口号
        db.setUserName(config.m_userName);      // 用户名
        db.setPassword(config.m_userPwd);       // 密码
    }
    // 连接数据库
    if(!db.open())
    {
        qDebug() << db.lastError().text();
        m_isConnected = false;
        return false;
    }

    QSqlQuery query(db);
    QString strSql = QString("CREATE DATABASE IF NOT EXISTS `%1`").arg(config.m_databaseName);
    //执行建表语句,若执行成功
    if(query.exec(strSql))
    {
        //若查询语句有错
        if(query.lastError().isValid())
        {
            qDebug()<< query.lastError().text();
            m_isConnected = false;
            return false;
        }
    }
    else
    {
        m_isConnected = false;
        return false;
    }

// 确认目标数据库创建成功,关闭之前打开的默认数据库
db.close();
// 重新设置连接的数据库名
db.setDatabaseName(config.m_databaseName);
// 重新打开数据库
if(!db.open())
{
    m_isConnected = false;
    return false;
}
else
{
    m_isConnected = true;
    return true;
}
}

在上述创建数据库的代码中有几个需要注意的地方:

1、一个QSqlDatabase对象表示的是一个数据库连接,而不是字面上的数据库(数据库连接与数据库的关系是:一个数据库连接可以通过修改配置连接到多个数据库,一个数据库也可以有多个数据库连接)

2、addDatabase()方法需要指定数据库驱动类型,如MySQL、SQLite......,如果没有指定连接名,将默认使用defaultConnection作为连接名。对于同一个连接,重复调用addDatabase(),会提示:

QSqlDatabasePrivate::addDatabase: duplicate connection name

'qt_sql_default_connection', old connection removed.

3、removeDatabase()删除数据库连接,需要在确保所有QSqlQuery查询操作执行完毕并且销毁,否则会提示:

QSqlDatabasePrivate::removeDatabase: connection 'qt_sql_default_connection' is still in use, all queries will cease to work.

4、在进行数据库连接时,目标数据库可能还不存在,这时候可以不指定数据库名进行连接,此会将连接到数据库服务器的主数据库,例如我使用的是MySQL数据库服务器,就会连接到mysql数据库

5、数据库连接建立成功后,创建目标数据库(不存在时才创建),确保目标数据库创建成功后,先关闭之前的数据库连接,设置数据库名称后再重新打开。(只有重新打开数据库连接,数据库配置才会生效)

多线程使用数据库

QSqlDatabase是非线程安全的,意味着在多线程环境下同时使用同一个数据库连接对象可能会导致竞态条件和数据一致性问题。因此,每个线程最好都维护一个独立的数据库连接,并且在同一个线程中创建、使用和关闭数据库连接。如果数据库连接有父对象,它和父对象也必须在同一个线程。

一般用QMap来管理线程和对应的数据库连接,可以用QMap<QThread*,QSqlDatabase*>来存储,也可以用QMap<QThread*,QString>来存储(存连接名)

由于程序可能在各个地方操作数据库,为了让数据库连接的创建和使用保持在同一个线程中,可以用QMetaObject::invokeMethod()将数据库的创建、使用、关闭都移动到数据库连接所在的线程中

cpp 复制代码
bool CDBOperatorAbstract::checkIsInWorkThread()
{
    if(QThread::currentThread() == this->m_pThread)
        return true;
    else
        return false;
}

bool CDBOperatorAbstract::openDBConn(CDatabaseConfig config)
{
    if(checkIsInWorkThread())
    {
        return _openDBConn(config);
    }
    else
    {
        bool isOk = false;
        QMetaObject::invokeMethod(this,"_openDBConn",Qt::BlockingQueuedConnection,
                                  Q_RETURN_ARG(bool,isOk),
                                  Q_ARG(CDatabaseConfig,config));
        return isOk;
    }
}

用BlockingQueuedConnection的连接方式,会将目标函数放在接收者的生存线程中执行,并且阻塞发送者所在的线程,直到目标函数执行完毕。

增删改查

numRowsAffected()

这个函数返回最后一个执行的 SQL 查询语句所影响的行数。通常用于 INSERT、UPDATE 或 DELETE 查询之后,用来确定有多少行受到了影响。返回值是一个整数。

lastInsertId()

这个函数用于在主键自增的情况下,获取最后一次 INSERT 操作生成的主键,返回值为QVariant类型,需要根据主键实际的类型进行转换。

size()

这个函数用于获取当前查询结果集中的记录数。例如执行 SELECT 查询后需要确定返回的记录数量。返回值是一个整数。

isValid()

这个函数用于检查 QSqlQuery 对象是否有效,即它是否正常地初始化以执行 SQL 查询。

一个值得借鉴的多线程数据库连接管理方式

相关推荐
ClickHouseDB7 小时前
ClickHouse Cloud 与 Snowflake 实时性价比对比:CostBench 测试中的 412 倍差距来
运维·数据库·系统架构
m0_639906947 小时前
Redis入门|五种数据结构、使用场景、持久化RDB&AOF
数据结构·数据库·redis
时间售卖铺9 小时前
触发器与函数实战:MySQL高效编程秘诀
运维·数据库·mysql·云原生
qetfw9 小时前
UOS MariaDB 数据库:安装、最小权限与备份验证
linux·数据库·mariadb·uos
数智园区合作社9 小时前
问数平台怎么把同一个指标锁成一个数
大数据·数据库·人工智能·ai
lbb 小魔仙10 小时前
Windows搭建PaddleOCR+飞书Webhook自动打卡通知机器人:识别、推送与远程触发完整教程
数据库·人工智能·prometheus·时序数据库
captain37611 小时前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis
心中有你021411 小时前
Java实现Dijkstra最短路径算法(邻接表 + 优先队列)
java·数据库·算法
龙亘川12 小时前
综合素质评价业务建模:从过程性评价到成长档案全链路闭环
数据库·智慧城市·数据可视化
数据库小组12 小时前
NineData 实战教程:MySQL 5.5 数据迁移同步到 MySQL 8.0(跨版本升级)
数据库·mysql·mysql优化·数据库开发·数据库迁移·版本升级·数据复制