C++ ODB ORM 完全指南:从入门到实战应用

C++ ODB ORM 完全指南:从入门到实战应用

文章目录

ODB基本概念

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

特点:

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

ODB框架安装

build2安装:
安装步骤: https://build2.org/install.xhtml#unix

dev@dev-host:~/workspace$ curl -sSfO https://download.build2.org/0.17.0/build2-install-0.17.0.sh
dev@dev-host:~/workspace$ sh build2-install-0.17.0.sh

安装 odb-compiler:

dev@dev-host:~/workspace$ #注意这里的 gcc-11 需要根据你自己版本而定
dev@dev-host:~/workspace$ sudo apt-get install gcc-11-plugin-dev
dev@dev-host:~/workspace$ mkdir odb-build && cd odb-build
dev@dev-host:~/workspace/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

dev@dev-host:~/workspace/odb-build$ cd odb-gcc-N
dev@dev-host:~/workspace/odb-build/odb-gcc-N$ bpkg build odb@https://pkg.cppget.org/1/beta
dev@dev-host:~/workspace/odb-build/odb-gcc-N$ bpkg test odb test odb-2.5.0-b.25+1/tests/testscript{testscript} tested odb/2.5.0-b.25+1
dev@dev-host:~/workspace/odb-build/odb-gcc-N$ bpkg install odb
dev@dev-host:~/workspace/odb-build/odb-gcc-N$ odb --version bash: /usr/bin/odb: No such file or directory
#如果报错了,找不到 odb,那就在执行下边的命令
dev@dev-host:~/workspace/odb-build/odb-gcc-N$ sudo echo 'export PATH=${PATH}:/usr/local/bin' >> ~/.bashrc
dev@dev-host:~/workspace/odb-build/odb-gcc-N$ export PATH=${PATH}:/usr/local/bin
dev@dev-host:~/workspace/odb-build/odb-gcc-N$ odb --version ODB object-relational mapping (ORM) compiler for C++ 2.5.0-b.25 Copyright (c) 2009-2023 Code Synthesis Tools CC. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

安装 ODB 运行时库:

dev@dev-host:~/workspace/odb-build/odb-gcc-N$ cd ..
dev@dev-host:~/workspace/odb-build$ bpkg create -d libodb-gcc-N cc \
config.cxx=g++ \
config.cc.coptions=-O3 \
config.install.root=/usr/ \
config.install.sudo=sudo

dev@dev-host:~/workspace/odb-build$ cd libodb-gcc-N
dev@dev-host:~/workspace/odb-build/libodb-gcc-N$ bpkg add https://pkg.cppget.org/1/beta
dev@dev-host:~/workspace/odb-build/libodb-gcc-N$ bpkg fetch
dev@dev-host:~/workspace/odb-build/libodb-gcc-N$ bpkg build libodb
dev@dev-host:~/workspace/odb-build/libodb-gcc-N$ bpkg build libodb-mysql

安装 boost profile 库:

dev@dev-host:~/workspace/odb-build/libodb-gcc-N$ bpkg build libodb-boost

总体打包安装:

dev@dev-host:~/workspace/odb-build/libodb-gcc-N$ bpkg install --all --recursive

常见操作

提示:接下以映射到mysql数据库为示例演示
核心头文件

/* 在 C++ 中,要使用 ODB 将类声明为持久化类,需要包含 ODB 的核心头文 件,并使用 #pragma db object 指令 #pragma db object 指示 ODB 编译器将 person 类视为一个持久化类。 */#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文件:

//将上文的头文件包含#pragmadb object table("person") class Person { public: private: friend class odb::access;//ODB需要访问私有成员,所以需要做友元声明#pragmadb id autounsignedint _id;#pragmadb column("user_age")default(18)unsignedshort _age;#pragmadb not_null std::string _name;#pragmadb unique type("varchar(20)") std::string _phone;};

代码编译:

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

