跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

C++ ODB ORM 入门与实战指南

综述由AI生成C++ ODB ORM 库通过编译期代码生成实现对象关系映射,无需运行时反射即可高效操作数据库。详细讲解了 ODB 的安装配置、类与表的映射规则、视图定义方式以及事务管理机制。内容涵盖从环境搭建到 MySQL 实战测试的全过程,包括学生与班级关系的 CRUD 操作示例,帮助开发者快速掌握 ODB 在 C++ 项目中的集成与应用方法。

1qazxsw2发布于 2026/1/15更新于 2026/6/1017 浏览
C++ ODB ORM 入门与实战指南

C++ ODB ORM 入门与实战指南

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

ODB 核心特性

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

环境搭建与安装

1. 安装 Build2 构建系统

首先需要在系统中安装 build2 工具链。Linux 环境下可以通过脚本快速安装:

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

2. 安装 ODB 编译器

接下来配置并安装 odb-compiler。注意这里的 gcc 版本需根据实际环境调整。

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 提示找不到命令,可能需要更新环境变量:

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

3. 安装运行时库与依赖

除了编译器,还需要安装运行时库和 Boost 支持。

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 bpkg build libodb-boost bpkg install --all --recursive

基础用法与映射

我们以 MySQL 为例演示如何将 C++ 类映射到数据库表。

核心头文件

在 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 类
#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;
};

注意事项:

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

编译与建表

使用 ODB 编译器生成 SQL 脚本和 C++ 代码:

odb -d mysql --generate-query --generate-schema --profile boost/date-time person.hxx

生成的 person.sql 文件包含了建表语句,可以直接导入 MySQL:

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

视图映射

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;
};

这相当于执行了以下 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:实现数据库的增删改查操作,包括事务管理、查询操作、单对象操作。
  • odb::result:查询结果集,用于遍历查询返回的数据。

实战测试示例

数据库操作流程

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

模型定义 (Student.hpp)

#pragma once
#include <string>
#include <cstddef>
#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, "<your_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 updata_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);
  // updata_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

通过以上步骤,我们完成了从环境搭建、模型定义到 CRUD 操作的全流程实践。ODB 通过编译期代码生成机制,在保证性能的同时提供了接近原生 C++ 的开发体验,是 C++ 后端开发中处理数据库交互的优秀选择。

目录

  1. C++ ODB ORM 入门与实战指南
  2. ODB 核心特性
  3. 环境搭建与安装
  4. 1. 安装 Build2 构建系统
  5. 2. 安装 ODB 编译器
  6. 3. 安装运行时库与依赖
  7. 基础用法与映射
  8. 核心头文件
  9. 类型映射规则
  10. 示例:定义 Person 类
  11. 编译与建表
  12. 视图映射
  13. 核心特点
  14. 定义方式一:基于对象关系的视图
  15. 定义方式二:基于原生 SQL 的视图
  16. ODB 类与接口
  17. 实战测试示例
  18. 数据库操作流程
  19. 模型定义 (Student.hpp)
  20. 测试代码 (test.cc)
  21. 编译链接 (Makefile)
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • AR 健身教练应用开发:基于 Rokid CXR-M SDK 的实战
  • OpenClaw 自动化 AI 助理使用指南与技能配置详解
  • Python 3.12.0 Windows 安装与环境配置指南
  • C++ AC 自动机:原理、实现与应用
  • Linux 系统学习:Git 原理与进阶使用(下)
  • 金仓 KingbaseES 融合架构实践:从多库并存到一库多能
  • 2026 国内 AI 编程套餐(Coding Plan)全量横评:选型指南与避坑手册
  • 即梦数字人视频生成 API 调用示例
  • WebRTC 架构全景解析:从浏览器 API 到 libwebrtc
  • AI 辅助 Java 零基础入门与核心实战教程
  • 基于 Rokid 灵珠平台构建旅游 AR 智能体实践
  • 基于 Rokid 灵珠平台搭建旅游 AR 智能体实战指南
  • Python 开源 AI 模型引入与测试全流程实战
  • 归并排序的核心思想与进阶应用
  • ChatGPT vs. 文心一言 vs. 通义千问:中文创作能力深度评测
  • 基于 Rokid 灵珠平台搭建旅游 AR 智能体实战
  • Pi0 机器人 VLA 大模型昇腾 A2 平台测评
  • Shell 脚本基础:参数校验与退出状态码解析
  • AI 绘画实战指南:从提示词到高质量图像生成
  • Fooocus 部署实战:本地手动配置与云平台方案对比

相关免费在线工具

  • 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