Realm数据库的数据迁移与功能扩展
1. Realm数据迁移总结
当再次启动应用时,如果Navigator在Realm文件中未找到任何迁移任务,整个迁移工作流将不会执行。设计数据迁移具有一定挑战性,因为没有通用模式可循,其理念是响应式的,即根据应用新版本中对象模式的更改来设计迁移方案。
关键点总结
:
- 发布对象模式的新版本只需更改Realm.Configuration的schemaVersion属性,Realm会自动执行实际的结构迁移。
- 在迁移过程中,借助Migration.enumerateObjects API可以同时遍历持久化数据库对象的新旧版本。
- 若启动时迁移不适合所需的数据库更改,可以设计自定义流程。
挑战任务
:
为完成Exams应用的最终迁移,添加代码检查每个考试对象分配的自由文本结果,以及是否包含“pass”或“fail”等关键字。若包含,则自动分配预定义结果,避免手动交互式迁移。
2. 扩展Realm数据库功能
Realm数据库具有良好的可扩展性,可以为其添加自定义功能。下面将介绍如何为Realm添加级联删除功能。
2.1 级联删除功能概述
数据库服务器通常具备丰富的功能,但嵌入式数据库(如Realm)为了保证性能,需要削减一些功能。级联删除是Realm缺少的功能之一,它允许在删除根记录时,根据预定义规则删除相关记录,以避免出现孤立记录。
2.2 开始实现级联删除
首先,打开相关的起始Playground,其中定义了三个对象:Person、Car和InsurancePolicy,它们之间的关系如下:
- 一个Person可以拥有一辆Car和一份InsurancePolicy。
- 一辆Car可以有多个InsurancePolicy。
运行Playground,会看到创建的对象信息。若直接删除一个Person对象(如Jane),其关联的Car和InsurancePolicy对象仍会保留在Realm文件中,形成孤立记录。
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(Person):::process -->|拥有| B(Car):::process
A -->|拥有| C(InsurancePolicy):::process
B -->|关联| D(InsurancePolicy):::process
2.3 实现硬级联删除
- 定义协议 :在Playground顶部添加CascadeDeleting协议,用于询问Realm对象是否需要执行级联删除。
protocol CascadeDeleting {
func hardCascadeDeleteProperties() -> [String]
}
- 让Person对象采用协议 :
extension Person: CascadeDeleting {
func hardCascadeDeleteProperties() -> [String] {
return [Key.insurance.rawValue, Key.car.rawValue]
}
}
- 添加级联删除方法到Realm类 :
extension Realm {
func cascadeDelete(_ object: Object) {
guard let cascading = object as? CascadeDeleting else {
delete(object)
return
}
for property in cascading.hardCascadeDeleteProperties() {
if let linkedObject = object.value(forKey: property) as? Object {
cascadeDelete(linkedObject)
continue
}
if let linkedObjects = object.value(forKey: property) as? ListBase {
(0..<linkedObjects._rlmArray.count)
.compactMap {
linkedObjects._rlmArray.object(at: $0) as? Object
}
.forEach(cascadeDelete)
continue
}
}
delete(object)
}
}
- 让Car对象也采用协议 :
extension Car: CascadeDeleting {
func hardCascadeDeleteProperties() -> [String] {
return [Key.insurances.rawValue]
}
}
通过以上步骤,实现了硬级联删除,即删除一个对象时,会无条件删除其关联的所有对象。
2.4 实现软级联删除
在某些情况下,希望仅在没有其他对象引用时才删除关联对象,这时需要实现软级联删除。
-
修改协议
:为CascadeDeleting协议添加softCascadeDeleteProperties方法。
func softCascadeDeleteProperties() -> [String]
- 修改Person和Car对象的实现 :
extension Person {
func softCascadeDeleteProperties() -> [String] {
return [Key.car.rawValue]
}
func hardCascadeDeleteProperties() -> [String] {
return [Key.insurance.rawValue]
}
}
extension Car {
func softCascadeDeleteProperties() -> [String] {
return []
}
}
- 在cascadeDelete方法中添加软级联删除逻辑 :
for property in cascading.softCascadeDeleteProperties() {
guard let linkedObject = object.value(forKey: property) as? Object
else { continue }
let predicate = NSPredicate(format: "%K = %@",
property, linkedObject)
guard realm.objects(type(of: object))
.filter(predicate)
.count <= 1 else { continue }
cascadeDelete(linkedObject)
}
通过以上步骤,实现了软级联删除,即只有在没有其他对象引用关联对象时才进行删除。
3. 总结与挑战
硬级联删除和软级联删除功能增强了Realm数据库的实用性,且实现代码并不复杂。在使用这些功能时,建议添加适当的索引以提高搜索谓词的性能。
挑战任务
:
添加一个名为ConditionallyDeleted的新协议,包含一个返回Bool的方法。在现有的cascadeDelete(_)方法中,将delete(object)语句包裹在一个条件中,检查要删除的对象是否遵循ConditionallyDeleted协议,只有当协议方法返回true时才删除该对象。
通过以上操作,可以为Realm数据库添加强大的自定义功能,满足应用的特定需求。
Realm数据库的数据迁移与功能扩展
4. 功能总结与优势分析
通过对Realm数据库的数据迁移和功能扩展的学习,我们可以总结出以下几个重要方面:
| 功能 | 描述 | 优势 |
|---|---|---|
| 数据迁移 | 根据应用新版本中对象模式的更改,自动或手动执行数据库迁移 | 保证数据在版本更新时的兼容性,避免数据丢失或错误 |
| 硬级联删除 | 在删除对象时,无条件删除其关联的所有对象 | 确保数据的一致性,避免孤立记录的产生 |
| 软级联删除 | 仅在没有其他对象引用时,才删除关联对象 | 保留有用的数据关系,避免误删 |
这些功能的实现,不仅增强了Realm数据库的实用性,还为开发者提供了更多的灵活性和控制力。
5. 操作步骤回顾
为了更好地理解和应用这些功能,下面我们对操作步骤进行回顾:
5.1 数据迁移操作步骤
- 更改Realm.Configuration的schemaVersion属性,发布对象模式的新版本。
- Realm会自动执行实际的结构迁移。
- 若启动时迁移不适合所需的数据库更改,可以设计自定义流程。
5.2 硬级联删除操作步骤
- 定义CascadeDeleting协议,包含hardCascadeDeleteProperties方法。
- 让需要进行级联删除的对象采用该协议,并实现hardCascadeDeleteProperties方法。
- 在Realm类中添加cascadeDelete方法,实现级联删除逻辑。
- 让关联对象也采用CascadeDeleting协议,确保级联删除的完整性。
5.3 软级联删除操作步骤
- 为CascadeDeleting协议添加softCascadeDeleteProperties方法。
- 修改需要进行软级联删除的对象的实现,返回需要检查的属性。
- 在cascadeDelete方法中添加软级联删除逻辑,根据引用情况决定是否删除关联对象。
6. 流程图展示
下面是硬级联删除和软级联删除的流程图,帮助大家更直观地理解其执行过程。
graph TD
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(开始删除对象):::process --> B{对象是否遵循CascadeDeleting协议}:::process
B -- 是 --> C(获取硬级联删除属性):::process
B -- 否 --> D(直接删除对象):::process
C --> E{属性关联对象是否为单个对象}:::process
E -- 是 --> F(递归调用cascadeDelete删除关联对象):::process
E -- 否 --> G{属性关联对象是否为列表}:::process
G -- 是 --> H(遍历列表,递归调用cascadeDelete删除每个对象):::process
G -- 否 --> I(跳过该属性):::process
F --> J(继续处理下一个属性):::process
H --> J
I --> J
J --> K{是否还有属性未处理}:::process
K -- 是 --> C
K -- 否 --> L(删除当前对象):::process
graph TD
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
M(执行硬级联删除后):::process --> N(获取软级联删除属性):::process
N --> O{属性关联对象是否存在}:::process
O -- 是 --> P(构建谓词检查引用情况):::process
O -- 否 --> Q(跳过该属性):::process
P --> R{是否只有当前对象引用该关联对象}:::process
R -- 是 --> S(递归调用cascadeDelete删除关联对象):::process
R -- 否 --> Q
S --> T(继续处理下一个属性):::process
Q --> T
T --> U{是否还有属性未处理}:::process
U -- 是 --> N
U -- 否 --> V(结束):::process
7. 挑战实现示例
下面是实现ConditionallyDeleted协议和修改cascadeDelete方法的示例代码:
// 定义ConditionallyDeleted协议
protocol ConditionallyDeleted {
func shouldBeDeleted() -> Bool
}
// 修改cascadeDelete方法
extension Realm {
func cascadeDelete(_ object: Object) {
guard let cascading = object as? CascadeDeleting else {
if let conditionallyDeleted = object as? ConditionallyDeleted {
if conditionallyDeleted.shouldBeDeleted() {
delete(object)
}
} else {
delete(object)
}
return
}
for property in cascading.hardCascadeDeleteProperties() {
if let linkedObject = object.value(forKey: property) as? Object {
cascadeDelete(linkedObject)
continue
}
if let linkedObjects = object.value(forKey: property) as? ListBase {
(0..<linkedObjects._rlmArray.count)
.compactMap {
linkedObjects._rlmArray.object(at: $0) as? Object
}
.forEach(cascadeDelete)
continue
}
}
for property in cascading.softCascadeDeleteProperties() {
guard let linkedObject = object.value(forKey: property) as? Object
else { continue }
let predicate = NSPredicate(format: "%K = %@",
property, linkedObject)
guard realm.objects(type(of: object))
.filter(predicate)
.count <= 1 else { continue }
cascadeDelete(linkedObject)
}
if let conditionallyDeleted = object as? ConditionallyDeleted {
if conditionallyDeleted.shouldBeDeleted() {
delete(object)
}
} else {
delete(object)
}
}
}
8. 总结
通过对Realm数据库的数据迁移和功能扩展的学习,我们掌握了如何根据应用需求对数据库进行灵活的操作。数据迁移保证了数据在版本更新时的兼容性,而级联删除功能则增强了数据的一致性和完整性。同时,通过自定义协议和方法,我们可以为Realm数据库添加强大的自定义功能,满足应用的特定需求。在实际开发中,建议开发者根据具体情况选择合适的功能,并添加适当的索引以提高性能。
超级会员免费看
1123

被折叠的 条评论
为什么被折叠?



