热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

十六、Qt数据库之QSqlQueryModel&QSqlTableModel

声明:本文原创于yafeilinux的百度博客,http:hi.baidu.comyafeilinux转载请注明出处。(1)QSqlQueryModel在上一篇的最后我

 

声明:本文原创于yafeilinux的百度博客,http://hi.baidu.com/yafeilinux 转载请注明出处。


(1)QSqlQueryModel

在上一篇的最后我们说到,Qt中使用了自己的机制来避免使用SQL语句,它为我们提供了更简单的数据库操作和数据显示模型。它们分别是只读的QSqlQueryModel,操作单表的QSqlTableModel和以及可以支持外键的QSqlRelationalTableModel。这次我们先讲解QSqlQueryModel。

QSqlQueryModel类为SQL的结果集提供了一个只读的数据模型,下面我们先利用这个类进行一个最简单的操作。

我们新建Qt4 Gui Application工程,我这里工程名为queryModel ,然后选中QtSql模块,Base class选QWidget。工程建好后,添加C++ Header File ,命名为database.h,更改其内容如下:

#ifndef DATABASE_H
#define DATABASE_H

#include
#include

static bool createConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("database.db");
    if(!db.open()) return false;
    QSqlQuery query;
    query.exec("create table student (id int primary key, name vchar)");
    query.exec("insert into student values (0,'yafei0')");
    query.exec("insert into student values (1,'yafei1')");
    query.exec("insert into student values (2,'yafei2')");
    return true;
}

#endif // DATABASE_H

这里我们使用了db.setDatabaseName("database.db"); ,我们没有再使用以前的内存数据库,而是使用了真实的文件,这样后面对数据库进行的操作就能保存下来了。

然后进入main.cpp,将其内容更改如下:

#include
#include "widget.h"
#include "database.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    if(!createConnection())
        return 1;

    Widget w;
    w.show();
    return a.exec();
}

下面我们在widget.ui中添加一个显示为“查询”的Push Button,并进入其单击事件槽函数,更改如下:

void Widget::on_pushButton_clicked()
{
    QSqlQueryModel *model = new QSqlQueryModel;
    model->setQuery("select * from student");
    model->setHeaderData(0, Qt::Horizontal, tr("id"));
    model->setHeaderData(1, Qt::Horizontal, tr("name"));
    QTableView *view = new QTableView;
    view->setModel(model);
    view->show();

}

我们新建了QSqlQueryModel类对象model,并用setQuery()函数执行了SQL语句“("select * from student");”用来查询整个student表的内容,可以看到,该类并没有完全避免SQL语句。然后我们设置了表中属性显示时的名字。最后我们建立了一个视图view,并将这个model模型关联到视图中,这样数据库中的数据就能在窗口上的表中显示出来了。

我们在widget.cpp中添加头文件:

#include
#include

我们运行程序,并按下“查询”按键,效果如下:

我们在工程文件夹下查看数据库文件:



下面我们利用这个模型来操作数据库。

1.我们在void Widget::on_pushButton_clicked()函数中添加如下代码:

int column = model->columnCount(); //获得列数
int row = model->rowCount();    // 获得行数
QSqlRecord record = model->record(1); //获得一条记录
QModelIndex index = model->index(1,1);   //获得一条记录的一个属性的值
qDebug() <<"column num is:" <            <<"row num is:" <            <<"the second record is:" <            <<"the data of index(1,1) is:"<

我们在widget.cpp中添加头文件:

#include
#include
#include

此时运行程序,效果如下:


 

 

2.当然我们在这里也可以使用前面介绍过的query执行SQL语句。

例如我们在void Widget::on_pushButton_clicked()函数中添加如下代码:

QSqlQuery query = model->query();
query.exec("select name from student where id = 2 ");
query.next();
qDebug() <

这样就可以输出表中的值了,你可以运行程序测试一下。

3.当我们将函数改为如下。

void Widget::on_pushButton_clicked()
{
    QSqlQueryModel *model = new QSqlQueryModel;
    model->setQuery("select * from student");
    model->setHeaderData(0, Qt::Horizontal, tr("id"));
    model->setHeaderData(1, Qt::Horizontal, tr("name"));
    QTableView *view = new QTableView;
    view->setModel(model);
    view->show();

    QSqlQuery query = model->query();
    query.exec("insert into student values (10,'yafei10')");
 
    //插入一条记录
}

这时我们运行程序,效果如下:

我们发现表格中并没有增加记录,怎么回事呢?

我们关闭程序,再次运行,效果如下:

