Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Library Management System Based on Qt: SQL Function Development

Tech 1

1 Encapsulate a Global Object

Create a new C++ class named sqlmange, and add sql to the .pro file.

Add SQL module

Use the C++ singleton pattern to ensure only one instance exists and provide a global access point.

Singleton pattern

The following code implements the SQLManager class, which manages database connections and operations using the singleton pattern.

// sqlmanager.h
#ifndef SQLMANAGER_H
#define SQLMANAGER_H

#include <QDebug>

class SQLManager
{
public:
    static SQLManager* getInstance();
    void test();

private:
    SQLManager();
    SQLManager(const SQLManager&) = delete;
    SQLManager& operator=(const SQLManager&) = delete;

    static SQLManager* instance;
};

#endif // SQLMANAGER_H

// sqlmanager.cpp
#include "sqlmanager.h"

SQLManager* SQLManager::instance = nullptr;

SQLManager::SQLManager() {}

SQLManager* SQLManager::getInstance()
{
    if (nullptr == instance) {
        instance = new SQLManager();
    }
    return instance;
}

void SQLManager::test()
{
    qDebug() << "test";
}

Call the interface in main():

SQLManager::getInstance()->test();

2 Design All Interfaces

// Constructor
SQLManager();

// Singleton instance
static SQLManager* getInstance();

// Initialize database
void init();

// Login
bool login(const QString& username, const QString& password);

// Get all users
QVector<QStringList> getUsers(const QString& condition = "");

// Add user
void addUser(const QVector<QStringList>& data);

// Delete user
void deleteUser(const QString& id);

// Get all books
QVector<QStringList> getBooks(const QString& condition = "");

// Add book
void addBook(const QVector<QStringList>& data);

// Modify book
void modifyBook(const QStringList& data);

// Delete book
void deleteBook(const QString& id);

// Return book
QString returnBook(const QString& userId, const QString& bookId);

// Borrow book
QString borrowBook(const QString& userId, const QString& bookId);

// Get records
QVector<QStringList> getRecords(const QString& condition = "");

// Clear records
QString clearRecord();

2.1 Initialize Database Interface

Use QSqlDatabase functions provided by Qt.

Qt SQL documentation

Place the database file in a db folder under the application directory, then implement:

void SQLManager::init()
{
    m_db = QSqlDatabase::addDatabase("QSQLITE");
    m_db.setDatabaseName(QCoreApplication::applicationDirPath() + "/db/book.db");
    qDebug() << m_db.open();
}

If no driver is found, ensure the database file is included in the build path.

2.2 Login Interface

Use a query to verify username and password.

bool SQLManager::login(const QString& username, const QString& password)
{
    QSqlQuery query(m_db);
    QString sql = QString("SELECT * FROM user WHERE username = '%1' AND password = '%2'")
                      .arg(username).arg(password);
    bool ret = query.exec(sql);
    if (!ret) {
        qDebug() << query.lastError().text();
    }
    return ret;
}

// Usage in main:
qDebug() << "login:" << SQLManager::getInstance()->login("xiaoming", "123456");

2.3 Conditional Query Users Interface

Pass the WHERE clause as a condition.

QVector<QStringList> SQLManager::getUsers(const QString& condition)
{
    QSqlQuery query(m_db);
    QString sql = QString("SELECT * FROM user %1").arg(condition);
    QVector<QStringList> result;
    bool ret = query.exec(sql);
    if (!ret) {
        qDebug() << query.lastError().text();
    } else {
        int cols = query.record().count();
        while (query.next()) {
            QStringList row;
            for (int i = 0; i < cols; ++i) {
                row << query.value(i).toString();
            }
            result.push_back(row);
        }
    }
    return result;
}

Example usage:

qDebug() << "getuser:" << SQLManager::getInstance()->getUsers("WHERE username LIKE '%xiao%' OR nickname LIKE '%小%'");

3 User Interface

3.1 Initialize Page

Fetch data from the database and display it in a table view.

