C++ ODB ORM 从入门到实战应用(ODB使用)

 更新时间:2025年12月01日 11:14:05   作者:编程小Y  
ODB 作为 C++ 的高效 ORM 框架,通过编译期代码生成实现了对象与数据库的无缝映射,既保留了 C++ 的性能优势,又简化了数据库操作,本文从基础概念到实战开发,全面讲解ODB的使用,感兴趣的朋友一起看看吧

ODB(Object-Relational Mapping)是 C++ 领域成熟的 ORM 框架,由 Code Synthesis 开发,能将 C++ 对象与关系型数据库(如 MySQL、PostgreSQL、SQLite)无缝映射,避免手动编写 SQL 语句,大幅提升数据库开发效率。本文从基础概念到实战开发,全面讲解 ODB 的使用。

一、ODB 核心概念与环境准备

1. ORM 与 ODB 简介

ORM(对象关系映射)的核心是 “对象 - 表映射”:C++ 类对应数据库表,类成员对应表字段,对象实例对应表中的行。ODB 作为 C++ 专属 ORM,具备以下特性:

  • 支持主流数据库(MySQL、PostgreSQL、SQLite、Oracle 等);
  • 编译期代码生成,无运行时开销;
  • 支持事务、关联映射(一对一、一对多)、继承映射等高级特性;
  • 轻量级,仅需链接 ODB 库,无额外依赖。

2. 环境搭建

(1)安装 ODB 工具链

  • 下载 ODB 编译器:从Code Synthesis 官网下载对应平台的 ODB 编译器(odb命令行工具);
  • 安装数据库驱动:如 MySQL 需安装libmysqlclient,PostgreSQL 需安装libpq
  • 配置 C++ 编译环境:确保编译器支持 C++11 及以上(如 GCC、Clang、MSVC)。

