QT编程之数据库开发

发布于:2025-03-22 ⋅ 阅读:(24) ⋅ 点赞:(0)

一、架构层次

  1. 用户接口层

    • QSqlQueryModel‌:管理SQL查询结果,提供表格数据模型用于展示‌
    • QSqlTableModel‌:支持直接操作数据库表(增删改查)‌
    • QSqlRelationalTableModel‌:支持带外键关联的复杂表操作‌
  2. SQL接口层

    • QSqlDatabase‌:创建和管理数据库连接‌
    • QSqlQuery‌:执行SQL语句和遍历结果集‌
    • QSqlRecord‌:封装数据库记录操作(字段管理)‌
  3. 驱动层
    支持多种数据库驱动插件(QSQLITE、QMYSQL等),通过抽象接口实现跨数据库兼容‌

二、Qt支持的数据库类型

  1. SQLite
    Qt内置支持SQLite数据库,无需额外安装驱动,适合嵌入式和小型应用‌。
  2. MySQL/Oracle/SQL Server
    需安装对应数据库驱动模块(如QMYSQL、QODBC等),支持通过ODBC或原生驱动连接‌。
  3. 其他数据库
    包括PostgreSQL等,需通过QSqlDatabase::drivers()查看当前支持的驱动列表‌。

三、数据库连接步骤

  1. 添加SQL模块
    在项目配置文件(.pro)中添加:QT += sql
  2. 加载驱动并创建连接
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");  // SQLite示例
    db.setDatabaseName("my.db");  // 设置数据库文件路径
    if (!db.open()) {
        qDebug() << "连接失败:" << db.lastError().text();
    }
  3. 多数据库连接
    通过指定不同连接名实现多个数据库同时操作:
    QSqlDatabase db1 = QSqlDatabase::addDatabase("QSQLITE", "connection1");
    QSqlDatabase db2 = QSqlDatabase::addDatabase("QMYSQL", "connection2");

四、基本数据库操作

  1. 执行SQL语句
    使用QSqlQuery执行增删改查操作:

    QSqlQuery query;
    query.exec("CREATE TABLE user (id INT PRIMARY KEY, name TEXT)");  // 创建表
    query.exec("INSERT INTO user VALUES (1, 'Alice')");               // 插入数据
  2. 查询数据

    if (query.exec("SELECT * FROM user")) {
        while (query.next()) {
            int id = query.value("id").toInt();
            QString name = query.value("name").toString();
        }
    }
  3. 预处理语句(防SQL注入)

    query.prepare("INSERT INTO user (id, name) VALUES (:id, :name)");
    query.bindValue(":id", 2);
    query.bindValue(":name", "Bob");
    query.exec();

五、高级功能

  1. 事务处理

    db.transaction();
    // 执行多条SQL操作
    if (操作成功) db.commit();
    else db.rollback();
  2. 模型-视图架构
    使用QSqlTableModelQSqlQueryModel实现数据与UI组件的绑定:

    QSqlTableModel *model = new QSqlTableModel(this);
    model->setTable("user");
    model->select();
    tableView->setModel(model);

六、注意事项

  1. 驱动兼容性
    确保目标平台已安装对应数据库驱动(如Windows需libmysql.dll)‌。
  2. 错误处理
    通过QSqlError捕获并处理数据库操作中的异常‌。
  3. 性能优化
    批量操作建议使用事务,避免频繁打开/关闭连接‌。
  4. 中文乱码
    在连接字符串中添加 ;Charset=UTF8;,确保数据库字段使用 NVARCHAR。

七、MS SQL SERVER

在Qt中操作Microsoft SQL Server,你可以使用多种方法,包括使用ODBC(Open Database Connectivity)连接,或者使用专门的库如QODBC或QSqlServer(如果你使用的是Qt 5或更高版本,并且有对应的插件)。以下是两种常见的方法:

方法1:使用QODBC

1)确保你的系统安装了ODBC驱动:
对于Windows,通常SQL Server ODBC驱动已经预装。你可以在ODBC数据源管理器中检查这一点。
2)在Qt项目中配置ODBC连接:
如果你使用的是Qt 5或更高版本,并且你的系统上安装了SQL Server ODBC驱动,你可以直接使用QODBC驱动。
3)连接数据库代码:

#include <QSqlDatabase>
#include <QDebug>
 
