目录
- 框架简介
- 安装与配置
- 基础概念
- 实体映射
- 数据库操作
- 查询操作
- 高级功能
- 性能优化
- 最佳实践
框架简介
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.cxxCOMMAND odb --database mysql --generate-query --generate-schema person.hxxDEPENDS person.hxxCOMMENT "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 autounsigned 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) {}// Gettersunsigned 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_; }// Settersvoid 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 autounsigned 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 autounsigned 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 autounsigned 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 autounsigned long id_;std::string first_name_;std::string last_name_;unsigned short age_;#pragma db transientmutable 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 autounsigned 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 autounsigned long id_;std::string first_name_;std::string last_name_;unsigned short age_;#pragma db nullstd::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,ageFROM 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++应用程序的数据库操作效率和代码质量。