QT操作PostgreSQL数据库并实现增删改查功能

 更新时间:2025年05月15日 08:58:08   作者:code_shenbing  
Qt 提供了强大的数据库支持,通过 Qt SQL 模块可以方便地操作 PostgreSQL 数据库,本文将详细介绍如何在 Qt 中连接 PostgreSQL 数据库,并实现基本的增删改查(CRUD)操作,需要的朋友可以参考下

一、环境准备

1. 安装 PostgreSQL

确保已安装 PostgreSQL 并创建了测试数据库。

2. 安装 Qt 开发环境

确保已安装 Qt 开发环境(Qt Creator 或命令行工具)。

3. 配置 Qt 连接 PostgreSQL

在项目文件(.pro)中添加:

QT += sql

二、连接 PostgreSQL 数据库

1. 基本连接方式

#include <QCoreApplication>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlError>
#include <QDebug>
 
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    // 创建数据库连接
    QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
    
    // 设置连接参数
    db.setHostName("localhost");      // 主机名
    db.setPort(5432);                 // 端口
    db.setDatabaseName("testdb");     // 数据库名
    db.setUserName("postgres");       // 用户名
    db.setPassword("password");       // 密码
    
    // 打开连接
    if (!db.open()) {
        qDebug() << "数据库连接失败:" << db.lastError().text();
        return -1;
    }
    
    qDebug() << "成功连接到数据库";
    
    // 关闭连接
    db.close();
    
    return a.exec();
}

2. 使用连接池(推荐)

// 创建连接池
QSqlDatabase createConnectionPool(const QString &connectionName) {
    QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL", connectionName);
    db.setHostName("localhost");
    db.setPort(5432);
    db.setDatabaseName("testdb");
    db.setUserName("postgres");
    db.setPassword("password");
    
    if (!db.open()) {
        qCritical() << "创建连接池失败:" << db.lastError().text();
        return QSqlDatabase();
    }
    
    return db;
}
 
// 获取连接
QSqlDatabase getConnection(const QString &connectionName) {
    QSqlDatabase db = QSqlDatabase::database(connectionName);
    if (!db.isOpen()) {
        if (!db.open()) {
            qCritical() << "获取连接失败:" << db.lastError().text();
            return QSqlDatabase();
        }
    }
    return db;
}
 
// 释放连接
void releaseConnection(const QString &connectionName) {
    QSqlDatabase::removeDatabase(connectionName);
}

三、实现增删改查操作

1. 创建测试表

首先在 PostgreSQL 中创建测试表:

CREATE TABLE employees (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    position VARCHAR(50),
    salary NUMERIC(10, 2),
    hire_date DATE
);

2. 插入数据(Add)

bool insertEmployee(QSqlDatabase &db, const QString &name, 
                    const QString &position, double salary, 
                    const QDate &hireDate) {
    QSqlQuery query(db);
    
    // 使用预处理语句防止SQL注入
    query.prepare("INSERT INTO employees (name, position, salary, hire_date) "
                  "VALUES (:name, :position, :salary, :hire_date)");
    
    query.bindValue(":name", name);
    query.bindValue(":position", position);
    query.bindValue(":salary", salary);
    query.bindValue(":hire_date", hireDate);
    
    if (!query.exec()) {
        qDebug() << "插入数据失败:" << query.lastError().text();
        return false;
    }
    
    return true;
}

3. 查询数据(Query)

3.1 查询单条记录

QSqlRecord getEmployeeById(QSqlDatabase &db, int id) {
    QSqlQuery query(db);
    query.prepare("SELECT * FROM employees WHERE id = :id");
    query.bindValue(":id", id);
    
    if (!query.exec() || !query.next()) {
        qDebug() << "查询员工失败:" << query.lastError().text();
        return QSqlRecord();
    }
    
    return query.record();
}

3.2 查询所有记录

QList<QSqlRecord> getAllEmployees(QSqlDatabase &db) {
    QList<QSqlRecord> employees;
    QSqlQuery query(db);
    query.exec("SELECT * FROM employees ORDER BY id");
    
    while (query.next()) {
        employees.append(query.record());
    }
    
    return employees;
}

3.3 使用模型查询(Qt SQL 模型)

QSqlTableModel *createEmployeeModel(QObject *parent = nullptr) {
    QSqlTableModel *model = new QSqlTableModel(parent);
    model->setTable("employees");
    model->select();
    
    // 设置表头
    model->setHeaderData(1, Qt::Horizontal, tr("Name"));
    model->setHeaderData(2, Qt::Horizontal, tr("Position"));
    model->setHeaderData(3, Qt::Horizontal, tr("Salary"));
    model->setHeaderData(4, Qt::Horizontal, tr("Hire Date"));
    
    return model;
}

