sModel 2.0.0

sModel 2.0.0

Stephen Lynn 维护。



sModel 2.0.0

  • Stephen Lynn

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将通过使用syncStatussyncInFlightStatus字段来跟踪行的当前同步状态来防止本地更改被服务器更新覆盖。系统将不允许使用辅助键更新尚未同步的行。这假设您的表的主键是一个仅本地值,服务器更新将只为辅助键提供一个值。

同步状态

正确处理同步状态对于使用 SyncableModel 尤为重要。行更新只有在以下条件满足时才会发生:

  1. 您提供了行的主键或
  2. 您提供了行的辅助键,并且在数据库中将 syncStatussyncInFlightStatus 属性都设置为 .synced

粘性属性

粘性属性是一种不可为空属性,一旦赋予了值,就无法设置为空。这对于一些计算成本高但希望在对象更新间保留的属性非常有帮助。通过实现 StickyProperties 协议,ModelDef 可以被标记为包含粘性属性。

批量处理

使用 savedelete 方法管理对象对小型数据集非常有效,但是处理大量数据时会明显影响性能。DBManager.executeStatements 方法将接受一个语句数组,并将它们作为一个单个数据库事务的一部分执行。这意味着如果其中一个语句失败,所有更改都会回滚,从而防止数据库进入损坏状态。它还显著提高了数据添加/更新/从数据库删除的速度。数据库语句可以通过手动生成,也可以通过 ModelDef 对象的 createSaveStatementcreateDeleteStatement 方法生成。

查询

从数据库中查询数据也是非常直接的。每个模型对象都有一组静态方法,可用于查询数据库。

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中运行单元测试