如何在 Swift 中使用 GRDB 创建多对多关联?
How to create many-to-many association using GRDB in Swift?
我正在尝试建立歌曲和专辑之间的关联。每首歌可以出现在一个或多个专辑中,每个专辑可以包含一首或多首歌曲。我决定使用 GRDB 作为我的数据库解决方案,但我被困在这个问题上。
我尝试了什么:
如文档所示,我创建了一个 passport
结构,如下所示:
public struct AlbumPassport: TableRecord {
static let track = belongsTo(SPTTrack.self)
static let album = belongsTo(SPTAlbum.self)
}
然后在 SPTTrack class:
public static let passports = hasMany(AlbumPassport.self)
public static let albums = hasMany(SPTAlbum.self, through: passports, using: AlbumPassport.album)
并且在 SPTAlbum class:
public static let passports = hasMany(AlbumPassport.self)
public static let tracks = hasMany(SPTTrack.self, through: passports, using: AlbumPassport.track)
我在 documentation 中找不到有关如何使用这些关联构建请求的好示例。在 SPTAlbum class 我添加了 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
结构创建 table ,如果有任何额外的步骤我应该采取实际填充这个table 有 album/track 个连接。
两个 SPTTrack/SPTAlbum 都有一个名为 id
的字段,在第一次迁移期间设置为 primaryKey
。
您的关联没有问题。所有 hasMany()
和 belongsTo()
都是正确的。你得到的错误告诉我你的数据库设置有问题(你没有包括在你的问题中)。
以下是我的实现方式:
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 answer of Many-to-many relationship where one entity has multiple properties of the same type。
我正在尝试建立歌曲和专辑之间的关联。每首歌可以出现在一个或多个专辑中,每个专辑可以包含一首或多首歌曲。我决定使用 GRDB 作为我的数据库解决方案,但我被困在这个问题上。
我尝试了什么:
如文档所示,我创建了一个 passport
结构,如下所示:
public struct AlbumPassport: TableRecord {
static let track = belongsTo(SPTTrack.self)
static let album = belongsTo(SPTAlbum.self)
}
然后在 SPTTrack class:
public static let passports = hasMany(AlbumPassport.self)
public static let albums = hasMany(SPTAlbum.self, through: passports, using: AlbumPassport.album)
并且在 SPTAlbum class:
public static let passports = hasMany(AlbumPassport.self)
public static let tracks = hasMany(SPTTrack.self, through: passports, using: AlbumPassport.track)
我在 documentation 中找不到有关如何使用这些关联构建请求的好示例。在 SPTAlbum class 我添加了 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
结构创建 table ,如果有任何额外的步骤我应该采取实际填充这个table 有 album/track 个连接。
两个 SPTTrack/SPTAlbum 都有一个名为 id
的字段,在第一次迁移期间设置为 primaryKey
。
您的关联没有问题。所有 hasMany()
和 belongsTo()
都是正确的。你得到的错误告诉我你的数据库设置有问题(你没有包括在你的问题中)。
以下是我的实现方式:
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 answer of Many-to-many relationship where one entity has multiple properties of the same type。