4. 更新数据(Update)

bool updateEmployee(QSqlDatabase &db, int id, 
                    const QString &name, const QString &position, 
                    double salary, const QDate &hireDate) {
    QSqlQuery query(db);
    query.prepare("UPDATE employees SET name = :name, position = :position, "
                  "salary = :salary, hire_date = :hire_date WHERE id = :id");
    
    query.bindValue(":name", name);
    query.bindValue(":position", position);
    query.bindValue(":salary", salary);
    query.bindValue(":hire_date", hireDate);
    query.bindValue(":id", id);
    
    if (!query.exec()) {
        qDebug() << "更新员工失败:" << query.lastError().text();
        return false;
    }
    
    return true;
}

5. 删除数据(Delete)

bool deleteEmployee(QSqlDatabase &db, int id) {
    QSqlQuery query(db);
    query.prepare("DELETE FROM employees WHERE id = :id");
    query.bindValue(":id", id);
    
    if (!query.exec()) {
        qDebug() << "删除员工失败:" << query.lastError().text();
        return false;
    }
    
    return true;
}

四、完整示例

1. 使用控制台程序演示CRUD操作

#include <QCoreApplication>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>
#include <QtSql/QSqlRecord>
#include <QDebug>
#include <QDate>
 
bool openDatabase(QSqlDatabase &db) {
    db = QSqlDatabase::addDatabase("QPSQL");
    db.setHostName("localhost");
    db.setPort(5432);
    db.setDatabaseName("testdb");
    db.setUserName("postgres");
    db.setPassword("password");
    
    if (!db.open()) {
        qDebug() << "数据库连接失败:" << db.lastError().text();
        return false;
    }
    return true;
}
 
void closeDatabase(QSqlDatabase &db) {
    db.close();
}
 
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    QSqlDatabase db;
    if (!openDatabase(db)) {
        return -1;
    }
    
    // 插入数据
    QSqlQuery query(db);
    query.prepare("INSERT INTO employees (name, position, salary, hire_date) "
                  "VALUES (:name, :position, :salary, :hire_date)");
    
    query.bindValue(":name", "张三");
    query.bindValue(":position", "开发工程师");
    query.bindValue(":salary", 15000.00);
    query.bindValue(":hire_date", QDate::currentDate());
    
    if (!query.exec()) {
        qDebug() << "插入失败:" << query.lastError().text();
    } else {
        qDebug() << "插入成功,ID:" << query.lastInsertId().toInt();
    }
    
    // 查询数据
    QSqlQuery selectQuery(db);
    selectQuery.exec("SELECT * FROM employees ORDER BY id");
    
    while (selectQuery.next()) {
        QSqlRecord record = selectQuery.record();
        qDebug() << "ID:" << record.value("id").toInt()
                 << "姓名:" << record.value("name").toString()
                 << "职位:" << record.value("position").toString()
                 << "薪资:" << record.value("salary").toDouble()
                 << "入职日期:" << record.value("hire_date").toDate();
    }
    
    // 更新数据
    query.prepare("UPDATE employees SET salary = :salary WHERE id = :id");
    query.bindValue(":salary", 16000.00);
    query.bindValue(":id", 1); // 假设ID为1的员工
    
    if (!query.exec()) {
        qDebug() << "更新失败:" << query.lastError().text();
    } else {
        qDebug() << "更新成功";
    }
    
    // 删除数据
    query.prepare("DELETE FROM employees WHERE id = :id");
    query.bindValue(":id", 1); // 假设要删除ID为1的员工
    
    if (!query.exec()) {
        qDebug() << "删除失败:" << query.lastError().text();
    } else {
        qDebug() << "删除成功";
    }
    
    closeDatabase(db);
    return a.exec();
}

2. 使用Qt Widgets实现GUI界面

// employeeform.h
#ifndef EMPLOYEEFORM_H
#define EMPLOYEEFORM_H
 
#include <QWidget>
#include <QSqlTableModel>
#include <QDataWidgetMapper>
 
QT_BEGIN_NAMESPACE
namespace Ui { class EmployeeForm; }
QT_END_NAMESPACE
 
class EmployeeForm : public QWidget
{
    Q_OBJECT
 
public:
    EmployeeForm(QWidget *parent = nullptr);
    ~EmployeeForm();
 
private slots:
    void on_addButton_clicked();
    void on_saveButton_clicked();
    void on_deleteButton_clicked();
    void on_refreshButton_clicked();
 
private:
    Ui::EmployeeForm *ui;
    QSqlTableModel *model;
    QDataWidgetMapper *mapper;
};
 
