odb使用

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、创建学生类和班级类
    • 1.学生类
    • 2.班级类
    • 3.生成数据库支持代码
  • 二、创建数据库对象,对数据库进行操作
    • 1.构建连接池工厂配置对象
    • 2.构造数据库操作对象
    • 3.数据库操作
      • 1.新增数据
    • 2.查询数据
    • 3.更新数据
    • 4.删除操作
    • 5.多表查询
    • 5.部分字段查询


前言

odb可以简化数据库的操作,通过将c++对象直接映射到数据库表,通过对 对象的操作,完成数据库表的操作。我们这次简单使用下odb来实现一个学生表和班级表的增删改查操作。


一、创建学生类和班级类

由于odb是通过是通过对象映射到表的,所以创建表,我们就需要先创建两个类,一个学生类,一个班级类。

1.学生类

首先学生表中需要id(编号),sn(学号),name(姓名),age(年龄),classid(班级编号)这几个字段,于是我们的类中也要有这几个编号。
另外,我们需要提供默认构造和有参构造,并且提供get和set方法,来进行成员变量的设置和获取。这些都是和我们定义普通类是一样的。
可以看到代码中有很多#pragma db指令。
#pragma db object就代表我们需要将这个类映射成数据库表,这也是最重要的。
#pragma db id 代表该成员变量需要作为表的主键,同时后面跟上了一个auto 代表需要设置自增约束。
#pragma db unique 代表设置字段唯一性约束。
#pragma index代表设置普通索引。
其中所有的字段默认都会添加not null约束的,如果需要指定该字段可以为空,就需要使用odb::nullable类型来定义变量,这样映射到数据库表中的变量就是null约束了。
但是有一点需要注意,就是在获取该字段时,由于该字段可能为空,所以需要对该字段进行判断。怎么获取我们后面介绍。
在类中我们声明了一个友元类odb::access,因为我们的成员变量都是私有,后续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){}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:friend class odb::access;#pragma db id autounsigned long _id;#pragma db uniqueunsigned long _sn;std::string _name;odb::nullable<unsigned short> _age;#pragma db indexunsigned long _classes_id;
};

2.班级类

班级类和学生类都是一样的操作。
我们总结以下odb需要在普通的类中新增哪些操作:

  • #pragma db object
  • 定义默认构造和有参构造,和get set方法
  • 申明友元类odb::access;
  • 对成员变量设置主键和约束等。
#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 autounsigned long _id;std::string _name;
};

3.生成数据库支持代码

上面的两份代码写完后,我们需要基于上面的代码来生成mysql数据库交互所需要的代码。
这里和protubuf的.proto文件生成有点类似。

  • odb:这是 ODB 工具的命令行可执行文件,负责代码生成和数据库模式创建。
  • -d mysql:指定目标数据库为 MySQL。这意味着生成的代码和模式将针对 MySQL 数据库进行优化和配置。
  • -std c++11:指定使用 C++11 标准进行代码生成。这通常是为了支持 C++11 的特性(如智能指针、范围for循环等)。
  • -profile boost/date-time:在数据库表中有一些时间字段datatime等,我们c++类中是使用的boost中的类来定义的,因此加上这个选项。
  • 最后的student.hxx就是我们上面编写的学生类的源文件。
odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time student.hxx

上面的指令执行完毕后,我们就会得到五个源文件,分别是
student-odb.cxx ------student-odb.hxx-----------studnet-odb.ixx----------------studnet.sql--------student.hxx
其中-odb.cxx是我们需要进行编译的,-odb.hxx是我们需要进行include包含的,ixx是一个中间文件我们不需要关心。.sql就是odb帮我们生成的sql语句用于创建数据库表.
我们需要手动到导入.sql文件创建数据库表。

mysql -uroot -p < student.sql

二、创建数据库对象,对数据库进行操作

1.构建连接池工厂配置对象

odb::mysql::connection_pool_factory数据库连接池工厂对象,这里需要指定两个参数一个是最大连接数量和最小连接数量.FLAGS_max_pool和0.

std::unique_ptr<odb::mysql::connection_pool_factory> cpf(new odb::mysql::connection_pool_factory(FLAGS_max_pool, 0));

2.构造数据库操作对象

构造数据库操作对象,后续我们进行的增删改查操作都是通过这个对象的函数来完成。这里需要指定几个参数,分别是用户名,密码,数据库名称,主机地址,端口号,socket,字符集,client_falgs,和连接池工程对象。这里的连接池工厂对象需要使用unique_ptr创建,且必须传入右值,不然会报错。
在这里插入图片描述

