ODB-Mysql API接口(常用类与函数)
文章目录
- ODB API接口
- ODB与mysql的数据类型映射表
- pragma 预处理
- 核心 `#pragma` 指令分类与详解
- 一、类级指令:定义类与数据库表的映射
- 二、成员级指令:定义成员与表列的映射
- 三、关系映射指令:定义类之间的关联
- 四、其他常用指令
- 五、查询相关的指令
- ODB 指令的解析流程
- ODB 编译命令的详细解析
- 基本命令格式
- 核心选项详解
- 1. 指定目标数据库(必选)
- 2. 生成查询支持(常用)
- 3. 生成数据库 schema(表结构)
- 4. 生成会话支持(事务管理)
- 5. 控制生成文件的输出路径
- 6. 包含路径和宏定义
- 7. 其他常用选项
- 完整示例:生成 MySQL 映射代码
- odb核心命名空间和类
- odb::schema_catalog
- 核心功能概述表
- 详细方法用法表格
- 1. 模式创建方法
- 2. 模式删除方法
- 3. 迁移和检查方法
- 完整使用案例
- 案例1:完整的数据库初始化
- 案例2:条件创建和增量更新
- 案例3:版本管理和迁移
- 案例4:多模式管理(高级用法)
- 案例5:测试环境管理
- 错误处理最佳实践
- 错误处理模板
- 配置和编译要求
- ODB编译选项表
- 编译命令示例
- odb::mysql
- 核心组件概述表
- mysql::database 详细用法
- 构造函数参数表
- 连接选项参数表
- 完整使用案例
- 案例1:基础数据库连接
- 案例2:连接池配置
- 案例3:高级连接选项
- 案例4:事务管理和错误处理
- 案例5:批量操作和性能优化
- 连接工厂类型对比表
- 连接工厂使用示例
- 异常处理详细表
- 高级错误处理
- 编译和链接要求
- 编译命令示例
- 依赖库说明表
- odb::mysql::database
- 构造函数参数详解表
- 连接选项参数详解表
- 核心方法功能表
- 数据库操作方法
- 连接管理方法
- 完整使用案例
- 案例1:基础数据库连接和操作
- 案例2:连接池配置和高并发
- 案例3:高级查询和事务管理
- 案例4:调试和性能监控
- 案例5:生产环境配置
- odb::result 和 odb::query
- odb::result 核心方法详解表
- odb::query 查询构建操作符表
- 完整使用案例
- 案例1:基础查询操作
- 案例2:结果集遍历和处理
- 案例3:高级查询功能
- 案例4:聚合查询和统计
- 案例5:分页查询和性能优化
- 案例6:动态查询构建
- 案例7:结果集转换和数据处理
- 性能优化提示表
- 错误处理表
- 复杂多表查询操作
- 一、复合查询(多条件组合)
- 1. 核心用法
- 2. 案例:查询 “年龄 18-30 岁且姓名以‘张’开头的人”
- 二、连接查询(多表关联)
- 1. 前提:定义关联类
- 2. 核心用法
- 3. 案例:查询 “住在北京且年龄> 25 岁的人及其地址”
- 三、高级用法:显式指定连接类型(左连接、右连接)
- 四、注意事项
- 老版本创建库
- 一、方法 1:执行 ODB 工具生成的建表 SQL(推荐,类型安全)
- 1. 用 ODB 工具生成建表 SQL
- 2. 在代码中读取并执行 SQL 文件
- 二、方法 2:手动拼接建表 SQL(无 ODB 工具时用)
- 关键注意:
ODB API接口
ODB与mysql的数据类型映射表
在使用 ODB(如 CodeSynthesis ODB)与 MySQL 数据库映射时,C++ 数据类型与 MySQL 字段类型的映射遵循一定规则。以下是常见的 C++ 类型到 MySQL 类型的映射表,包含默认映射及可自定义的选项:
C++ 类型 | 默认 MySQL 类型 | 说明 / 可选 MySQL 类型 |
---|---|---|
bool | TINYINT(1) | 存储布尔值(0 或 1) |
char | TINYINT | 可指定 UNSIGNED (如 TINYINT UNSIGNED ) |
signed char | TINYINT | 有符号 8 位整数 |
unsigned char | TINYINT UNSIGNED | 无符号 8 位整数 |
short / int16_t | SMALLINT | 有符号 16 位整数,可选 SMALLINT UNSIGNED |
unsigned short | SMALLINT UNSIGNED | 无符号 16 位整数 |
int / int32_t | INT | 有符号 32 位整数,可选 INT UNSIGNED |
unsigned int | INT UNSIGNED | 无符号 32 位整数 |
long | BIGINT | 取决于平台(通常 64 位),有符号 64 位整数,可选 BIGINT UNSIGNED |
unsigned long | BIGINT UNSIGNED | 无符号 64 位整数 |
long long / int64_t | BIGINT | 有符号 64 位整数 |
unsigned long long | BIGINT UNSIGNED | 无符号 64 位整数 |
float | FLOAT | 单精度浮点数(4 字节) |
double | DOUBLE | 双精度浮点数(8 字节) |
long double | DOUBLE | 通常映射为 DOUBLE (MySQL 不直接支持 long double ,需自定义) |
std::string | VARCHAR(255) | 可通过 #pragma db type("VARCHAR(n)") 自定义长度(如 VARCHAR(1024) ),或指定 TEXT |
std::wstring | VARCHAR(255) | 宽字符串,默认使用 UTF-8 编码,可选 TEXT 或 LONGTEXT |
std::vector<char> | BLOB | 二进制数据,可选 TINYBLOB 、MEDIUMBLOB 、LONGBLOB |
std::vector<unsigned char> | BLOB | 同上 |
std::tm (日期时间) | DATETIME | 包含日期和时间(年 - 月 - 日 时:分: 秒),可选 DATE (仅日期)、TIME (仅时间) |
boost::date_time::date | DATE | 仅日期(需引入 boost 库支持) |
boost::date_time::ptime | DATETIME | 日期 + 时间(需引入 boost 库支持) |
pragma 预处理
在 ODB(Object-Database Binding,如 CodeSynthesis ODB)中,预处理器指令 #pragma
是核心元数据载体,用于向 ODB 代码生成器传递 C++ 类型与数据库模式(表、列、关系等)的映射规则。这些 #pragma
指令不影响 C++ 编译器的正常编译(编译器会忽略未识别的 #pragma
),但会被 ODB 专用工具(如 odb
编译器)解析,生成对应的数据库操作代码(如建表语句、对象序列化 / 反序列化逻辑)。
核心 #pragma
指令分类与详解
ODB 的 #pragma
指令按功能可分为 类级指令(控制类与表的映射)、成员级指令(控制成员与列的映射)、关系指令(控制类之间的关联)等,以下是最常用的指令解析:
一、类级指令:定义类与数据库表的映射
类级指令作用于整个类,声明类与数据库表的对应关系,是 ODB 映射的基础。
-
#pragma db object
-
功能:声明当前类是一个 “持久化对象”,对应数据库中的一张表。
-
说明:只有标记此指令的类才会被 ODB 处理,生成对应的表结构和操作代码。
-
示例:
#pragma db object // 该类映射到数据库表 class User { ... };
-
-
#pragma db table("table_name")
-
功能:自定义类对应的数据库表名(默认表名为类名,通常小写)。
-
示例:
#pragma db object #pragma db table("sys_user") // 映射到表 "sys_user",而非默认的 "user" class User { ... };
-
-
#pragma db abstract
-
功能:声明类为 “抽象类”,不生成对应的表,但可被其他类继承(用于抽取公共字段)。
-
示例:
#pragma db object abstract // 抽象类,无对应表 class Base { protected:std::string name_; };#pragma db object class Derived : public Base { ... }; // 继承 Base 的字段,生成包含 name_ 的表
-
-
#pragma db polymorphic
- 功能:声明类为 “多态类”(含虚函数),支持多态查询(如查询父类时返回所有子类对象)。
- 说明:需配合继承使用,ODB 会生成额外的类型标识字段(如
type_id
)区分子类。
二、成员级指令:定义成员与表列的映射
成员级指令作用于类的成员变量,控制其对应的数据库列的属性(名称、类型、约束等)。
-
#pragma db id
-
功能:指定成员为数据库表的主键(
PRIMARY KEY
)。 -
说明:每个持久化类必须有且仅有一个主键(复合主键需特殊处理)。
-
示例:
#pragma db object class User { private:friend class odb::access;#pragma db id // id_ 作为主键unsigned long id_;std::string name_; };
-
-
#pragma db column("column_name")
-
功能:自定义成员对应的数据库列名(默认列名为成员变量名)。
-
示例:
#pragma db object class User { private:friend class odb::access;#pragma db idunsigned long id_;#pragma db column("user_name") // 映射到列 "user_name"std::string name_; };
-
-
#pragma db type("db_type")
-
功能:自定义成员对应的数据库字段类型(覆盖默认映射)。
-
示例:
#pragma db object class User { private:friend class odb::access;#pragma db idunsigned long id_;#pragma db type("VARCHAR(1000)") // 自定义字符串长度为 1000std::string bio_; // 默认映射为 VARCHAR(255),此处修改为 VARCHAR(1000) };
-
-
#pragma db not_null
-
功能:指定列不为空(
NOT NULL
约束)。 -
示例:
#pragma db object class User { private:friend class odb::access;#pragma db idunsigned long id_;#pragma db not_null // 列 "user_name" 不允许为 NULLstd::string name_; };
-
-
#pragma db unique
-
功能:指定列的值唯一(
UNIQUE
约束)。 -
示例:
#pragma db object class User { private:friend class odb::access;#pragma db idunsigned long id_;#pragma db unique // 用户名唯一std::string username_; };
-
-
#pragma db default(value)
-
功能:指定列的默认值(
DEFAULT
约束)。 -
示例:
#pragma db object class User { private:friend class odb::access;#pragma db idunsigned long id_;#pragma db default("guest") // 默认用户名为 "guest"std::string username_;#pragma db default(0) // 默认年龄为 0int age_; };
-
-
#pragma db index
-
功能:为列创建索引(加速查询)。
-
示例:
#pragma db object class User { private:friend class odb::access;#pragma db idunsigned long id_;#pragma db index // 为 email 列创建索引std::string email_; };
-
三、关系映射指令:定义类之间的关联
用于描述多个类(表)之间的关系(如一对一、一对多、多对多),对应数据库的外键约束。
-
#pragma db one
(一对一关系)-
功能:声明当前类与另一个类为一对一关联(如
User
与Profile
)。 -
示例:
#pragma db object class Profile { private:friend class odb::access;#pragma db idunsigned long id_;std::string bio_; };#pragma db object class User { private:friend class odb::access;#pragma db idunsigned long id_;std::string name_;#pragma db one // User 与 Profile 一对一关联std::shared_ptr<Profile> profile_; // 外键指向 Profile 的 id };
-
-
#pragma db many
(一对多关系)-
功能:声明当前类与另一个类为一对多关联(如
Author
与Book
)。 -
示例:
#pragma db object class Book { private:friend class odb::access;#pragma db idunsigned long id_;std::string title_;#pragma db not_null // 外键不为空std::shared_ptr<Author> author_; // 多对一:多个 Book 关联一个 Author };#pragma db object class Author { private:friend class odb::access;#pragma db idunsigned long id_;std::string name_;#pragma db many // 一对多:一个 Author 关联多个 Bookstd::vector<std::shared_ptr<Book>> books_; };
-
-
#pragma db value_type
(值类型映射)-
功能:声明自定义结构体为 “值类型”(非独立表,其成员作为主表的列)。
-
示例:
// 地址结构体(值类型,无独立表) #pragma db value_type struct Address {std::string street_;std::string city_; };#pragma db object class User { private:friend class odb::access;#pragma db idunsigned long id_;Address addr_; // 地址的成员(street_、city_)会作为 User 表的列 };
-
四、其他常用指令
-
#pragma db version
-
功能:为类添加版本字段(用于乐观锁,避免并发更新冲突)。
-
示例:
#pragma db object class User { private:friend class odb::access;#pragma db idunsigned long id_;std::string name_;#pragma db version // 自动添加版本列(如 "version"),更新时自动递增unsigned int version_; };
-
-
#pragma db transient
-
功能:标记成员为 “临时成员”,不映射到数据库列(仅在内存中存在)。
-
示例:
#pragma db object class User { private:friend class odb::access;#pragma db idunsigned long id_;std::string name_;#pragma db transient // 不存储到数据库std::string temp_data_; };
-
五、查询相关的指令
这些指令用于辅助查询(如定义成员别名、索引加速查询等),确保 ODB 能正确解析查询条件中的类成员。
#pragma db member(member).query_name("alias")
-
功能:为类成员定义查询别名(在查询表达式中可使用别名代替成员名,避免命名冲突)。
-
示例:
#pragma db object class Person { private:friend class odb::access;#pragma db idunsigned long id_;#pragma db member(name_).query_name("username") // 别名 "username"std::string name_;#pragma db member(age_).query_name("user_age") // 别名 "user_age"int age_; };
后续查询中可使用username代替name_:
odb::query<Person>(odb::query<Person>::username == "Alice")
#pragma db index(...)
-
功能:为成员创建数据库索引,加速查询(尤其是频繁作为查询条件的字段)。
-
示例:
#pragma db object class Person { private:friend class odb::access;#pragma db idunsigned long id_;#pragma db index // 为 name_ 建立索引,加速按名称查询std::string name_;#pragma db index("age_idx") // 自定义索引名int age_; };
#pragma db queryable
- 功能:声明类成员可被查询(默认所有非
transient
成员均可查询,此指令主要用于显式标记或覆盖默认行为)。
ODB 指令的解析流程
- 代码编写:开发者在 C++ 类中添加 ODB
#pragma
指令,定义映射规则。 - 代码生成:使用odb编译器(如odb --database mysql user.hxx)解析带指令的头文件,生成:
- 数据库表结构脚本(
CREATE TABLE
语句); - 对象与数据库之间的序列化 / 反序列化代码(如
user-odb.hxx
、user-odb.cxx
)。
- 数据库表结构脚本(
- 编译运行:将生成的代码与业务代码一起编译,通过 ODB 运行时库操作数据库。
ODB 编译命令(即 odb
工具的使用指令)是将带有 ODB 元数据(#pragma
指令)的 C++ 代码转换为数据库映射代码(如建表语句、对象序列化逻辑)的核心步骤。odb
工具由 CodeSynthesis 提供,其命令格式和参数需根据目标数据库、代码生成需求进行配置。以下是
ODB 编译命令的详细解析
基本命令格式
odb [选项]... 源文件...
源文件
:包含 ODB 元数据的 C++ 头文件(如person.hxx
),odb
工具会解析这些文件生成映射代码。选项
:控制代码生成的规则(如目标数据库类型、生成文件类型等)。
核心选项详解
1. 指定目标数据库(必选)
--database
(缩写 -d
):指定目标数据库类型,决定生成的 SQL 语法和映射逻辑(不同数据库的类型、约束语法有差异)。支持的数据库类型:mysql
、sqlite
、postgresql
、oracle
、mssql
等。
示例:
odb -d mysql person.hxx # 生成适配 MySQL 的代码
odb --database sqlite user.hxx # 生成适配 SQLite 的代码
2. 生成查询支持(常用)
--generate-query
(缩写 -q
):生成查询相关的代码,支持通过 ODB 提供的查询语法(类似 SQL)查询对象。若需使用 db.query<Person>(...)
等查询操作,必须添加此选项。
示例:
odb -d mysql -q person.hxx # 生成 MySQL 代码并支持查询
3. 生成数据库 schema(表结构)
--generate-schema
(缩写 -s
):生成创建数据库表的 SQL 脚本(CREATE TABLE
语句)。生成的脚本默认包含在代码中,也可通过 --schema-format
指定输出格式。
示例:
odb -d mysql -s person.hxx # 生成建表 SQL 脚本
附加选项:
-
--schema-name <name>
:指定生成的 schema 名称(如数据库名)。 -
--schema-format <format>
:指定 SQL 脚本格式,可选embedded(嵌入代码,默认)、separate(单独生成.sql文件)。odb -d mysql -s --schema-format separate person.hxx # 生成单独的 person.sql 文件
4. 生成会话支持(事务管理)
--generate-session
(缩写 -e
):生成会话(session
)相关代码,用于管理对象缓存和事务上下文(多线程或复杂关系场景常用)。
示例:
odb -d postgresql -e -q user.hxx # 生成会话支持和查询代码
5. 控制生成文件的输出路径
--output-dir <dir>
(缩写 -o
):指定生成文件的输出目录(默认与源文件同目录)。
示例:
odb -d mysql -o ./generated person.hxx # 生成的代码放入 ./generated 目录
6. 包含路径和宏定义
与 C++ 编译器类似,odb
工具需要知道头文件路径和宏定义才能正确解析代码:
--include <dir>
(缩写-I
):添加头文件搜索路径(如依赖的第三方库头文件)。--define <macro>
(缩写-D
):定义宏(如条件编译的宏)。
示例:
odb -d mysql -I ./include -D DEBUG person.hxx # 包含 ./include 路径,定义 DEBUG 宏
7. 其他常用选项
--hxx-suffix <suffix>
:指定生成的头文件后缀(默认.hxx
),如--hxx-suffix .h
。--cxx-suffix <suffix>
:指定生成的源文件后缀(默认.cxx
),如--cxx-suffix .
。--verbose
(缩写-v
):输出详细的生成过程(调试时用)。--help
(缩写-h
):查看所有选项的帮助信息。
完整示例:生成 MySQL 映射代码
假设我们有一个 person.hxx
文件(带 ODB 元数据),需要生成:
- 适配 MySQL 的代码;
- 支持查询操作;
- 单独的建表 SQL 脚本;
- 输出到
./odb-gen
目录。
命令如下:
odb -d mysql \-q \-s --schema-format separate \-o ./odb-gen \person.hxx
执行后会生成:
./odb-gen/person-odb.hxx
:映射头文件(声明序列化 / 反序列化接口);./odb-gen/person-odb.cxx
:映射源文件(实现数据库操作逻辑);./odb-gen/person.sql
:MySQL 建表脚本(CREATE TABLE person (...)
)。
odb核心命名空间和类
odb::schema_catalog
核心功能概述表
功能类别 | 方法名称 | 作用 | 适用场景 |
---|---|---|---|
模式创建 | create_schema() | 创建所有持久化类的数据库表 | 初始化数据库 |
表创建 | create_table() | 创建特定类的数据库表 | 增量添加表 |
模式删除 | drop_schema() | 删除所有持久化类的表 | 清理测试数据 |
表删除 | drop_table() | 删除特定类的表 | 删除单个表 |
模式迁移 | migrate_schema() | 升级数据库模式到新版本 | 版本升级 |
存在性检查 | exists() | 检查表是否存在 | 条件创建 |
详细方法用法表格
1. 模式创建方法
方法签名 | 参数说明 | 返回值 | 异常 |
---|---|---|---|
void create_schema(database&) | 数据库连接引用 | void | odb::exception |
void create_schema(database&, const std::string& name) | 数据库连接,模式名 | void | odb::exception |
void create_table(database&, const std::string& name = "") | 数据库连接,表名前缀 | void | odb::exception |
2. 模式删除方法
方法签名 | 参数说明 | 返回值 | 异常 |
---|---|---|---|
void drop_schema(database&) | 数据库连接引用 | void | odb::exception |
void drop_schema(database&, const std::string& name) | 数据库连接,模式名 | void | odb::exception |
void drop_table(database&, const std::string& name = "") | 数据库连接,表名前缀 | void | odb::exception |
3. 迁移和检查方法
方法签名 | 参数说明 | 返回值 | 异常 |
---|---|---|---|
void migrate_schema(database&, unsigned long long version) | 数据库连接,目标版本 | void | odb::exception |
bool exists(database&, const std::string& name = "") | 数据库连接,表名 | 是否存在 | odb::exception |
unsigned long long version(database&, const std::string& name = "") | 数据库连接,模式名 | 当前版本号 | odb::exception |
完整使用案例
案例1:完整的数据库初始化
#include <odb/database.hxx>
#include <odb/transaction.hxx>
#include <odb/mysql/database.hxx>
#include <odb/schema-catalog.hxx>#include "person.hxx"
#include "department.hxx"
#include "employee.hxx"class DatabaseManager {
public:static void initializeDatabase(odb::database& db) {try {odb::transaction t(db.begin());// 删除现有模式(如果存在)if (odb::schema_catalog::exists(db)) {std::cout << "删除现有模式..." << std::endl;odb::schema_catalog::drop_schema(db);}// 创建新表结构std::cout << "创建数据库表..." << std::endl;odb::schema_catalog::create_schema(db);t.commit();std::cout << "数据库初始化成功!" << std::endl;} catch (const odb::exception& e) {std::cerr << "数据库初始化失败: " << e.what() << std::endl;throw;}}
};
案例2:条件创建和增量更新
void conditionalSchemaCreation(odb::database& db) {odb::transaction t(db.begin());// 检查Person表是否存在if (!odb::schema_catalog::exists<Person>(db)) {std::cout << "创建Person表..." << std::endl;odb::schema_catalog::create_table<Person>(db);}// 检查Department表是否存在if (!odb::schema_catalog::exists<Department>(db)) {std::cout << "创建Department表..." << std::endl;odb::schema_catalog::create_table<Department>(db);}t.commit();
}
案例3:版本管理和迁移
void schemaMigrationExample(odb::database& db) {try {// 获取当前模式版本unsigned long long current_version = odb::schema_catalog::version(db);std::cout << "当前数据库版本: " << current_version << std::endl;// 迁移到最新版本unsigned long long target_version = 2; // 假设目标版本是2if (current_version < target_version) {odb::transaction t(db.begin());odb::schema_catalog::migrate_schema(db, target_version);t.commit();std::cout << "数据库迁移到版本 " << target_version << " 成功" << std::endl;}} catch (const odb::exception& e) {std::cerr << "迁移失败: " << e.what() << std::endl;}
}
案例4:多模式管理(高级用法)
void multiSchemaExample(odb::mysql::database& db) {// 创建主业务表(默认模式){odb::transaction t(db.begin());odb::schema_catalog::create_schema(db, "main_schema");t.commit();}// 创建日志表(独立模式){odb::transaction t(db.begin());odb::schema_catalog::create_schema(db, "log_schema");t.commit();}
}
案例5:测试环境管理
class TestDatabaseManager {
public:static void setupTestDatabase(odb::database& db) {odb::transaction t(db.begin());// 清理测试环境odb::schema_catalog::drop_schema(db);// 创建测试表结构odb::schema_catalog::create_schema(db);// 插入测试数据insertTestData(db);t.commit();}static void cleanupTestDatabase(odb::database& db) {odb::transaction t(db.begin());odb::schema_catalog::drop_schema(db);t.commit();}private:static void insertTestData(odb::database& db) {// 插入测试数据Person p1("测试用户1", 25);Person p2("测试用户2", 30);db.persist(p1);db.persist(p2);}
};
错误处理最佳实践
错误处理模板
void safeSchemaOperation(odb::database& db) {try {odb::transaction t(db.begin());// 执行模式操作if (!odb::schema_catalog::exists(db)) {odb::schema_catalog::create_schema(db);}t.commit();} catch (const odb::schema_catalog_exception& e) {std::cerr << "模式操作异常: " << e.what() << std::endl;// 处理模式相关异常} catch (const odb::exception& e) {std::cerr << "ODB异常: " << e.what() << std::endl;// 处理通用ODB异常} catch (const std::exception& e) {std::cerr << "标准异常: " << e.what() << std::endl;// 处理其他异常}
}
配置和编译要求
ODB编译选项表
编译选项 | 作用 | 示例 |
---|---|---|
--generate-schema | 生成模式创建代码 | odb -d mysql --generate-schema person.hxx |
--schema-format | 模式文件格式 | --schema-format separate |
--schema-name | 指定模式名称 | --schema-name myapp |
--default-database | 默认数据库类型 | --default-database mysql |
编译命令示例
# 生成包含模式支持的ODB代码
odb -d mysql --generate-schema --std c++11 \--schema-format separate \person.hxx department.hxx employee.hxx# 编译程序
g++ -std=c++11 -o app \main. person-odb.cxx department-odb.cxx employee-odb.cxx \-lodb -lodb-mysql -lmysqlclient
odb::mysql
核心组件概述表
组件类别 | 类名 | 作用 | 关键特性 |
---|---|---|---|
数据库连接 | mysql::database | MySQL数据库连接实现 | 连接池、字符集、SSL支持 |
连接工厂 | connection_factory | 连接创建策略 | 单连接、连接池 |
连接池 | connection_pool | 数据库连接池管理 | 最小/最大连接数、健康检查 |
异常类型 | mysql::exception | MySQL特定异常 | 错误码、SQL状态 |
mysql::database 详细用法
构造函数参数表
参数顺序 | 参数类型 | 默认值 | 说明 |
---|---|---|---|
1 | const std::string& user | - | 数据库用户名 |
2 | const std::string& password | - | 数据库密码 |
3 | const std::string& database | - | 数据库名称 |
4 | const std::string& host | "localhost" | 主机地址 |
5 | unsigned int port | 0 | 端口号(0表示默认) |
6 | const std::string& socket | "" | Unix socket路径 |
7 | const std::string& options | "" | 连接选项字符串 |
连接选项参数表
选项 | 格式 | 示例 | 作用 |
---|---|---|---|
字符集 | charset=编码 | charset=utf8mb4 | 设置客户端字符集 |
SSL模式 | ssl_mode=模式 | ssl_mode=REQUIRED | SSL连接模式 |
超时设置 | connect_timeout=秒 | connect_timeout=10 | 连接超时时间 |
压缩 | compress=true | compress=true | 启用压缩协议 |
本地文件 | local_infile=1 | local_infile=1 | 允许LOAD DATA LOCAL |
完整使用案例
案例1:基础数据库连接
#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include <odb/transaction.hxx>void basicConnectionExample() {try {// 基础连接参数odb::mysql::database db("test_user", // 用户名"test_password", // 密码 "test_db", // 数据库名"localhost", // 主机3306, // 端口"", // Unix socket"charset=utf8mb4" // 连接选项);std::cout << "MySQL数据库连接成功!" << std::endl;// 测试查询odb::transaction t(db.begin());auto result = db.query<int>("SELECT 1");t.commit();} catch (const odb::mysql::exception& e) {std::cerr << "MySQL错误: " << e.what() << " [代码: " << e.error_code() << "]" << std::endl;} catch (const std::exception& e) {std::cerr << "错误: " << e.what() << std::endl;}
}
案例2:连接池配置
#include <odb/mysql/connection.hxx>
#include <odb/mysql/connection-factory.hxx>void connectionPoolExample() {try {// 创建连接池工厂auto factory = std::make_shared<odb::mysql::connection_pool_factory>(10, // 最大连接数2, // 最小连接数true // 启用健康检查);// 使用连接池创建数据库实例odb::mysql::database db("user", "password", "database", "localhost", 3306,"", "charset=utf8mb4", factory);// 在多线程环境中使用#pragma omp parallel forfor (int i = 0; i < 10; ++i) {odb::transaction t(db.begin());// 每个线程使用独立的数据库连接db.execute("INSERT INTO logs (message) VALUES ('Thread: " + std::to_string(i) + "')");t.commit();}} catch (const odb::exception& e) {std::cerr << "连接池错误: " << e.what() << std::endl;}
}
案例3:高级连接选项
void advancedConnectionExample() {try {// 构建复杂连接选项std::string options = "charset=utf8mb4&""connect_timeout=10&""ssl_mode=REQUIRED&""compress=true&""auto_reconnect=1";odb::mysql::database db("app_user", // 用户名"secure_password", // 密码"production_db", // 数据库名"db.cluster.example.com", // 主机3306, // 端口"", // socketoptions // 连接选项);// 设置SQL跟踪器(调试用)db.tracer(std::make_shared<odb::stderr_tracer>());// 获取连接信息std::cout << "客户端版本: " << db.client_version() << std::endl;std::cout << "服务器版本: " << db.server_version() << std::endl;} catch (const odb::mysql::exception& e) {std::cerr << "MySQL连接错误 [" << e.error_code() << "]: " << e.what() << std::endl;}
}
案例4:事务管理和错误处理
class MySQLTransactionManager {
public:static bool transferMoney(odb::mysql::database& db, long from_account, long to_account, double amount) {odb::transaction t(db.begin());try {// 检查发送方余额double from_balance = db.query<double>("SELECT balance FROM accounts WHERE id = " + std::to_string(from_account)).begin()->get();if (from_balance < amount) {std::cerr << "余额不足" << std::endl;t.rollback();return false;}// 扣款db.execute("UPDATE accounts SET balance = balance - " + std::to_string(amount) + " WHERE id = " + std::to_string(from_account));// 存款db.execute("UPDATE accounts SET balance = balance + " + std::to_string(amount) + " WHERE id = " + to_string(to_account));// 记录交易db.execute("INSERT INTO transactions (from_acc, to_acc, amount) VALUES (" +std::to_string(from_account) + ", " +std::to_string(to_account) + ", " +std::to_string(amount) + ")");t.commit();std::cout << "转账成功: " << amount << std::endl;return true;} catch (const odb::mysql::exception& e) {std::cerr << "MySQL事务错误: " << e.what() << std::endl;t.rollback();return false;}}
};
案例5:批量操作和性能优化
void batchOperationsExample(odb::mysql::database& db) {odb::transaction t(db.begin());try {// 启用批量插入优化db.execute("SET autocommit=0");db.execute("SET unique_checks=0");db.execute("SET foreign_key_checks=0");// 批量插入数据for (int i = 0; i < 1000; ++i) {db.execute("INSERT INTO bulk_data (value, timestamp) VALUES (" +std::to_string(i) + ", NOW())");}// 恢复设置db.execute("SET foreign_key_checks=1");db.execute("SET unique_checks=1");t.commit();std::cout << "批量插入完成" << std::endl;} catch (const odb::exception& e) {std::cerr << "批量操作失败: " << e.what() << std::endl;t.rollback();}
}
连接工厂类型对比表
工厂类型 | 类名 | 特点 | 适用场景 |
---|---|---|---|
单连接工厂 | single_connection_factory | 每次返回同一连接 | 单线程应用 |
新连接工厂 | new_connection_factory | 每次创建新连接 | 简单多线程 |
连接池工厂 | connection_pool_factory | 连接池管理 | 高并发生产环境 |
连接工厂使用示例
void connectionFactoryExamples() {// 1. 单连接工厂(默认)auto single_factory = std::make_shared<odb::mysql::single_connection_factory>();// 2. 新连接工厂auto new_factory = std::make_shared<odb::mysql::new_connection_factory>();// 3. 连接池工厂auto pool_factory = std::make_shared<odb::mysql::connection_pool_factory>(20, // 最大连接数5, // 最小连接数true, // 启用ping检查60, // 连接超时秒数300 // 连接最大空闲时间);// 使用连接池创建数据库odb::mysql::database db_with_pool("user", "pass", "db", "localhost", 3306, "", "", pool_factory);
}
异常处理详细表
异常类型 | 触发条件 | 处理方法 |
---|---|---|
mysql::database_exception | 数据库操作错误 | 检查SQL语法、权限 |
mysql::connection_exception | 连接相关错误 | 检查网络、认证信息 |
mysql::timeout_exception | 操作超时 | 调整超时设置、优化查询 |
mysql::deadlock_exception | 死锁检测 | 重试机制、调整事务隔离级别 |
高级错误处理
void comprehensiveErrorHandling() {try {odb::mysql::database db("user", "pass", "db");// 数据库操作...} catch (const odb::mysql::deadlock_exception& e) {// 死锁处理:重试逻辑std::cerr << "检测到死锁,正在重试..." << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));// 重试操作} catch (const odb::mysql::timeout_exception& e) {// 超时处理std::cerr << "操作超时: " << e.what() << std::endl;} catch (const odb::mysql::connection_exception& e) {// 连接异常处理std::cerr << "连接失败: " << e.what() << " [错误码: " << e.error_code() << "]" << std::endl;} catch (const odb::mysql::database_exception& e) {// 数据库操作异常std::cerr << "数据库错误: " << e.what() << " [SQL状态: " << e.sql_state() << "]" << std::endl;}
}
编译和链接要求
编译命令示例
# 编译ODB MySQL应用
g++ -std=c++11 -o mysql_app \main. person-odb.cxx \-lodb -lodb-mysql -lmysqlclient \-I/usr/local/include/mysql \-L/usr/local/lib/mysql# 使用pkg-config(推荐)
g++ -std=c++11 -o mysql_app \main. person-odb.cxx \$(pkg-config --cflags --libs libodb libodb-mysql mysqlclient)
依赖库说明表
库名称 | 作用 | 安装命令(Ubuntu) |
---|---|---|
libodb | ODB核心库 | apt-get install libodb-dev |
libodb-mysql | MySQL数据库适配器 | apt-get install libodb-mysql-dev |
libmysqlclient | MySQL客户端库 | apt-get install libmysqlclient-dev |
odb::mysql::database
构造函数参数详解表
参数位置 | 参数类型 | 默认值 | 说明 | 示例 |
---|---|---|---|---|
1 | const std::string& user | - | 数据库用户名 | "root" |
2 | const std::string& password | - | 数据库密码 | "password" |
3 | const std::string& database | - | 数据库名称 | "myapp_db" |
4 | const std::string& host | "localhost" | 主机地址 | "127.0.0.1" |
5 | unsigned int port | 0 | 端口号(0=默认3306) | 3306 |
6 | const std::string& socket | "" | Unix socket路径 | "/var/run/mysqld/mysqld.sock" |
7 | const std::string& options | "" | 连接选项字符串 | "charset=utf8mb4" |
8 | connection_factory_ptr factory | nullptr | 连接工厂指针 | 连接池工厂 |
连接选项参数详解表
选项参数 | 格式 | 默认值 | 说明 | 示例 |
---|---|---|---|---|
字符集 | charset=编码 | - | 客户端字符集 | charset=utf8mb4 |
SSL模式 | ssl_mode=模式 | - | SSL连接模式 | ssl_mode=REQUIRED |
连接超时 | connect_timeout=秒 | - | 连接超时时间 | connect_timeout=10 |
压缩 | compress=布尔值 | false | 启用压缩协议 | compress=true |
自动重连 | auto_reconnect=布尔值 | false | 自动重新连接 | auto_reconnect=1 |
本地数据 | local_infile=布尔值 | false | 允许LOAD DATA | local_infile=1 |
初始化命令 | init_command=SQL | - | 连接后执行命令 | init_command=SET NAMES utf8mb4 |
核心方法功能表
数据库操作方法
方法名 | 参数 | 返回值 | 说明 |
---|---|---|---|
persist(T& object) | 对象引用 | void | 插入对象到数据库 |
find<T>(const id_type& id) | 主键值 | shared_ptr<T> | 根据主键查找对象 |
update(const T& object) | 对象常量引用 | void | 更新数据库中的对象 |
erase(const T& object) | 对象常量引用 | void | 删除数据库中的对象 |
erase_query<T>(const query<T>&) | 查询条件 | unsigned long long | 条件删除,返回影响行数 |
query<T>(const query<T>&) | 查询条件 | result<T> | 执行查询返回结果集 |
query_value<T>(const query<T>&) | 查询条件 | T | 查询单个值 |
execute(const std::string& sql) | SQL语句 | void | 执行原始SQL语句 |
连接管理方法
方法名 | 参数 | 返回值 | 说明 |
---|---|---|---|
tracer(tracer_ptr t) | 跟踪器指针 | void | 设置SQL查询跟踪器 |
connection_factory() | 无 | connection_factory_ptr | 获取连接工厂 |
client_version() | 无 | unsigned long | 获取客户端版本 |
server_version() | 无 | unsigned long | 获取服务器版本 |
完整使用案例
案例1:基础数据库连接和操作
#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include <odb/transaction.hxx>
#include <iostream>void basicDatabaseOperations() {try {// 创建数据库连接odb::mysql::database db("app_user", // 用户名"secure_password", // 密码"my_application", // 数据库名"localhost", // 主机3306, // 端口"", // Unix socket"charset=utf8mb4&connect_timeout=10" // 选项);std::cout << "数据库连接成功!" << std::endl;std::cout << "MySQL服务器版本: " << db.server_version() << std::endl;// 开启事务odb::transaction t(db.begin());// 执行原始SQLdb.execute("CREATE TABLE IF NOT EXISTS test_table (id INT PRIMARY KEY, name VARCHAR(50))");// 插入数据db.execute("INSERT INTO test_table VALUES (1, '测试数据')");t.commit();std::cout << "操作完成!" << std::endl;} catch (const odb::mysql::exception& e) {std::cerr << "MySQL错误 [" << e.error_code() << "]: " << e.what() << std::endl;} catch (const std::exception& e) {std::cerr << "错误: " << e.what() << std::endl;}
}
案例2:连接池配置和高并发
#include <odb/mysql/connection-factory.hxx>
#include <odb/mysql/database.hxx>
#include <thread>
#include <vector>class DatabaseService {
private:odb::mysql::database db_;public:DatabaseService() : db_("user", "password", "db", "localhost", 3306, "", "charset=utf8mb4",std::make_shared<odb::mysql::connection_pool_factory>(20, 5, true)) {}void concurrentOperations() {std::vector<std::thread> threads;// 创建10个线程并发操作for (int i = 0; i < 10; ++i) {threads.emplace_back([this, i]() {try {odb::transaction t(db_.begin());// 每个线程插入数据db_.execute("INSERT INTO concurrent_log (thread_id, message) VALUES (" +std::to_string(i) + ", '线程消息')");t.commit();std::cout << "线程 " << i << " 完成操作" << std::endl;} catch (const std::exception& e) {std::cerr << "线程 " << i << " 错误: " << e.what() << std::endl;}});}// 等待所有线程完成for (auto& thread : threads) {thread.join();}}
};
案例3:高级查询和事务管理
#include <odb/query.hxx>
#include <odb/result.hxx>class AdvancedDatabaseOperations {odb::mysql::database& db_;public:AdvancedDatabaseOperations(odb::mysql::database& db) : db_(db) {}// 复杂事务操作bool transferFunds(long from_account, long to_account, double amount) {odb::transaction t(db_.begin());try {// 检查余额double balance = db_.query_value<double>(odb::query<double>::value.in(db_.query<double>("SELECT balance FROM accounts WHERE id = " + std::to_string(from_account))));if (balance < amount) {std::cerr << "余额不足" << std::endl;t.rollback();return false;}// 执行转账db_.execute("UPDATE accounts SET balance = balance - " + std::to_string(amount) + " WHERE id = " + std::to_string(from_account));db_.execute("UPDATE accounts SET balance = balance + " + std::to_string(amount) + " WHERE id = " + std::to_string(to_account));t.commit();std::cout << "转账成功: " << amount << std::endl;return true;} catch (const odb::exception& e) {std::cerr << "转账失败: " << e.what() << std::endl;t.rollback();return false;}}// 批量操作void batchInsertUsers(const std::vector<std::pair<std::string, int>>& users) {odb::transaction t(db_.begin());try {// 优化批量插入性能db_.execute("SET autocommit=0");db_.execute("SET unique_checks=0");for (const auto& user : users) {db_.execute("INSERT INTO users (name, age) VALUES ('" + user.first + "', " + std::to_string(user.second) + ")");}db_.execute("SET unique_checks=1");t.commit();std::cout << "批量插入 " << users.size() << " 条记录完成" << std::endl;} catch (const std::exception& e) {std::cerr << "批量插入失败: " << e.what() << std::endl;t.rollback();}}
};
案例4:调试和性能监控
#include <odb/tracer.hxx>class DebugTracer : public odb::tracer {
public:void execute(odb::connection&, const char* statement) override {std::cout << "[SQL执行] " << statement << std::endl;}void execute(odb::connection&, const std::string& statement) override {std::cout << "[SQL执行] " << statement << std::endl;}void prepare(odb::connection&, const char* statement) override {std::cout << "[SQL准备] " << statement << std::endl;}
};void debugDatabaseOperations() {// 创建带调试功能的数据库连接odb::mysql::database db("user", "password", "test_db", "localhost", 3306, "", "charset=utf8mb4");// 设置SQL跟踪器auto tracer = std::make_shared<DebugTracer>();db.tracer(tracer);// 执行操作,所有SQL都会被记录odb::transaction t(db.begin());db.execute("SELECT * FROM users WHERE age > 18");db.execute("UPDATE users SET status = 'active'");t.commit();
}
案例5:生产环境配置
class ProductionDatabaseConfig {
public:static odb::mysql::database createProductionDatabase() {// 生产环境连接选项std::string options = "charset=utf8mb4&""connect_timeout=10&""ssl_mode=REQUIRED&""auto_reconnect=1&""init_command=SET SESSION sql_mode='STRICT_TRANS_TABLES'";// 使用连接池auto connection_pool = std::make_shared<odb::mysql::connection_pool_factory>(50, // 最大连接数10, // 最小连接数true, // 启用健康检查30, // 连接超时(秒)300 // 最大空闲时间(秒));return odb::mysql::database("prod_user", // 用户名"strong_password", // 密码"production_db", // 数据库名"db-cluster.example.com", // 主机3306, // 端口"", // socketoptions, // 连接选项connection_pool // 连接池);}static void initializeDatabase(odb::mysql::database& db) {try {odb::transaction t(db.begin());// 设置数据库会话参数db.execute("SET NAMES utf8mb4");db.execute("SET time_zone='+08:00'");db.execute("SET sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'");t.commit();std::cout << "生产数据库初始化完成" << std::endl;} catch (const std::exception& e) {std::cerr << "数据库初始化失败: " << e.what() << std::endl;throw;}}
};
odb::result 和 odb::query
odb::result 核心方法详解表
方法名 | 返回值类型 | 参数 | 说明 | 时间复杂度 |
---|---|---|---|---|
begin() | iterator | 无 | 返回指向第一个元素的迭代器 | O(1) |
end() | iterator | 无 | 返回尾后迭代器 | O(1) |
size() | size_type | 无 | 返回结果集大小 | O(1) |
empty() | bool | 无 | 检查结果集是否为空 | O(1) |
front() | pointer_type | 无 | 返回第一个元素的指针 | O(1) |
first() | pointer_type | 无 | 返回第一个元素的指针 | O(1) |
count() | size_type | 无 | 返回结果数量 | O(1) |
odb::query 查询构建操作符表
操作符 | 语法 | 对应SQL | 说明 |
---|---|---|---|
== | query<T>::age == 25 | age = 25 | 等于比较 |
!= | query<T>::age != 25 | age != 25 | 不等于比较 |
< | query<T>::age < 25 | age < 25 | 小于比较 |
<= | query<T>::age <= 25 | age <= 25 | 小于等于 |
> | query<T>::age > 25 | age > 25 | 大于比较 |
>= | query<T>::age >= 25 | age >= 25 | 大于等于 |
&& | q1 && q2 | condition1 AND condition2 | 逻辑与 |
` | ` | `q1 | |
! | !query<T>::deleted | NOT deleted | 逻辑非 |
完整使用案例
案例1:基础查询操作
#include <odb/database.hxx>
#include <odb/query.hxx>
#include <odb/result.hxx>
#include <iostream>#pragma db object
class Person {
public:Person() {}Person(const std::string& name, int age, double salary) : name_(name), age_(age), salary_(salary) {}// Gettersconst std::string& name() const { return name_; }int age() const { return age_; }double salary() const { return salary_; }private:friend class odb::access;#pragma db id autounsigned long id_;std::string name_;int age_;double salary_;
};void basicQueryOperations(odb::database& db) {try {odb::transaction t(db.begin());// 1. 简单等值查询odb::result<Person> result1 = db.query<Person>(odb::query<Person>::name == "张三");std::cout << "找到 " << result1.size() << " 个名为张三的人" << std::endl;// 2. 范围查询odb::result<Person> result2 = db.query<Person>(odb::query<Person>::age >= 18 && odb::query<Person>::age <= 65);// 3. 多条件组合查询odb::result<Person> result3 = db.query<Person>((odb::query<Person>::age > 25 || odb::query<Person>::salary > 5000) &&odb::query<Person>::name != "测试用户");t.commit();} catch (const std::exception& e) {std::cerr << "查询错误: " << e.what() << std::endl;}
}
案例2:结果集遍历和处理
void resultSetProcessing(odb::database& db) {odb::transaction t(db.begin());// 执行查询odb::result<Person> people = db.query<Person>(odb::query<Person>::age > 20);std::cout << "查询结果数量: " << people.size() << std::endl;// 方法1: 使用迭代器遍历std::cout << "\n=== 迭代器遍历 ===" << std::endl;for (odb::result<Person>::iterator it = people.begin(); it != people.end(); ++it) {std::cout << "ID: " << it->id() << ", 姓名: " << it->name() << ", 年龄: " << it->age() << std::endl;}// 方法2: 基于范围的for循环 (C++11)std::cout << "\n=== 范围for循环 ===" << std::endl;for (const Person& person : people) {std::cout << "姓名: " << person.name() << ", 年龄: " << person.age() << ", 薪资: " << person.salary() << std::endl;}// 方法3: 使用front()获取第一个结果if (!people.empty()) {auto first_person = people.front();std::cout << "\n第一个结果: " << first_person->name() << std::endl;}t.commit();
}
案例3:高级查询功能
void advancedQueryFeatures(odb::database& db) {odb::transaction t(db.begin());try {// 1. LIKE 模糊查询odb::result<Person> like_result = db.query<Person>(odb::query<Person>::name.like("张%"));std::cout << "姓张的人数: " << like_result.size() << std::endl;// 2. IN 查询std::vector<int> ages = {20, 25, 30, 35};odb::result<Person> in_result = db.query<Person>(odb::query<Person>::age.in(ages.begin(), ages.end()));std::cout << "年龄在指定范围内的人数: " << in_result.size() << std::endl;// 3. BETWEEN 范围查询odb::result<Person> between_result = db.query<Person>(odb::query<Person>::salary.between(3000.0, 8000.0));std::cout << "薪资在3000-8000之间的人数: " << between_result.size() << std::endl;// 4. IS NULL 查询odb::result<Person> null_result = db.query<Person>(odb::query<Person>::name.is_null());std::cout << "姓名为空的人数: " << null_result.size() << std::endl;// 5. 排序查询odb::result<Person> sorted_result = db.query<Person>(odb::query<Person>::age > 18,"ORDER BY" + odb::query<Person>::age + "DESC," + odb::query<Person>::salary + "ASC");t.commit();} catch (const std::exception& e) {std::cerr << "高级查询错误: " << e.what() << std::endl;}
}
案例4:聚合查询和统计
#include <odb/query-dyn.hxx>void aggregateQueries(odb::database& db) {odb::transaction t(db.begin());try {// 1. 计数查询std::size_t count = db.query_value<std::size_t>("COUNT(*)",odb::query<Person>::age > 25);std::cout << "年龄大于25岁的人数: " << count << std::endl;// 2. 平均值查询double avg_salary = db.query_value<double>("AVG(" + odb::query<Person>::salary + ")",odb::query<Person>::age.between(25, 40));std::cout << "25-40岁平均薪资: " << avg_salary << std::endl;// 3. 最大值查询int max_age = db.query_value<int>("MAX(" + odb::query<Person>::age + ")",odb::query<Person>::salary > 0);std::cout << "最大年龄: " << max_age << std::endl;// 4. 分组查询struct AgeGroup {int age_group;std::size_t count;double avg_salary;#pragma db objectAgeGroup() {}};odb::result<AgeGroup> group_result = db.query<AgeGroup>("SELECT FLOOR(age/10)*10 AS age_group, ""COUNT(*) AS count, ""AVG(salary) AS avg_salary ""FROM Person ""GROUP BY FLOOR(age/10)*10 ""ORDER BY age_group");for (const AgeGroup& group : group_result) {std::cout << "年龄组 " << group.age_group << "+: " << group.count << "人, 平均薪资: " << group.avg_salary << std::endl;}t.commit();} catch (const std::exception& e) {std::cerr << "聚合查询错误: " << e.what() << std::endl;}
}
案例5:分页查询和性能优化
class PaginatedQuery {odb::database& db_;std::size_t page_size_;public:PaginatedQuery(odb::database& db, std::size_t page_size = 10) : db_(db), page_size_(page_size) {}void queryWithPagination(const std::string& search_name = "") {odb::transaction t(db_.begin());try {// 构建基础查询条件auto base_query = odb::query<Person>::age > 18;if (!search_name.empty()) {base_query = base_query && odb::query<Person>::name.like("%" + search_name + "%");}// 获取总记录数std::size_t total_count = db_.query_value<std::size_t>("COUNT(*)",base_query);std::size_t total_pages = (total_count + page_size_ - 1) / page_size_;std::cout << "总记录数: " << total_count << ", 总页数: " << total_pages << std::endl;// 分页查询for (std::size_t page = 0; page < total_pages; ++page) {std::size_t offset = page * page_size_;odb::result<Person> page_result = db_.query<Person>(base_query,"LIMIT " + std::to_string(page_size_) + " OFFSET " + std::to_string(offset));std::cout << "\n=== 第 " << (page + 1) << " 页 ===" << std::endl;for (const Person& person : page_result) {std::cout << "姓名: " << person.name() << ", 年龄: " << person.age() << std::endl;}}t.commit();} catch (const std::exception& e) {std::cerr << "分页查询错误: " << e.what() << std::endl;}}
};
案例6:动态查询构建
#include <odb/query-dyn.hxx>class DynamicQueryBuilder {
public:static odb::query<Person> buildDynamicQuery(const std::string& name_filter = "",int min_age = 0,int max_age = 0,double min_salary = 0.0) {odb::query<Person> query;// 动态添加姓名条件if (!name_filter.empty()) {query = query && odb::query<Person>::name.like("%" + name_filter + "%");}// 动态添加年龄条件if (min_age > 0) {query = query && odb::query<Person>::age >= min_age;}if (max_age > 0 && max_age >= min_age) {query = query && odb::query<Person>::age <= max_age;}// 动态添加薪资条件if (min_salary > 0.0) {query = query && odb::query<Person>::salary >= min_salary;}return query;}
};void dynamicQueryExample(odb::database& db) {odb::transaction t(db.begin());// 构建动态查询auto dynamic_query = DynamicQueryBuilder::buildDynamicQuery("张", // 姓名包含"张"25, // 最小年龄2540, // 最大年龄405000.0 // 最低薪资5000);// 执行动态查询odb::result<Person> result = db.query<Person>(dynamic_query);std::cout << "动态查询结果数量: " << result.size() << std::endl;for (const Person& person : result) {std::cout << person.name() << " - " << person.age() << "岁" << std::endl;}t.commit();
}
案例7:结果集转换和数据处理
#include <vector>
#include <algorithm>class ResultProcessor {
public:// 将result转换为vectorstatic std::vector<Person> toVector(odb::result<Person>& result) {std::vector<Person> vec;vec.reserve(result.size());for (const Person& person : result) {vec.push_back(person);}return vec;}// 处理结果集并返回统计信息static void analyzeResult(odb::result<Person>& result) {if (result.empty()) {std::cout << "结果集为空" << std::endl;return;}int min_age = std::numeric_limits<int>::max();int max_age = std::numeric_limits<int>::min();double total_salary = 0.0;std::size_t count = 0;for (const Person& person : result) {min_age = std::min(min_age, person.age());max_age = std::max(max_age, person.age());total_salary += person.salary();count++;}std::cout << "统计信息:" << std::endl;std::cout << " 记录数: " << count << std::endl;std::cout << " 年龄范围: " << min_age << " - " << max_age << std::endl;std::cout << " 平均薪资: " << (total_salary / count) << std::endl;}
};
性能优化提示表
优化技巧 | 说明 | 示例 |
---|---|---|
使用索引 | 为查询条件字段创建索引 | #pragma db index |
限制结果集 | 使用LIMIT避免返回大量数据 | query + "LIMIT 100" |
选择合适字段 | 只查询需要的字段 | 使用特定字段而非SELECT * |
批量处理 | 对大结果集进行分页处理 | 使用OFFSET和LIMIT |
连接池 | 使用连接池减少连接开销 | connection_pool_factory |
错误处理表
错误类型 | 原因 | 解决方法 |
---|---|---|
odb::result_not_unique | 期望单个结果但找到多个 | 使用query_one() 或检查查询条件 |
odb::object_not_persistent | 操作未持久化的对象 | 先调用persist() |
odb::database_exception | 数据库操作错误 | 检查SQL语法和权限 |
复杂多表查询操作
在 ODB 中,复合查询(多条件组合)和连接查询(多表关联)是处理复杂数据查询的核心能力。以下结合具体案例说明其用法,假设存在两个持久化类:Person
(人员)和 Address
(地址),其中 Person
与 Address
是一对一关联(一个人对应一个地址)。
一、复合查询(多条件组合)
复合查询通过逻辑运算符(&&
、||
、!
)组合多个条件,实现复杂筛选。
1. 核心用法
- 使用
odb::query<T>
模板类,通过类成员(如age_
、name_
)构建条件; - 支持
&&
(与)、||
(或)、!
(非)组合条件; - 支持
LIKE
(模糊匹配)、IN
(包含)、BETWEEN
(范围)等辅助操作符。
2. 案例:查询 “年龄 18-30 岁且姓名以‘张’开头的人”
#include <odb/query.hxx>
#include "person.hxx"
#include "person-odb.hxx"// 复合查询示例
void complex_query(odb::mysql::database& db) {odb::transaction t(db.begin());// 构建查询条件:年龄在18-30之间(BETWEEN)且姓名以"张"开头(LIKE)odb::query<Person> q((odb::query<Person>::age_ BETWEEN 18 AND 30) && // 年龄范围(odb::query<Person>::name_ LIKE "张%") // 姓名模糊匹配);// 执行查询odb::result<Person> res = db.query<Person>(q);// 遍历结果std::cout << "符合条件的人员:\n";for (const auto& p : res) {std::cout << "ID: " << p.id() << ", 姓名: " << p.name() << ", 年龄: " << p.age() << "\n";}t.commit();
}
-
生成的 SQL 类似:
SELECT * FROM Person WHERE age BETWEEN 18 AND 30 AND name LIKE '张%'
二、连接查询(多表关联)
连接查询用于关联多个表的数据(如 Person
和 Address
),需通过类之间的关联关系(如 #pragma db one
声明一对一)实现。
1. 前提:定义关联类
首先定义 Person
和 Address
类,并声明关联关系:
// address.hxx
#pragma db object
class Address {
public:#pragma db id autounsigned long id_;std::string city_; // 城市std::string street_; // 街道// ... 其他成员
};// person.hxx
#pragma db object
class Person {
public:#pragma db id autounsigned long id_;std::string name_;int age_;// 一对一关联:一个人对应一个地址#pragma db onestd::shared_ptr<Address> addr_; // 关联到Address// ... 其他成员
};
通过 ODB 工具生成映射文件(person-odb.hxx
、address-odb.hxx
)后,即可进行连接查询。
2. 核心用法
- 通过关联成员(如
Person::addr_
)访问关联表的字段; - 使用
odb::query
中的join
逻辑(ODB 自动生成关联条件); - 支持跨表条件组合(如筛选 “住在北京且年龄> 25 岁的人”)。
3. 案例:查询 “住在北京且年龄> 25 岁的人及其地址”
#include "person.hxx"
#include "address.hxx"
#include "person-odb.hxx"
#include "address-odb.hxx"// 连接查询示例
void join_query(odb::mysql::database& db) {odb::transaction t(db.begin());// 构建连接查询条件:// 1. 人员年龄>25岁(Person表)// 2. 关联的地址城市为"北京"(Address表)odb::query<Person> q((odb::query<Person>::age_ > 25) && (odb::query<Person>::addr_->city_ == "北京") // 跨表访问Address的city_);// 执行查询(结果包含Person及其关联的Address)odb::result<Person> res = db.query<Person>(q);// 遍历结果,同时访问关联的地址信息std::cout << "符合条件的人员及地址:\n";for (const auto& p : res) {std::cout << "姓名: " << p.name() << ", 年龄: " << p.age()<< ", 地址: " << p.addr_->city_ << p.addr_->street_ << "\n";}t.commit();
}
-
生成的 SQL 类似(自动关联Person.addr_id和Address.id):
SELECT * FROM Person JOIN Address ON Person.addr_id = Address.id WHERE Person.age > 25 AND Address.city = '北京'
三、高级用法:显式指定连接类型(左连接、右连接)
ODB 默认为内连接(INNER JOIN
),若需左连接(LEFT JOIN
),可通过原生 SQL 片段拼接实现:
// 左连接查询:所有人员,即使没有地址也会被查询出来
odb::query<Person> q = odb::query<Person>() + "LEFT JOIN Address ON Person.addr_id = Address.id ""WHERE Person.age < 30";odb::result<Person> res = db.query<Person>(q);
四、注意事项
- 关联关系必须声明:连接查询依赖类中的关联注解(
#pragma db one
/#pragma db many
),否则 ODB 无法生成关联条件。 - 性能优化:关联查询可能触发多表扫描,建议为关联字段(如
Person.addr_id
)创建索引(#pragma db index
)。 - 类型安全:ODB 通过模板检查跨表字段访问的合法性(如
addr_->city_
必须是Address
类的成员),编译期报错避免拼写错误。
老版本创建库
在 ODB 老版本中,若没有 odb::schema_catalog::create_table<Person>(db)
方法,可通过以下两种兼容方式创建表,核心是直接执行建表 SQL 语句(绕开 ODB 高版本的 schema 管理接口):
一、方法 1:执行 ODB 工具生成的建表 SQL(推荐,类型安全)
ODB 工具(odb
命令)可根据持久化类生成对应的 CREATE TABLE
语句,老版本也支持。步骤如下:
1. 用 ODB 工具生成建表 SQL
在终端执行以下命令,根据你的 Person
类头文件(如 person.hxx
)生成建表 SQL:
# 语法:odb -d 数据库类型 -s 持久化类头文件
odb -d mysql -s person.hxx
-
参数说明:
-
-d mysql
:指定数据库类型(生成 MySQL 语法的 SQL); -
-s
(--generate-schema
):生成建表 SQL 脚本; -
执行后会生成person.sql文件,内容为CREATE TABLE语句(如:
CREATE TABLE `Person` (`id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,`name` VARCHAR(255) NOT NULL,`age` INT NOT NULL ) ENGINE=InnoDB;
-
2. 在代码中读取并执行 SQL 文件
通过 C++ 读取 person.sql
内容,调用 database::execute
执行建表语句(老版本 ODB 均支持 execute
方法):
#include <fstream>
#include <string>
#include <odb/mysql/database.hxx>
#include <odb/transaction.hxx>
#include "person.hxx"
#include "person-odb.hxx"// 读取 SQL 文件内容
std::string read_sql(const std::string& path) {std::ifstream file(path);if (!file) throw std::runtime_error("无法打开 SQL 文件: " + path);return std::string((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
}// 创建表(老版本 ODB 兼容)
void create_person_table(odb::mysql::database& db) {odb::transaction t(db.begin());try {// 读取 ODB 生成的建表 SQLstd::string sql = read_sql("person.sql");// 执行 SQL 创建表db.execute(sql);std::cout << "Person 表创建成功!\n";t.commit();} catch (const std::exception& e) {std::cerr << "创建表失败: " << e.what() << "\n";t.rollback();}
}// 主函数中调用
int main() {try {// 连接数据库(假设已存在,或通过之前的方法创建)odb::mysql::database db("user", "password", "testdb");// 创建表create_person_table(db);} catch (const std::exception& e) {std::cerr << "错误: " << e.what() << "\n";}return 0;
}
二、方法 2:手动拼接建表 SQL(无 ODB 工具时用)
若无法使用 ODB 工具生成 SQL,可根据 Person
类的字段手动编写 CREATE TABLE
语句,直接通过 execute
执行:
void create_person_table_manual(odb::mysql::database& db) {odb::transaction t(db.begin());try {// 手动拼接建表 SQL(字段需与 Person 类完全匹配)std::string sql = "CREATE TABLE IF NOT EXISTS `Person` (""`id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, " // 自增主键"`name` VARCHAR(255) NOT NULL, " // 对应 std::string name_"`age` INT NOT NULL" // 对应 int age_") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"; // 字符集避免中文乱码db.execute(sql); // 执行手动编写的 SQLstd::cout << "Person 表创建成功(手动方式)!\n";t.commit();} catch (const std::exception& e) {std::cerr << "创建表失败: " << e.what() << "\n";t.rollback();}
}
关键注意:
- 表名、字段名需与
person-odb.hxx
中定义的映射一致(如Person
类映射的表名是Person
,字段是id
、name
、age
); - 字段类型需匹配(如
id
对应BIGINT UNSIGNED
,name
对应VARCHAR
或TEXT
); - 加
IF NOT EXISTS
避免重复创建时抛错(MySQL 错误码 1050)。