int main() {
    QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
    db.setHostName("你的服务器名"); // 例如 localhost 或 IP 地址
    db.setDatabaseName("你的数据库名");
    db.setUserName("你的用户名");
    db.setPassword("你的密码");
    
    if (!db.open()) {
        qDebug() << "无法打开数据库:" << db.lastError().text();
        return -1;
    }
    
    qDebug() << "数据库连接成功!";
    
    // 执行查询等操作...
    
    return 0;
}
方法2:使用QSqlServer

如果你使用的是Qt 5.7或更高版本,可以直接使用QSqlServer驱动,而无需ODBC。
1)确保你的Qt版本支持QSqlServer:
2)连接数据库代码:

#include <QSqlDatabase>
#include <QDebug>
 
int main() {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLSERVER");
    db.setHostName("你的服务器名"); // 例如 localhost 或 IP 地址 (对于本地服务器可以留空)
    db.setDatabaseName("你的数据库名"); // 不需要加前缀例如"数据库名"而不是"Driver={SQL Server};Server=服务器名;Database=数据库名;"
    db.setUserName("你的用户名");
    db.setPassword("你的密码");
    
    if (!db.open()) {
        qDebug() << "无法打开数据库:" << db.lastError().text();
        return -1;
    }
    
    qDebug() << "数据库连接成功!";
    
    // 执行查询等操作...
    
    return 0;
}

完整实例代码:

1)数据库连接类 DatabaseManager.h

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>

class DatabaseManager {
public:
    static bool connect() {
        QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
        QString connectionString = 
            "DRIVER={SQL Server};"
            "SERVER=YourPC\\YourInstance;"  // 命名实例写法
            "DATABASE=TestDB;"
            "UID=sa;"
            "PWD=YourPassword;";
        db.setDatabaseName(connectionString);

        if (!db.open()) {
            qDebug() << "连接失败:" << db.lastError().text();
            return false;
        }
        qDebug() << "数据库连接成功!";
        return true;
    }

    static void disconnect() {
        QSqlDatabase::database().close();
    }
};

连接字符串示例

"DRIVER={SQL Server};SERVER=MyPC\\MyInstance,5000;DATABASE=MyDB;UID=sa;PWD=123456;"

2)主窗口类 MainWindow.cpp(含增删改查、事务处理)

#include "MainWindow.h"
#include "ui_MainWindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    DatabaseManager::connect(); // 初始化连接
    loadDataToTable(); // 加载数据到表格
}

MainWindow::~MainWindow() {
    DatabaseManager::disconnect();
    delete ui;
}

// 加载数据到表格
void MainWindow::loadDataToTable() {
    QSqlQuery query;
    query.exec("SELECT * FROM Users");
    ui->tableWidget->setRowCount(0);

    while (query.next()) {
        int row = ui->tableWidget->rowCount();
        ui->tableWidget->insertRow(row);
        ui->tableWidget->setItem(row, 0, new QTableWidgetItem(query.value("ID").toString()));
        ui->tableWidget->setItem(row, 1, new QTableWidgetItem(query.value("Name").toString()));
        ui->tableWidget->setItem(row, 2, new QTableWidgetItem(query.value("Age").toString()));
    }
}

// 添加用户
void MainWindow::on_btnAdd_clicked() {
    QSqlQuery query;
    query.prepare("INSERT INTO Users (Name, Age) VALUES (?, ?)");
    query.addBindValue(ui->txtName->text());
    query.addBindValue(ui->txtAge->text().toInt());
    
    if (!query.exec()) {
        qDebug() << "插入失败:" << query.lastError().text();
    } else {
        loadDataToTable(); // 刷新表格
    }
}

// 删除用户
void MainWindow::on_btnDelete_clicked() {
    int selectedRow = ui->tableWidget->currentRow();
    if (selectedRow == -1) return;

    QString id = ui->tableWidget->item(selectedRow, 0)->text();
    QSqlQuery query;
    query.prepare("DELETE FROM Users WHERE ID = ?");
    query.addBindValue(id);
    
    if (!query.exec()) {
        qDebug() << "删除失败:" << query.lastError().text();
    } else {
        loadDataToTable(); // 刷新表格
    }
}

// 执行事务处理示例
void MainWindow::on_btnTransaction_clicked() {
    QSqlDatabase::database().transaction(); // 开启事务

    QSqlQuery query;
    query.exec("UPDATE Users SET Age = Age + 1 WHERE Name = 'Alice'");
    query.exec("UPDATE Users SET Age = Age - 1 WHERE Name = 'Bob'");

    if (query.lastError().isValid()) {
        QSqlDatabase::database().rollback(); // 回滚
        qDebug() << "事务执行失败,已回滚";
    } else {
        QSqlDatabase::database().commit(); // 提交
        qDebug() << "事务执行成功";
    }
}

