首页 > 用户笔记 > wf的笔记 阅读:19

Qt QSqlTableModel中select函数查询失败的问题

背景

我在 widget 上写了一个 QTableView--QSqlTableModel--mysql 关联关系的 UI。

需求

添加分页显示的功能。

问题现象

添加分页显示功能的过程中,当涉及到排序时,view 显示清空。

解决过程

QSqlTableModel 提供了两个函数用来增加sql查询的条件语句,
void QSqlTableModel::setSort(int column, Qt::SortOrder order)
void QSqlTableModel::setFilter(const QString &filter)
setFilter用来描述条件策略,setSort用来描述排序策略。

通过调用 QSqlTableModel::select() 函数来执行语句,并更新 view 显示。

1) 开始添加分页功能

使用 sql 中的 limit 语句限制每页条数(此处默认10条),具体操作如下:

因为 setFilter 自动增加“where”关键字,因此需要这样写:
setFilter(QString(“where 1 = 1 limit 10 offset 0”));
运行程序,view 上显示 10 条数据,成功。

2) 增加排序

setSort(index, Qt::AscendingOrder); //index为某字段序号,AscendingOrder为升序排序。
运行程序,view 上数据清空。

3) 问题排查

打开 mysql 的安装目录,查看 my.ini,修改日志目录为 ./mysql.err、./mysql.log,并将日志等级调成最低(log_error_verbosity = 3),运行程序,并查看 mysql 日志,mysql.log 只显示正确执行的日志,mysql.err 不显示具体错误执行语句。0.0,但是可以确定的是执行的语句有问题。

Qt 是开源的,因此可以查看源码,QSqlTableModel::select 函数的部分源码如下:
return Sql::concat(Sql::concat(stmt, Sql::where(d->filter)), orderByClause());
由此可知,qt 封装的 select 函数就是简单的将 filter 和 order 通过 concat 函数拼接在一起的,因此select * from tableName where 1=1 limit 10 offset 0 order by XX;是它的执行语句,放到 mysql 命令框执行报错,limit 语句不能放在 order 语句前面。

经过查阅 QSqlTableModel 的成员方法,没有 limit 语句的相关函数,只能通过 setFilter 函数写入。

因此只能重写 select 函数了,重写如下:
bool QSqlTableModel::select()
{
    Q_D(QSqlTableModel);
    const QString query = selectStatement() + m_limit; //只有这一行与被覆盖的函数不同,m_limit为继承的自定义类的成员变量,内容为具体的limit语句
    if (query.isEmpty())
        return false;

    beginResetModel();

    d->clearCache();

    QSqlQuery qu(query, d->db);
    setQuery(qu);

    if (!qu.isActive() || lastError().isValid()) {
        // something went wrong - revert to non-select state
        d->initRecordAndPrimaryIndex();
        endResetModel();
        return false;
    }
    endResetModel();
    return true;
}
运行程序,一堆 private 报错,原来 Q_D 会返回私有指针,所以这个 select 函数是个假的虚函数,不能通过覆盖修改 query 内容。

在 select 函数源码中,使用 setQuery 函数执行语句,我试图直接使用 QSqlTableModel::setQuery,具体代码如下:
TableModel->setQuery(QSqlQuery(QString(“select * from tableName where 1=1 offset 0 order by XX limit 10 ;”)));
运行程序,protected 报错,setQuery 是个保护函数,此时我应当再写一个子类继承 QTableModel,封装一下这个 setQuery,但是我好巧不巧看了一眼源码,setQuery 的源码如下:
void QSqlTableModel::setQuery(const QSqlQuery &query)
{
    QSqlQueryModel::setQuery(query);
}
这个 QSqlQueryModel::setQuery(query) 是一个 public 的方法,因此像如下这样写:
TableModel->QSqlQueryModel::setQuery(QSqlQuery(QString(“select * from tableName where 1=1 offset 0 order by XX limit 10 ;”)));
运行程序,成功,view 上显示了 10 条数据。

总结

TableModel->QSqlQueryModel::setQuery(QSqlQuery(QString(“sql语句”)));可以规避 limit 和 order 同时存在的问题。

相关文章