#endif // EMPLOYEEFORM_H
 
// employeeform.cpp
#include "employeeform.h"
#include "ui_employeeform.h"
#include <QSqlDatabase>
#include <QSqlError>
#include <QMessageBox>
 
EmployeeForm::EmployeeForm(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::EmployeeForm)
{
    ui->setupUi(this);
 
    // 连接数据库
    QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
    db.setHostName("localhost");
    db.setPort(5432);
    db.setDatabaseName("testdb");
    db.setUserName("postgres");
    db.setPassword("password");
 
    if (!db.open()) {
        QMessageBox::critical(this, "错误", "无法连接到数据库: " + db.lastError().text());
        return;
    }
 
    // 创建模型
    model = new QSqlTableModel(this, db);
    model->setTable("employees");
    model->select();
 
    // 设置表头
    model->setHeaderData(1, Qt::Horizontal, tr("姓名"));
    model->setHeaderData(2, Qt::Horizontal, tr("职位"));
    model->setHeaderData(3, Qt::Horizontal, tr("薪资"));
    model->setHeaderData(4, Qt::Horizontal, tr("入职日期"));
 
    // 设置视图
    ui->tableView->setModel(model);
    ui->tableView->setEditTriggers(QAbstractItemView::DoubleClicked);
 
    // 设置数据映射器
    mapper = new QDataWidgetMapper(this);
    mapper->setModel(model);
    mapper->addMapping(ui->nameEdit, 1);
    mapper->addMapping(ui->positionEdit, 2);
    mapper->addMapping(ui->salaryEdit, 3);
    mapper->addMapping(ui->hireDateEdit, 4);
 
    // 连接信号槽
    connect(ui->tableView->selectionModel(), &QItemSelectionModel::currentRowChanged,
            this, [this](const QModelIndex &current, const QModelIndex &) {
                mapper->setCurrentModelIndex(current);
            });
}
 
EmployeeForm::~EmployeeForm()
{
    delete ui;
}
 
void EmployeeForm::on_addButton_clicked()
{
    int row = model->rowCount();
    model->insertRow(row);
    ui->tableView->selectRow(row);
    mapper->setCurrentIndex(row);
    ui->nameEdit->setFocus();
}
 
void EmployeeForm::on_saveButton_clicked()
{
    if (!model->submitAll()) {
        QMessageBox::warning(this, "错误", "保存失败: " + model->lastError().text());
    } else {
        model->database().transaction();
        if (model->submitAll()) {
            model->database().commit();
            QMessageBox::information(this, "成功", "数据保存成功");
        } else {
            model->database().rollback();
            QMessageBox::warning(this, "错误", "保存失败: " + model->lastError().text());
        }
    }
}
 
void EmployeeForm::on_deleteButton_clicked()
{
    QModelIndex index = ui->tableView->currentIndex();
    if (index.isValid()) {
        int ret = QMessageBox::question(this, "确认", "确定要删除这条记录吗?",
                                        QMessageBox::Yes | QMessageBox::No);
        if (ret == QMessageBox::Yes) {
            model->removeRow(index.row());
            if (!model->submitAll()) {
                QMessageBox::warning(this, "错误", "删除失败: " + model->lastError().text());
                model->revertAll();
            }
        }
    }
}
 
void EmployeeForm::on_refreshButton_clicked()
{
    model->select();
}

五、高级功能

1. 事务处理

bool performTransaction(QSqlDatabase &db) {
    db.transaction();
    
    QSqlQuery query(db);
    bool success = true;
    
    // 执行多个操作
    if (!query.exec("INSERT INTO employees (...) VALUES (...)" )) {
        success = false;
    }
    
    if (!query.exec("UPDATE ...")) {
        success = false;
    }
    
    if (success) {
        db.commit();
    } else {
        db.rollback();
    }
    
    return success;
}

2. 批量插入

bool batchInsertEmployees(QSqlDatabase &db, const QList<QVariantList> &employees) {
    QSqlDatabase::database().transaction();
    
    QSqlQuery query(db);
    query.prepare("INSERT INTO employees (name, position, salary, hire_date) "
                  "VALUES (?, ?, ?, ?)");
    
    foreach (const QVariantList &employee, employees) {
        query.addBindValue(employee);
        if (!query.execBatch()) {
            QSqlDatabase::database().rollback();
            return false;
        }
    }
    
    QSqlDatabase::database().commit();
    return true;
}

3. 使用存储过程

