何时或如何在QSqlTableModel上使用fetchMore()并使用SQLite数据库为rowCount()工作?

时间:2014-10-14 20:08:06

标签: qt sqlite qsqltablemodel qsqldatabase

我的类DataTable派生自QAbstractTableModel。它在内部使用QSqlTableModel对象从db表中获取数据。它表示db中每一行的记录(它执行的操作更多,但记录计数始终是db表中的行数)。

使用MySql,我的DataTable :: rowCount()实现只调用QSqlTableModel上的rowCount(),效果很好。

现在使用SQLite,如果db表中有超过256行,Qt的SQLite驱动程序返回的行数为256,因此我的DataTable类也返回256 - 这是错误的。 documentation告诉我拨打while (sql_model->canFetchMore()) sql_model->fetchMore();。在创建内部QSqlTableModel之后立即调用fetchMore()实际上会导致以下rowCount()调用返回正确的值。但是一旦数据库中的某些内容发生了变化(我的类将在QSqlTableModel上调用insertRow()或setData()),下一个QSqlTableModel :: rowCount()调用将再次返回256。

数据库仅由我的类修改,而我的类又使用该特定的QSqlTableModel对象(或使用我的DataTable作为模型的视图可以更新某些内容)。因此,没有其他进程可以将行插入数据库。

那么我的DataTable类何时应该为rowCount()调用fetchMore()以始终返回实际的行数?
我认为我的类应该将QSqlTableModel发出的一些信号连接到一个调用fetchMore()的插槽,虽然我不确定这是否是正确/可靠的方法吗? < / p>


更新

以下是一些用于演示基本问题的代码。

QSqlTableModel *model = new QSqlTableModel(0, database); //QSqlDatabase
model->setTable("tablename");
qDebug() << "0 row count" << model->rowCount(); //0 row count 0 
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
qDebug() << "1 row count" << model->rowCount(); //1 row count 256 
while (model->canFetchMore()) model->fetchMore();
qDebug() << "2 row count" << model->rowCount(); //2 row count 1520 
//... other methods ...
model->setData(model->index(0, 0), "TEST");
model->submitAll();
qDebug() << "3 row count" << model->rowCount(); //3 row count 256 
while (model->canFetchMore()) model->fetchMore();
qDebug() << "4 row count" << model->rowCount(); //4 row count 1520 

加载sql模型后,rowCount()返回256(1),因此必须调用fetchMore()。然后rowCount()返回实际的行数 之后,数据被更改,之后rowCount()再次返回256(3)。

因此,似乎必须在sql模型上的每次写操作之后调用fetchMore()。但是,而不是在修改模型的每个方法的末尾放置/ canFetchMore()/ fetchMore()循环,我想知道是否足以连接beforeInsert(QSqlRecord&amp;),beforeUpdate(int,QSqlRecord&amp; )和beforeDelete(int)发信号到一个槽,然后调用fetchAll()?这会是可靠和适当的吗?

更正:不在*信号之前(太早),但可能是layoutChanged(),dataChanged(),rowsInserted()和rowsRemoved()。


更新2

关于SQL的注意事项:我知道我可以在理论上向数据库发送单独的SELECT COUNT SQL查询,但这不能回答这个问题。只要我能避免SQL,我就不会编写SQL。在我看来,发送这样一个SQL查询违背了面向对象的QAbstractTableModel类的目的。另外rowCount()是const(不应该发送查询),应该很快。无论如何,这不会修复rowCount()。

我最终将一个调用fetchMore()的插槽连接到相关信号(见上文) AND 断言所有内容都已在rowCount()中获取:
assert(!sql_model->canFetchMore())

这是因为rowCount()无法报告正确的行计数对我来说是故障状态,因此断言。换句话说,我宁愿让我的应用程序崩溃而不是使用错误的行数。

仅将其连接到dataChanged()信号(如first answerI would probably try to use dataChanged signal.中所述)是不够的。我已将其与dataChanged(const QModelIndex&, const QModelIndex&)rowsInserted(const QModelIndex&, int, int)rowsRemoved(const QModelIndex&, int, int)layoutChanged()相关联。

似乎工作,断言尚未失败。

如果有人能够明确证实这一点(或解释为什么它不会一直有效),我会很感激答案。

3 个答案:

答案 0 :(得分:1)

根据我的经验,Qt的SQLite驱动程序以256行的步长将行插入到模型中。在数据提取结束时QSqlTableModel::rowCount()将返回正确的值,因此无需调用fetchMore()。它只取决于你在何时何地调用rowCount()。

