摘要:
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
更多精彩文章 http://h2cloud.org