如下:

在这里插入图片描述


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

在这里插入图片描述


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

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

登陆mysql数据库查看表属性:

在这里插入图片描述

注意1:

  • 作用域:#pragma db 只影响紧随其后的类、数据成员或访问器
  • 顺序:多个属性可以写在同一行,也可以分开写
  • 访问控制:ODB 需要访问私有成员,通常使用 friend class odb::access

注意2:

  • 数据库表的列名默认为成员变量名,但会去掉成员名的前缀下划线。

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

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

视图的两种定义方式
方式一:基于对象关系的视图
这种视图使用ODB的查询语言来定义表之间的关联:

#pragmadb view object(Student)\object(Classes: Student::classes_id == Classes::id)structStudentClassView{#pragmadb column(Student::name) std::string student_name;#pragmadb column(Student::age)unsignedshort student_age;#pragmadb 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:

#pragmadb view query("SELECT s.name, s.age, c.name "\"FROM student s "\"JOIN classes c ON s.classes_id = c.id "\"WHERE s.age > ?")structAdultStudentView{ std::string student_name;unsignedshort student_age; std::string class_name;};

特点:

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

ODB类与接口

odb::database类:实现数据库的增删改查操作

构造函数

成员函数

事务管理

查询操作

单对象操作

odb::result 类 :查询结果集

测试示例

数据库操作流程:

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

Student.hpp

#pragmaonce#include<string>#include<cstddef>// std::size_t#include<boost/date_time/posix_time/posix_time.hpp>#include<odb/nullable.hxx>#include<odb/core.hxx>classStudent{private:unsignedlong _id; std::string _sn; std::string _name;unsignedshort _age;unsignedlong _classes_id;};classClasses{private:unsignedlong _id; std::string _name;};#pragmadb view object(Student)\object(Classes classes:Student._classes_id == Classes._id)\query((?))structclasses_student{#pragmadb column(Student::_id)unsignedlong _id;#pragmadb column(Student::_sn) std::string _sn;#pragmadb column(Student::_name) std::string _name;#pragmadb column(Student::_age)unsignedshort _age;#pragmadb column(Classes::_name) std::string _classes_name;};#pragmadb view query("select name from Student "+(?))structall_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,"I5sLBKpqjGjPZi","mysql用户密码");DEFINE_string(cset,"utf8","mysql客户端字符集");DEFINE_int32(max_pool,3,"最大的连接池数");voidinsert_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;}}voidinsert_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';}}voidremove_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;returnStudent();} ret =*r.begin(); trans.commit();}catch(const std::exception &e){ std::cerr <<"查询失败"<< e.what()<<'\n';}return ret;}voidselect_student_classes(odb::mysql::database& db){ try { odb::transaction trans(db.begin());typedef odb::query<structclasses_student> query;typedef odb::result<structclasses_student> result; result r(db.query<structclasses_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;}}voidupdata_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';}}intmain(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);return0;}

makefile

test : test.cc student_classes-odb.cxx c++ -g $^ -o $@ -lodb-mysql -lodb -lodb-boost -lgflags 
非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=45sbqgxtzjv

Read more

【脉脉】AI 创作者 xAMA 知无不言:在浪潮里,做会发光的造浪者

【脉脉】AI 创作者 xAMA 知无不言:在浪潮里,做会发光的造浪者

🎬 个人主页:秦苒& ❄专栏传送门:《C语言》 🍀指尖燃热血,代码铸锋芒;以信仰破局,向顶峰生长 🎬秦苒&的简介: 前言:由于篇幅原因,本节内容我们分两节讲。欢迎大家在评论区讨论留言! 文章目录 * AI 创作者 xAMA|在技术浪潮里,做会发光的造浪者 * 前言 * 一、重新定义:AI 创作者的三个核心身份 * 二、破局能力:AI 创作者的 “硬核生存法则” * 三、真实困局:从脉脉话题看 AI 创作者的 “行业痛点” * 四、实操指南:如何成为 AI 创作者,或加入这场对话? * 五、未来已来:AI 创作者的 “生态机会” * 结尾