例如,如果用户正在对rowsInserted()信号进行一些计算,他的插槽方法将被多次调用,并且每次最后一行的索引将增加最多值256。

因此,您需要在数据提取结束时使用rowCount()(在一些重写的​​QSqlTableModel方法中,提取通常不会完成),或者不依赖于中间行计数值并在每次更改时重复计算。

这是一般答案,因为您没有发布任何代码,我们可以看到您正在尝试做什么,但您可以看到我指向的位置。

编辑:

QSqlTableModel::submitAll()调用之后,模型被重新填充,这就是为什么需要再次重复fetchMore()的原因。调用fetchMore()的位置取决于用例。它可以在submitAll()之后调用,或者在某个插槽中,这里没有一般答案。我可能会尝试使用dataChanged信号。但是,获取数据的目标始终应该是主要的,以便在视图中显示它,并且视图在大多数情况下自行完成。

在我的一个应用程序中,我依靠表视图为我提取数据。将模型设置为表视图后,我调用QTableView::scrollToBottom()为我提取数据(在我的情况下,用户应该看到底部的最新数据,滚动后行计数具有正确的值)或者在rowsInserted中进行计算( )当用户滚动表时,插槽,并再次以256行的步长自动获取数据。

非常重要的是要知道,如果应用程序需要显示表格中的某些行数,或者例如一个列数据的汇总值,使用额外的QSqlQuery来获取其他信息(如:从xyz中选择count(*))然后从大型表模型中读取数据通常要高效得多。 表格模型主要用于向表格视图提供数据

答案 1 :(得分:1)

目前的答案并没有完全回答这个问题(提到dataChanged()信号而不是其他信号),所以我正在写自己的答案。

已经有一段时间了,我相信我已经涵盖了所有案例: 我最终将一个调用fetchMore()的插槽连接到相关信号并断言所有内容都已在我的DataTable :: rowCount()方法中获取: 断言(sql_model-&GT;!canFetchMore())

(当然rowCount()是一个const方法,所以如果还没有提取任何东西我就无法获取,但是那不管是getter的工作;断言很好,因为canFetchMore()也是const 。)

Signals:dataChanged(const QModelIndex&amp;,const QModelIndex&amp;),rowsInserted(const QModelIndex&amp;,int,int),rowsRemoved(const QModelIndex&amp;,int,int)和layoutChanged()

我正在使用断言来确保我的模型接收到正确的行计数,否则应用程序将崩溃(如果并非所有提到的信号都连接,则会发生这种情况,例如layoutChanged())。这在我的情况下很重要,因为不正确的行数可能会导致数据丢失。

到目前为止,断言没有失败,所以我假设这解决了它。

答案 2 :(得分:1)

你所面临的两难困境与我最近遇到的情况类似。我写了一个QT gui程序,它执行了以下操作 -

我。连接并查询Oracle数据库 II。在QTableView中显示查询结果 III。将QTableView的结果导出到.csv文件 IV。将.csv文件导入loacl sqlite3数据库 v。连接并查询本地sqlite3数据库 六。执行查询并在另一个QTableview中显示结果

在步骤ii。中,(即将结果导出到.csv)我观察到的是虽然在QTableView上生成了543条记录,但只有256多条记录被导出到.csv文件。

我在用,

int rows=model->rowCount();
int columns=model->columnCount();

    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < columns; j++)
        {
            textData += model->data(model->index(i,j)).toString();
            textData += ", ";      // for .csv file format
        }
        textData += "\n";             // (optional: for new line segmentation)
    }

    QFile csvfile("/home/aj/ora_exported.csv");
    if(csvfile.open(QIODevice::WriteOnly|QIODevice::Truncate))
    {
        QTextStream out(&csvfile);
        out<<textData;
    }
    csvfile.close();
事实证明,在获取所有结果之前,模型会读取 model-&gt; rowcount

正如SO社区所建议的那样,我用了---

while (model->canFetchMore())
       model->fetchMore();

    int rows=model->rowCount();
    int columns=model->columnCount();

    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < columns; j++)
        {
            textData += model->data(model->index(i,j)).toString();
            textData += ", ";      // for .csv file format
        }
        textData += "\n";             // (optional: for new line segmentation)
    }

并且在.csv文件中填充了所有记录(全部为543)。

您可以在here上查看我的问题。