C++ ORM 实战:ODB 框架全解析(Linux + MySQL)

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 还内置超多企业级能力,满足各类复杂场景:

  1. 多数据库完美兼容:一键支持 SQLite、MySQL、PostgreSQL、Oracle、SQL Server 等主流数据库,代码零改动切换;
  2. 编译期安全:所有映射、查询在编译期校验,杜绝运行期 SQL 错误,比动态 ORM 更稳定;
  3. 高级对象映射:支持一对多、多对一、多对多关联、懒加载、继承映射,轻松处理复杂业务对象;
  4. 高性能:无运行时开销,原生 C++ 实现,内置连接池、查询缓存,性能接近原生数据库 API;
  5. 完善的事务机制:支持显式 / 隐式事务、嵌套事务、回滚,保证数据原子性和一致性;
  6. 类型安全查询:支持面向对象的查询语法(ODB Query),无需手写字符串 SQL,杜绝注入风险;
  7. 零侵入设计:不修改原有业务类结构,仅通过注解实现持久化,不影响程序原有逻辑;
  8. 跨平台:支持 Linux、Windows、macOS、嵌入式 Linux 等所有 C++ 支持的平台。

从简单的本地数据存储,到大型企业级 C++ 应用的分布式数据持久化,ODB 都能完美适配,真正做到简洁易用、性能强悍、工业级可靠

其实就是:

  1. ODB = C++ 的 ORM 工具
  • ORM :对象关系映射,把类 ↔ 数据表对象 ↔ 一行数据自动对应。
  • ODB:专门给 C++ 做的、工业级、高性能 ORM。
  1. 你不用写 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 声明多对多关联 UserRole 多对多
#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.hxxuser-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)")
};
相关推荐
weixin_5206498737 分钟前
数据库函数
数据库
Bert.Cai1 小时前
MySQL LPAD()函数详解
数据库·mysql
OnlyEasyCode2 小时前
Navicat 任务自动备份指定数据库
数据库
if else3 小时前
Redis 哨兵集群部署方案
数据库·redis
yejqvow123 小时前
Pandas 高效实现组内跨行时间戳匹配与布尔标记
jvm·数据库·python
了不起的云计算V3 小时前
从DeepSeek V4适配看国产算力的三个拐点
数据库·人工智能
qq_189807033 小时前
html标签如何提升可访问性_aria-label与title区别【指南】
jvm·数据库·python
norq juox3 小时前
MySQL 导出数据
数据库·mysql·adb
qq_349317484 小时前
mysql如何设置定时自动备份脚本_编写shell脚本与cron任务
jvm·数据库·python
952364 小时前
Spring IoC&DI
java·数据库·spring