odb::mysql::database db(FLAGS_user, FLAGS_pswd, FLAGS_db,FLAGS_host, FLAGS_port, "", FLAGS_cset,0, std::move(cpf));

3.数据库操作

接下来就可以通过数据库对象来进行数据库操作了。我们封装一个插入学生数据的函数,形参传入我们的数据库对象。
需要注意的是,我们在进行数据库操作的时候需要开启事务,在操作完毕之后选哟手动提交事务。然后就是进行数据库操作可能会失败,odb会抛出异常,因此为了防止程序崩溃,我们需要捕获异常。

1.新增数据

接下来的操作都是通过数据库对象提供的函数实现的。
通过begin()来获取一个事务对象,
然后定义几个student对象,这里的student就是我们前面.hxx定义的。这里的一个student对象就代表数据库表中的一行记录。
接着使用persist进行插入操作。
最后手动提交事务。

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

对班级表新增数据,都是一样的操作。

void insert_classes(odb::mysql::database &db) 
{try {//获取事务对象开启事务odb::transaction trans(db.begin());Classes c1("一年级一班");Classes c2("一年级二班");db.persist(c1);db.persist(c2);//5. 提交事务trans.commit();}catch (std::exception &e) {std::cout << "插入数据出错:" << e.what() << std::endl;}
}

2.查询数据

查询数据就一定会返回一个结果并且查询数据一定需要指定where条件。
在odb中通过 odb::result< Student >来获取结果,这个< student >需要我们显示指定,你要查询那一张表,就填入你要查询表的映射的类。
where条件是通过odb::query< student >来指定的。

需要使用数据库对象提供的query函数来进行查询操作,这里的query不是我们上面的odb::query。这俩个不是一回事,数据库对象的query是提供查询操作,而odb::query是指定where条件的。
db.query需要传入一个参数,这个参数就是where条件,query::name == “张三”,这里的query::name就是我们上面定义的odb::query< student >,这里我们显示指定的< student >,代表我们的where条件就是student中的字段,
这里的db.query< student >也指定了一个studnet,这里指定的studnet代表我们进行查询的表是studnet表。
这里可能有点绕,odb的只支持全量查询,默认显示表中所有字段,如果想只显示莫几个字段,则需要根据视图,来指定一个新的类,这个类中只有你需要显示的字段,进而对这个类的操作,显示出你要查询的字段。

		typedef odb::query<Student> query;typedef odb::result<Student> result;result r(db.query<Student>(query::name == "张三"));

查询操作的全部代码,可以result可以当作一个vector,里面存放这你查询到的所有记录,每个记录都是一个student,它是支持迭代器的,r.begin()这既是获取迭代器,然后对他解引用获取第一个studnet,由于我们直插入了一个张三的学生,所以这里只有一个值。

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

3.更新数据

更新数据这块有些不同,你要更新数据,你要先查询到你要更新的记录,然后将该记录也就是一个Student对象更改后,通过update写入数据库中。可以看到,这里的形参就是查询到的student对象,我们已经对他进行了更改,将age改为了20,接着通过update传入student进行数据库更新操作。

void update_student(odb::mysql::database &db, Student &stu)
{try {//获取事务对象开启事务odb::transaction trans(db.begin());db.update(stu);//5. 提交事务trans.commit();}catch (std::exception &e) {std::cout << "更新学生数据出错:" << e.what() << std::endl;}
}

4.删除操作

删除操作,和更新不同,可以通过erase_query来进行删除数据。需要提供where条件,那么就需要创建odb::query对象。同样的erase_query< student >这里显示指定的student代表你要操作的表,而这里odb::query< syudent >这里指定的studnet是你where条件需要用到的表的映射类。

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);//5. 提交事务trans.commit();}catch (std::exception &e) {std::cout << "更新学生数据出错:" << e.what() << std::endl;}
}

5.多表查询

我们需要通过视图,创建一个视图,这个视图中包含了我们需要查询的多张表中的字段。例如这里我们需要所有学生的信息,包括学生的班级,那么我们就需要创建一个视图,把学生表的所有字段和班级表的班级姓名字段都包含进来。这里我们创建一个新的类classes_student代表我们多表查询的一个临时表。后续我们操作这个类对象就可以完成多表查询的结果了。

