在这里插入图片描述

Ⅰ. ODB 的介绍

https://codesynthesis.com/products/odb/doc/manual.xhtml

https://codesynthesis.com/products/odb/

https://codesynthesis.com/products/odb/doc/install-build2.xhtml

ODB 是对象数据库(Object Database)的缩写。它是一种将对象模型直接映射到数据库中的数据库管理系统,与传统的关系型数据库(RDBMS)相比,ODB 允许开发者直接存储、查询和操作对象,而不需要像关系型数据库那样将对象转换成行和列的形式。

简单的说就是 ODB 可以将数据结构与数据库操作进行映射,之后通过操作数据结构来完成数据库对应操作

ODB 的基本特点如下所示:

  1. 面向对象ODB 直接存储面向对象编程语言中的对象。它支持对象之间的关系(如继承、关联、聚合等),可以将对象模型直接映射到数据库中,而无需传统的关系型表格结构。
  2. 透明持久化:开发者不需要处理数据库的低级细节。对象会自动持久化到数据库,开发者只需关注对象的创建、读取、更新和删除(CRUD)操作,而不需要编写 SQL 查询语句。
  3. 对象与数据库的映射ODB 使用特定的对象关系映射(ORM)技术来处理对象与数据库之间的映射,通常是通过一个对象数据库管理系统(ODBMS)来实现。
  4. 查询语言:与 SQL 不同,ODB 通常会使用特定于对象模型的查询语言,有时是类似于编程语言的表达式,或者是特定的查询 API。

ODB 使用示例:假设你使用 C++ODBC++ 中的对象数据库),你的类可以直接映射到数据库表:

#include <odb/core.hxx>

class Employee {
public:
    int id;
    std::string name;
    double salary;

    Employee() = default;
    Employee(int id, const std::string &name, double salary)
        : id(id), name(name), salary(salary) {}
};

#pragma db object(Employee) // 这会将 Employee 类映射到数据库中的表

​ 在这个例子中,你不需要编写 SQL 来插入、查询或更新 Employee 对象,而是通过 ODB 提供的 API 来操作。

Ⅱ. ODB 的安装

安装 build2

​ 因为 build2 安装时,有可能会版本更新,从 16 变成 17,或从 1718,因此注意,先从 build2 官网查看安装步骤:https://build2.org/install.xhtml#unix。

curl -sSfO https://download.build2.org/0.17.0/build2-install-0.17.0.sh
sudo sh build2-install-0.17.0.sh --timeout 1800

​ 整个过程需要大概半小时,耐心等待!

安装 odb-compiler

sudo apt-get install gcc-13-plugin-dev  # 安装对应gcc版本的插件,可以用gcc -v查看
mkdir odb-build && cd odb-build
sudo bpkg create -d odb-gcc-N cc \
 config.cxx=g++ \
 config.cc.coptions=-O3 \
 config.bin.rpath=/usr/lib \
 config.install.root=/usr/ \
 config.install.sudo=sudo
cd odb-gcc-N/
sudo bpkg build odb@https://pkg.cppget.org/1/beta
sudo bpkg test odb
sudo bpkg install odb
odb --version # 验证是否安装成功

# 如果失败了,才执行下面指令:
sudo echo 'export PATH=${PATH}:/usr/local/bin' >> ~/.bashrc
export PATH=${PATH}:/usr/local/bin

安装 ODB 运行时库

​ 这里需要 进入到前面安装 odb-compiler 时候创建的 odb-build 目录中

liren@hcss-ecs-7ba8:~/odb-build$ ls
odb-gcc-N
liren@hcss-ecs-7ba8:~/odb-build$ sudo bpkg create -d libodb-gcc-N cc \
 config.cxx=g++ \
 config.cc.coptions=-O3 \
 config.install.root=/usr/ \
 config.install.sudo=sudo
liren@hcss-ecs-7ba8:~/odb-build$ cd libodb-gcc-N/

# 然后执行以下指令:
sudo bpkg add https://pkg.cppget.org/1/beta
sudo bpkg fetch
sudo bpkg build libodb
sudo bpkg build libodb-mysql

安装 mysql 和客户端开发包

sudo apt install -y mysql-server 
sudo apt install -y libmysqlclient-dev

