sqlit3 數據庫操做的實現與解析

設計思想

選擇官方c接口,實現Idb通用接口。具體的數據庫操做,主要由兩個函數ExecQuerySql和ExecNoneQuerySql來封裝,底層的操做,主要使用sqlite3_prepare_v2來實現。mysql

具體實現

數據庫鏈接處理

由於Sqlit3是文件型數據庫,因此用智能指針來保存打開數據庫的句柄,防止內存泄漏。c++

std::unique_ptr<sqlite3, Deleter> mSQLitePtr;

對應的釋放結構git

struct Deleter
{
    void operator()(sqlite3* apSQLite) {
        const int ret = sqlite3_close(apSQLite);
        (void)ret;
        SQLITECPP_ASSERT(SQLITE_OK == ret, "database is locked");
    };
};

打開數據庫鏈接

Sqlit3Db(const char* apFilename,
    const int   aFlags = OPEN_READWRITE,
    const int   aBusyTimeoutMs = 0,
    const char* apVfs = nullptr) : mFilename(apFilename)
{
    sqlite3* handle;
    const int ret = sqlite3_open_v2(apFilename, &handle, aFlags, apVfs);        //打開數據庫句柄
    mSQLitePtr.reset(handle);                                                    //保存到智能指針中
    ...                                                                          //錯誤處理,略
};

執行返回結果集的查詢

Rjson ExecQuerySql(string aQuery, vector<string> fields) {
    Rjson rs = Utils::MakeJsonObjectForFuncReturn(STSUCCESS);            //默認返回成功,使用utils.h中的json組裝函數
    sqlite3_stmt* stmt = NULL;
    sqlite3* handle = getHandle();                                        //取得打開的數據庫句柄
    string u8Query = Utils::UnicodeToU8(aQuery);
    const int ret = sqlite3_prepare_v2(handle, u8Query.c_str(), static_cast<int>(u8Query.size()), &stmt, NULL);
    if (SQLITE_OK != ret)
    {
        string errmsg = sqlite3_errmsg(getHandle());                    //取得錯誤信息,重置返回信息
        rs.ExtendObject(Utils::MakeJsonObjectForFuncReturn(STDBOPERATEERR, errmsg));
    }
    else {
        ... //處理獲取表字段名稱的sql語句,略
        sqlite3_get_table(handle, aQueryLimit0.c_str(), &pRes, &nRow, &nCol, &pErr);
        for (int j = 0; j < nCol; j++)
        {
            string fs = *(pRes + j);
            if (find(fields.begin(), fields.end(), fs) == fields.end()) {
                fields.push_back(fs);                                //保存數據表字段名稱
            }
        }
        ...
        vector<Rjson> arr;                                            //存儲查詢結果集的json對象數組
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            Rjson al;
            for (int j = 0; j < nCol; j++)
            {
                string k = fields.at(j);
                int nType = sqlite3_column_type(stmt, j);            //取得字段值的類型
                if (nType == 1) {                    //SQLITE_INTEGER
                    al.AddValueInt(k, sqlite3_column_int(stmt, j)); //數值類型處理
                }
                else if (nType == 2) {                //SQLITE_FLOAT
                }
                else if (nType == 3) {                //SQLITE_TEXT    字符串類型處理
                    al.AddValueString(k, Utils::U8ToUnicode((char*)sqlite3_column_text(stmt, j)));
                }
                else if (nType == 4) {                //SQLITE_BLOB
                }
                else if (nType == 5) {                //SQLITE_NULL
                }                                    //暫時只處理了數值與字符串,其它暫未用到
            }
            arr.push_back(al);
        }
        if (arr.empty())                            //結果集爲空,返回查詢空
            rs.ExtendObject(Utils::MakeJsonObjectForFuncReturn(STQUERYEMPTY));
        rs.AddValueObjectArray("data", arr);        //增長結果集,類型爲數組
    }
    sqlite3_finalize(stmt);
    cout << "SQL: " << aQuery << endl;
    return rs;
    }

執行無結果集的查詢

Rjson ExecNoneQuerySql(string aQuery) {
    Rjson rs = Utils::MakeJsonObjectForFuncReturn(STSUCCESS);
    sqlite3_stmt* stmt = NULL;
    sqlite3* handle = getHandle();
    string u8Query = Utils::UnicodeToU8(aQuery);
    const int ret = sqlite3_prepare_v2(handle, u8Query.c_str(), static_cast<int>(u8Query.size()), &stmt, NULL);
    if (SQLITE_OK != ret)
    {
        string errmsg = sqlite3_errmsg(getHandle());
        rs.ExtendObject(Utils::MakeJsonObjectForFuncReturn(STDBOPERATEERR, errmsg));
    }
    else {
        sqlite3_step(stmt);                        //只須要執行一次就好
    }
    sqlite3_finalize(stmt);
    cout << "SQL: " << aQuery << endl;
    return rs;
}

智能查詢實現

提取保留關鍵字,它們是fuzzy,sort,page,size,sum,count,group,完成排序、分頁、分組等特殊查詢操做。github

string fuzzy = params.GetStringValueAndRemove("fuzzy");                    //精確匹配與模糊查詢切換開關
string sort = params.GetStringValueAndRemove("sort");                    //排序
int page = atoi(params.GetStringValueAndRemove("page").c_str());        //分頁
int size = atoi(params.GetStringValueAndRemove("size").c_str());        //分頁大小
string sum = params.GetStringValueAndRemove("sum");                        //字段求和
string count = params.GetStringValueAndRemove("count");                    //字段統計
string group = params.GetStringValueAndRemove("group");                    //分組