(2)项目依赖

  • 链接 ODB 核心库(odb)和对应数据库驱动库(如odb-mysqlodb-sqlite);
  • 包含 ODB 头文件路径(#include <odb/database.hxx>等)。

二、ODB 入门:简单对象映射

1. 定义持久化类

首先定义一个 C++ 类,通过 ODB 注解标记为 “持久化类”,并指定表名、字段属性:

// person.hxx
#include <string>
#include <odb/core.hxx> // ODB核心头文件
// 标记为持久化类,对应数据库表"person"
#pragma db object table("person")
class Person
{
public:
    Person() {} // 必须提供默认构造函数
    Person(const std::string& name, int age, const std::string& email)
        : name_(name), age_(age), email_(email) {}
    // 访问器方法
    const std::string& name() const { return name_; }
    int age() const { return age_; }
    const std::string& email() const { return email_; }
private:
    // 主键(auto表示自增)
    #pragma db id auto
    unsigned long id_;
    // 普通字段(可指定列名、约束,如not_null)
    #pragma db column("name") not_null
    std::string name_;
    #pragma db column("age")
    int age_;
    #pragma db column("email") unique // 唯一约束
    std::string email_;
    // ODB需要访问私有成员,声明友元
    friend class odb::access;
};

2. 生成数据库访问代码

ODB 通过编译器(odb命令)分析注解,生成持久化类的数据库操作代码:

# 针对MySQL生成代码,输出person-odb.hxx和person-odb.cxx
odb -d mysql --generate-query --generate-schema person.hxx
  • -d mysql:指定数据库类型(可选sqlitepgsql等);
  • --generate-query:生成查询支持代码;
  • --generate-schema:生成建表 SQL 语句。

3. 基础数据库操作(CRUD)

(1)初始化数据库连接

#include <odb/database.hxx>
#include <odb/mysql/database.hxx> // MySQL驱动
// 创建数据库连接(MySQL示例)
std::unique_ptr<odb::database> db(
    new odb::mysql::database(
        "root",      // 用户名
        "password",  // 密码
        "test_db",   // 数据库名
        "localhost", // 主机
        3306         // 端口
    )
);

(2)插入数据(Create)

#include "person-odb.hxx" // 生成的代码
Person p("Alice", 25, "alice@example.com");
odb::transaction t(db->begin()); // 开启事务
db->persist(p); // 插入对象到数据库
t.commit();     // 提交事务

(3)查询数据(Read)

主键查询

odb::transaction t(db->begin());
std::shared_ptr<Person> p = db->load<Person>(1); // 根据主键1查询
std::cout << "Name: " << p->name() << ", Age: " << p->age() << std::endl;
t.commit();

条件查询

#include <odb/query.hxx>
#include <odb/session.hxx>
odb::session s; // 查询需开启session
odb::transaction t(db->begin());
// 构建查询:age > 20 且 name LIKE 'A%'
odb::query<Person> q(
    odb::query<Person>::age > 20 &&
    odb::query<Person>::name.like("A%")
);
// 执行查询并遍历结果
for (auto& p : db->query<Person>(q))
    std::cout << p.name() << ": " << p.email() << std::endl;
t.commit();

(4)更新数据(Update)

odb::transaction t(db->begin());
std::shared_ptr<Person> p = db->load<Person>(1);
p->age_ = 26; // 修改对象(需将age_改为public或提供setter)
db->update(*p); // 更新数据库
t.commit();

(5)删除数据(Delete)

odb::transaction t(db->begin());
db->erase<Person>(1); // 根据主键删除
t.commit();

三、ODB 进阶特性

1. 关联映射

(1)一对一关联(One-to-One)

例如Person关联Address

// address.hxx
#pragma db object
class Address
{
    // ... 字段定义(主键id_,street_,city_等)
};
// person.hxx中添加关联
#pragma db object
class Person
{
    // ... 其他字段
    #pragma db one_to_one // 一对一关联
    odb::lazy_ptr<Address> address_; // 懒加载(访问时才查询)
};

(2)一对多关联(One-to-Many)

例如Department包含多个Employee

// employee.hxx
#pragma db object
class Employee
{
    // ... 字段
    #pragma db many_to_one // 多对一(反向关联)
    odb::ptr<Department> dept_;
};
// department.hxx
#pragma db object
class Department
{
    // ... 字段
    #pragma db one_to_many(mapped_by = "dept_") // 一对多,指定反向关联字段
    std::vector<odb::ptr<Employee>> employees_;
};

2. 继承映射

ODB 支持类继承的表映射,分为三种策略:

  • 单表策略:所有子类映射到同一张表(通过discriminator区分);
  • Joined 策略:父类和子类分别映射到不同表,查询时关联;
  • Table-per-class 策略:每个子类映射到独立表。

示例(单表策略):

#pragma db object abstract // 抽象父类
class User
{
    #pragma db id auto
    unsigned long id_;
    std::string username_;
};
#pragma db object
class AdminUser : public User
{
    std::string role_;
};
#pragma db object
class NormalUser : public User
{
    std::string avatar_;
};

3. 事务与并发

ODB 支持事务的 ACID 特性,可通过odb::transaction控制:

odb::transaction t(db->begin());
try
{
    // 批量操作
    db->persist(p1);
    db->persist(p2);
    t.commit();
}
catch (const odb::exception& e)
{
    t.rollback(); // 异常时回滚
    std::cerr << "Transaction failed: " << e.what() << std::endl;
}

四、实战案例:用户管理系统

1. 需求分析

实现一个简单的用户管理系统,支持用户的增删改查、角色关联(用户 - 角色一对多)。

2. 核心代码实现

(1)定义持久化类

// role.hxx
#pragma db object
class Role
{
public:
    Role(const std::string& name) : name_(name) {}
private:
    friend class odb::access;
    Role() {}
    #pragma db id auto
    unsigned long id_;
    #pragma db not_null unique
    std::string name_; // 角色名:admin, user
};
// user.hxx
#pragma db object
class User
{
public:
    User(const std::string& name, int age) : name_(name), age_(age) {}
private:
    friend class odb::access;
    User() {}
    #pragma db id auto
    unsigned long id_;
    #pragma db not_null
    std::string name_;
    int age_;
    #pragma db many_to_many // 多对多关联角色
    std::vector<odb::ptr<Role>> roles_;
};

(2)生成代码并编译

odb -d sqlite --generate-query --generate-schema role.hxx user.hxx
g++ -c user-odb.cxx role-odb.cxx main.cxx -I/path/to/odb/include -L/path/to/odb/lib -lodb -lodb-sqlite

(3)业务逻辑实现

#include <odb/sqlite/database.hxx>
#include "user-odb.hxx"
#include "role-odb.hxx"
int main()
{
    // SQLite数据库(文件test.db)
    std::unique_ptr<odb::database> db(new odb::sqlite::database("test.db"));
    // 1. 创建角色
    odb::transaction t1(db->begin());
    db->persist(Role("admin"));
    db->persist(Role("user"));
    t1.commit();
    // 2. 创建用户并关联角色
    odb::transaction t2(db->begin());
    auto admin_role = db->query_one<Role>(odb::query<Role>::name == "admin");
    User u("Bob", 30);
    u.roles_.push_back(admin_role);
    db->persist(u);
    t2.commit();
    // 3. 查询用户及其角色
    odb::session s;
    odb::transaction t3(db->begin());
    auto user = db->load<User>(1);
    std::cout << "User: " << user->name_ << ", Roles: ";
    for (auto& r : user->roles_)
        std::cout << r->name_ << " ";
    t3.commit();
    return 0;
}

五、ODB 最佳实践与注意事项

  1. 避免过度封装:简单查询直接使用 ODB 的query接口,复杂 SQL 可通过odb::queryraw()方法嵌入原生 SQL;
  2. 懒加载优化:关联对象默认懒加载,批量查询时可通过odb::join主动关联,减少数据库请求;
  3. 性能考量:编译期生成代码无运行时开销,但频繁的对象持久化需注意事务批量处理;
  4. 数据库迁移:使用--generate-schema生成的 SQL 文件管理表结构,配合版本控制实现迁移;
  5. 异常处理:捕获odb::exception及其子类(如odb::sql_exception),处理数据库错误。

六、总结

ODB 作为 C++ 的高效 ORM 框架,通过编译期代码生成实现了对象与数据库的无缝映射,既保留了 C++ 的性能优势,又简化了数据库操作。从简单的 CRUD 到复杂的关联映射、事务管理,ODB 都能提供优雅的解决方案,是 C++ 后端开发中替代原生 SQL 的理想选择。掌握 ODB 的核心用法,可大幅提升项目的开发效率和可维护性。

到此这篇关于C++ ODB ORM 从入门到实战应用的文章就介绍到这了,更多相关C++ ODB ORM 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中list的使用方法及常用list操作总结

    C++中list的使用方法及常用list操作总结

    这篇文章主要介绍了C++中list的使用方法及常用list操作总结的相关资料,需要的朋友可以参考下
    2017-06-06
  • C语言版三子棋游戏实现代码

    C语言版三子棋游戏实现代码

    这篇文章主要为大家详细介绍了C语言版三子棋游戏的实现代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • C语言之快速排序案例详解

    C语言之快速排序案例详解

    这篇文章主要介绍了C语言之快速排序案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C++ 哈夫曼树对文件压缩、加密实现代码

    C++ 哈夫曼树对文件压缩、加密实现代码

    这篇文章主要介绍了C++ 哈夫曼树对文件压缩、加密实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • C++类型兼容规则详情

    C++类型兼容规则详情

    这篇文章主要介绍了C++类型兼容规则详情,共有继承时,任何需要父类对象的地方,都能使用子类对象“替代”,这就是类型兼容规则,下面一起来了解文章相关内容吧
    2022-03-03
  • Qt实现电子时钟的示例代码

    Qt实现电子时钟的示例代码

    这篇文章主要为大家详细介绍了如何利用Qt实现显示与桌面上并可以随意拖拽至桌面任意位置的电子时钟案例,感兴趣的小伙伴可以尝试一下
    2022-06-06
  • C++设置系统时间及系统时间网络更新的方法

    C++设置系统时间及系统时间网络更新的方法

    这篇文章主要介绍了C++设置系统时间及系统时间网络更新的方法,涉及网络程序设计与系统函数的使用,需要的朋友可以参考下
    2014-10-10
  • C语言实现三子棋小游戏(vs2013多文件)

    C语言实现三子棋小游戏(vs2013多文件)

    这篇文章主要为大家详细介绍了C语言实现三子棋小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • 详解c++中的 static 关键字及作用

    详解c++中的 static 关键字及作用

    这篇文章主要介绍了c++中的 static 关键字,在我们日常使用过程中,static通常有两个作用,具体内容在文中给大家详细介绍,需要的朋友可以参考下
    2020-02-02
  • C++编写简易的飞机大战

    C++编写简易的飞机大战

    一款自己设计的飞机小游戏,本程序于运行环境WINDOWS XP系统,采用C++语言编写。游戏具有得分排名榜,而且在游戏完成后可以提交得分到网络上的世界排名榜中。
    2015-08-08

最新评论