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)")
};
相关推荐
chushiyunen2 小时前
django日志使用笔记
数据库·笔记·django
听雪楼主.2 小时前
某客户核心业务系统报ORA-600错误分析处理
数据库·oracle
威联通安全存储2 小时前
严谨性的数字基石:某精密医疗器械企业基于威联通的数据治理实践
运维·数据库·python
俩娃妈教编程2 小时前
C++基础知识点:位运算
java·开发语言·jvm·c++·位运算
不剪发的Tony老师2 小时前
DbPaw:一款AI驱动的现代化数据库开发工具
数据库
2301_767902642 小时前
mysql备份
数据库·mysql·adb
剩下了什么2 小时前
Redis 密码设置
数据库·redis·缓存
NineData2 小时前
NineData 社区版 V4.10.0 正式发布
数据库·mysql·代码规范
heze092 小时前
sqli-labs-Less-46
数据库·mysql·网络安全