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 ¤t, 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 数据库的增删改查操作。本文介绍了从基本连接到高级功能的实现方法,并提供了完整的代码示例。在实际开发中,可以根据项目需求选择合适的实现方式,结合事务处理、批量操作等技术提高应用性能。