Qt操作SQLite數據庫的教程詳解

0.前言

SQLite是一款開源、輕量級、跨平臺的數據庫,無需server,無需安裝和管理配置。它的設計目標是嵌入式的,所以很適合小型應用,也是Qt應用開發種常用的一種數據庫。

1.驅動

Qt SQL模塊使用驅動程序插件(plugins)與不同的數據庫API進行通信。由於Qt的SQL模塊API與數據庫無關,因此所有特定於數據庫的代碼都包含在這些驅動程序中。Qt提供瞭幾個驅動程序,也可以添加其他驅動程序。提供驅動程序源代碼,可用作編寫自己的驅動程序的模型。

QtCreator在*.pro中引入sql模塊(QT+=sql),或是VS中在Qt VS Tool裡勾選上sql模塊,就可以使用該模塊的接口瞭。

可以使用QSqlDatabase::drivers()獲取驅動程序列表並打印,Qt5.9.7輸出如下:

其中,SQLite是一個進程內數據庫,這意味著沒有必要擁有數據庫服務器。SQLite在單個文件上運行,在打開連接時必須將其設置為數據庫名稱。如果該文件不存在,SQLite將嘗試創建它。。

2.初相遇

/*
 * ... ...
 * 我喜歡那樣的夢
 * 在夢裡 一切都可以重新開始
 * 一切都可以慢慢解釋
 * 心裡甚至還能感覺到所有被浪費的時光
 * 竟然都能重回時的狂喜和感激
 * ... ...
 * 
 *                ----席慕容《初相遇》
 */
 
 
#include <QSqlDatabase>
#include <QDebug>
 
void initDb()
{   
    //qDebug()<<QSqlDatabase::drivers();//打印驅動列表
    QSqlDatabase db;
    //檢測已連接的方式 - 默認連接名
    //QSqlDatabase::contains(QSqlDatabase::defaultConnection)
    if(QSqlDatabase::contains("qt_sql_default_connection"))
        db = QSqlDatabase::database("qt_sql_default_connection");
    else
        db = QSqlDatabase::addDatabase("QSQLITE");
    //檢測已連接的方式 - 自定義連接名
    /*if(QSqlDatabase::contains("mysql_connection"))
        db = QSqlDatabase::database("mysql_connection");
    else
        db = QSqlDatabase::addDatabase("QSQLITE","mysql_connection");*/
    //設置數據庫路徑,不存在則創建
    db.setDatabaseName("sqltest.db");
    //db.setUserName("gongjianbo");  //SQLite不需要用戶名和密碼
    //db.setPassword("qq654344883");
    
    //打開數據庫
    if(db.open()){
        qDebug()<<"open success";
        //關閉數據庫
        db.close();
    }
    
}

上面的代碼中,先是創建瞭一個QSqlDatabase對象,該類用於處理數據庫的連接。contains方法用於查看給定的連接名稱是否在連接列表中,database方法獲取數據庫連接,前提是已使用addDatabase添加數據庫連接。addDatabase聲明如下:

QSqlDatabase QSqlDatabase::addDatabase(const QString &type, const QString &connectionName = QLatin1String(defaultConnection)) [static]

第一個參數對應驅動名,第二個參數為連接名稱 ,如果不使用默認連接名稱“qt_sql_default_connection”的話需要填寫該參數。

設置瞭驅動及連接名稱後,就是設置數據庫文件的名稱/路徑,因為SQLite不需要用戶名和密碼,接下來直接就可以通過open和close函數來打開關閉該數據庫瞭。

此外,如果需要在內存中創建數據庫,而不是指定一個文件,可以setDatabaseName(“:memory:”);

db.setDatabaseName(":memory:");

3.創建表

SQL語句執行需要用到QSqlQuery類,文檔有雲:

QSqlQuery封裝瞭在QSqlDatabase上執行的SQL查詢中創建,導航和檢索數據所涉及的功能。它可以被用來執行DML(數據操縱語言)語句,例如select、insert、update、delete,以及DDL(數據定義語言)語句,如create table,還可以用於執行非標準SQL的特定於數據庫的命令。

成功執行的SQL語句將查詢的狀態設置為活動狀態,以便isActive()返回true。否則,查詢的狀態將設置為非活動狀態。在任何一種情況下,執行新的SQL語句時,查詢都位於無效記錄上。必須先將活動查詢導航到有效記錄(以便isActive()返回true),然後才能檢索值。

#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
 
