[C++][第三方库][ODB]详细讲解


1.介绍

  • ODB框架:数据库ORM框架 --> 对象关系映射框架
  • 形象理解将数据结构与数据库表进行关系映射,通过数据结构的操作实现数据库中数据操作
    • 通过ODB框架实现MySQL数据库的关系映射操作

2.安装

1.安装 build2

  • 因为build2安装时,可能会版本更新,因此注意, 先从build2官网查看安装步骤
  • 如果安装过程中,因为网络问题超时失败,可以尝试:将超时时间设置的更长一些
    sh build2-install-0.17.0.sh --timeout 1800
    

2.安装 odb-compiler

#注意这里的gcc-13需要根据自己现有版本而定
~/workspace$ sudo apt-get install gcc-13-plugin-dev
~/workspace$ mkdir odb-build && cd odb-build

~/workspace/odb-build$ 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

~/workspace/odb-build$ cd odb-gcc-N
~/workspace/odb-build/odb-gcc-N$ bpkg build odb@https://pkg.cppget.org/1/beta
~/workspace/odb-build/odb-gcc-N$ bpkg test odb
~/workspace/odb-build/odb-gcc-N$ bpkg install odb
~/workspace/odb-build/odb-gcc-N$ odb --version

# 如果上述命令报错找不到odb,则执行下面的命令
$ sudo echo 'export PATH=${PATH}:/usr/local/bin' >> ~/.bashrc
$ export PATH=${PATH}:/usr/local/bin
$ odb --version

3.安装 ODB 运行时库

~/workspace/odb-build/odb-gcc-N$ cd ..

~/workspace/odb-build$ bpkg create -d libodb-gcc-N cc \
    config.cxx=g++ \
    config.cc.coptions=-O3 \
    config.install.root=/usr/ \
    config.install.sudo=sudo

~/workspace/odb-build$ cd libodb-gcc-N
~/workspace/odb-build/libodb-gcc-N$ bpkg add https://pkg.cppget.org/1/beta
~/workspace/odb-build/libodb-gcc-N$ bpkg fetch
~/workspace/odb-build/libodb-gcc-N$ bpkg build libodb
~/workspace/odb-build/libodb-gcc-N$ bpkg build libodb-mysql

4.安装MySQL和客户端开发包

  • 安装
    sudo apt install mysql-server
    sudo apt install -y libmysqlclient-dev
    
  • 配置MySQLsudo vim /etc/my.cnf或者/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
    Enter password: # 这里输入上边看到的密码
    
    mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'PASSWORD';
    		
    mysql> FLUSH PRIVILEGES;
    
    mysql> quit
    
  • 重启MySQL,并设置开机启动
    sudo systemctl restart mysql 
    sudo systemctl enable mysql
    

5.安装 boost profile 库

bpkg build libodb-boost

6.总体操作

  • 总体打包安装
    bpkg install --all --recursive
    
  • 总体卸载
    bpkg uninstall - all --recursive
    
  • 总体升级
    bpkg fetch
    bpkg status
    bpkg uninstall - all --recursive
    bpkg build --upgrade --recursive
    bpkg install --all --recursive
    

