找回密码
 立即注册
收起左侧

第25篇 数据库(五)SQL表格模型QSqlTableModel

82
回复
59914
查看
[复制链接]
累计签到:1564 天
连续签到:1 天
来源: 2013-5-21 20:36:22 显示全部楼层 |阅读模式
SQL表格模型QSqlTableModel

版权声明

该文章原创于Qter开源社区(www.qter.org),作者yafeilinux,转载请注明出处!




导语

在上一篇我们讲到只读的QsqlQueryModel模型其实也可以实现编辑功能的,但是实现起来很麻烦。而QSqlTableModel提供了一个一次只能操作单个SQL表的读写模型,它是QSqlQuery的更高层次的替代品,可以浏览和修改独立的SQL表,并且只需编写很少的代码,而且不需要了解SQL语法。



环境:Windows Xp + Qt 4.8.4+QtCreator 2.6.2



目录

一、创建数据库
二、修改操作
三、查询操作
四、排序操作
五、删除操作
六、插入操作




正文


一、创建数据库


1.新建Qt Gui应用,项目名称为tableModel,基类QMainWindow,类名MainWindow


2.完成后打开tableModel.pro文件,将第一行代码更改为:
QT       += coregui sql
       然后保存文件。


3.向项目中添加新的C++头文件,名称为connection.h。完成后将其内容更改如下:
#ifndef CONNECTION_H
#define CONNECTION_H
#include <QSqlDatabase>
#include <QSqlQuery>
static bool createConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("database.db");
    if(!db.open()) return false;
    QSqlQuery query;
    query.exec(QString(
      "create table student (id int primary key, name vchar)"));
    query.exec(QString("insert into student values (0,'刘明')"));
    query.exec(QString("insert into student values (1,'陈刚')"));
    query.exec(QString("insert into student values (2,'王红')"));
    return true;
}
#endif // CONNECTION_H
       这里因为语句中使用了中文,所以使用了QString()进行编码转换,这个还需要在main()函数中设置编码。


4.下面将main.cpp文件更改如下:
#include "mainwindow.h"
#include <QApplication>
#include "connection.h"
#include <QTextCodec>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
    QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
    if(!createConnection())
       return 1;
    MainWindow w;
    w.show();
   
    return a.exec();
}
    这里的setCodecForCStrings()就是用来设置字符串编码的。


5.下面进入设计模式,向窗口上拖入LabelPush ButtonLine EditTable View等部件,进行界面设计,效果如下图所示。


6.完成后到mainwindow.h文件中,先包含头文件:
#include <QSqlTableModel>
然后添加私有对象声明:
QSqlTableModel *model;


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

这里创建一个QSqlTableModel后,只需使用setTable()来为其指定数据库表,然后使用select()函数进行查询,调用这两个函数就等价于执行了“select * from student”这个SQL语句。这里还可以使用setFilter()来指定查询时的条件,在后面会看到这个函数的使用。在使用该模型以前,一般还要设置其编辑策略,它由QSqlTableModel::EditStrategy枚举变量定义,一共有三个值,如下图所示。用来说明当数据库中的值被编辑后,什么情况下提交修改。

运行程序,效果如下图所示。

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


二、修改操作


1.我们进入“提交修改”按钮的单击信号槽,更改如下:
void MainWindow::on_pushButton_3_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 MainWindow::on_pushButton_4_clicked()
{
    model->revertAll();
}

3.在mainwindow.cpp文件中包含头文件:
#include <QMessageBox>
#include <QSqlError>


4.现在运行程序,我们将陈刚改为李强,如果我们点击撤销修改,那么它就会重新改为陈刚,而当我们点击提交修改后它就会保存到数据库,此时再点击撤销修改就修改不回来了。
       可以看到,这个模型可以将所有修改先保存到model中,只有当我们执行提交修改后,才会真正写入数据库。当然这也是因为我们在最开始设置了它的保存策略:
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
这里的OnManualSubmit表明我们要提交修改才能使其生效。


三、查询操作


1.进入“查询”按钮的单击信号槽,更改如下:
void MainWindow::on_pushButton_clicked()
{
    QString name = ui->lineEdit->text();
    //根据姓名进行筛选
    model->setFilter(QString("name = '%1'").arg(name));
    //显示结果
    model->select();
}
       使用setFilter()函数进行关键字筛选,这个函数是对整个结果集进行查询。


2.进入“显示全表”按钮的单击信号槽,更改如下:
void MainWindow::on_pushButton_2_clicked()
{
    model->setTable("student");   //重新关联表
    model->select();   //这样才能再次显示整个表的内容
}
为了再次显示整个表的内容,我们需要再次关联这个表。


3.下面运行程序,输入一个姓名,点击查询按钮后,就可以显示该记录了。再点击显示全表按钮则返回。如下图所示。


四、排序操作

