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 同时存在的问题。