阅读 517

从0开始弄一个面向OC数据库--终结篇

前言

我们从0开始封了一个面向OC模型的数据库就要结束了,开发工作其实在上一篇就做完了,之后做了一些小的优化以及代码的调整来发布这篇文章,先看一下我们最终形态。

  • 直接: 调用sqlite原生API操作数据库,没有中间框架。
  • 易用: 一行代码实现数据库增删查改操作。
  • 多元: 支持所有基本数据类型、集合类型以及自定义模型。
  • 智能: 一行代码,智能实现插入、更新、升级、迁移数据。
  • 强大: 0代码支持数组嵌套模型、嵌套字典,字典嵌套模型等相互嵌套。
  • 灵活: 支持多种查询、删除操作,支持使用sql语句查询、删除。
  • 还原: 存入数据库时为A模型,查询的数据一定还给你A模型。

我们支持的类型有

所有的基本数据类型(int,float),NSNumber,NSArray,NSMutableArray,NSDictionary,NSMutableDictionary,
UIImage,NSURL,UIColor,NSSet,NSRange,NSAttributedString,NSData,自定义模型,以及数组、字典、模型相互嵌套
复制代码

这篇文章,将全方位介绍我们数据库--CWDB以及如何使用!

架构

类名 作用
CWModelProtocol.h 需要遵守以及实现的协议方法(用户关注)
CWSqliteModelTool.h、CWSqliteModelTool.m 本库操作数据库的所有API(用户关注)
CWDatabase.h、CWDatabase.m 直接调用sqlite底层API的类
CWModelTool.h、CWModelTool.m 处理模型的工具类
CWSqliteTableTool.h、CWSqliteTableTool. 处理数据库表的工具类

在使用前,我们先模拟一个对应的使用场景,要解决什么问题,这个场景也是我们demo中的演示代码所展现的(你可以打开demo在Clasess目录下找到它)。

我们希望保存几所学校的资料到数据库。

  • 用一个类来表示学校的资料,资料有学校id,名字,整个学校的所有班级,最好的班级,最优秀的老师的资料。
  • 有对应类来表示班级的资料,班级里面有班级号,名称,班级的所有老师,班级的所有学生,班主任以及班长。
  • 又分别有两个类保存学生和老师的资料

以上的层级关系有模型嵌套数组嵌套模型的场景、模型嵌套模型再嵌套数据再嵌套模型的场景也就模型直接嵌套模型的场景。

然后我们使用工厂方法来快速构造各个模型,用来搭建整个学校

  • 首先,所有的学生都是身高168,年龄20,体重100,性格温和乖巧的女同学
  • 其次,所有的老师都是身高155,年龄28,体重100的御姐老师
  • 再次,我给每个班级添加5名松岛🌟同学,2名林🌟玲老师以及一位苍老师班主任
  • 最后,我任命我为全校唯一男人+校长,我在学校里面开设了2种专业总共4个班级,1个技巧班,3个声音班。保龄球玩得溜的关🌟琳同学获得最优学生,技巧班为最优班级,最好的老师当然是传授身教的苍老师

学校的结构设定好了之后~我们用代码构造一所所这样的梦想学院。。然后我们要使用我们的数据库将学校的资料保存起来。

How to use CWDB?

