ODB MySQL-SDK 介绍及使用
- [一. ODB 介绍](#一. ODB 介绍)
- [二. odb SDK 安装](#二. odb SDK 安装)
- [三. odb 常见操作](#三. odb 常见操作)
-
- [1. odb 类型映射](#1. odb 类型映射)
- [2. odb 编程](#2. odb 编程)
- [四. odb 类与接口](#四. odb 类与接口)
-
- [1. database、transaction](#1. database、transaction)
- [2. query、result](#2. query、result)
- [3. nullable、tracer、exception](#3. nullable、tracer、exception)
- [五. odb 使用样例](#五. odb 使用样例)
-
- [1. 目录结构](#1. 目录结构)
- [2. 项目构建](#2. 项目构建)
- [3. 代码实现](#3. 代码实现)
-
- [1. 增删查改](#1. 增删查改)
- [2. 多表联查](#2. 多表联查)
- [3. 分组查询](#3. 分组查询)
- [4. 分页查询](#4. 分页查询)
- [5. 原生 sql 查询](#5. 原生 sql 查询)
- [六. odb 封装](#六. odb 封装)
-
- [1. 设计与实现](#1. 设计与实现)
-
- [1.1 目录结构](#1.1 目录结构)
- [1.2 代码实现](#1.2 代码实现)
- [2. 使用样例](#2. 使用样例)
-
- [1.1 目录结构](#1.1 目录结构)
- [1.2 项目构建](#1.2 项目构建)
- [1.3 代码实现](#1.3 代码实现)
一. ODB 介绍
- ODB (Object--Relational Database Mapping) 是一款面向 C++ 的对象关系映射,也叫 Object--Relational Mapping (ORM) 框架,主要用于在 C++ 应用程序中实现对象与关系型数据库之间的自动映射。它支持将 C++ 类直接映射为数据库表,把对象的成员变量映射为表字段,从而让开发者以操作对象的方式完成数据库的增删改查,减少大量手写 SQL 的工作量。ODB 通过代码生成的方式工作,开发者只需在 C++ 类中使用少量注解 (如 #pragma db),即可由 ODB 编译器生成高效、类型安全的数据库访问代码。
- ODB 支持多种主流数据库,包括 MySQL、PostgreSQL、SQLite 和 Oracle,具备事务管理、延迟加载、对象缓存、关系映射 (一对一、一对多、多对多) 等特性。与许多动态语言 ORM 不同,ODB 强调编译期检查和高性能,生成的代码几乎没有运行时反射开销,特别适合对性能和类型安全要求较高的 C++ 后端服务和系统级应用。
二. odb SDK 安装
Github 地址:https://github.com/codesynthesis-com/odb.git
由于在环境搭建中已经安装了 ODB、MySQL 服务器并且运行,我们不需要再次安装。

但是我们需要安装一个 MySQL 客户端,方便查看数据的变化:
bash
sudo apt update
sudo apt install mysql-client
MySQL 服务器密码就在 docker-compose.yml 文件中:


登录 MySQL 服务器:

三. odb 常见操作
1. odb 类型映射


2. odb 编程
ODB (Open Database) 在数据元结构定义时,使用预处理器指令 (#pragma) 来提供元数据,这些元数据指示如何将 C++ 类型映射到数据库模式。这些 #pragma 指令是在 C++ 代码中使用的,它们不是 C++ 语言的一部分,而是特定于 ODB 编译器的扩展。以下是 ODB 中常用的一些 #pragma 指令:
- #pragma db object:用于声明一个类是数据库对象,即这个类将映射到数据库中的一个表。
- #pragma db table("table_name"):指定类映射到数据库中的表名。如果不指定,则默认使用类名。
- #pragma db id:标记类中的一个成员变量作为数据库表的主键。
- #pragma db auto:指定成员变量的值在插入时自动生成 (例如:自动递增的主键)
- #pragma db column("column_name"):指定类成员映射到数据库表中的列名。如果不指定,则默认使用成员变量的名字。
- #pragma db type("type_name"):指定成员变量的数据库类型。
- #pragma db transient:指定成员变量不应该被持久化到数据库中。
- #pragma db view:用于声明一个类是一个数据库视图,而不是一个表。
- #pragma db session:用于声明一个全局或成员变量是数据库会话。
- #pragma db query("query"):用于定义自定义的查询函数。
- #pragma db index("index_name"):指定成员变量应该被索引。
- #pragma db default("default_value"):指定成员变量的默认值。
- #pragma db unique:指定成员变量或一组变量应该具有唯一性约束。
- #pragma db not_null / null:指定成员变量是否允许为空,不建议使用 (因为如果数据库中该字段没有数据,成员的默认值就会造成歧义,建议针对字段使用 odb::nullable 类型进行声明,与 std::optional<T> 功能相似)
- #pragma db convert("converter"):指定用于成员变量的自定义类型转换器。
- #pragma db pool("pool_name"):指定用于数据库连接的连接池。
- #pragma db trigger("trigger_name"):指定在插入、更新或删除操作时触发的触发器。
四. odb 类与接口
1. database、transaction
- odb::database:表示一个具体的数据库实例,是 ODB 与真实数据库之间的桥梁。它负责建立和管理数据库连接,并提供对象持久化相关的操作接口,例如插入 (persist)、查询 (load / query)、更新 (update) 和删除 (erase),在 ODB 中,所有对数据库的访问都必须通过 database 对象完成。
- odb::transaction:用于表示一次数据库事务,控制一组数据库操作的原子性。事务开始后,所有操作要么在 commit() 时一次性生效,要么在事务回滚时全部撤销。transaction 采用 RAII 设计,如果事务对象在作用域结束前没有显式提交,就会自动回滚,从而保证异常安全和数据一致性。
两者关系:database 负责 "连到哪一个数据库并执行操作",transaction 负责 "这些操作是否最终生效"。在 ODB 中,所有数据库操作都必须在事务中进行。
注意:不要直接通过最初创建的 database 进行数据操作,而是通过 transaction 创建的 database 对象进行操作。原因:数据库句柄是共享的,transaction 创建的 database 对象是独占的,多线程最怕共享 (线程安全问题),而 transaction 刻意帮你隔离。
cpp
namespace odb {
namespace mysql {
// 数据库连接池对象类,继承自 connection_factory
class LIBODB_MYSQL_EXPORT connection_pool_factory : public connection_factory {
connection_pool_factory(std::size_t max_connections = 0, // 最大连接数
std::size_t min_connections = 0, // 最小连接数
bool ping = true) // 保持连接鲜活,避免连接过期
};
// 数据库操作句柄类,继承自 odb::database
class LIBODB_MYSQL_EXPORT database: public odb::database {
database(const std::string& user, // 用户名
const std::string& passwd, // 密码
const std::string& db, // 数据库名
const std::string& host = "", // 主机名
unsigned int port = 0, // 端口号
const std::string* socket = 0, // 套接字
const std::string& charset = "", // 字符集
unsigned long client_flags = 0, // 客户端标志
details::transfer_ptr<connection_factory> =
details::transfer_ptr<connection_factory>() // 连接工厂
);
persist(T& object); // 新增单条数据
void update(T& object); // 更新单条数据
void erase(T& object); // 删除单条数据
unsigned long long erase_query(const std::string&); // 根据SQL语句删除数据
unsigned long long erase_query(const odb::query<T>&); // 根据查询对象删除数据
result<T> query(const std::string&); // 根据SQL语句查询数据
result<T> query(const odb::query<T>&, bool cache = true); // 根据查询对象查询数据
typename result<T>::pointer_type query_one(const odb::query<T>&); // 查询单条数据
virtual transaction_impl* begin () = 0; // 创建/获取一个事务对象
}
// 事务操作类,继承自 odb::transaction
class LIBODB_MYSQL_EXPORT transaction: public odb::transaction;
}
// 数据库操作句柄类
class LIBODB_EXPORT database;
// 事务操作类
class LIBODB_EXPORT transaction {
transaction(transaction_impl*, bool make_current = true); // 构造函数,创建/获取一个事务对象
odb::database& database(); // 获取数据库操作句柄
void commit(); // 提交事务
void rollback(); // 回滚事务
void tracer(tracer_type& t) // 设置事务跟踪器
};
}
2. query、result
- odb::query:表示一个查询条件的抽象描述,用于构造数据库查询语句。它并不直接访问数据库,而是用来表达 "要查什么数据",例如字段的比较、逻辑组合 (与、或、非)、范围条件等。query 通常作为参数传递给 database::query(),由 ODB 根据对象映射关系将其转换为对应的 SQL WHERE 条件。
- odb::result:表示一次查询返回的结果集合,是对数据库查询结果的封装。它本身不保存所有数据,而是以惰性 (延迟) 方式从数据库中逐条读取对象,支持迭代访问。result 的生命周期通常与事务绑定,事务结束后结果集即失效,用于安全、高效地遍历查询得到的对象。
两者关系:query 用来描述 "筛选规则",result 用来承载 "筛选后的对象集合"。前者负责条件表达,后者负责结果访问。
cpp
namespace odb {
// 针对查询结果所封装的容器类
template <typename T>
class result: result_base<T, class_traits<T>::kind> {
typedef typename iterator::pointer_type pointer_type; // 指向结果容器中元素的指针类型
result();
result(const result& r);
iterator begin(); // 返回指向结果容器第一个元素的迭代器
iterator end(); // 返回指向结果容器最后一个元素的下一个位置的迭代器
size_type size(); // 返回结果容器中元素的数量
bool empty(); // 判断结果容器是否为空
};
// 查询条件的基础封装类
class LIBODB_EXPORT query_base {
explicit query_base (const std::string& native); // 接受原生 SQL 查询字符串
const clause_type& clause () const; // 返回查询条件的子句对象
};
// 强类型查询类(绑定具体对象类型 T)
template <typename T>
class query<T, mysql::query_base>: public mysql::query<T> {
query (bool v); // 接受布尔值 v
query (const std::string& q); // 接受原生 SQL 查询字符串
query (const mysql::query_base& q); // 接受 query_base 类型的查询对象
}
namespace mysql {
// 模板特化适配类
template <typename T>
class query: public query_base, public query_selector<T, id_mysql>::columns_type {
query(const std::string& q); // 接受原生 SQL 查询字符串
query(const query_base& q); // 接受 query_base 类型的查询对象
query(bool v); // 接受布尔值 v
}
}
}
3. nullable、tracer、exception
- odb::nullable<T>:表示一个可为空 (NULL) 的值类型,用于映射数据库中允许为 NULL 的列。它在 C++ 中显式区分 "有值" 和 "无值" 两种状态,避免使用特殊值 (如 0、-1) 来表达空语义。nullable 常用于基本类型或时间类型,与数据库列的 NULL / NOT NULL 语义一一对应。
- odb::tracer:用于跟踪和记录数据库操作的接口类,主要用于调试、日志和性能分析。通过实现或启用 tracer,可以在 SQL 语句执行前后获取相关信息,例如执行的 SQL、参数以及执行时机。它不会影响 ORM 逻辑本身,只用于观察和诊断数据库行为。
- odb::exception:是 ODB 中所有异常类型的基类,用于表示数据库访问和 ORM 操作过程中发生的错误。包括连接失败、SQL 执行错误、事务问题、数据约束违反等。通过捕获 odb::exception,可以统一处理 ODB 抛出的各种数据库相关异常。
cpp
namespace odb {
// 针对可能为空的字段封装的类似于智能指针的类型
template <typename T>
class nullable {
typedef T value_type;
T& get();
const T& get() const;
T* operator->();
const T* operator->() const;
T& operator*();
const T& operator*() const;
};
// 异常类,用于数据库操作中的错误处理
struct LIBODB_EXPORT exception: std::exception, details::shared_base {
virtual const char* what() const ODB_NOTHROW_NOEXCEPT = 0;
virtual exception* clone() const = 0;
};
// 标准错误输出 tracer
extern LIBODB_EXPORT tracer& stderr_tracer;
// 标准错误输出 tracer,包含完整 SQL 语句
extern LIBODB_EXPORT tracer& stderr_full_tracer;
namespace mysql {
// 针对 MySQL 数据库的 tracer 实现
class LIBODB_MYSQL_EXPORT tracer: private odb::tracer {
public:
virtual ~tracer();
// 准备 SQL 语句
virtual void prepare(connection&, const statement&);
// 执行 SQL 语句
virtual void execute(connection&, const statement&);
// 执行原生 SQL 语句
virtual void execute(connection&, const char* statement) = 0;
// 释放 SQL 语句
virtual void deallocate(connection&, const statement&);
};
// 针对 MySQL 数据库的 statement 实现
class LIBODB_MYSQL_EXPORT statement: public odb::statement {
public:
// 获取 SQL 语句文本
virtual const char* text() const;
};
}
}
五. odb 使用样例
1. 目录结构
bash
example/
|-- odb
| |-- crud
| | |-- crud.cc
| | |-- makefile
| | |-- student.h
| |-- group
| | |-- group.cc
| | |-- makefile
| | |-- student.h
| |-- limit
| | |-- limit.cc
| | |-- makefile
| | |-- student.h
| |-- native
| | |-- makefile
| | |-- native.cc
| | |-- student.h
| |-- view
| |-- makefile
| |-- student.h
| |-- view.cc
2. 项目构建
curd 目录下的 makefile:
bash
crud:crud.cc student-odb.cxx
g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
.PHONY: clean
clean:
rm -f crud *-odb.* *.sql
view 目录下的 makefile:
bash
view:view.cc student-odb.cxx
g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
.PHONY: clean
clean:
rm -f view *-odb.* *.sql
group 目录下的 makefile:
bash
group:group.cc student-odb.cxx
g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
.PHONY: clean
clean:
rm -f group *-odb.* *.sql
limit 目录下的 makefile:
bash
limit:limit.cc student-odb.cxx
g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
.PHONY: clean
clean:
rm -f limit *-odb.* *.sql
native 目录下的 makefile:
bash
native:native.cc student-odb.cxx
g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
.PHONY: clean
clean:
rm -f native *-odb.* *.sql
3. 代码实现
1. 增删查改
cpp
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>
// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student")
class Student {
public:
Student(){};
Student(const std::string& name): _name(name) {}
// 获取/设置 _sn
size_t sn() const { return _sn; }
void set_sn(size_t sn) { _sn = sn; }
// 获取/设置 _name
const std::string& name() const { return _name; }
void set_name(const std::string& name) { _name = name; }
// 获取/设置 _age
const odb::nullable<int>& age() const { return _age; }
void set_age(odb::nullable<int> age) { _age = age; }
// 获取/设置 _score
const odb::nullable<double>& score() const { return _score; }
void set_score(odb::nullable<double> score) { _score = score; }
// 获取/设置 _birthday
const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
// 友元类 odb::access 可以访问 private 成员
friend class odb::access;
// 设置 _sn 为自增主键
#pragma db id auto
size_t _sn;
std::string _name;
// 设置 _age、_score、_birthday 为可空整数
odb::nullable<int> _age;
odb::nullable<double> _score;
odb::nullable<boost::posix_time::ptime> _birthday;
};
编译生成可执行程序和 student.sql 文件,如下:

sql
-- student.sql
-- 需要手动添加的sql语句 --
DROP DATABASE IF EXISTS `mytest`;
CREATE DATABASE IF NOT EXISTS `mytest`;
USE `mytest`;
-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_student`;
CREATE TABLE `tbl_student` (
`sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` TEXT NOT NULL,
`age` INT NULL,
`score` DOUBLE NULL,
`birthday` DATETIME NULL)
ENGINE=InnoDB;
将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp
// crud.cc
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "student.h"
#include "student-odb.hxx"
// 定义数据库连接参数:主机名、用户名、密码、数据库名、端口号、字符集
const std::string HOST = "192.168.174.128";
const std::string USER = "root";
const std::string PASSWORD = "123456";
const std::string DBNAME = "mytest";
const unsigned int PORT = 3306;
const std::string CSET = "utf8";
void insert(std::unique_ptr<odb::mysql::database>& handler) {
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.数据操作:添加一条学生记录
Student stu("zhangsan");
stu.set_age(28);
stu.set_birthday(boost::posix_time::time_from_string("2003-03-07 12:00:00"));
db.persist(stu);
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
}
void select(std::unique_ptr<odb::mysql::database>& handler) {
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.数据操作:查询sn为1的学生记录
typedef odb::query<Student> Query;
typedef odb::result<Student> Result;
Result::pointer_type stu = db.query_one<Student>(Query::sn == 1);
if (!stu) {
std::cout << "sn为1的学生,查询结果为空" << std::endl;
return;
}
std::cout << "sn: " << stu->sn() << std::endl;
std::cout << "name: " << stu->name() << std::endl;
if (stu->age()) std::cout << "age: " << stu->age().get() << std::endl;
if (stu->score()) std::cout << "score: " << stu->score().get() << std::endl;
if (stu->birthday()) std::cout << "birthday: " << boost::posix_time::to_simple_string(stu->birthday().get()) << std::endl;
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
}
void select_all(std::unique_ptr<odb::mysql::database>& handler) {
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.数据操作:查询数据库中的所有学生记录
typedef odb::query<Student> Query;
typedef odb::result<Student> Result;
Result stu(db.query<Student>());
for (auto it = stu.begin(); it != stu.end(); ++it) {
std::cout << "sn: " << it->sn() << std::endl;
std::cout << "name: " << it->name() << std::endl;
if (it->age()) std::cout << "age: " << it->age().get() << std::endl;
if (it->score()) std::cout << "score: " << it->score().get() << std::endl;
if (it->birthday()) std::cout << "birthday: " << boost::posix_time::to_simple_string(it->birthday().get()) << std::endl;
}
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
}
void update(std::unique_ptr<odb::mysql::database>& handler) {
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.数据操作:更新sn为1的学生记录的score字段为90
typedef odb::query<Student> Query;
typedef odb::result<Student> Result;
Result::pointer_type stu = db.query_one<Student>(Query::sn == 1);
if (!stu) {
std::cout << "sn为1的学生,查询结果为空" << std::endl;
return;
}
stu->set_score(90);
db.update(stu);
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
}
void remove(std::unique_ptr<odb::mysql::database>& handler) {
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.数据操作:删除sn为1的学生记录
typedef odb::query<Student> Query;
typedef odb::result<Student> Result;
db.erase_query<Student>(Query::sn == 1);
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
}
int main()
{
// 1. 构造数据库连接池
std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(5));
// 2.构造数据库操作句柄
std::unique_ptr<odb::mysql::database> handler = std::make_unique<odb::mysql::database>(USER, PASSWORD, DBNAME, HOST, PORT, nullptr, CSET, 0, std::move(pool));
// 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
insert(handler);
select(handler);
select_all(handler);
update(handler);
remove(handler);
// 注意:不要直接通过最初创建的数据库句柄进行数据操作,而是通过事物创建的database对象进行操作
// 原因:数据库句柄是共享的,事务创建的database对象是独占的,多线程最怕共享(线程安全问题),而事务刻意帮你隔离
return 0;
}

数据库中刚插入的一条学生数据就立即被删除了,没有数据。

2. 多表联查
cpp
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>
// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student")
class Student {
public:
Student(){};
Student(const std::string& name): _name(name) {}
// 获取/设置 _sn
size_t sn() const { return _sn; }
void set_sn(size_t sn) { _sn = sn; }
// 获取/设置 _name
const std::string& name() const { return _name; }
void set_name(const std::string& name) { _name = name; }
// 获取/设置 _age
const odb::nullable<int>& age() const { return _age; }
void set_age(odb::nullable<int> age) { _age = age; }
// 获取/设置 _score
const odb::nullable<double>& score() const { return _score; }
void set_score(odb::nullable<double> score) { _score = score; }
// 获取/设置 _birthday
const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
// 友元类 odb::access 可以访问 private 成员
friend class odb::access;
// 设置 _sn 为自增主键
#pragma db id auto
size_t _sn;
// 设置 _classes_id 为索引
#pragma db index
size_t _classes_id;
std::string _name;
odb::nullable<int> _age;
odb::nullable<double> _score;
odb::nullable<boost::posix_time::ptime> _birthday;
};
// 设置数据库表名为 tbl_classes
#pragma db object table("tbl_classes")
class Classes {
public:
Classes(){};
Classes(const std::string& name): _name(name) {}
size_t id() const { return _id; }
void set_id(size_t id) { _id = id; }
const std::string& name() const { return _name; }
void set_name(const std::string& name) { _name = name; }
private:
// 友元类 odb::access 可以访问 private 成员
friend class odb::access;
// 设置 _id 为自增主键
#pragma db id auto
size_t _id;
// 设置 _name 类型为 VARCHAR(32)的唯一索引
#pragma db unique type("VARCHAR(32)")
std::string _name;
};
// 查询条件:Query::Classes::name == "class one"
#pragma db view object(Classes) \
object(Student: Student::_classes_id == Classes::_id) \
query((?))
struct ClassesStudent {
std::shared_ptr<Student> student;
std::shared_ptr<Classes> classes;
};
// 查询条件:Query::Student::name == "zhangsan"
#pragma db view object(Student) \
object(Classes: Student::_classes_id == Classes::_id) \
query((?))
struct DetailStudent {
std::shared_ptr<Student> student;
std::shared_ptr<Classes> classes;
};
编译生成可执行程序和 student.sql 文件,如下:

sql
-- 需要手动添加的sql语句 --
DROP DATABASE IF EXISTS `mytest`;
CREATE DATABASE IF NOT EXISTS `mytest`;
USE `mytest`;
-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_classes`;
DROP TABLE IF EXISTS `tbl_student`;
CREATE TABLE `tbl_student` (
`sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`classes_id` BIGINT UNSIGNED NOT NULL,
`name` TEXT NOT NULL,
`age` INT NULL,
`score` DOUBLE NULL,
`birthday` DATETIME NULL)
ENGINE=InnoDB;
CREATE INDEX `classes_id_i`
ON `tbl_student` (`classes_id`);
CREATE TABLE `tbl_classes` (
`id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL)
ENGINE=InnoDB;
CREATE UNIQUE INDEX `name_i`
ON `tbl_classes` (`name`);
-- 需要手动添加的sql语句 --
INSERT INTO `tbl_classes` VALUES (1, 'class one'),
(2, 'class two'),
(3, 'class three'),
(4, 'class four');
INSERT INTO `tbl_student` VALUES (1, 1, "zhangsan", 18, 80, "2000-01-01 00:00:00"),
(2, 2, "lisi", 19, 85, "2000-02-02 00:00:00"),
(3, 3, "wangwu", 20, 90, "2000-03-03 00:00:00"),
(4, 4, "zhaoliu", 21, 95, "2000-04-04 00:00:00"),
(5, 1, "tianqi", 22, 100, "2000-05-05 00:00:00");
将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp
// view.cc
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "student.h"
#include "student-odb.hxx"
// 定义数据库连接参数:主机名、用户名、密码、数据库名、端口号、字符集
const std::string HOST = "192.168.174.128";
const std::string USER = "root";
const std::string PASSWORD = "123456";
const std::string DBNAME = "mytest";
const unsigned int PORT = 3306;
const std::string CSET = "utf8";
void get_classes_students(std::unique_ptr<odb::mysql::database>& handler, const std::string& class_name) {
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.查询班级为"class_name"的学生信息
typedef odb::query<ClassesStudent> Query;
typedef odb::result<ClassesStudent> Result;
Result cla(db.query<ClassesStudent>(Query::Classes::name == class_name));
for (auto it = cla.begin(); it != cla.end(); ++it) {
if (it->student) std::cout << "姓名: " << it->student->name() << "\t";
if (it->classes) std::cout << "班级: " << it->classes->name() << std::endl;
}
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
}
void get_detail_students(std::unique_ptr<odb::mysql::database>& handler, const std::string& student_name) {
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.查询学生姓名为"student_name"的详细信息
typedef odb::query<DetailStudent> Query;
typedef odb::result<DetailStudent> Result;
Result cla(db.query<DetailStudent>(Query::Student::name == student_name));
for (auto it = cla.begin(); it != cla.end(); ++it) {
if (it->student) std::cout << "姓名: " << it->student->name() << "\t";
if (it->classes) std::cout << "班级: " << it->classes->name() << std::endl;
}
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
}
int main()
{
// 1. 构造数据库连接池
std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(5));
// 2.构造数据库操作句柄
std::unique_ptr<odb::mysql::database> handler = std::make_unique<odb::mysql::database>(USER, PASSWORD, DBNAME, HOST, PORT, nullptr, CSET, 0, std::move(pool));
// 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
get_classes_students(handler, "class one");
get_detail_students(handler, "zhangsan");
return 0;
}

3. 分组查询
cpp
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>
// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student")
class Student {
public:
Student(){};
Student(const std::string& name): _name(name) {}
// 获取/设置 _sn
size_t sn() const { return _sn; }
void set_sn(size_t sn) { _sn = sn; }
// 获取/设置 _name
const std::string& name() const { return _name; }
void set_name(const std::string& name) { _name = name; }
// 获取/设置 _age
const odb::nullable<int>& age() const { return _age; }
void set_age(odb::nullable<int> age) { _age = age; }
// 获取/设置 _score
const odb::nullable<double>& score() const { return _score; }
void set_score(odb::nullable<double> score) { _score = score; }
// 获取/设置 _birthday
const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
// 友元类 odb::access 可以访问 private 成员
friend class odb::access;
// 设置 _sn 为自增主键
#pragma db id auto
size_t _sn;
// 设置 _classes_id 为索引
#pragma db index
size_t _classes_id;
std::string _name;
odb::nullable<int> _age;
odb::nullable<double> _score;
odb::nullable<boost::posix_time::ptime> _birthday;
};
// 设置数据库表名为 tbl_classes
#pragma db object table("tbl_classes")
class Classes {
public:
Classes(){};
Classes(const std::string& name): _name(name) {}
size_t id() const { return _id; }
void set_id(size_t id) { _id = id; }
const std::string& name() const { return _name; }
void set_name(const std::string& name) { _name = name; }
private:
// 友元类 odb::access 可以访问 private 成员
friend class odb::access;
// 设置 _id 为自增主键
#pragma db id auto
size_t _id;
// 设置 _name 类型为 VARCHAR(32)的唯一索引
#pragma db unique type("VARCHAR(32)")
std::string _name;
};
// 以班级id进行分组,统计每个班级的学生总数以及班级平均分
#pragma db view object(Student) \
query((?) + "group by " + Student::_classes_id)
struct GroupCount {
#pragma db column(Student::_classes_id)
size_t classes_id;
#pragma db column("count(" + Student::_sn + ")")
size_t student_count;
#pragma db column("avg(" + Student::_score + ")")
double avg_score;
};
编译生成可执行程序和 student.sql 文件,如下:

sql
-- 需要手动添加的sql语句 --
DROP DATABASE IF EXISTS `mytest`;
CREATE DATABASE IF NOT EXISTS `mytest`;
USE `mytest`;
-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_classes`;
DROP TABLE IF EXISTS `tbl_student`;
CREATE TABLE `tbl_student` (
`sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`classes_id` BIGINT UNSIGNED NOT NULL,
`name` TEXT NOT NULL,
`age` INT NULL,
`score` DOUBLE NULL,
`birthday` DATETIME NULL)
ENGINE=InnoDB;
CREATE INDEX `classes_id_i`
ON `tbl_student` (`classes_id`);
CREATE TABLE `tbl_classes` (
`id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL)
ENGINE=InnoDB;
CREATE UNIQUE INDEX `name_i`
ON `tbl_classes` (`name`);
-- 需要手动添加的sql语句 --
INSERT INTO `tbl_classes` VALUES (1, 'class one'),
(2, 'class two'),
(3, 'class three'),
(4, 'class four');
INSERT INTO `tbl_student` VALUES (1, 1, "zhangsan", 18, 80, "2000-01-01 00:00:00"),
(2, 2, "lisi", 19, 85, "2000-02-02 00:00:00"),
(3, 3, "wangwu", 20, 90, "2000-03-03 00:00:00"),
(4, 4, "zhaoliu", 21, 95, "2000-04-04 00:00:00"),
(5, 1, "tianqi", 22, 100, "2000-05-05 00:00:00");
将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp
// group.cc
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "student.h"
#include "student-odb.hxx"
// 定义数据库连接参数:主机名、用户名、密码、数据库名、端口号、字符集
const std::string HOST = "192.168.174.128";
const std::string USER = "root";
const std::string PASSWORD = "123456";
const std::string DBNAME = "mytest";
const unsigned int PORT = 3306;
const std::string CSET = "utf8";
void group_count(std::unique_ptr<odb::mysql::database>& handler) {
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.分组统计每个班级的学生总数以及班级平均分
typedef odb::query<GroupCount> Query;
typedef odb::result<GroupCount> Result;
Result grp(db.query<GroupCount>());
for (auto it = grp.begin(); it != grp.end(); ++it) {
std::cout << "班级id: " << it->classes_id << "\t";
std::cout << "学生总数: " << it->student_count << "\t";
std::cout << "班级平均分: " << it->avg_score << std::endl;
}
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
}
int main()
{
// 1. 构造数据库连接池
std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(5));
// 2.构造数据库操作句柄
std::unique_ptr<odb::mysql::database> handler = std::make_unique<odb::mysql::database>(USER, PASSWORD, DBNAME, HOST, PORT, nullptr, CSET, 0, std::move(pool));
// 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
group_count(handler);
return 0;
}

4. 分页查询
cpp
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>
// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student")
class Student {
public:
Student(){};
Student(const std::string& name): _name(name) {}
// 获取/设置 _sn
size_t sn() const { return _sn; }
void set_sn(size_t sn) { _sn = sn; }
// 获取/设置 _name
const std::string& name() const { return _name; }
void set_name(const std::string& name) { _name = name; }
// 获取/设置 _age
const odb::nullable<int>& age() const { return _age; }
void set_age(odb::nullable<int> age) { _age = age; }
// 获取/设置 _score
const odb::nullable<double>& score() const { return _score; }
void set_score(odb::nullable<double> score) { _score = score; }
// 获取/设置 _birthday
const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
// 友元类 odb::access 可以访问 private 成员
friend class odb::access;
// 设置 _sn 为自增主键
#pragma db id auto
size_t _sn;
// 设置 _classes_id 为索引
#pragma db index
size_t _classes_id;
std::string _name;
odb::nullable<int> _age;
odb::nullable<double> _score;
odb::nullable<boost::posix_time::ptime> _birthday;
};
// 设置数据库表名为 tbl_classes
#pragma db object table("tbl_classes")
class Classes {
public:
Classes(){};
Classes(const std::string& name): _name(name) {}
size_t id() const { return _id; }
void set_id(size_t id) { _id = id; }
const std::string& name() const { return _name; }
void set_name(const std::string& name) { _name = name; }
private:
// 友元类 odb::access 可以访问 private 成员
friend class odb::access;
// 设置 _id 为自增主键
#pragma db id auto
size_t _id;
// 设置 _name 类型为 VARCHAR(32)的唯一索引
#pragma db unique type("VARCHAR(32)")
std::string _name;
};
// 分页查询学生信息
#pragma db view object(Student) \
query((?))
struct LimitStudent {
#pragma db column(Student::_classes_id)
size_t classes_id;
#pragma db column(Student::_name)
std::string student_name;
#pragma db column(Student::_score + "as order_field")
double student_score;
};
编译生成可执行程序和 student.sql 文件,如下:

sql
-- 需要手动添加的sql语句 --
DROP DATABASE IF EXISTS `mytest`;
CREATE DATABASE IF NOT EXISTS `mytest`;
USE `mytest`;
-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_classes`;
DROP TABLE IF EXISTS `tbl_student`;
CREATE TABLE `tbl_student` (
`sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`classes_id` BIGINT UNSIGNED NOT NULL,
`name` TEXT NOT NULL,
`age` INT NULL,
`score` DOUBLE NULL,
`birthday` DATETIME NULL)
ENGINE=InnoDB;
CREATE INDEX `classes_id_i`
ON `tbl_student` (`classes_id`);
CREATE TABLE `tbl_classes` (
`id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL)
ENGINE=InnoDB;
CREATE UNIQUE INDEX `name_i`
ON `tbl_classes` (`name`);
-- 需要手动添加的sql语句 --
INSERT INTO `tbl_classes` VALUES (1, 'class one'),
(2, 'class two'),
(3, 'class three'),
(4, 'class four');
INSERT INTO `tbl_student` VALUES (1, 1, "zhangsan", 18, 80, "2000-01-01 00:00:00"),
(2, 2, "lisi", 19, 85, "2000-02-02 00:00:00"),
(3, 3, "wangwu", 20, 90, "2000-03-03 00:00:00"),
(4, 4, "zhaoliu", 21, 95, "2000-04-04 00:00:00"),
(5, 1, "tianqi", 22, 100, "2000-05-05 00:00:00");
将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp
// limit.cc
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "student.h"
#include "student-odb.hxx"
// 定义数据库连接参数:主机名、用户名、密码、数据库名、端口号、字符集
const std::string HOST = "192.168.174.128";
const std::string USER = "root";
const std::string PASSWORD = "123456";
const std::string DBNAME = "mytest";
const unsigned int PORT = 3306;
const std::string CSET = "utf8";
void limit_offset(std::unique_ptr<odb::mysql::database>& handler) {
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.分页查询:查询班级id和学生姓名,按班级id降序排序,每页显示3条记录,偏移量为0
typedef odb::query<LimitStudent> Query;
typedef odb::result<LimitStudent> Result;
Result lim(db.query<LimitStudent>("order by order_field desc limit 3 offset 0"));
for (auto it = lim.begin(); it != lim.end(); ++it) {
std::cout << "班级id: " << it->classes_id << "\t";
std::cout << "学生姓名: " << it->student_name << "\t";
std::cout << "学生成绩: " << it->student_score << std::endl;
}
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
}
int main()
{
// 1. 构造数据库连接池
std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(5));
// 2.构造数据库操作句柄
std::unique_ptr<odb::mysql::database> handler = std::make_unique<odb::mysql::database>(USER, PASSWORD, DBNAME, HOST, PORT, nullptr, CSET, 0, std::move(pool));
// 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
limit_offset(handler);
return 0;
}

5. 原生 sql 查询
cpp
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>
// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student")
class Student {
public:
Student(){};
Student(const std::string& name): _name(name) {}
// 获取/设置 _sn
size_t sn() const { return _sn; }
void set_sn(size_t sn) { _sn = sn; }
// 获取/设置 _name
const std::string& name() const { return _name; }
void set_name(const std::string& name) { _name = name; }
// 获取/设置 _age
const odb::nullable<int>& age() const { return _age; }
void set_age(odb::nullable<int> age) { _age = age; }
// 获取/设置 _score
const odb::nullable<double>& score() const { return _score; }
void set_score(odb::nullable<double> score) { _score = score; }
// 获取/设置 _birthday
const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
// 友元类 odb::access 可以访问 private 成员
friend class odb::access;
// 设置 _sn 为自增主键
#pragma db id auto
size_t _sn;
// 设置 _classes_id 为索引
#pragma db index
size_t _classes_id;
std::string _name;
odb::nullable<int> _age;
odb::nullable<double> _score;
odb::nullable<boost::posix_time::ptime> _birthday;
};
// 原生sql语句查询
#pragma db view query("select name, classes_id, score from tbl_student")
struct NativeStudent {
std::string name;
size_t classes_id;
odb::nullable<double> score;
};
编译生成可执行程序和 student.sql 文件,如下:

sql
-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_student`;
CREATE TABLE `tbl_student` (
`sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`classes_id` BIGINT UNSIGNED NOT NULL,
`name` TEXT NOT NULL,
`age` INT NULL,
`score` DOUBLE NULL,
`birthday` DATETIME NULL)
ENGINE=InnoDB;
CREATE INDEX `classes_id_i`
ON `tbl_student` (`classes_id`);
-- 需要手动添加的sql语句 --
INSERT INTO `tbl_classes` VALUES (1, 'class one'),
(2, 'class two'),
(3, 'class three'),
(4, 'class four');
将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp
// native.cc
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "student.h"
#include "student-odb.hxx"
// 定义数据库连接参数:主机名、用户名、密码、数据库名、端口号、字符集
const std::string HOST = "192.168.174.128";
const std::string USER = "root";
const std::string PASSWORD = "123456";
const std::string DBNAME = "mytest";
const unsigned int PORT = 3306;
const std::string CSET = "utf8";
void native_select(std::unique_ptr<odb::mysql::database>& handler) {
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.原生sql语句查询
typedef odb::query<NativeStudent> Query;
typedef odb::result<NativeStudent> Result;
Result native(db.query<NativeStudent>());
for (auto it = native.begin(); it != native.end(); ++it) {
std::cout << "班级id: " << it->classes_id << "\t";
std::cout << "学生姓名: " << it->name << "\t";
if (it->score) std::cout << "学生成绩: " << it->score.get() << std::endl;
}
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
}
int main()
{
// 1. 构造数据库连接池
std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(5));
// 2.构造数据库操作句柄
std::unique_ptr<odb::mysql::database> handler = std::make_unique<odb::mysql::database>(USER, PASSWORD, DBNAME, HOST, PORT, nullptr, CSET, 0, std::move(pool));
// 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
native_select(handler);
return 0;
}

六. odb 封装
1. 设计与实现
ODB 的所有对数据库的操作都是封装好的,因此本身从功能上来说我们无需做更多的操作,因此这里封装主要封装一个 mysql 的配置结构以及一个 database 工厂类即可。
1.1 目录结构
bash
source/
|-- odb.cc
|-- odb.h
1.2 代码实现
cpp
// odb.h
#pragma once
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
namespace xzyodb {
// 数据库连接配置结构体
struct mysql_settings {
std::string host;
std::string user = "root";
std::string passwd;
std::string db;
std::string cset = "utf8";
unsigned int port = 3306;
unsigned int connection_pool_size = 3;
};
// 数据库工厂类,用于创建数据库连接
class DBFactory {
public:
static std::shared_ptr<odb::database> mysql(const mysql_settings& settings);
};
}
cpp
// odb.cc
#include "odb.h"
namespace xzyodb {
std::shared_ptr<odb::database> DBFactory::mysql(const mysql_settings& settings) {
// 1.创建连接池
std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(settings.connection_pool_size));
// 2.创建数据库操作句柄
std::shared_ptr<odb::database> handler = std::make_shared<odb::mysql::database>(settings.user, settings.passwd, settings.db, settings.host, settings.port, nullptr, settings.cset, 0, std::move(pool));
// 3.返回数据库操作句柄
return handler;
}
}
2. 使用样例
1.1 目录结构
bash
test/
|-- odb
| |-- makefile
| |-- odb_test.cc
| |-- student.h
1.2 项目构建
bash
# makefile
odb_test:odb_test.cc student-odb.cxx ../../source/odb.cc
g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
.PHONY: clean
clean:
rm -f odb_test *-odb.* *.sql
1.3 代码实现
cpp
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>
// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student")
class Student {
public:
Student(){};
Student(const std::string& name): _name(name) {}
// 获取/设置 _sn
size_t sn() const { return _sn; }
void set_sn(size_t sn) { _sn = sn; }
// 获取/设置 _name
const std::string& name() const { return _name; }
void set_name(const std::string& name) { _name = name; }
// 获取/设置 _age
const odb::nullable<int>& age() const { return _age; }
void set_age(odb::nullable<int> age) { _age = age; }
// 获取/设置 _score
const odb::nullable<double>& score() const { return _score; }
void set_score(odb::nullable<double> score) { _score = score; }
// 获取/设置 _birthday
const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
// 友元类 odb::access 可以访问 private 成员
friend class odb::access;
// 设置 _sn 为自增主键
#pragma db id auto
size_t _sn;
// 设置 _classes_id 为索引
#pragma db index
size_t _classes_id;
std::string _name;
odb::nullable<int> _age;
odb::nullable<double> _score;
odb::nullable<boost::posix_time::ptime> _birthday;
};
// 设置数据库表名为 tbl_classes
#pragma db object table("tbl_classes")
class Classes {
public:
Classes(){};
Classes(const std::string& name): _name(name) {}
size_t id() const { return _id; }
void set_id(size_t id) { _id = id; }
const std::string& name() const { return _name; }
void set_name(const std::string& name) { _name = name; }
private:
// 友元类 odb::access 可以访问 private 成员
friend class odb::access;
// 设置 _id 为自增主键
#pragma db id auto
size_t _id;
// 设置 _name 类型为 VARCHAR(32)的唯一索引
#pragma db unique type("VARCHAR(32)")
std::string _name;
};
// 查询条件:Query::Classes::name == "class one"
#pragma db view object(Classes) \
object(Student: Student::_classes_id == Classes::_id) \
query((?))
struct ClassesStudent {
std::shared_ptr<Student> student;
std::shared_ptr<Classes> classes;
};
编译生成可执行程序和 student.sql 文件,如下:

sql
-- 需要手动添加的sql语句 --
DROP DATABASE IF EXISTS `mytest`;
CREATE DATABASE IF NOT EXISTS `mytest`;
USE `mytest`;
-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_classes`;
DROP TABLE IF EXISTS `tbl_student`;
CREATE TABLE `tbl_student` (
`sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`classes_id` BIGINT UNSIGNED NOT NULL,
`name` TEXT NOT NULL,
`age` INT NULL,
`score` DOUBLE NULL,
`birthday` DATETIME NULL)
ENGINE=InnoDB;
CREATE INDEX `classes_id_i`
ON `tbl_student` (`classes_id`);
CREATE TABLE `tbl_classes` (
`id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL)
ENGINE=InnoDB;
CREATE UNIQUE INDEX `name_i`
ON `tbl_classes` (`name`);
-- 需要手动添加的sql语句 --
INSERT INTO `tbl_classes` VALUES (1, 'class one'),
(2, 'class two'),
(3, 'class three'),
(4, 'class four');
INSERT INTO `tbl_student` VALUES (1, 1, "zhangsan", 18, 80, "2000-01-01 00:00:00"),
(2, 2, "lisi", 19, 85, "2000-02-02 00:00:00"),
(3, 3, "wangwu", 20, 90, "2000-03-03 00:00:00"),
(4, 4, "zhaoliu", 21, 95, "2000-04-04 00:00:00"),
(5, 1, "tianqi", 22, 100, "2000-05-05 00:00:00");
将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp
// odb_test.cc
#include <iostream>
#include "../../source/odb.h"
#include "student.h"
#include "student-odb.hxx"
int main()
{
// 1.构造服务器配置
xzyodb::mysql_settings settings = {
.host = "192.168.174.128",
.passwd = "123456",
.db = "mytest",
.connection_pool_size = 5
};
// 2.创建数据库操作句柄
std::shared_ptr<odb::database> handler = xzyodb::DBFactory::mysql(settings);
// 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
try {
// 1.通过句柄构造事务对象
odb::transaction tx(handler->begin());
// 2.通过事务构造database对象
odb::database& db = tx.database();
// 3.查询班级为"class_name"的学生信息
typedef odb::query<ClassesStudent> Query;
typedef odb::result<ClassesStudent> Result;
Result cla(db.query<ClassesStudent>(Query::Classes::name == "class one"));
for (auto it = cla.begin(); it != cla.end(); ++it) {
if (it->student) std::cout << "姓名: " << it->student->name() << "\t";
if (it->classes) std::cout << "班级: " << it->classes->name() << std::endl;
}
// 4.提交事务
tx.commit();
} catch (const odb::exception& e) {
std::cerr << "数据库操作异常: " << e.what() << std::endl;
}
return 0;
}