​ 配置 mysql

sudo vim /etc/mysql/my.cnf

# 添加以下内容
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
character-set-server=utf8
bind-address = 0.0.0.0

​ 修改 root 用户密码:

sudo cat /etc/mysql/debian.cnf  # 在文件中找到临时密码

sudo mysql -u debian-sys-maint -p 
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';
FLUSH PRIVILEGES;

​ 重启 mysql,并设置开机启动:

sudo systemctl restart mysql
sudo systemctl enable mysql

安装 boost profile 库

sudo bpkg build libodb-boost

总体打包安装

sudo bpkg install --all --recursive

​ 安装完即可查看头文件是否存在:

在这里插入图片描述

总体卸载

sudo bpkg uninstall --all --recursive

总体升级

sudo bpkg fetch
sudo bpkg status
sudo bpkg uninstall --all --recursive
sudo bpkg build --upgrade --recursive
sudo bpkg install --all --recursive

测试样例

​ 这里具体的语法我们先不讲,先测试一下程序能不能跑通即可!

​ 首先创建一个 person.hxx 文件:

#pragma once
#include <string>
#include <cstddef> // std::size_t
#include <boost/date_time/posix_time/posix_time.hpp>
/*
    在 C++ 中,要使用 ODB 将类声明为持久化类,需要包含 ODB 的核心头文
    件,并使用 #pragma db object 指令
    #pragma db object 指示 ODB 编译器将 person 类视为一个持久化类。
*/
#include <odb/core.hxx>
typedef boost::posix_time::ptime ptime;
#pragma db object
class Person 
{
public:
    Person(const std::string &name, int age, const ptime &update)
        : _name(name), _age(age), _update(update)
    {}
    void age(int val) { _age = val; }
    int age() { return _age; }
    void name(const std::string& val) { _name = val; }
    std::string name() { return _name; }
    void update(const ptime &update) { _update = update; }
    std::string update() { return 
    boost::posix_time::to_simple_string(_update); }
private:
    // 将 odb::access 类作为 person 类的朋友。
    // 这是使数据库支持代码可访问默认构造函数和数据成员所必需的。
    // 如果类具有公共默认构造函数和公共数据成员或数据成员的公共访问器和修饰符,则不需要友元声明
    friend class odb::access;
    Person () {}
    //_id 成员前面的 pragma 告诉 ODB 编译器,以下成员是对象的标识符。auto 说明符指示它是数据库分配的 ID。
    #pragma db id auto // 表示 ID 字段将自动生成(通常是数据库中的主键)
    unsigned long _id;
    unsigned short _age;
    std::string _name;
    #pragma db type("TIMESTAMP") not_null
    boost::posix_time::ptime _update;
};
//将 ODB 编译指示组合在一起,并放在类定义之后。它们也可以移动到一个单独的标头中,使原始类完全保持不变
// #pragma db object(person)
// #pragma db member(person::_name) id
//完成后,需要使用 odb 编译器将当前所写的代码生成数据库支持代码
//odb -d mysql --generate-query --generate-schema person.hxx
//如果用到了 boost 库中的接口,则需要使用选项 : --profile boost/date-time

//odb -d mysql --generate-query --generate-schema --profile boost/date-time person.hxx

​ 然后输入 odb -d mysql --generate-query --generate-schema --profile boost/date-time person.hxx,会生成一系列支持文件!

​ 此时我们修改一下其中的 person.sql,如下所示:

/* This file was generated by ODB, object-relational mapping (ORM)
 * compiler for C++.
 */

DROP DATABASE IF EXISTS `TestDB`;
CREATE DATABASE `TestDB`;
USE `TestDB`;

DROP TABLE IF EXISTS `Person`;

