Shark ORM
Shark让您可以使用简单、干净的语法在iOS、macOS或tvOS应用程序中以自然的方式创建模型层。Shark为您做了所有繁重的工作,因此您不需要花费不必要的时间来处理数据对象。
它的座右铭很简单,要快速、简单且成为任何开发者的首选。
入门步骤
Shark旨在快速使您的应用程序工作,可以将其作为源代码或作为框架进行集成。
要求
XCode 9+, iOS8+
从CocoaPods安装
要安装它,只需将以下行添加到您的 Podfile
pod "SharkORM"
作为框架安装
从 GitHub 发布版下载源代码,然后在您的应用中添加以下内容
import SharkORM
作为源安装
从 GitHub 下载源代码,并将 Core 和 SQLite 的内容添加到您的目标中,然后添加 SharkORM.h
到桥接头。
获取帮助和支持
如果您在使用过程中遇到问题,请直接在 Stack Overflow 上提问,团队会积极监控 SO 并尽快回答您的问题。
如果您发现了错误或想要建议新功能,请随时使用 GitHub 的问题追踪器(https://github.com/sharksync/sharkorm/issues)提交问题。
使用方法
设置您的项目
一旦将SharkORM框架添加到您的应用程序中,您就应在应用程序生命周期的最早阶段启动它。SRKDelegate也需要被设置,我们建议将其添加到您的应用程序代理中。
// Swift
class AppDelegate: UIResponder, UIApplicationDelegate, SRKDelegate
接着您需要尽早启动SharkORM。
// Swift
SharkORM.setDelegate(self)
SharkORM.openDatabaseNamed("MyDatabase")
对象
SharkORM对象是具有在它们上定义属性的普通类,ORM然后检查所有这些类并将它们的结构自动反映在SQLite数据库中。如果您添加或删除列,则表会更新以表示类的当前结构。
您可以使用这些类的方式与系统中任何其他类相同,并且可以通过方法扩展和子类化,以及通过无问题在各个线程之间传递。
类似于其他ORM,Swift和Objective-C实体必须将其属性定义为一动态属性,以允许ORM安装它的get/set方法。
// Swift
class Person: SRKObject {
@objc dynamic var name: String?
@objc dynamic var age: Int = 0
@objc dynamic var height: Float = 0
// add a one->many relationship from the Person entity to the Department entity.
@objc dynamic var department: Department?
}
模式(迁移)
模式将自动根据类签名和维护,所有新增、删除和类型更改都自动应用到数据库中。尽可能保留数据并转换类型。
如果使用defaultValuesForEntity
指定了默认值并且添加了一个属性,则列会自动填充默认值。
通过引用一个SRKObject的子类,自动创建表。
从模式中排除属性。
默认情况下,所有@objc dynamic var
属性都会被SharkORM捕获并添加到相应的表中。如果您希望排除某些属性(例如非持久化的属性),请实现ignoredProperties
类方法,返回一个字符串值数组,这些值与您希望ORM忽略的属性相匹配。
示例
// Swift
override class func ignoredProperties() -> [String] {
return ["height"]
}
示例对象
// Swift
class Person: SRKObject {
@objc dynamic var name: String?
@objc dynamic var age: Int = 0
@objc dynamic var payrollNumber: Int = 0
// add a one->many relationship from the Person entity to the Department entity.
@objc dynamic var department: Department?
}
初始值
您可以使用字典来初始化SRKObject,使您能够以编程方式填充对象。
示例
// Swift
let p = Person(dictionary: ["name" : "Shark Developer", "age" : 39])
##支持类型
Shark支持以下(Objective-c)类型: BOOL
、bool
、int
、int64
、uint
、uint64
、float
、double
、long
、long long
、unsigned long long
、NSString
、NSDate
、NSData
、NSNumber
。要使用某些本地Swift类型,您需要为它们提供一个默认值,因为它们的等效项是不可为空的。
关系
SRKObject
可以通过直接嵌入以创建一对一关系(例如:dynamic var department : Department?
)进行关联,或者为了实现一对一关系,我们可以使用一个返回NSArray<Person*>*
或SRKResultSet
对象的方法。
在已经定义了Person对象,并具有department属性的情况下,让我们来看看Department类。
Swift
class Department : SRKObject {
@objc dynamic var name: String?
@objc dynamic var location: Location?
}
一对一行关系
这是通过在SRKObject
类中添加Department
属性创建的,一旦设置了一定的值,就可以像使用任何其他属性一样使用它,并利用对象点表示法。
Swift
let employee = Person()
let section = Department()
employee.department = section
然后可以直接访问属性,Shark将自动检索任何相关对象,并允许您访问它们的属性。例如:使用代码employee.deparment.location.address
将自动检索满足语句所必需的对象,通过加载相关的Department
和Location
对象。
一对多关系
您可以通过向反向关系添加方法来定义一对多关系。例如,要将Department
和Person
关系定义为一对多关系,我们将在Department
中添加以下方法:
Swift
func people() -> [Person] {
return Person.query()
.where("department = ?", parameters: [self])
.fetch() as! [Person]
}
索引属性
Shark 支持通过重写 indexDefinitionForEntity
方法,返回一个 SRKIndexDefinition
对象,描述对象上需要维护的所有索引。
Swift
override class func indexDefinitionForEntity() -> SRKIndexDefinition? {
return SRKIndexDefinition(["name","age"])
}
这些索引将自动匹配到相应的查询,以帮助提高性能。所有相关对象属性都会自动索引,以满足缓存的必需。因此,例如,无需添加 Person.department
的索引,因为它已经创建好了。
默认值
通过覆盖方法 defaultValuesForEntity
,并在返回的默认值字典中指定默认值,可以为每次创建新的 SRKObject
指定一组默认值:Swift
override class func defaultValuesForEntity() -> [String : Any] {
return ["name" : "Billy", "age" : 36]
}
触发器
Shark 对象具有相同的可用于它们的公共方法,以在写入之前或之后强制约束并检查有效性的约束。
entityWillInsert(), entityWillUpdate(), entityWillDelete() 返回 bool
在执行任何操作之前,对象将接收到此方法。在此可以测试是否要继续执行操作。如果返回 true
,则会继续操作,但如果返回 false
,则会终止事务,并使提交返回 false
。
Swift
override func entityWillDelete() -> Bool {
return self.people().count == 0;
}
entityDidInsert(), entityDidUpdate(), entityDidDelete()
事件发生后,在事务完成后,对象将接收到此消息。
使用print()、NSLog或po打印对象
我们提供了一种可打印的字典样式输出,在调用时,会产生如下输出。
{
entity = Person;
joins = {
};
"pk column" = Id;
"pk value" = 36664;
properties = (
{
name = Id;
type = number;
value = 36664;
},
{
name = payrollNumber;
type = number;
value = 0;
},
{
name = age;
type = number;
value = 36;
},
{
name = Name;
type = unset;
value = "<null>";
},
{
name = location;
type = unset;
value = "<null>";
},
{
name = department;
type = unset;
value = "<null>";
},
{
name = seq;
type = number;
value = 0;
}
);
relationships = (
{
property = department;
status = unloaded;
target = Department;
},
{
property = location;
status = unloaded;
target = Location;
}
);
}
写入对象
Shark旨在简化对象的持久性到简单的commit
方法。这可以在任何时刻和任何线程中调用。如果对象包含单个或多个处于其内部的关联对象,那么在父对象上调用commit
将自动存储所有后续的对象。
Swift
// Create a new object
var thisPerson = Person()
// Set some properties
thisPerson.age = 38;
thisPerson.payrollNumber = 123456;
thisPerson.name = "Adrian Herridge";
// Persist the object into the datastore
thisPerson.commit()
对象将立即提交并将以原子方式写入存储。提交完成后将立即可查询。
commitOptions(属性)
commitOptions属性存在于所有SRKObjects中,使得开发者能够根据对象控制SharkORM在要求提交特定对象时的行为。
以下属性用于控制逻辑和添加精细粒度控制:
postCommitBlock 成功提交后调用
postRemoveBlock 对象从数据存储中删除后调用
ignoreEntities 允许开发者指定一个子/相关实体数组,当父对象提交时将不会被持久化。
commitChildObjects 如果设置,则所有子/相关实体将不会自动提交。
resetOptionsAfterCommit 如果设置,则所有默认值都将恢复,并且所有块都将清除。
raiseErrors 如果设置为false,则生成的任何错误都将被忽略,并且不会与委托一起引发。事务也将不会失败。
triggerEvents 如果设置为true,则会对插入、更新、删除操作引发事件。
在事务中写入
对于某些批量存储情况,将大量的写操作合并成一个事务可能更好,这样可以提高速度,并确保数据到存储的持久性具有原子性。所有对象的所有更改,在块中任何异常发生时将会回滚。只有在事务成功完成后才会执行事件触发。
您可以在块中调用SRKFailTransaction()
来手动失败一个事务,允许开发者根据应用程序逻辑中止和回滚。
Swift
SRKTransaction.transaction({
// Create a new object
var thisPerson = Person()
thisPerson.name = "Adrian Herridge";
thisPerson.commit()
}) {
// the rollback on failure
}
查询
为了检索对象,我们使用与每个SRKObject
类关联的SRKQuery对象。这然后将可选参数,如where
、limit
、orderBy
和offset
。所有这些参数都会返回相同的查询对象,使得在单个嵌套指令中构建查询成为可能。
通过调用fetch
、count
、sum
、fetchLightweight
和fetchAsync
来完成查询对象的最终调用,然后执行查询并返回结果。
以下是一个查询整个表的示例:Swift
var results : SRKResultSet = Person.query().fetch()
可以使用FLUENT接口来构建查询,因此除检索方法之外的每个调用都返回自身作为SRKQuery
,允许您嵌套参数。Swift
var results = Person.query()
.where("age = ?", parameters: [35])
.limit(99)
.order("name")
.fetch()
您也可以使用对象点符号通过属性路径查询相关对象。如果我们以Person类作为示例,该类通过department
属性与Department类相关。
Swift
Person.query().where("department.name = ?", parameters: ["Test Department"]).fetch()
当name
位于相关对象中时,SharkORM现在会自动重新排列查询,在那种关系上连接两个表,并因此验证该条件。
SRKQuery
的参数
支持给Shark支持以下可选查询参数
where、where(with参数)
这是提供给查询的查询字符串,可以包含由?
表示的占位符。不需要将字符串参数用引号括起来,因为SQLite的bind参数调用会自动处理。
参数对象也可以是用于子查询的数组或集,例如"department IN (?)", parameters: [[1,2,3,4,5]]
。
限制
指定要返回的查询结果的数量限制
排序
指定按何种顺序返回 SRKResultSet
。这些可以链接在一起以产生多个向量。例如,.....order("Name").order(descending: "age").fetch()
偏移
指定要检索值中的偏移量,以便开发人员可以在需要时只检索数据窗口。
批处理
虽然这不会影响查询,但允许开发人员迭代大量数据集,而无需处理整个数据集的性能和内存问题。如果指定了10个批处理大小,那么SRKResultSet
将执行完整的查询,但只会完全检索前10个对象。然后,它将保持批处理大小窗口,在遍历结果时自动批量检索。这使得开发人员可以在不改变代码编写方式的情况下优化系统。
连接到
Shark 允许进行 LEFT JOIN
联接,以实现更快速和较少嵌套的查询。有关更多信息,请参见连接。
其他查询类型
除了检索整个对象之外,还有一些额外的查询类型,帮助开发者解决其他问题。
fetchLightweight
从存储中获取对象,但不检索任何属性值。这些值在访问时懒加载,可以配置为在之后永久可用或立即释放。
fetchAsync
在后台线程上执行异步查询,并在结果完成时执行提供的代码块。
count
返回查询的计数,类似于 COUNT(*)
。
sum
返回由提供的属性名称计算出的 SUM(field)
值,这些值也可以是复合的,例如 SUM(property1 + property2)
。
distinct
返回特定列的不同值的 NSArray,使用方式类似于 distinct("surname")
。
groupBy
返回一个NSDictionary,按指定的属性分组,例如 groupBy("surname")
。
ids
返回匹配对象的PK值,这是在子查询中使用结果的更快速方式。
Joins
连接是SQL最具高效能的功能,因为任何关系数据库管理系统(RDBMS)优化不通过子查询实现,而是通过连接和空值检查实现。
在Shark中,目前所有连接都是 LEFT JOIN
。这是因为我们必须从原始查询类检索整个对象。但连接可以是多级和复合的。
[Person] -> [Department]
连接的示例 Swift
Person.query()
.joinTo(Department, leftParameter: "department", targetParameter: "Id")
但你也可以使用第一连接的结果来创建一个 [Person]->[Department]->[Location]
三路连接,使用第一连接的结果执行第二个连接。Swift
Person.query()
.joinTo(Department, leftParameter: "department", targetParameter: "Id")
.joinTo(Location, leftParameter: "Department.location", targetParameter: "Id")
完成你的连接后,结果存储在每个对象中,在字典 joinedResults
中。
输出示例如下。
{
"Department.Id" = 61;
"Department.location" = 35;
"Department.name" = Development;
"Location.Id" = 35;
"Location.locationName" = Alton;
}
删除对象
要从Shark中删除对象,只需在此对象上调用 remove()
,这将将其从数据存储中删除并消毒以确保不会在将来的某个时候意外地写回它。为了优化对象的批量删除,可以将查询与对结果集的 removeAll()
调用相结合以一次性删除多个对象。
Swift
Person.query()
.whereWithFormat("age < %@", withParameters: [18])
.fetch()
.remove()
其扩展版本如下
Swift
for person in Person.query().fetch() {
person.remove()
}
事件处理
Shark事件分为两类,第一类是针对单个对象的事件,第二类是针对类的事件。
当类中存储的值发生任何潜在变化时,会引发类事件。这在数据在后台线程上写入或事件触发器执行时更新视图非常有用。
注册事件块只需要您通过在类上调用创建方法来创建一个新的SRKEventHandler
对象。Swift
let eHandler = Person.eventHandler()
eHandler.registerBlockForEvents(SharkORMEventInsert, withBlock: { (event: SRKEvent!) in
// update the tableview here
}, onMainThread: true)
对于对象事件处理器,所有单个对象都有通过向registerBlockForEvents
发送相同调用对其进行注册的能力。这将使对象自动处于活动状态,并观察数据存储中相应对象的所有更改,这些更改将跨任何线程发生。
SharkSync.io
SharkSync.io是SharkORM团队的无代码数据同步平台。该服务的背后设计原则是要求开发者尽可能少的努力,同时简化在线/离线体验的实现,并最大限度地减少数据冲突和冲突解决。它提供了一个灵活的安全/可见性模型,允许记录基础的跨所有数据表的分区。这使得指定哪些客户端有权访问哪些数据变得简单而独特。当正确使用时,它还将应用程序的结构简化得多,同时有效地利用分组。
所有数据都在设备上加密和解密,我们任何时候都无法访问数据。开发者可以使用我们标准化的AES256实现并指定自己的密钥。或者他们可以覆盖加密函数并使用他们选择的任何内容。
要开始使用,请在中创建账户并获取初始的免费积分块(1MM令牌以供您开始使用)。这不是一个以盈利为目的的服务,令牌成本和支出与使用AWS的高可扩展性基础设施提供服务的成本相匹配。
该服务也是开源的,所以您欢迎自己托管。但是,由于成本相同,这成为了那时的政治选择。此外,本着完全公平的精神,您可以从我们的服务中下载所有数据并将其导入自托管的设置,如果您愿意,只需将应用程序指向不同的端点即可。
它是如何工作的?
要与其他应用程序同步数据,只需让共享对象从SRKSyncObject
继承而不是从SRKObject
继承。然后它将记录对您对象的所有更改并将它们推送到该服务。
所有对象都属于一个可见性组,只有订阅这些组的设备才能看到这些对象。
需求
- CocoaPods 1.0.0