前言
移动端的数据库选型一直是一个难题,直到前段时间看到了 WeMobileDev(微信前端团队)放出了第三个开源组件-WCDB
WCDB(WeChat DataBase)是微信官方的移动端数据库组件,致力于提供一个高效、易用、完整的移动端存储方案
基于 SQLCipher
WCDB-iOS/Mac
WCDB-Android
数据库损坏修复工具 WDBRepair
WCDB 的出现可以说解决了目前移动端数据库的几个难点
首先在选型上,FMDB的 SQL 拼接、难以防止的 SQL 注入;CoreData虽然可以方便 ORM,但学习成本高,稳定性堪忧,而且多线程鸡肋;另外基于 C 语言的sqlite我想用的人也应该不多;除了上述关系型数据库之外然后还有一些其他的 Key-Value 型数据库,如我用过的 Realm,对于 ObjC 开发者来说,上手倒是没什么难度,但缺点显而易见,需要继承,入侵性强,对于单继承的 OC 来说这并不理想,而且对于集合类型不完全支持,复杂查询也比较无力。
高效
多线程高并发:WCDB 支持多线程读与读、读与写并发执行,写与写串行执行。
批量写操作性能测试:
| 批量写 | ops/sec |
|---|---|
| WCDB | 458000 |
| FMDB | 161000 |
易用 WCDB 支持一句代码即可将数据取出并组合为 object
WINQ(WCDB 语言集成查询):通过 WINQ,开发者无须为了拼接 SQL 的字符串而写一大坨胶水代码。
ORM(Object Relational Mapping):WCDB 支持灵活、易用的 ORM。开发者可以很便捷地定义表、索引、约束,并进行增删改查操作。
像这样
[database getObjectsOfClass:WCTSampleConvenient.class
fromTable:tableName
where:WCTSampleConvenient.intValue>=10
limit:20];完整
在 WCDB 内,ORM(Object Relational Mapping)是指
将一个 ObjC 的类,映射到数据库的表和索引;
将类的 property,映射到数据库表的字段;
这一过程。通过 ORM,可以达到直接通过 Object 进行数据库操作,省去拼装过程的目的。
WCDB 通过内建的宏实现 ORM 的功能。如下


PS:但我不建议这么做,首先要避免在.h 文件中引用<WCDB/WCDB.h>,因为你一旦引用,就需要改变.m 文件为.mm 文件,因为 WCDB 是基于 objectiveC++;你可以使用 Category 特性将其隔离,在 category 中引用<WCDB/WCDB.h>,并遵守WCTTableCoding协议,使用WCDB_PROPERTY将声明绑定到数据库表的字段。然后在模型类中引用 category。达到不印象 Controller 和 View 的目的。这点官方 wiki 中也有提到,使用文件模板来创建。具体请见 Demo
对于一个已有的 ObjC 类,
引用 WCDB 框架头文件#import <WCDB/WCDB.h>,并定义类遵循 WCTTableCoding 协议
WCDB_PROPERTY用于在头文件中声明绑定到数据库表的字段。
WCDB_IMPLEMENTATION,用于在类文件中定义绑定到数据库表的类。同时,该宏内实现了 WCTTableCoding。因此,开发者无须添加更多的代码来完成 WCTTableCoding 的接口
WCDB_SYNTHESIZE,用于在类文件中定义绑定到数据库表的字段。
WCDB_PRIMARY用于定义主键
WCDB_PRIMARY_AUTO_INCREMENT 用于定义自增主键
WCDB_INDEX用于定义索引
WCDB_UNIQUE用于定义唯一约束
WCDB_NOT_NULL用于定义非空约束
得益于 ORM 的定义,WCDB 可以直接进行通过 object 进行增删改查(CRUD)操作。
增
//插入
Person *man = [[Person alloc] init];
man.isAutoIncrement = YES;
man.name = @"Hello, WCDB!";
man.age = 12;
return [database insertObject:man into:TABLE_WCDB_NAME];删
return [database deleteObjectsFromTable:TABLE_WCDB_NAME where:Person.studentId == studentId];改
Person *person = [[Person alloc] init];
person.name = content;
return [database updateRowsInTable:TABLE_WCDB_NAME onProperties:Person.name withObject:person where:Person.studentId == studentId];查
NSArray<Person *> * person = [database getObjectsOfClass:Person.class fromTable:TABLE_WCDB_NAME orderBy:Person.localID.order()];WCDB 内可通过两种方式执行 Transaction(事务),一是 runTransaction:接口