void createTable()
{
    //sql語句不熟悉的推薦《sql必知必會》,輕松入門
    //如果不存在則創建my_table表
    //id自增,name唯一
    const QString sql=R"(
                      CREATE TABLE IF NOT EXISTS  my_table (
                      id   INTEGER   PRIMARY KEY AUTOINCREMENT NOT NULL,
                      name CHAR (50) UNIQUE NOT NULL,
                      age  INTEGER
                      );)";
    //QSqlQuery構造前,需要db已打開並連接
    //未指定db或者db無效時使用默認連接進行操作
    QSqlQuery query;
    
    if(query.exec(sql)){
        qDebug()<<"init table success";
    }else{
        //打印sql語句錯誤信息
        qDebug()<<"init table error"<<query.lastError();
    }
}

代碼通過QSqlQuery的exec方法執行SQL語句,創建瞭一個簡單的數據表。exec執行SQL語句成功返回true,否則返回false。

QSqlQuery的構造函數可以指定QDatabase參數,如果未指定db,或者db無效將使用默認連接。

QSqlQuery::QSqlQuery(QSqlDatabase db);

要註意的是,對於SQLite,exec方法一次隻能執行一個語句。

4.執行增刪改查

執行瞭上面的操作且數據庫已open,就能使用QSqlQuery的exec方法來完成增刪改查瞭。

//插入數據
void insertRecord(const QString &name, int age)
{
    QSqlQuery query;
    //方式一,直接執行SQL語句
    query.exec(QString(R"(INSERT INTO my_table(name,age) VALUES('%1',%2);)")
               .arg(name).arg(age));
    //方式二,綁定值,待定變量默認用問號占位,註意字符串也沒有引號
    /*query.prepare(R"(INSERT INTO my_table(name,age) VALUES(?,?);)");
    query.addBindValue(name);
    query.addBindValue(age);
    query.exec();*/
}
 
//刪除數據
void deleteRecord(const QString &name)
{
    QSqlQuery query;
    //方式一,直接執行SQL語句
    query.exec(QString(R"(DELETE FROM my_table WHERE name='%1';)")
               .arg(name));
    //方式二,綁定值,待定變量默認用問號占位
    /*query.prepare(R"(DELETE FROM my_table WHERE name=?;)");
    query.addBindValue(name);
    query.exec();*/
}
 
//更新數據
void updateRecord(const QString &name, int age)
{
    QSqlQuery query;
    //方式一,直接執行SQL語句
    query.exec(QString(R"(UPDATE my_table SET age=%2 WHERE name='%1';)")
               .arg(name).arg(age));
    //方式二,綁定值,待定變量默認問號,可自定義
    /*query.prepare(R"(UPDATE my_table SET age=:age WHERE name=:name;)");
    query.bindValue(":name",name);//通過自定義的別名來替代
    query.bindValue(":age",age);
    query.exec();*/
}
 
//查詢數據
int searchRecord(const QString &name)
{
    QSqlQuery query;
    query.exec(QString(R"(SELECT age FROM my_table WHERE name='%1';)")
               .arg(name));
 
    //獲取查詢結果的第0個值,
    //如果結果是多行數據,可用while(query.next()){}遍歷每一行
    int ageValue=-1;
    if(query.next()){
        ageValue=query.value(0).toInt();
    }
    qDebug()<<ageValue;
    return ageValue;
}

可以看到,如果熟悉SQL語句的話,很容易就實現瞭增刪改查功能。

對於 BLOB 類型,查詢後可以 toByteArray,修改時可以 bindValue QByteArray。

5.進階

有時候會遇到大量數據操作的情況,這時候用普通的insert之類的語句循環操作可能會很慢。

技巧一:開啟事務

SQLite通過執行”BEGIN;“或是”BEGIN TRANSACTION;“開啟事務,執行”ROLLBACK;“進行回滾,執行”COMMIT;“或是”END TRANSACTION;“提交事務。QSqlDatabase也提供瞭對應的transaction、rollback、commit三個函數來執行對應操作。

技巧二:關閉寫同步(synchrnous)

在SQLite中,數據庫配置的參數都由編譯指示(pragma)來實現的,而其中synchronous選項有三種可選狀態,分別是full、normal、off。簡要說來,full寫入速度最慢,但保證數據是安全的,不受斷電、系統崩潰等影響,而off可以加速數據庫的一些操作,但如果系統崩潰或斷電,則數據庫可能會損毀。通過執行”PRAGMA synchronous = OFF;“語句,可以提升效率,不過若不是臨時數據庫不建議此操作。

SQLite 默認是文件鎖, Qt 中 SQLite 默認是以多線程讀寫模式打開,如果同時寫入就會出現寫入錯誤:

可以將寫操作上鎖,但是實測線程中循環寫入時,隻讀打開去查詢也是會阻塞很久,毫秒到幾秒不等,這時候就得把超時設置長一點。

所以還是得讀寫都加鎖,但這也隻能解決單個進程的並發訪問。

到此這篇關於Qt操作SQLite數據庫的教程詳解的文章就介紹到這瞭,更多相關Qt操作SQLite數據庫內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: