ios - 如何在 Swift 中使用 GRDB 创建多对多关联?

标签 ios swift sqlite grdb

我正在尝试在歌曲和专辑之间建立关联。每首歌曲可以出现在一张或多张专辑中,并且每张专辑可以包含一首或多首歌曲。我决定选择GRDB对于我的数据库解决方案,但我陷入了这个问题。

我尝试过的: 正如文档所示,我创建了一个 passport 结构,如下所示:

public struct AlbumPassport: TableRecord {
    static let track = belongsTo(SPTTrack.self)
    static let album = belongsTo(SPTAlbum.self)
}

然后在 SPTTrack 类中:

public static let passports = hasMany(AlbumPassport.self)
public static let albums = hasMany(SPTAlbum.self, through: passports, using: AlbumPassport.album)

在 SPTAlbum 类中:

public static let passports = hasMany(AlbumPassport.self)
public static let tracks = hasMany(SPTTrack.self, through: passports, using: AlbumPassport.track)

我在 documentation 中找不到关于如何使用这些关联构建请求的一个很好的例子。在 SPTAlbum 类中,我添加了 linkedTracks 属性

public var linkedTracks: QueryInterfaceRequest<SPTTrack> {
    request(for: Self.tracks)
}

然后在我的数据库管理器中:

func fetchTracks(for album: SPTAlbum) -> [SPTTrack] {
    do {
        return try dbQueue.read { db in
            try album.linkedTracks.fetchAll(db)
        }
    } catch {
        print(error)
    }
    return []
}

我收到错误:

SQLite error 1: no such table: albumPassport

这是不言自明的,但我不知道应该如何以及在哪里为 AlbumPassport 结构创建表,以及是否应该采取任何其他步骤来实际用专辑填充此表/跟踪连接。

SPTTrack/SPTAlbum 都有一个名为 id 的字段,该字段在首次迁移期间设置为 primaryKey

最佳答案

您的关联没有问题。所有 hasMany()belongsTo() 都是正确的。您收到的错误告诉我您的数据库设置有问题(您没有将其包含在问题中)。

这是我的实现方式:

Schema

import GRDB

struct Album: Codable, Hashable, FetchableRecord, MutablePersistableRecord {
    mutating func didInsert(with rowID: Int64, for column: String?) { id = rowID }
    var id: Int64?
    var name: String
    static let passports = hasMany(AlbumPassport.self)
    static let tracks = hasMany(Track.self, through: passports, using: AlbumPassport.track)
}

struct Track: Codable, Hashable, FetchableRecord, MutablePersistableRecord {
    mutating func didInsert(with rowID: Int64, for column: String?) { id = rowID }
    var id: Int64?
    var name: String
    static let passports = hasMany(AlbumPassport.self)
    static let albums = hasMany(Album.self, through: passports, using: AlbumPassport.album)
}

struct AlbumPassport: Codable, Hashable, FetchableRecord, PersistableRecord {
    let track: Int64
    let album: Int64
    static let track = belongsTo(Track.self)
    static let album = belongsTo(Album.self)
}

let queue = DatabaseQueue()
try queue.write { db in
    
    try db.create(table: "album") { t in
        t.autoIncrementedPrimaryKey("id")
        t.column("name", .text).notNull()
    }
    try db.create(table: "track") { t in
        t.autoIncrementedPrimaryKey("id")
        t.column("name", .text).notNull()
    }
    try db.create(table: "albumPassport") { t in
        t.column("track", .integer).notNull().indexed().references("track")
        t.column("album", .integer).notNull().indexed().references("album")
        t.primaryKey(["track", "album"])
    }
    
    // Testing real data from https://music.apple.com/de/artist/yiruma/73406786
    
    var solo = Album(name: "SOLO")
    try solo.insert(db)
    var sometimes = Track(name: "Sometimes Someone")
    try sometimes.insert(db)
    try AlbumPassport(track: sometimes.id!, album: solo.id!).insert(db)
    var destiny = Track(name: "Destiny Of Love")
    try destiny.insert(db)
    try AlbumPassport(track: destiny.id!, album: solo.id!).insert(db)
    
    var bestOf = Album(name: "Best of Yiroma")
    try bestOf.insert(db)
    var poem = Track(name: "Poem")
    try poem.insert(db)
    try AlbumPassport(track: poem.id!, album: bestOf.id!).insert(db)
    var river = Track(name: "River Flows In You")
    try river.insert(db)
    try AlbumPassport(track: river.id!, album: bestOf.id!).insert(db)
}

// Fetch all albums and their tracks
try queue.read { db in
    struct AlbumInfo: FetchableRecord, Decodable, CustomStringConvertible {
        var album: Album
        var tracks: Set<Track>
        var description: String { "\(album.name) → \(tracks.map(\.name))" }
    }
    let request = Album.including(all: Album.tracks)
    let result = try AlbumInfo.fetchAll(db, request)
    print(result)
    // > [SOLO → ["Sometimes Someone", "Destiny Of Love"], Best of Yiroma → ["River Flows In You", "Poem"]]
}

参见my answerMany-to-many relationship where one entity has multiple properties of the same type另一个代码示例。

关于ios - 如何在 Swift 中使用 GRDB 创建多对多关联?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69296355/

相关文章:

php - 使用 Urban Airship 和 php 自动向 ios/android 设备发送通知

ios - 无法使用 Xcode 在模拟器中运行应用程序

sqlite - sqlite查询问题

sqlite - 如何加密 SQLite 数据库

mysql - MySQL 日期时间格式是否适用于 SQLite 和 PostgreSQL?

ios - UIApplication 将在后台运行多长时间

ios - 在github上推送时Xcode不添加框架

iphone - UITableView 内容高度问题导致应用程序崩溃

swift - 尝试访问 TabBar 时,IOS swift fatal error nil 异常

Swift 3 使用 uialertcontroller 添加新行到 uitableviewcontroller