这种方式要求数据库操作在一个 BLOCK 内完成,简单易用。
另一种方式则是获取 WCTTransaction 对象

WCTTransaction 对象可以在类或函数间传递,因此这种方式也更具灵活性。
WINQ(WCDB Integrated Query,音'wink'),即 WCDB 集成查询,是将自然查询的 SQL 集成到 WCDB 框架中的技术,基于 C++实现。
as 重定向
基于 ORM 的支持,我们可以从数据库直接取出一个 Object。然而,有时候需要取出并非是某个字段,而是有一些组合。例如:
这段代码从数据库中取出了消息的最新的修改时间,并以此将此时间作为消息的创建时间,新建了一个 message。这种情况下,就可以使用 as 重定向。
as 重定向,它可以将一个查询结果重定向到某一个字段,如下:
通过 as(Message.createTime)的语法,将查询结果重新指向了 createTime。因此只需一行代码便可完成原来的任务。
链式调用
链式调用是指对象的接口返回一个对象,从而允许在单个语句中将调用链接在一起,而不需要变量来存储中间结果。
WCDB 对于增删改查操作,都提供了对应的类以实现链式调用
where、orderBy、limit 等接口的返回值均为 self,因此可以通过链式调用,更自然更灵活的写出对应的查询。
传统的接口方便快捷,可以直接获得操作结果;链式接口则更具灵活性,开发者可以获取数据库操作的耗时、错误信息;也可以通过遍历逐个生成 object。
WCDB 内同时支持这两种接口,优势互补,开发者可以根据需求,选择使用。
多表查询
SQLite 支持联表查询,在某些特定的场景下,可以起到优化性能、简化表结构的作用。
WCDB 同样提供了对应的接口,并在 ORM 的支持下,通过 WCTMultiSelect 的链式接口,可以同时从表中取出多个类的对象。
类字段绑定
在 ORM 中,我们通过宏,将 ObjC 类的 property 绑定为数据库的一个字段。但并非所有 property 的类型都能绑定到字段。
WCDB 内置支持的类型有:
然而,内置支持得再多,也不可能完全覆盖开发者所有的需求。因此 WCDB 支持开发者自定义类字段绑定。
类只需实现 WCTColumnCoding 协议,即可支持绑定。
基本功能
接入与迁移
数据库修复
从源码编译
参考资料
前言
移动端的数据库选型一直是一个难题,直到前段时间看到了 WeMobileDev(微信前端团队)放出了第三个开源组件-WCDB
WCDB(WeChat DataBase)是微信官方的移动端数据库组件,致力于提供一个高效、易用、完整的移动端存储方案
基于 SQLCipher
WCDB-iOS/Mac
WCDB-Android
数据库损坏修复工具 WDBRepair
WCDB 的出现可以说解决了目前移动端数据库的几个难点
首先在选型上,FMDB的 SQL 拼接、难以防止的 SQL 注入;CoreData虽然可以方便 ORM,但学习成本高,稳定性堪忧,而且多线程鸡肋;另外基于 C 语言的sqlite我想用的人也应该不多;除了上述关系型数据库之外然后还有一些其他的 Key-Value 型数据库,如我用过的 Realm,对于 ObjC 开发者来说,上手倒是没什么难度,但缺点显而易见,需要继承,入侵性强,对于单继承的 OC 来说这并不理想,而且对于集合类型不完全支持,复杂查询也比较无力。
高效
多线程高并发:WCDB 支持多线程读与读、读与写并发执行,写与写串行执行。
批量写操作性能测试:
| 批量写 | ops/sec |
|---|---|
| WCDB | 458000 |
| FMDB | 161000 |
易用 WCDB 支持一句代码即可将数据取出并组合为 object
WINQ(WCDB 语言集成查询):通过 WINQ,开发者无须为了拼接 SQL 的字符串而写一大坨胶水代码。
ORM(Object Relational Mapping):WCDB 支持灵活、易用的 ORM。开发者可以很便捷地定义表、索引、约束,并进行增删改查操作。
像这样
[database getObjectsOfClass:WCTSampleConvenient.class
fromTable:tableName
where:WCTSampleConvenient.intValue>=10
limit:20];完整
在 WCDB 内,ORM(Object Relational Mapping)是指
将一个 ObjC 的类,映射到数据库的表和索引;
将类的 property,映射到数据库表的字段;
这一过程。通过 ORM,可以达到直接通过 Object 进行数据库操作,省去拼装过程的目的。
WCDB 通过内建的宏实现 ORM 的功能。如下