分别进入id升序排序id降序排序按钮的单击信号槽,更改如下:
// 升序
void MainWindow::on_pushButton_7_clicked()
{
    model->setSort(0, Qt::AscendingOrder); //id属性即第0列,升序排列
    model->select();
}
// 降序
void MainWindow::on_pushButton_8_clicked()
{
    model->setSort(0, Qt::DescendingOrder);
    model->select();  
}
这里使用了setSort()函数进行排序,它有两个参数,第一个参数表示按第几个属性排序,表头从左向右,最左边是第0个属性,这里就是id属性。第二个参数是排序方法,有升序和降序两种。运行程序,效果如下图所示。


五、删除操作

我们进入“删除选中行”按钮的单击信号槽,更改如下:
void MainWindow::on_pushButton_6_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 MainWindow::on_pushButton_5_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语句,就算你不怎么懂数据库知识,也可以利用它进行大部分常用的操作。我们也看到了,这个模型提供了缓冲区,可以先将修改保存起来,当我们执行提交函数时,再去真正地修改数据库。当然,这个模型比前面的模型更高级,前面讲的所有操作,在这里都能执行。





涉及到的源码:



上一篇:第24篇 数据库(四)SQL查询模型QSqlQueryModel

下一篇: 第26篇 数据库(六)SQL关系表格模型QSqlRelationalTableModel

返回:系列教程目录   


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
参与人数 1人气 +2 收起 理由
newbix + 2 对我帮助很大!非常感谢!

查看全部评分总评分 : 人气 +2

回复

使用道具 举报

累计签到:11 天
连续签到:1 天
2015-5-2 20:09:36 显示全部楼层
我用的QSQLITE数据库,向里面不断的写数据,采用QSqlTableModel的insertRow()和setData()向数据库写数据的方式,并且附带表格显示。
一开始正常,运行一段时间后大概在第25**512个数据的时候会出错,表格上显示的数据就卡在这个位置,还会出现“column id is not unique Unable to fetch row”这种数据库的错误。
我用用rowCount()来获取行数
有人说:这是因为在用rowCount()来获取行数的时候,当数据超过256时,rowCount()获取的行数是错误的。
怎么解决这个问题??谢谢
回复 支持 1 反对 0

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-9-24 10:41:48 显示全部楼层
baoniu 发表于 2013-9-23 21:26
model->rowCount()获取不到数据库里总行数,只能获取UI上表格里面的总行数,有点老火 ...

那得看model里面的内容是不是数据库总的内容啊,如果不是,当然就无法获得了。
回复 支持 1 反对 0

使用道具 举报

累计签到:75 天
连续签到:1 天
2013-8-8 12:02:40 显示全部楼层
本帖最后由 noway 于 2013-8-8 12:05 编辑

楼主您好,有几个问题请教:
1.当使用createConnection() 函数新建数据库时,编译之后,在修改源文件中query.exec()内容无效 ,必须删除debug下的database文件我考虑是不是数据库保护了 。
2.我现在用的QT 版本是Qt Creator 2.8.0 Based on Qt 4.8.4 (MSVC 2010, 32 bit) 系统是win7(32)
  使用 query.exec(QString("insert into student values (0,'刘明')"));向数据库写入中文时,乱码。我下载您的程序
  编译运行正常。我检查了两者的区别。发现我使用的QT 版本默认编码是UTF-8,然后改为 GB2312 或者GB18030就正常了 这不知道为什么,楼主帮忙分析一下
3.在createConnection() 函数中query.exec()添加数据库数据时,ID 一样不能添加(比如ID都是2) ,我考虑这也是数据库保护作用吧
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-8-8 16:21:00 显示全部楼层
noway 发表于 2013-8-8 12:02
楼主您好,有几个问题请教:
1.当使用createConnection() 函数新建数据库时,编译之后,在修改源文件中quer ...

1.数据库文件创建一次就不再创建了,所以在源码中修改表格内容,需要先删除前面生成的data文件;
2.在新版本的Qt中默认使用utf8编码,所以直接使用query.exec("insert into student values (0,'刘明')");,然后将代码用utf8编码重新保存,在“编辑”“选择编码”中可以设置。

3.id是主键吧,主键是不能重复的,这个是sql的知识。
回复 支持 反对

使用道具 举报

累计签到:75 天
连续签到:1 天
2013-8-8 22:58:05 显示全部楼层
yafeilinux 发表于 2013-8-8 16:21
1.数据库文件创建一次就不再创建了,所以在源码中修改表格内容,需要先删除前面生成的data文件;
2.在新 ...

1,3 明白了 ,谢谢楼主。
第二个问题,直接使用query.exec("insert into student values (0,'刘明')"); 设置utf8不行 ,设置为system 就可以了,应该还是版本的问题
回复 支持 反对

使用道具 举报

累计签到:36 天
连续签到:1 天
2013-9-23 21:26:10 显示全部楼层
model->rowCount()获取不到数据库里总行数,只能获取UI上表格里面的总行数,有点老火
回复 支持 反对