7.测试样例

  • 编写数据结构文件person.hpp
    #pragma once
    #include <string>
    #include <cstddef>
    #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/datetime
    // 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.hpp
    
    $ ls
    person.hpp  person-odb.cxx  person-odb.hxx  person-odb.ixx  person.sql
    
  • 编写主函数代码main.cc
    #include <string>
    #include <memory>
    #include <cstdlib>
    #include <iostream>
    #include <odb/database.hxx>
    #include <odb/mysql/database.hxx>
    #include "person.hpp"
    #include "person-odb.hxx"
    
    int main()
    {
        std::shared_ptr<odb::core::database> db(new odb::mysql::database
                ("root", "SnowK8989", "TestDB", "127.0.0.1", 0, 0, "utf8"));
        if (!db)
        {
            return -1;
        }
    
        ptime p = boost::posix_time::second_clock::local_time();
        Person Die("Die", 18, p);
        Person SnowK("SnowK", 19, p);
    
        typedef odb::query<Person> query;
        typedef odb::result<Person> result;
    
        // 新增数据
        {
            odb::core::transaction t(db->begin());
            size_t zid = db->persist(Die);
            size_t wid = db->persist(SnowK);
            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;
    }
    
    // 如果用到了boost库中的接口,需要链接库: -lodb-boost
    // c++ -o mysql_test mysql_test.cpp person-odb.cxx -lodb-mysqllodb - lodb - boost
    
  • 代码编译
    c++ -o test test.cpp person odb.cxx -lodb-mysql -lodb -lodb-boost
    

3.ODB 常见操作

1.ODB 类型映射

请添加图片描述


2.ODB 编程

1.指令

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

      • #pragma db object:用于声明一个类是数据库对象
        • 这个类将映射到数据库中的一个表
      • #pragma db table("table_name"):指定类映射到数据库中的表名
        • 如果不指定,则默认使用类名
    • 字段的映射

      • #pragma db id:标记类中的一个成员变量作为数据库表的主键
      • #pragma db column("column_name"):指定类成员映射到数据库表中的列名
        • 如果不指定,则默认使用成员变量的名字
      • #pragma db auto:指定成员变量的值在插入时自动生成
        • 例如:自动递增的主键
      • #pragma db type("type_name"):指定成员变量不应该被持久化到数据库中
      • #pragma db unique:指定成员变量或一组变量应该具有唯一性约束
      • #pragma db index("index_name"):指定成员变量应该被索引
      • #pragma db null:指定成员变量允许为空
        • odb::nullable<>也可以实现
      • #pragma db not_null:指定成员变量不允许为空
      • #pragma db default("default_value"):指定成员变量的默认值
    • 查询相关

      • #pragma db view:用于声明一个类是一个数据库视图,而不是一个表
      • #pragma db query("query"):用于定义自定义的查询函数
    • 其它

      • #pragma db session:用于声明一个全局或成员变量是数据库会话
      • #pragma db transient:指定成员变量不应该被持久化到数据库中
      • #pragma db convert("converter"):指定用于成员变量的自定义类型转换器
      • #pragma db pool("pool_name"):指定用于数据库连接的连接池
      • #pragma db trigger("trigger_name"):指定在插入、更新或删除操作时触发的触发器

2.示例

#pragma once
#include <string>
#include <cstddef>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <odb/nullable.hxx>
#include <odb/core.hxx>

#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) 
    {}

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

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

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

    void classes_id(unsigned long cid) { _classes_id = cid; }
    unsigned long classes_id() { return _classes_id; }

private:
    // 将odb::access类作为Person类的友元
    // 这是使数据库支持代码可访问默认构造函数和数据成员所必需的
    friend class odb::access;

    // id: 标记成员变量为主键, auto: 值在插入时自动生成
    #pragma db id auto
    unsigned long _id;

    // unique: 指定变量或一组变量具有唯一键约束
    #pragma db unique
    unsigned long _sn;
    std::string _name;
    odb::nullable<unsigned short> _age;

    // 指定成员变量应该被索引
    #pragma db index
    unsigned long _classes_id;
};

#pragma db object
class Classes
{
public:
    Classes() {}
    Classes(const std::string &name) 
        : _name(name) 
    {}

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

private:
    friend class odb::access;

    #pragma db id auto
    unsigned long _id;
    std::string _name;
};

// 查询所有的学生信息, 并显示班级名称
#pragma db view object(Student)\
                object(Classes = classes : Student::_classes_id == classes::_id)\
                query((?)) // 用于自定义查询函数
struct Classes_Student
{
    // 指定类成员映射到数据库表中的列名
    #pragma db column(Student::_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 + (?)")
struct All_Name
{
    std::string name;
};

// odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time student.hpp

4.类与接口

namespace odb
{ 
    namespace mysql 
    { 
        // mysql连接池对象类
        class LIBODB_MYSQL_EXPORT new_connection_factory:  
        public connection_pool_factory
        { 
            connection_pool_factory (std::size_t max_connections = 0, 
                                     std::size_t min_connections = 0, 
                                     bool ping = true);
        }; 
    }
    
    // 操作句柄类, 实现数据库的增删查改操作
    class LIBODB_EXPORT database 
    { 
        // 新增数据 
        persist (T& object); 
        
        // 更新数据 
        void update (T& object);

