【视频点播系统】ODB MySQL-SDK 介绍及使用

ODB MySQL-SDK 介绍及使用

  • [一. ODB 介绍](#一. ODB 介绍)
  • [二. odb SDK 安装](#二. odb SDK 安装)
  • [三. odb 常见操作](#三. odb 常见操作)
    • [1. odb 类型映射](#1. odb 类型映射)
    • [2. odb 编程](#2. odb 编程)
  • [四. odb 类与接口](#四. odb 类与接口)
    • [1. database、transaction](#1. database、transaction)
    • [2. query、result](#2. query、result)
    • [3. nullable、tracer、exception](#3. nullable、tracer、exception)
  • [五. odb 使用样例](#五. odb 使用样例)
    • [1. 目录结构](#1. 目录结构)
    • [2. 项目构建](#2. 项目构建)
    • [3. 代码实现](#3. 代码实现)
      • [1. 增删查改](#1. 增删查改)
      • [2. 多表联查](#2. 多表联查)
      • [3. 分组查询](#3. 分组查询)
      • [4. 分页查询](#4. 分页查询)
      • [5. 原生 sql 查询](#5. 原生 sql 查询)
  • [六. odb 封装](#六. odb 封装)
    • [1. 设计与实现](#1. 设计与实现)
      • [1.1 目录结构](#1.1 目录结构)
      • [1.2 代码实现](#1.2 代码实现)
    • [2. 使用样例](#2. 使用样例)
      • [1.1 目录结构](#1.1 目录结构)
      • [1.2 项目构建](#1.2 项目构建)
      • [1.3 代码实现](#1.3 代码实现)

一. ODB 介绍

  • ODB (Object--Relational Database Mapping) 是一款面向 C++ 的对象关系映射,也叫 Object--Relational Mapping (ORM) 框架,主要用于在 C++ 应用程序中实现对象与关系型数据库之间的自动映射。它支持将 C++ 类直接映射为数据库表,把对象的成员变量映射为表字段,从而让开发者以操作对象的方式完成数据库的增删改查,减少大量手写 SQL 的工作量。ODB 通过代码生成的方式工作,开发者只需在 C++ 类中使用少量注解 (如 #pragma db),即可由 ODB 编译器生成高效、类型安全的数据库访问代码。
  • ODB 支持多种主流数据库,包括 MySQL、PostgreSQL、SQLite 和 Oracle,具备事务管理、延迟加载、对象缓存、关系映射 (一对一、一对多、多对多) 等特性。与许多动态语言 ORM 不同,ODB 强调编译期检查和高性能,生成的代码几乎没有运行时反射开销,特别适合对性能和类型安全要求较高的 C++ 后端服务和系统级应用。

二. odb SDK 安装

Github 地址:https://github.com/codesynthesis-com/odb.git

由于在环境搭建中已经安装了 ODB、MySQL 服务器并且运行,我们不需要再次安装。

但是我们需要安装一个 MySQL 客户端,方便查看数据的变化:

bash 复制代码
sudo apt update
sudo apt install mysql-client

MySQL 服务器密码就在 docker-compose.yml 文件中:


登录 MySQL 服务器:

三. odb 常见操作

1. odb 类型映射


2. odb 编程

ODB (Open Database) 在数据元结构定义时,使用预处理器指令 (#pragma) 来提供元数据,这些元数据指示如何将 C++ 类型映射到数据库模式。这些 #pragma 指令是在 C++ 代码中使用的,它们不是 C++ 语言的一部分,而是特定于 ODB 编译器的扩展。以下是 ODB 中常用的一些 #pragma 指令:

  • #pragma db object:用于声明一个类是数据库对象,即这个类将映射到数据库中的一个表。
  • #pragma db table("table_name"):指定类映射到数据库中的表名。如果不指定,则默认使用类名。
  • #pragma db id:标记类中的一个成员变量作为数据库表的主键。
  • #pragma db auto:指定成员变量的值在插入时自动生成 (例如:自动递增的主键)
  • #pragma db column("column_name"):指定类成员映射到数据库表中的列名。如果不指定,则默认使用成员变量的名字。
  • #pragma db type("type_name"):指定成员变量的数据库类型。
  • #pragma db transient:指定成员变量不应该被持久化到数据库中。
  • #pragma db view:用于声明一个类是一个数据库视图,而不是一个表。
  • #pragma db session:用于声明一个全局或成员变量是数据库会话。
  • #pragma db query("query"):用于定义自定义的查询函数。
  • #pragma db index("index_name"):指定成员变量应该被索引。
  • #pragma db default("default_value"):指定成员变量的默认值。
  • #pragma db unique:指定成员变量或一组变量应该具有唯一性约束。
  • #pragma db not_null / null:指定成员变量是否允许为空,不建议使用 (因为如果数据库中该字段没有数据,成员的默认值就会造成歧义,建议针对字段使用 odb::nullable 类型进行声明,与 std::optional<T> 功能相似)
  • #pragma db convert("converter"):指定用于成员变量的自定义类型转换器。
  • #pragma db pool("pool_name"):指定用于数据库连接的连接池。
  • #pragma db trigger("trigger_name"):指定在插入、更新或删除操作时触发的触发器。

四. odb 类与接口

1. database、transaction

  • odb::database:表示一个具体的数据库实例,是 ODB 与真实数据库之间的桥梁。它负责建立和管理数据库连接,并提供对象持久化相关的操作接口,例如插入 (persist)、查询 (load / query)、更新 (update) 和删除 (erase),在 ODB 中,所有对数据库的访问都必须通过 database 对象完成。
  • odb::transaction:用于表示一次数据库事务,控制一组数据库操作的原子性。事务开始后,所有操作要么在 commit() 时一次性生效,要么在事务回滚时全部撤销。transaction 采用 RAII 设计,如果事务对象在作用域结束前没有显式提交,就会自动回滚,从而保证异常安全和数据一致性。

两者关系:database 负责 "连到哪一个数据库并执行操作",transaction 负责 "这些操作是否最终生效"。在 ODB 中,所有数据库操作都必须在事务中进行。

注意:不要直接通过最初创建的 database 进行数据操作,而是通过 transaction 创建的 database 对象进行操作。原因:数据库句柄是共享的,transaction 创建的 database 对象是独占的,多线程最怕共享 (线程安全问题),而 transaction 刻意帮你隔离。

cpp 复制代码
namespace odb {
    namespace mysql {
        // 数据库连接池对象类,继承自 connection_factory
        class LIBODB_MYSQL_EXPORT connection_pool_factory : public connection_factory {
            connection_pool_factory(std::size_t max_connections = 0, // 最大连接数
                                    std::size_t min_connections = 0, // 最小连接数
                                    bool ping = true) // 保持连接鲜活,避免连接过期
        };
        // 数据库操作句柄类,继承自 odb::database
        class LIBODB_MYSQL_EXPORT database: public odb::database {
            database(const std::string& user, // 用户名
                const std::string& passwd, // 密码
                const std::string& db, // 数据库名
                const std::string& host = "", // 主机名
                unsigned int port = 0, // 端口号
                const std::string* socket = 0, // 套接字
                const std::string& charset = "", // 字符集
                unsigned long client_flags = 0, // 客户端标志
                details::transfer_ptr<connection_factory> =
                details::transfer_ptr<connection_factory>() // 连接工厂
            );
            persist(T& object); // 新增单条数据
            void update(T& object); // 更新单条数据
            void erase(T& object); // 删除单条数据
            unsigned long long erase_query(const std::string&); // 根据SQL语句删除数据
            unsigned long long erase_query(const odb::query<T>&); // 根据查询对象删除数据
            result<T> query(const std::string&); // 根据SQL语句查询数据
            result<T> query(const odb::query<T>&, bool cache = true); // 根据查询对象查询数据
            typename result<T>::pointer_type query_one(const odb::query<T>&); // 查询单条数据
            virtual transaction_impl* begin () = 0; // 创建/获取一个事务对象
        }
        // 事务操作类,继承自 odb::transaction
        class LIBODB_MYSQL_EXPORT transaction: public odb::transaction;
    }
    // 数据库操作句柄类
    class LIBODB_EXPORT database;
    // 事务操作类
    class LIBODB_EXPORT transaction {
        transaction(transaction_impl*, bool make_current = true); // 构造函数,创建/获取一个事务对象
        odb::database& database(); // 获取数据库操作句柄
        void commit(); // 提交事务
        void rollback(); // 回滚事务
        void tracer(tracer_type& t) // 设置事务跟踪器
    };
}

2. query、result

  • odb::query:表示一个查询条件的抽象描述,用于构造数据库查询语句。它并不直接访问数据库,而是用来表达 "要查什么数据",例如字段的比较、逻辑组合 (与、或、非)、范围条件等。query 通常作为参数传递给 database::query(),由 ODB 根据对象映射关系将其转换为对应的 SQL WHERE 条件。
  • odb::result:表示一次查询返回的结果集合,是对数据库查询结果的封装。它本身不保存所有数据,而是以惰性 (延迟) 方式从数据库中逐条读取对象,支持迭代访问。result 的生命周期通常与事务绑定,事务结束后结果集即失效,用于安全、高效地遍历查询得到的对象。

两者关系:query 用来描述 "筛选规则",result 用来承载 "筛选后的对象集合"。前者负责条件表达,后者负责结果访问。

cpp 复制代码
namespace odb {
    // 针对查询结果所封装的容器类 
    template <typename T>
    class result: result_base<T, class_traits<T>::kind> {
        typedef typename iterator::pointer_type pointer_type; // 指向结果容器中元素的指针类型
        result();
        result(const result& r);
        iterator begin(); // 返回指向结果容器第一个元素的迭代器
        iterator end(); // 返回指向结果容器最后一个元素的下一个位置的迭代器
        size_type size(); // 返回结果容器中元素的数量
        bool empty(); // 判断结果容器是否为空
    };
    // 查询条件的基础封装类
    class LIBODB_EXPORT query_base {
        explicit query_base (const std::string& native); // 接受原生 SQL 查询字符串
        const clause_type& clause () const; // 返回查询条件的子句对象
    };
    // 强类型查询类(绑定具体对象类型 T)
    template <typename T>
    class query<T, mysql::query_base>: public mysql::query<T> {
        query (bool v); // 接受布尔值 v
        query (const std::string& q); // 接受原生 SQL 查询字符串
        query (const mysql::query_base& q); // 接受 query_base 类型的查询对象
    }
    namespace mysql {
        // 模板特化适配类
        template <typename T>
        class query: public query_base, public query_selector<T, id_mysql>::columns_type {
            query(const std::string& q); // 接受原生 SQL 查询字符串
            query(const query_base& q); // 接受 query_base 类型的查询对象
            query(bool v); // 接受布尔值 v
        }
    }
}

3. nullable、tracer、exception

  • odb::nullable<T>:表示一个可为空 (NULL) 的值类型,用于映射数据库中允许为 NULL 的列。它在 C++ 中显式区分 "有值" 和 "无值" 两种状态,避免使用特殊值 (如 0、-1) 来表达空语义。nullable 常用于基本类型或时间类型,与数据库列的 NULL / NOT NULL 语义一一对应。
  • odb::tracer:用于跟踪和记录数据库操作的接口类,主要用于调试、日志和性能分析。通过实现或启用 tracer,可以在 SQL 语句执行前后获取相关信息,例如执行的 SQL、参数以及执行时机。它不会影响 ORM 逻辑本身,只用于观察和诊断数据库行为。
  • odb::exception:是 ODB 中所有异常类型的基类,用于表示数据库访问和 ORM 操作过程中发生的错误。包括连接失败、SQL 执行错误、事务问题、数据约束违反等。通过捕获 odb::exception,可以统一处理 ODB 抛出的各种数据库相关异常。
cpp 复制代码
namespace odb {
    // 针对可能为空的字段封装的类似于智能指针的类型
    template <typename T>
    class nullable {
        typedef T value_type;
        T& get();
        const T& get() const;
        T* operator->();
        const T* operator->() const;
        T& operator*();
        const T& operator*() const;
    };
    // 异常类,用于数据库操作中的错误处理
    struct LIBODB_EXPORT exception: std::exception, details::shared_base {
        virtual const char* what() const ODB_NOTHROW_NOEXCEPT = 0;
        virtual exception* clone() const = 0;
    };
    // 标准错误输出 tracer
    extern LIBODB_EXPORT tracer& stderr_tracer; 
    // 标准错误输出 tracer,包含完整 SQL 语句
    extern LIBODB_EXPORT tracer& stderr_full_tracer; 
    namespace mysql {
        // 针对 MySQL 数据库的 tracer 实现
        class LIBODB_MYSQL_EXPORT tracer: private odb::tracer {
        public:
            virtual ~tracer();
            // 准备 SQL 语句
            virtual void prepare(connection&, const statement&);
            // 执行 SQL 语句
            virtual void execute(connection&, const statement&);
            // 执行原生 SQL 语句
            virtual void execute(connection&, const char* statement) = 0;
            // 释放 SQL 语句    
            virtual void deallocate(connection&, const statement&);
        };
        // 针对 MySQL 数据库的 statement 实现
        class LIBODB_MYSQL_EXPORT statement: public odb::statement {
        public:
            // 获取 SQL 语句文本
            virtual const char* text() const;
        };
    }
}

五. odb 使用样例

1. 目录结构

bash 复制代码
example/
|-- odb
|   |-- crud
|   |   |-- crud.cc
|   |   |-- makefile
|   |   |-- student.h
|   |-- group
|   |   |-- group.cc
|   |   |-- makefile
|   |   |-- student.h
|   |-- limit
|   |   |-- limit.cc
|   |   |-- makefile
|   |   |-- student.h
|   |-- native
|   |   |-- makefile
|   |   |-- native.cc
|   |   |-- student.h
|   |-- view
|       |-- makefile
|       |-- student.h
|       |-- view.cc

2. 项目构建

curd 目录下的 makefile:

bash 复制代码
crud:crud.cc student-odb.cxx
	g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
	odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^

.PHONY: clean
clean:
	rm -f crud *-odb.* *.sql

view 目录下的 makefile:

bash 复制代码
view:view.cc student-odb.cxx
	g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
	odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
	
.PHONY: clean
clean:
	rm -f view *-odb.* *.sql

group 目录下的 makefile:

bash 复制代码
group:group.cc student-odb.cxx
	g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
	odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
	
.PHONY: clean
clean:
	rm -f group *-odb.* *.sql

limit 目录下的 makefile:

bash 复制代码
limit:limit.cc student-odb.cxx
	g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
	odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
	
.PHONY: clean
clean:
	rm -f limit *-odb.* *.sql

native 目录下的 makefile:

bash 复制代码
native:native.cc student-odb.cxx
	g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
	odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
	
.PHONY: clean
clean:
	rm -f native *-odb.* *.sql

3. 代码实现

1. 增删查改

cpp 复制代码
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>

// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student") 
class Student {
public:
    Student(){};
    Student(const std::string& name): _name(name) {}
    // 获取/设置 _sn
    size_t sn() const { return _sn; }
    void set_sn(size_t sn) { _sn = sn; }
    // 获取/设置 _name
    const std::string& name() const { return _name; }
    void set_name(const std::string& name) { _name = name; }
    // 获取/设置 _age
    const odb::nullable<int>& age() const { return _age; }
    void set_age(odb::nullable<int> age) { _age = age; }
    // 获取/设置 _score
    const odb::nullable<double>& score() const { return _score; }
    void set_score(odb::nullable<double> score) { _score = score; }
    // 获取/设置 _birthday
    const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
    void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
    // 友元类 odb::access 可以访问 private 成员
    friend class odb::access; 
    // 设置 _sn 为自增主键
    #pragma db id auto 
    size_t _sn;
    std::string _name;
    // 设置 _age、_score、_birthday 为可空整数
    odb::nullable<int> _age;
    odb::nullable<double> _score;
    odb::nullable<boost::posix_time::ptime> _birthday;
};

编译生成可执行程序和 student.sql 文件,如下:

sql 复制代码
-- student.sql
-- 需要手动添加的sql语句 --
DROP DATABASE IF EXISTS `mytest`;
CREATE DATABASE IF NOT EXISTS `mytest`;
USE `mytest`;

-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_student`;

CREATE TABLE `tbl_student` (
  `sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` TEXT NOT NULL,
  `age` INT NULL,
  `score` DOUBLE NULL,
  `birthday` DATETIME NULL)
 ENGINE=InnoDB;

将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp 复制代码
// crud.cc
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "student.h"
#include "student-odb.hxx"

// 定义数据库连接参数:主机名、用户名、密码、数据库名、端口号、字符集
const std::string HOST = "192.168.174.128";
const std::string USER = "root";
const std::string PASSWORD = "123456";
const std::string DBNAME = "mytest";
const unsigned int PORT = 3306;
const std::string CSET = "utf8";

void insert(std::unique_ptr<odb::mysql::database>& handler) {
    try {
        // 1.通过句柄构造事务对象
        odb::transaction tx(handler->begin());
        // 2.通过事务构造database对象
        odb::database& db = tx.database();
        // 3.数据操作:添加一条学生记录
        Student stu("zhangsan");
        stu.set_age(28);
        stu.set_birthday(boost::posix_time::time_from_string("2003-03-07 12:00:00"));
        db.persist(stu);
        // 4.提交事务
        tx.commit();
    } catch (const odb::exception& e) {
        std::cerr << "数据库操作异常: " << e.what() << std::endl;
    }
}

void select(std::unique_ptr<odb::mysql::database>& handler) {
    try {
        // 1.通过句柄构造事务对象
        odb::transaction tx(handler->begin());
        // 2.通过事务构造database对象
        odb::database& db = tx.database();
        // 3.数据操作:查询sn为1的学生记录
        typedef odb::query<Student> Query;
        typedef odb::result<Student> Result;
        Result::pointer_type stu = db.query_one<Student>(Query::sn == 1);
        if (!stu) {
            std::cout << "sn为1的学生,查询结果为空" << std::endl;
            return;
        }
        std::cout << "sn: " << stu->sn() << std::endl;
        std::cout << "name: " << stu->name() << std::endl;
        if (stu->age()) std::cout << "age: " << stu->age().get() << std::endl;
        if (stu->score()) std::cout << "score: " << stu->score().get() << std::endl;
        if (stu->birthday()) std::cout << "birthday: " << boost::posix_time::to_simple_string(stu->birthday().get()) << std::endl;
        // 4.提交事务
        tx.commit();
    } catch (const odb::exception& e) {
        std::cerr << "数据库操作异常: " << e.what() << std::endl;
    }
}

void select_all(std::unique_ptr<odb::mysql::database>& handler) {
    try {
        // 1.通过句柄构造事务对象
        odb::transaction tx(handler->begin());
        // 2.通过事务构造database对象
        odb::database& db = tx.database();
        // 3.数据操作:查询数据库中的所有学生记录
        typedef odb::query<Student> Query;
        typedef odb::result<Student> Result;
        Result stu(db.query<Student>());
        for (auto it = stu.begin(); it != stu.end(); ++it) {
            std::cout << "sn: " << it->sn() << std::endl;
            std::cout << "name: " << it->name() << std::endl;
            if (it->age()) std::cout << "age: " << it->age().get() << std::endl;
            if (it->score()) std::cout << "score: " << it->score().get() << std::endl;
            if (it->birthday()) std::cout << "birthday: " << boost::posix_time::to_simple_string(it->birthday().get()) << std::endl;
        }
        // 4.提交事务
        tx.commit();
    } catch (const odb::exception& e) {
        std::cerr << "数据库操作异常: " << e.what() << std::endl;
    }
}

void update(std::unique_ptr<odb::mysql::database>& handler) {
    try {
        // 1.通过句柄构造事务对象
        odb::transaction tx(handler->begin());
        // 2.通过事务构造database对象
        odb::database& db = tx.database();
        // 3.数据操作:更新sn为1的学生记录的score字段为90
        typedef odb::query<Student> Query;
        typedef odb::result<Student> Result;
        Result::pointer_type stu = db.query_one<Student>(Query::sn == 1);
        if (!stu) {
            std::cout << "sn为1的学生,查询结果为空" << std::endl;
            return;
        }
        stu->set_score(90);
        db.update(stu);
        // 4.提交事务
        tx.commit();
    } catch (const odb::exception& e) {
        std::cerr << "数据库操作异常: " << e.what() << std::endl;
    }
}

void remove(std::unique_ptr<odb::mysql::database>& handler) {
    try {
        // 1.通过句柄构造事务对象
        odb::transaction tx(handler->begin());
        // 2.通过事务构造database对象
        odb::database& db = tx.database();
        // 3.数据操作:删除sn为1的学生记录
        typedef odb::query<Student> Query;
        typedef odb::result<Student> Result;
        db.erase_query<Student>(Query::sn == 1);
        // 4.提交事务
        tx.commit();
    } catch (const odb::exception& e) {
        std::cerr << "数据库操作异常: " << e.what() << std::endl;
    }
}

int main() 
{
    // 1. 构造数据库连接池
    std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(5));
    // 2.构造数据库操作句柄
    std::unique_ptr<odb::mysql::database> handler = std::make_unique<odb::mysql::database>(USER, PASSWORD, DBNAME, HOST, PORT, nullptr, CSET, 0, std::move(pool));
    // 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
    insert(handler);
    select(handler);
    select_all(handler);
    update(handler);
    remove(handler);
    // 注意:不要直接通过最初创建的数据库句柄进行数据操作,而是通过事物创建的database对象进行操作
    // 原因:数据库句柄是共享的,事务创建的database对象是独占的,多线程最怕共享(线程安全问题),而事务刻意帮你隔离

    return 0;
}

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

2. 多表联查

cpp 复制代码
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>

// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student") 
class Student {
public:
    Student(){};
    Student(const std::string& name): _name(name) {}
    // 获取/设置 _sn
    size_t sn() const { return _sn; }
    void set_sn(size_t sn) { _sn = sn; }
    // 获取/设置 _name
    const std::string& name() const { return _name; }
    void set_name(const std::string& name) { _name = name; }
    // 获取/设置 _age
    const odb::nullable<int>& age() const { return _age; }
    void set_age(odb::nullable<int> age) { _age = age; }
    // 获取/设置 _score
    const odb::nullable<double>& score() const { return _score; }
    void set_score(odb::nullable<double> score) { _score = score; }
    // 获取/设置 _birthday
    const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
    void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
    // 友元类 odb::access 可以访问 private 成员
    friend class odb::access; 
    // 设置 _sn 为自增主键
    #pragma db id auto 
    size_t _sn;
    // 设置 _classes_id 为索引
    #pragma db index
    size_t _classes_id;
    std::string _name;
    odb::nullable<int> _age;
    odb::nullable<double> _score;
    odb::nullable<boost::posix_time::ptime> _birthday;
};

// 设置数据库表名为 tbl_classes
#pragma db object table("tbl_classes")
class Classes {
public:
    Classes(){};
    Classes(const std::string& name): _name(name) {}
    size_t id() const { return _id; }
    void set_id(size_t id) { _id = id; }
    const std::string& name() const { return _name; }
    void set_name(const std::string& name) { _name = name; }
private:
    // 友元类 odb::access 可以访问 private 成员
    friend class odb::access;
    // 设置 _id 为自增主键
    #pragma db id auto
    size_t _id;
    // 设置 _name 类型为 VARCHAR(32)的唯一索引
    #pragma db unique type("VARCHAR(32)")
    std::string _name;
};

// 查询条件:Query::Classes::name == "class one"
#pragma db view object(Classes) \
                object(Student: Student::_classes_id == Classes::_id) \
                query((?))
struct ClassesStudent {
    std::shared_ptr<Student> student;
    std::shared_ptr<Classes> classes;
};
// 查询条件:Query::Student::name == "zhangsan"
#pragma db view object(Student) \
                object(Classes: Student::_classes_id == Classes::_id) \
                query((?))
struct DetailStudent {
    std::shared_ptr<Student> student;
    std::shared_ptr<Classes> classes;
};

编译生成可执行程序和 student.sql 文件,如下:

sql 复制代码
-- 需要手动添加的sql语句 --
DROP DATABASE IF EXISTS `mytest`;
CREATE DATABASE IF NOT EXISTS `mytest`;
USE `mytest`;

-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_classes`;

DROP TABLE IF EXISTS `tbl_student`;

CREATE TABLE `tbl_student` (
  `sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `classes_id` BIGINT UNSIGNED NOT NULL,
  `name` TEXT NOT NULL,
  `age` INT NULL,
  `score` DOUBLE NULL,
  `birthday` DATETIME NULL)
 ENGINE=InnoDB;

CREATE INDEX `classes_id_i`
  ON `tbl_student` (`classes_id`);

CREATE TABLE `tbl_classes` (
  `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(32) NOT NULL)
 ENGINE=InnoDB;

CREATE UNIQUE INDEX `name_i`
  ON `tbl_classes` (`name`);

-- 需要手动添加的sql语句 --
INSERT INTO `tbl_classes` VALUES (1, 'class one'),
                                (2, 'class two'),
                                (3, 'class three'),
                                (4, 'class four');
INSERT INTO `tbl_student` VALUES (1, 1, "zhangsan", 18, 80, "2000-01-01 00:00:00"),
                                (2, 2, "lisi", 19, 85, "2000-02-02 00:00:00"),
                                (3, 3, "wangwu", 20, 90, "2000-03-03 00:00:00"),
                                (4, 4, "zhaoliu", 21, 95, "2000-04-04 00:00:00"),
                                (5, 1, "tianqi", 22, 100, "2000-05-05 00:00:00");

将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp 复制代码
// view.cc
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "student.h"
#include "student-odb.hxx"

// 定义数据库连接参数:主机名、用户名、密码、数据库名、端口号、字符集
const std::string HOST = "192.168.174.128";
const std::string USER = "root";
const std::string PASSWORD = "123456";
const std::string DBNAME = "mytest";
const unsigned int PORT = 3306;
const std::string CSET = "utf8";

void get_classes_students(std::unique_ptr<odb::mysql::database>& handler, const std::string& class_name) {
    try {
        // 1.通过句柄构造事务对象
        odb::transaction tx(handler->begin());
        // 2.通过事务构造database对象
        odb::database& db = tx.database();
        // 3.查询班级为"class_name"的学生信息
        typedef odb::query<ClassesStudent> Query;
        typedef odb::result<ClassesStudent> Result;
        Result cla(db.query<ClassesStudent>(Query::Classes::name == class_name));
        for (auto it = cla.begin(); it != cla.end(); ++it) {
            if (it->student) std::cout << "姓名: " << it->student->name() << "\t";
            if (it->classes) std::cout << "班级: " << it->classes->name() << std::endl;
        }
        // 4.提交事务
        tx.commit();
    } catch (const odb::exception& e) {
        std::cerr << "数据库操作异常: " << e.what() << std::endl;
    }
}

void get_detail_students(std::unique_ptr<odb::mysql::database>& handler, const std::string& student_name) {
    try {
        // 1.通过句柄构造事务对象
        odb::transaction tx(handler->begin());
        // 2.通过事务构造database对象
        odb::database& db = tx.database();
        // 3.查询学生姓名为"student_name"的详细信息
        typedef odb::query<DetailStudent> Query;
        typedef odb::result<DetailStudent> Result;
        Result cla(db.query<DetailStudent>(Query::Student::name == student_name));
        for (auto it = cla.begin(); it != cla.end(); ++it) {
            if (it->student) std::cout << "姓名: " << it->student->name() << "\t";
            if (it->classes) std::cout << "班级: " << it->classes->name() << std::endl;
        }
        // 4.提交事务
        tx.commit();
    } catch (const odb::exception& e) {
        std::cerr << "数据库操作异常: " << e.what() << std::endl;
    }
}

int main() 
{
    // 1. 构造数据库连接池
    std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(5));
    // 2.构造数据库操作句柄
    std::unique_ptr<odb::mysql::database> handler = std::make_unique<odb::mysql::database>(USER, PASSWORD, DBNAME, HOST, PORT, nullptr, CSET, 0, std::move(pool));
    // 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
    get_classes_students(handler, "class one");
    get_detail_students(handler, "zhangsan");

    return 0;
}

3. 分组查询

cpp 复制代码
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>

// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student") 
class Student {
public:
    Student(){};
    Student(const std::string& name): _name(name) {}
    // 获取/设置 _sn
    size_t sn() const { return _sn; }
    void set_sn(size_t sn) { _sn = sn; }
    // 获取/设置 _name
    const std::string& name() const { return _name; }
    void set_name(const std::string& name) { _name = name; }
    // 获取/设置 _age
    const odb::nullable<int>& age() const { return _age; }
    void set_age(odb::nullable<int> age) { _age = age; }
    // 获取/设置 _score
    const odb::nullable<double>& score() const { return _score; }
    void set_score(odb::nullable<double> score) { _score = score; }
    // 获取/设置 _birthday
    const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
    void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
    // 友元类 odb::access 可以访问 private 成员
    friend class odb::access; 
    // 设置 _sn 为自增主键
    #pragma db id auto 
    size_t _sn;
    // 设置 _classes_id 为索引
    #pragma db index
    size_t _classes_id;
    std::string _name;
    odb::nullable<int> _age;
    odb::nullable<double> _score;
    odb::nullable<boost::posix_time::ptime> _birthday;
};

// 设置数据库表名为 tbl_classes
#pragma db object table("tbl_classes")
class Classes {
public:
    Classes(){};
    Classes(const std::string& name): _name(name) {}
    size_t id() const { return _id; }
    void set_id(size_t id) { _id = id; }
    const std::string& name() const { return _name; }
    void set_name(const std::string& name) { _name = name; }
private:
    // 友元类 odb::access 可以访问 private 成员
    friend class odb::access;
    // 设置 _id 为自增主键
    #pragma db id auto
    size_t _id;
    // 设置 _name 类型为 VARCHAR(32)的唯一索引
    #pragma db unique type("VARCHAR(32)")
    std::string _name;
};

// 以班级id进行分组,统计每个班级的学生总数以及班级平均分
#pragma db view object(Student) \
                query((?) + "group by " + Student::_classes_id)
struct GroupCount {
    #pragma db column(Student::_classes_id)
    size_t classes_id;
    #pragma db column("count(" + Student::_sn + ")")
    size_t student_count;
    #pragma db column("avg(" + Student::_score + ")")
    double avg_score;
};

编译生成可执行程序和 student.sql 文件,如下:

sql 复制代码
-- 需要手动添加的sql语句 --
DROP DATABASE IF EXISTS `mytest`;
CREATE DATABASE IF NOT EXISTS `mytest`;
USE `mytest`;

-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_classes`;

DROP TABLE IF EXISTS `tbl_student`;

CREATE TABLE `tbl_student` (
  `sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `classes_id` BIGINT UNSIGNED NOT NULL,
  `name` TEXT NOT NULL,
  `age` INT NULL,
  `score` DOUBLE NULL,
  `birthday` DATETIME NULL)
 ENGINE=InnoDB;

CREATE INDEX `classes_id_i`
  ON `tbl_student` (`classes_id`);

CREATE TABLE `tbl_classes` (
  `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(32) NOT NULL)
 ENGINE=InnoDB;

CREATE UNIQUE INDEX `name_i`
  ON `tbl_classes` (`name`);

-- 需要手动添加的sql语句 --
INSERT INTO `tbl_classes` VALUES (1, 'class one'),
                                (2, 'class two'),
                                (3, 'class three'),
                                (4, 'class four');
INSERT INTO `tbl_student` VALUES (1, 1, "zhangsan", 18, 80, "2000-01-01 00:00:00"),
                                (2, 2, "lisi", 19, 85, "2000-02-02 00:00:00"),
                                (3, 3, "wangwu", 20, 90, "2000-03-03 00:00:00"),
                                (4, 4, "zhaoliu", 21, 95, "2000-04-04 00:00:00"),
                                (5, 1, "tianqi", 22, 100, "2000-05-05 00:00:00");

将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp 复制代码
// group.cc
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "student.h"
#include "student-odb.hxx"

// 定义数据库连接参数:主机名、用户名、密码、数据库名、端口号、字符集
const std::string HOST = "192.168.174.128";
const std::string USER = "root";
const std::string PASSWORD = "123456";
const std::string DBNAME = "mytest";
const unsigned int PORT = 3306;
const std::string CSET = "utf8";

void group_count(std::unique_ptr<odb::mysql::database>& handler) {
    try {
        // 1.通过句柄构造事务对象
        odb::transaction tx(handler->begin());
        // 2.通过事务构造database对象
        odb::database& db = tx.database();
        // 3.分组统计每个班级的学生总数以及班级平均分
        typedef odb::query<GroupCount> Query;
        typedef odb::result<GroupCount> Result;
        Result grp(db.query<GroupCount>());
        for (auto it = grp.begin(); it != grp.end(); ++it) {
            std::cout << "班级id: " << it->classes_id << "\t";
            std::cout << "学生总数: " << it->student_count << "\t";
            std::cout << "班级平均分: " << it->avg_score << std::endl;
        }
        // 4.提交事务
        tx.commit();
    } catch (const odb::exception& e) {
        std::cerr << "数据库操作异常: " << e.what() << std::endl;
    }
}

int main() 
{
    // 1. 构造数据库连接池
    std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(5));
    // 2.构造数据库操作句柄
    std::unique_ptr<odb::mysql::database> handler = std::make_unique<odb::mysql::database>(USER, PASSWORD, DBNAME, HOST, PORT, nullptr, CSET, 0, std::move(pool));
    // 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
    group_count(handler);

    return 0;
}

4. 分页查询

cpp 复制代码
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>

// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student") 
class Student {
public:
    Student(){};
    Student(const std::string& name): _name(name) {}
    // 获取/设置 _sn
    size_t sn() const { return _sn; }
    void set_sn(size_t sn) { _sn = sn; }
    // 获取/设置 _name
    const std::string& name() const { return _name; }
    void set_name(const std::string& name) { _name = name; }
    // 获取/设置 _age
    const odb::nullable<int>& age() const { return _age; }
    void set_age(odb::nullable<int> age) { _age = age; }
    // 获取/设置 _score
    const odb::nullable<double>& score() const { return _score; }
    void set_score(odb::nullable<double> score) { _score = score; }
    // 获取/设置 _birthday
    const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
    void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
    // 友元类 odb::access 可以访问 private 成员
    friend class odb::access; 
    // 设置 _sn 为自增主键
    #pragma db id auto 
    size_t _sn;
    // 设置 _classes_id 为索引
    #pragma db index
    size_t _classes_id;
    std::string _name;
    odb::nullable<int> _age;
    odb::nullable<double> _score;
    odb::nullable<boost::posix_time::ptime> _birthday;
};

// 设置数据库表名为 tbl_classes
#pragma db object table("tbl_classes")
class Classes {
public:
    Classes(){};
    Classes(const std::string& name): _name(name) {}
    size_t id() const { return _id; }
    void set_id(size_t id) { _id = id; }
    const std::string& name() const { return _name; }
    void set_name(const std::string& name) { _name = name; }
private:
    // 友元类 odb::access 可以访问 private 成员
    friend class odb::access;
    // 设置 _id 为自增主键
    #pragma db id auto
    size_t _id;
    // 设置 _name 类型为 VARCHAR(32)的唯一索引
    #pragma db unique type("VARCHAR(32)")
    std::string _name;
};

// 分页查询学生信息
#pragma db view object(Student) \
                query((?))
struct LimitStudent {
    #pragma db column(Student::_classes_id)
    size_t classes_id;
    #pragma db column(Student::_name)
    std::string student_name;
    #pragma db column(Student::_score + "as order_field")
    double student_score;
};

编译生成可执行程序和 student.sql 文件,如下:

sql 复制代码
-- 需要手动添加的sql语句 --
DROP DATABASE IF EXISTS `mytest`;
CREATE DATABASE IF NOT EXISTS `mytest`;
USE `mytest`;

-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_classes`;

DROP TABLE IF EXISTS `tbl_student`;

CREATE TABLE `tbl_student` (
  `sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `classes_id` BIGINT UNSIGNED NOT NULL,
  `name` TEXT NOT NULL,
  `age` INT NULL,
  `score` DOUBLE NULL,
  `birthday` DATETIME NULL)
 ENGINE=InnoDB;

CREATE INDEX `classes_id_i`
  ON `tbl_student` (`classes_id`);

CREATE TABLE `tbl_classes` (
  `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(32) NOT NULL)
 ENGINE=InnoDB;

CREATE UNIQUE INDEX `name_i`
  ON `tbl_classes` (`name`);

-- 需要手动添加的sql语句 --
INSERT INTO `tbl_classes` VALUES (1, 'class one'),
                                (2, 'class two'),
                                (3, 'class three'),
                                (4, 'class four');
INSERT INTO `tbl_student` VALUES (1, 1, "zhangsan", 18, 80, "2000-01-01 00:00:00"),
                                (2, 2, "lisi", 19, 85, "2000-02-02 00:00:00"),
                                (3, 3, "wangwu", 20, 90, "2000-03-03 00:00:00"),
                                (4, 4, "zhaoliu", 21, 95, "2000-04-04 00:00:00"),
                                (5, 1, "tianqi", 22, 100, "2000-05-05 00:00:00");

将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp 复制代码
// limit.cc
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "student.h"
#include "student-odb.hxx"

// 定义数据库连接参数:主机名、用户名、密码、数据库名、端口号、字符集
const std::string HOST = "192.168.174.128";
const std::string USER = "root";
const std::string PASSWORD = "123456";
const std::string DBNAME = "mytest";
const unsigned int PORT = 3306;
const std::string CSET = "utf8";

void limit_offset(std::unique_ptr<odb::mysql::database>& handler) {
    try {
        // 1.通过句柄构造事务对象
        odb::transaction tx(handler->begin());
        // 2.通过事务构造database对象
        odb::database& db = tx.database();
        // 3.分页查询:查询班级id和学生姓名,按班级id降序排序,每页显示3条记录,偏移量为0
        typedef odb::query<LimitStudent> Query;
        typedef odb::result<LimitStudent> Result;
        Result lim(db.query<LimitStudent>("order by order_field desc limit 3 offset 0"));
        for (auto it = lim.begin(); it != lim.end(); ++it) {
            std::cout << "班级id: " << it->classes_id << "\t";
            std::cout << "学生姓名: " << it->student_name << "\t";
            std::cout << "学生成绩: " << it->student_score << std::endl;
        }
        // 4.提交事务
        tx.commit();
    } catch (const odb::exception& e) {
        std::cerr << "数据库操作异常: " << e.what() << std::endl;
    }
}

int main() 
{
    // 1. 构造数据库连接池
    std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(5));
    // 2.构造数据库操作句柄
    std::unique_ptr<odb::mysql::database> handler = std::make_unique<odb::mysql::database>(USER, PASSWORD, DBNAME, HOST, PORT, nullptr, CSET, 0, std::move(pool));
    // 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
    limit_offset(handler);

    return 0;
}

5. 原生 sql 查询

cpp 复制代码
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>

// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student") 
class Student {
public:
    Student(){};
    Student(const std::string& name): _name(name) {}
    // 获取/设置 _sn
    size_t sn() const { return _sn; }
    void set_sn(size_t sn) { _sn = sn; }
    // 获取/设置 _name
    const std::string& name() const { return _name; }
    void set_name(const std::string& name) { _name = name; }
    // 获取/设置 _age
    const odb::nullable<int>& age() const { return _age; }
    void set_age(odb::nullable<int> age) { _age = age; }
    // 获取/设置 _score
    const odb::nullable<double>& score() const { return _score; }
    void set_score(odb::nullable<double> score) { _score = score; }
    // 获取/设置 _birthday
    const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
    void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
    // 友元类 odb::access 可以访问 private 成员
    friend class odb::access; 
    // 设置 _sn 为自增主键
    #pragma db id auto 
    size_t _sn;
    // 设置 _classes_id 为索引
    #pragma db index
    size_t _classes_id;
    std::string _name;
    odb::nullable<int> _age;
    odb::nullable<double> _score;
    odb::nullable<boost::posix_time::ptime> _birthday;
};

// 原生sql语句查询
#pragma db view query("select name, classes_id, score from tbl_student")
struct NativeStudent {
    std::string name;
    size_t classes_id;
    odb::nullable<double> score;
};

编译生成可执行程序和 student.sql 文件,如下:

sql 复制代码
-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_student`;

CREATE TABLE `tbl_student` (
  `sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `classes_id` BIGINT UNSIGNED NOT NULL,
  `name` TEXT NOT NULL,
  `age` INT NULL,
  `score` DOUBLE NULL,
  `birthday` DATETIME NULL)
 ENGINE=InnoDB;

CREATE INDEX `classes_id_i`
  ON `tbl_student` (`classes_id`);

-- 需要手动添加的sql语句 --
INSERT INTO `tbl_classes` VALUES (1, 'class one'),
                                (2, 'class two'),
                                (3, 'class three'),
                                (4, 'class four');

将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp 复制代码
// native.cc
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "student.h"
#include "student-odb.hxx"

// 定义数据库连接参数:主机名、用户名、密码、数据库名、端口号、字符集
const std::string HOST = "192.168.174.128";
const std::string USER = "root";
const std::string PASSWORD = "123456";
const std::string DBNAME = "mytest";
const unsigned int PORT = 3306;
const std::string CSET = "utf8";

void native_select(std::unique_ptr<odb::mysql::database>& handler) {
    try {
        // 1.通过句柄构造事务对象
        odb::transaction tx(handler->begin());
        // 2.通过事务构造database对象
        odb::database& db = tx.database();
        // 3.原生sql语句查询
        typedef odb::query<NativeStudent> Query;
        typedef odb::result<NativeStudent> Result;
        Result native(db.query<NativeStudent>());
        for (auto it = native.begin(); it != native.end(); ++it) {
            std::cout << "班级id: " << it->classes_id << "\t";
            std::cout << "学生姓名: " << it->name << "\t";
            if (it->score) std::cout << "学生成绩: " << it->score.get() << std::endl;
        }
        // 4.提交事务
        tx.commit();
    } catch (const odb::exception& e) {
        std::cerr << "数据库操作异常: " << e.what() << std::endl;
    }
}

int main() 
{
    // 1. 构造数据库连接池
    std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(5));
    // 2.构造数据库操作句柄
    std::unique_ptr<odb::mysql::database> handler = std::make_unique<odb::mysql::database>(USER, PASSWORD, DBNAME, HOST, PORT, nullptr, CSET, 0, std::move(pool));
    // 3.数据操作:odb要求所有的数据操作,都必须在事务之内进行
    native_select(handler);

    return 0;
}

六. odb 封装

1. 设计与实现

ODB 的所有对数据库的操作都是封装好的,因此本身从功能上来说我们无需做更多的操作,因此这里封装主要封装一个 mysql 的配置结构以及一个 database 工厂类即可。

1.1 目录结构

bash 复制代码
source/
|-- odb.cc
|-- odb.h

1.2 代码实现

cpp 复制代码
// odb.h
#pragma once
#include <odb/database.hxx>
#include <odb/mysql/transaction.hxx>
#include <odb/mysql/database.hxx>

namespace xzyodb {
    // 数据库连接配置结构体
    struct mysql_settings {
        std::string host;
        std::string user = "root";
        std::string passwd;
        std::string db;
        std::string cset = "utf8";
        unsigned int port = 3306;
        unsigned int connection_pool_size = 3;
    };
    // 数据库工厂类,用于创建数据库连接
    class DBFactory {
    public:
        static std::shared_ptr<odb::database> mysql(const mysql_settings& settings);
    };
}
cpp 复制代码
// odb.cc
#include "odb.h"

namespace xzyodb {
    std::shared_ptr<odb::database> DBFactory::mysql(const mysql_settings& settings) {
        // 1.创建连接池
        std::unique_ptr<odb::mysql::connection_factory> pool(new odb::mysql::connection_pool_factory(settings.connection_pool_size));
        // 2.创建数据库操作句柄
        std::shared_ptr<odb::database> handler = std::make_shared<odb::mysql::database>(settings.user, settings.passwd, settings.db, settings.host, settings.port, nullptr, settings.cset, 0, std::move(pool));
        // 3.返回数据库操作句柄
        return handler;
    }
}

2. 使用样例

1.1 目录结构

bash 复制代码
test/
|-- odb
|   |-- makefile
|   |-- odb_test.cc
|   |-- student.h

1.2 项目构建

bash 复制代码
# makefile
odb_test:odb_test.cc student-odb.cxx ../../source/odb.cc
	g++ -std=c++17 $^ -o $@ -lodb-mysql -lodb -lodb-boost -lpthread
student-odb.cxx: student.h
	odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time $^
	
.PHONY: clean
clean:
	rm -f odb_test *-odb.* *.sql

1.3 代码实现

cpp 复制代码
// student.h
#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>

// 设置数据库表名为 tbl_student
#pragma db object table("tbl_student") 
class Student {
public:
    Student(){};
    Student(const std::string& name): _name(name) {}
    // 获取/设置 _sn
    size_t sn() const { return _sn; }
    void set_sn(size_t sn) { _sn = sn; }
    // 获取/设置 _name
    const std::string& name() const { return _name; }
    void set_name(const std::string& name) { _name = name; }
    // 获取/设置 _age
    const odb::nullable<int>& age() const { return _age; }
    void set_age(odb::nullable<int> age) { _age = age; }
    // 获取/设置 _score
    const odb::nullable<double>& score() const { return _score; }
    void set_score(odb::nullable<double> score) { _score = score; }
    // 获取/设置 _birthday
    const odb::nullable<boost::posix_time::ptime>& birthday() const { return _birthday; }
    void set_birthday(odb::nullable<boost::posix_time::ptime> birthday) { _birthday = birthday; }
private:
    // 友元类 odb::access 可以访问 private 成员
    friend class odb::access; 
    // 设置 _sn 为自增主键
    #pragma db id auto 
    size_t _sn;
    // 设置 _classes_id 为索引
    #pragma db index
    size_t _classes_id;
    std::string _name;
    odb::nullable<int> _age;
    odb::nullable<double> _score;
    odb::nullable<boost::posix_time::ptime> _birthday;
};

// 设置数据库表名为 tbl_classes
#pragma db object table("tbl_classes")
class Classes {
public:
    Classes(){};
    Classes(const std::string& name): _name(name) {}
    size_t id() const { return _id; }
    void set_id(size_t id) { _id = id; }
    const std::string& name() const { return _name; }
    void set_name(const std::string& name) { _name = name; }
private:
    // 友元类 odb::access 可以访问 private 成员
    friend class odb::access;
    // 设置 _id 为自增主键
    #pragma db id auto
    size_t _id;
    // 设置 _name 类型为 VARCHAR(32)的唯一索引
    #pragma db unique type("VARCHAR(32)")
    std::string _name;
};

// 查询条件:Query::Classes::name == "class one"
#pragma db view object(Classes) \
                object(Student: Student::_classes_id == Classes::_id) \
                query((?))
struct ClassesStudent {
    std::shared_ptr<Student> student;
    std::shared_ptr<Classes> classes;
};

编译生成可执行程序和 student.sql 文件,如下:

sql 复制代码
-- 需要手动添加的sql语句 --
DROP DATABASE IF EXISTS `mytest`;
CREATE DATABASE IF NOT EXISTS `mytest`;
USE `mytest`;

-- 自动生成的sql语句 --
DROP TABLE IF EXISTS `tbl_classes`;

DROP TABLE IF EXISTS `tbl_student`;

CREATE TABLE `tbl_student` (
  `sn` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `classes_id` BIGINT UNSIGNED NOT NULL,
  `name` TEXT NOT NULL,
  `age` INT NULL,
  `score` DOUBLE NULL,
  `birthday` DATETIME NULL)
 ENGINE=InnoDB;

CREATE INDEX `classes_id_i`
  ON `tbl_student` (`classes_id`);

CREATE TABLE `tbl_classes` (
  `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(32) NOT NULL)
 ENGINE=InnoDB;

CREATE UNIQUE INDEX `name_i`
  ON `tbl_classes` (`name`);

-- 需要手动添加的sql语句 --
INSERT INTO `tbl_classes` VALUES (1, 'class one'),
                                (2, 'class two'),
                                (3, 'class three'),
                                (4, 'class four');
INSERT INTO `tbl_student` VALUES (1, 1, "zhangsan", 18, 80, "2000-01-01 00:00:00"),
                                (2, 2, "lisi", 19, 85, "2000-02-02 00:00:00"),
                                (3, 3, "wangwu", 20, 90, "2000-03-03 00:00:00"),
                                (4, 4, "zhaoliu", 21, 95, "2000-04-04 00:00:00"),
                                (5, 1, "tianqi", 22, 100, "2000-05-05 00:00:00");

将该 student.sql 文件导入 MySQL 数据库中:

登录 MySQL 数据库查看结果:

cpp 复制代码
// odb_test.cc
#include <iostream>
#include "../../source/odb.h"
#include "student.h"
#include "student-odb.hxx"

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

    return 0;
}
相关推荐
小高不会迪斯科9 小时前
CMU 15445学习心得(二) 内存管理及数据移动--数据库系统如何玩转内存
数据库·oracle
e***89010 小时前
MySQL 8.0版本JDBC驱动Jar包
数据库·mysql·jar
l1t10 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
失忆爆表症11 小时前
03_数据库配置指南:PostgreSQL 17 + pgvector 向量存储
数据库·postgresql
AI_567811 小时前
Excel数据透视表提速:Power Query预处理百万数据
数据库·excel
SQL必知必会12 小时前
SQL 窗口帧:ROWS vs RANGE 深度解析
数据库·sql·性能优化
Gauss松鼠会13 小时前
【GaussDB】GaussDB数据库开发设计之JDBC高可用性
数据库·数据库开发·gaussdb
+VX:Fegn089513 小时前
计算机毕业设计|基于springboot + vue鲜花商城系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
识君啊13 小时前
SpringBoot 事务管理解析 - @Transactional 的正确用法与常见坑
java·数据库·spring boot·后端
一个天蝎座 白勺 程序猿14 小时前
破译JSON密码:KingbaseES全场景JSON数据处理实战指南
数据库·sql·json·kingbasees·金仓数据库