目录
框架简介
ODB(Object-Relational Database)是一个专为C++设计的对象关系映射(ORM)框架,由CodeSynthesis公司开发。它提供了一种现代化的方式来处理C++应用程序中的数据库操作,将复杂的SQL操作抽象为简单的C++对象操作。
主要特性
- 类型安全:编译时类型检查,避免运行时错误
- 高性能:零开销抽象,接近原生SQL性能
- 多数据库支持:MySQL、PostgreSQL、SQLite、Oracle、SQL Server
- 自动代码生成:基于C++类自动生成数据库访问代码
- 事务支持:完整的ACID事务处理
- 查询语言:类型安全的查询DSL
- 模式演化:数据库模式版本管理
架构概述
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 应用程序 │ │ ODB编译器 │ │ 数据库 │
│ │ │ │ │ │
│ C++对象模型 │◄──►│ 代码生成器 │◄──►│ 关系模型 │
│ │ │ │ │ │
│ 业务逻辑 │ │ SQL映射 │ │ 数据存储 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
安装与配置
系统要求
- C++11或更高版本编译器
- 支持的数据库客户端库
- CMake 3.5+(推荐)
安装步骤
1. 下载ODB编译器
# Ubuntu/Debian
sudo apt-get install odb libodb-dev
# CentOS/RHEL
sudo yum install odb libodb-devel
# 或从源码编译
wget https://www.codesynthesis.com/download/odb/2.4/odb-2.4.0.tar.gz
tar -xzf odb-2.4.0.tar.gz
cd odb-2.4.0
./configure --prefix=/usr/local
make && sudo make install
2. 安装数据库特定库
# MySQL支持
sudo apt-get install libodb-mysql-dev
# PostgreSQL支持
sudo apt-get install libodb-pgsql-dev
# SQLite支持
sudo apt-get install libodb-sqlite-dev
3. CMake配置
cmake_minimum_required(VERSION 3.5)
project(ODBExample)
set(CMAKE_CXX_STANDARD 11)
# 查找ODB库
find_package(PkgConfig REQUIRED)
pkg_check_modules(ODB REQUIRED libodb)
pkg_check_modules(ODB_MYSQL REQUIRED libodb-mysql)
# 设置包含目录和链接库
include_directories(${ODB_INCLUDE_DIRS} ${ODB_MYSQL_INCLUDE_DIRS})
link_directories(${ODB_LIBRARY_DIRS} ${ODB_MYSQL_LIBRARY_DIRS})
# 添加可执行文件
add_executable(example main.cpp person.cxx person-odb.cxx)
target_link_libraries(example ${ODB_LIBRARIES} ${ODB_MYSQL_LIBRARIES})
# ODB代码生成规则
add_custom_command(
OUTPUT person-odb.hxx person-odb.ixx person-odb.cxx
COMMAND odb --database mysql --generate-query --generate-schema person.hxx
DEPENDS person.hxx
COMMENT "Generating ODB files"
)
基础概念
持久化类
在ODB中,需要持久化到数据库的C++类称为持久化类。通过pragma指令标记:
#include <odb/core.hxx>
#include <string>
#pragma db object
class Person {
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string first_name_;
std::string last_name_;
unsigned short age_;
public:
Person() = default;
Person(const std::string& first, const std::string& last, unsigned short age)
: first_name_(first), last_name_(last), age_(age) {}
// Getters
unsigned long id() const { return id_; }
const std::string& first_name() const { return first_name_; }
const std::string& last_name() const { return last_name_; }
unsigned short age() const { return age_; }
// Setters
void first_name(const std::string& name) { first_name_ = name; }
void last_name(const std::string& name) { last_name_ = name; }
void age(unsigned short a) { age_ = a; }
};
数据库连接
#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include <memory>
std::unique_ptr<odb::database> create_database() {
return std::make_unique<odb::mysql::database>(
"user", // 用户名
"password", // 密码
"database_name", // 数据库名
"localhost", // 主机
3306, // 端口
nullptr, // socket
"utf8" // 字符集
);
}
基本CRUD操作
#include "person.hxx"
#include "person-odb.hxx"
#include <odb/transaction.hxx>
void basic_operations() {
auto db = create_database();
// 创建表结构
{
odb::transaction t(db->begin());
db->execute("DROP TABLE IF EXISTS Person");
db->execute(R"(
CREATE TABLE Person (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
age SMALLINT UNSIGNED NOT NULL
)
)");
t.commit();
}
// 插入数据
{
odb::transaction t(db->begin());
Person john("John", "Doe", 30);
Person jane("Jane", "Smith", 25);
db->persist(john);
db->persist(jane);
t.commit();
std::cout << "John ID: " << john.id() << std::endl;
std::cout << "Jane ID: " << jane.id() << std::endl;
}
// 查询数据
{
odb::transaction t(db->begin());
std::unique_ptr<Person> p(db->load<Person>(1));
std::cout << "Loaded: " << p->first_name() << " " << p->last_name() << std::endl;
t.commit();
}
// 更新数据
{
odb::transaction t(db->begin());
std::unique_ptr<Person> p(db->load<Person>(1));
p->age(31);
db->update(*p);
t.commit();
}
// 删除数据
{
odb::transaction t(db->begin());
db->erase<Person>(2);
t.commit();
}
}
#### 多对多关系
```cpp
#pragma db object
class Student {
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string name_;
#pragma db many_to_many("student_course")
std::vector<odb::lazy_shared_ptr<Course>> courses_;
public:
// 构造函数和访问器...
};
#pragma db object
class Course {
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string title_;
#pragma db many_to_many("student_course") inverse(courses_)
std::vector<odb::lazy_weak_ptr<Student>> students_;
public:
// 构造函数和访问器...
};
继承映射
#pragma db object polymorphic
class Animal {
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string name_;
public:
virtual ~Animal() = default;
// 构造函数和访问器...
};
#pragma db object
class Dog : public Animal {
private:
friend class odb::access;
std::string breed_;
public:
// 构造函数和访问器...
};
#pragma db object
class Cat : public Animal {
private:
friend class odb::access;
bool indoor_;
public:
// 构造函数和访问器...
};
数据库操作
事务管理
#include <odb/transaction.hxx>
void transaction_example() {
auto db = create_database();
try {
odb::transaction t(db->begin());
// 批量操作
for (int i = 0; i < 100; ++i) {
Person p("User" + std::to_string(i), "Test", 20 + i % 50);
db->persist(p);
}
t.commit();
std::cout << "Transaction committed successfully" << std::endl;
}
catch (const odb::exception& e) {
std::cerr << "Database error: " << e.what() << std::endl;
// 事务会自动回滚
}
}
// 嵌套事务
void nested_transaction_example() {
auto db = create_database();
odb::transaction outer(db->begin());
try {
Person p1("Alice", "Johnson", 28);
db->persist(p1);
// 保存点
odb::transaction inner(db->begin());
try {
Person p2("Bob", "Wilson", 35);
db->persist(p2);
inner.commit();
}
catch (...) {
// inner事务回滚,但outer事务继续
}
outer.commit();
}
catch (...) {
// outer事务回滚
}
}
批量操作
void batch_operations() {
auto db = create_database();
// 批量插入
{
odb::transaction t(db->begin());
std::vector<Person> people;
for (int i = 0; i < 1000; ++i) {
people.emplace_back("User" + std::to_string(i), "Batch", 20 + i % 50);
}
for (auto& person : people) {
db->persist(person);
}
t.commit();
}
// 批量更新
{
odb::transaction t(db->begin());
db->execute("UPDATE Person SET age = age + 1 WHERE age < 30");
t.commit();
}
// 批量删除
{
odb::transaction t(db->begin());
db->erase_query<Person>(odb::query<Person>::age > 65);
t.commit();
}
}
查询操作
基础查询
#include "person-odb.hxx"
void basic_queries() {
auto db = create_database();
odb::transaction t(db->begin());
// 简单查询
typedef odb::query<Person> query;
typedef odb::result<Person> result;
// 按年龄查询
result r(db->query<Person>(query::age >= 25 && query::age <= 35));
for (const auto& person : r) {
std::cout << person.first_name() << " " << person.last_name()
<< " (age: " << person.age() << ")" << std::endl;
}
// 按姓名查询
result r2(db->query<Person>(query::first_name == "John"));
// 模糊查询
result r3(db->query<Person>(query::last_name.like("%son")));
// 排序查询
result r4(db->query<Person>(query::age > 20 + "ORDER BY" + query::age));
t.commit();
}
高级查询
void advanced_queries() {
auto db = create_database();
odb::transaction t(db->begin());
typedef odb::query<Person> query;
typedef odb::result<Person> result;
// 参数化查询
std::string name_pattern = "J%";
int min_age = 25;
result r(db->query<Person>(
query::first_name.like(query::_ref(name_pattern)) &&
query::age >= query::_val(min_age)
));
// 聚合查询
typedef odb::query<PersonStat> stat_query;
typedef odb::result<PersonStat> stat_result;
#pragma db view object(Person)
struct PersonStat {
#pragma db column("count(*)")
std::size_t count;
#pragma db column("avg(age)")
double avg_age;
#pragma db column("min(age)")
unsigned short min_age;
#pragma db column("max(age)")
unsigned short max_age;
};
stat_result sr(db->query<PersonStat>());
const PersonStat& stat = *sr.begin();
std::cout << "Total persons: " << stat.count << std::endl;
std::cout << "Average age: " << stat.avg_age << std::endl;
// 连接查询
typedef odb::query<PersonAddress> pa_query;
typedef odb::result<PersonAddress> pa_result;
#pragma db view object(Person) object(Address)
struct PersonAddress {
#pragma db column(Person::first_name_)
std::string first_name;
#pragma db column(Person::last_name_)
std::string last_name;
#pragma db column(Address::city_)
std::string city;
};
pa_result par(db->query<PersonAddress>(
pa_query::Person::address == pa_query::Address::id &&
pa_query::Address::city == "New York"
));
t.commit();
}
延迟加载
void lazy_loading_example() {
auto db = create_database();
// 保存数据
{
odb::transaction t(db->begin());
auto dept = std::make_shared<Department>("Engineering");
db->persist(*dept);
Employee emp1("Alice", dept);
Employee emp2("Bob", dept);
db->persist(emp1);
db->persist(emp2);
t.commit();
}
// 延迟加载
{
odb::transaction t(db->begin());
std::unique_ptr<Employee> emp(db->load<Employee>(1));
// 此时department_还未加载
std::cout << "Employee: " << emp->name() << std::endl;
// 访问时才加载
if (emp->department().loaded()) {
std::cout << "Department already loaded" << std::endl;
} else {
std::cout << "Loading department..." << std::endl;
auto dept = emp->department().load();
std::cout << "Department: " << dept->name() << std::endl;
}
t.commit();
}
}
高级功能
视图(Views)
// 简单视图
#pragma db view object(Person)
struct PersonView {
#pragma db column(Person::first_name_ + " " + Person::last_name_)
std::string full_name;
#pragma db column(Person::age_)
unsigned short age;
};
// 复杂视图
#pragma db view query("SELECT p.first_name, p.last_name, a.city " \
"FROM Person p " \
"LEFT JOIN Address a ON p.address_id = a.id " \
"WHERE p.age > (?) AND a.city IS NOT NULL")
struct PersonCityView {
std::string first_name;
std::string last_name;
std::string city;
};
void view_example() {
auto db = create_database();
odb::transaction t(db->begin());
// 使用简单视图
typedef odb::result<PersonView> view_result;
view_result vr(db->query<PersonView>());
for (const auto& pv : vr) {
std::cout << pv.full_name << " (age: " << pv.age << ")" << std::endl;
}
// 使用参数化视图
typedef odb::result<PersonCityView> city_result;
city_result cr(db->query<PersonCityView>(25));
for (const auto& pcv : cr) {
std::cout << pcv.first_name << " " << pcv.last_name
<< " lives in " << pcv.city << std::endl;
}
t.commit();
}
回调函数
#pragma db object callback(db_callback)
class Person {
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string first_name_;
std::string last_name_;
unsigned short age_;
#pragma db transient
mutable std::string cached_full_name_;
public:
// 构造函数和访问器...
// 回调函数
void db_callback(odb::callback_event e, odb::database& db) const {
switch (e) {
case odb::callback_event::pre_persist:
std::cout << "About to persist: " << first_name_ << std::endl;
break;
case odb::callback_event::post_persist:
std::cout << "Persisted with ID: " << id_ << std::endl;
break;
case odb::callback_event::pre_load:
cached_full_name_.clear();
break;
case odb::callback_event::post_load:
cached_full_name_ = first_name_ + " " + last_name_;
std::cout << "Loaded: " << cached_full_name_ << std::endl;
break;
case odb::callback_event::pre_update:
std::cout << "About to update: " << first_name_ << std::endl;
break;
case odb::callback_event::post_update:
std::cout << "Updated: " << first_name_ << std::endl;
break;
case odb::callback_event::pre_erase:
std::cout << "About to erase: " << first_name_ << std::endl;
break;
}
}
};
模式演化
// 版本1的Person类
#pragma db object table("person_v1")
class PersonV1 {
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string name_;
unsigned short age_;
};
// 版本2的Person类 - 添加了email字段
#pragma db object table("person_v2")
class PersonV2 {
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string first_name_;
std::string last_name_;
unsigned short age_;
#pragma db null
std::string email_;
};
// 数据迁移
void migrate_schema() {
auto db = create_database();
odb::transaction t(db->begin());
// 创建新表
db->execute(R"(
CREATE TABLE person_v2 (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
age SMALLINT UNSIGNED NOT NULL,
email VARCHAR(255)
)
)");
// 迁移数据
db->execute(R"(
INSERT INTO person_v2 (id, first_name, last_name, age)
SELECT id,
SUBSTRING_INDEX(name, ' ', 1) as first_name,
SUBSTRING_INDEX(name, ' ', -1) as last_name,
age
FROM person_v1
)");
// 删除旧表
db->execute("DROP TABLE person_v1");
t.commit();
}
性能优化
连接池
#include <odb/connection-pool-factory.hxx>
class DatabaseManager {
private:
std::unique_ptr<odb::database> db_;
public:
DatabaseManager() {
// 创建连接池工厂
std::unique_ptr<odb::connection_pool_factory> pool_factory(
new odb::connection_pool_factory(10, 0) // 最大10个连接,无最小连接数
);
db_ = std::make_unique<odb::mysql::database>(
"user", "password", "database", "localhost", 3306,
nullptr, "utf8", 0, std::move(pool_factory)
);
}
odb::database& get_database() { return *db_; }
};
// 单例模式
DatabaseManager& get_db_manager() {
static DatabaseManager manager;
return manager;
}
预编译语句
void prepared_statement_example() {
auto db = create_database();
// 预编译查询
typedef odb::query<Person> query;
typedef odb::prepared_query<Person> prepared_query;
odb::transaction t(db->begin());
// 准备查询语句
prepared_query pq(
db->prepare_query<Person>("find_by_age",
query::age >= query::_ref(age_param))
);
// 多次执行
for (int age = 20; age <= 30; ++age) {
int age_param = age;
odb::result<Person> r(pq.execute());
std::cout << "People aged " << age << ":" << std::endl;
for (const auto& person : r) {
std::cout << " " << person.first_name() << " " << person.last_name() << std::endl;
}
}
t.commit();
}
批量操作优化
void optimized_batch_operations() {
auto db = create_database();
// 使用事务批量插入
const size_t batch_size = 1000;
std::vector<Person> people;
// 准备数据
for (size_t i = 0; i < 10000; ++i) {
people.emplace_back("User" + std::to_string(i), "Test", 20 + i % 50);
}
// 分批插入
for (size_t i = 0; i < people.size(); i += batch_size) {
odb::transaction t(db->begin());
size_t end = std::min(i + batch_size, people.size());
for (size_t j = i; j < end; ++j) {
db->persist(people[j]);
}
t.commit();
std::cout << "Inserted batch " << (i / batch_size + 1) << std::endl;
}
}
查询优化
void query_optimization() {
auto db = create_database();
odb::transaction t(db->begin());
typedef odb::query<Person> query;
typedef odb::result<Person> result;
// 使用索引
result r1(db->query<Person>(
query::last_name == "Smith" + "ORDER BY" + query::first_name
));
// 限制结果集大小
result r2(db->query<Person>(
query::age > 25 + "LIMIT 100"
));
// 使用缓存
result r3(db->query<Person>(query::age >= 30));
std::vector<Person> cached_results(r3.begin(), r3.end());
// 延迟加载优化
typedef odb::query<Employee> emp_query;
typedef odb::result<Employee> emp_result;
emp_result er(db->query<Employee>(emp_query::department->name == "Engineering"));
for (auto& emp : er) {
// 批量加载关联对象
if (!emp.department().loaded()) {
emp.department().load();
}
}
t.commit();
}
最佳实践
1. 项目结构
project/
├── src/
│ ├── models/ # 实体类
│ │ ├── person.hxx
│ │ ├── department.hxx
│ │ └── employee.hxx
│ ├── dao/ # 数据访问对象
│ │ ├── person_dao.hxx
│ │ └── person_dao.cxx
│ ├── services/ # 业务逻辑
│ │ ├── person_service.hxx
│ │ └── person_service.cxx
│ └── main.cxx
├── generated/ # ODB生成的文件
│ ├── person-odb.hxx
│ ├── person-odb.ixx
│ └── person-odb.cxx
├── sql/ # SQL脚本
│ ├── schema.sql
│ └── migrations/
└── CMakeLists.txt
2. DAO模式实现
// person_dao.hxx
#pragma once
#include "models/person.hxx"
#include <odb/database.hxx>
#include <memory>
#include <vector>
class PersonDAO {
private:
std::shared_ptr<odb::database> db_;
public:
explicit PersonDAO(std::shared_ptr<odb::database> db) : db_(db) {}
// CRUD操作
void create(Person& person);
std::unique_ptr<Person> find_by_id(unsigned long id);
std::vector<Person> find_by_age_range(unsigned short min_age, unsigned short max_age);
void update(const Person& person);
void remove(unsigned long id);
// 统计操作
size_t count_all();
double average_age();
};
// person_dao.cxx
#include "dao/person_dao.hxx"
#include "generated/person-odb.hxx"
#include <odb/transaction.hxx>
void PersonDAO::create(Person& person) {
odb::transaction t(db_->begin());
db_->persist(person);
t.commit();
}
std::unique_ptr<Person> PersonDAO::find_by_id(unsigned long id) {
odb::transaction t(db_->begin());
auto result = db_->load<Person>(id);
t.commit();
return result;
}
std::vector<Person> PersonDAO::find_by_age_range(unsigned short min_age, unsigned short max_age) {
odb::transaction t(db_->begin());
typedef odb::query<Person> query;
typedef odb::result<Person> result;
result r(db_->query<Person>(
query::age >= min_age && query::age <= max_age
));
std::vector<Person> people(r.begin(), r.end());
t.commit();
return people;
}
void PersonDAO::update(const Person& person) {
odb::transaction t(db_->begin());
db_->update(person);
t.commit();
}
void PersonDAO::remove(unsigned long id) {
odb::transaction t(db_->begin());
db_->erase<Person>(id);
t.commit();
}
3. 服务层实现
// person_service.hxx
#pragma once
#include "dao/person_dao.hxx"
#include <string>
class PersonService {
private:
std::unique_ptr<PersonDAO> dao_;
public:
explicit PersonService(std::shared_ptr<odb::database> db)
: dao_(std::make_unique<PersonDAO>(db)) {}
// 业务方法
unsigned long register_person(const std::string& first_name,
const std::string& last_name,
unsigned short age);
bool update_person_age(unsigned long id, unsigned short new_age);
std::vector<Person> get_adults();
bool delete_person(unsigned long id);
// 统计方法
size_t get_total_count();
double get_average_age();
};
// person_service.cxx
#include "services/person_service.hxx"
unsigned long PersonService::register_person(const std::string& first_name,
const std::string& last_name,
unsigned short age) {
if (first_name.empty() || last_name.empty()) {
throw std::invalid_argument("Name cannot be empty");
}
if (age > 150) {
throw std::invalid_argument("Invalid age");
}
Person person(first_name, last_name, age);
dao_->create(person);
return person.id();
}
bool PersonService::update_person_age(unsigned long id, unsigned short new_age) {
try {
auto person = dao_->find_by_id(id);
if (!person) {
return false;
}
person->age(new_age);
dao_->update(*person);
return true;
}
catch (const odb::exception&) {
return false;
}
}
std::vector<Person> PersonService::get_adults() {
return dao_->find_by_age_range(18, 150);
}
4. 错误处理
#include <odb/exception.hxx>
void error_handling_example() {
try {
auto db = create_database();
odb::transaction t(db->begin());
// 数据库操作
Person person("John", "Doe", 30);
db->persist(person);
t.commit();
}
catch (const odb::database_exception& e) {
std::cerr << "Database error: " << e.what() << std::endl;
std::cerr << "Database message: " << e.message() << std::endl;
}
catch (const odb::connection_lost& e) {
std::cerr << "Connection lost: " << e.what() << std::endl;
// 实现重连逻辑
}
catch (const odb::timeout& e) {
std::cerr << "Operation timeout: " << e.what() << std::endl;
}
catch (const odb::object_not_persistent& e) {
std::cerr << "Object not persistent: " << e.what() << std::endl;
}
catch (const odb::object_already_persistent& e) {
std::cerr << "Object already persistent: " << e.what() << std::endl;
}
catch (const odb::exception& e) {
std::cerr << "ODB error: " << e.what() << std::endl;
}
catch (const std::exception& e) {
std::cerr << "Standard error: " << e.what() << std::endl;
}
}
5. 配置管理
// config.hxx
#pragma once
#include <string>
struct DatabaseConfig {
std::string host = "localhost";
unsigned int port = 3306;
std::string database = "test";
std::string user = "root";
std::string password = "";
std::string charset = "utf8";
unsigned int pool_size = 10;
unsigned int timeout = 30;
};
class ConfigManager {
private:
DatabaseConfig db_config_;
public:
void load_from_file(const std::string& filename);
void load_from_env();
const DatabaseConfig& get_db_config() const { return db_config_; }
};
// 使用配置
std::unique_ptr<odb::database> create_configured_database() {
ConfigManager config;
config.load_from_file("config.json");
const auto& db_config = config.get_db_config();
std::unique_ptr<odb::connection_pool_factory> pool_factory(
new odb::connection_pool_factory(db_config.pool_size, 0)
);
return std::make_unique<odb::mysql::database>(
db_config.user,
db_config.password,
db_config.database,
db_config.host,
db_config.port,
nullptr,
db_config.charset,
0,
std::move(pool_factory)
);
}
总结
ODB框架为C++开发者提供了一个强大而灵活的ORM解决方案。它的主要优势包括:
- 类型安全:编译时检查,减少运行时错误
- 高性能:接近原生SQL的性能表现
- 多数据库支持:支持主流关系数据库
- 现代C++特性:充分利用C++11/14/17特性
- 灵活的映射:支持复杂的对象关系映射
在实际项目中使用ODB时,建议:
- 合理设计实体类和关系映射
- 使用DAO模式组织数据访问代码
- 实现适当的错误处理和事务管理
- 利用连接池和预编译语句优化性能
- 遵循最佳实践,保持代码的可维护性
通过正确使用ODB框架,可以显著提高C++应用程序的数据库操作效率和代码质量。