		// 删除数据
        void erase (T& object); 
        
        unsigned long long erase_query (const std::string&);
		// 过滤并删除
        unsigned long long erase_query (const odb::query<T>&);
        
        result<T> query (const std::string&); 
		// 查询
        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; 
    }; 
    
    // 事务操作类
    class LIBODB_EXPORT transaction 
    { 
        transaction (transaction_impl*, bool make_current = true); 
        void commit ();   // 事务提交操作
        void rollback (); // 事务回滚操作
    }// 针对可能为空的字段封装的类似于智能指针的类型
	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; 
	};
    
    // 针对查询结果所封装的容器类 
    template <typename T> 
    class result: result_base<T, class_traits<T>::kind> 
    { 
        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);
		const clause_type& clause ();
	};
	
	namespace mysql 
	{ 
		template <typename T> 
		class query: public query_base,
		public query_selector<T, id_mysql>::columns_type 
		{ 
			query (const std::string& q);
			query (const query_base& q); 
			query (bool v);
		};
	}     

	// 过滤条件类
    template <typename T> 
    class query<T, mysql::query_base>: public mysql::query<T>  
    { 
		query (bool v);
		query (const std::string& q);
		query (const mysql::query_base& q);
    } 
}

5.使用

  • Student.hpp
    #pragma once
    #include <string>
    #include <cstddef>
    #include <boost/date_time/posix_time/posix_time.hpp>
    #include <odb/nullable.hxx>
    #include <odb/core.hxx>
    
    #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) 
        {}
    
        void sn(unsigned long num) { _sn = num; }
        unsigned long sn() { return _sn; }
    
        void name(const std::string &name) { _name = name; }
        std::string name() { return _name; }
    
        void age(unsigned short num) { _age = num; }
        odb::nullable<unsigned short> age() { return _age; }
    
        void classes_id(unsigned long cid) { _classes_id = cid; }
        unsigned long classes_id() { return _classes_id; }
    
    private:
        // 将odb::access类作为Person类的友元
        // 这是使数据库支持代码可访问默认构造函数和数据成员所必需的
        friend class odb::access;
    
        // id: 标记成员变量为主键, auto: 值在插入时自动生成
        #pragma db id auto
        unsigned long _id;
    
        // unique: 指定变量或一组变量具有唯一键约束
        #pragma db unique
        unsigned long _sn;
        std::string _name;
        odb::nullable<unsigned short> _age;
    
        // 指定成员变量应该被索引
        #pragma db index
        unsigned long _classes_id;
    };
    
    #pragma db object
    class Classes
    {
    public:
        Classes() {}
        Classes(const std::string &name) 
            : _name(name) 
        {}
    
        void name(const std::string &name) { _name = name; }
        std::string name() { return _name; }
    
    private:
        friend class odb::access;
    
        #pragma db id auto
        unsigned long _id;
        std::string _name;
    };
    
    // 查询所有的学生信息, 并显示班级名称
    #pragma db view object(Student)\
                    object(Classes = classes : \
                    Student::_classes_id == classes::_id)\
                    query((?)) // 用于自定义查询函数
    struct Classes_Student
    {
        // 指定类成员映射到数据库表中的列名
        #pragma db column(Student::_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" + (?))
    struct All_Name
    {
        std::string name;
    };
    
    // odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time student.hpp
    
  • main.cc
    #include <odb/database.hxx>
    #include <odb/mysql/database.hxx>
    #include <gflags/gflags.h>
    #include "student.hpp"
    #include "student-odb.hxx"
    
    DEFINE_string(host, "127.0.0.1", "Mysql服务器地址");
    DEFINE_int32(port, 0, "Mysql服务器端口");
    DEFINE_string(db, "TestDB", "数据库默认库名称");
    DEFINE_string(user, "root", "Mysql用户名");
    DEFINE_string(pwd, "SnowK8989", "Mysql密码");
    DEFINE_string(cset, "utf8", "Mysql客户端字符集");
    DEFINE_int32(max_pool, 3, "Mysql连接池最大连接数量");
    
    void Insert_Classes(odb::mysql::database& db)
    {
        try
        {
            // 3.获取事务对象, 开启事务
            odb::transaction trans(db.begin());
    
            Classes c1("Electronic 221");
            Classes c2("Electronic 222");
    
            db.persist(c1);
            db.persist(c2);
    
            // 5.提交事务
            trans.commit();
        }
        catch(const std::exception& e)
        {
            std::cout << "插入数据出错: " << e.what() << std::endl;
        }
    }
    
    void Insert_Student(odb::mysql::database &db)
    {
        try
        {
            // 3.获取事务对象, 开启事务
            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);
    
            // 5. 提交事务
            trans.commit();
        }
        catch (const std::exception &e)
        {
            std::cout << "插入学生数据出错: " << e.what() << std::endl;
        }
    }
    
    // 先查询,再修改
    void Update_Student(odb::mysql::database &db, Student &stu)
    {
        try
        {
            // 3.获取事务对象, 开启事务
            odb::transaction trans(db.begin());
            db.update(stu);
    
            // 5. 提交事务
            trans.commit();
        }
        catch (const std::exception &e)
        {
            std::cout << "更新学生数据出错: " << e.what() << std::endl;
        }
    }
    
    // TODO
    Student Select_Student(odb::mysql::database &db)
    {
        Student ret;
        try
        {
            // 3.获取事务对象, 开启事务
            odb::transaction trans(db.begin());
    
            odb::result<Student> r(db.query<Student>(odb::query<Student>::name == "张三"));
            if (r.size() != 1)
            {
                std::cout << "数据量不对" << std::endl;
                return Student();
            }
    
            ret = *r.begin();
    
            // 5. 提交事务
            trans.commit();
        }
        catch (const std::exception &e)
        {
            std::cout << "更新学生数据出错: " << e.what() << std::endl;
        }
    
        return ret;
    }
    
    void Remove_Student(odb::mysql::database &db)
    {
        try
        {
            // 3.获取事务对象, 开启事务
            odb::transaction trans(db.begin());
    
            // 查询和删除操作合并
            db.erase_query<Student>(odb::query<Student>::name == "李四");
    
            // 5. 提交事务
            trans.commit();
        }
        catch (const std::exception &e)
        {
            std::cout << "更新学生数据出错: " << e.what() << std::endl;
        }
    }
    
    void Classes_Student(odb::mysql::database &db)
    {
        try
        {
            // 3.获取事务对象, 开启事务
            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::endl;
                std::cout << it->sn << std::endl;
                std::cout << it->name << std::endl;
                std::cout << *it->age << std::endl; // nullable类型类似智能指针, 需要解引用
                std::cout << it->classes_name << std::endl;
            }
    
            // 5. 提交事务
            trans.commit();
        }
        catch (const std::exception &e)
        {
            std::cout << "更新学生数据出错: " << e.what() << std::endl;
        }
    }
    
    void All_Student(odb::mysql::database &db)
    {
        try
        {
            // 3.获取事务对象, 开启事务
            odb::transaction trans(db.begin());
    
            typedef odb::query<Student> query;
            typedef odb::result<All_Name> result;
    
            result r(db.query<All_Name>(query::id == 1));
            for (auto it = r.begin(); it != r.end(); ++it)
            {
                std::cout << it->name << std::endl;
            }
    
            // 5. 提交事务
            trans.commit();
        }
        catch (const std::exception &e)
        {
            std::cout << "查询所有学生姓名数据出错: " << e.what() << std::endl;
        }
    }
    
    int main(int argc, char *argv[])
    {
        google::ParseCommandLineFlags(&argc, &argv, true);
    
        // 1.构造连接池工厂配置对象, 这里只能用unique_ptr
        auto cpf = std::make_unique<odb::mysql::connection_pool_factory>(FLAGS_max_pool, 0);
    
        // 2.构造数据库操作对象
        odb::mysql::database db(FLAGS_user, FLAGS_pwd, FLAGS_db, FLAGS_host, 
                                FLAGS_port, "", FLAGS_cset, 0, std::move(cpf));
    
        // 4.数据操作
        // Insert_Classes(db);
        // Insert_Student(db);
        // {
        //     Student stu = Select_Student(db);
        //     stu.age(22);
        //     Update_Student(db, stu);
        // }
        // Remove_Student(db);
        // Classes_Student(db);
        All_Student(db);
    
        return 0;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DieSnowK

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值