PS:但我不建议这么做,首先要避免在.h 文件中引用<WCDB/WCDB.h>,因为你一旦引用,就需要改变.m 文件为.mm 文件,因为 WCDB 是基于 objectiveC++;你可以使用 Category 特性将其隔离,在 category 中引用<WCDB/WCDB.h>,并遵守WCTTableCoding协议,使用WCDB_PROPERTY将声明绑定到数据库表的字段。然后在模型类中引用 category。达到不印象 Controller 和 View 的目的。这点官方 wiki 中也有提到,使用文件模板来创建。具体请见 Demo
对于一个已有的 ObjC 类,
引用 WCDB 框架头文件#import <WCDB/WCDB.h>,并定义类遵循 WCTTableCoding 协议
WCDB_PROPERTY用于在头文件中声明绑定到数据库表的字段。
WCDB_IMPLEMENTATION,用于在类文件中定义绑定到数据库表的类。同时,该宏内实现了 WCTTableCoding。因此,开发者无须添加更多的代码来完成 WCTTableCoding 的接口
WCDB_SYNTHESIZE,用于在类文件中定义绑定到数据库表的字段。
WCDB_PRIMARY用于定义主键
WCDB_PRIMARY_AUTO_INCREMENT 用于定义自增主键
WCDB_INDEX用于定义索引
WCDB_UNIQUE用于定义唯一约束
WCDB_NOT_NULL用于定义非空约束
得益于 ORM 的定义,WCDB 可以直接进行通过 object 进行增删改查(CRUD)操作。
增
//插入
Person *man = [[Person alloc] init];
man.isAutoIncrement = YES;
man.name = @"Hello, WCDB!";
man.age = 12;
return [database insertObject:man into:TABLE_WCDB_NAME];删
return [database deleteObjectsFromTable:TABLE_WCDB_NAME where:Person.studentId == studentId];改
Person *person = [[Person alloc] init];
person.name = content;
return [database updateRowsInTable:TABLE_WCDB_NAME onProperties:Person.name withObject:person where:Person.studentId == studentId];查
NSArray<Person *> * person = [database getObjectsOfClass:Person.class fromTable:TABLE_WCDB_NAME orderBy:Person.localID.order()];WCDB 内可通过两种方式执行 Transaction(事务),一是 runTransaction:接口

这种方式要求数据库操作在一个 BLOCK 内完成,简单易用。
另一种方式则是获取 WCTTransaction 对象

WCTTransaction 对象可以在类或函数间传递,因此这种方式也更具灵活性。
WINQ(WCDB Integrated Query,音'wink'),即 WCDB 集成查询,是将自然查询的 SQL 集成到 WCDB 框架中的技术,基于 C++实现。
as 重定向
基于 ORM 的支持,我们可以从数据库直接取出一个 Object。然而,有时候需要取出并非是某个字段,而是有一些组合。例如:
这段代码从数据库中取出了消息的最新的修改时间,并以此将此时间作为消息的创建时间,新建了一个 message。这种情况下,就可以使用 as 重定向。
as 重定向,它可以将一个查询结果重定向到某一个字段,如下:
通过 as(Message.createTime)的语法,将查询结果重新指向了 createTime。因此只需一行代码便可完成原来的任务。
链式调用
链式调用是指对象的接口返回一个对象,从而允许在单个语句中将调用链接在一起,而不需要变量来存储中间结果。
WCDB 对于增删改查操作,都提供了对应的类以实现链式调用
where、orderBy、limit 等接口的返回值均为 self,因此可以通过链式调用,更自然更灵活的写出对应的查询。
传统的接口方便快捷,可以直接获得操作结果;链式接口则更具灵活性,开发者可以获取数据库操作的耗时、错误信息;也可以通过遍历逐个生成 object。
WCDB 内同时支持这两种接口,优势互补,开发者可以根据需求,选择使用。
多表查询
SQLite 支持联表查询,在某些特定的场景下,可以起到优化性能、简化表结构的作用。
WCDB 同样提供了对应的接口,并在 ORM 的支持下,通过 WCTMultiSelect 的链式接口,可以同时从表中取出多个类的对象。
类字段绑定
在 ORM 中,我们通过宏,将 ObjC 类的 property 绑定为数据库的一个字段。但并非所有 property 的类型都能绑定到字段。
WCDB 内置支持的类型有:
然而,内置支持得再多,也不可能完全覆盖开发者所有的需求。因此 WCDB 支持开发者自定义类字段绑定。
类只需实现 WCTColumnCoding 协议,即可支持绑定。
基本功能
接入与迁移
数据库修复
从源码编译
参考资料