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