跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++

C++ ODB ORM 框架入门与实战

C++ ODB ORM 库实现对象与数据库映射,通过编译期代码生成避免运行时反射。支持 MySQL、PostgreSQL 等主流数据库切换。安装依赖 build2 环境及 odb-compiler。核心使用#pragma db 指令标记持久化类,定义主键、索引及列属性。支持基于对象关系或原生 SQL 的视图映射。示例演示连接池构建、事务管理及增删改查操作,包含学生与班级表关联查询逻辑。

GRACE Grace发布于 2026/3/23更新于 2026/5/59 浏览
C++ ODB ORM 框架入门与实战

ODB 基本概念

ODB 是一个针对 C++ 的对象关系映射(ORM)库,它允许开发者以面向对象的方式操作数据库,将 C++ 对象与数据库表进行映射,从而避免直接编写 SQL 语句,简化数据库操作。

特点:

  • 对象 - 关系映射:将 C++ 类映射到数据库表,类的成员变量映射到表的字段,对象的创建、修改、删除等操作会自动转换为对应的数据库操作(如 INSERT、UPDATE、DELETE)。
  • 代码生成机制:ODB 不依赖运行时反射(C++ 本身不支持),而是通过编译期代码生成实现映射:开发者使用特殊的注解(如 #pragma db object)标记需要持久化的类,然后通过 ODB 编译器生成与数据库交互的代码(如 SQL 语句、CRUD 函数等)。
  • 支持多数据库:兼容主流数据库,如 MySQL、PostgreSQL、SQLite、Oracle 等,切换数据库时无需修改核心业务代码,只需调整配置。
  • 查询能力:提供类似 SQL 的查询接口(通过 C++ 表达式构建),例如通过 query 类组合条件,实现复杂查询逻辑,避免手写 SQL。
  • 事务支持:内置事务管理机制,确保数据库操作的原子性、一致性、隔离性和持久性(ACID)。

ODB 框架安装

build2 安装

安装步骤参考官方文档:https://build2.org/install.xhtml#unix

curl -sSfO https://download.build2.org/0.17.0/build2-install-0.17.0.sh
sh build2-install-0.17.0.sh

安装 odb-compiler

# 注意这里的 gcc-11 需要根据你自己版本而定
sudo apt-get install gcc-11-plugin-dev
mkdir odb-build && cd odb-build
bpkg create -d odb-gcc-N cc \
    config.cxx=g++ \
    config.cc.coptions=-O3 \
    config.bin.rpath=/usr/lib \
    config.install.root=/usr/ \
    config.install.sudo=sudo
cd odb-gcc-N
bpkg build odb@https://pkg.cppget.org/1/beta
bpkg test odb test odb-2.5.0-b.25+1/tests/testscript{testscript} tested odb/2.5.0-b.25+1
bpkg install odb
odb --version

如果报错找不到 odb,可执行以下命令添加环境变量:

sudo echo 'export PATH=${PATH}:/usr/local/bin' >> ~/.bashrc
export PATH=${PATH}:/usr/local/bin
odb --version

安装 ODB 运行时库

cd ..
bpkg create -d libodb-gcc-N cc \
    config.cxx=g++ \
    config.cc.coptions=-O3 \
    config.install.root=/usr/ \
    config.install.sudo=sudo
cd libodb-gcc-N
bpkg add https://pkg.cppget.org/1/beta
bpkg fetch
bpkg build libodb
bpkg build libodb-mysql

安装 boost profile 库

bpkg build libodb-boost

总体打包安装

bpkg install --all --recursive

常见操作

提示:接下以映射到 MySQL 数据库为示例演示。

核心头文件

在 C++ 中,要使用 ODB 将类声明为持久化类,需要包含 ODB 的核心头文件,并使用 #pragma db object 指令。

#include<cstddef> // std::size_t
#include<boost/date_time/posix_time/posix_time.hpp>
#include<odb/nullable.hxx>
#include<odb/core.hxx>

类型映射

通过指令 #pragma 声明 C++ 类与数据库表之间的映射关系,必须放在它们所描述的 C++ 实体(类、数据成员、访问器)之前。格式:

#pragma db 指令 [参数...]

指令及参数:

  • object:表示该类将被映射到数据库。
    • table():默认生成的表名就是类名,使用该参数可以指定表名。
  • id:表示该成员将被作为主键。
  • auto:表示该成员有自增属性。
  • unique:表示唯一键索引字段。
  • index:为该字段创建普通索引。
  • null:该字段允许为空。
  • not_null:该字段不允许为空。
  • default():指定默认值。
  • column():指定该成员映射到数据库表后的列名。
  • type():指定该成员映射到数据库表中的字段类型。
示例 person.hxx 文件
// 将上文的头文件包含
#pragma db object table("person")
class Person {
public:
private:
    friend class odb::access; // ODB 需要访问私有成员,所以需要做友元声明
    #pragma db id auto
    unsigned int _id;
    #pragma db column("user_age") default(18)
    unsigned short _age;
    #pragma db not_null
    std::string _name;
    #pragma db unique type("varchar(20)")
    std::string _phone;
};
代码编译
odb -d mysql --generate-query --generate-schema --profile boost/date-time person.hxx

如下:

(此处省略图片描述,实际部署时请确认生成的 SQL 脚本)

可以发现自动生成了一个 SQL 脚本文件,通过指令 cat person.sql 查看该文件。

通过该文件构建 MySQL 数据库表:

mysql -u root -p 库名 < person.sql

登陆 MySQL 数据库查看表属性。

注意事项:

  1. 作用域:#pragma db 只影响紧随其后的类、数据成员或访问器。
  2. 顺序:多个属性可以写在同一行,也可以分开写。
  3. 访问控制:ODB 需要访问私有成员,通常使用 friend class odb::access。
  4. 列名:数据库表的列名默认为成员变量名,但会去掉成员名的前缀下划线。

视图映射

ODB 视图是一个只读的、虚拟的投影,它基于一个或多个持久化类(数据库表),通过查询组合出你需要的数据结构。视图本身不对应数据库中的实际表,而是在查询时动态生成的结果集。

视图的核心特点:

  • 只读性:视图只能用于查询,不能进行插入、更新、删除操作。
  • 虚拟性:不占用实际数据库存储空间。
  • 灵活性:可以自由组合多个表的字段,形成新的数据结构。
  • 类型安全:在 C++ 编译期就确定了返回类型。
方式一:基于对象关系的视图

这种视图使用 ODB 的查询语言来定义表之间的关联:

#pragma db view object(Student)\
object(Classes: Student::classes_id == Classes::id)
struct StudentClassView {
    #pragma db column(Student::name)
    std::string student_name;
    #pragma db column(Student::age)
    unsigned short student_age;
    #pragma db column(Classes::name)
    std::string class_name;
};

工作方式:

  • object(Student):指定主表。
  • object(Classes: Student::classes_id == Classes::id):指定关联表及连接条件。

相当于 SQL:

SELECT Student.name, Student.age, Classes.name FROM Student JOIN Classes ON Student.classes_id = Classes.id
方式二:基于原生 SQL 的视图

当需要复杂查询时,可以直接使用原生 SQL:

#pragma db view query("SELECT s.name, s.age, c.name "\
"FROM student s "\
"JOIN classes c ON s.classes_id = c.id "\
"WHERE s.age > ?")
struct AdultStudentView {
    std::string student_name;
    unsigned short student_age;
    std::string class_name;
};

