QT数据库(四):QSqlRelationalTableModel 类

关系数据库概念

例如下列departments、majors、studInfo 这 3 个数据表之间存在关系。

主键与外键

标记“**”的是主键字段,标记“*”的是外键字段。主键字段是一个数据表中表示记录唯一性的字段,例如 studInfo 数据表中的 studID 字段。外键字段是与其他数据表的主键存在关系的字段。

studInfo 数据表中的 departID 字段就是外键字段,它与 departments 数据表中的 departID 字段存在关系。studInfo 数据表中的 departID 存储的是编码,编码的含义需要查询departments 数据表中具有相同 departID 值的一条记录中的 department 字段才可知道。

编码与含义

studInfo 数据表中的 departID 和 majorID 字段一般称为编码字段,departments 数据表和 majors 数据表一般称为编码表。编码表的一条记录有编码字段和编码含义字段,例如 departments 数据表 中,departID 是编码字段,department 是编码含义字段。

在数据库设计中经常采用编码字段和编码表,一是可以减少数据表存储的数据量,例如在studInfo 数据表中不用存储每个学院的全名,而只需存储学院的编码;二是便于修改,例如如果某个学院的名称改变了,那么只需修改 departments 数据表中的一条记录。

主从关系

具有外键关联的两个数据表构成主从关系,主表中的一条记录关联从表中的多条记录。例如, departments 数据表是主表,studInfo 数据表是从表,通过 departments 数据表中一条记录的departID值可以查询出 studInfo 数据表中的多条记录。在删除departments表中的某个学院后,studInfo 表中隶属于该学院的记录也将会被删除。这种方式更新、修改删除都比较方便。

QSqlRelationalTableModel介绍

QSqlRelationalTableModel 是 QSqlTableModel 的子类,它可以作为关系数据表的模型类。

创建对象和设置数据表

QSqlRelationalTableModel 只有一种构造函数,其函数原型定义如下:

QSqlRelationalTableModel(QObject *parent = nullptr, const QSqlDatabase &db=QSqlDatabase())

在创建 QSqlRelationalTableModel 对象时,如果不指定参数 db 的值,就使用应用程序默认的

数据库连接。

创建数据库连接后,用函数 setTable()设置数据表:

tabModel= new QSqlRelationalTableModel(this);

tabModel->setTable("studInfo"); //设置数据表

设置外键关系

关键函数是 setRelation(),该函数原型定义如下:

void QSqlRelationalTableModel::setRelation(int column, const QSqlRelation &relation)

其中,参数 column 是外键字段的字段序号,例如 studInfo 数据表中 departID 字段的序号是 3;参 数 relation 是一个 QSqlRelation 类型的变量,用于表示外键字段关联的编码表、编码字段、编含义字段等信息。

QSqlRelation 类的构造函数原型定义如下:

QSqlRelation(const QString &tableName, const QString &indexColumn, const QString &displayColumn)

其中,tableName 是编码表名称,indexColumn 是外键字段名称,displayColumn 是编码表中代码含义字段的名称。

当使用函数 setRelation()为一个外键字段设置关系时,QSqlRelationalTableModel 内部实际上会创建一个 QSqlTableModel 对象作为编码表的数据模型。

示例程序解读

设置数据表关系

在打开数据表时,创建一个QSqlRelationalTableModel并连接到DB数据库,设置模型的数据表为studInfo,编辑策略为OnManualSubmit(需要手动调用submitAll来更新到数据库)。

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

    //打开数据库
    DB=QSqlDatabase::addDatabase("QSQLITE"); //添加SQLITE数据库驱动
    DB.setDatabaseName(aFile);      //设置数据库名称
    //    DB.setHostName();
    //    DB.setUserName();
    //    DB.setPassword();
    if (DB.open())
        openTable();    //打开数据表
    else
        QMessageBox::warning(this, "错误", "打开数据库失败");
}


