目录

使用-QML-和-QtSql-实现-SQLite-数据库操作

使用 QML 和 QtSql 实现 SQLite 数据库操作

文章目录


前言

在现代应用程序开发中,数据库操作是不可或缺的一部分。Qt 提供了强大的 QtSql 模块,可以轻松地与 SQLite 数据库进行交互。本文将详细介绍如何使用 QML 和 C++ 后端实现 SQLite 数据库的增删改查(CRUD)操作。


一、 项目结构

项目的文件结构如下:

qml_SQLite_demo1/
├── database.h
├── database.cpp
├── main.cpp
├── main.qml
├── qml.qrc
└── sqlite_crud.pro

二、配置 .pro 文件

首先,在 .pro 文件中启用 Sql 模块,并配置项目的基本信息。

TEMPLATE = app
QT += qml quick widgets core gui sql
CONFIG += c++17
SOURCES += main.cpp database.cpp
HEADERS += database.h
RESOURCES += qml.qrc

三、创建 Database 类

Database 类负责处理数据库的打开、关闭以及增删改查操作。

3.1 database.h

#ifndef DATABASE_H
#define DATABASE_H

#include <QObject>
#include <QSqlDatabase>
#include <QVariantList>

class Database : public QObject
{
    Q_OBJECT
public:
    explicit Database(QObject *parent = nullptr);
    ~Database();

    Q_INVOKABLE bool openDatabase(const QString &path);
    Q_INVOKABLE QVariantList executeQuery(const QString &query, const QVariantList &params = QVariantList());
    Q_INVOKABLE bool executeNonQuery(const QString &query, const QVariantList &params = QVariantList());

private:
    QSqlDatabase db;
};

#endif // DATABASE_H

3.2 database.cpp

#include "database.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlRecord>
#include <QDebug>

Database::Database(QObject *parent) : QObject(parent)
{
}

Database::~Database()
{
    if (db.isOpen()) {
        db.close();
    }
}

bool Database::openDatabase(const QString &path)
{
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(path);
    if (!db.open()) {
        qWarning() << "Failed to open database:" << db.lastError().text();
        return false;
    }
    return true;
}

QVariantList Database::executeQuery(const QString &query, const QVariantList &params)
{
    QVariantList result;
    QSqlQuery sqlQuery;
    sqlQuery.prepare(query);
    for (const QVariant &param : params) {
        sqlQuery.addBindValue(param);
    }
    if (sqlQuery.exec()) {
        while (sqlQuery.next()) {
            QVariantMap row;
            for (int i = 0; i < sqlQuery.record().count(); i++) {
                row[sqlQuery.record().fieldName(i)] = sqlQuery.value(i);
            }
            result.append(row);
        }
    } else {
        qWarning() << "Query failed:" << sqlQuery.lastError().text();
    }
    return result;
}

bool Database::executeNonQuery(const QString &query, const QVariantList &params)
{
    QSqlQuery sqlQuery;
    sqlQuery.prepare(query);
    for (const QVariant &param : params) {
        sqlQuery.addBindValue(param);
    }
    if (!sqlQuery.exec()) {
        qWarning() << "Query failed:" << sqlQuery.lastError().text();
        return false;
    }
    return true;
}

四、main.cpp

在 main.cpp 中,我们将 Database 类注册到 QML 中,并初始化数据库。

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "database.h"
#include <QDir>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    // 创建 Database 对象
    Database database;

    // 将 Database 对象注册到 QML 中
    engine.rootContext()->setContextProperty("database", &database);

    // 设置数据库文件路径
    QString dbPath = QDir::currentPath() + "/MyDatabase.db";
    qDebug() << "Database path:" << dbPath; // 打印数据库文件路径

    // 打开数据库
    if (!database.openDatabase(dbPath)) {
        qWarning() << "Failed to open database!";
        return -1;
    }

    // 加载 QML 文件
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

五、编写 QML 界面

在 main.qml 中,我们设计一个简单的界面,用于展示数据并实现增删改查操作。

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12