void UserManagementPage::initPage(const QString& condition)
{
    QVector<QStringList> data = SQLManager::getInstance()->getUsers(condition);
    m_model.clear();
    m_model.setHorizontalHeaderLabels({"User ID", "Name", "Grade", "Permission", "College", "Username", "Password"});
    for (const auto& row : data) {
        QList<QStandardItem*> items;
        for (const auto& cell : row) {
            items.append(new QStandardItem(cell));
        }
        m_model.appendRow(items);
    }
}

Call initPage when switching to the user page:

if (str == "btn_user") {
    m_userPage->initPage("");
    ui->stackedWidget->setCurrentIndex(0);
}

3.2 Delete User

First, implement the deletion in SQLManager:

void SQLManager::deleteUser(const QString& id)
{
    QSqlQuery query(m_db);
    QString sql = QString("DELETE FROM user WHERE userid = '%1'").arg(id);
    query.exec(sql);
}

Then connect the delete button:

void UserManagementPage::onDeleteButtonClicked()
{
    int row = ui->tableView->currentIndex().row();
    if (row < 0) {
        QMessageBox::information(nullptr, "Error", "No user selected");
        return;
    }
    QString id = m_model.item(row, 0)->text();
    SQLManager::getInstance()->deleteUser(id);
    QMessageBox::information(nullptr, "Success", "User deleted");
    initPage();
}

3.3 Search User

Connect the text changed signal of the search line edit:

void UserManagementPage::onSearchTextChanged(const QString& text)
{
    QString condition = QString("WHERE username LIKE '%%1%' OR nickname LIKE '%%2%'").arg(text).arg(text);
    initPage(condition);
}

3.4 Import Users

Read a CSV file and ensert data in to the database.

void SQLManager::addUser(const QVector<QStringList>& data)
{
    m_db.transaction();
    QSqlQuery query(m_db);
    for (const auto& row : data) {
        QString sql = QString("INSERT INTO user VALUES(NULL,'%1','%2','%3','%4','%5','%6')")
                          .arg(row[0].toUtf8()).arg(row[1].toUtf8()).arg(row[2].toUtf8())
                          .arg(row[3].toUtf8()).arg(row[4].toUtf8()).arg(row[5].toUtf8());
        query.exec(sql);
    }
    m_db.commit();
}

void UserManagementPage::onImportButtonClicked()
{
    QString path = QFileDialog::getOpenFileName(nullptr, "Select CSV file");
    if (path.isEmpty()) return;
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return;
    QVector<QStringList> data;
    while (!file.atEnd()) {
        QString line = file.readLine();
        QStringList fields = line.split(",");
        if (fields.size() != 6) {
            QMessageBox::information(nullptr, "Error", "Invalid column count");
            return;
        }
        fields.last().chop(2); // Remove trailing newline
        data.push_back(fields);
    }
    SQLManager::getInstance()->addUser(data);
    QMessageBox::information(nullptr, "Success", "Import successful");
    initPage();
}

Note: Encoding isues may occur if the CSV is in GBK.

4 Book Management Interface

4.1 Initialize Book Interface

Similar to user initialization but querying the book table.

4.2 Search Interface

void BookManagementPage::onSearchTextChanged(const QString& text)
{
    QString condition = QString("WHERE name LIKE '%%1%' OR type1 LIKE '%%2%' OR type2 LIKE '%%3%' OR type3 LIKE '%%4%'")
                            .arg(text).arg(text).arg(text).arg(text);
    initPage(condition);
}

4.3 Delete Book Interface

Similar to delete user.

4.4 Add and Modify Book

Create a new dialog form class for adding/editing books.

Book dialog

Dialog layout

The cancel button simply hides the dialog.

Implement the SQL logic:

void SQLManager::addBook(const QVector<QStringList>& data)
{
    if (data.isEmpty()) return;
    QSqlQuery query(m_db);
    QStringList row = data[0];
    QString sql = QString("INSERT INTO book VALUES(NULL,'%1','%2','%3','%4','%5','%6','')")
                      .arg(row[1]).arg(row[2]).arg(row[3]).arg(row[4]).arg(row[5]).arg(row[6]);
    query.exec(sql);
}

