Xác định và truy vấn mối quan hệ nhiều với nhiều

Mối quan hệ nhiều với nhiều giữa hai thực thể là mối quan hệ trong đó mỗi bản sao của thực thể mẹ tương ứng với không hoặc nhiều bản sao của thực thể con và ngược lại.

Trong ví dụ về ứng dụng phát nhạc trực tuyến, hãy xem xét các bài hát trong danh sách phát do người dùng lập ra. Mỗi danh sách phát có thể bao gồm nhiều bài hát và mỗi bài hát có thể thuộc nhiều danh sách phát. Do đó, có mối quan hệ nhiều với nhiều giữa thực thể PlaylistSong.

Hãy làm theo các bước sau để xác định và truy vấn mối quan hệ nhiều với nhiều trong cơ sở dữ liệu của bạn:

  1. Xác định mối quan hệ: Thiết lập các thực thể và thực thể liên kết hoặc bảng tham chiếu chéo để biểu thị mối quan hệ nhiều với nhiều.
  2. Truy vấn các thực thể: Xác định cách bạn muốn truy vấn các thực thể có liên quan và tạo các lớp dữ liệu để biểu thị đầu ra dự kiến.

Xác định mối quan hệ

Để xác định mối quan hệ nhiều với nhiều, trước tiên, hãy tạo một lớp cho mỗi thực thể. Mối quan hệ nhiều với nhiều khác hẳn với các loại quan hệ khác bởi vì thường không có tham chiếu đến thực thể mẹ trong thực thể con. Thay vào đó, hãy tạo một lớp thứ ba để đại diện cho một thực thể liên kết hoặc bảng tham chiếu chéo giữa hai thực thể. Bảng tham chiếu chéo phải có các cột cho khoá chính từ mỗi thực thể trong mối quan hệ nhiều với nhiều được thể hiện trong bảng. Trong ví dụ này, mỗi hàng trong bảng tham chiếu chéo tương ứng với một cặp gồm bản sao Playlist và bản sao Song trong đó danh sách phát được tham chiếu sẽ bao gồm bài hát được tham chiếu.

@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
)

Truy vấn các thực thể

Bước tiếp theo phụ thuộc vào cách bạn muốn truy vấn các thực thể liên quan này.

  • Nếu bạn muốn truy vấn danh sách phát và danh sách bài hát tương ứng với từng danh sách phát, hãy tạo một lớp dữ liệu mới chứa một đối tượng Playlist và danh sách các đối tượng Song mà danh sách phát chứa.
  • Nếu bạn muốn truy vấn bài hát và danh sách danh sách phát tương ứng với từng bài hát, hãy tạo một lớp dữ liệu mới chứa một đối tượng Song duy nhất và danh sách các đối tượng Playlist chứa bài hát.

Trong cả hai trường hợp, hãy mô hình hoá mối quan hệ giữa các thực thể bằng cách sử dụng thuộc tính associateBy trong chú thích @Relation của từng lớp này để xác định thực thể tham chiếu chéo cung cấp mối quan hệ giữa thực thể Playlist và thực thể 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>
)

Cuối cùng, hãy thêm một hàm vào lớp đối tượng truy cập dữ liệu (DAO) để hiển thị hàm truy vấn mà ứng dụng của bạn cần.

getPlaylistsWithSongs
Truy vấn cơ sở dữ liệu và trả về tất cả các đối tượng PlaylistWithSongs thu được.
getSongsWithPlaylists
Truy vấn cơ sở dữ liệu và trả về tất cả đối tượng SongWithPlaylists thu được.

Mỗi hàm đều yêu cầu Room chạy hai truy vấn. Thêm chú giải @Transaction vào cả hai hàm để đảm bảo thao tác chạy một cách tỷ mỷ.

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

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

Khoá kết hợp

Nếu bạn xác định mối quan hệ bằng khoá kết hợp, hãy chỉ định nhiều cột trong parentColumnsentityColumns của chú thích @Relation.

Nếu bạn cần chỉ định các cột trong Junction, hãy sử dụng parentColumnsentityColumns trong chú thích Junction.

Trong ví dụ sau, Playlist có một khoá chính kết hợp bao gồm playlistIdcreatorId. Bảng tham chiếu chéo PlaylistSongCrossRef cũng có các cột này để tham chiếu đến danh sách phát.

@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>
)