
ODB 介绍
开发 C++ 高性能数据持久化程序时,你是否也有过这样的烦恼:想把程序里的对象存到数据库,却要手写 SQL 语句、对象与数据表的映射、结果集解析,光实现一个简单的增删改查就要写几十行重复代码;想兼容 MySQL、PostgreSQL、SQLite 等多种数据库,却要针对每种数据库写不同的底层操作,代码耦合度极高;想保证数据操作的安全性,还要手动处理 SQL 注入、事务管理、连接池,手写代码极易出错且难以维护;更糟的是,复杂对象的关联映射(一对多、多对一)、懒加载、缓存机制,手写实现起来无比繁琐,开发效率大打折扣。
尤其是在企业级 C++ 应用场景:桌面客户端、后端服务、嵌入式系统需要稳定高效的数据持久化,原生数据库 API 太过底层,开发成本高;面向对象的业务逻辑和关系型数据库的表结构无法直接匹配,手动映射极易产生数据不一致;项目需要快速切换数据库,却要重构大量数据操作代码,耗时又易出故障;轻则数据层代码臃肿混乱,重则因数据操作 bug 导致程序崩溃、数据丢失。
而 ODB 正是为解决这些痛点而来 ------ 这款工业级、跨平台的 C++ 对象关系映射(ORM)框架,核心就是让你用极简的代码,快速实现 C++ 对象到关系型数据库的自动映射、持久化和查询,它就像给你的 C++ 程序装上一个智能数据持久化引擎,无需手写底层 SQL 和映射逻辑,完美兼容主流数据库,自带事务、连接池、缓存、安全防护等高级特性,堪称 C++ 数据持久化开发的 "利器",广泛应用于桌面软件、企业服务、嵌入式设备等各类 C++ 项目。
光说不练假把式,我们用一个**"对象持久化 + 数据库增删改查"**的例子,快速感受 ODB 的便捷性:(基于注解定义对象映射,几步就能实现对象与数据库的无缝交互)
如果不清楚 ORM 是什么的话,可以先简单了解:ORM 是对象关系映射,让你操作数据库像操作本地 C++ 对象一样简单 😊
其实就是:ODB 就是 C++ 里的 ORM 框架,它的核心作用:让我们直接用 C++ 类、C++ 对象操作数据库,完全不用写 SQL!
第一步:定义持久化 C++ 类(person.hxx)
ODB 基于编译期注解 实现对象与数据表的自动映射,只需给类和成员添加简单注解,即可自动生成数据库操作代码,无需手动写建表语句和映射逻辑。
cpp
#ifndef PERSON_HXX
#define PERSON_HXX
// 包含ODB核心注解头文件
#include <odb/core.hxx>
#include <string>
// -------------------------- ODB 核心注解 --------------------------
// #pragma db object:标记该类为持久化类(自动映射为数据库表)
// #pragma db id:标记该成员为主键(唯一标识)
// #pragma db type:指定数据库字段类型(可选,ODB自动推导)
// -----------------------------------------------------------------
#pragma db object
class Person
{
public:
// 无参构造函数(ODB 要求必须有)
Person() = default;
// 自定义构造函数
Person(unsigned long id, const std::string& name, unsigned short age)
: id_(id), name_(name), age_(age) {}
// 成员的get/set方法(ODB通过访问器操作成员)
unsigned long id() const { return id_; }
void id(unsigned long id) { id_ = id; }
const std::string& name() const { return name_; }
void name(const std::string& name) { name_ = name; }
unsigned short age() const { return age_; }
void age(unsigned short age) { age_ = age; }
private:
// 主键:自增无符号长整型
#pragma db id auto
unsigned long id_;
// 姓名:映射为数据库VARCHAR字段
std::string name_;
// 年龄:映射为数据库SMALLINT字段
unsigned short age_;
};
#endif // PERSON_HXX
第二步:ODB 编译生成持久化代码
ODB 是编译型 ORM ,会根据注解自动生成数据库操作代码(增删改查、建表、映射),无需手动编写。执行命令生成映射文件(以 SQLite 数据库为例):
bash
# 语法:odb -d [数据库类型] --generate-query [头文件]
odb -d sqlite --generate-query person.hxx
执行后会自动生成 4 个核心文件:
person-odb.hxx:声明文件person-odb.ixx:内联实现person-odb.cxx:核心持久化逻辑(ODB 自动生成)
第三步:实现数据持久化主程序(main.cxx)
ODB 封装了所有数据库底层逻辑,只需创建数据库连接 、事务,直接操作 C++ 对象就能完成增删改查,无需手写任何 SQL 语句,自动处理连接、事务、异常。
cpp
#include <iostream>
#include <memory>
// 包含ODB数据库核心头文件
#include <odb/database.hxx>
#include <odb/transaction.hxx>
// 包含SQLite数据库驱动
#include <odb/sqlite/database.hxx>
// 包含自动生成的持久化类
#include "person-odb.hxx"
using namespace std;
using namespace odb::core;
int main()
{
try
{
// ===================== 1. 初始化数据库连接 =====================
// 创建SQLite数据库实例(文件型数据库,无需独立服务)
// 参数:数据库文件名、是否创建新库
unique_ptr<database> db(new odb::sqlite::database(
"test.db",
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
));
// ===================== 2. 创建数据表(自动) =====================
// ODB自动根据对象注解创建对应数据表,无需手写CREATE TABLE
{
transaction t(db->begin()); // 开启数据库事务
db->persist_schema<Person>(); // 创建Person表
t.commit(); // 提交事务
cout << "数据表创建成功!" << endl;
}
// ===================== 3. 插入数据(新增) =====================
{
transaction t(db->begin());
// 创建C++对象
Person p(0, "张三", 25);
// 持久化对象(自动生成INSERT语句)
db->persist(p);
t.commit();
cout << "数据插入成功,ID:" << p.id() << endl;
}
// ===================== 4. 查询数据 =====================
{
transaction t(db->begin());
// 根据ID查询对象(自动生成SELECT语句)
auto p = db->load<Person>(1);
cout << "查询结果:ID=" << p->id()
<< ",姓名=" << p->name()
<< ",年龄=" << p->age() << endl;
t.commit();
}
// ===================== 5. 更新数据 =====================
{
transaction t(db->begin());
auto p = db->load<Person>(1);
p->age(26); // 修改对象属性
db->update(*p); // 自动更新到数据库(UPDATE)
t.commit();
cout << "数据更新成功!" << endl;
}
// ===================== 6. 删除数据 =====================
{
transaction t(db->begin());
db->erase<Person>(1); // 根据ID删除(DELETE)
t.commit();
cout << "数据删除成功!" << endl;
}
}
catch (const exception& e)
{
// ODB统一异常处理,捕获所有数据库操作错误
cerr << "数据库操作失败:" << e.what() << endl;
return 1;
}
return 0;
}
第四步:编写 Makefile 并编译运行
ODB 编译简洁,只需链接对应数据库驱动和核心库,一键编译运行,无复杂依赖配置。
bash
# 编译器
CXX = g++
# 编译选项:C++11及以上,开启优化
CXXFLAGS = -std=c++11 -O2
# 链接库:ODB核心 + SQLite驱动
LDFLAGS = -lodb -lodb-sqlite
# 目标文件
TARGET = odb_demo
# ODB自动生成的代码
ODB_SRC = person-odb.cxx
# 编译所有文件
all: $(TARGET)
$(TARGET): main.cxx $(ODB_SRC)
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
# 清理编译产物
clean:
rm -f $(TARGET) *.o test.db
bash
# 1. 先生成ODB映射代码
odb -d sqlite --generate-query person.hxx
# 2. 编译程序
make
# 3. 运行
./odb_demo
bash
数据表创建成功!
数据插入成功,ID:1
查询结果:ID=1,姓名=张三,年龄=25
数据更新成功!
数据删除成功!
运行后会自动生成 test.db 数据库文件,所有数据操作都通过 C++ 对象完成,全程无需手写 SQL!
当我们想新增一个持久化对象时,只需添加注解定义类,ODB 自动生成所有数据库操作代码;想切换数据库(MySQL/PostgreSQL/SQLite),只需修改一行连接代码,业务逻辑零改动;想实现事务、批量操作、关联查询,只需调用 ODB 封装好的 API,无需处理底层细节;想保证数据安全,ODB 内置防 SQL 注入、自动类型校验,彻底避免手动编码的漏洞。
这就是 ODB 的核心价值:把 C++ 对象持久化的复杂底层逻辑全部封装,让开发者聚焦业务逻辑,用几行代码实现企业级稳定的数据操作,同时兼容多数据库、支持高级特性,是 C++ 开发桌面应用、后端服务、嵌入式系统数据持久化的 "不二之选"。
除了基础的增删改查,ODB 还内置超多企业级能力,满足各类复杂场景:
- 多数据库完美兼容:一键支持 SQLite、MySQL、PostgreSQL、Oracle、SQL Server 等主流数据库,代码零改动切换;
- 编译期安全:所有映射、查询在编译期校验,杜绝运行期 SQL 错误,比动态 ORM 更稳定;
- 高级对象映射:支持一对多、多对一、多对多关联、懒加载、继承映射,轻松处理复杂业务对象;
- 高性能:无运行时开销,原生 C++ 实现,内置连接池、查询缓存,性能接近原生数据库 API;
- 完善的事务机制:支持显式 / 隐式事务、嵌套事务、回滚,保证数据原子性和一致性;
- 类型安全查询:支持面向对象的查询语法(ODB Query),无需手写字符串 SQL,杜绝注入风险;
- 零侵入设计:不修改原有业务类结构,仅通过注解实现持久化,不影响程序原有逻辑;
- 跨平台:支持 Linux、Windows、macOS、嵌入式 Linux 等所有 C++ 支持的平台。
从简单的本地数据存储,到大型企业级 C++ 应用的分布式数据持久化,ODB 都能完美适配,真正做到简洁易用、性能强悍、工业级可靠!
其实就是:
- ODB = C++ 的 ORM 工具
- ORM :对象关系映射,把类 ↔ 数据表 、对象 ↔ 一行数据自动对应。
- ODB:专门给 C++ 做的、工业级、高性能 ORM。
- 你不用写 SQL,只用写 C++
以前操作数据库:
cpp// 手写 SQL,容易错、麻烦、不安全 execute("INSERT INTO person (name,age) VALUES ('张三',25)");用 ODB 以后:
cpp// 直接操作 C++ 对象 Person p{"张三", 25}; db->persist(p); // 插入ODB 自动帮你生成 SQL、执行、映射结果。
ODB 就是让你通过 C++ 类来操作数据库。
- 定义一个 C++ 类 = 定义一张表
- 创建一个对象 = 新增一行数据
- 修改对象 = 更新数据库
- 查询对象 = 查数据库
- 删除对象 = 删数据
全程 零 SQL、零拼接、零注入风险、零重复代码。
C++ 概念 数据库概念 类(Class) 数据表(Table) 对象(Object) 一行记录(Row) 成员变量 字段(Column) 成员函数 数据操作逻辑
下面我们可以进行 ODB 的安装【这个就不演示了,太多了,可以自己去找资料,挺多的】和全方位深入学习!
ODB 框架全解析(C++ 持久化层开发指南)
下面基于 Linux 环境 + MySQL 数据库,解析类型映射、编译器指令、核心类 / 函数、完整实战案例及高级特性
完整类型映射表【为了建表/关联试图】
ODB 自动完成 C++ 类型与 MySQL 类型的映射,以下是完整映射表 ,包含字段含义、默认值及适用场景,补充了原内容缺失的复合类型、自定义类型映射。
| C++ 基础类型 | MySQL 类型 | 默认值 | 是否可为 NULL | 语义说明 |
|---|---|---|---|---|
bool |
TINYINT(1) |
0 |
NOT NULL | 布尔值(0/1) |
char |
CHAR(1) |
\0 |
NOT NULL | 单字符 |
signed char |
TINYINT |
0 |
NOT NULL | 有符号单字节整数 |
unsigned char |
TINYINT UNSIGNED |
0 |
NOT NULL | 无符号单字节整数 |
signed short |
SMALLINT |
0 |
NOT NULL | 有符号短整型 |
unsigned short |
SMALLINT UNSIGNED |
0 |
NOT NULL | 无符号短整型 |
int |
INT |
0 |
NOT NULL | 有符号整型 |
unsigned int |
INT UNSIGNED |
0 |
NOT NULL | 无符号整型 |
long |
BIGINT |
0 |
NOT NULL | 有符号长整型(32/64 位适配) |
unsigned long |
BIGINT UNSIGNED |
0 |
NOT NULL | 无符号长整型 |
long long |
BIGINT |
0 |
NOT NULL | 64 位有符号整型 |
unsigned long long |
BIGINT UNSIGNED |
0 |
NOT NULL | 64 位无符号整型 |
float |
FLOAT |
0.0 |
NOT NULL | 单精度浮点 |
double |
DOUBLE |
0.0 |
NOT NULL | 双精度浮点 |
std::string |
VARCHAR(255) |
"" |
NOT NULL | 可变长度字符串(可指定长度) |
std::u16string |
VARCHAR(255) |
"" |
NOT NULL | UTF-16 字符串 |
std::u32string |
VARCHAR(255) |
"" |
NOT NULL | UTF-32 字符串 |
| 日期时间类型(ODB 封装) | MySQL 类型 | 默认值 | 是否可为 NULL | 语义说明 |
|---|---|---|---|---|
odb::date |
DATE |
无 | NULL | 日期(年 - 月 - 日) |
odb::time |
TIME |
无 | NULL | 时间(时 - 分 - 秒) |
odb::datetime |
DATETIME |
无 | NULL | 日期时间(年 - 月 - 日 时 - 分 - 秒) |
odb::timestamp |
TIMESTAMP |
无 | NULL | 时间戳(含毫秒,自动更新) |
| 复合 / 特殊类型 | MySQL 映射方式 | 说明 |
|---|---|---|
std::vector<T> |
关联表 + 外键 | 一对多关联,需配合 #pragma odb container |
std::map<K, V> |
关联表 + 双外键 | 键值对关联,适用于多对多映射 |
| 自定义枚举 | ENUM(枚举值) |
需通过 #pragma odb value 声明 |
| 自定义结构体 | 嵌入表(列拼接) | 需通过 #pragma odb value 声明为值类型 |
ODB 编译器指令
ODB 的 #pragma odb 指令是映射规则的核心 ,下面会给出完整指令集、字段含义、使用场景及代码实例,按 "类级指令""成员级指令" 分类,结构更清晰。
类级指令(作用于整个 C++ 类)
| 指令格式 | 字段 / 参数含义 | 核心作用 | 适用场景 | 实例 |
|---|---|---|---|---|
#pragma odb object |
无参数 | 声明类为持久化对象 ,映射到数据库表 | 所有需要持久化的核心类 | #pragma odb object\nclass User { ... }; |
#pragma odb table("table_name") |
table_name:数据库表名 |
自定义类映射的表名(默认:类名小写) | 避免类名与数据库关键字冲突 | #pragma odb object\n#pragma odb table("t_user")\nclass User { ... }; |
#pragma odb view |
无参数 | 声明类为视图对象,映射到数据库视图(非表) | 多表联查、只读数据展示 | #pragma odb view\nclass UserView { ... }; |
#pragma odb namespace("ns") |
ns:数据库命名空间 |
为表指定数据库命名空间**(MySQL 中对应库名)** | 多库隔离场景 | #pragma odb object\n#pragma odb namespace("test_db")\nclass User { ... }; |
#pragma odb abstract |
无参数 | 声明类为抽象持久化类,不生成表,仅作为父类被继承 | 多态持久化的基类 | #pragma odb object abstract\nclass BaseEntity { ... }; |
成员级指令(作用于类的成员变量)
| 指令格式 | 字段 / 参数含义 | 核心作用 | 适用场景 | 实例 |
|---|---|---|---|---|
#pragma odb id |
无参数 | 声明成员为主键 | 唯一标识表中记录 | int id_;\n#pragma odb id; |
#pragma odb id auto |
无参数 | 声明主键为自增列 | 无需手动赋值的主键 | int id_;\n#pragma odb id auto; |
#pragma odb column("col_name") |
col_name:数据库列名 |
自定义成员映射的列名(默认:成员名) | 成员名与关键字冲突 / 规范列名 | std::string name_;\n#pragma odb column("user_name"); |
#pragma odb not_null |
无参数 | 声明列不可为 NULL | 必选字段 | std::string phone_;\n#pragma odb not_null; |
#pragma odb null |
无参数 | 声明列可为 NULL(覆盖类型默认规则) | 可选字段 | std::string avatar_;\n#pragma odb null; |
#pragma odb default("val") |
val:默认值(字符串 / 数值) |
为列设置数据库默认值 | 字段有固定默认值 | int status_ = 1;\n#pragma odb default("1"); |
#pragma odb unique |
无参数 | 声明列唯一约束 | 避免重复数据(如手机号) | std::string phone_;\n#pragma odb unique; |
#pragma odb index("idx_name") |
idx_name:索引名 |
为列创建普通索引 | 提升查询效率 | std::string email_;\n#pragma odb index("idx_user_email"); |
#pragma odb unique index("idx_name") |
idx_name:唯一索引名 |
为列创建唯一索引 | 唯一约束 + 查询优化 | std::string id_card_;\n#pragma odb unique index("idx_id_card"); |
#pragma odb transient |
无参数 | 声明成员不持久化 | 临时业务字段(如缓存数据) | std::string temp_token_;\n#pragma odb transient; |
#pragma odb type("sql_type") |
sql_type:MySQL 原生类型 |
强制指定成员的数据库类型 | 覆盖默认映射(如长字符串) | std::string desc_;\n#pragma odb type("TEXT"); |
#pragma odb converter(cvt) |
cvt:自定义类型转换器 |
为自定义类型指定映射规则 | 非基础类型(如自定义日期类) | MyDate birth_;\n#pragma odb converter(MyDateConverter); |
#pragma odb container |
无参数 | 声明容器成员为关联集合 | 一对多 / 多对多关联 | std::vector<Order> orders_;\n#pragma odb container; |
关联与高级指令
| 指令格式 | 核心作用 | 实例 |
|---|---|---|
#pragma odb relationship one_to_many |
声明一对多关联 | 父类 User 关联子类 Order |
#pragma odb relationship many_to_one |
声明多对一关联 | 子类 Order 关联父类 User |
#pragma odb relationship many_to_many |
声明多对多关联 | User 与 Role 多对多 |
#pragma odb trigger("trigger_name") |
绑定数据库触发器 | 插入数据时自动更新时间戳 |
ODB 核心类 / 成员 / 函数
ODB 的核心操作通过持久化辅助类 + 框架核心类完成,以下分「核心框架类」「持久化辅助接口」「结果集类」三类,详解类的作用、成员变量、核心函数及使用场景。
核心框架类(ODB 内置,负责核心能力)
odb::mysql::database(数据库连接类)
作用 :创建与 MySQL 的连接,是所有数据库操作的入口,支持连接池。核心成员(构造参数):
| 参数 | 类型 | 含义 |
|---|---|---|
user |
const std::string& |
数据库用户名 |
password |
const std::string& |
数据库密码 |
database |
const std::string& |
数据库名 |
host |
const std::string& |
数据库地址(默认:localhost) |
port |
unsigned int |
数据库端口(默认:3306) |
conn_pool_size |
size_t |
连接池大小(默认:1) |
核心成员函数:
| 函数签名 | 功能 | 适用场景 |
|---|---|---|
template <typename T> void persist(T& obj) |
持久化对象(插入数据) | 新增记录 |
template <typename T> void update(T& obj) |
更新对象(更新数据) | 修改已有记录 |
template <typename T> void erase(T& obj) |
删除对象(删除数据) | 删除单条记录 |
template <typename T> void erase(const odb::id_t& id) |
按主键删除 | 通过主键快速删除 |
template <typename T> odb::result<T> query(const odb::query<T>& q) |
执行查询,返回结果集 | 条件查询、分页查询 |
template <typename T> T* load(const odb::id_t& id) |
按主键加载对象(返回指针) | 主键查询单条记录 |
template <typename T> bool find(const odb::id_t& id, T& obj) |
按主键查找并赋值(返回是否存在) | 避免空指针的主键查询 |
std::shared_ptr<odb::transaction> begin() |
开启事务 | 原子性操作(如转账) |
odb::transaction(事务类)
作用 :管理数据库事务,保证操作的原子性、一致性、隔离性、持久性(ACID)。核心成员函数:
| 函数签名 | 功能 |
|---|---|
void commit() |
提交事务(执行操作) |
void rollback() |
回滚事务(放弃操作) |
static std::shared_ptr<transaction> current() |
获取当前线程的事务 |
odb::query<T>(查询条件类)
作用 :构建类型安全的 SQL 查询条件,替代手写 SQL,支持链式调用。核心用法:
- 基础条件:
odb::query<User>::name == "张三"、odb::query<User>::age > 18; - 逻辑运算:
&&(与)、||(或)、!(非); - 模糊查询:
odb::like(odb::query<User>::name, "张%"); - 分页查询:
odb::limit(10)、odb::offset(20); - 排序:
odb::query<User>::age.asc()(升序)、odb::query<User>::age.desc()(降序)。
持久化辅助接口(ODB 编译生成,绑定业务类)
当使用 odb 编译器编译 #pragma odb object 声明的类后,会自动生成持久化辅助函数,嵌入业务类的作用域,核心如下:
| 函数签名 | 功能 |
|---|---|
void odb_persist(odb::database& db) |
快速持久化当前对象 |
void odb_update(odb::database& db) |
快速更新当前对象 |
void odb_erase(odb::database& db) |
快速删除当前对象 |
odb::id_t odb_id() const |
获取当前对象的主键值 |
结果集类(odb::result<T>)
作用 :存储查询结果,支持迭代、遍历、统计,是 ODB 封装的 "查询结果容器"。核心成员函数:
| 函数签名 | 功能 |
|---|---|
iterator begin() |
获取结果集起始迭代器 |
iterator end() |
获取结果集结束迭代器 |
bool empty() |
判断结果集是否为空 |
size_t size() |
获取结果集记录数 |
T& front() |
获取第一条记录 |
T& back() |
获取最后一条记录 |
template <typename U> odb::result<U> cast() |
多态结果集类型转换 |
完整实战案例
本节实现一个用户 - 订单 一对多关联的完整案例,包含「类定义」「编译指令」「核心 CRUD」「事务操作」「关联查询」,代码基于 Linux + MySQL,可直接编译运行。
步骤 1:定义持久化类(user.hxx)
包含用户类(User)和订单类(Order),实现一对多关联,补充原内容缺失的关联映射。
cpp
// user.hxx
#pragma once
#include <odb/core.hxx>
#include <odb/nullable.hxx>
#include <string>
#include <vector>
#include <ctime>
// 前置声明:解决循环依赖
class Order;
/**
* @brief 用户持久化类
* 映射到 MySQL 表:t_user
*/
#pragma odb object
#pragma odb table("t_user") // 自定义表名
class User {
public:
// 构造函数
User() = default;
User(const std::string& name, int age, const std::string& phone)
: name_(name), age_(age), phone_(phone) {}
// ===== 持久化成员 =====
// 主键:自增整型
int id_;
#pragma odb id auto
// 用户名:非空,自定义列名
std::string name_;
#pragma odb column("user_name") not_null
// 年龄:可选(可为NULL),默认值18
odb::nullable<int> age_;
#pragma odb default("18")
// 手机号:唯一约束,索引
std::string phone_;
#pragma odb unique index("idx_user_phone") not_null
// 注册时间:ODB 时间戳类型,默认当前时间
odb::timestamp reg_time_;
#pragma odb default("CURRENT_TIMESTAMP")
// 一对多关联:一个用户对应多个订单
// container:声明为容器关联;one_to_many:一对多关系
std::vector<Order> orders_;
#pragma odb container relationship one_to_many
// ===== 非持久化成员(临时字段)=====
std::string temp_token_;
#pragma odb transient
// ODB 编译生成的持久化辅助函数(声明)
#pragma odb user
};
/**
* @brief 订单持久化类
* 映射到 MySQL 表:t_order
*/
#pragma odb object
#pragma odb table("t_order")
class Order {
public:
// 构造函数
Order() = default;
Order(int user_id, const std::string& goods, double price)
: user_id_(user_id), goods_(goods), price_(price) {}
// ===== 持久化成员 =====
// 主键:自增
int id_;
#pragma odb id auto
// 外键:关联用户表主键(多对一)
int user_id_;
#pragma odb column("user_id") not_null index("idx_order_user_id")
// 商品名称:非空
std::string goods_;
#pragma odb not_null
// 订单金额:非空,MySQL 类型 DECIMAL(10,2)
double price_;
#pragma odb type("DECIMAL(10,2)") not_null
// 订单状态:枚举类型(自定义映射)
enum class Status {
UNPAID = 0,
PAID = 1,
SHIPPED = 2,
FINISHED = 3
};
Status status_;
#pragma odb default("0") // 默认未支付
// ODB 编译生成的持久化辅助函数(声明)
#pragma odb user
};
// ODB 枚举类型映射:将 Order::Status 映射为 MySQL ENUM
#pragma odb value(Order::Status) type("ENUM('UNPAID', 'PAID', 'SHIPPED', 'FINISHED')")
步骤 2:编译持久化类(生成辅助代码)
在 Linux 终端执行以下命令,使用 odb 编译器生成 user-odb.hxx、user-odb.cxx(核心持久化代码):
bash
# 编译命令:-d mysql 指定数据库为MySQL,-o . 指定输出目录为当前目录
odb -d mysql -o . user.hxx
步骤 3:核心业务操作(main.cxx)
包含初始化数据库连接 「新增用户」「新增订单」「更新订单」「按主键查询」「条件查询」「事务操作」「关联查询」,补充原内容缺失的异常处理 和复杂查询。
cpp
// main.cxx
#include <iostream>
#include <memory>
#include <odb/database.hxx>
#include <odb/transaction.hxx>
#include <odb/query.hxx>
#include <odb/mysql/database.hxx>
#include "user.hxx"
#include "user-odb.hxx" // ODB 编译生成的辅助头文件
// 数据库配置(替换为你的 MySQL 信息)
const std::string DB_USER = "root";
const std::string DB_PWD = "123456";
const std::string DB_NAME = "test_odb";
const std::string DB_HOST = "localhost";
const unsigned int DB_PORT = 3306;
const size_t CONN_POOL_SIZE = 5; // 连接池大小
/**
* @brief 创建数据库连接实例
* @return 数据库连接智能指针
*/
std::shared_ptr<odb::mysql::database> create_db() {
try {
return std::make_shared<odb::mysql::database>(
DB_USER, DB_PWD, DB_NAME, DB_HOST, DB_PORT, CONN_POOL_SIZE
);
} catch (const odb::mysql::error& e) {
std::cerr << "数据库连接失败:" << e.what() << std::endl;
exit(1);
}
}
int main() {
// 1. 初始化数据库连接
auto db = create_db();
std::cout << "数据库连接成功!" << std::endl;
try {
// ===================== 2. 事务操作:新增用户 + 新增订单 =====================
{
odb::transaction t(db->begin()); // 开启事务
// 2.1 新增用户
User user("张三", 25, "13800138000");
db->persist(user); // 持久化后,id_ 会被自动赋值(自增主键)
std::cout << "新增用户成功,用户ID:" << user.odb_id() << std::endl;
// 2.2 新增订单(关联刚创建的用户)
Order order1(user.id_, "小米14手机", 3999.0);
Order order2(user.id_, "华为Mate 70 Pro", 6999.0);
db->persist(order1);
db->persist(order2);
std::cout << "新增订单成功,订单1ID:" << order1.odb_id()
<< ",订单2ID:" << order2.odb_id() << std::endl;
t.commit(); // 提交事务
std::cout << "事务提交成功!" << std::endl;
}
// ===================== 3. 按主键查询用户 =====================
{
// 假设用户ID为1(实际应替换为上面的 user.odb_id())
int user_id = 1;
std::shared_ptr<User> user = db->load<User>(user_id);
if (user) {
std::cout << "\n按主键查询用户:" << std::endl;
std::cout << "用户ID:" << user->id_ << std::endl;
std::cout << "用户名:" << user->name_ << std::endl;
std::cout << "年龄:" << (user->age_ ? std::to_string(*user->age_) : "未设置") << std::endl;
std::cout << "手机号:" << user->phone_ << std::endl;
}
}
// ===================== 4. 条件查询:查询价格大于5000的订单 =====================
{
// 构建查询条件:price > 5000 且 status = UNPAID,按价格降序
odb::query<Order> q = odb::query<Order>::price > 5000 &&
odb::query<Order>::status == Order::Status::UNPAID &&
odb::query<Order>::order_by(odb::query<Order>::price.desc());
odb::result<Order> orders = db->query<Order>(q);
std::cout << "\n价格大于5000的未支付订单:" << std::endl;
for (const auto& order : orders) {
std::cout << "订单ID:" << order.id_ << std::endl;
std::cout << "商品:" << order.goods_ << std::endl;
std::cout << "价格:" << order.price_ << std::endl;
}
}
// ===================== 5. 更新操作:将订单1标记为已支付 =====================
{
odb::transaction t(db->begin());
int order_id = 1; // 订单1 ID
std::shared_ptr<Order> order = db->load<Order>(order_id);
if (order) {
order->status_ = Order::Status::PAID;
db->update(*order);
std::cout << "\n订单" << order_id << "已更新为已支付!" << std::endl;
}
t.commit();
}
// ===================== 6. 删除操作:删除订单2 =====================
{
odb::transaction t(db->begin());
int order_id = 2; // 订单2 ID
db->erase<Order>(order_id); // 按主键删除
std::cout << "\n订单" << order_id << "已删除!" << std::endl;
t.commit();
}
// ===================== 7. 关联查询:查询用户的所有订单 =====================
{
int user_id = 1;
odb::query<Order> q = odb::query<Order>::user_id == user_id;
odb::result<Order> user_orders = db->query<Order>(q);
std::cout << "\n用户" << user_id << "的所有订单:" << std::endl;
for (const auto& order : user_orders) {
std::cout << "订单ID:" << order.id_ << ",商品:" << order.goods_ << ",状态:"
<< static_cast<int>(order.status_) << std::endl;
}
}
} catch (const odb::exception& e) {
// ODB 异常统一处理
std::cerr << "\n数据库操作异常:" << e.what() << std::endl;
return 1;
}
return 0;
}
步骤 4:编译并运行(Linux)
创建 CMakeLists.txt 简化编译(ODB 依赖较多,推荐使用 CMake):
bash
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(odb_demo)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 查找 ODB 库
find_package(ODB REQUIRED)
include(${ODB_USE_FILE})
# 查找 MySQL 客户端库
find_library(MYSQLCLIENT_LIB mysqlclient)
# 源文件
set(SOURCES
main.cxx
user-odb.cxx # ODB 编译生成的源文件
)
# 生成可执行文件
add_executable(odb_demo ${SOURCES})
# 链接库
target_link_libraries(odb_demo
odb::odb
odb::mysql
${MYSQLCLIENT_LIB}
)
终端执行编译与运行命令:
bash
# 创建构建目录
mkdir build && cd build
# 编译
cmake .. && make
# 运行(确保 MySQL 服务已启动,且 test_odb 库已创建)
./odb_demo
高级特性补充
原内容未覆盖 ODB 的高级特性,以下补充多态持久化「连接池优化」「自定义类型转换器」,是企业级开发的核心知识点。
多态持久化
适用于 "父类指针指向子类对象" 的场景,ODB 支持自动识别子类类型并持久化。
cpp
// 抽象父类
#pragma odb object abstract
class Shape {
public:
int id_;
#pragma odb id auto
virtual double area() const = 0;
};
// 子类:圆
#pragma odb object
class Circle : public Shape {
public:
double radius_;
#pragma odb not_null
double area() const override { return 3.14 * radius_ * radius_; }
};
// 子类:矩形
#pragma odb object
class Rectangle : public Shape {
public:
double width_, height_;
#pragma odb not_null
double area() const override { return width_ * height_; }
};
// 用法:父类指针持久化
std::shared_ptr<Shape> s = std::make_shared<Circle>(5.0);
db->persist(*s); // ODB 自动识别为 Circle,存入对应表
连接池优化(Linux 下性能调优)
ODB 的 database 类原生支持连接池,在高并发场景下,需调整连接池大小 和空闲超时:
cpp
// 自定义连接池配置
odb::mysql::connection_pool_factory factory;
factory.max_connections(CONN_POOL_SIZE); // 最大连接数
factory.min_connections(2); // 最小空闲连接数
factory.idle_timeout(std::chrono::seconds(300)); // 空闲超时5分钟
// 传入连接池工厂创建数据库
auto db = std::make_shared<odb::mysql::database>(
DB_USER, DB_PWD, DB_NAME, DB_HOST, DB_PORT, &factory
);
自定义类型转换器
当 C++ 自定义类型无法直接映射到 MySQL 时,可通过类型转换器 实现,例如将 std::pair<int, int> 映射为 VARCHAR:
cpp
// 自定义转换器
class PairConverter {
public:
// C++ 类型 -> SQL 字符串
static std::string to_db(const std::pair<int, int>& p) {
return std::to_string(p.first) + "," + std::to_string(p.second);
}
// SQL 字符串 -> C++ 类型
static std::pair<int, int> from_db(const std::string& s) {
size_t pos = s.find(',');
return {std::stoi(s.substr(0, pos)), std::stoi(s.substr(pos + 1))};
}
};
// 持久化类中使用
#pragma odb object
class Point {
public:
int id_;
#pragma odb id auto
std::pair<int, int> coord_;
#pragma odb converter(PairConverter) type("VARCHAR(20)")
};