插入数据

温馨提示:本文最后更新于2025-01-19 16:45:46,某些文章具有时效性,若有错误或已失效,请在下方留言

在SwiftData 中插入数据非常简单,默认情况下数据会自动保存,无需手动调用保存操作。以下示例演示了如何创建一个 Student 模型并插入一条记录,数据将自动保存:

@Model class Student {
    var name: String
    init(name: String) {
        self.name = name
    }
}

struct ContentView: View {
    @Environment(\.modelContext) private var context
    var body: some View {
        Button(action: addStudent) {
            Label("添加学生", systemImage: "plus")
        }
    }
    private func addStudent() {
        let newItem = Student(name: "小王")
        context.insert(newItem)
    }
}

如果通过 ModelConfiguration 禁用了自动保存,则需要手动调用保存操作来插入数据,下面是示例代码:

private func addStudent() {
    let newItem = Student(name: "小王")
    context.insert(newItem)
    try? context.save()
}

多对多关系添加数据

在多对多关系模型中插入数据时,确保所有实体首先插入是非常重要的。以下示例展示了如何在修改数组之前插入实体:

let tchr = Teacher(name: "王老师", students: [])
let student1 = Student(name: "小明", teachers: [tchr])
let student2 = Student(name: "小爱", teachers: [tchr])

modelContext.insert(tchr)
modelContext.insert(student1)
modelContext.insert(student2)

tchr.students.append(student1)
tchr.students.append(student2)

或者,你也可以将 Teacher 添加到 Student 中:

student1.teachers.append(tchr)
student2.teachers.append(tchr)

支持撒消和重做

为 SwiftData 添加撤销和重做支持非常简单,实际上只需要两步:首先启用撤销功能,然后通过 SwiftUI 环境或模型上下文的撤销管理器调用撤销。首先,需要调整创建模型容器的方式,以便启用撤销功能。如果你使用的是 SwiftUl,可能需要将 modelContainer() 修饰符修改为以下代码:

var body: some Scene {
    WindowGroup {
        ContentView()
    }
    .modelContainer(
        for: [ School.self, Student.self ],
        isUndoEnabled: true
    )
}

配置好撤销管理器后,你可以调用 undo()redo()方法,SwiftData 会自动撤销最近的一组更改。例如,在使用SwiftUI 时,你可以通过以下方式获取环境中的撤销管理器:

@Environment(\.undoManager) var undoManager

然后触发撤消操作,如下所示:

undoManager?.undo()

如果你是手动创建模型容器,那么你也需要手动创建撤销管理器:

container = try ModelContainer(for: Comment.self, Recipe.self)
container.mainContext.undoManager = UndoManager()

你可以使用 undoManager?.undo() 来执行撤销操作。

调用 undo()redo() 会撤销或重做你最近进行的一组更改,但”一组更改”的定义比较模糊,使用时需谨慎:
1. 如果一次进行了多个更改,它们会被一起撤销。
2. 如果你进行了一次更改,调用了 save(),然后又进行了另一项更改,两个更改都会被撤销。

你也可以直接在任何模型上下文中调用撤销管理器,但如果视图中存在其他可撤销的操作,可能会引发问题。你还可以通过以下方式重做操作:

struct ContentView: View {
    @Environment(\.undoManager) var undoManager
    var body: some View {
        // ...
    }
    private func redo() {
        undoManager?.redo()
    }
}

撤销操作在处理插入对象和关系的删除时非常稳定,而重做操作则相对不太稳定,使用时请谨慎。

当撤销和重做功能正常运行时,你还可以通过三指滑动手势来操作:三指向左滑动撤销,向右滑动重做。此外,除了调用 undo()redo()之外,你还可以通过检查撤销管理器的 canUndocanRedo属性,判断这些操作是否可用。

不保存时回滚更改

当需要一次性对数据进行多项更改,并希望在出错时能够回滚这些更改时,ModelContexttransaction() 方法可能无法满足需求。正确的做法是在一个 Task 中创建一个新的模型上下文,进行所有更改后,只有在确认需要保存时才调用 save()。如果不调用 save(),模型上下文及其内容将自动丢弃,相当于回滚了所有更改。

例如,在批量插入用户数据时,如果发现某个用户的幸运数字是 13,可以选择不保存并退出任务,从而确保所有插入操作被取消。

Task {
    let newContext = ModelContext(modelContext.container)
    for i in 1...1_000 {
        let number = Int.random(in: 1...10_000)
        if number == 13 {
            print("That's not a lucky number!")
            return
        }

        let user = User(name: "User \(i)", luckyNumber: number)
        newContext.insert(user)
    }

    try? newContext.save()
}

使用事务回滚

通过事务可以确保一系列操作要么全部成功,要么全部失败,从而保证数据的一致性和完整性。以下是一个示例,演示如何同时添加三条数据。在此示例中,假设进行条件检查,如果插入失败,则抛出错误,输出错误信息并回滚已插入的数据:

struct ContentView: View {
    @Environment(\.modelContext) private var context
    var body: some View {
        Button(action: addItem) {
            Label("Add Item", systemImage: "plus")
        }
    }
    private func addItem() {
        let newItems = [Student(name: "Hello1"), Student(name: "Hello2"), Student(name: "Hello3")]
        do {
          try context.transaction {
              for item in newItems {
                  context.insert(item)
                  if item.name == "Hello2" {
                      throw DataError.insertionFailed("insert error...")
                  }
              }
          }
        } catch DataError.insertionFailed(let message) {
            print("Error: \(message)")  // 输出: 插入失败
            context.rollback() // ❌ 发生错误回滚
        } catch {
            print("Error: \(error.localizedDescription)")
            context.rollback() // ❌ 发生错误回滚
        }
    }
}

enum DataError: Error {
    case insertionFailed(String)
}

允许开发者将多条读写操作打包在一起。若中途发生错误,事务可以回滚所有更改,恢复到操作前的状态。
通常用于批量插入、更新、删除等操作,以确保数据的安全性和一致性。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容