Slick 3 多对多关系:如何获取表的所有元素及其关系(如果存在)?

Posted

技术标签:

【中文标题】Slick 3 多对多关系:如何获取表的所有元素及其关系(如果存在)?【英文标题】:Slick 3 many to many relations: how to get all the element of a table and their relations if they exist? 【发布时间】:2015-10-23 15:28:31 【问题描述】:

我正在使用 Slick 3 and Play! 2.4 我有一个非常常见的问题,我无法解决。

我有一个表 playlist 可以在关系表 playlistsTracks 的帮助下链接到一些轨道。我希望能够获得所有播放列表及其曲目关系和曲目。我的问题是,如果播放列表没有任何关系,我将无法获取它们。

这里有 3 个表格:

class Playlists(tag: Tag) extends Table[Playlist](tag, "playlists") 
    def id = column[Long]("playlistid", O.PrimaryKey, O.AutoInc)
    def name = column[String]("name")

    def * = (id.?, name) <> ((Playlist.apply _).tupled, Playlist.unapply)
  

class PlaylistsTracks(tag: Tag) extends Table[PlaylistTrack](tag, "playliststracks") 
    def playlistId = column[Long]("playlistid")
    def trackId = column[UUID]("trackid")
    def trackRank = column[Double]("trackrank")

    def * = (playlistId, trackId, trackRank) <> ((PlaylistTrack.apply _).tupled, PlaylistTrack.unapply)

    def aFK = foreignKey("playlistId", playlistId, playlists)(_.id, onDelete = ForeignKeyAction.Cascade)
    def bFK = foreignKey("trackId", trackId, tracks)(_.uuid, onDelete = ForeignKeyAction.Cascade)
  

class Tracks(tag: Tag) extends Table[Track](tag, "tracks") 
    def uuid = column[UUID]("trackid", O.PrimaryKey)
    def title = column[String]("title")

    def * = (uuid, title) <> ((Track.apply _).tupled, Track.unapply)
  

目前获取播放列表的代码的 sn-p 如下所示:

val query = for 
    playlist <- playlists
    playlistTrack <- playlistsTracks if playlistTrack.playlistId === playlist.id
    track <- tracks if playlistTrack.trackId === track.uuid
 yield (playlist, playlistTrack, track)

db.run(query.result) map  println 

它打印类似Vector(Playlist, PlaylistTrack, Track)(我想要的)的东西,但我只得到有关系的播放列表,而不是得到所有的播放列表,即使是我想要的没有关系的播放列表。

我尝试了很多方法,例如使用 join(或 joinFull、joinLeft、joinRight...),但都没有成功,不幸的是,很难找到一些不仅关系非常简单的示例项目。

【问题讨论】:

如果你想要所有的播放列表,那么当你有一个播放列表值但没有 playlistTrack 或轨道时应该“产生”什么?换句话说,你想要 (playlist1, None, None) 只是 playlist1...? Yes (playlist1, None, None) 会很好:然后我会将它映射到我的 PlaylistWithTracks 案例类中(看起来像这样:(playlist: Playlist, trackingWithRank: Seq[TrackWithRank])跨度> 我能想到的唯一解决方案是执行 3 个单独的查询并在应用程序中执行“加入”逻辑(而不是服务器端)。这可以接受吗? 并非如此,因为据我所知,数据库引擎 (PostgreSQL) 不会使用三个单独的查询来优化查询。 嗯,我想不出一个 sql 查询会生成您正在寻找的结果类型。如果底层 sql 做不到,我不确定 slick 是否能做很多事情...... 【参考方案1】:

您需要在 Playlists 和 PlaylistTracks 表之间使用左连接,并在 PlaylistTracks 和 Tracks 之间使用内连接。 示例中缺少一些内容,因此我实际上无法编译以下内容,但我认为您可以尝试以下操作:

val query = for 
  (playlist, optionalPlaylistTrackAndTrack) <- playlists joinLeft (playlistsTracks join tracks on (_.trackId === _.uuid)) on (_.id === _._1.playlistId)
 yield (playlist, optionalPlaylistTrackAndTrack)

请注意,optionalPlaylistTrackAndTrack 是表示播放列表曲目和曲目的元组的选项。这是因为可能存在没有 playlistTrack 的播放列表。

【讨论】:

它似乎完全符合我的需要,并且编译时没有任何变化;)谢谢!

以上是关于Slick 3 多对多关系:如何获取表的所有元素及其关系(如果存在)?的主要内容,如果未能解决你的问题,请参考以下文章

Slick 3:如何丢弃和接收具有某些关系的集合

从父多对多关系获取所有子模型 Laravel Eloquent

如何获取没有多对多关系的实体

如何从雄辩关系中的第三个表中获取数据雄辩的多对多关系

如何从 laravel 中的中间表(多对多关系)中获取数据

跨多对多表的关系