发现这次新的记录已经添加了。在上面我们执行了添加记录的SQL语句,但是在添加记录之前,就已经进行显示了,所以我们的更新没能动态的显示出来。为了能让其动态地显示我们的更新,我们可以将函数更改如下:

void Widget::on_pushButton_clicked()
{
    QSqlQueryModel *model = new QSqlQueryModel;
    model->setQuery("select * from student");
    model->setHeaderData(0, Qt::Horizontal, tr("id"));
    model->setHeaderData(1, Qt::Horizontal, tr("name"));
    QTableView *view = new QTableView;
    view->setModel(model);
    view->show();

    QSqlQuery query = model->query();
    query.exec("insert into student values (20,'yafei20')");
    //插入一条记录
   model->setQuery("select * from student"); //再次查询整张表
    view->show(); //再次进行显示
}

 

 

这时运行程序,效果如下:

可以看到,这时已经将新添的记录显示出来了。

刚开始我们就讲到,这个模型默认是只读的,所以我们在窗口上并不能对表格中的内容进行修改。但是我们可以创建自己的模型,然后按照我们自己的意愿来显示数据和修改数据。要想使其可读写,需要自己的类继承自QSqlQueryModel,并且重写setData() 和 flags() 两个函数。如果我们要改变数据的显示,就要重写data() 函数。

下面的例子中我们让student表的id属性列显示红色,name属性列可编辑。

1.我们在工程中添加C++ Class,然后Class name设为MySqlQueryModel,Base Class设为QSqlQueryModel,如下:


 

2.我们将mysqlquerymodel.h中的内容更改如下:

class MySqlQueryModel : public QSqlQueryModel
{
public:
    MySqlQueryModel();
    //下面三个函数都是虚函数,我们对其进行重载
    Qt::ItemFlags flags(const QModelIndex &index) const;
    bool setData(const QModelIndex &index, const QVariant &value, int role);

    QVariant data(const QModelIndex &item, int role=Qt::DisplayRole) const;
    //
private:
    bool setName(int studentId, const QString &name);
    void refresh();
};

然后将mysqlquerymodel.cpp文件更改如下:

#include "mysqlquerymodel.h"
#include
#include

MySqlQueryModel::MySqlQueryModel()
{
}

Qt::ItemFlags MySqlQueryModel::flags(
        const QModelIndex &index) const //返回表格是否可更改的标志
{
    Qt::ItemFlags flags = QSqlQueryModel::flags(index);
    if (index.column() == 1) //第二个属性可更改
        flags |= Qt::ItemIsEditable;
    return flags;
}

bool MySqlQueryModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
        //添加数据
{
    if (index.column() <1 || index.column() > 2)
        return false;

    QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
    int id = data(primaryKeyIndex).toInt(); //获取id号

    clear();

    bool ok;
    if (index.column() == 1) //第二个属性可更改
        ok = setName(id, value.toString());

    refresh();
    return ok;
}

void MySqlQueryModel::refresh() //更新显示
{
    setQuery("select * from student");
    setHeaderData(0, Qt::Horizontal, QObject::tr("id"));
    setHeaderData(1, Qt::Horizontal, QObject::tr("name"));
}

bool MySqlQueryModel::setName(int studentId, const QString &name) //添加name属性的值
{
    QSqlQuery query;
    query.prepare("update student set name = ? where id = ?");
    query.addBindValue(name);
    query.addBindValue(studentId);
    return query.exec();
}

QVariant MySqlQueryModel::data(const QModelIndex &index, int role) const
        //更改数据显示样式
{
    QVariant value = QSqlQueryModel::data(index, role);

    if (role == Qt::TextColorRole && index.column() == 0)
        return qVariantFromValue(QColor(Qt::red)); //第一个属性的字体颜色为红色
    return value;
}

在widget.cpp文件中添加头文件:#include "mysqlquerymodel.h"

然后更改函数如下:

void Widget::on_pushButton_clicked()
{
    QSqlQueryModel *model = new QSqlQueryModel;
    model->setQuery("select * from student");
    model->setHeaderData(0, Qt::Horizontal, tr("id"));
    model->setHeaderData(1, Qt::Horizontal, tr("name"));
    QTableView *view = new QTableView;
    view->setModel(model);
    view->show();

    MySqlQueryModel *myModel = new MySqlQueryModel; //创建自己模型的对象
    myModel->setQuery("select * from student");
    myModel->setHeaderData(0, Qt::Horizontal, tr("id"));
    myModel->setHeaderData(1, Qt::Horizontal, tr("name"));
    QTableView *view1 = new QTableView;
    view1->setWindowTitle("mySqlQueryModel"); //修改窗口标题
    view1->setModel(myModel);
    view1->show();
}