使用道具 举报

累计签到:1 天
连续签到:1 天
2013-10-15 18:14:32 显示全部楼层
感谢yafeilinux,每天都能从你这里学到新知识
回复 支持 反对

使用道具 举报

累计签到:213 天
连续签到:1 天
2013-10-27 16:53:54 显示全部楼层
我在学习。顺利完成这本篇。
毕业设计就是做了一个类似的界面,站长的教程更简单。谢谢站长!
回复 支持 反对

使用道具 举报

累计签到:10 天
连续签到:1 天
2014-4-8 14:14:36 显示全部楼层
Qt里面可以同时建立两个工程嘛??
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2014-4-8 18:58:16 显示全部楼层
cloudyf 发表于 2014-4-8 14:14
Qt里面可以同时建立两个工程嘛??

当然可以了。
回复 支持 反对

使用道具 举报

累计签到:10 天
连续签到:1 天
2014-4-8 20:57:21 显示全部楼层
yafeilinux 发表于 2014-4-8 18:58
当然可以了。

不同的工程有什么意义嘛?比如说我要做一个平台,里面有很多不同的窗口,窗口实现不同的功能,这样需要建立不同的工程嘛
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2014-4-9 18:26:10 显示全部楼层
cloudyf 发表于 2014-4-8 20:57
不同的工程有什么意义嘛?比如说我要做一个平台,里面有很多不同的窗口,窗口实现不同的功能,这样需要建 ...

这个得看自己需求啊,一般在一个工程里面即可。因为一个工程编译后就是一个可执行文件(也就是一个独立的软件。)
回复 支持 反对

使用道具 举报

累计签到:3 天
连续签到:1 天
2014-6-18 14:31:39 显示全部楼层
想问一下yafei,下面代码的submitAll() 和 database().commit() ,为什么要有两个提交的?具体的作用分别是什么?

if (model->submitAll()) {
       model->database().commit(); //提交
    } else {
       model->database().rollback(); //回滚
       QMessageBox::warning(this, tr("tableModel"),
                             tr("数据库错误: %1")
                             .arg(model->lastError().text()));
    }
回复 支持 反对

使用道具 举报

累计签到:3 天
连续签到:1 天
2014-6-19 22:00:50 显示全部楼层
codemoon 发表于 2014-6-18 14:31
想问一下yafei,下面代码的submitAll() 和 database().commit() ,为什么要有两个提交的?具体的作用分别是 ...

大概弄明白了,submitAll是提交对model的更改到数据库,但这里的具体情况是因为要放到事务里执行,所以需要commit。
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2014-6-30 21:34:25 显示全部楼层
codemoon 发表于 2014-6-19 22:00
大概弄明白了,submitAll是提交对model的更改到数据库,但这里的具体情况是因为要放到事务里执行,所以需 ...

呵呵,是啊。
回复 支持 反对

使用道具 举报

累计签到:3 天
连续签到:1 天
2014-7-1 00:06:35 显示全部楼层
yafeilinux 发表于 2014-6-30 21:34
呵呵,是啊。

yafei老师什么时候继续更新课程呢?嘿嘿
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2014-7-2 22:22:49 显示全部楼层
codemoon 发表于 2014-7-1 00:06
yafei老师什么时候继续更新课程呢?嘿嘿

快了。
回复 支持 反对

使用道具 举报

累计签到:597 天
连续签到:1 天
2014-8-6 15:59:35 显示全部楼层
本帖最后由 shasidaran 于 2014-8-6 16:03 编辑

发现一处小错误  
3.向项目中添加新的C++头文件,名称为connection.h。完成后将其内容更改如下:
#ifndef CONNECTION_H
#define CONNECTION_H
#include <QSqlDatabase>
#include <QSqlQuery>
static bool createConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("database.db");
    if(!db.open()) return false;
    QSqlQuery query;
    query.exec(QString(
      "create tablestudent (id int primary key, name vchar)"));
    query.exec(QString("insert into student values (0,'刘明')"));
    query.exec(QString("insert into student values (1,'陈刚')"));
    query.exec(QString("insert into student values (2,'王红')"));
    return true;
}
#endif // CONNECTION_H
       这里因为语句中使用了中文,所以使用了QString()进行编码转换,这个还需要在main()函数中设置编码。


在一、3中create tablestudent应该是create table student
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2014-8-8 19:59:22 显示全部楼层
shasidaran 发表于 2014-8-6 15:59
发现一处小错误  
3.向项目中添加新的C++头文件,名称为connection.h。完成后将其内容更改如下:[/backcolo ...

嗯。代码是没有错误的,可能是发帖的时候格式乱了。
回复 支持 反对

使用道具 举报

尚未签到

2014-12-23 16:25:37 显示全部楼层
非常感谢 受教了 字数补丁
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了