15、Realm数据库的数据迁移与功能扩展

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 数据迁移操作步骤
  1. 更改Realm.Configuration的schemaVersion属性,发布对象模式的新版本。
  2. Realm会自动执行实际的结构迁移。
  3. 若启动时迁移不适合所需的数据库更改,可以设计自定义流程。
5.2 硬级联删除操作步骤
  1. 定义CascadeDeleting协议,包含hardCascadeDeleteProperties方法。
  2. 让需要进行级联删除的对象采用该协议,并实现hardCascadeDeleteProperties方法。
  3. 在Realm类中添加cascadeDelete方法,实现级联删除逻辑。
  4. 让关联对象也采用CascadeDeleting协议,确保级联删除的完整性。
5.3 软级联删除操作步骤
  1. 为CascadeDeleting协议添加softCascadeDeleteProperties方法。
  2. 修改需要进行软级联删除的对象的实现,返回需要检查的属性。
  3. 在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数据库添加强大的自定义功能,满足应用的特定需求。在实际开发中,建议开发者根据具体情况选择合适的功能,并添加适当的索引以提高性能。

下载前可以先看下教程 https://pan.quark.cn/s/a4b39357ea24 在网页构建过程中,表单(Form)扮演着用户网站之间沟通的关键角色,其主要功能在于汇集用户的各类输入信息。 JavaScript作为网页开发的核心技术,提供了多样化的API和函数来操作表单组件,诸如input和select等元素。 本专题将详细研究如何借助原生JavaScript对form表单进行视觉优化,并对input输入框select下拉框进行功能增强。 一、表单基础1. 表单组件:在HTML语言中,<form>标签用于构建一个表单,该标签内部可以容纳多种表单组件,包括<input>(输入框)、<select>(下拉框)、<textarea>(多行文本输入区域)等。 2. 表单参数:诸如action(表单提交的地址)、method(表单提交的协议,为GET或POST)等属性,它们决定了表单的行为特性。 3. 表单行为:诸如onsubmit(表单提交时触发的动作)、onchange(表单元素值变更时触发的动作)等事件,能够通过JavaScript进行响应式处理。 二、input元素视觉优化1. CSS定制:通过设定input元素的CSS属性,例如border(边框)、background-color(背景色)、padding(内边距)、font-size(字体大小)等,能够调整其视觉表现。 2. placeholder特性:提供预填的提示文字,以帮助用户明确输入框的预期用途。 3. 图标集成:借助:before和:after伪元素或者额外的HTML组件结合CSS定位技术,可以在输入框中嵌入图标,从而增强视觉吸引力。 三、select下拉框视觉优化1. 复选功能:通过设置multiple属性...
【EI复现】基于深度强化学习的微能源网能量管理优化策略研究(Python代码实现)内容概要:本文围绕“基于深度强化学习的微能源网能量管理优化策略”展开研究,重点探讨了如何利用深度强化学习技术对微能源系统进行高效的能量管理优化调度。文中结合Python代码实现,复现了EI级别研究成果,涵盖了微电网中分布式能源、储能系统及负荷的协调优化问题,通过构建合理的奖励函数状态空间模型,实现对复杂能源系统的智能决策支持。研究体现了深度强化学习在应对不确定性可再生能源出力、负荷波动等挑战中的优势,提升了系统运行的经济性稳定性。; 适合人群:具备一定Python编程基础和机器学习背景,从事能源系统优化、智能电网、强化学习应用等相关领域的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于微能源网的能量调度优化控制,提升系统能效经济效益;②为深度强化学习在能源管理领域的落地提供可复现的技术路径代码参考;③服务于学术研究论文复现,特别是EI/SCI级别高水平论文的仿真实验部分。; 阅读建议:建议读者结合提供的Python代码进行实践操作,深入理解深度强化学习算法在能源系统建模中的具体应用,重点关注状态设计、动作空间定义奖励函数构造等关键环节,并可进一步扩展至多智能体强化学习或其他优化算法的融合研究。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值