运行效果如下:


可以看到我们要的效果已经出来了。

 


 

(2)QSqlTableModel


在上一篇我们讲到只读的QSqlQueryModel也可以使其可编辑,但是很麻烦。Qt提供了操作单表的QSqlTableModel,如果我们需要对表的内容进行修改,那么我们就可以直接使用这个类。

 

QSqlTableModel,该类提供了一个可读写单张SQL表的可编辑数据模型。我们下面就对其的几个常用功能进行介绍,分别是修改,插入,删除,查询,和排序。

在开始讲之前,我们还是新建Qt4 Gui Application工程,我这里工程名为tableModel ,然后选中QtSql模块,Base class选QWidget。工程建好后,添加C++ Header File ,命名为database.h,更改其内容如下:

#ifndef DATABASE_H
#define DATABASE_H

#include
#include
#include

static bool createConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("database.db");
    if(!db.open()) return false;
    QSqlQuery query;
    query.exec(QObject::tr("create table student (id int primary key, name vchar)"));
    query.exec(QObject::tr("insert into student values (0,'刘明')"));
    query.exec(QObject::tr("insert into student values (1,'陈刚')"));
    query.exec(QObject::tr("insert into student values (2,'王红')"));
    return true;

}

#endif // DATABASE_H

为了在数据库中能使用中文,我们使用了QObject类的tr()函数。而在下面的main()函数中我们也需要添加相应的代码来支持中文。

然后将main.cpp的文件更改如下:

#include
#include "widget.h"
#include "database.h"
#include

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
    if(!createConnection())
        return 1;
    Widget w;
    w.show();
    return a.exec();
}

下面我们打开widget.ui,设计界面如下:

其中的部件有Table View 和 Line Edit,其余是几个按钮部件。

然后在widget.h中加入头文件: #include

在private中声明对象:QSqlTableModel *model;

因为我们要在不同的函数中使用model对象,所以我们在这里声明它。

我们到widget.cpp文件中的构造函数里添加如下代码:

model = new QSqlTableModel(this);
model->setTable("student");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select(); //选取整个表的所有行
// model->removeColumn(1); //不显示name属性列,如果这时添加记录,则该属性的值添加不上
ui->tableView->setModel(model);
// ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);   
//使其不可编辑

此时运行程序,效果如下:

可以看到,这个模型已经完全脱离了SQL语句,我们只需要执行select()函数就能查询整张表。上面有两行代码被注释掉了,你可以取消注释,测试一下它们的作用。

第一,修改操作。

1.我们进入“提交修改”按钮的单击事件槽函数,修改如下:

void Widget::on_pushButton_clicked() //提交修改
{
     model->database().transaction(); //开始事务操作
    if (model->submitAll()) {
        model->database().commit(); //提交
    } else {
        model->database().rollback(); //回滚
        QMessageBox::warning(this, tr("tableModel"),
                             tr("数据库错误: %1")
                             .arg(model->lastError().text()));
    }
}

这里用到了事务操作,真正起提交操作的是model->submitAll()一句,它提交所有更改。

2.进入“撤销修改”按钮单击事件槽函数,并更改如下:

void Widget::on_pushButton_2_clicked() //撤销修改
{
     model->revertAll();
}

它只有简单的一行代码。

我们需要在widget.cpp文件中添加头文件:

#include
#include

此时运行程序,效果如下:

我们将“陈刚”改为“李强”,如果我们点击“撤销修改”,那么它就会重新改为“陈刚”,而当我们点击“提交修改”后它就会保存到数据库,此时再点击“撤销修改”就修改不回来了。

可以看到,这个模型可以将所有修改先保存到model中,只有当我们执行提交修改后,才会真正写入数据库。当然这也是因为我们在最开始设置了它的保存策略:

model->setEditStrategy(QSqlTableModel::OnManualSubmit);

OnManualSubmit表明我们要提交修改才能使其生效。

第二,查询操作。

1.我们进入“查询”按钮的单击事件槽函数,更改如下:

void Widget::on_pushButton_7_clicked() //查询
{
    QString name = ui->lineEdit->text();
    model->setFilter(QObject::tr("name = '%1'").arg(name)); //根据姓名进行筛选
    model->select(); 
//显示结果
}

我们使用setFilter()函数进行关键字筛选,这个函数是对整个结果集进行查询。为了使用变量,我们使用了QObject类的tr()函数。

