9. 数据库设计与使用
大家好,我是程序喵。
上一节介绍了上报协议的设计,这一节我会主要介绍埋点项目中,数据库的结构设计与使用。
提到数据库,大家肯定会想到MySQL、Postgresql,它们确实好用,不过对于客户端来说有些重。
一般客户端都是使用sqlite或者leveldb这种轻量数据库,sqlite会更常用一些。我们这里也会使用sqlite数据库。
正常在C++中使用sqlite数据库,比如增删改查,都需要写对应的数据库sql语句,相当麻烦。有其他编程经验的朋友可能会想到,有没有ORM相关的封装呢?类似于Rust中的Diesel。
C++中ORM相关的sqlite三方库很少,我找了好久,只找到了一个相对好用而且star也比较多的三方库 sqlite_orm( https://github.com/fnc12/sqlite_orm )
sqlite_orm的使用非常简单,它只有一个头文件,我们只需include一个头文件就可以:https://github.com/fnc12/sqlite_orm/blob/master/include/sqlite_orm/sqlite_orm.h
写代码之前,我们需要先设计数据库的结构。
数据库中需要存储哪些内容?
其实只需要四个字段:
- ID:每条数据都有个对应的ID,作为数据的唯一标识,这个应该不用多说
- priority:表示每条数据的优先级,从数据库中取数据的时候,会按优先级从高到低读取
- timestamp:表示每条数据上报的时间戳
- content:表示实际要上传的文本内容,不过我们这里数据库中存储的数据肯定是加密过的数据,取出来需要解密后再上传到后端。否则数据都被用户看见了,不安全。
我们数据库相关的需求应该很明确,就是增删查,那相关的类如何设计呢?
对应的类叫BuriedDb,里面有几个成员函数,可以直观的看到,功能就是增删查。
类中有一个Data的struct定义,就是我们上面介绍的数据库结构需要的字段,其中有一点不太一样,正常来说content一般是string类型,而这里我们使用的是vector<char>类型,因为sqlite_orm这个库中文本类型需要是vector<char>,否则会导致数据库功能异常。
注意我们这里只使用了一个成员变量,即 std::unique_ptr<BuriedDbImpl> impl,而且这个BuriedDbImpl只有前置声明,没有具体的定义,这里使用的是C++中比较常见的pimpl模式,即头文件中不做具体定义和实现,只保留一个指针,具体的定义和实现放在源文件中。关于pimpl,我写过一篇文章,可以看看:https://t.zsxq.com/14r7qn8UQ
如果不用这种方式,我们就需要在头文件database.h中引入sqlite_orm.h,如果其他好多头文件都引入database.h,会导致重复引入sqlite_orm.h,可能会导致代码段体积过大。
再看源文件中BuriedDb成员函数的实现:
BuriedDb的所有成员函数的实现,都是调用的impl的对应方法,实际的定义都在BuriedDbImpl中。
这里的增删查操作都是使用的storage相关方法。
比如插入方法的三个步骤:
先开启事务,再插入数据,再提交事务,整个插入流程非常简单而且方便,这也正是ORM数据库的优势。
代码中使用了transaction_guard开启事务,又创建了一个guard对象,并在最后使用guard.commit提交事务,这里使用了RAII概念,假如中间有哪个步骤失败,return后,会在guard的析构函数中触发回滚事务的操作。
删除查询操作,也是类似,这里就不多介绍,大家直接看代码应该就可以明白。
这里重点在介绍下storage成员变量的类型,相关代码是:
首先,在InitStorage函数中,使用make_storage函数创建了一个数据库存储对象。在这个函数中,使用make_table函数定义了一个名为"buried_data"的表,其中包含了"ID"、"priority"、"timestamp"和"content"四个字段。每个字段都使用了make_column函数进行定义,指定了字段的名称以及对应的数据类型。其中,"id"字段被定义为主键并自增。
接着,使用using关键字定义了一个名为DBStorage的类型,它是通过调用InitStorage函数的返回类型推断得到的。
如果不建表的话,我们还不知道storage的类型,代码中使用InitStorage函数建表,返回值就是对象类型,我们也不知道具体类型是什么,如果自己推断,那是相当麻烦。所以这里使用了inline auto + decltype关键字来推断出整个storage的类型。
最后,使用std::unique_ptr定义了一个名为storage_的成员变量,它是DBStorage类型的指针,用于操作数据库存储对象。
到这里,数据库相关的使用已经介绍完毕,下一章节我会介绍如何发起http请求,下节见。
本节相关代码详见:https://github.com/chengxumiaodaren/BuriedPoint/blob/main/src/database/database.cc
