sModel
sModel是一个在FMDB之上的Swift框架,提供了
- 简单的数据库模式管理(包括模式更新)
- 将数据库行映射到Swift对象的简单映射
- 批量更新以改善大型更新的性能
- 简化与外部数据同步的本地数据管理
该sModel库已在AppStore中的多个应用程序中使用多年。此代码是生产就绪的,并在数百万用户多个应用程序中经过战斗测试。兼容Swift 5。
DB Schema Management
sModel将接受一个sql
字符串数组并对其数据库执行。这些sql
字符串的执行顺序很重要,因此我们建议将它们存储在数组中。每个sql
字符串保证在整个应用程序安装的设备生命周期内仅运行一次。简单地将新的sql
字符串添加到数组的末尾,以根据应用程序的需求调整模式,下次应用程序运行时,sModel将更新数据库模式。
注意:永远不要删除旧的sql
字符串。这些字符串将在新安装时执行,并确保在所有设备上一致地构建数据库模式。
let defs: [String] = ["CREATE TABLE \"Thing\" (\"tid\" TEXT PRIMARY KEY, \"name\" TEXT);"]
try? DBManager.open(nil, dbDefs: defs)
Bad Upgrade Recovery
如果数据库文件已被损坏或由于某些原因无法更新,系统将尝试通过删除现有数据库并初始化新的数据库来恢复。
对象映射
sModel会从数据库中读取数据并将其映射到您的模型中。为此,您只需采用ModelDef
协议。以下是一个与上述数据库模式定义相匹配的示例模型结构。
struct Thing: ModelDef {
var tid: String
var name: String?
typealias ModelType = Thing
static let tableName = "Thing"
var primaryKeys: Array<CodingKey> { return [CodingKeys.tid] }
var secondaryKeys: Array<CodingKey> { return [] }
}
sModel更倾向于明确性而不是推论您配置的信息。这就是为什么您需要指定模型对象将存储的数据库表名称。这种明确性避免了任何“魔法”,并为您提供了根据项目配置灵活配置事物的灵活性。
ModelDef
可以安全地使用Int、Double、Bool、String和Date类型的属性。您甚至可以将属性设置为枚举,只要枚举符合Codable
协议。
插入/更新
将对象插入数据库就像创建该对象的实例、填充数据并调用save
一样简单。
let thing = Thing(tid: "tid1", name: "thing 1")
try? thing.save()
要更新现有对象,只需修改其属性并调用save
。
注意:如果对save
的调用导致约束违反,默认情况下系统将抛出一个包含导致约束违反的现有模型对象的ModelError.duplicate
错误。可以通过采用SyncableModel
协议或通过全局的DBManager.blindlyReplaceDuplicates
标志来更改表表之间约束违反的处理方式。有关这些属性的详细信息,请参阅注释。
处理可同步的数据
ModelDef
可以通过实现SyncableModel
协议来标记为可同步。当您数据库中的数据可能在从外部源获得更新(如从服务器获得的更新)的同时在本地可能发生变化时,这很有帮助。一个SyncableModel
将通过使用syncStatus
和syncInFlightStatus
字段来跟踪行的当前同步状态来防止本地更改被服务器更新覆盖。系统将不允许使用辅助键更新尚未同步的行。这假设您的表的主键是一个仅本地值,服务器更新将只为辅助键提供一个值。
同步状态
正确处理同步状态对于使用 SyncableModel
尤为重要。行更新只有在以下条件满足时才会发生:
- 您提供了行的主键或
- 您提供了行的辅助键,并且在数据库中将
syncStatus
和syncInFlightStatus
属性都设置为.synced
。
粘性属性
粘性属性是一种不可为空属性,一旦赋予了值,就无法设置为空。这对于一些计算成本高但希望在对象更新间保留的属性非常有帮助。通过实现 StickyProperties
协议,ModelDef
可以被标记为包含粘性属性。
批量处理
使用 save
或 delete
方法管理对象对小型数据集非常有效,但是处理大量数据时会明显影响性能。DBManager.executeStatements
方法将接受一个语句数组,并将它们作为一个单个数据库事务的一部分执行。这意味着如果其中一个语句失败,所有更改都会回滚,从而防止数据库进入损坏状态。它还显著提高了数据添加/更新/从数据库删除的速度。数据库语句可以通过手动生成,也可以通过 ModelDef
对象的 createSaveStatement
和 createDeleteStatement
方法生成。
查询
从数据库中查询数据也是非常直接的。每个模型对象都有一组静态方法,可用于查询数据库。
let things = Thing.allInstances() //Returns Array<Thing> that holds every instance of `Thing`
// Where clauses
let thing = Thing.firstInstanceWhere("tid = ?", "tid1") //Returns a Thing?
let someThings = Thing.instancesWhere("tid in (?, ?)", "tid1", "tid2") //Return Array<Thing> for each `Thing` that matches the where clause
完整工作示例
要查看所有部分如何协同工作,请参考 sModelTests/example
文件夹中的完整工作示例。这包括模式定义文件、模型示例以及执行sModel中所有CRUD操作的代码。其他单元测试也可以用来查看如何使用每个公开API。
本地运行
要在本地运行测试
- 安装Carthage
- 在项目根目录下,运行
carthage update --platform ios
- 在XCode中运行单元测试