2.我们进入“返回全表”按钮的单击事件槽函数,更改如下:

void Widget::on_pushButton_8_clicked() //返回全表
{
    model->setTable("student");   //重新关联表
    model->select();   //这样才能再次显示整个表的内容
}

为了再次显示整个表的内容,我们需要再次关联这个表。

运行效果如下:

我们输入一个姓名,点击“查询”按钮后,就可以显示该记录了。再点击“返回全表”按钮则返回。

第三,排序操作。

我们分别进入“按id升序排列”和“按id降序排列”按钮的单击事件槽函数,更改如下:

void Widget::on_pushButton_5_clicked() //升序
{
    model->setSort(0,Qt::AscendingOrder); //id属性,即第0列,升序排列
    model->select();
}

void Widget::on_pushButton_6_clicked() //降序
{
    model->setSort(0,Qt::DescendingOrder);
    model->select();

}

我们这里使用了setSort()函数进行排序,它有两个参数,第一个参数表示按第几个属性排序,表头从左向右,最左边是第0个属性,这里就是id属性。第二个参数是排序方法,有升序和降序两种。

运行程序,效果如下:

这是按下“按id降序排列”按钮后的效果。

第四,删除操作。

我们进入“删除选中行”按钮单击事件槽函数,进行如下更改:

void Widget::on_pushButton_4_clicked() //删除当前行
{
    int curRow = ui->tableView->currentIndex().row();
    //获取选中的行
    model->removeRow(curRow);
    //删除该行
    int ok = QMessageBox::warning(this,tr("删除当前行!"),tr("你确定"
                                                           "删除当前行吗?"),
                         QMessageBox::Yes,QMessageBox::No);
    if(ok == QMessageBox::No)
    {

       model->revertAll(); //如果不删除,则撤销
    }
    else model->submitAll(); //否则提交,在数据库中删除该行
}

这里删除行的操作会先保存在model中,当我们执行了submitAll()函数后才会真正的在数据库中删除该行。这里我们使用了一个警告框来让用户选择是否真得要删除该行。

运行效果如下:

我们点击第二行,然后单击“删除选中行”按钮,出现了警告框。这时你会发现,表中的第二行前面出现了一个小感叹号,表明该行已经被修改了,但是还没有真正的在数据库中修改,这时的数据有个学名叫脏数据(Dirty Data)。当我们按钮“Yes”按钮后数据库中的数据就会被删除,如果按下“No”,那么更改就会取消。

第五,插入操作。

我们进入“添加记录”按钮的单击事件槽函数,更改如下:

void Widget::on_pushButton_3_clicked() //插入记录
{
     int rowNum = model->rowCount(); //获得表的行数
    int id = 10;
     model->insertRow(rowNum); //添加一行
     model->setData(model->index(rowNum,0),id);
    // model->submitAll(); //可以直接提交
}

我们在表的最后添加一行,因为在student表中我们设置了id号是主键,所以这里必须使用setData函数给新加的行添加id属性的值,不然添加行就不会成功。这里可以直接调用submitAll()函数进行提交,也可以利用“提交修改”按钮进行提交。

运行程序,效果如下:

按下“添加记录”按钮后,就添加了一行,不过在该行的前面有个星号,如果我们按下“提交修改”按钮,这个星号就会消失。当然,如果我们将上面代码里的提交函数的注释去掉,也就不会有这个星号了。

       可以看到这个模型很强大,而且完全脱离了SQL语句,就算你不怎么懂数据库,也可以利用它进行大部分常用的操作。我们也看到了,这个模型提供了缓冲区,可以先将修改保存起来,当我们执行提交函数时,再去真正地修改数据库。当然,这个模型比前面的模型更高级,前面讲的所有操作,在这里都能执行。

 

 