八、MY SQL

1)数据库连接类 DatabaseManager.h

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>

class DatabaseManager {
public:
    static bool connect() {
        QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
        db.setHostName("localhost");    // MySQL 服务器地址
        db.setPort(3306);               // MySQL 默认端口
        db.setDatabaseName("TestDB");   // 数据库名
        db.setUserName("qtuser");       // 用户名
        db.setPassword("123456");       // 密码

        if (!db.open()) {
            qDebug() << "连接失败:" << db.lastError().text();
            return false;
        }
        qDebug() << "数据库连接成功!";
        return true;
    }

    static void disconnect() {
        QSqlDatabase::database().close();
    }
};

2)主窗口类 MainWindow.cpp(含增删改查、事务处理)

#include "MainWindow.h"
#include "ui_MainWindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    DatabaseManager::connect();
    loadDataToTable();  // 初始化加载数据
}

MainWindow::~MainWindow() {
    DatabaseManager::disconnect();
    delete ui;
}

// 加载数据到表格
void MainWindow::loadDataToTable() {
    QSqlQuery query("SELECT * FROM Users");
    ui->tableWidget->setRowCount(0);

    while (query.next()) {
        int row = ui->tableWidget->rowCount();
        ui->tableWidget->insertRow(row);
        ui->tableWidget->setItem(row, 0, new QTableWidgetItem(query.value("ID").toString()));
        ui->tableWidget->setItem(row, 1, new QTableWidgetItem(query.value("Name").toString()));
        ui->tableWidget->setItem(row, 2, new QTableWidgetItem(query.value("Age").toString()));
    }
}

// 添加用户
void MainWindow::on_btnAdd_clicked() {
    QSqlQuery query;
    query.prepare("INSERT INTO Users (Name, Age) VALUES (?, ?)");
    query.addBindValue(ui->txtName->text());
    query.addBindValue(ui->txtAge->text().toInt());

    if (!query.exec()) {
        qDebug() << "插入失败:" << query.lastError().text();
    } else {
        loadDataToTable();  // 刷新表格
    }
}

// 删除用户
void MainWindow::on_btnDelete_clicked() {
    int row = ui->tableWidget->currentRow();
    if (row == -1) return;

    QString id = ui->tableWidget->item(row, 0)->text();
    QSqlQuery query;
    query.prepare("DELETE FROM Users WHERE ID = ?");
    query.addBindValue(id);

    if (!query.exec()) {
        qDebug() << "删除失败:" << query.lastError().text();
    } else {
        loadDataToTable();  // 刷新表格
    }
}

// 更新用户年龄
void MainWindow::on_btnUpdate_clicked() {
    int row = ui->tableWidget->currentRow();
    if (row == -1) return;

    QString id = ui->tableWidget->item(row, 0)->text();
    int newAge = ui->txtNewAge->text().toInt();

    QSqlQuery query;
    query.prepare("UPDATE Users SET Age = ? WHERE ID = ?");
    query.addBindValue(newAge);
    query.addBindValue(id);

    if (!query.exec()) {
        qDebug() << "更新失败:" << query.lastError().text();
    } else {
        loadDataToTable();  // 刷新表格
    }
}

void MainWindow::on_btnTransaction_clicked() {
    QSqlDatabase::database().transaction();  // 开启事务

    QSqlQuery query;
    query.exec("UPDATE Users SET Age = Age + 1 WHERE Name = 'Alice'");
    query.exec("UPDATE Users SET Age = Age - 1 WHERE Name = 'Bob'");

    if (query.lastError().isValid()) {
        QSqlDatabase::database().rollback();
        qDebug() << "事务回滚!";
    } else {
        QSqlDatabase::database().commit();
        qDebug() << "事务提交!";
    }
}

九、SQLITE

1)数据库连接类 DatabaseManager.h

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QStandardPaths>

class DatabaseManager {
public:
    static bool connect() {
        // 数据库文件路径(自动创建)
        QString dbPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/mydatabase.db";
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
        db.setDatabaseName(dbPath);

        if (!db.open()) {
            qDebug() << "连接失败:" << db.lastError().text();
            return false;
        }
        qDebug() << "数据库连接成功!路径:" << dbPath;
        
        // 自动建表
        createTables();
        return true;
    }

