QSqlRelationalTableModel 深度讲解与实战示例

        QSqlRelationalTableModel 是 Qt 中提供的高级数据库模型类,继承自 QSqlTableModel,专门用于处理关系型数据表的外键关联。它能在显示数据时自动将外键ID转换为关联表的实际值,极大简化了关系数据的展示和编辑。

一、核心特点

  1. 外键关系处理:自动将外键ID转换为关联表的可读值

  2. 可编辑性:支持通过视图直接编辑数据

  3. 组合框支持:在外键字段提供下拉框选择

  4. 数据一致性:维护关系完整性

二、基本用法示例

#include <QApplication>
#include <QSqlDatabase>
#include <QSqlRelationalTableModel>
#include <QTableView>
#include <QSqlQuery>
#include <QComboBox>
#include <QSqlRelation>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    
    // 建立内存数据库
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(":memory:");
    if (!db.open()) {
        qDebug() << "无法打开数据库";
        return 1;
    }
    
    // 创建示例表:员工表和部门表
    QSqlQuery query;
    query.exec("CREATE TABLE departments (id INTEGER PRIMARY KEY, name TEXT)");
    query.exec("INSERT INTO departments VALUES (1, '研发部')");
    query.exec("INSERT INTO departments VALUES (2, '市场部')");
    
    query.exec("CREATE TABLE employees ("
               "id INTEGER PRIMARY KEY, "
               "name TEXT, "
               "department_id INTEGER, "
               "FOREIGN KEY(department_id) REFERENCES departments(id))");
    
    query.exec("INSERT INTO employees VALUES (1, '张三', 1)");
    query.exec("INSERT INTO employees VALUES (2, '李四', 2)");
    query.exec("INSERT INTO employees VALUES (3, '王五', 1)");
    
    // 创建关系模型
    QSqlRelationalTableModel model;
    model.setTable("employees");
    
    // 设置部门ID到部门名称的关系
    model.setRelation(2, QSqlRelation("departments", "id", "name"));
    
    // 创建视图
    QTableView view;
    view.setModel(&model);
    view.setItemDelegate(new QSqlRelationalDelegate(&view));
    
    // 显示数据
    model.select();
    view.show();
    
    return app.exec();
}

三、核心方法详解

1. 设置表关系

// 设置关系:参数依次为(外键列索引, 关联表名, 关联ID列, 关联显示列)
model.setRelation(2, QSqlRelation("departments", "id", "name"));

// 设置多个关系
model.setRelation(3, QSqlRelation("positions", "pos_id", "pos_name"));
model.setRelation(4, QSqlRelation("levels", "level_id", "level_name"));

2. 数据操作

// 插入记录
QSqlRecord record = model.record();
record.setValue("name", "赵六");
record.setValue("department_id", 2); // 使用实际ID值
model.insertRecord(-1, record);

// 更新记录
model.setData(model.index(0, 1), "张三丰"); // 修改姓名
model.submitAll(); // 提交更改

// 删除记录
model.removeRow(1); // 删除第二行
model.submitAll();

3. 自定义关系处理

// 自定义关系显示
QVariant MyModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole && index.column() == 2) {
        // 获取原始外键值
        int deptId = QSqlRelationalTableModel::data(index, Qt::EditRole).toInt();
        
        // 自定义查询关联表
        QSqlQuery query;
        query.prepare("SELECT short_name FROM departments WHERE id = ?");
        query.addBindValue(deptId);
        if (query.exec() && query.next()) {
            return query.value(0); // 返回简称
        }
    }
    return QSqlRelationalTableModel::data(index, role);
}

四、高级用法

1. 级联关系(多级外键)

// 三级关联:员工->部门->公司
model.setTable("employees");

// 第一级关系:部门
model.setRelation(2, QSqlRelation("departments", "id", "name"));

// 获取部门模型并设置第二级关系:公司
QSqlTableModel *deptModel = model.relationModel(2);
if (deptModel) {
    deptModel->setRelation(2, QSqlRelation("companies", "comp_id", "comp_name"));
    deptModel->select();
}

2. 自定义委托(增强编辑)

class CustomRelationalDelegate : public QSqlRelationalDelegate
{
public:
    QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                         const QModelIndex &index) const override
    {
        if (index.column() == 2) { // 部门列
            QComboBox *editor = new QComboBox(parent);
            
            // 添加额外选项
            editor->addItem("未分配", 0);
            
            // 获取关系模型
            QSqlTableModel *relModel = model()->relationModel(index.column());
            if (relModel) {
                for (int i = 0; i < relModel->rowCount(); ++i) {
                    int id = relModel->data(relModel->index(i, 0)).toInt();
                    QString name = relModel->data(relModel->index(i, 1)).toString();
                    editor->addItem(name, id);
                }
            }
            return editor;
        }
        return QSqlRelationalDelegate::createEditor(parent, option, index);
    }
};

// 使用自定义委托
view.setItemDelegate(new CustomRelationalDelegate());

3. 条件过滤

