Qt QSqlTableModel详解

背景知识:

Qt SQL的API分为不同层:

  1. 驱动层 

 驱动层  对于QT是基于C++来实现的框架,该层主要包括QSqlDriver、QSqlDriverCreator、QSqlDriverCreatorbase、QSqlDriverPlugin and QSqlResult。这一层提供了特定数据库和SQL API层之间的底层桥梁。

  1. SQL API层

SQL API层  对于SQL API 层提供了数据库的访问相关类,其中,QSqlDataBase类进行连接,QSqlQuery完成数据库的交互。除此之外,还有QSqlError、QSqlField、QSqlIndex and QSqlRecord类。

  1. 用户接口层

用户接口层  用户接口层的几个类实现将数据库中的数据链接到窗口部件上,这些类是使用模型/视图框架实现的,他们是更高层次的抽象,主要包括QSqlQureyModel,QSqlTableModel and QSqlRelationalTableModel。

用户接口层的类使用模型/视图框架实现了将数据库中的数据链接到窗口控件上

QTableView是常用的内容显示视图组件。数据模型类有:QSqlQueryModel 、QSqlTableModel 、QSqlRelationalTableModel

Qt QSqlTableModel详解_第1张图片

QSqlQueryModel :通过设置SELECT语句查询获取内容,Model数据是只读的,不能进行编辑。

QSqlTableModel : 直接设置一个数据表的名称,可以获取数据表的全部记录,结果是可编辑的。实现对数据的编辑、插入、删除等操作。实现数据的排序和过滤。

QSqlRelationalTableModel: 编辑一个数据表,将代码字段通过关系与代码表关联,将代码字段的编辑转换为直观的内容选择编辑。

QSqlTableModel

直接设置一个数据表的名称,可以获取数据表的全部记录,结果是可编辑的。实现对数据的编辑、插入、删除等操作。实现数据的排序和过滤。

一般是模型使用QSqlTableModel,视图使用QTableView。

优点:

  1. 简单易用,可以通过model直接操作字段数据
  2. 支持编辑,直接在视图中添加、修改、删除数据。
  3. 内置排序和过滤功能

缺点

  1. 只有用户单张表数据,无法执行自定义的sql语句
  2. 性能稍差,需要额外处理字段信息和刷新模型
  3. 不支持事务操作

常用的api函数

//设置数据表的名称,不立即读取数据

virtual void setTable(const QString &tableName);

//从字段获取到字段的索引号