    static void createTables() {
        QSqlQuery query;
        query.exec("CREATE TABLE IF NOT EXISTS Users ("
                   "ID INTEGER PRIMARY KEY AUTOINCREMENT,"
                   "Name TEXT NOT NULL,"
                   "Age INTEGER)");
        if (query.lastError().isValid()) {
            qDebug() << "建表失败:" << query.lastError().text();
        }
    }

    static void disconnect() {
        QSqlDatabase::database().close();
    }
};

2)主窗口类 MainWindow.cpp(含增删改查、事务处理)

#include "MainWindow.h"
#include "ui_MainWindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    DatabaseManager::connect();
    loadDataToTable();  // 初始化加载数据
}

MainWindow::~MainWindow() {
    DatabaseManager::disconnect();
    delete ui;
}

// 加载数据到表格
void MainWindow::loadDataToTable() {
    QSqlQuery query("SELECT * FROM Users");
    ui->tableWidget->setRowCount(0);

    while (query.next()) {
        int row = ui->tableWidget->rowCount();
        ui->tableWidget->insertRow(row);
        ui->tableWidget->setItem(row, 0, new QTableWidgetItem(query.value("ID").toString()));
        ui->tableWidget->setItem(row, 1, new QTableWidgetItem(query.value("Name").toString()));
        ui->tableWidget->setItem(row, 2, new QTableWidgetItem(query.value("Age").toString()));
    }
}

// 添加用户
void MainWindow::on_btnAdd_clicked() {
    QSqlQuery query;
    query.prepare("INSERT INTO Users (Name, Age) VALUES (:name, :age)");
    query.bindValue(":name", ui->txtName->text());
    query.bindValue(":age", ui->txtAge->text().toInt());

    if (!query.exec()) {
        qDebug() << "插入失败:" << query.lastError().text();
    } else {
        loadDataToTable();  // 刷新表格
    }
}

// 删除用户
void MainWindow::on_btnDelete_clicked() {
    int row = ui->tableWidget->currentRow();
    if (row == -1) return;

    QString id = ui->tableWidget->item(row, 0)->text();
    QSqlQuery query;
    query.prepare("DELETE FROM Users WHERE ID = ?");
    query.addBindValue(id);

    if (!query.exec()) {
        qDebug() << "删除失败:" << query.lastError().text();
    } else {
        loadDataToTable();  // 刷新表格
    }
}

// 更新用户年龄
void MainWindow::on_btnUpdate_clicked() {
    int row = ui->tableWidget->currentRow();
    if (row == -1) return;

    QString id = ui->tableWidget->item(row, 0)->text();
    int newAge = ui->txtNewAge->text().toInt();

    QSqlQuery query;
    query.prepare("UPDATE Users SET Age = ? WHERE ID = ?");
    query.addBindValue(newAge);
    query.addBindValue(id);

    if (!query.exec()) {
        qDebug() << "更新失败:" << query.lastError().text();
    } else {
        loadDataToTable();  // 刷新表格
    }
}


// 批量插入数据(事务加速)
void MainWindow::on_btnBatchInsert_clicked() {
    QSqlDatabase::database().transaction();  // 开启事务

    QSqlQuery query;
    query.prepare("INSERT INTO Users (Name, Age) VALUES (?, ?)");
    
    for (int i = 0; i < 1000; ++i) {
        query.addBindValue("User_" + QString::number(i));
        query.addBindValue(20 + i % 10);
        query.exec();
    }

    if (query.lastError().isValid()) {
        QSqlDatabase::database().rollback();
        qDebug() << "批量插入失败,已回滚";
    } else {
        QSqlDatabase::database().commit();
        qDebug() << "批量插入成功,耗时优化 10x+";
        loadDataToTable();
    }
}

十、ORACLE

在使用Qt进行Oracle数据库操作时,通常有两种方法比较常见:使用Oracle的OCI(Oracle Call Interface)或者使用ODBC(Open Database Connectivity)。下面我将详细介绍如何使用这两种方法在Qt应用程序中操作Oracle数据库。

方法1:使用OCI

