กําหนดและค้นหาความสัมพันธ์แบบหลายต่อหลาย

ความสัมพันธ์แบบหลายต่อหลายระหว่าง 2 เอนทิตีคือความสัมพันธ์ที่แต่ละอินสแตนซ์ของเอนทิตีหลักสอดคล้องกับอินสแตนซ์ 0 รายการหรือมากกว่าของเอนทิตีรอง และในทางกลับกันก็เป็นจริงด้วย

ในตัวอย่างแอปสตรีมมิงเพลง ให้พิจารณาเพลงในเพลย์ลิสต์ที่ผู้ใช้กำหนด เพลย์ลิสต์แต่ละรายการมีเพลงได้หลายเพลง และเพลงแต่ละเพลงอาจอยู่ในเพลย์ลิสต์ได้หลายรายการ ดังนั้น จึงมีความสัมพันธ์แบบหลายต่อหลายระหว่างเอนทิตี Playlist และ Song

ทำตามขั้นตอนต่อไปนี้เพื่อกำหนดและค้นหาความสัมพันธ์แบบหลายต่อหลายในฐานข้อมูล

  1. กำหนดความสัมพันธ์: สร้างเอนทิตีและ เอนทิตีที่เชื่อมโยง หรือตารางการอ้างอิงโยง เพื่อแสดงความสัมพันธ์แบบหลายต่อหลาย
  2. ค้นหาเอนทิตี: กำหนดวิธีที่ต้องการค้นหาเอนทิตีที่เกี่ยวข้อง และสร้างคลาสข้อมูลเพื่อแสดงเอาต์พุตที่ต้องการ

กำหนดความสัมพันธ์

หากต้องการกำหนดความสัมพันธ์แบบหลายต่อหลาย ให้สร้างคลาสสำหรับเอนทิตีทั้ง 2 รายการก่อน ความสัมพันธ์แบบหลายต่อหลายแตกต่างจากความสัมพันธ์ประเภทอื่นๆ เนื่องจากโดยทั่วไปแล้วจะไม่มีการอ้างอิงถึงเอนทิตีหลักในเอนทิตีรอง แต่ให้สร้างคลาสที่ 3 เพื่อแสดงเอนทิตีที่เชื่อมโยง หรือ ตารางการอ้างอิงโยง ระหว่าง 2 เอนทิตี ตารางการอ้างอิงโยงต้องมีคอลัมน์สำหรับคีย์หลักจากเอนทิตีแต่ละรายการในความสัมพันธ์แบบหลายต่อหลายที่แสดงในตาราง ในตัวอย่างนี้ แต่ละแถวในตารางการอ้างอิงโยงจะสอดคล้องกับการจับคู่ของอินสแตนซ์ Playlist และอินสแตนซ์ Song โดยเพลย์ลิสต์ที่อ้างอิงจะมีเพลงที่อ้างอิง

@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val playlistName: String
)

@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)

@Entity(primaryKeys = ["playlistId", "songId"], indices = [Index("playlistId", "songId")])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

ค้นหาเอนทิตี

ขั้นตอนถัดไปจะขึ้นอยู่กับวิธีที่ต้องการค้นหาเอนทิตีที่เกี่ยวข้องเหล่านี้

  • หากต้องการค้นหา เพลย์ลิสต์ และรายการ เพลง ที่เกี่ยวข้องสำหรับแต่ละเพลย์ลิสต์ ให้สร้างคลาสข้อมูลใหม่ที่มีออบเจ็กต์ Playlist รายการเดียวและรายการออบเจ็กต์ Song ที่เพลย์ลิสต์มี
  • หากต้องการค้นหา เพลง และรายการ เพลย์ลิสต์ ที่เกี่ยวข้องสำหรับแต่ละเพลง ให้สร้างคลาสข้อมูลใหม่ที่มีออบเจ็กต์ Song รายการเดียวและรายการออบเจ็กต์ Playlist ที่มีเพลง

ไม่ว่าจะกรณีใด ให้สร้างโมเดลความสัมพันธ์ระหว่างเอนทิตีโดยใช้พร็อพเพอร์ตี้ associateByในคำอธิบายประกอบ@Relationในคลาสเหล่านี้แต่ละคลาสเพื่อระบุเอนทิตีการอ้างอิงโยงที่แสดงความสัมพันธ์ระหว่างเอนทิตีPlaylistและเอนทิตีSong

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
        parentColumns = ["playlistId"],
        entityColumns = ["songId"],
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)

data class SongWithPlaylists(
    @Embedded val song: Song,
    @Relation(
        parentColumns = ["songId"],
        entityColumns = ["playlistId"],
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val playlists: List<Playlist>
)

สุดท้าย ให้เพิ่มฟังก์ชันลงในคลาสออบเจ็กต์การเข้าถึงข้อมูล (DAO) เพื่อแสดงฟังก์ชันการค้นหาที่แอปต้องการ

getPlaylistsWithSongs
ค้นหาฐานข้อมูลและแสดงผลออบเจ็กต์ PlaylistWithSongs ทั้งหมด
getSongsWithPlaylists
ค้นหาฐานข้อมูลและแสดงผลออบเจ็กต์ SongWithPlaylists ทั้งหมด

แต่ละฟังก์ชันต้องใช้ Room เพื่อเรียกใช้การค้นหา 2 รายการ เพิ่มคำอธิบายประกอบ @Transaction ลงในทั้ง 2 ฟังก์ชันเพื่อให้แน่ใจว่าการดำเนินการจะทำงานแบบอะตอมมิก

@Transaction
@Query("SELECT * FROM Playlist")
suspend fun getPlaylistsWithSongs(): List<PlaylistWithSongs>

@Transaction
@Query("SELECT * FROM Song")
suspend fun getSongsWithPlaylists(): List<SongWithPlaylists>

คีย์ผสม

หากกำหนดความสัมพันธ์โดยใช้คีย์ผสม ให้ระบุหลายคอลัมน์ใน parentColumns และ entityColumns ของคำอธิบายประกอบ @Relation

หากต้องการระบุคอลัมน์ใน Junction ให้ใช้ parentColumns และ entityColumns ในคำอธิบายประกอบ Junction ด้วย

ในตัวอย่างต่อไปนี้ Playlist มีคีย์หลักแบบผสมที่ประกอบด้วย playlistId และ creatorId ตารางการอ้างอิงโยง PlaylistSongCrossRef ยังมีคอลัมน์เหล่านี้เพื่ออ้างอิงเพลย์ลิสต์

@Entity(primaryKeys = ["playlistId", "creatorId"])
data class Playlist(
    val playlistId: Long,
    val creatorId: Long,
    val playlistName: String
)

@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)

@Entity(primaryKeys = ["playlistId", "creatorId", "songId"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val creatorId: Long,
    val songId: Long
)

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
        parentColumns = ["playlistId", "creatorId"],
        entityColumns = ["songId"],
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)