int fieldIndex(const QString &fieldNameconst;

//数据表的主索引

QSqlIndex primaryKey() const;

//设置编辑策略

virtual void setEditStrategy(EditStrategy strategy);

enum EditStrategy {OnFieldChangeOnRowChangeOnManualSubmit};

OnFieldChange字段变化立即更新到数据库

OnRowChange当前行变换时更新到数据库

OnManualSubmit所有修改暂时缓存,只有手动调submitAll才保存修改到数据库

bool isDirty() const;//若有未更新到数据库的修改,返回true.

bool submitAll();//提交所有未更新的修改到数据库

void revertAll();//取消所有未更新的修改

QSqlError lastError() const;//获取错误信息

//设置模型的表头

bool setHeaderData(int sectionQt::Orientation orientationconst QVariant &value,nt role = Qt::EditRoleoverride;

//模型的行数

int rowCount(const QModelIndex &parent = QModelIndex()) const override;

//一条空记录获取字段名,没有值

QSqlRecord record() const;

//获取某行的记录数据

QSqlRecord record(int rowconst;

//QSqlRecord的value函数,获取某个字段的值

QVariant value(const QStringnameconst;

QModelIndex index(int rowint columnconst QModelIndex &parent = QModelIndex()) const override;

//QModelIndex 类的data函数,从而得到某行某列的数据

inline QVariant data(int role = Qt::DisplayRoleconst;或者

QVariant data(const QModelIndex &idxint role = Qt::DisplayRoleconst override;

//QSqlRecord类设置某个字段的值

void setValue(int iconst QVariantval);

void setValue(const QStringnameconst QVariantval);

//插入列

inline bool insertColumn(int columnconst QModelIndex &parent = QModelIndex());

//在某row行前插入数据

inline bool insertRow(int rowconst QModelIndex &parent = QModelIndex());

bool insertRows(int rowint countconst QModelIndex &parent = QModelIndex()) override;

bool insertRecord(int rowconst QSqlRecord &record);

//设置某行某列的数据

bool setData(const QModelIndex &indexconst QVariant &valueint role = Qt::EditRoleoverride;

//删除某行记录

bool removeRows(int rowint countconst QModelIndex &parent = QModelIndex()) override;

inline bool removeRow(int rowconst QModelIndex &parent = QModelIndex());

//修改某行的值

bool setRecord(int rowconst QSqlRecord &record);

//调用其基类QSqlQueryModel的setQuery函数,实现精准过滤,并显示部分字段

void setQuery(const QSqlQuery &query);

void setQuery(const QString &queryconst QSqlDatabase &db = QSqlDatabase());

tabModel->QSqlQueryModel::setQuery("select empNo, Name from employee where Name='王五'");

setQuery函数相当于QSqlQuery对象执行了exec函数

//按某一列排序和排序规则,需要调用select函数才生效,实际上是sql上午ORDER BY子句

virtual void setSort(int columnQt::SortOrder order);

//设置记录数据的过滤条件,过滤的字符串实际上为sql语句where后的字段。调用setFilter后无需调用select函数就可以立即刷新记录

virtual void setFilter(const QString &filter);

//查询数据表的数据,并使用设置的排序和过滤规则这是查询并显示全部的字段数据

virtual bool select();

//清除释放所有数据

void clear() override;

例子

  1. 数据库使用SQLite数据库,格式为.db3
  2. 模型使用QSqlTableModel,视图使用QTableView
  3. 因为QSqlTableModel可编辑,可使用代理处理编辑操作。
  4. QSqlTableModel的数据和界面其他控件通过QDataWidgetMapper进行关联
  5. 这里使用的OnManualSubmit:所有修改暂时缓存,只有手动调submitAll才保存修改到数据库。

打开数据库:

void MainWindow::on_actOpenDB_triggered()
{
    QString aFile=QFileDialog::getOpenFileName(this,"选择数据库文件","",
                             "SQL Lite数据库(*.db *.db3)");
    if (aFile.isEmpty())  //选择SQL Lite数据库文件
       return;

//打开数据库
    DB=QSqlDatabase::addDatabase("QSQLITE"); //添加 SQL LITE数据库驱动
    DB.setDatabaseName(aFile); //设置数据库名称
//    DB.setHostName();
//    DB.setUserName();
//    DB.setPassword();
    if (!DB.open())   //打开数据库
    {
        QMessageBox::warning(this, "错误", "打开数据库失败",
                                 QMessageBox::Ok,QMessageBox::NoButton);
        return;
    }

//打开数据表
    openTable();
}

打开数据表、设置表头、选择模型、数据映射、信号槽

void MainWindow::openTable()
{//打开数据表
    tabModel=new QSqlTableModel(this,DB);//数据表
    tabModel->setTable("employee"); //设置数据表
    tabModel->setEditStrategy(QSqlTableModel::OnManualSubmit);//数据保存方式,OnManualSubmit , OnRowChange
    tabModel->setSort(tabModel->fieldIndex("empNo"),Qt::AscendingOrder); //排序
    if (!(tabModel->select()))//查询数据
    {
       QMessageBox::critical(this, "错误信息",
              "打开数据表错误,错误信息\n"+tabModel->lastError().text(),
                 QMessageBox::Ok,QMessageBox::NoButton);
       return;
    }

//字段显示名
    tabModel->setHeaderData(tabModel->fieldIndex("empNo"),Qt::Horizontal,"工号");
    tabModel->setHeaderData(tabModel->fieldIndex("Name"),Qt::Horizontal,"姓名");
    tabModel->setHeaderData(tabModel->fieldIndex("Gender"),Qt::Horizontal,"性别");
    tabModel->setHeaderData(tabModel->fieldIndex("Height"),Qt::Horizontal,"身高");
    tabModel->setHeaderData(tabModel->fieldIndex("Birthday"),Qt::Horizontal,"出生日期");
    tabModel->setHeaderData(tabModel->fieldIndex("Mobile"),Qt::Horizontal,"手机");
    tabModel->setHeaderData(tabModel->fieldIndex("Province"),Qt::Horizontal,"省份");
    tabModel->setHeaderData(tabModel->fieldIndex("City"),Qt::Horizontal,"城市");
    tabModel->setHeaderData(tabModel->fieldIndex("Department"),Qt::Horizontal,"部门");
    tabModel->setHeaderData(tabModel->fieldIndex("Education"),Qt::Horizontal,"学历");
    tabModel->setHeaderData(tabModel->fieldIndex("Salary"),Qt::Horizontal,"工资");

    tabModel->setHeaderData(tabModel->fieldIndex("Memo"),Qt::Horizontal,"备注"); //这两个字段不再tableView中显示
    tabModel->setHeaderData(tabModel->fieldIndex("Photo"),Qt::Horizontal,"照片");

    theSelection=new QItemSelectionModel(tabModel);//关联选择模型
//theSelection当前项变化时触发currentChanged信号
    connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
            this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));
//选择行变化时
    connect(theSelection,SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
            this,SLOT(on_currentRowChanged(QModelIndex,QModelIndex)));

    ui->tableView->setModel(tabModel);//设置数据模型
    ui->tableView->setSelectionModel(theSelection); //设置选择模型
    ui->tableView->setColumnHidden(tabModel->fieldIndex("Memo"),true);//隐藏列
    ui->tableView->setColumnHidden(tabModel->fieldIndex("Photo"),true);//隐藏列

//tableView上为“性别”和“部门”两个字段设置自定义代理组件
    QStringList strList;
    strList<<"男"<<"女";
    bool isEditable=false;
    delegateSex.setItems(strList,isEditable);
    ui->tableView->setItemDelegateForColumn(
       tabModel->fieldIndex("Gender"),&delegateSex); //Combbox选择型

    strList.clear();
    strList<<"销售部"<<"技术部"<<"生产部"<<"行政部";
    isEditable=true;
    delegateDepart.setItems(strList,isEditable);
    ui->tableView->setItemDelegateForColumn(tabModel->fieldIndex("Department"),&delegateDepart); //Combbox选择型

//创建界面组件与数据模型的字段之间的数据映射
    dataMapper= new QDataWidgetMapper();
    dataMapper->setModel(tabModel);//设置数据模型
    dataMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);//

//    dataMapper->setItemDelegate(new QSqlRelationalDelegate(this)); //含有外键的
//界面组件与tabModel的具体字段之间的联系
    dataMapper->addMapping(ui->dbSpinEmpNo,tabModel->fieldIndex("empNo"));
    dataMapper->addMapping(ui->dbEditName,tabModel->fieldIndex("Name"));
    dataMapper->addMapping(ui->dbComboSex,tabModel->fieldIndex("Gender"));

    dataMapper->addMapping(ui->dbSpinHeight,tabModel->fieldIndex("Height"));
    dataMapper->addMapping(ui->dbEditBirth,tabModel->fieldIndex("Birthday"));
    dataMapper->addMapping(ui->dbEditMobile,tabModel->fieldIndex("Mobile"));

    dataMapper->addMapping(ui->dbComboProvince,tabModel->fieldIndex("Province"));
    dataMapper->addMapping(ui->dbEditCity,tabModel->fieldIndex("City"));
    dataMapper->addMapping(ui->dbComboDep,tabModel->fieldIndex("Department"));

    dataMapper->addMapping(ui->dbComboEdu,tabModel->fieldIndex("Education"));
    dataMapper->addMapping(ui->dbSpinSalary,tabModel->fieldIndex("Salary"));

    dataMapper->addMapping(ui->dbEditMemo,tabModel->fieldIndex("Memo"));

//    dataMapper->addMapping(ui->dbPhoto,tabModel->fieldIndex("Photo")); //图片无法直接映射

    dataMapper->toFirst();//移动到首记录

    getFieldNames();//获取字段名称列表,填充ui->groupBoxSort组件

//更新actions和界面组件的使能状态
    ui->actOpenDB->setEnabled(false);

    ui->actRecAppend->setEnabled(true);
    ui->actRecInsert->setEnabled(true);
    ui->actRecDelete->setEnabled(true);
    ui->actScan->setEnabled(true);

    ui->groupBoxSort->setEnabled(true);
    ui->groupBoxFilter->setEnabled(true);
}

添加操作

void MainWindow::on_actRecAppend_triggered()
{//添加记录
    tabModel->insertRow(tabModel->rowCount(),QModelIndex()); //在末尾添加一个记录

    QModelIndex curIndex=tabModel->index(tabModel->rowCount()-1,1);//创建最后一行的ModelIndex
    theSelection->clearSelection();//清空选择项
    theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);//设置刚插入的行为当前选择行

    int currow=curIndex.row(); //获得当前行
    tabModel->setData(tabModel->index(currow,0),2000+tabModel->rowCount()); //自动生成编号
    tabModel->setData(tabModel->index(currow,2),"男");
// 插入行时设置缺省值,需要在primeInsert()信号里去处理
}

 删除

void MainWindow::on_actRecDelete_triggered()
{//删除当前记录
//    tabModel->removeRow(theSelection->currentIndex().row());

    QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
    tabModel->removeRow(curIndex.row()); //删除最后一行

}

保存

void MainWindow::on_actSubmit_triggered()
{//保存修改
    bool res=tabModel->submitAll();

    if (!res)
        QMessageBox::information(this, "消息", "数据保存错误,错误信息\n"+tabModel->lastError().text(),
                                 QMessageBox::Ok,QMessageBox::NoButton);
    else
    {
        ui->actSubmit->setEnabled(false);
        ui->actRevert->setEnabled(false);
    }
}

取消

void MainWindow::on_actRevert_triggered()
{//取消修改
    tabModel->revertAll();
    ui->actSubmit->setEnabled(false);
    ui->actRevert->setEnabled(false);
}

排序

void MainWindow::on_radioBtnAscend_clicked()
{//升序 tabModel->setSort(ui->comboFields->currentIndex(),Qt::AscendingOrder);
    tabModel->select();
}

void MainWindow::on_radioBtnDescend_clicked()
{//降序   tabModel->setSort(ui->comboFields->currentIndex(),Qt::DescendingOrder);
    tabModel->select();
}

过滤

void MainWindow::on_radioBtnMan_clicked()
{
    tabModel->setFilter(" Gender='男' ");
}

void MainWindow::on_radioBtnWoman_clicked()
{
    tabModel->setFilter(" Gender='女' ");
}

void MainWindow::on_radioBtnBoth_clicked()
{
    tabModel->setFilter("");
}


或者是直接调用QSqlQueryModel的setQuery函数,这样视图就会按实际查询的字段显示
void MainWindow::on_actPhotoClear_triggered()
{
    tabModel->QSqlQueryModel::setQuery("select empNo, Name from employee where Name='王五'");
}

你可能感兴趣的:(qt,qt,开发语言)