1、将CWDB拖进你的项目或者使用cocoapods(pod ‘CWDB’, '~> 1.5.0’),给项目添加sqlite3.0.tbd库,需要存储进数据库的模型导入CWModelProtocol.h头文件,然后遵守CWModelProtocol协议,实现下面这个方法返回对应的主键信息:

+ (NSString *)primaryKey {
    // 返回schoolId为主键
    return @"schoolId";
}
复制代码

2、为所欲为之为所欲为操作数据库

  • 插入或者更新数据
// 使用工厂方法创建的shool模型
CWSchool *school = [self cwSchoolWithID:9999 name:@"梦想学院"];
    
// 调用保存或者更新方法,uid为userId,对应数据库的名字,targetId为目标ID,与数据库表名相关,可以传nil。
BOOL result = [CWSqliteModelTool insertOrUpdateModel:school uid:nil targetId:nil];
复制代码

为什么要设置两个多余的参数uid与TargetId?为了迎合下面一些场景,如果你不关注,传nil即可

  • uid:在做IM的时候,一个手机APP可能登陆不同的账号,这个时候,我们希望将不同账号的信息,分为不同的数据库去保存,比如张三登陆了,我们以张三.db 新建一个数据库(之前没有数据库的情况下),李四登陆了,我们再以李四.db 新建一个数据库,他们的信息分别存在自己的数据库里,这样设计有什么好处?我们可以把各个用户的聊天记录以及相关信息分别存储,方便管理以及查询,比如你去银行存钱,你的钱一定是存在你的银行卡里面,而不会是你、我、他的钱都存在同一个银行卡里面,所以我们这里要传uid,用来分辨是哪一个用户的数据库。如果不传,会默认为CWDB公共数据库。
  • targetId:首先我们要说明,我们数据库的表名是以模型类型的名称来命名的。在大部分情况下,仅仅这样是没有问题的,但是,同样是在IM的场景下,比如我和张三聊天,那么我和张三的聊天记录会以Message的模型存在Message的表里面,如果我和李四聊天,那么我和李四的聊天记录也存在Message的表里面吗?这样数据库的数据会非常混乱,因为你可能会和更多的人聊天,那Message这个表会非常臃肿,所以我们引入targetId目标ID这一参数,我们的表名就会是《模型名称+targetId》,以张三为例,他的聊天记录表就是Message张三,李四就是Message李四。如果你的数据库里面一个模型只需要统一管理一个表,那么你传nil即可。

关于数据库升级以及数据库迁移,假设我存在数据库的数据为聊天记录Message,里面有10个成员变量,有一天,业务的提升,我要在Message里面多加一个成员变量,如新增一个成员变量用来标记是否是撤回的消息,这个时候由于数据库的表结构固定死了没这个字段,插入数据肯定是失败的,为了解决这个问题,我们将要进行数据库升级,并且要将之前的数据都保留下来,这个要怎么做呢?这里压根不需要你思考这个问题,我作为一个负责任的男人,我很负责任的告诉你,假如你的Message模型新增加了1个2个10个成员变量,你只管加,加了之后只管调用上面的方法存,数据的升级以及迁移我们默认会帮你完成!!!

  • 查询所有数据
// result内的元素为CWSchool的模型
NSArray *result = [CWSqliteModelTool queryAllModels:[CWSchool class] uid:nil targetId:nil];
复制代码

查询方法返回值是一个数组,这个数组里面的数据全部为CWSchool的模型,因为我们承诺过,保存的时候是模型A,查询出来的一定也是模型A。

  • 删除数据
CWSchool *school = [self cwSchoolWithID:9999 name:@"梦想学院"];
// 删除school数据
BOOL result = [CWSqliteModelTool deleteModel:school uid:nil targetId:nil];
复制代码

这个方法,会根据传进来的模型的主键值去找到数据表里面的数据删除。

我们的功能基本都在这了,除了还有一些条件查询与删除没写在这里。有人会问,数据库不是增删查改4个吗?怎么你只有3个,我们也实现了改的操作,只是我们把这个操作和增合并成了一个方法,也就是这里的第一个方法,我们会根据主键来判断数据库内是否存在对应的数据,如果存在,我们则进行更新操作,不存在则插入数据。

我们的演示代码对数据库操作提供了一个可视化的操作界面,大概是这样子

当然插入的数据与查询的数据是否对应从界面上是无法看出来的,你可以像我这样打一个断点查看

关于CWDB的更多用法,请转到github:CWDB查看或者看代码提供的API。。如果有不能满足你需求的场景或者有任何疑问,欢迎留言或者提issue,最终看在全校女同学的面子上 give me one star !!

如果你也想封装一个有很多女同学的数据库,但是没有资料学的话,可以前往我之前文章的传送门,每个功能的实现,我们都提供了非常详细的讲解:

从0开始弄一个面向OC数据库(一)

从0开始弄一个面向OC数据库(二)

从0开始弄一个面向OC数据库(三)

从0开始弄一个面向OC数据库(四)

从0开始弄一个面向OC数据库(五)