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

[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$ sudoapt-getinstall 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,则执行下面的命令 $ sudoecho'export PATH=${PATH}:/usr/local/bin'>> ~/.bashrc $ exportPATH=${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和客户端开发包

重启MySQL,并设置开机启动

sudo systemctl restart mysql sudo systemctl enable mysql 

修改root用户密码

sudocat /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 

配置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 

安装

sudoaptinstall mysql-server sudoaptinstall -y libmysqlclient-dev 

5.安装 boost profile 库

bpkg build libodb-boost 

6.总体操作

总体升级

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

总体卸载

bpkg uninstall - all --recursive 

总体打包安装

bpkg install --all --recursive 

7.测试样例

代码编译

c++ -o test test.cpp person odb.cxx -lodb-mysql -lodb -lodb-boost 

编写主函数代码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"intmain(){ 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();}return0;}// 如果用到了boost库中的接口,需要链接库: -lodb-boost// c++ -o mysql_test mysql_test.cpp person-odb.cxx -lodb-mysqllodb - lodb - boost

生成数据库支持的代码文件

$ 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 

编写数据结构文件person.hpp

#pragmaonce#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;#pragmadb objectclassPerson{public:Person(const std::string &name,int age,const ptime &update):_name(name),_age(age),_update(update){}voidage(int val){ _age = val;}intage(){return _age;}voidname(const std::string &val){ _name = val;} std::string name(){return _name;}voidupdate(const ptime &update){ _update = update;} std::string update(){return boost::posix_time::to_simple_string(_update);}private:// 将odb::access类作为Person类的友元// 这是使数据库支持代码可访问默认构造函数和数据成员所必需的// 如果类具有公共默认构造函数和公共数据成员或数据成员的公共访问器和修饰符,则不需要友元声明friendclassodb::access;Person(){}// _id 成员前面的 pragma 告诉 ODB 编译器,以下成员是对象的标识符// auto说明符指示它是数据库分配的 ID#pragmadb id auto// 表示 ID 字段将自动生成(通常是数据库中的主键)。 unsignedlong _id;unsignedshort _age; std::string _name;#pragmadb 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

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.示例

#pragmaonce#include<string>#include<cstddef>#include<boost/date_time/posix_time/posix_time.hpp>#include<odb/nullable.hxx>#include<odb/core.hxx>#pragmadb object // 声明一个类是数据库对象classStudent{public:Student(){}Student(unsignedlong sn,const std::string &name,unsignedshort age,unsignedlong cid):_sn(sn),_name(name),_age(age),_classes_id(cid){}voidsn(unsignedlong num){ _sn = num;}unsignedlongsn(){return _sn;}voidname(const std::string &name){ _name = name;} std::string name(){return _name;}voidage(unsignedshort num){ _age = num;} odb::nullable<unsignedshort>age(){return _age;}voidclasses_id(unsignedlong cid){ _classes_id = cid;}unsignedlongclasses_id(){return _classes_id;}private:// 将odb::access类作为Person类的友元// 这是使数据库支持代码可访问默认构造函数和数据成员所必需的friendclassodb::access;// id: 标记成员变量为主键, auto: 值在插入时自动生成#pragmadb id autounsignedlong _id;// unique: 指定变量或一组变量具有唯一键约束#pragmadb uniqueunsignedlong _sn; std::string _name; odb::nullable<unsignedshort> _age;// 指定成员变量应该被索引#pragmadb indexunsignedlong _classes_id;};#pragmadb objectclassClasses{public:Classes(){}Classes(const std::string &name):_name(name){}voidname(const std::string &name){ _name = name;} std::string name(){return _name;}private:friendclassodb::access;#pragmadb id autounsignedlong _id; std::string _name;};// 查询所有的学生信息, 并显示班级名称#pragmadb view object(Student)\object(Classes = classes : Student::_classes_id == classes::_id)\query((?))// 用于自定义查询函数structClasses_Student{// 指定类成员映射到数据库表中的列名#pragmadb column(Student::_id)unsignedlong id;#pragmadb column(Student::_sn)unsignedlong sn;#pragmadb column(Student::_name) std::string name;#pragmadb column(Student::_age) odb::nullable<unsignedshort> age;#pragmadb column(classes::_name) std::string classes_name;};// 只查询学生姓名, (?): 外部调用时传入的过滤条件#pragmadb view query("select name from Student + (?)")structAll_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连接池对象类classLIBODB_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);};}// 操作句柄类, 实现数据库的增删查改操作classLIBODB_EXPORT database {// 新增数据 persist(T& object);// 更新数据 voidupdate(T& object);// 删除数据voiderase(T& object);unsignedlonglongerase_query(const std::string&);// 过滤并删除unsignedlonglongerase_query(const odb::query<T>&); result<T>query(const std::string&);// 查询 result<T>query(const odb::query<T>&,bool cache =true);typenameresult<T>::pointer_type query_one(const odb::query<T>&);// 获取事务对象指针virtual transaction_impl*begin()=0;};// 事务操作类classLIBODB_EXPORT transaction {transaction(transaction_impl*,bool make_current =true);voidcommit();// 事务提交操作voidrollback();// 事务回滚操作}; // 针对可能为空的字段封装的类似于智能指针的类型template<typenameT>classnullable{typedef T value_type; T&get();const T&get()const; T*operator->();const T*operator->()const; T&operator*();const T&operator*()const;};// 针对查询结果所封装的容器类 template<typenameT>classresult:result_base<T,class_traits<T>::kind>{result();result(const result& r); iterator begin(); iterator end(); size_type size();boolempty();};// 针对查询封装的条件类classLIBODB_EXPORT query_base {explicitquery_base(const std::string& native);const clause_type&clause();};namespace mysql {template<typenameT>classquery:publicquery_base,publicquery_selector<T,id_mysql>::columns_type{query(const std::string& q);query(const query_base& q);query(bool v);};}// 过滤条件类template<typenameT>classquery<T, mysql::query_base>:public mysql::query<T>{query(bool v);query(const std::string& q);query(const mysql::query_base& q);}}

5.使用

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连接池最大连接数量");voidInsert_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;}}voidInsert_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;}}// 先查询,再修改voidUpdate_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;returnStudent();} ret =*r.begin();// 5. 提交事务 trans.commit();}catch(const std::exception &e){ std::cout <<"更新学生数据出错: "<< e.what()<< std::endl;}return ret;}voidRemove_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;}}voidClasses_Student(odb::mysql::database &db){try{// 3.获取事务对象, 开启事务 odb::transaction trans(db.begin());typedef odb::query<structClasses_Student> query;typedef odb::result<structClasses_Student> result; result r(db.query<structClasses_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;}}voidAll_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;}}intmain(int argc,char*argv[]){ google::ParseCommandLineFlags(&argc,&argv,true);// 1.构造连接池工厂配置对象, 这里只能用unique_ptrauto 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);return0;}

Student.hpp

#pragmaonce#include<string>#include<cstddef>#include<boost/date_time/posix_time/posix_time.hpp>#include<odb/nullable.hxx>#include<odb/core.hxx>#pragmadb object // 声明一个类是数据库对象classStudent{public:Student(){}Student(unsignedlong sn,const std::string &name,unsignedshort age,unsignedlong cid):_sn(sn),_name(name),_age(age),_classes_id(cid){}voidsn(unsignedlong num){ _sn = num;}unsignedlongsn(){return _sn;}voidname(const std::string &name){ _name = name;} std::string name(){return _name;}voidage(unsignedshort num){ _age = num;} odb::nullable<unsignedshort>age(){return _age;}voidclasses_id(unsignedlong cid){ _classes_id = cid;}unsignedlongclasses_id(){return _classes_id;}private:// 将odb::access类作为Person类的友元// 这是使数据库支持代码可访问默认构造函数和数据成员所必需的friendclassodb::access;// id: 标记成员变量为主键, auto: 值在插入时自动生成#pragmadb id autounsignedlong _id;// unique: 指定变量或一组变量具有唯一键约束#pragmadb uniqueunsignedlong _sn; std::string _name; odb::nullable<unsignedshort> _age;// 指定成员变量应该被索引#pragmadb indexunsignedlong _classes_id;};#pragmadb objectclassClasses{public:Classes(){}Classes(const std::string &name):_name(name){}voidname(const std::string &name){ _name = name;} std::string name(){return _name;}private:friendclassodb::access;#pragmadb id autounsignedlong _id; std::string _name;};// 查询所有的学生信息, 并显示班级名称#pragmadb view object(Student)\object(Classes = classes :\Student::_classes_id == classes::_id)\query((?))// 用于自定义查询函数structClasses_Student{// 指定类成员映射到数据库表中的列名#pragmadb column(Student::_id)unsignedlong id;#pragmadb column(Student::_sn)unsignedlong sn;#pragmadb column(Student::_name) std::string name;#pragmadb column(Student::_age) odb::nullable<unsignedshort> age;#pragmadb column(classes::_name) std::string classes_name;};// 只查询学生姓名, (?): 外部调用时传入的过滤条件#pragmadb view query("select name from Student"+(?))structAll_Name{ std::string name;};// odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time student.hpp

Read more

【C++经典例题】字符串转整数(atoi)的实现与解析

【C++经典例题】字符串转整数(atoi)的实现与解析

💓 博客主页:倔强的石头的ZEEKLOG主页             📝Gitee主页:倔强的石头的gitee主页             ⏩ 文章专栏:C++经典例题                                   期待您的关注   目录 一、问题描述 二、解题思路 三、代码实现 四、代码逻辑详解 1. 变量初始化 2. 忽略前导空格 3. 处理符号 4. 转换数字 5. 返回结果     一、问题描述 LCR 192. 把字符串转换成整数 (atoi) - 力扣(LeetCode) 在编程中,经常会遇到将字符串转换为整数的需求,就像标准库中的 atoi 函数一样。 本题要求实现一个 myAtoi 函数,将输入的字符串转换为 32 位有符号整数,具体规则如下:   1. 读入字符串并丢弃无用的前导空格。

By Ne0inhk
Effective Modern C++ 条款37:使std::thread在所有路径最后都不可结合

Effective Modern C++ 条款37:使std::thread在所有路径最后都不可结合

Effective Modern C++ 条款37:使std::thread在所有路径最后都不可结合 * 引言:线程生命周期的关键问题 * 线程的两种状态:可结合与不可结合 * 可结合(Joinable)状态的特征 * 不可结合(Unjoinable)状态的四种情况 * 为什么可结合性如此重要? * 两种被拒绝的替代方案 * RAII拯救方案:ThreadRAII类 * ThreadRAII实现详解 * 关键设计决策 * 实际应用案例 * 高级讨论:何时选择join或detach * 性能考量与最佳实践 * 结论:让线程管理无忧 BiliBili上对应的视频为:https://www.bilibili.com/video/BV1iZZgBiE9j 引言:线程生命周期的关键问题 在多线程程序设计中,std::thread的管理是一个看似简单实则暗藏玄机的话题。想象一下,你精心设计的并发程序在大多数情况下运行良好,却在某些边缘情况下突然崩溃——这正是许多开发者在使用原生线程时遇到的噩梦场景。本文将深入探讨std::thread对象

By Ne0inhk
《C++ 动态规划》第001-002题:第N个泰波拉契数,三步问题

《C++ 动态规划》第001-002题:第N个泰波拉契数,三步问题

🔥个人主页:Cx330🌸 ❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》 《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔 《Git深度解析》:版本管理实战全解 🌟心向往之行必能至 🎥Cx330🌸的简介: 目录 前言: 01.第N个泰波拉契数 算法原理(动态规划): 思路: 解法代码(C++): 博主手记(字体还请见谅哈): 02.三步问题 算法原理(动态规划): 思路: 解法代码(C++): 博主手记(字体还请见谅哈): 结尾: 前言: 聚焦算法题实战,系统讲解三大核心板块:“精准定位最优解”——优选算法,“简化逻辑表达,系统性探索与剪枝优化”——递归与回溯,“以局部最优换全局高效”——贪心算法,讲解思路与代码实现,帮助大家快速提升代码能力 01.

By Ne0inhk
【C++】AVL树的底层以及实现

【C++】AVL树的底层以及实现

个人主页 文章目录 * ⭐一、AVL树的概念 * 🎉二、AVL树的性质 * 🏝️三、AVL树的实现 * 1. 树的基本结构 * 2. 树的插入 * 3. 树的旋转 * • 左单旋 * • 右单旋 * • 左右双旋 * • 右左双旋 * 🎡四、AVL树的其它功能 * 1. 树的查找 * 2. 树的遍历 * 3. 树的高度 * 4. 树的大小 * 🚀五、总结 * 1. AVL树的优缺点 * 2. 完整代码 ⭐一、AVL树的概念 AVL树是一种高度平衡的平衡二叉树,相比于搜索二叉树,它的特点在于左右子树都为AVL树且树的高度差的绝对值不超过1。 这里我们会引入一个新的概念叫做平衡因子。平衡因子也就是左右子树的高度差,我们可以通过平衡因子方便我们后续去观察和控制树是否平衡。 🎉二、AVL树的性质 AVL树主要有三大性质: 1.每棵树的左右子树都是AVL树。 2.左子树和右子树的高度之差的绝对值不超过1。 3.

By Ne0inhk