找回密码
 立即注册
收起左侧

QSqlDatabase使用疑问

15
回复
9090
查看
[复制链接]
累计签到:17 天
连续签到:1 天
来源: 2017-7-30 20:04:38 显示全部楼层 |阅读模式
5Qter豆
本帖最后由 fox123 于 2017-7-30 20:04 编辑

Qt Creator快速入门(第3版)一书中,在作者写的函数createConnection()中,作者定义了一个局部变量QSqlDatabase db1 = QSqlDatabase::addDatabase("QSQLITE","connection1"),来接收静态函数返回的对象,并进行了一些设置。但是main函数是如何拿到这个连接的?这是个局部变量啊。另外,看QT的源码里边竟然也是直接返回的一个局部QSqlDatabase对象,这是为何???1、createConncetion()函数

2、main函数

3.QT源码



附件: 您需要 登录 才可以下载或查看,没有帐号?立即注册

最佳答案

查看完整内容

不好意思啊,昨天时间有限,没有去看源码!这个标记确实是说明是静态函数! 刚下载源码看了下。 大概的原理是: 每次返回的确实是 不同的复制体 但是,你的set操作,其实都是通过 QSqlDatabase 中的一个 指针 d 去操作的! 实例化 QSqlDatabase 的时候就会 new 一个 QSqlDatabasePrivate 指向 *d 虽然你拿到的 QSqlDatabase 不同,但其中的d 指针指向的QSqlDatabasePrivate 都是一样的。 所以你在任何地方拿到的QSqlDatabas ...
回复

使用道具 举报

累计签到:53 天
连续签到:1 天
2017-7-30 20:04:39 显示全部楼层
本帖最后由 angelus 于 2017-8-1 16:10 编辑
fox123 发表于 2017-7-31 23:20
然而,static的作用是标记这个函数是静态函数,而不是用来修饰返回值吧!! ...

不好意思啊,昨天时间有限,没有去看源码!这个标记确实是说明是静态函数!

刚下载源码看了下。

大概的原理是:
每次返回的确实是 不同的复制体
但是,你的set操作,其实都是通过  QSqlDatabase 中的一个 指针 d 去操作的!

实例化 QSqlDatabase 的时候就会 new 一个 QSqlDatabasePrivate  指向 *d

虽然你拿到的 QSqlDatabase 不同,但其中的d 指针指向的QSqlDatabasePrivate 都是一样的。
所以你在任何地方拿到的QSqlDatabase,都是在操作同一个地址的对象!
换句话说,每次调用静态 add函数生成的 QSqlDatabasePrivate *d 都是不一样的地址,而且每次必定都会加入到静态hash中,如果有重复的就会挤掉原来的!

QSqlDatabasePrivate 内部会有一个进行原子操作的引用计数器
QSqlDatabase实例化一次就会+1
QSqlDatabase析构一次就会-1
直到引用计数为0,内部的QSqlDatabasePrivate *d 才会 delete

只要你不主动去移除Hash表中的 QSqlDatabase ,那么对应的QSqlDatabasePrivate *d就不会被delete因为引用计数数值永远大于0
只有在程序结束,或者你主动移除,才会删除!

需要注意的是,除非调用静态函数addDatabase去进行池管理外。
自己创建的addDatabase是不会加入到池中进行管理的。换句话说,你自己实例的QSqlDatabase,如果没有任何复制体,一旦析构,就会真的结束!
除非你用new放到堆中自己管理生命周期!




回复

使用道具 举报

累计签到:344 天
连续签到:1 天
2017-7-31 09:35:09 显示全部楼层
连接名都是connection1,在main中,你不是使用了连接名获取了该连接了吗
回复

使用道具 举报

累计签到:17 天
连续签到:1 天
2017-7-31 13:52:29 显示全部楼层
cai901022 发表于 2017-7-31 09:35
连接名都是connection1,在main中,你不是使用了连接名获取了该连接了吗

QT源码里边函数返回的QSqlDatabase对象赋值给db1是执行的一个拷贝动作吧,生成了一个临时对象赋值给db1,然后就析构了,这样的话,后面再通过连接名拿到的应该不是同一个吧!!!
回复

使用道具 举报

累计签到:53 天
连续签到:1 天
2017-7-31 14:31:02 显示全部楼层
本帖最后由 angelus 于 2017-7-31 14:51 编辑

QSqlDatabase维护一个全局数据库链接池,在任何地方都可以用来获取数据链接的!
使用静态方法 addDatabase可以生成一个数据库对象,然后QSqlDatabase会维护它的生命周期

使用静态方法database     可以返回已经存在的数据库对象,不加参数链接名称 就直接返回默认链接


这是一种经典的程序设计模式!

程序中经常会出现数据库链接重复的一串警告信息,就是因为 重复调用 addDatabase还不加参数
这个程序会自动销毁前一次创建的默认链接,重新建立一个默认的链接!
建议把addDatabase调用一次就可以了,以后要使用,就直接database

可以考虑的设计是在addDatabase的代码段加判断,
静态方法contains检查是否  已经创建过数据库对象了!

好的习惯是 创建链接的时候 写上链接的 第二个参数,
这样就可以在多链接的时候任意根据链接名称获取正确的数据库链接对象了!







回复

使用道具 举报

累计签到:17 天
连续签到:1 天
2017-7-31 14:54:49 显示全部楼层
angelus 发表于 2017-7-31 14:31
QSqlDatabase维护一个全局数据库链接池,在任何地方都可以用来获取数据链接的!
使用静态方法 addDatabase ...

