WCDB 背景
自己初次见到 WCDB 是微信开发团队公众号在今年五月份推送的一篇文章中(开发者团队的微信号上面图片中有,值得大家关注一下),那时候就说在筹备着 WCDB 的开源,觉得很是新奇,在两个多月前 WCDB 开源了!自己是最近才有时间看的 WCDB,总结一下自己的理解和学习的东西,WCDB 是微信团队开源的支持 Android, 也支持 iOS, 那当然也是会支持 macOS 的一个移动端数据库框架,FMDB 估计做 iOS 的 99.99% 的都知道,就像 Android 开发中使用 LitePal 一样,都是在 SQLite 的基础上封装的移动数据库框架,WCDB 是微信团队提供一个高效、易用、完整的移动端存储方案。 它包含三个模块:
1、WCDB-iOS/Mac
2、WCDB-Android
3、数据库损坏修复工具 WCDBRepair
iOS 数据库框架对比分析
一:关系型数据库,代表有 CoreData、FMDB 等
CoreData:微信团队在公众号的文章中对它的总结是这样:它是苹果内建框架,和 Xcode 深度结合,可以很方便进行 ORM;但其上手学习成本较高,不容易掌握。稳定性也堪忧,很容易 crash;多线程的支持也比较鸡肋。
FMDB:它基于 SQLite 封装,对于有 SQLite 和 ObjC 基础的开发者来说,简单易懂,可以直接上手;而缺点也正是在此,FMDB 只是将 SQLite 的 C 接口封装成了 ObjC 接口,没有做太多别的优化,即所谓的胶水代码 (Glue Code)。使用过程需要用大量的代码拼接 SQL、拼装 Object,并不方便
二:key-value 数据库,代表有 Realm、LevelDB、RocksDB 等
微信团队对上面的总结是这样:因其在各平台封装、优化的优势,比较受移动开发者的欢迎。对于 iOS 开发者,key-value 的实现直接易懂,可以像使用 NSDictionary 一样使用 Realm。并且 ORM 彻底,省去了拼装 Object 的过程。但其对代码侵入性很强,Realm 要求类继承 RLMObject 的基类。这对于单继承的 ObjC,意味着不能再继承其他自定义的子类。同时,key-value 数据库对较为复杂的查询场景也比较无力。
说说自己的理解:上面的像 Realm、LevelDB、RocksDB 等 key - value 类型的这几个框架我都没有使用过,没有什么话语权,说说自己用过的,上面的 CoreData 和 FMDB,我记得我去年有写过一篇博客,就这两者之间的区别等等的做过总结,有兴趣的可以去翻翻以前的,我也记得唐巧哥以前在他的公众号文章中也说过这事,就这两者之间还是支持 FMDB,当然我相信 CoreData 苹果说不定哪天就让它变得受人们青睐,但当前可能还是做得不够吧,所以你这样看可能也就不难理解,一起为什么那么多人用 FMDB,但确实也是有些场景中 CoreData 能做起来容易点的的不一定 FMDB 也容易,比如在两张表之间建立联系的时候,CoreData 就会相对容易一点,所以,就像微信团队最后总结那那句一样:各个方案都有其独特的优势及劣势,没有最好的,只有最适合的。
你期盼的数据库框架是什么样子的?
下面这一段内容我不知道有多少伙伴在微信开发团队的公众号当中看到过,我自己看完下面这段话的时候,觉得总结的每一句话都是开发者的心声,也许看完这段话你也会和我一样,更加期盼的想去看看 WCDB:
初试 WCDB- 理解 ORM
下面的内容就从最基本的开始,从表的创建,到后面的 CRUD 的操作,以及再到后面一些高级的用法全都过一遍,在这当中涉及到的问题,有些可能会给连接大家可以自己去学习理解,有些我会说书我自己的理解,WCDB 我们上路......
安装 WCDB,在 Wiki 里面说的也比较完整,这里我们就不在多说,直接使用 CocoaPods 直接安装即可。
想理解 WCDB 需要先理解最基本的这个概念 ORM ,大家可以点进去看看微信给的使用说明,我们接着说:
在我们的 Demo 中,我们创建一个 Message 类,然后在这个类中声明我们需要的一些属性:
上面文件大家看到了这个 Message+WCTTableCoding.h ,看着很像是我们常用的类别,其实就是,下面会说它的创建和作用,我们在我们的 Message 类中声明我们的属性,然后至于为什么要把. m 后缀改成. mm , 下面也会说,慢慢来。 下面就是我们为 Message 类建立 ORM 类字段绑定的过程:
1、定义该类遵守 WCTTableCoding 协议,可以在类声明上定义,也可以通过文件模版在 category 内定义 (下面具体说)。这里推荐大家使用第二种,通过文件模板在 category 内定义,为什么要这样做,就是为了隔离 Objective-C++ 代码, WCDB 基于 WINQ,引入了 Objective-C++ 代码,所以对于引入了 WCDB 的源文件,都需要把后缀. m 改为. mm,(这就是我们上面改后缀的原因) 为减少影响范围,可以通过 Objective-C 的 category 特性将其隔离,达到只在 model 层使用 Objective-C++ 编译,而不影响 Controller 和 View。这一点在 Wiki 中是有提到的,
这样做的好处是不知道大家都有没有理解,这么说,要是你通过第一种方法,不通过 category 定义,而是选择了在类声明中写,这样的话 Message.h 中就需要有宏 WCDB_PROPERTY,这样你就在 Message.h 使用了 WCDB 的代码,当你把 Message.h 在其他 Controller/View 中引用的时候,那相应的 Controller/View 的. m 就需要修改成. mm 。造成不必要的工作,但你用第二种方法写的时候,你就发现在 Message.h 中是没有任何的关于 WCDB 的代码的,后面你引用也不需要再去修改!希望大家理解这里。
在你项目中你集成了 WCDB 之后,你编译一下你的项目,你就可以看到上面我们说的模板文件,如下所示:
2、使用 WCDB_PROPERTY 宏在头文件声明需要绑定到数据库表的字段(也就是把你的表里面需要的字段在这里用这宏声明一次)
3、使用 WCDB_IMPLEMENTATIO 宏在类文件定义绑定到数据库表的类(把这个类绑定到数据库的表,你会在下面创建数据库的时候创建相应的表,表会和类绑定)
4、使用 WCDB_SYNTHESIZE 宏在类文件定义绑定到数据库表的类(第二步声明了表需要的字段,第三步绑定了表中的类,第四步就等于把表和字段绑定)
根据上面的步骤,就简单的完成了 ORM 的基本操作,想要了解更过的关于 ORM 宏的用法以及定义,还是查看 Wiki 文档:ORM 使用教程
WCDB 初试 - CRUD
上面说完了 ORM 的操作,下面说说基本的数据库的创建以及 CRUD 的操作,在说下面之前,我们扯一点其他的,不知道会不会有人不知道该怎样去查看你建立的数据库内容,这里说推荐一个我自己一直在用的挺好用的工具 -- Navicat Premium ,你可以点击它去下载,提取密码是 e73y 。当然这不是我提供的,是简书同行提供的,谢谢无私奉献!
下载下来之后你点击安装的时候可能还需要密码:xclient.info
完了你打开,要是出现出损坏无法使用,打不开的情况,你需要设置:
注意: 要是你系统没有这个任何来源,终端命令: sudo spctl --master-disable
通过上面的操作,相信应该没什么问题了,接着简单一步带过怎样查看已有的数据库的:
左上角 Connection 选择 SQLite 如下:
下面是正题,数据库的创建:
- - (BOOL) creatDataBaseWithName: (NSString * ) tableName {
- //获取沙盒根目录
- NSString * documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
- // 文件路径
- NSString * filePath = [documentsPath stringByAppendingPathComponent: @"model.sqlite"];
- NSLog(@"path = %@", filePath);
- database = [[WCTDatabase alloc] initWithPath: filePath];
- // 数据库加密
- //NSData *password = [@"MyPassword" dataUsingEncoding:NSASCIIStringEncoding];
- //[database setCipherKey:password];
- //测试数据库是否能够打开
- if ([database canOpen]) {
- // WCDB大量使用延迟初始化(Lazy initialization)的方式管理对象,因此SQLite连接会在第一次被访问时被打开。开发者不需要手动打开数据库。
- // 先判断表是不是已经存在
- if ([database isOpened]) {
- if ([database isTableExists: tableName]) {
- NSLog(@"表已经存在");
- return NO;
- } else
- // 创建方法
- return [database createTableAndIndexesOfName: tableName withClass: Message.class];
- }
- }
- return NO;
- }
下面是基本的增删查改的操作,这里写的一些都是最基本最基本的,下面再说两个基本的事务处理方法,然后再把 CRUD 操作的代码放出来,我们说的也知识基本的,要是想灵活应用还是得慢慢学,掌握它。
1、 block 处理事务, 顾名思义就是把我们的事务处理放在 block 当中,如下我们举的这个例子:
- // 另一种事务处理方法Block
- - (BOOL) insertMessageWithBlock {
- BOOL commit = [database runTransaction: ^BOOL {
- BOOL ret = [self insertMessage];
- if (ret) {
- return YES;
- } else
- return NO;
- }
- event: ^(WCTTransactionEvent event) {
- NSLog(@"Event %d", event);
- }];
- return commit;
- }
2、利用 WCTTransaction 来处理事务:
- // WCTDatabase 事务操作,利用WCTTransaction
- - (BOOL) insertMessageWithTransaction {
- BOOL ret = [database beginTransaction];
- ret = [self insertMessage];
- if (ret) {
- [database commitTransaction];
- } else
- [database rollbackTransaction];
- return ret;
- }
下面是我们写的简单的一个 CRUD 的操作的代码:
- - (BOOL) insertMessage {
- //插入
- Message * message = [[Message alloc] init];
- message.localID = 1;
- message.content = @"Hello, WCDB!";
- message.createTime = [NSDate date];
- message.modifiedTime = [NSDate date];
- /*
- INSERT INTO message(localID, content, createTime, modifiedTime)
- VALUES(1, "Hello, WCDB!", 1496396165, 1496396165);
- */
- return [database insertObject: message into: @"message"];
- }
- - (BOOL) deleteMessage {
- //删除
- //DELETE FROM message WHERE localID>0;
- return [database deleteObjectsFromTable: @"message"where: Message.localID > 0];
- }
- - (BOOL) updataMessage {
- //修改
- //UPDATE message SET content="Hello, Wechat!";
- Message * message = [[Message alloc] init];
- message.content = @"Hello, Wechat!";
- //下面这句在17号的时候和微信团队的人在学习群里面沟通过,这个方法确实是不存在的,使用教程应该会更新,要是没更新注意这个方法
- //BOOL result = [database updateTable:@"message" onProperties:Message.content withObject:message];
- return [database updateAllRowsInTable: @"message"onProperty: Message.content withObject: message];
- }
- //查询
- - (NSArray * ) seleteMessage {
- //SELECT * FROM message ORDER BY localID
- NSArray < Message * >*message = [database getObjectsOfClass: Message.class fromTable: @"message"orderBy: Message.localID.order()];
- return message;
- }
上面事务方面的暂时先说这么多,当然这方面的内容可以看具体的文档: 基础类、CRUD 与 Transaction
WCDB 其他
1、全局监控与错误处理
WCDB 提供了对错误和性能的全局监控,可用于调试错误和性能。 也可以获取某个特定操作的错误信息。所有错误都以 WCTError 的形式出现。WCTError 就是继承自我们常见的 NSError。
2、损坏修复
WCDB 内建了修复工具,以应对数据库损坏,无法使用的情况。我们需要在数据库未损坏时,对数据库元信息定时进行备份,如下:
- NSData *backupPassword = [@"MyBackupPassword" dataUsingEncoding:NSASCIIStringEncoding];
- [database backupWithCipher:backupPassword];
注意:当检测到数据库损坏,即 WCTError 的 type 为 WCTError
,code 为 11 或 26(SQLITE_CORRUPT 或 SQLITE_NOTADB)时,可以进行修复, 下面是官方给出的代码示例:
- TypeSQLite
- //Since recovering is a long time operation, you'd better call it in sub-thread.
- [view startLoading];
- dispatch_async(DISPATCH_QUEUE_PRIORITY_BACKGROUND, ^{
- WCTDatabase *recover = [[WCTDatabase alloc] initWithPath:recoverPath];
- NSData *password = [@"MyPassword" dataUsingEncoding:NSASCIIStringEncoding];
- NSData *backupPassword = [@"MyBackupPassword" dataUsingEncoding:NSASCIIStringEncoding];
- int pageSize = 4096;//Default to 4096 on iOS and 1024 on macOS.
- [database close:^{
- [recover recoverFromPath:path
- withPageSize:pageSize
- backupCipher:cipher
- databaseCipher:password];
- }];
- [view stopLoading];
- });
3、性能数据
为了测试 WCDB 的性能数据,WCDB 提供了 benchmark,用于横向比较 FMDB、纵向比较不同的参数配置,并可用于验证后续更多性能优化的效果。这里吧官方的文档给大家,有需要有兴趣的可以看看,这部分的内容以及下面从 FMDB 迁移到 WCDB 的内容我们会抓们整理出来,因为项目我也准备迁移到 WCDB,等搞定会把相应新的分享出来。
4、从 FMDB 迁移到 WCDB
这部分的内容上面提到自己在准备迁移到 WCDB,所以就等迁移完成了会分享出心得。
上面的内容其实都是一些 WCDB 最基本的使用,也是希望 WCDB 大家都能掌握,既然是比 FMDB 好的存在我们也是肯定需要掌握的!
上面涉及到的 Demo 地址
来源: http://www.cnblogs.com/taoxu/p/7373916.html