//打开数据表
void MainWindow::openTable()
{
    tabModel=new QSqlRelationalTableModel(this,DB);
    tabModel->setTable("studInfo");     //设置数据表
    tabModel->setEditStrategy(QSqlTableModel::OnManualSubmit);  //编辑策略
    tabModel->setSort(tabModel->fieldIndex("studID"),Qt::AscendingOrder);

    selModel=new QItemSelectionModel(tabModel,this);     //创建选择模型
    connect(selModel,&QItemSelectionModel::currentChanged,this, &MainWindow::do_currentChanged);
//    connect(selModel,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
//            this,SLOT(do_currentChanged(QModelIndex,QModelIndex)));

    ui->tableView->setModel(tabModel);
    ui->tableView->setSelectionModel(selModel);

    tabModel->setHeaderData(tabModel->fieldIndex("studID"),  Qt::Horizontal, "学号");
    tabModel->setHeaderData(tabModel->fieldIndex("name"),    Qt::Horizontal, "姓名");
    tabModel->setHeaderData(tabModel->fieldIndex("gender"),  Qt::Horizontal, "性别");
    tabModel->setHeaderData(tabModel->fieldIndex("departID"),Qt::Horizontal, "学院");
    tabModel->setHeaderData(tabModel->fieldIndex("majorID"), Qt::Horizontal, "专业");

    //设置代码字段的关系
    tabModel->setRelation(tabModel->fieldIndex("departID"),
                          QSqlRelation("departments","departID","department")); //学院
    tabModel->setRelation(tabModel->fieldIndex("majorID"),
                          QSqlRelation("majors","majorID","major"));            //专业

    ui->tableView->setItemDelegate(new QSqlRelationalDelegate(ui->tableView));  //为关系型字段设置默认代理组件
//    ui->tableView->setItemDelegateForColumn(tabModel->fieldIndex("departID"),
//                                            new QSqlRelationalDelegate(ui->tableView)); //为关系型字段设置缺省代理组件
   // ui->tableView->setItemDelegateForColumn(tabModel->fieldIndex("majorID"),
   //                                new QSqlRelationalDelegate(ui->tableView)); //为关系型字段设置缺省代理组件

    tabModel->select(); //查询数据表的数据

    ui->actOpenDB->setEnabled(false);
    ui->actRecAppend->setEnabled(true);
    ui->actRecInsert->setEnabled(true);
    ui->actRecDelete->setEnabled(true);
    ui->actFields->setEnabled(true);
}

示例代码中,有两个点比较关键

1、设置外键字段的关系

    tabModel->setRelation(tabModel->fieldIndex("departID"),QSqlRelation("departments","departID","department")); //学院
    tabModel->setRelation(tabModel->fieldIndex("majorID"),QSqlRelation("majors","majorID","major"));            //专业

将studInfo数据表中departID外键字段设置为另一个编码表departments对应的字段,并设置字段的含义为编码表中department字段。这样在显示studInfo表中departID时,会自动映射到departments编码表中的department字段。

2、设置代理组件

ui->tableView->setItemDelegate(new QSqlRelationalDelegate(ui->tableView));

设置后,假设在 tableView 中编辑“学院”和“专业”两个字段的数据时,就会出现一个下拉列表框,

下拉列表内容就是编码表中编码含义字段的所有记录的数据。

添加插入删除记录

这个不用多讲,可查看往期博客

void MainWindow::on_actRecAppend_triggered()
{//添加记录
    tabModel->insertRow(tabModel->rowCount(),QModelIndex());    //在末尾添加一行
    QModelIndex curIndex=tabModel->index(tabModel->rowCount()-1,1);
    selModel->clearSelection();     //清空选择项
    selModel->setCurrentIndex(curIndex,QItemSelectionModel::Select); //设置当前行
}

void MainWindow::on_actRecInsert_triggered()
{//插入记录
    QModelIndex curIndex=ui->tableView->currentIndex(); //当前行的模型索引
    tabModel->insertRow(curIndex.row(),QModelIndex());
    selModel->clearSelection();
    selModel->setCurrentIndex(curIndex,QItemSelectionModel::Select);
}