如果是全局数据库连接池,那应该是通过new的方式来创建QSqlDatabase对象,然后将它添加进连接池,返回也应该是返回一个指针吧??
回复

使用道具 举报

累计签到:53 天
连续签到:1 天
2017-7-31 15:00:37 显示全部楼层
首先,你创建的new 数据库对象,都在链接池中,你仅保留一个new后的指针操控。
你从任何 QSqlDatabase 中二次获取的 对象,都是返回一个实体的,这个是类自己通过接口方法控制的。
你可以直接用指针取实体,也可以通过实体取地址
回复

使用道具 举报

累计签到:53 天
连续签到:1 天
2017-7-31 15:04:03 显示全部楼层
不建议自己直接new,因为没有实际好处!
QSqlDatabase的接口大部分都是用实体操作的,用指针反而是个障碍。
随用随取,是个好习惯,也不用自己去管理指针的生命周期!
回复

使用道具 举报

累计签到:53 天
连续签到:1 天
2017-7-31 15:10:06 显示全部楼层
fox123 发表于 2017-7-31 14:54
如果是全局数据库连接池,那应该是通过new的方式来创建QSqlDatabase对象,然后将它添加进连接池,返回也 ...

qt已经帮你管理链接了,不需要你自己去NEW,然后加入
相当与 你只是个 傻瓜式的 使用者,拿来用,用后就不管了。

使用指针你就必须自己管理指针生命周期了
回复

使用道具 举报

累计签到:53 天
连续签到:1 天
2017-7-31 15:16:02 显示全部楼层
数据库对象,当初设计的时候就是为了使用者避免操作指针,和数据库对象的管理。
回复

使用道具 举报

累计签到:17 天
连续签到:1 天
2017-7-31 15:16:47 显示全部楼层
angelus 发表于 2017-7-31 15:00
首先,你创建的new 数据库对象,都在链接池中,你仅保留一个new后的指针操控。
你从任何 QSqlDatabase 中二 ...

以下是QT里的源码:
1、添加时创建的局部对象
QSqlDatabase QSqlDatabase::addDatabase(const QString &type, const QString &connectionName)
{
    QSqlDatabase db(type);
    QSqlDatabasePrivate::addDatabase(db, connectionName);
    return db;
}
2、这里是QT里边自己维护的一个map集合,把这个局部的对象和连接名添加进map集合了
void QSqlDatabasePrivate::addDatabase(const QSqlDatabase &db, const QString &name)
{
    QConnectionDict *dict = dbDict();
    Q_ASSERT(dict);
    QWriteLocker locker(&dict->lock);

    if (dict->contains(name)) {
        invalidateDb(dict->take(name), name);
        qWarning("QSqlDatabasePrivate::addDatabase: duplicate connection name '%s', old "
                 "connection removed.", name.toLocal8Bit().data());
    }
    dict->insert(name, db);
    db.d->connName = name;
}

执行完addDatabase函数后 QSqlDatabase db(type)这个对象不就析构了啊??
回复

使用道具 举报

累计签到:53 天
连续签到:1 天
2017-7-31 16:24:15 显示全部楼层
本帖最后由 angelus 于 2017-7-31 16:40 编辑

析构以前它加入到了 全局静态列表中!其实add函数结束,它已经被析构了,加入到全局静态列表中的db只是一个副本!

dbDict是全局静态变量 QHash容器
Q_GLOBAL_STATIC(QConnectionDict, dbDict)

回复

使用道具 举报

累计签到:17 天
连续签到:1 天
2017-7-31 17:32:52 显示全部楼层
angelus 发表于 2017-7-31 16:24
析构以前它加入到了 全局静态列表中!其实add函数结束,它已经被析构了,加入到全局静态列表中的db只是一个 ...

那岂不是addDatabase函数返回的是一个副本,而全局容器中保存的是另一个副本,通过QSqlDatabase db1 =  QSqlDatabase::addDatabase("SQLITE"); 然后 db1.setDatabaseName("my1.db")和db1.open()设置的不是同一个对象的属性啊???
回复

使用道具 举报

累计签到:53 天
连续签到:1 天
2017-7-31 18:15:08 显示全部楼层
fox123 发表于 2017-7-31 17:32
那岂不是addDatabase函数返回的是一个副本,而全局容器中保存的是另一个副本,通过QSqlDatabase db1 =  Q ...

[static] QSqlDatabase QSqlDatabase::addDatabase
[static] QSqlDatabase QSqlDatabase::database
返回的都是静态对象

换句话说,你add和database获取到的,都是同一个对象!
回复

使用道具 举报

累计签到:17 天
连续签到:1 天
2017-7-31 23:20:04 显示全部楼层
angelus 发表于 2017-7-31 18:15
[static] QSqlDatabase QSqlDatabase::addDatabase
[static] QSqlDatabase QSqlDatabase::database
返回 ...

然而,static的作用是标记这个函数是静态函数,而不是用来修饰返回值吧!!
回复

使用道具 举报

累计签到:17 天
连续签到:1 天
2017-8-1 20:38:30 显示全部楼层
angelus 发表于 2017-7-30 20:04
不好意思啊,昨天时间有限,没有去看源码!这个标记确实是说明是静态函数!

刚下载源码看了下。

恩恩,搞明白了,内部确实是自己维持了一个指针,感谢感谢!!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了