void SQLManager::modifyBook(const QStringList& data)
{
    QSqlQuery query(m_db);
    QString sql = QString("UPDATE book SET name='%1', press='%2', type1='%3', type2='%4', type3='%5', count='%6' WHERE bookid='%7'")
                      .arg(data[1]).arg(data[2]).arg(data[4]).arg(data[5]).arg(data[6]).arg(data[3]).arg(data[0]);
    query.exec(sql);
}

In the dialog, determine whether to add or modify:

void BookAddEditDialog::setType(int id)
{
    m_id = id;
    if (id != -1) {
        auto bookData = SQLManager::getInstance()->getBooks(QString("WHERE bookid = %1").arg(id));
        if (bookData.size() == 1) {
            auto row = bookData[0];
            ui->nameEdit->setText(row[1]);
            ui->pressEdit->setText(row[2]);
            ui->combo1->setCurrentText(row[3]);
            ui->combo2->setCurrentText(row[4]);
            ui->combo3->setCurrentText(row[5]);
            ui->countEdit->setText(row[6]);
        }
    }
}

void BookAddEditDialog::onOkButtonClicked()
{
    QStringList data;
    data << QString::number(m_id);
    data << ui->nameEdit->text();
    data << ui->pressEdit->text();
    data << ui->countEdit->text();
    data << ui->combo1->currentText();
    data << ui->combo2->currentText();
    data << ui->combo3->currentText();
    if (m_id != -1) {
        SQLManager::getInstance()->modifyBook(data);
    } else {
        QVector<QStringList> vec;
        vec.push_back(data);
        SQLManager::getInstance()->addBook(vec);
    }
    accept();
}

Back in the book management page:

void BookManagementPage::onAddButtonClicked()
{
    BookAddEditDialog dialog;
    dialog.exec();
    initPage();
}

4.5 Borrow Book

Create a new dialog for borrowing.

Borrow dialog

Implement the borrow logic:

void SQLManager::borrowBook(const QString& userId, const QString& bookId)
{
    QSqlQuery query(m_db);
    QString sql1 = QString("UPDATE book SET count = count - 1 WHERE bookid = '%1'").arg(bookId);
    QString sql2 = QString("INSERT INTO record VALUES(NULL,'%1','%2','%3','%4','')")
                       .arg(bookId).arg(userId)
                       .arg(QDateTime::currentSecsSinceEpoch())
                       .arg(QDateTime::currentSecsSinceEpoch() + 3600 * 24 * 10);
    if (!query.exec(sql1)) qDebug() << query.lastError().text();
    if (!query.exec(sql2)) qDebug() << query.lastError().text();
}

In the borrow dialog, verify credentials and execute borrowing:

void BorrowDialog::onOkButtonClicked()
{
    QString username = ui->usernameEdit->text();
    QString password = ui->passwordEdit->text();
    int userId = 0;
    bool loginOk = SQLManager::getInstance()->login(username, password, userId);
    if (!loginOk) {
        reject();
        return;
    }
    SQLManager::getInstance()->borrowBook(QString::number(userId), QString::number(m_bookId));
    accept();
}

void BorrowDialog::setBookId(int id)
{
    m_bookId = id;
    auto bookData = SQLManager::getInstance()->getBooks(QString("WHERE bookid = %1").arg(id));
    if (!bookData.isEmpty()) {
        ui->bookNameLabel->setText(QString("Book Name: %1").arg(bookData[0][1]));
    }
}

5 Record Management Interface

5.1 Initialize Record Interface

Similar to previous initializations but querying the record table.

Return Book

Implement a return dialog similar to borrowing, but with the return logic:

void SQLManager::returnBook(const QString& userId, const QString& bookId)
{
    QSqlQuery query(m_db);
    QString sql1 = QString("UPDATE book SET count = count + 1 WHERE bookid = '%1'").arg(bookId);
    QString sql2 = QString("UPDATE record SET returntime = '%1' WHERE bookid = '%2' AND userid = '%3' AND returntime IS NULL")
                       .arg(QDateTime::currentSecsSinceEpoch()).arg(bookId).arg(userId);
    if (!query.exec(sql1)) qDebug() << query.lastError().text();
    if (!query.exec(sql2)) qDebug() << query.lastError().text();
}
Tags: qtsql

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.