ApplicationWindow {
    visible: true
    width: 800
    height: 600
    title: "SQLite CRUD Example"

    // 数据模型
    ListModel {
        id: dataModel
    }

    ColumnLayout {
        anchors.fill: parent
        spacing: 10
        anchors.margins: 20

        // 操作按钮
        RowLayout {
            spacing: 10
            Button {
                text: "添加"
                onClicked: addDialog.open()
            }
            Button {
                text: "修改"
                enabled: listView.currentIndex >= 0
                onClicked: editDialog.open()
            }
            Button {
                text: "删除"
                enabled: listView.currentIndex >= 0
                onClicked: deleteItem()
            }
            Button {
                text: "刷新"
                onClicked: loadData()
            }
        }

        // 表头
        Row {
            spacing: 0
            Repeater {
                model: ["ID", "姓名", "年龄"]
                Rectangle {
                    width: [100, 200, 100][index]
                    height: 30
                    color: "#f0f0f0"

                    Text {
                        text: modelData
                        anchors.centerIn: parent
                        font.bold: true
                    }
                }
            }
        }

        // 数据表格
        ListView {
            id: listView
            model: dataModel
            Layout.fillWidth: true
            Layout.fillHeight: true
            spacing: 1
            clip: true

            delegate: Rectangle {
                width: listView.width
                height: 30
                color: ListView.isCurrentItem ? "#d3d3d3" : "white"

                Row {
                    spacing: 0
                    Rectangle {
                        width: 100
                        height: 30
                        color: "transparent"

                        Text {
                            text: modelData.id
                            anchors.centerIn: parent
                        }
                    }
                    Rectangle {
                        width: 200
                        height: 30
                        color: "transparent"

                        Text {
                            text: modelData.name
                            anchors.centerIn: parent
                        }
                    }
                    Rectangle {
                        width: 100
                        height: 30
                        color: "transparent"

                        Text {
                            text: modelData.age
                            anchors.centerIn: parent
                        }
                    }
                }
            }
        }
    }

    // 添加对话框
    Dialog {
        id: addDialog
        title: "添加数据"
        width: 400
        height: 200

        ColumnLayout {
            anchors.fill: parent
            spacing: 10

            TextField {
                id: addNameField
                placeholderText: "请输入姓名"
                Layout.fillWidth: true
            }

            TextField {
                id: addAgeField
                placeholderText: "请输入年龄"
                Layout.fillWidth: true
            }

            RowLayout {
                Button {
                    text: "取消"
                    onClicked: addDialog.close()
                }
                Button {
                    text: "确定"
                    onClicked: {
                        database.executeNonQuery("INSERT INTO People (name, age) VALUES (?, ?)", [addNameField.text, parseInt(addAgeField.text)]);
                        loadData();
                        addDialog.close();
                    }
                }
            }
        }
    }

    // 修改对话框
    Dialog {
        id: editDialog
        title: "修改数据"
        width: 400
        height: 200

        property var currentItem: null

        ColumnLayout {
            anchors.fill: parent
            spacing: 10

            TextField {
                id: editNameField
                placeholderText: "请输入姓名"
                text: editDialog.currentItem ? editDialog.currentItem.name : ""
                Layout.fillWidth: true
            }

            TextField {
                id: editAgeField
                placeholderText: "请输入年龄"
                text: editDialog.currentItem ? editDialog.currentItem.age : ""
                Layout.fillWidth: true
            }

            RowLayout {
                Button {
                    text: "取消"
                    onClicked: editDialog.close()
                }
                Button {
                    text: "确定"
                    onClicked: {
                        database.executeNonQuery("UPDATE People SET name = ?, age = ? WHERE id = ?", [editNameField.text, parseInt(editAgeField.text), editDialog.currentItem.id]);
                        loadData();
                        editDialog.close();
                    }
                }
            }
        }
    }

    // 加载数据
    function loadData() {
        var data = database.executeQuery("SELECT * FROM People");
        dataModel.clear();
        data.forEach(function(item) {
            dataModel.append(item);
        });
    }

    // 删除数据
    function deleteItem() {
        var item = dataModel.get(listView.currentIndex);
        database.executeNonQuery("DELETE FROM People WHERE id = ?", [item.id]);
        loadData();
    }

    // 初始化
    Component.onCompleted: {
        database.executeNonQuery("CREATE TABLE IF NOT EXISTS People (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)");
        loadData();
    }
}

六、实验现象

在运行程序后,将在项目文件夹下创建.db数据库文件,并且可以在QML界面中实现对数据库的增删改查:

https://i-blog.csdnimg.cn/direct/07f0d09da7d84bf998bf5c3a292d94ff.png