CREATE TABLE `Person` (
  `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `age` SMALLINT UNSIGNED NOT NULL,
  `name` TEXT NOT NULL,
  `update` TIMESTAMP NOT NULL)
 ENGINE=InnoDB;

​ 然后将该 sql 文件导入到服务器中,如下所示:

liren@hcss-ecs-7ba8:~/chat_platform/test/odb$ ls
main.cc  makefile  person.hxx  person-odb.cxx  person-odb.hxx  person-odb.ixx  person.sql
liren@hcss-ecs-7ba8:~/chat_platform/test/odb$ mysql -uroot -p < person.sql
Enter password: 
liren@hcss-ecs-7ba8:~/chat_platform/test/odb$

​ 然后就编写 main.cc 文件:

#include <string>
#include <memory> // std::auto_ptr
#include <cstdlib> // std::exit
#include <iostream>
#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include "person.hxx"
#include "person-odb.hxx"

int main() {
    std::shared_ptr<odb::core::database> db(
        new odb::mysql::database("root", "TThh1314520!", 
        "TestDB", "127.0.0.1", 0, 0, "utf8"));
    if (!db) { return -1; }
    ptime p = boost::posix_time::second_clock::local_time();
    Person zhang("小张", 18, p);
    Person wang("小王", 19, p);
    typedef odb::query<Person> query;
    typedef odb::result<Person> result;
    { 
        odb::core::transaction t(db->begin());
        size_t zid = db->persist(zhang);
        size_t wid = db->persist(wang);
        t.commit();
    } 
    { 
        odb::core::transaction t (db->begin());
        result r (db->query<Person>());
        for (result::iterator i(r.begin()); i != r.end(); ++i) {
            std::cout << "Hello, " << i->name() << " ";
            std::cout << i->age() << " " << i->update() << 
            std::endl;
        } 
        t.commit();
    } 
    return 0;
}

​ 还有 makefile 文件:

main : main.cc person-odb.cxx
	c++ $^ -o $@  -lodb-mysql -lodb -lodb-boost

​ 最后执行成功说明已经安装完毕!

在这里插入图片描述

Ⅲ. ODB 常见操作介绍

ODB 类型映射

​ 无需记忆,了解即可!

在这里插入图片描述

ODB 编程

ODB 在数据元结构定义时,使用预处理器指令(#pragma)来提供元数据,这些元数据指示如何将 C++ 类型映射到数据库模式。

​ 这些 #pragma 指令是在 C++ 代码中使用的,它们不是 C++ 语言的一部分,而是特定于 ODB 编译器的扩展!

​ 一般我们只需要在对应类中比如某个成员变量的上方使用这些宏指令即可,如下所示:

#pragma db object table("person")  // 告诉 ODB 编译器将这个类映射为数据库中的一张名为 "person" 的表
class Person {
    public:
        ...
    private:
        // _id 成员前面的 pragma 告诉 ODB 编译器,以下成员是对象的标识符
        // auto 说明符指示它是由数据库自动分配的 ID
        #pragma db id auto 
        unsigned long _id;  // 数据库中的主键字段,类型为 unsigned long
        
        // user_age 对应数据库中的列 "user_age"
        // type("decimal") 指定数据库列的类型为 decimal
        // default(20) 为该字段设置默认值为 20
        #pragma db column("user_age") type("decimal") default(20)
        unsigned short _age;  // 存储用户年龄,类型为 unsigned short

        // 设置 _name 字段为唯一约束,意味着数据库中的每个名称必须是唯一的
        #pragma db unique 
        std::string _name;  // 存储用户名称,类型为 std::string

        // _update 字段对应数据库中的一个时间戳字段(TIMESTAMP 类型)
        // index 表示该字段应当被索引,以便更快地查询
        #pragma db type("TIMESTAMP") index
        boost::posix_time::ptime _update;  // 存储最后更新时间,类型为 boost::posix_time::ptime

        ...
};

​ 以下是 ODB 中常用的一些 #pragma 指令:

  1. #pragma db object:用于声明一个类是数据库对象,即这个类将映射到数据库中的一个表。

  2. #pragma db table(“table_name”):指定类映射到数据库中的表名。如果不指定,则默认使用类名。

  3. #pragma db id:标记类中的一个成员变量作为数据库表的主键。

  4. #pragma db column(“column_name”):指定类成员映射到数据库表中的列名。如果不指定,则默认使用成员变量的名字。

  5. #pragma db view:用于声明一个类是一个数据库视图,而不是一个表

  6. #pragma db query(“query”):用于定义自定义的查询函数

    // 比如创建一个视图,并且含有过滤条件的语句,那么下面的 (?) 是一个占位符,表示可能包含的过滤条件,如 where age > 18
    // 然后这个 all_name 类就会被映射为一个视图,里面存放的就是过滤条件后的 name 字段!
    #pragma db view query("select name from Student" + (?))
    class all_name {
        std::string name;
    };
    
  7. #pragma db session:用于声明一个全局或成员变量是数据库会话

  8. #pragma db default(“default_value”):指定成员变量的默认值

  9. #pragma db index:指定成员变量应该被索引

  10. #pragma db unique:指定成员变量或一组变量应该具有唯一性约束

  11. #pragma db null:指定成员变量允许为空

  12. #pragma db not_null:指定成员变量不允许为空

  13. #pragma db auto:指定成员变量的值在插入时自动生成(例如,自动递增的主键)

  14. #pragma db transient:指定成员变量不应该被持久化到数据库中

  15. #pragma db type(“type_name”):指定成员变量的数据库类型。

  16. #pragma db convert(“converter”):指定用于成员变量的自定义类型转换器

  17. #pragma db pool(“pool_name”):指定用于数据库连接的连接池

  18. #pragma db trigger(“trigger_name”):指定在插入、更新或删除操作时触发的触发器

类与接口介绍

  • new_connection_factory:这是一个用于创建 MySQL 连接池的工厂类。通过设置最大连接数、最小连接数和连接检查(ping)等参数来管理数据库连接。
  • database:提供数据库操作的基本接口。包括对数据的新增(persist)、更新(update)、删除(erase)操作,以及支持 SQL 查询(query)和条件查询(query_one)等。
  • transaction:用于数据库事务的管理。支持事务的提交和回滚,确保数据的一致性。
  • nullable:该模板类用于封装可能为空的字段,类似智能指针,提供访问封装对象的接口。比如数据库中可能有 NULL 值的字段,使用该类可以方便地处理。
  • result:封装了查询结果集,提供对查询结果的访问(如迭代器、大小等)。它是一个容器类,表示从数据库查询返回的数据集。
  • query_base:查询条件的基类,封装了一个查询条件字符串,并提供获取查询条件子句的接口。
  • mysql::queryMySQL 特定的查询条件类,继承自 query_base,并且支持 MySQL 特有的查询操作。它可以处理动态 SQL 查询,构建条件并传递给数据库。
  • query<T, mysql::query_base>:特化的查询类,用于处理 MySQL 查询条件,并支持 MySQL 的查询接口。
namespace odb 
{
    namespace mysql 
    {
        // MySQL 连接池工厂类,用于创建 MySQL 连接池对象
        class LIBODB_MYSQL_EXPORT new_connection_factory : public connection_pool_factory 
        {
        public:
            // 构造函数,初始化连接池的最大连接数、最小连接数以及是否进行连接检测(ping)
            new_connection_factory(std::size_t max_connections = 0,
                                    std::size_t min_connections = 0,
                                    bool ping = true);
        };

    }  // namespace mysql

    // 数据库操作句柄类,提供与数据库的交互接口
    class LIBODB_EXPORT database 
    {
    public:
        // 保存对象(新增数据)
        template <typename T>
        void persist(T& object);

        // 更新对象
        template <typename T>
        void update(T& object);

        // 删除对象
        template <typename T>
        void erase(T& object);

        // 删除符合条件的数据(通过 SQL 查询)
        unsigned long long erase_query(const std::string& query);
        unsigned long long erase_query(const odb::query<T>& query);

        // 执行 SQL 查询,返回查询结果
        template <typename T>
        result<T> query(const std::string& sql);

        // 执行查询,并返回查询结果(支持缓存)
        template <typename T>
        result<T> query(const odb::query<T>& query, bool cache = true);

        // 执行查询,返回符合条件的第一个对象
        template <typename T>
        typename result<T>::pointer_type query_one(const odb::query<T>& query);

        // 开始一个事务
        virtual transaction_impl* begin() = 0;
    };

    // 数据库事务类,用于提交和回滚事务
    class LIBODB_EXPORT transaction 
    {
    public:
        // 构造函数,接受事务实现对象和一个可选的标志来设置当前事务
        transaction(transaction_impl*, bool make_current = true);

        void commit();	 // 提交事务
        void rollback(); // 回滚事务
    };

    // 模板类,用于封装可能为空的字段,类似于智能指针
    template <typename T>
    class nullable 
    {
    public:
        typedef T value_type;

        // 获取封装的对象(非常量)
        T& get();

        // 获取封装的对象(常量)
        const T& get() const;

        // 重载箭头运算符,用于访问封装对象的成员
        T* operator->();

        // 重载箭头运算符(常量),用于访问封装对象的成员
        const T* operator->() const;

        // 重载解引用运算符
        T& operator*();

        // 重载解引用运算符(常量)
        const T& operator*() const;
    };

    // 查询结果封装类,表示一个查询的结果集
    template <typename T>
    class result : public result_base<T, class_traits<T>::kind> 
    {
    public:
        result();				// 默认构造函数
        result(const result& r); // 拷贝构造函数

        // 获取结果集的迭代器(开始)
        iterator begin();

        // 获取结果集的迭代器(结束)
        iterator end();

        // 获取结果集的大小
        size_type size();

        // 检查结果集是否为空
        bool empty();
    };

    // 查询条件基类,封装查询的条件
    class LIBODB_EXPORT query_base 
    {
    public:
        // 构造函数,接受原生的查询条件字符串
        explicit query_base(const std::string& native);

        // 获取查询条件的子句
        const clause_type& clause();
    };

    // MySQL 查询条件类,继承自 query_base
    namespace mysql 
    {
        template <typename T>
        class query : public query_base, public query_selector<T, id_mysql>::columns_type 
        {
        public:
            // 构造函数,接受查询条件字符串
            query(const std::string& q);

            // 构造函数,接受其他查询条件对象
            query(const query_base& q);

            // 构造函数,接受布尔值(用于某些特定场景)
            query(bool v);
        };
    }  // namespace mysql

    // 另一种针对 MySQL 查询的模板特化(用于传入 MySQL 特定的查询条件)
    template <typename T>
    class query<T, mysql::query_base> : public mysql::query<T> 
    {
    public:
        // 构造函数,接受布尔值
        query(bool v);

        // 构造函数,接受查询条件字符串
        query(const std::string& q);

        // 构造函数,接受 MySQL 查询基类对象
        query(const mysql::query_base& q);
    };

}  // namespace odb

测试样例

#pragma once  
#include <string>
#include <cstddef> // std::size_t
#include <boost/date_time/posix_time/posix_time.hpp>  // 引入 boost 日期时间库,用于处理时间相关类型
#include <odb/nullable.hxx>  // ODB 库的可空类型,用于表示可以为空的字段
#include <odb/core.hxx>  // ODB 核心库,提供 ORM(对象关系映射)功能

// ODB 注解,指定此类映射为数据库中的一张表
#pragma db object
class Student
{
public:
    // 默认构造函数
    Student() {}

    // 带参数的构造函数,初始化学生信息
    Student(unsigned long sn, const std::string &name, unsigned short age, unsigned long cid):
        _sn(sn), _name(name), _age(age), _classes_id(cid)
    {}

    unsigned long sn() { return _sn; }
    void sn(unsigned long num) { _sn = num; }

    std::string name() { return _name; }
    void name(const std::string &name) { _name = name; }

    odb::nullable<unsigned short> age() { return _age; }
    void age(unsigned short num) { _age = num; }

    unsigned long classes_id() { return _classes_id; }
    void classes_id(unsigned long cid) { _classes_id = cid; }
private:
    // ODB需要访问私有成员
    friend class odb::access;

    // 数据库中自动生成的主键 ID
    #pragma db id auto
    unsigned long _id;

    // 学号,唯一约束
    #pragma db unique
    unsigned long _sn;

    // 学生姓名
    std::string _name;

    // 学生年龄,使用 nullable 类型,表示此字段可能为空
    odb::nullable<unsigned short> _age;

    // 班级 ID,索引字段
    #pragma db index
    unsigned long _classes_id;
};

// ODB注解,指定此类映射为数据库中的一张表
#pragma db object
class Classes 
{
public:
    Classes() {}
    Classes(const std::string &name) : _name(name) {}

    std::string name() { return _name; }
    void name(const std::string &name) { _name = name; }
private:
    // ODB需要访问私有成员
    friend class odb::access;

    // 数据库中自动生成的主键 ID
    #pragma db id auto
    unsigned long _id;

    // 班级名称
    std::string _name;
};

// 查询所有的学生信息,并显示班级名称
// 使用 ODB 的视图(view),通过关联 Student 和 Classes 表
#pragma db view object(Student)\  
                object(Classes = classes : Student::_classes_id == classes::_id)\  
                query((?))  
struct classes_student {
    // 映射查询结果中的字段到结构体成员
    #pragma db column(Student::_id)  // 学生 ID
    unsigned long id;

    #pragma db column(Student::_sn)  // 学生学号
    unsigned long sn;

    #pragma db column(Student::_name)  // 学生姓名
    std::string name;

    #pragma db column(Student::_age)  // 学生年龄(可能为空)
    odb::nullable<unsigned short> age;

    #pragma db column(classes::_name)  // 班级名称
    std::string classes_name;
};

// 查询所有学生姓名,外部调用时传入的过滤条件
#pragma db view query("select name from Student where" + (?))  // 查询语句与外部传入的过滤条件拼接
struct all_name {
    // 映射查询结果中的学生姓名字段
    std::string name;
};

​ 生成 ODBMySQL 相关数据库模式和查询代码:odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time student.hxx

下面解释一下这串命令的含义:

  • -d mysql 表示数据库类型为 MySQL
  • --generate-query 生成查询接口
  • --generate-schema 生成数据库模式
  • --profile boost/date-time 表示使用 Boost 日期时间库配置
  • student.hxx 是头文件路径

​ 然后解释一下这小段代码:

#pragma db view object(Student)\  
                object(Classes = classes : Student::_classes_id == classes::_id)\  
                query((?))  
  1. #pragma db view:这是一个 ODB 编译指令,定义一个视图。
  2. object(Student):这指定视图中的第一个对象为 Student 类。也就是说,视图查询的数据将来自数据库中的 Student 表,并映射到 Student 类。
  3. object(Classes = classes : Student::_classes_id == classes::_id):这是对 Student 类和 Classes 类之间的联接(JOIN)的描述。它表示 Student 类的 _classes_id 字段与 Classes 类的 _id 字段相匹配。
    • Classes = classes:这里定义了一个别名 classes,用于引用 Classes 类。
    • Student::_classes_id == classes::_id:表示联接条件,即 Student 表中的 _classes_id 字段与 Classes 表中的 _id 字段相等。
    • 两者之间用 : 隔开。
    • 这意味着你查询的数据将包含 Student 表和 Classes 表联接后的信息。
  4. query((?)):这是查询的过滤条件,? 是一个占位符,它将被外部调用时传入的实际查询条件所替代。比如,外部传入的条件可能是 where age > 18where classes_name = 'Mathematics'。这个部分使得查询变得灵活,可以根据需要动态地调整查询条件。

​ 最后别忘了把 student.sql 导入到数据库中:mysql -uroot -p < student.sql

​ 然后就是 main.cc 文件,主要完成增删改查的操作:

#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include "student.hxx"
#include "student-odb.hxx"
#include <gflags/gflags.h>

DEFINE_string(host, "127.0.0.1", "这是Mysql服务器地址");
DEFINE_int32(port, 3306, "这是Mysql服务器端口");
DEFINE_string(db, "TestDB", "数据库默认库名称");
DEFINE_string(user, "root", "这是Mysql用户名");
DEFINE_string(pswd, "TThh1314520!", "这是Mysql密码");
DEFINE_string(cset, "utf8", "这是Mysql客户端字符集");
DEFINE_int32(max_pool, 3, "这是Mysql连接池最大连接数量");

void insert_classes(odb::mysql::database &db) 
{
    try {
        odb::transaction trans(db.begin()); // 获取事务对象开启事务

        Classes c1("一年级一班");
        Classes c2("一年级二班");
        db.persist(c1);
        db.persist(c2);
        
        trans.commit(); // 提交事务
    }catch (std::exception &e) {
        std::cout << "插入数据出错:" << e.what() << std::endl;
    }
}

void insert_student(odb::mysql::database &db) 
{
    try {
        odb::transaction trans(db.begin()); // 获取事务对象开启事务

        Student s1(1, "张三", 18, 1);
        Student s2(2, "李四", 19, 1);
        Student s3(3, "王五", 18, 1);
        Student s4(4, "赵六", 15, 2);
        Student s5(5, "刘七", 18, 2);
        Student s6(6, "孙八", 23, 2);
        db.persist(s1);
        db.persist(s2);
        db.persist(s3);
        db.persist(s4);
        db.persist(s5);
        db.persist(s6);

        trans.commit(); // 提交事务
    }catch (std::exception &e) {
        std::cout << "插入学生数据出错:" << e.what() << std::endl;
    }
}

void update_student(odb::mysql::database &db, Student &stu)
{
    try {
        odb::transaction trans(db.begin()); // 获取事务对象开启事务

        db.update(stu);
        
        trans.commit(); // 提交事务
    }catch (std::exception &e) {
        std::cout << "更新学生数据出错:" << e.what() << std::endl;
    }
}

Student select_student(odb::mysql::database &db)
{
    Student res;
    try {
        odb::transaction trans(db.begin()); // 获取事务对象开启事务

        typedef odb::query<Student> query;
        typedef odb::result<Student> result;

        result r(db.query<Student>(query::name == "张三"));
        if (r.size() != 1) {
            std::cout << "数据量不对!\n";
            return Student();
        }
        res = *r.begin(); // r类似一个迭代器,要解引用取出元素

        trans.commit(); // 提交事务
    }catch (std::exception &e) {
        std::cout << "更新学生数据出错:" << e.what() << std::endl;
    }
    return res;
}

void remove_student(odb::mysql::database &db)
{
    try {
        odb::transaction trans(db.begin()); // 获取事务对象开启事务

        typedef odb::query<Student> query;
        db.erase_query<Student>(query::classes_id == 2);
        
        trans.commit(); // 提交事务
    }catch (std::exception &e) {
        std::cout << "更新学生数据出错:" << e.what() << std::endl;
    }
}

void classes_student(odb::mysql::database &db)
{
    try {
        odb::transaction trans(db.begin()); // 获取事务对象开启事务

        typedef odb::query<struct classes_student> query;
        typedef odb::result<struct classes_student> result;
        result r(db.query<struct classes_student>(query::classes::id == 1));
        for (auto it = r.begin(); it != r.end(); ++it) {
            std::cout << it->id << "  ";
            std::cout << it->sn << "  ";
            std::cout << it->name << "  ";
            std::cout << *it->age << "  ";
            std::cout << it->classes_name << std::endl;
        }
        
        trans.commit(); // 提交事务
    }catch (std::exception &e) {
        std::cout << "更新学生数据出错:" << e.what() << std::endl;
    }
}

void all_student(odb::mysql::database &db)
{
    try {
        odb::transaction trans(db.begin()); // 获取事务对象开启事务

        typedef odb::query<Student> query;
        typedef odb::result<struct all_name> result;
        result r(db.query<struct all_name>(query::id == 1));
        for (auto it = r.begin(); it != r.end(); ++it) {
            std::cout << it->name << std::endl;
        }
        
        trans.commit(); // 提交事务
    }catch (std::exception &e) {
        std::cout << "查询所有学生姓名数据出错:" << e.what() << std::endl;
    }
}

int main(int argc, char *argv[])
{
    google::ParseCommandLineFlags(&argc, &argv, true);

    // 1. 构造连接池工厂配置对象
    std::unique_ptr<odb::mysql::connection_pool_factory> cpf(
         new odb::mysql::connection_pool_factory(FLAGS_max_pool, 0));

    // 2. 构造数据库操作对象
    odb::mysql::database db(
        FLAGS_user, FLAGS_pswd, FLAGS_db,
        FLAGS_host, FLAGS_port, "", FLAGS_cset,
        0, std::move(cpf));

    // 3. 数据操作
    // insert_classes(db);
    // insert_student(db);
    // auto stu = select_student(db);
    // std::cout << stu.sn() << std::endl;
    // std::cout << stu.name() << std::endl;
    // if (stu.age()) std::cout << *stu.age() << std::endl;
    // std::cout << stu.classes_id() << std::endl;

    // stu.age(15);
    // update_student(db, stu);
    // remove_student(db);
    // classes_student(db);
    all_student(db);
    return 0;
}

在这里插入图片描述

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