void MainWindow::on_actRecDelete_triggered()
{//删除当前行
    tabModel->removeRow(selModel->currentIndex().row());
    tabModel->submitAll();      //立即提交修改
}

保存和取消

在打开连接数据库后,设置的OnManualSubmit,因此界面的改变想要更新到数据库,需要手动调用submitAll()或者revertAll()来保存或撤销。submitAll()将界面的更改更新到数据库中,revertAll()将界面的更改撤销,也就是重新读取之前数据库中的数据还原界面组件的显示。


void MainWindow::on_actSubmit_triggered()
{//保存修改
    bool res=tabModel->submitAll();
    if (!res)
        QMessageBox::information(this, "消息", "数据保存错误,错误信息\n"
                                 +tabModel->lastError().text());
    else
    {
        ui->actSubmit->setEnabled(false);
        ui->actRevert->setEnabled(false);
    }
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/938078.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】-学习笔记10

第八章、Linux下的火墙管理及优化 1.什么是防火墙 从功能角度来讲 防火墙是位于内部网和外部网之间的屏障,它按照系统管理员预先定义好的规则来控制数据包的进出 从功能实现角度来讲 火墙是系统内核上的一个模块netfilter(数据包过滤机制) …

SpringBoot 手动实现动态切换数据源 DynamicSource (中)

大家好,我是此林。 SpringBoot 手动实现动态切换数据源 DynamicSource (上)-CSDN博客 在上一篇博客中,我带大家手动实现了一个简易版的数据源切换实现,方便大家理解数据源切换的原理。今天我们来介绍一个开源的数据源…

Crawl4AI:一个为大型语言模型(LLM)和AI应用设计的网页爬虫和数据提取工具实战

这里写目录标题 一、crawl4AI功能及简介1、简介2、特性 二、项目地址三、环境安装四、大模型申请五、代码示例1.生成markdown2.结构化数据 一、crawl4AI功能及简介 1、简介 Crawl4AI 是一个开源的网页爬虫和数据抓取工具,一个python项目,主要为大型语言…

【银河麒麟高级服务器操作系统】有关dd及cp测试差异的现象分析详解

了解更多银河麒麟操作系统全新产品,请点击访问 麒麟软件产品专区:https://product.kylinos.cn 开发者专区:https://developer.kylinos.cn 文档中心:https://documentkylinos.cn dd现象 使用银河麒麟高级服务器操作系统执行两次…

sqli-labs靶场第26-30关

第26关 这关将逻辑运算符,注释符以及空格给过滤了 我们先使用单引号进行闭合 这时我们查看源代码可以看到这一关过滤了很多字符 可以看到这里将or and / -- # 空格等字符都被注释了 空格被过滤了我们可以使用()来代替,and和or可以使用双写来绕过 因为…

ik分词器了解 和 通过zip安装包的方式 将ik分词器安装到elasticsearch中

目录 1. ik分词器的作用(效果) (1)标准分析器效果 (2)ik_smart分词 (3)ik_max_word分词 2. 首先根据自己的elasticsearch的版本下载对应的ik分词器版本 3. 将下载好的ik分词器…

实景视频与模型叠加融合?

[视频GIS系列]无人机视频与与实景模型进行实时融合_无人机视频融合-CSDN博客文章浏览阅读1.5k次,点赞28次,收藏14次。将无人机视频与实景模型进行实时融合是一个涉及多个技术领域的复杂过程,主要包括无人机视频采集、实景模型构建、视频与模型…

【解决】k8s使用kubeadm初始化集群失败问题整理

执行提示命令,查看报错信息 journalctl -xeu kubelet1、错误:running with swap on is no 报错 "command failed" err"failed to run Kubelet: running with swap on is no 解决: swap未禁用,需要禁用swap&…

aws(学习笔记第十七课) SQS Amazon Simple Queue Service服务

aws(学习笔记第十七课) SQS Amazon Simple Queue Service服务 学习内容: 使用SQS Amazon Simple Queue Service服务整体代码(nodejs的通常工程)代码动作 1. 使用SQS Amazon Simple Queue Service服务 利用应用程序来学习SQS 创建S3$ aws s…

qt-C++笔记之父类窗口、父类控件、对象树的关系

qt-C笔记之父类窗口、父类控件、对象树的关系 code review! 参考笔记 1.qt-C笔记之父类窗口、父类控件、对象树的关系 2.qt-C笔记之继承自 QWidget和继承自QObject 并通过 getWidget() 显示窗口或控件时的区别和原理 3.qt-C笔记之自定义类继承自 QObject 与 QWidget 及开发方式…

VMware ubuntu12.04怎么设置静态IP联网

记得刚开始学习嵌入式就是从ubuntu12.04的环境开始学习的C语言,当时没有弄清楚怎么设置静态IP联网,现在写一篇文章。 1.首先,关闭ubuntu的网络; 2.电脑使用的是wifi,将VMware桥接到该网卡上; 3.在虚拟机设置里面选择桥…

计算机视觉中的图像滤波与增强算法

摘要: 本文深入探讨了计算机视觉领域中的图像滤波与增强算法。首先介绍了图像滤波与增强的基本概念和重要性,随后详细阐述了线性滤波算法中的均值滤波和高斯滤波,以及非线性滤波算法中的中值滤波和双边滤波,包括它们的原理、数学模…

AI大模型学习笔记|神经网络与注意力机制(逐行解读)

来源分享链接:通过网盘分享的文件:详解神经网络是如何训练的 链接: https://pan.baidu.com/s/12EF7y0vJfH5x6X-0QEVezg 提取码: k924 内容摘要:本文深入探讨了神经网络与注意力机制的基础,以及神经网络参数训练的过程。以鸢尾花数…

腾讯云系统盘扩容

在腾讯云申请空间后,只要执行三行命令 云硬盘 在线扩展系统盘分区及文件系统-操作指南-文档中心-腾讯云 安装工具 yum install -y cloud-utils-growpart 给/eav/vda1扩分区 LC_ALLen_US.UTF-8 growpart /dev/vda 1 挂载扩容 ext4 文件系统 resize2fs /dev/vda1 …

【控制系统】深入理解反步控制(Backstepping) | 反步法控制器原理与应用实例解析(附Matlab/Simulink仿真实现)

💯 欢迎光临清流君的博客小天地,这里是我分享技术与心得的温馨角落 💯 🔥 个人主页:【清流君】🔥 📚 系列专栏: 运动控制 | 决策规划 | 机器人数值优化 📚 🌟始终保持好奇心&…

构建树莓派温湿度监测系统:从硬件到软件的完整指南

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…

人工智能系统

介绍人工智能 的基础书 点击这里 1.1 深度学习的历史,现状与发展 本章将介绍深度学习的由来,现状和趋势,让读者能够了解人工智能系统之上的深度学习负载的由来与趋势,为后面理解深度学习系统的设计和权衡形成初步的基础。我们在后…

NVR小程序接入平台EasyNVR设置预置位显示“参数错误”的解决方法

视频监控技术在现代社会中的应用已经变得越来越广泛,从城市安防到家庭安全,从交通管理到商业监控,其作用无处不在。随着科技的不断进步,视频监控不仅提高了安全性,还带来了许多新的机遇和挑战。 近期,我们收…

下载红米Note 9 Pro5G对应的LineageOS代码下载及编译

构建 LineageOS 进入网站:Info about gauguin | LineageOS Wiki,点击:Build for yourself,里面有详细的教程,我这里就按照Note 9 Pro 5G来。 机器环境 Ubuntu环境为:20.04.6LinagesOS版本:21-…

如何在 Ubuntu 22.04 上安装 Strapi CMS

简介 Strapi 是一个使用 JavaScript 构建的开源、无头内容管理系统 (CMS)。与其他无头 CMS 一样,Strapi 开箱即用不带前端。它使用 API 作为其前端,允许你使用流行的框架(如 React 和 Next.js)构建网站。Strapi 基于插件系统&…