fnc12/sqlite_orm 使用心得
===
sqlite_orm[^sqlite_orm] 是一個 C++ 的 slite 函式庫,雖然在 github 上跟同類型的庫相比算是星星較多的,但是有不少潛在的問題,以下針對使用過程留下一些筆記,避免後人繼續坑...
### 環境建置
首先,它仰賴 Catch2,於是我從 github[^Catch2] 上 clone 下來編譯後安裝。
接著 `make` 遲遲過不了,後來發現是自動下載有點問題,不知道為什麼仰賴的 sqlite 函式庫是透過自動下載但是卻總是載不完整導致 sha1 驗證過不了。於是我移除 cmake 對 sqlite-amalgamation-3210000.zip 的 sha1 驗證,並從官方網站[^sqlite]手動下載。
總算完成 `make install`,趕緊開個 hello world 使用看看這個函式庫,但是 `cmake` 卻總是卡住,後來才發現是套件的別名與路徑有問題(作者提供的引用方式語法不對),於是我在 `cmake` 使用 `find_package(sqlite_orm REQUIRED NAMES sqlite_orm SqliteOrm)`,並且用指令修復標頭檔的相對位置:
```bash
mkdir /usr/local/include/sqlite_orm
sudo mv sqlite_orm.h sqlite_orm/.
```
### 封裝問題
接著我試著要依照OOP的寫法封裝成 Repository ,卻碰到了問題。
從原始碼可以看出 ` make_storage` 的回傳型別與傳入的參數有關[^make_storage],也就是其型別受到資料庫的欄位設定影響。
> I mean there is no common storage_t type cause storage_t is templated just like `std::vector`: `std::vector<int>` ≠ `std::vector<char>`
因此要獲得型別必須使用以下方法[^type]:
```cpp=
struct Employee {
int id;
std::string name;
int age;
std::string address;
double salary;
};
inline auto initStorage(const std::string &path) {
using namespace sqlite_orm;
return make_storage(path,
make_table("COMPANY",
make_column("ID",
&Employee::id,
primary_key()),
make_column("NAME",
&Employee::name),
make_column("AGE",
&Employee::age),
make_column("ADDRESS",
&Employee::address),
make_column("SALARY",
&Employee::salary)));
}
using Storage = decltype(initStorage(""));
Storage storage = initStorage("storage.sqlite");
```
注意當中的 `using Storage = decltype(initStorage(""));`,
在獲得型別 `Storage` 必須先調用 `initStorage`,欲調用 `initStorage` 則必先定義該函數,這使得獲得型別時,程式碼已經處於定義階段,因此無法在宣告階段的標頭檔使用該型別[^undefined],換句話說,若要撰寫宣告與定義分離的的程式碼(`.cpp` 與`.h`)在實現上有一定程度的困難:
```cpp
// SpeciesRepository.h
class SpeciesRepository{
public:
SpeciesRepository();
private:
Storage storage;
};
```
最後總算是把大部分的前置作業放到 `.hpp`,再從 class include 解決:
```cpp=
//SpeciesStorage.hpp
#pragma once
#include <string>
#include <vector>
#include <sqlite_orm/sqlite_orm.h>
struct Species{
int id;
int parentId;
int generation;
int batch;
bool testing;
bool tested;
std::vector<char> chromosome;
int scored;
};
inline auto createSpeciesStorage(const std::string &path) {
using namespace sqlite_orm;
return make_storage(path,
make_table("species",
make_column("id", &Species::id, autoincrement(), primary_key()),
make_column("parent_id", &Species::parentId),
make_column("generation", &Species::generation),
make_column("batch", &Species::batch),
make_column("testing", &Species::testing),
make_column("tested", &Species::tested),
make_column("chromosome", &Species::chromosome),
make_column("scored", &Species::scored)
));
}
using SpeciesStorage = decltype(createSpeciesStorage(""));
```
```cpp
#pragma once
#include <memory>
#include "SpeciesStorage.hpp"
class SpeciesRepository{
public:
SpeciesRepository(const std::string &path);
private:
std::unique_ptr<SpeciesStorage> storage;
};
```
### 又一個教學問題
好了,解決封裝問題了,該來寫一些 CRUD 的邏輯了,沒想到又碰到問題,依照 `READ.md` 提供的語法[^crud]嘗試:
```cpp
storage.update_all(set(c(&User::lastName) = "Hardey",
c(&User::typeId) = 2),
where(c(&User::firstName) == "Tom"));
```
卻得到的錯誤訊息:
```
error: invalid operands
to binary expression ('internal::expression_t<bool Species::*>' and 'int')
```
接著我在範例發現不一樣的寫法[^example]:
```cpp
storage.update_all(set(assign(&Track::trackArtist, 3)),
where(is_equal(&Track::trackName, "Mr. Bojangles")));
```
### IDE 解析問題
我使用 Netbeans 11.1,IDE 解析得到一大堆紅底線,程式碼維護起來更為困難。主要原因是該函式庫大量使用 template ,因此 IDE 不能順暢的辨識型別。類似的問題在我使用 STL 的容器也會發生。
### 結論
花了不少時間在故障排除,雖然過程中也有學點東西,不過基本上已經到達我的停損點了,使用該函式庫的計畫暫時擱置,準備動身去嘗試其他替代方案了。對這個套件的作者沒有惡意,只是個人使用起來的體驗蠻糟糕的...
[^example]: https://github.com/fnc12/sqlite_orm/blob/e8a9e9416f421303f4b8970caab26dadf8bae98b/examples/foreign_key.cpp#L73
[^crud]: https://github.com/fnc12/sqlite_orm#crud
[^undefined]: https://stackoverflow.com/a/8752879/11833578
[^type]: https://github.com/fnc12/sqlite_orm/wiki/FAQ#quick--binary-fat
[^make_storage]: https://github.com/fnc12/sqlite_orm/blob/master/include/sqlite_orm/sqlite_orm.h#L11837
[^Catch2]: https://github.com/catchorg/Catch2
[^sqlite]: https://www.sqlite.org/2017/sqlite-amalgamation-3210000.zip
[^sqlite_orm]: https://github.com/fnc12/sqlite_orm
###### tags: `learning note` `c++` `2020-05-09`