通过#pragma db view创建视图,需要指定你创建的视图是由哪几个表组成的,所以使用object(),我们这里使用学生表和班级表,Classes后面有一个等号,代表取别名, 后面接着一个冒号,由于两个表联立会产生笛卡尔积,所以我们需要指定where条件。
这里的成员变量我们需要使用#pragma db column来指定这个变量是属于哪一个类的。

//查询所有的学生信息,并显示班级名称
#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;
};

以下是多表查询的全部代码。我们通过视图的方式创建了一个classes_student类,通过操作这个类就可以,对这个视图(临时表)进行操作,这样就把多表查询简化成了普通的查询。

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::endl;std::cout << it->sn << std::endl;std::cout << it->name << std::endl;std::cout << *it->age << std::endl;std::cout << it->classes_name << std::endl;}//5. 提交事务trans.commit();}catch (std::exception &e) {std::cout << "更新学生数据出错:" << e.what() << std::endl;}
}

5.部分字段查询

odb默认只支持全字段查询,所以我们如果只想查询某个表的某些字段,就需要通过视图,创建一个临时表,然后对这个临时表操作,完成部分字段查询的操作。
这里我们只想查询学生表中的学生姓名字段,于是我们的视图中只有一个成员变量。在
#pragma db query

// 只查询学生姓名  ,   (?)  外部调用时传入的过滤条件
#pragma db view query("select name from Student")
struct all_name {std::string name;
};

以下是所有的部分字段查询的全部代码。可以看到这里的odb::query< student >这个是指定where条件的,我们要查询id等于1的学生的姓名。
db.query< struct all_name >是要针对all_name这个表进行操作,这里要填all_name而不是studnet.

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;}//5. 提交事务trans.commit();}catch (std::exception &e) {std::cout << "查询所有学生姓名数据出错:" << e.what() << std::endl;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1537395.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

概率分布深度解析:PMF、PDF和CDF的技术指南

本文将深入探讨概率分布&#xff0c;详细阐述概率质量函数&#xff08;PMF&#xff09;、概率密度函数&#xff08;PDF&#xff09;和累积分布函数&#xff08;CDF&#xff09;这些核心概念&#xff0c;并通过实际示例进行说明。 在深入探讨PMF、PDF和CDF之前&#xff0c;有必…

JavaSE - 面向对象编程03

01 多态 01_01 认识多态 01_02 多态的好处和缺点 【1】好处&#xff1a;① 可以解耦合&#xff0c;扩展性更强&#xff0c;父类引用指向的子类对象可以随时切换&#xff0c;而后面的逻辑 代码并不需要更改。 ② 使用父类引用可以作为方法的形参或返…

java138-异常处理_java 138错误

//异常 public class test79 { //定义方法声明定义异常&#xff0c;在满足条件时抛出异常对象&#xff0c;程序转向异常处理 public double count(double n,double m)throws Exception { if (m 0) {//如果除数等于0.则抛出异常实例 throw new Ex…

C/C++:优选算法(持续更新~~)

一、双指针 1.1移动零 链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操…

近期值得关注的扩散模型Diffusion与时间序列结合的文章

扩散模型 扩散模型&#xff08;diffusion model&#xff09;是一类生成模型&#xff0c;运用了物理热力学扩散思想&#xff0c;主要用于对复杂数据分布进行建模和采样。以图片生成举例简要介绍下扩散模型运作方法。给定目标分布q(x)中的一些观测数据x&#xff0c;生成模型的目…

常见算法——自相关的含义及Python、C实现

常见算法——自相关的含义及C实现 一、概念1. 自相关概念2. 滞后期示例说明&#xff1a; 二、自相关的计算步骤&#xff1a;1. 确定滞后期 (Lag)&#xff1a;2. 计算平均值&#xff1a;3. 计算自相关&#xff1a; 三、示例 Python自相关计算1. 代码2. 运行结果 四、C语言实现自…

剖解杨辉三角

杨辉三角 思路&#xff1a; 我们将上述转换为一个二维数组&#xff0c;即可实现效果 另外在实现代码之前我们要了解Java中是如何实现二维数组的&#xff1a; 实现代码如下&#xff1a; public List<List<Integer>> generate(int numRows){List<List<Integ…

【linux-Day3】linux的基本指令<中>

【linux-Day3】linux的基本指令<中> linux下的基本指令&#x1f4e2;man&#xff1a;访问linux手册页&#x1f4e2;echo&#xff1a;把字符串写入指定文件中&#x1f4e2;cat&#xff1a;查看目标文件的内容&#x1f4e2;cp&#xff1a;复制文件或目录&#x1f4e2;mv&am…