By Ne0inhk

【AI大模型学习日志6:深度拆解字节跳动豆包系列——国民级全模态AI的普惠化突围之路】

在上一篇AI大模型学习日志中,我们完整拆解了xAI旗下的Grok系列,它凭借X平台实时数据原生接入、反过度对齐的极客风格,在海外巨头垄断的市场中撕开了差异化突围的口子,也让我们看到了大模型赛道“长板极致化”的破局逻辑。而当我们把视线拉回国内大模型赛道,真正把“普惠化”做到极致、彻底改写国内C端AI格局的产品,必然是字节跳动旗下的豆包系列。 在豆包诞生之前,国内大模型赛道始终陷入“对标GPT堆参数、拼跑分、做企业服务”的同质化内卷,普通用户想要用上AI,要么面对高昂的付费门槛,要么要忍受有限的免费额度、复杂的操作流程,AI技术始终停留在极客圈层与企业场景,无法真正走进大众的日常生活。而豆包从诞生之日起,就跳出了这条内卷路径,以“让顶尖AI能力零门槛走进10亿中国人的日常”为核心使命,用两年多时间成长为国内月活破2亿的国民级AI产品,成为国内C端通用大模型的绝对标杆。 本文所有核心信息均以字节跳动官方技术白皮书、产品发布会、官方技术论文与开源文档为唯一基准,严格遵循系列日志的统一框架,从官方定义与核心基本面、完整发展历程、解决的行业核心痛点与落地场景、核心优势与现存不足四大维度,完整拆

By Ne0inhk
腾讯三箭齐发!企业微信、WorkBuddy、Qclaw 共建AI办公新生态

腾讯三箭齐发!企业微信、WorkBuddy、Qclaw 共建AI办公新生态

腾讯三箭齐发!企业微信、WorkBuddy、Qclaw 共建AI办公新生态 📢 重磅消息! 2026年3月,腾讯在AI Agent领域连出重拳!3月8日:企业微信宣布接入OpenClaw3月9日:腾讯正式上线 WorkBuddy(桌面智能体)3月9日:腾讯电脑管家推出 Qclaw(微信AI助手) 三箭齐发!腾讯全面布局AI办公生态! 🔥 事件回顾 Day 1:企业微信宣布接入 OpenClaw 2026年3月8日,企业微信官方宣布支持接入OpenClaw智能机器人! Day 2:腾讯 WorkBuddy 正式上线 2026年3月9日,腾讯旗下全场景AI智能体WorkBuddy正式发布,完全兼容OpenClaw生态! 同期:腾讯电脑管家 Qclaw 亮相 腾讯电脑管家官方推出Qclaw——一款"随时随地,微信一下,帮你搞定一切"的AI助手! 🤖 腾讯AI三剑客对比 产品定位入口特点企业微信版OpenClaw接入企业微信企业级应用WorkBuddy桌面智能体工作台桌面客户端深度办公自动化Qclaw微信AI助手微信/电脑管家轻量级、

By Ne0inhk
AI 也能写爬虫?基于 Bright Data + Warp CLI 的网页抓取实战

AI 也能写爬虫?基于 Bright Data + Warp CLI 的网页抓取实战

🤵‍♂️ 个人主页:@艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏 📂加关注+ 目录 一、引言 1.1 写过爬虫的人,大概率都踩过这些坑 1.2 AI 已经很会写代码了,但它真的能“写爬虫”吗? 1.3 让 AI 不只是“写代码”,而是“驱动抓取” 二、技术与工具介绍 2.1 为什么“普通 AI + 爬虫代码”很难跑通真实网页? 2.2 Bright Data:爬虫工程真正的“底层基础设施” 2.3

By Ne0inhk