C++ 的 ORM:比较与示例
在 C++ 的世界里,与数据库的交互历史上一直比 Python 或 Java 等语言更底层。然而,现代项目需要便利性和生产力,因此开发者越来越频繁地使用 ORM(对象关系映射)。C++ 的 ORM 允许将表当作普通的 C++ 对象来处理,自动生成 SQL 查询并管理模式迁移。在本文中,我们将介绍五个流行的库:ODB、SQLiteCpp、SOCI、cppdb 和 sqlpp11,比较它们的特点并提供实际示例。
1. ODB — 企业级标准
ODB 是一个强大的 C++ ORM 框架,支持多种数据库管理系统:MySQL、PostgreSQL、SQLite、Oracle、SQL Server。其主要特点是代码生成:你在带有注解的头文件中描述模型,然后 ODB 编译器 (odb) 会生成用于数据库操作的代码。
// person.hxx#pragma once#include <string>#include <odb/core.hxx>
#pragma db objectclass Person {public: Person() {} Person(const std::string& name, int age) : name_(name), age_(age) {}
const std::string& getName() const { return name_; } int getAge() const { return age_; }
private: friend class odb::access; #pragma db id auto unsigned long id_; std::string name_; int age_;};运行 odb -d mysql person.hxx 后,会生成包含实现的文件。然后使用标准模式:
#include <odb/database.hxx>#include <odb/mysql/database.hxx>#include "person-odb.hxx"
int main() { auto db = std::make_shared<odb::mysql::database>("user", "pass", "mydb"); Person p("Alice", 30); db->persist(p); // 加载 auto loaded = db->load<Person>(1); return 0;}优点: 完整的 ORM,支持迁移、延迟加载、事务处理。缺点: 配置复杂,需要代码生成,对于小型项目来说过于重量级。
2. SQLiteCpp — 轻量且简单
SQLiteCpp 是一个基于 C++ RAII 风格的 SQLite3 封装。它不是一个完整的 ORM,但为查询操作提供了便捷的接口。
#include <SQLiteCpp/Database.h>#include <SQLiteCpp/Statement.h>
int main() { SQLite::Database db("test.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); db.exec("CREATE TABLE IF NOT EXISTS person (id INTEGER PRIMARY KEY, name TEXT, age INT)"); SQLite::Statement insert(db, "INSERT INTO person (name, age) VALUES (?, ?)"); insert.bind(1, "Bob"); insert.bind(2, 25); insert.exec(); // 读取 SQLite::Statement query(db, "SELECT * FROM person WHERE age > ?"); query.bind(1, 20); while (query.executeStep()) { std::cout << query.getColumn(1) << " " << query.getColumn(2) << ""; } return 0;}优点: 极简,无需代码生成,仅适用于 SQLite。缺点: 没有对象映射,需要手动编写 SQL。
3. SOCI — 通用抽象层
SOCI(C++ 数据库访问库)并非纯粹的 ORM,而是针对不同数据库管理系统(SQLite、PostgreSQL、MySQL、Oracle)的抽象层。它提供了便捷的语法来执行查询并将结果绑定到 C++ 结构体。
#include <soci/soci.h>#include <soci/sqlite3.h>
struct Person { int id; std::string name; int age;};
int main() { soci::session sql(soci::sqlite3, "test.db"); sql << "CREATE TABLE person (id INTEGER PRIMARY KEY, name TEXT, age INT)"; Person p{1, "Charlie", 35}; sql << "INSERT INTO person (id, name, age) VALUES (:id, :name
```