QT数据库(四):QSqlRelationalTableModel 类

news2025/2/27 14:16:36

关系数据库概念

例如下列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);
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入C语言文件操作:从库函数到系统调用

引言 文件操作是编程中不可或缺的一部分,尤其在C语言中,文件操作不仅是处理数据的基本手段,也是连接程序与外部世界的重要桥梁。C语言提供了丰富的库函数来处理文件,如 fopen、fclose、fread、fwrite 等。然而,这些库…

【HarmonyOS NEXT】ArkTs数据类型解析与使用

1. 背景 为什么设计ArkTS? 1.1 其它语言有版权【Java?Kotlin?】以及历史问题【Java内存?】 1.2 生态,可复用前端生态的三方库,兼容JS/TS语言生态ArkTs解决了JS/TS中的哪些问题? 2.1 **程序健壮性…

H5 scss 移动端的样式适配

在移动端样式的scss文件中,出现了这些变量 env() 与 constant() 设置安全区域,是css里IOS11新增的属性,webkit的css函数,用于设定安全区域与边界的距离,有4个预定义变量: safe-area-inset-left: 安全区域距…

YOLOv5与ViT目标检测中的热力图应用教程

文章目录 前言一、热力图介绍1、热力图应用说明2、热力图代码整体思路3、实验效果二、heatmap类解读三、GradCAM、GradCAMPlusPlus, GradCAM, XGradCAM, EigenCAM, HiResCAM, LayerCAM等类源码解读1、GradCAM类源码2、BaseCAM类源码解读1、BaseCAM源码2、forward函数源码解读ou…

组织病理学图像的再识别|文献速递-生成式模型与transformer在医学影像中的应用

Title 题目 Re-identification from histopathology images 组织病理学图像的再识别 01 文献速递介绍 在光学显微镜下评估苏木精和伊红(H&E)染色切片是肿瘤病理诊断的标准程序。随着全切片扫描仪的出现,能够将玻璃切片数字化为所谓的…

如何用重构解锁高效 Vue 开发之路

文章目录 摘要引言什么是代码重构为什么要减少重复逻辑Vue 示例代码问题场景初始代码的痛点重构后的通用组件 TaskList.vue详细说明 重用通用组件详细说明 模拟数据与运行结果 QA环节总结参考资料 摘要 代码重构是改善代码质量的重要手段,特别是在减少重复逻辑方面…

用户发送请求后服务端i/o工作过程

华子目录 服务端i/o介绍磁盘i/o机械磁盘的寻道时间、旋转延迟和数据传输时间常见的机械磁盘平均寻道时间值常见磁盘的平均延迟时间每秒最大IOPS的计算方法 网络i/o网络I/O处理过程磁盘和网络i/o 一次完整的请求在内部的执行过程 服务端i/o介绍 i/o在计算机中指Input/Output&am…

QT c++ 测控系统 一套报警规则(上)

本文适用于pc based的测控系统的上位机,定义了一套报警规则。 由5个部分组成:自定义4布尔类、在全局文件定义工位错误结构体和结构体变量,其它地方给此变量的当前值成员赋值,报警线程类、数据库保存类、弹框类。 1.自定义4布尔类…

概率论得学习和整理24:EXCEL的各种图形,统计图形

目录 0 EXCEL的各种图形,统计图形 1 统计图形 / 直方图 / 其实叫 频度图 hist最合适(用原始数据直接作图) 1.1 什么是频度图 1.2 如何创建频度图,一般是只选中1列数据(1个数组) 1.3 如何修改频度图的宽度 1.4 hist图的一个特…

项目二十三:电阻测量(需要简单的外围检测电路,将电阻转换为电压)测量100,1k,4.7k,10k,20k的电阻阻值,由数码管显示。要求测试误差 <10%

资料查找: 01 方案选择 使用单片机测量电阻有多种方法,以下是一些常见的方法及其原理: 串联分压法(ADC) 原理:根据串联电路的分压原理,通过测量已知电阻和待测电阻上的电压,计算出…

Linux中 vim 常用命令大全详细讲解

文章目录 前言一、Vim 基本操作 🕹️1.1 打开或创建1.2 退出编辑1.3 模式切换 二、Vim 光标移动命令 ↕️2.1 基本移动2.2 行内移动2.3. 单词移动2.4. 页面移动2.5. 行跳转 三、Vim 文本编辑命令 📋3.1 插入和删除3.2 复制、剪切与粘贴3.3 替换与修改 四…

ARM架构服务器国产麒麟V10安装nginx

目前ARM架构服务器越来越多的出现在我们的工作中,尤其大数据时代的需要,服务器操作系统linux国产化进程的推进。本人已经编写了很多ARM架构下安装java环境,安装mysql,安装redis等等的文档。现在我们演示一下国产麒麟V10安装nginx-…

【SQL】语句练习

1. 更新 1.1单表更新 例1: 所有薪水低于30000的员工薪水增加10% SQL命令&#xff1a; update employee set salarysalary*1.1 where salary < 30000; 1.2多表更新 例1: 将下图两表张三的语文成绩从95修改为80 SQL命令&#xff1a; update exam set score80 where subjec…

【开源】使用环信UIKit for uniapp 做一个IM即时聊天应用

环信单群聊 UIKit 是基于环信即时通讯云 IM SDK 开发的一款即时通讯 UI 组件库&#xff0c;提供各种组件实现会话列表、聊天界面、联系人列表及后续界面等功能&#xff0c;帮助开发者根据实际业务需求快速搭建包含 UI 界面的即时通讯应用。 本文教大家使用环信 uniapp UIKit 快…

用 Python Turtle 绘制经典杰瑞鼠:捕捉卡通世界中的小聪明

用 Python Turtle 绘制经典杰瑞鼠&#xff1a;捕捉卡通世界中的小聪明 &#x1f438; 前言 &#x1f438;&#x1f41e;往期绘画>>点击进所有绘画&#x1f41e;&#x1f40b; 效果图 &#x1f40b;&#x1f409; 代码 &#x1f409; &#x1f438; 前言 &#x1f438; 杰…

Excel拆分脚本

Excel拆分 工作表按行拆分为工作薄 工作表按行拆分为工作薄 打开要拆分的Excel文件&#xff0c;使用快捷键&#xff08;AltF11&#xff09;打开脚本界面&#xff0c;选择要拆分的sheet&#xff0c;打开Module&#xff0c;在Module中输入脚本代码&#xff0c;然后运行脚本 Su…

ModStartCMS v9.1.0 数据Grid样式优化,富文本格式刷支持,精简代码

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议&#xff0c;免费且不限制商业使用。 功能特性 丰富的模块市…

2024年12月16日Github流行趋势

项目名称&#xff1a;PDFMathTranslate 项目维护者&#xff1a;Byaidu reycn hellofinch Wybxc YadominJinta项目介绍&#xff1a;基于 AI 完整保留排版的 PDF 文档全文双语翻译&#xff0c;支持 Google/DeepL/Ollama/OpenAI 等服务&#xff0c;提供 CLI/GUI/Docker。项目star数…

3-机器人视觉-机器人抓取与操作

文章目录 3机器人视觉目录 1. 传感器和标定摄像头模型Intrinsic MatrixExtrinsic Matrix 标定内参标定手眼标定和外参标定 力传感器&其它传感器其它传感器 2. 神经网络和图像处理2D特征处理常见架构 训练流程推理流程部署流程2D 图像任务3D Point Cloud FeaturePointNet Ap…

Java String详解(二)

上一篇博客&#xff1a;Java String详解&#xff08;一&#xff09; 写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blo…