推荐阅读
  • SQL Server开发技巧:修改表结构后的视图批量更新方法与实践 ... [详细]
  • 优化后的标题:数据网格视图(DataGridView)在应用程序中的高效应用与优化策略
    在应用程序中,数据网格视图(DataGridView)的高效应用与优化策略至关重要。本文探讨了多种优化方法,包括但不限于:1)通过合理的数据绑定提升性能;2)利用虚拟模式处理大量数据,减少内存占用;3)在格式化单元格内容时,推荐使用CellParsing事件,以确保数据的准确性和一致性。此外,还介绍了如何通过自定义列类型和优化渲染过程,进一步提升用户体验和系统响应速度。 ... [详细]
  • 本文深入探讨了 iOS 开发中 `int`、`NSInteger`、`NSUInteger` 和 `NSNumber` 的应用与区别。首先,我们将详细介绍 `NSNumber` 类型,该类用于封装基本数据类型,如整数、浮点数等,使其能够在 Objective-C 的集合类中使用。通过分析这些类型的特性和应用场景,帮助开发者更好地理解和选择合适的数据类型,提高代码的健壮性和可维护性。苹果官方文档提供了更多详细信息,可供进一步参考。 ... [详细]
  • [TyvjP1050] 动态规划求解最长公共子序列问题
    在解决最长公共子序列问题时,动态规划是一种高效的方法。具体而言,我们使用二维数组 `dp[i][j]` 来表示第一个字符串匹配到第 `i` 位,第二个字符串匹配到第 `j` 位时的最长公共子序列长度。状态转移方程为:当两个字符相等时,`dp[i][j] = dp[i-1][j-1] + 1`;否则,`dp[i][j] = max(dp[i-1][j], dp[i][j-1])`。通过这种方法,我们可以有效地计算出两个字符串的最长公共子序列。 ... [详细]
  • 利用C#技术实现Word文档的动态生成与编辑
    本文通过一个简单的示例,介绍了如何使用C#语言实现Word文档的动态生成与编辑功能。文章详细阐述了在项目中引用Word动态库的方法,并通过具体代码示例展示了如何创建和操作Word表格。此内容旨在为初学者提供参考和学习资料,欢迎读者提出宝贵意见和建议。 ... [详细]
  • 本文探讨了如何在C#中实现USB条形码扫描仪的数据读取,并自动过滤掉键盘输入,即使不知道设备的供应商ID(VID)和产品ID(PID)。通过详细的技术指导和代码示例,展示了如何高效地处理条形码数据,确保系统能够准确识别并忽略来自键盘的干扰信号。该方法适用于多种USB条形码扫描仪,无需额外配置设备信息。 ... [详细]
  • 深入解析:使用C++实现Python字节数组(struct)的高效处理方法 ... [详细]
  • 本文深入探讨了数据库性能优化与管理策略,通过实例分析和理论研究,详细阐述了如何有效提升数据库系统的响应速度和处理能力。文章首先介绍了数据库性能优化的基本原则和常用技术,包括索引优化、查询优化和存储管理等。接着,结合实际应用场景,讨论了如何利用容器化技术(如Docker)来部署和管理数据库,以提高系统的可扩展性和稳定性。最后,文章还提供了具体的配置示例和最佳实践,帮助读者在实际工作中更好地应用这些策略。 ... [详细]
  • C#编程指南:实现列表与WPF数据网格的高效绑定方法 ... [详细]
  • 本文详细解析了 MySQL 5.7.20 版本中二进制日志(binlog)崩溃恢复机制的工作流程。假设使用 InnoDB 存储引擎,并且启用了 `sync_binlog=1` 配置,文章深入探讨了在系统崩溃后如何通过 binlog 进行数据恢复,确保数据的一致性和完整性。 ... [详细]
  • Java 零基础入门:SQL Server 学习笔记(第21篇)
    Java 零基础入门:SQL Server 学习笔记(第21篇) ... [详细]
  • MySQL性能优化与调参指南【数据库管理】
    本文详细探讨了MySQL数据库的性能优化与参数调整技巧,旨在帮助数据库管理员和开发人员提升系统的运行效率。内容涵盖索引优化、查询优化、配置参数调整等方面,结合实际案例进行深入分析,提供实用的操作建议。此外,还介绍了常见的性能监控工具和方法,助力读者全面掌握MySQL性能优化的核心技能。 ... [详细]
  • 本文深入探讨了 HTML 中的 `margin` 属性,详细解析了其基本特性和应用场景。文章不仅介绍了 `margin` 的基本概念,还重点讨论了垂直外边距合并现象,并分析了 `margin` 在块级元素与内联元素中的不同表现。通过实例和代码示例,帮助读者全面理解 `margin` 的使用技巧和常见问题。 ... [详细]
  • HTTP协议作为互联网通信的基础,其重要性不言而喻。相比JDK自带的URLConnection,HttpClient不仅提升了易用性和灵活性,还在性能、稳定性和安全性方面进行了显著优化。本文将深入解析HttpClient的使用方法与技巧,帮助开发者更好地掌握这一强大的工具。 ... [详细]
  • 技术日志:深入探讨Spark Streaming与Spark SQL的融合应用
    技术日志:深入探讨Spark Streaming与Spark SQL的融合应用 ... [详细]
author-avatar
许雅惠嘉文意芝
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有