[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

Flutter 组件 slug 的适配 鸿蒙Harmony 深度进阶 - 驾驭中英混合语义转码、实现鸿蒙端“拼音+Slug”组合路径与超大文件库冲突自愈方案

Flutter 组件 slug 的适配 鸿蒙Harmony 深度进阶 - 驾驭中英混合语义转码、实现鸿蒙端“拼音+Slug”组合路径与超大文件库冲突自愈方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 slug 的适配 鸿蒙Harmony 深度进阶 - 驾驭中英混合语义转码、实现鸿蒙端“拼音+Slug”组合路径与超大文件库冲突自愈方案 前言 在前文中,我们利用 slug 实现了基础的文本规范化(如将“Hello World”转为“hello-world”)。但在真正的“国产化办公软件”、“包含上千万条中文动态的社区平台”或“分布式海量文件索引”场景中。简单的拉丁化转换完全无法应对中文(CJK)环境。面对标题为 鸿蒙 0307 批次:跨平台实战! 的内容。如果不加干预,slugify 的结果可能是一串意义不明的字符或者是空字符串。 如果我们直接使用百分比编码,长路径可能会超出文件系统的 255 字节限制。 本文将作为

By Ne0inhk
Flutter for OpenHarmony:mqtt_client 连接 MQTT 代理,实现物联网(IoT)设备实时状态监控(轻量级发布订阅协议) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:mqtt_client 连接 MQTT 代理,实现物联网(IoT)设备实时状态监控(轻量级发布订阅协议) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 MQTT (Message Queuing Telemetry Transport) 是一种极轻量级的发布/订阅消息传输协议,广泛应用于物联网(IoT)、移动应用和车载设备。在智能家居控制、设备状态上报等场景中,APP 往往需要实时接收设备发来的消息。 mqtt_client 是 Dart 生态中最流行的 MQTT 客户端库,支持 MQTT 3.1 和 3.1.1 协议。它能够在 OpenHarmony 应用中稳定运行,帮助开发者轻松构建物联网控制端。 一、概念介绍/原理解析 1.1 基础概念 * Broker (代理): 消息的转发服务器(如

By Ne0inhk
Docker 安装部署全流程使用指南(Linux 通用版)

Docker 安装部署全流程使用指南(Linux 通用版)

整合 Docker 安装、配置、核心使用(含日志管理)、路径修改、Dockerfile 构建镜像等全维度内容,适配 Debian/Ubuntu(apt)、CentOS/RHEL(yum/dnf)等主流 Linux 发行版,无特定系统适配内容。 一、Linux 通用版 Docker 安装 1. 前置准备:卸载旧版本 # 通用卸载命令(适配apt/yum/dnf) sudo apt remove -y docker docker-engine docker.io containerd runc # Debian/Ubuntu # 或 sudo yum remove -y

By Ne0inhk
Flutter for OpenHarmony:password_strength 快速评估用户密码强度,拒绝弱口令(安全增强库) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:password_strength 快速评估用户密码强度,拒绝弱口令(安全增强库) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在注册或修改密码时,我们经常需要提示用户“您的密码太弱了”。虽然简单的正则表达式(如 .{8,})能限制长度,但很难识别出 123456, password, qwerty 这种高频弱口令。 password_strength 是一个基于熵(Entropy)计算和常见字典匹配的密码强度估算库。它能给出 0.0 到 1.0 的分数,帮助开发者构建更安全的认证系统。 一、概念介绍/原理解析 1.1 基础概念 * Entropy (熵): 信息论中的概念,密码的随机性越大,熵值越高,破解难度越大。 * Dictionary Attack (字典攻击): 黑客利用常用密码表尝试登录。 * Score: 综合长度、字符种类(

By Ne0inhk