特点:

  • 使用 query("原生 SQL") 定义。
  • ? 是参数占位符,可以在查询时传入具体值。
  • 适合复杂的、对象关系无法表达的查询。

ODB 类与接口

  • odb::database 类:实现数据库的增删改查操作。
    • 构造函数:初始化数据库连接。
    • 成员函数:
      • 事务管理:begin(), commit(), rollback()。
      • 查询操作:query<T>()。
      • 单对象操作:persist(), erase(), update()。
  • odb::result 类:查询结果集。

测试示例

数据库操作流程

  1. 构造连接池对象。
  2. 构造数据操作 database 对象。
  3. 获取事务对象,开启事务。
  4. 数据库操作。
  5. 提交事务(若无添加,在销毁前会进行事务回滚)。

Student.hpp

#pragma once
#include<string>
#include<cstddef> // std::size_t
#include<boost/date_time/posix_time/posix_time.hpp>
#include<odb/nullable.hxx>
#include<odb/core.hxx>

class Student {
private:
    unsigned long _id;
    std::string _sn;
    std::string _name;
    unsigned short _age;
    unsigned long _classes_id;
};

class Classes {
private:
    unsigned long _id;
    std::string _name;
};

#pragma db view object(Student)\
object(Classes classes:Student._classes_id == Classes._id)\
query((?))
struct classes_student {
    #pragma db column(Student::_id)
    unsigned long _id;
    #pragma db column(Student::_sn)
    std::string _sn;
    #pragma db column(Student::_name)
    std::string _name;
    #pragma db column(Student::_age)
    unsigned short _age;
    #pragma db column(Classes::_name)
    std::string _classes_name;
};

