Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang
Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
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:
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.
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.
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.
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")fungetPlaylistsWithSongs():List<PlaylistWithSongs>@Transaction@Query("SELECT * FROM Song")fungetSongsWithPlaylists():List<SongWithPlaylists>
Java
@Transaction@Query("SELECT * FROM Playlist")publicList<PlaylistWithSongs>getPlaylistsWithSongs();@Transaction@Query("SELECT * FROM Song")publicList<SongWithPlaylists>getSongsWithPlaylists();
Nội dung và mã mẫu trên trang này phải tuân thủ các giấy phép như mô tả trong phần Giấy phép nội dung. Java và OpenJDK là nhãn hiệu hoặc nhãn hiệu đã đăng ký của Oracle và/hoặc đơn vị liên kết của Oracle.
Cập nhật lần gần đây nhất: 2025-07-27 UTC.
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 2025-07-27 UTC."],[],[],null,["# Define and query many-to-many relationships\n\nA *many-to-many relationship* between two entities is a relationship where each\ninstance of the parent entity corresponds to zero or more instances of the child\nentity, and the reverse is also true.\n\nIn the music streaming app example, consider the songs in the user-defined\nplaylists. Each playlist can include many songs, and each song can be a part of\nmany different playlists. Therefore, there is a many-to-many relationship\nbetween the `Playlist` entity and the `Song` entity.\n\nFollow these steps to define and query many-to-many relationships in your\ndatabase:\n\n1. **[Define the relationship](#define)**: Establish the entities and the associative entity (cross-reference table) to represent the many-to-many relationship.\n2. **[Query the entities](#query)**: Determine how you want to query the related entities and create data classes to represent the intended output.\n\nDefine the relationship\n-----------------------\n\nTo define a many-to-many relationship, first create a class for each of your two\nentities. Many-to-many relationships are distinct from other relationship types\nbecause there is generally no reference to the parent entity in the child\nentity. Instead, create a third class to represent an [associative\nentity](https://en.wikipedia.org/wiki/Associative_entity), or *cross-reference table* , between the two entities.\nThe cross-reference table must have columns for the primary key from each entity\nin the many-to-many relationship represented in the table. In this example, each\nrow in the cross-reference table corresponds to a pairing of a `Playlist`\ninstance and a `Song` instance where the referenced song is included in the\nreferenced playlist. \n\n### Kotlin\n\n @Entity\n data class Playlist(\n @PrimaryKey val playlistId: Long,\n val playlistName: String\n )\n\n @Entity\n data class Song(\n @PrimaryKey val songId: Long,\n val songName: String,\n val artist: String\n )\n\n @Entity(primaryKeys = [\"playlistId\", \"songId\"])\n data class PlaylistSongCrossRef(\n val playlistId: Long,\n val songId: Long\n )\n\n### Java\n\n @Entity\n public class Playlist {\n @PrimaryKey public long playlistId;\n public String playlistName;\n }\n\n @Entity\n public class Song {\n @PrimaryKey public long songId;\n public String songName;\n public String artist;\n }\n\n @Entity(primaryKeys = {\"playlistId\", \"songId\"})\n public class PlaylistSongCrossRef {\n public long playlistId;\n public long songId;\n }\n\nQuery the entities\n------------------\n\nThe next step depends on how you want to query these related entities.\n\n- If you want to query *playlists* and a list of the corresponding *songs* for each playlist, create a new data class that contains a single `Playlist` object and a list of all of the `Song` objects that the playlist includes.\n- If you want to query *songs* and a list of the corresponding *playlists* for each, create a new data class that contains a single `Song` object and a list of all of the `Playlist` objects in which the song is included.\n\nIn either case, model the relationship between the entities by using the\n[`associateBy`](/reference/kotlin/androidx/room/Relation#associateBy()) property in the [`@Relation`](/reference/kotlin/androidx/room/Relation) annotation in each of these\nclasses to identify the cross-reference entity providing the relationship\nbetween the `Playlist` entity and the `Song` entity. \n\n### Kotlin\n\n data class PlaylistWithSongs(\n @Embedded val playlist: Playlist,\n @Relation(\n parentColumn = \"playlistId\",\n entityColumn = \"songId\",\n associateBy = Junction(PlaylistSongCrossRef::class)\n )\n val songs: List\u003cSong\u003e\n )\n\n data class SongWithPlaylists(\n @Embedded val song: Song,\n @Relation(\n parentColumn = \"songId\",\n entityColumn = \"playlistId\",\n associateBy = Junction(PlaylistSongCrossRef::class)\n )\n val playlists: List\u003cPlaylist\u003e\n )\n\n### Java\n\n public class PlaylistWithSongs {\n @Embedded public Playlist playlist;\n @Relation(\n parentColumn = \"playlistId\",\n entityColumn = \"songId\",\n associateBy = @Junction(PlaylistSongCrossref.class)\n )\n public List\u003cSong\u003e songs;\n }\n\n public class SongWithPlaylists {\n @Embedded public Song song;\n @Relation(\n parentColumn = \"songId\",\n entityColumn = \"playlistId\",\n associateBy = @Junction(PlaylistSongCrossref.class)\n )\n public List\u003cPlaylist\u003e playlists;\n }\n\nFinally, add a method to the DAO class to expose the query function your\napp needs.\n\n- `getPlaylistsWithSongs`: this method queries the database and returns all the resulting `PlaylistWithSongs` objects.\n- `getSongsWithPlaylists`: this method queries the database and returns all the resulting `SongWithPlaylists` objects.\n\nThese methods each require Room to run two queries, so add the\n[`@Transaction`](/reference/kotlin/androidx/room/Transaction) annotation to both methods so that the whole\noperation is performed atomically. \n\n### Kotlin\n\n @Transaction\n @Query(\"SELECT * FROM Playlist\")\n fun getPlaylistsWithSongs(): List\u003cPlaylistWithSongs\u003e\n\n @Transaction\n @Query(\"SELECT * FROM Song\")\n fun getSongsWithPlaylists(): List\u003cSongWithPlaylists\u003e\n\n### Java\n\n @Transaction\n @Query(\"SELECT * FROM Playlist\")\n public List\u003cPlaylistWithSongs\u003e getPlaylistsWithSongs();\n\n @Transaction\n @Query(\"SELECT * FROM Song\")\n public List\u003cSongWithPlaylists\u003e getSongsWithPlaylists();\n\n| **Note:** If the `@Relation` annotation does not meet your specific use case, you might need to use the `JOIN` keyword in your SQL queries to manually define the appropriate relationships. To learn more about querying multiple tables manually, read [Accessing data using Room\n| DAOs](/training/data-storage/room/accessing-data#query-multiple-tables)."]]