前言
最近对Swift兴趣浓厚,既然年初WCDB团队发布了Swift的版本(而且是纯Swift实现),自然要上手体验一下,毕竟实践出真知嘛。
废话不多说,直接进入主题,其实WCDB的文档已经很详细了,这里是 安装方法。
下面介绍WCDB的用法:
模型绑定
WCDBSwift的模型绑定分为五部分: 字段映射,字段约束,索引,表约束,虚拟表映射.
模型绑定(Object-relational Mapping,简称 ORM),通过对 Swift 类或结构进行绑定,形成类或结构 - 表模型、类或结构对象 - 表的映射关系,从而达到通过对象直接操作数据库的目的。
1.字段映射
本节主要讲怎么去定义一个TableCodable类型的模板对象。
1 | class Sample: TableCodable { |
- 字段映射要求在类内定义CodingKeys的枚举类,并遵循String和CodingTableKey;
- 枚举内需要列举每一个需要定义的字段;
- 对于变量名和表名不一样的情况,可以使用别名进行映射,如case ideatifity = ‘id’;
- 对于不需要写入数据库的字段则不需要写入映射里面。
- 对于变量名和SQLite保留关键字冲突的字段,可以使用别名进行映射。
2.字段约束
字段约束是TableEncodable的一个可选函数,可根据需求选择实现或不实现。它用于定义针对单独字段的约束,如主键约束,非空约束,唯一约束,默认值等.
字段约束示例代码如下:
1 | class Sample: TableCodable { |
字段约束通过使用CodingKeys到字段约束的字典实现,定义每个CodingKeys对应的约束。ColumnConstraintBinding初始化函数声明如下:
1 | ColumnConstraintBinding( |
定义了 isPrimary: 的字段,支持以自增的方式进行插入数据。但仍可以通过非自增的方式插入数据。
当需要进行自增插入时,对象需设置 isAutoIncrement 参数为 true,则数据库会使用 已有数据中最大的值+1 作为主键的值。
3.索引
索引是 TableEncodable 的一个可选函数,可根据需求选择实现或不实现。它用于定于针对单个或多个字段的索引,索引后的数据在能有更高的查询效率。
索引通过索引后的后缀与索引绑定的映射实现。
- 对于需要特别指明索引存储顺序的字段,可以通过 asIndex(orderBy:) 函数指定,如 description.asIndex(orderBy: .descending)。
- 对于具有唯一性的索引,可以通过 isUnique: 参数指定,如 IndexBinding(isUnique: true, indexesBy: identifier)。
- 对于由多个字段组成的联合索引,可以通过 indexesBy: 进行指定,如 (indexesBy: multiIndexPart1, multiIndexPart2.asIndex(orderBy: .ascending))
完整的索引名为表名+索引后缀,如:表 “sampleTable” 的索引分别为 “sampleTable_uniqueIndex”、”sampleTable_descendingIndex” 和 “sampleTable_multiIndex”。
4.表约束
表约束是 TableEncodable 的一个可选函数,可根据需求选择实现或不实现。它用于定于针对多个字段或表本身的约束。
表约束通过约束名到表约束的映射实现。
- MultiPrimaryBinding: 联合主键约束;
- MultiUniqueBinding: 联合唯一约束;
- MultiUniqueBinding: 约束检查;
- ForeignKeyBinding: 外键约束;
5.数据库升级
对于映射字段
- 表中存在但模型绑定中未定义的字段,会被忽略,这个可以用来删除字段。
- 表不存在但在模型绑定中有定义的字段,会被新增到表中。 这可用于新增字段。
- 对于需要重命名的字段,可以通过别名的方式重新映射。
忽略字段并不会删除字段。对于该字段的旧内容,会持续存在在表中,因此文件不会因此变小。实际上,数据库作为持续增长的二进制文件,只有将其数据导出生成另一个新的数据库,才有可能回收这个字段占用的空间。对于新插入的数据,该字段内容为空,不会对性能产生可见的影响。
增删查改
1.插入操作
插入操作有 “insert” 和 “insertOrReplace” 两个接口。故名思义,前者只是单纯的插入数据,当数据出现冲突时会失败,而后者在主键一致时,新数据会覆盖旧数据。
insert函数的原型:
1 | // insert 和 insertOrReplace 函数只有函数名不同,其他参数都一样。 |
插入是最常用且比较容易操作卡顿的操作,因此 WCDB Swift 对其进行了特殊处理。 当插入的对象数大于 1 时,WCDB Swift 会自动开启事务,进行批量化地插入,以获得更新的性能。
2.删除操作
删除函数原型:
1 | func delete(fromTable table: String, // 表名 |
删除示例代码:
1 | // 删除 sampleTable 中所有 identifier 大于 1 的行的数据 |
删除接口不会删除表本身,开发者需要调用 drop(table:) 接口删除表。
3.更新
更新的操作接口有两个,函数原型如下:
1 | func update<Object: TableEncodable>( |
4.查找接口
- getObjects
- getObject
- getRows
- getRow
- getColumn
- getDistinctColumn
- getValue
- getDistinctValue
虽然查找的接口比较多,但是大部分都是为了简化操作而提供的便捷接口。实现上其实与update类似,只有两种方式:
1 | func getObjects<Object: TableDecodable>( |
数据库,表,事务
WCDB Swift的三个基础类: 数据库(Database),表(Table),事务(Transaction)。他们同事拥有以下特性:
- 支持增删改查的便捷接口;
- 支持链式接口;
- 数据和状态共享;
- 线程安全;
- 数据和状态共享,意味着对于同一个路径的数据库中不同基础类,他们的标签,数据库是否打开,是否在进行读写操作等所有的状态和数据都始终保持一致。
基础类共享数据和状态的本质是,他们共享同一个Core,而所有的操作都在这个Core上发生。
线程安全,意味着开发者可以在任意线程对任意基础类调用任意接口,而不需要考虑数据库本身的线程安全问题。同时,WCDB会根据调用情况,并发执行操作,以达到更高的性能。
WCDB支持多线程读操作 或 单线程写多线程读 并发执行.
1.数据库
Database是WCDB最基础的类,几乎所有的操作都由该类发起.
1 | // 一般可以通过一个文件路径或者一个文件url初始化 |
数据库会在第一次进行操作时,自动打开并初始化。开发者不需要手动调用。 关闭数据库一般也不需要开发者手动调用。当没有指向database所有共享的core时,数据库会自动关闭,并回收内存.
2.表
Table指数据库中的一个表。可以通过getTable接口获取.
1 | let table = try database.getTable(named: "sampleTable", of: Sample.self) // 表不存在时会出错 |
表相当于指定了表名和模型绑定类的 Database,其实质只是后者的简化版。
3.事务
事务一般用于提升性能和保证数据原子性。Database和Table都能直接发起事务,也可以通过Transaction更好的控制事务.
1 | try database.run(transaction: { |
事务提升性能的实质是批量处理。
事务可以用来保证某些多线程操作的原子性,以保证线程安全。
4.语言集成查询
语言集成查询(WCDB Integrated Language Query,简称 WINQ),是 WCDB 的一项基础特性。它使得开发者能够通过 Swift 的语法特性去完成 SQL 语句。
1 | let objects: [Sample] = try database.getObjects(fromTable: "sampleTable", where: Sample.Properties.idetifier > 1) |
语言集成查询基于SQLite的SQL语法的实现。只要是SQL支持的语句,都能使用语言集成查询完成。也因此,语言集成查询具有和SQL语法一样的复杂性。通过WCDB可以将已有的SQL转换为语言集成查询的写法。
这里是官方文档 传送门
5.加密与配置
SQLite 支持的配置列表可参考其官方文档。
配置主要用以控制数据库的操作行为。WCDBSwift对SQLite数据库进行了基本的配置以满足WCDB的需求。同事开发者也可以根据自己的需要,自定义配置项。
加密功能可通过 setCipher(key:pageSize:) 接口开启。
其中,pageSize 是加密的页大小参数,SQLCipher 在 iOS 上默认使用 4096,macOS 上默认使用 1024。而 WCDB Swift 则在所有平台上都适用 4096,以获得更好的性能。开发者一般不需要做特别的修改。
1 | let database = Database(withPath: filePath) |
注意:开启加密会有部分性能损耗。 值得注意的是,设置密码是一件很慎重的事情。对于已经创建且存在数据的数据库,无论是原本未加密想要改为加密,还是已经加密想要修改密码,都是成本非常高的操作,因此不要轻易使用。
后记
WCDB本身就是微信内部使用的数据库封装库,而且开源也有一段时间了,功能十分全面,文档也到位,上手还是十分简单的。
只是我在写Demo的时候掉了一个坑,反复调试,并且和网上的Demo代码进行比对之后才算发现问题。原来是我在定义属性的同时给它赋了值,导致WCDB无法映射出字段名,从而引发crash,日常开发中可以避免这样的问题,所以影响不大。
附上我写的 Demo源码
使用ORM的便捷性自然不用说,用过WCDB之后可能再也回不去FMDB,手写SQL语句的时候了,这才是真正的编程啊(手动滑稽)。
更多深入的使用方法,在做业务的时候可能会用到,这里是WCDB的 官方接口文档