#pragma db view query("select name from Student "+(?))
struct all_name {
    std::string _name;
};

test.cc

#include<gflags/gflags.h>
#include<odb/database.hxx>
#include<odb/mysql/database.hxx>
#include"student_classes.hxx"
#include"student_classes-odb.hxx"

DEFINE_string(host,"127.0.0.1","主机号");
DEFINE_uint32(port,0,"端口号");
DEFINE_string(db,"qsy_test","mysql 数据库名");
DEFINE_string(user,"root","mysql 用户名");
DEFINE_string(pswd,"<password>","mysql 用户密码");
DEFINE_string(cset,"utf8","mysql 客户端字符集");
DEFINE_int32(max_pool,3,"最大的连接池数");

void insert_student(odb::mysql::database &db) {
    try {
        odb::transaction trans(db.begin());
        Student s1(1,"张三",18,1);
        Student s2(2,"李四",19,1);
        Student s3(3,"王五",17,1);
        Student s4(4,"赵六",21,2);
        Student s5(5,"田七",20,2);
        Student s6(6,"孙八",16,2);
        Student s7(7,"罗九",26,2);
        db.persist(s1);
        db.persist(s2);
        db.persist(s3);
        db.persist(s4);
        db.persist(s5);
        db.persist(s6);
        db.persist(s7);
        trans.commit();
    } catch(std::exception &e) {
        std::cout << "数据插入失败:" << e.what() << std::endl;
    }
}

void insert_classes(odb::mysql::database& db) {
    try {
        odb::transaction trans(db.begin());
        Classes c1("1 班");
        Classes c2("2 班");
        db.persist(c1);
        db.persist(c2);
        trans.commit();
    } catch(const std::exception& e) {
        std::cout << "数据插入失败" << e.what() << '\n';
    }
}

void remove_student(odb::mysql::database &db) {
    try {
        odb::transaction trans(db.begin());
        typedef odb::query<Student> query;
        db.erase_query<Student>(query::id == 4);
        trans.commit();
    } catch(const std::exception &e) {
        std::cerr << "删除失败:" << e.what() << '\n';
    }
}

Student select_student(odb::mysql::database &db) {
    Student ret;
    try {
        odb::transaction trans(db.begin());
        typedef odb::query<Student> query;
        typedef odb::result<Student> result;
        result r(db.query<Student>(query::name == "张三"));
        if(r.size()!=1) {
            std::cout << "查询失败" << std::endl;
            return Student();
        }
        ret = *r.begin();
        trans.commit();
    } catch(const std::exception &e) {
        std::cerr << "查询失败" << e.what() << '\n';
    }
    return ret;
}

