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 khác nhau. Do đó, có mối quan hệ nhiều với nhiều giữa thực thể Playlist và thực thể Song.

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:

  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 (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ể liên quan và tạo các lớp dữ liệu để thể hiện kết quả 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ộ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 đó bài hát được tham chiếu sẽ được đưa vào danh sách phát được tham chiếu đến.

Kotlin

@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"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

Java

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    public String playlistName;
}

@Entity
public class Song {
    @PrimaryKey public long songId;
    public String songName;
    public String artist;
}

@Entity(primaryKeys = {"playlistId", "songId"})
public class PlaylistSongCrossRef {
    public long playlistId;
    public long songId;
}

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 duy nhất và danh sách tất cả 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 các 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à một danh sách của tất cả các đối tượng Playlist mà 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.

Kotlin

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

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

Java

public class PlaylistWithSongs {
    @Embedded public Playlist playlist;
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Song> songs;
}

public class SongWithPlaylists {
    @Embedded public Song song;
    @Relation(
         parentColumn = "songId",
         entityColumn = "playlistId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Playlist> playlists;
}

Cuối cùng, hãy thêm một phương thức vào lớp DAO để hiển thị hàm truy vấn mà ứng dụng của bạn cần.

  • getPlaylistsWithSongs: phương thức này truy vấn cơ sở dữ liệu và trả về tất cả đối tượng PlaylistWithSongs thu được.
  • getSongsWithPlaylists: phương thức này truy vấn cơ sở dữ liệu và trả về tất cả đối tượng SongWithPlaylists thu được.

Mỗi phương thức này đòi hỏi Room phải chạy hai phương thức truy vấn. Vì vậy, hãy thêm chú thích @Transaction vào cả hai phương thức để đảm bảo thực hiện toàn bộ thao tác một cách tỉ mỉ.

Kotlin

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

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

Java

@Transaction
@Query("SELECT * FROM Playlist")
public List<PlaylistWithSongs> getPlaylistsWithSongs();

@Transaction
@Query("SELECT * FROM Song")
public List<SongWithPlaylists> getSongsWithPlaylists();