You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Swift/GRDB:无需添加列,如何将Recipes存入独立表

解决方案:拆分JSON解析与GRDB模型,避免数据冗余

核心思路

将JSON解析用的结构体与GRDB数据库模型解耦:解析时完整读取Fruit及其recipes,然后分别将Fruit数据存入Fruit表、recipes数据(关联对应fruitID)存入独立的Recipe表,GRDB的Fruit模型不包含recipes字段,从根源避免冗余。

步骤1:修正JSON解析用的Codable结构

先修复原代码中重复定义Fruit类的问题,确保解析时能正确读取所有字段:

// 用于JSON解析的结构体,包含完整字段
public struct DataResponse: Codable {
    let fruits: [FruitWithRecipes]
    
    enum CodingKeys: String, CodingKey {
        case fruits
    }
}

public struct FruitWithRecipes: Codable {
    let fruitID: String
    let name: String
    let color: String
    let recipes: [RecipeRaw]
    
    enum CodingKeys: String, CodingKey {
        case fruitID, name, color, recipes
    }
}

public struct RecipeRaw: Codable {
    let title: String
    let description: String
    let videoTutorial: String
    
    enum CodingKeys: String, CodingKey {
        case title, description, videoTutorial
    }
}

步骤2:定义GRDB数据库模型

分别创建Fruit和Recipe的GRDB模型,其中Fruit模型不包含recipes字段,Recipe模型添加fruitID作为外键关联:

import GRDB

// GRDB的Fruit模型,仅包含需要存入表的字段
struct Fruit: Codable, FetchableRecord, MutablePersistableRecord {
    let fruitID: String
    let name: String
    let color: String
    
    enum CodingKeys: String, CodingKey {
        case fruitID, name, color
    }
    
    // 定义表名
    static let databaseTableName = "fruits"
    
    // 主键冲突策略
    static let persistenceConflictPolicy = PersistenceConflictPolicy(
        insert: .replace,
        update: .replace
    )
}

// GRDB的Recipe模型,包含外键fruitID
struct Recipe: Codable, FetchableRecord, MutablePersistableRecord {
    let id: Int64? // 可选自增主键
    let fruitID: String
    let title: String
    let description: String
    let videoTutorial: String
    
    enum CodingKeys: String, CodingKey {
        case id, fruitID, title, description, videoTutorial
    }
    
    static let databaseTableName = "recipes"
    
    // 定义与Fruit的关联关系
    static let fruit = belongsTo(Fruit.self)
    
    // 自增主键赋值
    mutating func didInsert(with rowID: Int64, for column: String?) {
        id = rowID
    }
}

步骤3:解析并拆分存储数据

解析JSON后,将FruitWithRecipes拆分为Fruit和Recipe数据,分别存入对应表:

func parseAndStore(jsonData: Data, db: Database) throws {
    // 解析JSON
    let decoder = JSONDecoder()
    let response = try decoder.decode(DataResponse.self, from: jsonData)
    
    // 事务内批量存储,保证数据一致性
    try db.inTransaction {
        for fruitWithRecipes in response.fruits {
            // 存储Fruit数据
            let fruit = Fruit(
                fruitID: fruitWithRecipes.fruitID,
                name: fruitWithRecipes.name,
                color: fruitWithRecipes.color
            )
            try fruit.save(db)
            
            // 存储关联的Recipe数据
            for recipeRaw in fruitWithRecipes.recipes {
                let recipe = Recipe(
                    id: nil,
                    fruitID: fruitWithRecipes.fruitID,
                    title: recipeRaw.title,
                    description: recipeRaw.description,
                    videoTutorial: recipeRaw.videoTutorial
                )
                try recipe.save(db)
            }
        }
        return .commit
    }
}

补充说明

  • 若需要关联查询(比如通过Fruit获取其recipes),可利用GRDB的关联能力:
    // 查询指定Fruit的所有recipes
    func getRecipes(for fruitID: String, db: Database) throws -> [Recipe] {
        try Recipe.filter(Column("fruitID") == fruitID).fetchAll(db)
    }
    
  • 可通过GRDB迁移工具创建匹配的表结构:
    let migrator = DatabaseMigrator()
    migrator.registerMigration("createFruitsAndRecipes") { db in
        try db.create(table: "fruits") { t in
            t.column("fruitID", .text).primaryKey()
            t.column("name", .text).notNull()
            t.column("color", .text).notNull()
        }
        
        try db.create(table: "recipes") { t in
            t.column("id", .integer).primaryKey().autoincrement()
            t.column("fruitID", .text).notNull().references("fruits", onDelete: .cascade)
            t.column("title", .text).notNull()
            t.column("description", .text).notNull()
            t.column("videoTutorial", .text).notNull()
        }
    }
    

内容的提问来源于stack exchange,提问作者mgrene

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 11:48:18