void select_student_classes(odb::mysql::database& db) {
    try {
        odb::transaction trans(db.begin());
        typedef odb::query<struct classes_student> query;
        typedef odb::result<struct classes_student> result;
        result r(db.query<struct classes_student>(query::classes::id==1));
        for(auto it = r.begin();it!=r.end();it++){
            std::cout<<it->name<<" ";
            std::cout<<it->age<<" ";
            std::cout<<it->sn<<" ";
            std::cout<<it->classes_name<<" ";
            std::cout<<std::endl;
        }
        trans.commit();
    } catch(const std::exception &e) {
        std::cout << "查询失败:" << e.what() << std::endl;
    }
}

void update_student(odb::mysql::database &db, Student &stu) {
    try {
        odb::transaction trans(db.begin());
        db.update(stu);
        trans.commit();
    } catch(const std::exception &e) {
        std::cerr << "更新失败:" << e.what() << '\n';
    }
}

int main(int argc,char*argv[]) {
    google::ParseCommandLineFlags(&argc,&argv, true);
    // 创建连接池构建配置对象
    std::unique_ptr<odb::mysql::connection_pool_factory>cpf(
        new odb::mysql::connection_pool_factory(FLAGS_max_pool,0));
    // 创建数据操作对象
    odb::mysql::database db(FLAGS_user, FLAGS_pswd, FLAGS_db, FLAGS_host, FLAGS_port,"", FLAGS_cset,0, std::move(cpf));
    // 插入
    // insert_student(db);
    // insert_classes(db);
    // 更新
    // Student stu = select_student(db);
    // stu.age(5);
    // update_student(db,stu);
    // 删除
    // remove_student(db);
    // 查询
    select_student_classes(db);
    return 0;
}

makefile

test : test.cc student_classes-odb.cxx
c++ -g $^ -o $@ -lodb-mysql -lodb -lodb-boost -lgflags

目录

  1. ODB 基本概念
  2. ODB 框架安装
  3. build2 安装
  4. 安装 odb-compiler
  5. 注意这里的 gcc-11 需要根据你自己版本而定
  6. 安装 ODB 运行时库
  7. 安装 boost profile 库
  8. 总体打包安装
  9. 常见操作
  10. 核心头文件
  11. 类型映射
  12. 示例 person.hxx 文件
  13. 代码编译
  14. 视图映射
  15. 方式一:基于对象关系的视图
  16. 方式二:基于原生 SQL 的视图
  17. ODB 类与接口
  18. 测试示例
  19. 数据库操作流程
  20. Student.hpp
  21. test.cc
  22. makefile
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Dify 开源平台搭建 AI 聊天助手及 API 集成教程
  • Pencil.dev:AI 驱动设计工具安装与实战教程
  • llama-cpp-python Windows 部署实战:编译与运行配置
  • Kubernetes Helm 构建无端口类型 Chart 实践
  • 转行数据分析师:难度评估与备考指南
  • Windows 系统部署 RabbitMQ 及 Erlang 环境配置指南
  • Google 发布多模态嵌入模型 Gemini Embedding 2,MuleRun 推出自进化个人 AI
  • OpenFPGA FPGA IP 核生成技术指南
  • CentOS 系统定时执行 Python 邮件任务的五种方案
  • 大模型、通用大模型与企业及垂直大模型的概念辨析
  • OpenClaw 新手指南:从零开始的 AI 机器人搭建完全攻略
  • 用初中数学理解 LLM 工作原理
  • MySQL 慢查询日志配置、分析与优化实践
  • 边缘AI:解锁终端设备的智能潜能
  • 人工智能与机器人学的未来:智能自主系统的融合创新
  • C++ 笔试刷题:模拟、动态规划与回文判断
  • Java 哈希表原理与实现
  • 使用 Conda 创建 Python 3.10 虚拟环境
  • 星辰 RPA 与 Agent:构建小红书自动发文机器人
  • OD 机试真题:机器人活动区域

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online