두 항목 간의 다대다 관계 는 상위 항목의 각 인스턴스가 0개 이상의 하위 항목 인스턴스에 상응하며, 그 반대의 경우도 마찬가지입니다.
음악 스트리밍 앱의 예에서 사용자가 정한 재생목록의 노래를 생각해 보세요. 각 재생목록에는 노래가 여러 곡 포함될 수 있으며 각 노래는 여러 재생목록에 속할 수 있습니다. 따라서 Playlist 항목과 Song 항목 간에는 다대다 관계가 성립됩니다.
다음 단계에 따라 데이터베이스에서 다대다 관계를 정의하고 쿼리합니다.
- **관계 정의**: 다대다 관계를 나타내기 위해 항목과 연결 항목 또는 교차 참조 테이블을 설정합니다.
- 항목 쿼리: 관련 항목을 쿼리하는 방법을 결정하고 의도한 출력을 나타내는 데이터 클래스를 만듭니다.
관계 정의
다대다 관계를 정의하려면 먼저 두 항목 각각의 클래스를 만듭니다. 다대다 관계는 일반적으로 하위 항목에 상위 항목에 대한 참조가 없기 때문에 다른 관계 유형과 구별됩니다. 대신 세 번째 클래스를 만들어 두
항목 간의
연결 항목(또는 상호 참조 테이블)을 나타냅니다. 교차 참조 테이블에는 테이블에 표시된 다대다 관계에 있는 각 항목의 기본 키 열이 있어야 합니다. 이 예에서 상호 참조 테이블의 각 행은 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객체 목록을 포함하는 새 데이터 클래스를 만듭니다.
어느 경우든 이러한 각
클래스의 @Relation 주석에서
associateBy 속성을 사용하여 항목 간의 관계를 모델링함으로써 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에서 두 개의 쿼리를 실행해야 합니다. 두 함수 모두에 @Transaction
주석을 추가하여 작업이 원자적으로 실행되도록 합니다.
@Transaction @Query("SELECT * FROM Playlist") suspend fun getPlaylistsWithSongs(): List<PlaylistWithSongs> @Transaction @Query("SELECT * FROM Song") suspend fun getSongsWithPlaylists(): List<SongWithPlaylists>
복합 키
복합 키를 사용하여 관계를 정의하는 경우 @Relation 주석의 parentColumns 및 entityColumns에 여러 열을 지정합니다.
Junction에서 열을 지정해야 하는 경우 Junction 주석의 parentColumns 및 entityColumns도 사용합니다.
다음 예에서 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> )