在 C++ 中操作 MySQL 一直缺乏像 Java Hibernate 或 Python SQLAlchemy 這樣成熟的 ORM 框架。SOCI (The C++ Database Access Library) 提供了簡潔的類 SQL 語法、參數綁定與行到結構體的自動映射,且依賴輕、編譯快,是 C++ 專案接入 MySQL 的優選。本文通過完整單元測試代碼演示核心用法。
-- 建庫建表 CREATE DATABASE soci; USE soci; CREATE TABLE tb_test ( id INT(11) NOT NULL AUTO_INCREMENT, name VARCHAR(32) DEFAULT '', sex INT(11) DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY name (name) );
session sql(backend, conn_str):建立連接,MySQL 後端為 mysql。sql << "SQL" [, into(...), use(...)]:流式綁定輸出/輸入參數。indicator:指示查詢結果狀態(i_ok、i_null、i_truncated)。type_conversion<T>:自定義結構體與表列的雙向映射。TEST(soci, select_one) {
try {
session sql(mysql, "host=localhost db=soci user=dantezhu");
indicator ind;
string name = "dandan";
int sex;
sql << "SELECT sex FROM tb_test WHERE name = :name", into(sex, ind), use(name);
ASSERT_EQ(ind, i_ok) << name;
} catch (exception const &e) { FAIL() << e.what(); }
}
into(sex, ind) 接收結果,use(name) 綁定輸入參數。成功時 ind == i_ok。
TEST(soci, select_multi) {
session sql(mysql, "db=soci user=dantezhu");
indicator ind;
int count;
sql << "SELECT COUNT(*) FROM tb_test", into(count, ind);
if (count == 0) return;
int sex = 1;
vector<string> vec_name(count);
vector<int> vec_sex(count);
sql << "SELECT name, sex FROM tb_test WHERE sex = :sex", into(vec_name), into(vec_sex), use(sex);
}
多行時 into() 參數為 vector,需預先分配大小。
TEST(soci, insert) {
session sql(mysql, "db=soci user=dantezhu");
string name = "dandan"; int sex = 1;
sql << "INSERT INTO tb_test(name, sex) VALUES(:name, :sex)", use(name), use(sex);
}
已知限制:SOCI 目前無法直接返回 affected_rows 或自增主鍵值,需額外查詢 LAST_INSERT_ID()。
struct Person {
int id;
std::string name;
int sex;
};
namespace soci {
template<>
struct type_conversion<Person> {
typedef values base_type;
static void from_base(values const &v, indicator /*ind*/, Person &p) {
p.id = v.get<int>("id");
p.name = v.get<std::string>("name");
p.sex = v.get<int>("sex");
}
static void to_base(const Person &p, values &v, indicator &ind) {
v.set("id", p.id);
v.set("name", p.name);
v.set("sex", p.sex);
ind = i_ok;
}
};
}
注意:官方文檔範例中模板特化語法有誤,需按上述寫法。
TEST(soci, select_obj) {
session sql(mysql, "db=soci user=dantezhu");
string name = "dandan";
Person p;
sql << "SELECT id, name, sex FROM tb_test WHERE name = :name", into(p), use(name);
if (sql.got_data()) cout << p.id << "," << p.name << "," << p.sex << endl;
}
TEST(soci, insert_obj) {
session sql(mysql, "db=soci user=dantezhu");
Person p = {0, "niuniu", 2};
sql << "INSERT INTO tb_test(name, sex) VALUES(:name, :sex)", use(p);
}
自動展開結構體成員為參數,極大簡化代碼。
litesql 或 ODB,但編譯依賴更重。