處理關鍵字 ins,lks,ors ,完成in查詢,多字段模糊與查詢,多字段精確或查詢。sql

if (k.compare("ins") == 0) {
    string c = ele.at(0);        //ins參數處理,第一個是字段名,後面是多個查詢值
    vector<string>(ele.begin() + 1, ele.end()).swap(ele);    //拼接in查詢sql語句
    whereExtra.append(c).append(" in ( ").append(Utils::GetVectorJoinStr(ele)).append(" )");
}
else if (k.compare("lks") == 0 || k.compare("ors") == 0) {
    whereExtra.append(" ( ");
    for (size_t j = 0; j < ele.size(); j += 2) {    //lks與ors參數處理,(字段,值)的重複
        if (j > 0) {
            whereExtra.append(" or ");
        }
        whereExtra.append(ele.at(j)).append(" ");    //拼接sql語句
        string eqStr = k.compare("lks") == 0 ? " like '" : " = '";
        string vsStr = ele.at(j + 1);
        if (k.compare("lks") == 0) {
            vsStr.insert(0, "%");
            vsStr.append("%");
        }
        vsStr.append("'");
        whereExtra.append(eqStr).append(vsStr);
    }
    whereExtra.append(" ) ");
}

處理值,不等操做是在值中加入了不等符號;fuzzy開關。數據庫

if (Utils::FindStartsCharArray(QUERY_UNEQ_OPERS, (char*)v.c_str())) {
    vector<string> vls = Utils::MakeVectorInitFromString(v);
    if (vls.size() == 2) {        //一個不等條件處理
        where.append(k).append(vls.at(0)).append("'").append(vls.at(1)).append("'");
    }
    else if (vls.size() == 4) {    //一對不等條件處理
        where.append(k).append(vls.at(0)).append("'").append(vls.at(1)).append("' and ");
        where.append(k).append(vls.at(2)).append("'").append(vls.at(3)).append("'");
    }
}
else if (!fuzzy.empty() && vType == kStringType) {    //精確查詢與模糊查詢切換
    where.append(k).append(" like '%").append(v).append("%'");
}
else {
    if (vType == kNumberType)        //數值類型不加單引號
        where.append(k).append(" = ").append(v);
    else                            //字符類型要加單引號
        where.append(k).append(" = '").append(v).append("'");
}

分頁處理json

if (page > 0) {
    page--;
    querySql.append(" limit ").append(Utils::IntTransToString(page * size)).append(",").append(Utils::IntTransToString(size));
}

批量插入

多值插入,sqlit3採用標準sql語法,insert into users (field1,field2) select 'val1','val2' union all select 'val3','val4' union all select ...api

Rjson insertBatch(string tablename, vector<Rjson> elements) {
    string sql = "insert into ";
    string keyStr = " (";
    keyStr.append(Utils::GetVectorJoinStr(elements[0].GetAllKeys())).append(" ) ");        //拼接表字段
    for (size_t i = 0; i < elements.size(); i++) {
        vector<string> keys = elements[i].GetAllKeys();
        string valueStr = " select ";
        for (size_t j = 0; j < keys.size(); j++) {                                    //組裝一條紀錄
            valueStr.append("'").append(elements[i][keys[j]]).append("'");
            if (j < keys.size() - 1) {
                valueStr.append(",");
            }
        }
        if (i < elements.size() - 1) {                                                //準備拼接下一條紀錄
            valueStr.append(" union all ");
        }
        keyStr.append(valueStr);
    }
    sql.append(tablename).append(keyStr);
    return ExecNoneQuerySql(sql);                                            //調用無結果集操做
}

事務操做

Rjson transGo(vector<string> sqls, bool isAsync = false) {
    char* zErrMsg = 0;
    bool isExecSuccess = true; 
    sqlite3_exec(getHandle(), "begin;", 0, 0, &zErrMsg);                //開始事務處理
    for (size_t i = 0; i < sqls.size(); i++) {                            
        string u8Query = Utils::UnicodeToU8(sqls[i]);                    //處理中文
        int rc = sqlite3_exec(getHandle(), u8Query.c_str(), 0, 0, &zErrMsg);    //處理一個操做
        if (rc != SQLITE_OK)
        {
            isExecSuccess = false;
            cout << "Transaction Fail, sql " << i + 1 << " is wrong. Error: " << zErrMsg << endl;
            sqlite3_free(zErrMsg);
            break;
        }
    }
    if (isExecSuccess)                    //成功,提交到數據庫
    {
        sqlite3_exec(getHandle(), "commit;", 0, 0, 0);
        sqlite3_close(getHandle());
        cout << "Transaction Success: run " << sqls.size() << " sqls." << endl;
        return Utils::MakeJsonObjectForFuncReturn(STSUCCESS, "insertBatch success.");
    }
    else                                //有失敗的,回滾
    {
        sqlite3_exec(getHandle(), "rollback;", 0, 0, 0);
        sqlite3_close(getHandle());
        return Utils::MakeJsonObjectForFuncReturn(STDBOPERATEERR, zErrMsg);
    }
}

項目地址

https://github.com/zhoutk/Jorm

系列文章規劃

  • c++操做關係數據庫通用接口設計(JSON-ORM c++版)
  • Rjson -- rapidjson代理的設計與實現
  • sqlit3 數據庫操做的實現與解析
  • mysql 數據庫操做的實現與解析
  • postgres 數據庫操做的實現與解析
  • oracle 數據庫操做的實現與解析
  • mssql 數據庫操做的實現與解析
  • 總結(若是須要的話)

感覺

批量插入的標準sql居然很陌生,平時被mysql的方言慣壞了...數組

相關文章
相關標籤/搜索