bool callStoredProcedure(QSqlDatabase &db, int employeeId) {
    QSqlQuery query(db);
    query.prepare("CALL update_employee_salary(:id, :percentage)");
    query.bindValue(":id", employeeId);
    query.bindValue(":percentage", 10); // 增加10%
    
    if (!query.exec()) {
        qDebug() << "调用存储过程失败:" << query.lastError().text();
        return false;
    }
    
    return true;
}

六、常见问题解决

1. 连接失败

  • 检查PostgreSQL服务是否运行
  • 验证连接参数(主机名、端口、数据库名、用户名、密码)
  • 检查防火墙设置
  • 确保安装了PostgreSQL客户端库

2. 中文乱码

// 设置编码
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

或者在连接字符串中指定编码:

db.setConnectOptions("client_encoding=UTF8");

3. 性能优化

  • 使用预处理语句
  • 批量操作代替单条操作
  • 合理使用事务
  • 为常用查询创建索引

七、总结

Qt 提供了强大而灵活的数据库访问功能,通过 Qt SQL 模块可以轻松实现 PostgreSQL 数据库的增删改查操作。本文介绍了从基本连接到高级功能的实现方法,并提供了完整的代码示例。在实际开发中,可以根据项目需求选择合适的实现方式,结合事务处理、批量操作等技术提高应用性能。

以上就是QT操作PostgreSQL数据库并实现增删改查功能的详细内容,更多关于QT操作PostgreSQL增删改查的资料请关注脚本之家其它相关文章!

相关文章

  • PostgreSQL生成列实现过程介绍

    PostgreSQL生成列实现过程介绍

    PostgreSQL 12 增加新的特性——生成列(Generated Columns),也就是计算列。在之前版本也可以实现,但需要定义函数和触发器,利用该功能可以更容易使用并可以提升性能。生成列是给表指定计算列,其数据可以根据其他列数据自动生成,当原数据更新时其自动更新
    2023-01-01
  • PostgreSQL数据库性能调优的注意点以及pg数据库性能优化方式

    PostgreSQL数据库性能调优的注意点以及pg数据库性能优化方式

    这篇文章主要介绍了PostgreSQL数据库性能调优的注意点以及pg数据库性能优化方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • PostgreSQL开启慢查询日志的方法详解

    PostgreSQL开启慢查询日志的方法详解

    PostgreSQL 可以通过配置参数来记录执行时间超过指定阈值的查询,即慢查询日志,下面小编就为大家详细讲讲开启慢查询日志的详细步骤吧
    2025-11-11
  • PostgreSQL 备份与恢复实战操作pg_dump / pg_restore 全方位指南

    PostgreSQL 备份与恢复实战操作pg_dump / pg_restore 

    本文将带你深入掌握 PostgreSQL 最核心的备份恢复工具 —— pg_dump 和 pg_restore,涵盖逻辑备份、物理备份、增量备份、自动化脚本等企业级实践,感兴趣的朋友跟随小编一起看看吧
    2025-10-10
  • PostgreSQL行转列的多种方法

    PostgreSQL行转列的多种方法

    这篇文章主要介绍了PostgreSQL行转列的多种方法,本文给大家分享三种方法,每种方法结合示例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-10-10
  • PostgreSQL 恢复误删数据的操作

    PostgreSQL 恢复误删数据的操作

    这篇文章主要介绍了PostgreSQL 恢复误删数据的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • PostgreSQL 自定义自动类型转换操作(CAST)

    PostgreSQL 自定义自动类型转换操作(CAST)

    这篇文章主要介绍了PostgreSQL 自定义自动类型转换操作(CAST),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • PostgreSQL查看带有绑定变量SQL的通用方法详解

    PostgreSQL查看带有绑定变量SQL的通用方法详解

    今天我们要探讨的是 custom执行计划和通用执行计划。这一技术在 Oracle中被称为绑定变量窥视。但 Postgresql中并没有这样的定义,更严格地说,Postgresql叫做custom执行计划和通用执行计划
    2022-09-09
  • PostgreSQL数据目录迁移的全过程

    PostgreSQL数据目录迁移的全过程

    生产环境中随着PostgreSQL数据库表数据的不断产生,数据库目录会不断增长,当磁盘空间不足时会有将PostgreSQL数据库数据目录迁移到其他目录的需求,下面详细介绍目录迁移过程,需要的朋友可以参考下
    2024-04-04
  • PostgreSQL数据库中DISTINCT关键字的四种用法详解

    PostgreSQL数据库中DISTINCT关键字的四种用法详解

    PostgreSQL 不但高度兼容 SQL 标准,同时还对很多语法进行了扩展,可以用于实现一些特殊的功能,今天我们就来介绍一下 PostgreSQL 数据库中 DISTINCT 关键字的 4 种不同用法,需要的朋友可以参考下
    2024-04-04

最新评论