前言:
iOS 中常用的数据库有 CoreData
、 SQLite
和 FMDB
等等,其中 CoreData
和 Xcode 深度结合,易费用较差; SQLite
自身便是C言语,运用需求了解C言语接口; FMDB
是对 SQLite
的一层封装,很多胶水代码,依然自己需求写 SQL
语句,而 WCDB
是微信团队开发的一个易用、高效、完整的移动数据库结构,它根据 SQLite
和 SQLCipher
开发,支撑加密、损坏检测、数据备份、和数据修正,在微信中运用广泛,且支撑在 C++
、 Swift
、 Objc
三种言语环境中运用。
WCDB 根底调用
最根底的调用进程大致分为三个过程:
- 模型绑定
- 创立数据库与表
- 操作数据
一、模型绑定
假定存在一个Person
类:
class Person {
var identifier: Int = 0
var name: String? = nil
var age: Int = 0
}
要把该类的数据存储进数据库,能够采用 WCDB
的文件模板:
1. 文件与代码模版
假如没有获取 WCDB
的 Github
仓库,能够终端执行命令:
curl https://raw.githubusercontent.com/Tencent/wcdb/master
/tools/templates/install.sh -s | sh
翻开 cmd + n
拉到最下面:
选swift创立,因为模板是旧代码,需求把
import WCDB
改成import WCDBSwift
2. WCDB Swift 的模型绑定分为五个部分:
- 字段映射
- 字段束缚
- 索引
- 表束缚
- 虚拟表映射
1)字段映射
- 在类内界说
CodingKeys
的枚举类,遵循String
和CodingTableKey
; - 把想要存储的变量写到枚举
CodingKeys
里面的case
后边,表明绑定到了数据库表中的字段; - 把需求在数据库重命名的字段,进行别号映射,
case identifier = "id"
表明把identifier
在数据库表中重命名为id
; - 假如运用的字段与
SQLite
字段关键字相同,也需求做别号映射。
2)字段束缚
字段束缚,它用于针对单个字段的束缚,例如主键束缚、非空束缚、唯一束缚,默许值等等,能够根据自己的需求选择完成或许不完成。方法是 columnConstraintBindings:
,Github
上和 TableCodable
模板默许生成的是以前的代码,新代码如下:
static var columnConstraintBindings: [CodingKeys:
ColumnConstraintBinding]? {
return [
identifier: ColumnConstraintBinding(isPrimary: true),
name: ColumnConstraintBinding(isNotNull: true, defaultTo:"空"),
age: ColumnConstraintBinding(isNotNull: true, defaultTo: 0)
]
}
3)自增属性
isAutoIncrement
表明是否自增,束缚界说 isPrimary
为 true
,支撑自增,可是依然能够支撑非自增方法刺进。
当需求自增刺进,需求设置 isAutoIncrement
为 true
,数据库会将主键最大值 + 1
作为新的最大主键值。
索引,表束缚,虚拟表映射相对复杂,一般表用不到,这里就不写。
4)swift6 错误正告
因为 Github上WCDB
的是 swift4.0
的代码,现在运用有些需求修改,会提示:
在 class Person
前面添加 final
消除正告。
最终模型绑定代码:
final class Person: TableCodable {
var identifier: Int = 0
var name: String? = nil
var age: Int = 0
enum CodingKeys: String, CodingTableKey {
typealias Root = Person
case identifier = "id"
case name
case age
static let objectRelationalMapping =
TableBinding(CodingKeys.self)
static var columnConstraintBindings:
[CodingKeys: ColumnConstraintBinding]? {
return [
identifier: ColumnConstraintBinding
(isPrimary: true),
name: ColumnConstraintBinding
(isNotNull: true, defaultTo: "空"),
age: ColumnConstraintBinding
(isNotNull: true, defaultTo: 0)
]
}
}
var isAutoIncrement: Bool = true
}
二、创立数据库与表
1. 创立数据库
var database = Database(withPath: NSSearchPathForDirectoriesInDomains(
.documentDirectory,
.userDomainMask,
true).last!+”/Person/person.db")
2. 创立表
一行代码就创立表:
try database?.create(table: "personTable", of: Person.self)
因为 WCDB
推荐用表操作数据,所以能够获取表对象:
var personTable = try database.getTable(named: "personTable")
三、操作数据
1. 刺进操作
向表中刺进一条数据,id
前面已经界说了自增:
try database?.insert(objects: p, intoTable: "personTable")
WCDB
推荐操作表,因为操作的对象更明确,更简练,后边代码都是表操作:
try personTable?.insert(objects: p)
2. 删去操作
示例代码,删去 id
为 2 的数据:
try personTable?.delete(where: Person.Properties.identifier == 2)
3. 更新操作
示例代码,更新 id
为 2 的数据的 name
字段:
try personTable?.update(on: Person.Properties.name, with: p,
where: Person.Properties.identifier == 2 )
4. 查找操作
示例代码,查找年纪大于 25 的数据:
let persons: [Person]! = try personTable?.getObjects(where:
Person.Properties.age > 25)
主要功能代码都是一行代码搞定,而且让增修改查的语法共同,运用十分便利。
四、数据库、表
1. 翻开数据库
因为 WCDB
是采用推迟初始化,运用时候才会创立而且初始化,所以不需求手动调用 open
,但能够运用 database.canOpen
测试数据是否能够正常翻开,另外 database.isOpened
需求创立表之后才会 true
。
2. 封闭数据库
WCDB
一般情况下不需求开发者手动调用封闭,假如操控器被收回,数据库会主动封闭,而且主动收回内存。当然,也能够手动调用,一般都是根据文件操作,比方移动文件影响到了数据库的数据,才需求手动封闭,接口是:
try database.close(onClosed: {
try database.moveFiles(toDirectory: otherDirectory)
})
3. 表
通过 getTable
接口获取数据库中的一个表:
let table = database.getTable(named: "sampleTable", of: Sample.self)
WCDB
中 Table
具有了 database
的所有增修改查接口,而且更简练,以表为单位来办理数据读写逻辑更合理便利,所以尽量运用 Table
来进行数据读写操作,上面代码已经演示。
五、业务
业务一般用来提高功能和保证操作的原子性,通过 transaction
操控业务。
1. 功能
假定给数据库刺进 100 万条数据,看消耗时间和运用业务的优化情况,先准备 100 万条数据:
print("startTime ------------------" + startTime)
var persons:[Person] = [];
for i in 1...1000000{
let p = Person()
p.age = Int(arc4random_uniform(20)) + 10;
p.name = String(format: "张%d", i)
persons.append(p)
}
1)普通刺进操作
// 单独刺进,功率很差
for p in persons {
do{
try personTable!.insert(objects: p)
}catch let error{
debugPrint("刺进数据失利 \(error.localizedDescription)")
}
}
let endTime = Self.getCurrentTime(timeFormat: TimeFormat.HHMMSS)
print("endTime ------------------" + endTime)
数据库表中如下:
运转打印:
startTime ------------------ 12:05:43
endTime ------------------ 12:06:23
普通操作,刺进 100 万条数据,耗时差不多 41 秒。
2)业务刺进操作
// 业务刺进,功能较好
do{
try database.run(transaction: {
for object in persons {
try personTable?.insert(objects: object)
}
})}catch let error{
debugPrint("刺进数据失利 \(error.localizedDescription)")
}
let endTime = Self.getCurrentTime(timeFormat: TimeFormat.HHMMSS)
print("endTime ------------------" + endTime)
运转打印:
startTime ------------------ 12:14:42
endTime ------------------ 12:14:55
业务操作,刺进 100 万条数据,耗时差不多 13 秒,功能有显着提高。
3)内置业务刺进操作
insert(objects:)
接口内置了业务,并对批量数据做了针对性的优化,功能更好
do{
try personTable?.insert(objects: persons)
}catch let error{
debugPrint("刺进数据失利 \(error.localizedDescription)")
}
let endTime = Self.getCurrentTime(timeFormat: TimeFormat.HHMMSS)
print("endTime ------------------" + endTime)
运转打印:
startTime ------------------ 12:23:05
endTime ------------------ 12:23:08
能够看出,内置业务接口的优化十分显着,刺进 100 万条数据只运用了 3 秒。
2. 原子性
在多线程下,删去数据,一起刺进一条数据,操作在瞬间,很难确实哪个先执行:
1)非业务操作
DispatchQueue(label: "other thread").async {
do{
try self.personTable?.delete()
}catch let error{
debugPrint("业务操作失利 \(error.localizedDescription)")
}
}
do{
let p = Person()
p.age = Int(arc4random_uniform(20)) + 10;
p.name = "马可bro"
try personTable?.insert(objects: p)
let objects = try personTable?.getObjects()
print(objects?.count ?? "出错") // 或许输出 0 或 1
}catch let error{
debugPrint("业务操作失利 \(error.localizedDescription)")
}
成果:或许输出 0 或 1 或 2 。
2)业务操作
DispatchQueue(label: "other thread").async {
do{
try self.personTable?.delete()
}catch let error{
debugPrint("业务操作失利 \(error.localizedDescription)"
}
}
do {
try database.run(transaction: {
let p = Person()
p.age = Int(arc4random_uniform(20)) + 10;
p.name = "大小姐"
try personTable?.insert(objects: p)
let objects = try personTable?.getObjects()
print(objects?.count ?? "出错") // 输出 1
})
}catch let error{
debugPrint("业务操作失利 \(error.localizedDescription)")
}
成果:只会输出 1。
参阅
- Tencent/wcdb
- 微信移动端数据库组件WCDB系列