【Go】Go语言中延迟函数、函数数据的类型、匿名函数、闭包等高阶函数用法与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

通信工程学习:什么是EPON以太网无源光网络

EPON&#xff1a;以太网无源光网络 EPON&#xff08;Ethernet Passive Optical Network&#xff0c;以太网无源光网络&#xff09;是一种结合了以太网技术和无源光网络&#xff08;PON&#xff09;优势的高速、大容量宽带接入技术。以下是关于EPON的详细解释&#xff1a; 一、…

springboot Controller层返回的结果,日志添加traceId ,方便对日志的追踪查询

要解决的问题&#xff1f; 接口报错&#xff0c;如何快速定位问题&#xff1f;这个需要日志的辅助&#xff0c;一般错误日志中有详细的堆栈信息&#xff0c;具体是哪行代码报错&#xff0c;都可以看到。 要想快速定位问题&#xff0c;前提是要能够快速定位日志。 海量日志&am…

GPU加速生物信息分析的尝试

GPU工具分类 实话实说&#xff0c;暂时只有英伟达的GPU才能实现比较方便的基因组分析集成化解决方案&#xff0c;其他卡还需要努力呀&#xff0c;或者需要商业公司或学术团体的努力开发呀&#xff01;FPGA等这种专用卡的解决方案也是有的&#xff0c;比如某测序仪厂家&#xf…

Leetcode—815. 公交路线【困难】(unordered_map+queue)

2024每日刷题&#xff08;163&#xff09; Leetcode—815. 公交路线 bfs实现代码 class Solution { public:int numBusesToDestination(vector<vector<int>>& routes, int source, int target) {if(source target) {return 0;}unordered_map<int, vector…

ROS组合导航笔记1:融合传感器数据

使用机器人定位包&#xff08;robot_localization package&#xff09;来合并来自不同传感器的数据&#xff0c;以改进机器人定位时的姿态估计。 基本概念 在现实生活中操作机器人时&#xff0c;有时我们需要处理不够准确的传感器数据。如果我们想要实现机器人的高精度定位&am…

Jemter项目实战(黑马程序员)

视频网址&#xff1a;02测试数据准备_哔哩哔哩_bilibili 自动化脚本架构搭建 新增和修改 新增 删除和查询 弱压力、高并发、高频率 弱压力测试 高并发 高频率 生成图形化报告

记忆化搜索算法专题——算法简介力扣实战应用

目录 1、记忆化搜索算法简介 1.1 什么是记忆化搜索 1.2 如何实现记忆化搜索 1.3 记忆化搜索与动态规划的区别 2、算法应用【leetcode】 2.1 题一&#xff1a;斐波那契数 2.1.1 递归暴搜解法代码 2.1.2 记忆化搜索解法代码 2.1.3 动态规划解法代码 2.2 题二&#xff1…

JavaScript高级——闭包的作用

1、使用函数内部的变量在函数执行完后&#xff0c;仍然存活在内存中&#xff08;延长了局部变量的生命周期&#xff09; 2、让函数外部可以操作&#xff08;读写&#xff09;到函数内部的数据&#xff08;变量/函数&#xff09; 3、函数执行完后&#xff0c;函数内部声明的局…

【1.使用Index和Match函数自动补全内容】

目录 前言如何利用函数自动填充内容效果学会使用的方法(文字图片版本)只管使用&#xff0c;不看原理原理解读MATCH函数INDEX函数组合 学会使用的方法(视频版本) 后言最后想说的话 前言 如何利用函数自动填充内容 先说结论&#xff0c;本文的目的是通过使用Excel的函数&#xf…

31.递归、搜索、回溯之综合练习

1.找出所有子集的异或总和再求和&#xff08;easy&#xff09; . - 力扣&#xff08;LeetCode&#xff09; 题目解析 算法原理 代码 class Solution {int path;int sum;public int subsetXORSum(int[] nums) {dfs(nums, 0);return sum;}public void dfs(int[] nums, int pos…

Vue(12)——路由的基本使用

VueRouter 作用&#xff1a;修改地址栏路径时&#xff0c;切换显示匹配的组件 基本步骤&#xff08;固定&#xff09; 下载&#xff1a;下载VueRouter模块到当前工程引入安装注册创建路由对象注入&#xff0c;将路由对象注入到new Vue 实例中&#xff0c;建立关联 发现了#/表…