// 基本过滤
model.setFilter("department_id = 1"); // 只显示研发部员工
model.select();

// 动态过滤
void filterByDepartment(int deptId)
{
    model.setFilter(QString("department_id = %1").arg(deptId));
    model.select();
}

// 复杂条件过滤
model.setFilter("department_id IN (SELECT id FROM departments WHERE name LIKE '%技术%')");

五、实际应用场景

1. 主从表视图

// 主表:部门
QSqlRelationalTableModel deptModel;
deptModel.setTable("departments");
deptModel.select();

// 从表:员工(根据主表选择过滤)
QSqlRelationalTableModel empModel;
empModel.setTable("employees");
empModel.setRelation(2, QSqlRelation("departments", "id", "name"));

// 连接选择信号
QObject::connect(deptView->selectionModel(), &QItemSelectionModel::currentRowChanged,
    [&](const QModelIndex &current, const QModelIndex &){
        int deptId = deptModel.data(deptModel.index(current.row(), 0)).toInt();
        empModel.setFilter(QString("department_id = %1").arg(deptId));
        empModel.select();
    });

2. 数据验证

bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    // 验证部门ID是否存在
    if (index.column() == 2 && role == Qt::EditRole) {
        int deptId = value.toInt();
        QSqlQuery query;
        query.prepare("SELECT 1 FROM departments WHERE id = ?");
        query.addBindValue(deptId);
        if (!query.exec() || !query.next()) {
            qWarning() << "无效的部门ID";
            return false;
        }
    }
    return QSqlRelationalTableModel::setData(index, value, role);
}

3. 级联删除

// 删除部门时同时删除员工
bool deleteDepartment(int deptId)
{
    QSqlDatabase::database().transaction();
    
    QSqlQuery query;
    query.prepare("DELETE FROM employees WHERE department_id = ?");
    query.addBindValue(deptId);
    if (!query.exec()) {
        QSqlDatabase::database().rollback();
        return false;
    }
    
    query.prepare("DELETE FROM departments WHERE id = ?");
    query.addBindValue(deptId);
    if (!query.exec()) {
        QSqlDatabase::database().rollback();
        return false;
    }
    
    QSqlDatabase::database().commit();
    return true;
}

六、性能优化技巧

按需加载关系:不是所有外键都需要设置为关系

// 只对需要显示的外键设置关系
model.setRelation(2, QSqlRelation("departments", "id", "name")); // 需要显示
// 不需要显示的status_id不设置关系

批量操作:关闭自动提交

model.setEditStrategy(QSqlTableModel::OnManualSubmit);
// 批量操作...
model.submitAll(); // 最后统一提交

索引优化:确保外键字段有索引

CREATE INDEX idx_employee_dept ON employees(department_id);

延迟加载:大数据量时

model.setTable("large_table");
model.setRelation(2, QSqlRelation("ref_table", "id", "name"));
model.select(); // 初始不加载数据
model.fetchMore(); // 需要时加载

七、常见问题解决方案

问题1:如何显示复合字段?

// 自定义data()方法显示"部门-职位"
QVariant MyModel::data(const QModelIndex &index, int role) const
{
    if (index.column() == 2 && role == Qt::DisplayRole) {
        int deptId = QSqlRelationalTableModel::data(index, Qt::EditRole).toInt();
        QString deptName = QSqlRelationalTableModel::data(index).toString();
        
        // 获取职位
        QSqlQuery query;
        query.prepare("SELECT position FROM employees WHERE id = ?");
        query.addBindValue(data(this->index(index.row(), 0)).toInt());
        if (query.exec() && query.next()) {
            return QString("%1-%2").arg(deptName).arg(query.value(0).toString());
        }
    }
    return QSqlRelationalTableModel::data(index, role);
}

问题2:如何处理多对多关系?

// 使用中间表处理学生-课程多对多关系
model.setTable("student_course"); // 中间表
model.setRelation(1, QSqlRelation("students", "stu_id", "name"));
model.setRelation(2, QSqlRelation("courses", "course_id", "title"));

// 自定义委托允许选择多个课程
// 需要实现自定义委托创建多选控件

问题3:如何实现级联下拉框?

// 国家->省->市三级联动
model.setTable("addresses");
model.setRelation(1, QSqlRelation("countries", "country_id", "country_name"));

// 自定义委托实现级联
class CascadeDelegate : public QSqlRelationalDelegate {
    // 根据国家选择变化动态更新省份下拉框
    // 实现createEditor和setModelData
};

八、总结

QSqlRelationalTableModel 是 Qt 中处理关系数据的强大工具:

  1. 自动处理外键关系,简化开发

  2. 与视图组件完美集成,支持直接编辑

  3. 通过 QSqlRelationalDelegate 提供友好的下拉选择

  4. 适合大多数主从关系数据的展示和编辑场景

        对于复杂的关系数据应用,结合自定义模型和委托可以满足各种特殊需求。相比直接使用 QSqlTableModel 或 QSqlQuery,它能显著减少处理外键关系的代码量,提高开发效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值