摘要:
C++ 操作DB真心不是太省心的事,一方面C++操作DB的介面大部分都使用C API,如Mysql、Sqlite 提供的API。儘管其C API文檔已經足夠清晰詳細,仍然存在一些問題,如記憶體申請、釋放,結果集的遍曆等。大多數人都會稍作封裝來隱藏CAPI 的細節,畢竟常用的操作無非是增刪改查。另一方面目前沒有比較方便易用的C++ 資料庫操作架構,導致C++ 的物件導向的記憶體模型與SQL DB 的關係型模型很難適配。我曾在幾個項目中看到過非常究竟的C++對象與SQL 行的映射架構。從那時起我就想完成一個輕量又實用的DB操作類庫。今天,此類庫已經初具雛形, 那就是FFDB。
FFDB 只是一個非常輕量的C++ 類庫,然而他實現的功能絕對能讓人印象深刻,某種意義上說具有些許的開創性。FFDB 是與可擴充的,FFDB 當前已經實現了Mysql和Sqlite的支援, 增加其他sql 支援也是很容易的。FFDB具有如下功能:
- FFDB 封裝針對DB 串連,統一了介面,當前FFDB 做了相當大的取捨,在我的大部分日常工作中,他讀完全滿足需求。
- FFDB 封裝了各個SQL DB 之間的區別,當需要換DB時,只需修稿串連的參數,而不是去修改所有相關的API 呼叫代碼。
- FFDB 提供了一個工具類FFCRUD, 封裝了對SQL DB的CRUD操作,FFCRUD保證了C++中操作記憶體對象後,同步到DB的操作變得舒服又容易。
FFDB 封裝DB串連
串連SQL DB, ffdb 提供了非常簡易的文法,串連sqlite的代碼:
if (ffdb.connect("sqlite:///tmp/test.db")) { printf("connect error:%s, %d\n", ffdb.error_msg(), ffdb.is_connected()); return 1; }
而串連mysql 則只需該成:
if (ffdb.connect("mysql://127.0.0.1:3306/user/passwd/db")) { printf("connect error:%s, %d\n", ffdb.error_msg(), ffdb.is_connected()); return 1; }
FFDB 執行SQL
FFDB 中執行sql的介面為:
int exe_sql(const string& sql_, db_each_row_callback_i* cb_ = NULL); int exe_sql(const string& sql_, vector<vector<string> >& ret_data_); int exe_sql(const string& sql_, vector<vector<string> >& ret_data_, vector<string>& col_names_);
第一個版本介面可以通過傳遞迴調函數定製如何擷取結果集資料。一般而言,要遍曆結果集只需要第二個版本的介面,將所有資料集轉換為字串數組。使用者剩了關心記憶體配置釋放細節。
FFDB 的關閉和影響行數
void close(); int affect_rows();
ffcrud 實現記憶體對象在SQL DB的增刪改查
ffcrud是模板類,重要的介面如下:
string insert_sql()string select_sql()string update_sql()string del_sql()int insert(ffdb_t& ffdb)int select(ffdb_t& ffdb)int update(ffdb_t& ffdb)int del(ffdb_t& ffdb)
xx_sql相關的操作返回要執行相關操作的sql字串,ffcrud沒有限定必須使用ffdb,這樣某些場合可以使用該sql語句用來非同步執行。同時ffcrud與 ffdb可以完美的結合,只要提供ffdb執行個體對象,記憶體對象資料可以直接同步處理到sql db中。
ffcrud如何映射記憶體對象到sql db中
ffcrud_register_t 完成記憶體對象和sql db中表的映射,在日常開發中,我發現最煩的最易變化的就是對象中的欄位和資料庫中的欄位的對應關係。使用ffcrud_register_t,盡最大程度的把映射關係做出配置,如果你願意,完全可以從設定檔中讀入映射關係。
範例程式碼:
#include "db/ffdb.h"#include "db/ffcrud.h"using namespace ff;#include <stdio.h>void dump(vector<vector<string> >& ret_data){ for (size_t i = 0; i < ret_data.size(); ++i) { printf("row[%u] begin======= ", i); for (size_t j = 0; j < ret_data[i].size(); ++j) { printf(" %s", ret_data[i][j].c_str()); } printf(" =======row[%u] end\n", i); } ret_data.clear();}struct foo_t: public ffcrud_t<foo_t>{ foo_t(): a(167), b("ddd"), m_c(11.22){} int a; string b; double& c() { return m_c; } double m_c;};int main(int argc, char* argv[]){ ffdb_t ffdb; foo_t foo; vector<vector<string> > ret_data; if (ffdb.connect("sqlite://./test.db")) { printf("connect error:%s, %d\n", ffdb.error_msg(), ffdb.is_connected()); return 1; } if (ffdb.exe_sql("CREATE TABLE IF NOT EXISTS dumy (A int, c float, b varchar(200), primary key (A))")) { printf("exe error:%s\n", ffdb.error_msg()); } if (ffdb.exe_sql("select * from dumy", ret_data)) { printf("exe error:%s\n", ffdb.error_msg()); } dump(ret_data); ffcrud_register_t<foo_t>::bind_table("dumy", "A") .def(&foo_t::a, "A") .def(&foo_t::c, "C") .def(&foo_t::b, "B"); printf("foo insert:<%s>\n", foo.insert_sql().c_str()); printf("foo select:<%s>\n", foo.select_sql().c_str()); printf("foo update:<%s>\n", foo.update_sql().c_str()); printf("foo delete:<%s>\n", foo.del_sql().c_str()); if (foo.insert(ffdb)) { printf("exist foo insert:<%s>\n", foo.insert_sql().c_str()); } foo.select(ffdb); if (ffdb.exe_sql("select * from dumy", ret_data)) { printf("exe error:%s\n", ffdb.error_msg()); } dump(ret_data); foo.m_c = 23.99; if (ffdb.exe_sql(foo.update_sql(), ret_data)) { printf("exe error:%s\n", ffdb.error_msg()); } if (ffdb.exe_sql("select * from dumy", ret_data)) { printf("exe error:%s\n", ffdb.error_msg()); } dump(ret_data); /* if (ffdb.exe_sql(foo.del_sql(), ret_data)) { printf("exe error:%s\n", ffdb.error_msg()); } if (ffdb.exe_sql("select * from dumy", ret_data)) { printf("exe error:%s\n", ffdb.error_msg()); } dump(ret_data); */ //if (ffdb.exe_sql("SELECT C FROM foo WHERE A = 167 limit 1;", ret_data)) if (ffdb.exe_sql(foo.select_sql(), ret_data)) { printf("exe error:%s\n", ffdb.error_msg()); } dump(ret_data); foo.b += "a"; foo.update(ffdb, &foo_t::b); if (ffdb.exe_sql(foo.select_sql(), ret_data)) { printf("exe error:%s\n", ffdb.error_msg()); } dump(ret_data); return 0;}
原始碼:
https://github.com/fanchy/fflib/tree/master/example/book/sqlite