【问题标题】:Qt Table Model ExampleQt 表模型示例
【发布时间】:2016-06-20 23:03:53
【问题描述】:

我在看Qt示例“表模型示例”

在该示例中,connection.h 中的 createConnection() 方法包含以下代码:

static bool createConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(":memory:");
    if (!db.open()) {...

我在我的代码中复制了这个示例。我不明白关于它的两件事。

  1. 局部变量 db 在 createConnection() 函数结束时被销毁(参见 RAII)。由于我的数据库不需要初始化并且我没有使用“db”变量,我不明白为什么我需要这段代码。但是,如果我删除它,我的程序将无法从现有数据库中读取。我希望数据库的破坏会关闭数据库并使其不可用。但是,由于我不接触 db 变量,为什么需要先创建然后销毁它才能访问数据库?由于我没有在任何地方使用 db 变量,并且在调用我的代码之前它就被销毁了,所以我根本不明白为什么我需要调用 createConnection() 函数。另一方面,我看不到如何打开数据库并导致变量销毁具有异常安全的 RAII 代码。

  2. 我从来没有让 db.open() 失败。如果我没有数据库,它会创建它然后打开一个空白的。如果我确实有数据库,它会打开它。我有一个数据库,如果由于某种原因无法打开,我不想创建它,我想要一个错误,因为出现了严重错误。打开一个空白数据库对我来说只会是个麻烦。我该如何处理这种情况?

我是 Qt 新手,但在 C++ 方面非常有经验。这段代码对我来说意义不大。我希望 db 变量的生命周期是开放数据库的生命周期。如示例中的编码,我看不到此代码如何没有资源泄漏。据我所知,初始化 db 会打开某种全局资源,即使在 db 变量被破坏导致泄漏后也保持打开状态。

这很令人困惑。

谢谢。

H Os 平铺

【问题讨论】:

  • QSqlDatabase 是一个相当奇怪的类。阅读它的文档:它并没有按照您的想法行事。这不是 RAII 类。

标签: c++ database qt sqlite


【解决方案1】:

这只是一个小例子。

  1. QSqlDatabase::addDatabase("QSQLITE"); 将创建默认连接,任何未明确命名另一个连接的后续查询都将使用该默认连接。范围结束时连接不会被破坏。在此示例中,createConnection() 将用于初始化 inMemory 数据库。稍后可以使用QSqlDatabase::database() 检索连接。在标准应用程序中,可能会将QSqlDatabase db 保留为类成员。

  2. 使用 inMemory db 只会创建一个空数据库,因为它适用于基于 SQLite 文件的数据库。如果要检查现有文件 (db),请使用 QFile::exists(QString path)

如果您要打开许多数据库(QSqlDatabase::addDatabase("QSQLITE","anotherConnectionName"); 上有不同的连接名称,您将有泄漏。否则您只是覆盖默认数据库连接。QApplication 本身将保留连接。可以使用 @987654327 显式删除它们@;

【讨论】:

  • 很难理解你在说什么。我不清楚为什么 inMemory 数据库会让事情变得不同。我的代码是从示例中复制而来的,并且没有生成任何“inMemory”,它创建的数据库都是一样的。 Sqlite 文档说 SQLITE_OPEN_READWRITE 将返回数据库必须已经存在的错误,否则返回错误。此外,使 db 成为成员似乎会将生命周期从变量转移到包装器的实例,所以我看不出这有什么帮助。听起来 Qt 不是线程安全的或不支持异常?我想我现在的理解力更差了。
  • 文档按类说明线程安全。由于旧编译器的兼容性问题,支持异常但未广泛实施。这可能会随着 Qt 5.7 发生很大变化。
  • 正如 KubaOber 所说,QSqlDatabase 有点特殊。如果您确实创建了QSqlQuery 或类似的类(QSqlTable,...)并且不提供数据库句柄,Qt 将自动使用默认连接。这使得查询数据库实际上非常容易。值得一提的是,sql 连接本身不是线程安全的。您应该为打算直接查询数据库的每个线程打开一个连接。
【解决方案2】:

QSqlDatabase 是一个相当奇怪的类。阅读其文档。连接仍然存在,QSqlDatabase 是它的句柄。连接被命名,addDatabase 返回具有给定名称的连接,如果它不存在,则可以选择创建它。您可以让任意数量的QSqlDatabase 对象代表一个连接。

由于 sqlite 内存数据库是特定于您的进程的,因此在您要求时不创建它几乎没有意义。您提前创建它的唯一方法是直接从您的流程中使用 sqlite api。这些数据库不会持久存在。这里的行为是相当明智的。

【讨论】:

  • 重新输入:在我的情况下,我的数据库在磁盘上,并且必须在我的应用程序启动之前存在,因此这种行为是不明智的。必须可靠地诊断丢失的数据库并将其报告给用户。如果同时启动多个程序,这也必须如此。该文档没有说明这些函数的线程安全性。由于它不会报告错误并在丢失时在磁盘上创建数据库,因此无论该主题的文档沉默如何,它都不能是线程安全的。遗憾的是它会泄漏资源并且也不是异常安全的。 Qt 应该像一个好的 C++ 库一样坚持使用 RAII。
  • @HOsTile 只要您一次使用来自单个线程的连接,就可以了。您的问题不涉及磁盘数据库:如果您想问其他问题,为了对所有神圣事物的热爱,请这样做。
  • @HOsTile 我看不懂你的想法。如果您必须发表评论以解释您真正在做什么(即:与内存数据库无关),那么它表明您的问题需要解决......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多