Oracle Call Interface (OCI) 是Oracle提供的一套C语言API,用于访问Oracle数据库。要在Qt中使用OCI,你需要先确保你的系统上安装了Oracle的客户端库和头文件,并且你的项目需要链接到这些库。
步骤:
1)Oracle 客户端安装
下载 Oracle Instant Client 的 ‌Basic‌ 和 ‌SDK‌ 包(需与 Qt 编译器位数一致)‌。解压到目录如 C:\oracle\instantclient_12_2,将 oci.dll 所在路径加入系统环境变量 PATH

2)Qt OCI 驱动编译

cd Qt/6.5.0/Src/qtbase/src/plugins/sqldrivers/oci
qmake "INCLUDEPATH+=C:/oracle/instantclient_12_2/sdk/include" "LIBS+=-LC:/oracle/instantclient_12_2 -loci"
nmake  # 或make

编译生成的 qsqloci.dll 需复制到 Qt/6.5.0/mingw_64/plugins/sqldrivers
3)在Qt项目中包含头文件和链接库:

#include <oci.h>

4) 在.pro文件中添加链接器选项:

QT += core gui sql
CONFIG += c++17

# 配置 Oracle 库路径
win32 {
    LIBS += -LC:/oracle/instantclient_12_2 -loci
    INCLUDEPATH += C:/oracle/instantclient_12_2/sdk/include
}

‌完整实例代码:

1)数据库连接类 OracleManager.h

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>

class OracleManager {
public:
    static bool connect(const QString &serviceName) {
        QSqlDatabase db = QSqlDatabase::addDatabase("QOCI");
        db.setHostName("192.168.1.100");        // Oracle 服务器 IP
        db.setPort(1521);                       // 默认端口
        db.setDatabaseName(serviceName);        // 服务名(非 SID)
        db.setUserName("scott");                // 用户名
        db.setPassword("tiger");                // 密码

        if (!db.open()) {
            qDebug() << "[ERROR] 连接失败:" << db.lastError().text() << "‌:ml-citation{ref="1,6" data="citationList"}";
            return false;
        }
        qDebug() << "数据库连接成功!";
        return true;
    }

    static void disconnect() {
        QSqlDatabase::database().close();
    }
};

2)主窗口类 MainWindow.cpp(含增删改查、事务处理)

// 创建表
void MainWindow::createTable() {
    QSqlQuery query;
    query.exec("CREATE TABLE Employees ("
               "EmpID NUMBER PRIMARY KEY,"
               "Name NVARCHAR2(50),"
               "Salary NUMBER(10,2))");
    if(query.lastError().isValid()) 
        qDebug() << "建表错误:" << query.lastError().text() << "‌:ml-citation{ref="8" data="citationList"}";
}

// 插入数据(绑定参数)
void MainWindow::onInsertClicked() {
    QSqlQuery query;
    query.prepare("INSERT INTO Employees (EmpID, Name, Salary) VALUES (:id, :name, :sal)");
    query.bindValue(":id", 1001);
    query.bindValue(":name", "张三");
    query.bindValue(":sal", 8500.50);
    
    if (!query.exec()) 
        qDebug() << "插入失败:" << query.lastError().text() << "‌:ml-citation{ref="3,6" data="citationList"}";
}

// 查询数据(事务处理)
void MainWindow::onQueryClicked() {
    QSqlDatabase::database().transaction();
    QSqlQuery query("SELECT * FROM Employees");
    
    while (query.next()) {
        int id = query.value("EmpID").toInt();
        QString name = query.value("Name").toString();
        double salary = query.value("Salary").toDouble();
        // 显示到 UI...
    }
    
    if (query.lastError().isValid()) {
        QSqlDatabase::database().rollback();
        qDebug() << "事务回滚!" << "‌:ml-citation{ref="8" data="citationList"}";
    } else {
        QSqlDatabase::database().commit();
    }
}
    方法2:使用ODBC

    ODBC提供了一个数据库无关的应用程序编程接口(API),允许应用程序通过ODBC驱动程序与数据库进行通信。Qt提供了对ODBC的支持,可以通过QSqlDatabaseQSqlQuery类来操作。

    QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
    db.setHostName("hostname"); // 通常是localhost或服务器地址
    db.setDatabaseName("DSN_NAME"); // 数据源名称,在ODBC中配置的DSN名称
    db.setUserName("username");
    db.setPassword("password");
     
    if (db.open()) {
        QSqlQuery query(db);
        query.exec("SELECT * FROM your_table");
        while (query.next()) {
            // 处理查询结果...
        }
        db.close();
    } else {
        qDebug() << "Database connection failed";
    }