다대다 관계 정의 및 쿼리

두 항목 간의 다대다 관계는 상위 항목의 각 인스턴스가 0개 이상의 하위 항목 인스턴스에 상응하며, 그 반대의 경우도 마찬가지입니다.

음악 스트리밍 앱의 예에서 사용자가 정한 재생목록의 노래를 생각해 보세요. 각 재생목록에는 노래가 여러 곡 포함될 수 있으며 각 노래는 여러 다양한 재생목록에 속할 수 있습니다. 따라서 Playlist 항목과 Song 항목은 다대다 관계입니다.

데이터베이스에서 다대다 관계를 정의하고 쿼리하려면 다음 단계를 따르세요.

  1. 관계 정의: 다대다 관계를 나타내는 항목과 연결 항목 (교차 참조 테이블)을 설정합니다.
  2. 항목 쿼리: 관련 항목을 쿼리하는 방법을 결정하고 의도한 출력을 나타내는 데이터 클래스를 만듭니다.

관계 정의

다대다 관계를 정의하려면 먼저 두 항목 각각의 클래스를 만듭니다. 다대다 관계는 일반적으로 하위 항목에 상위 항목에 대한 참조가 없기 때문에 다른 관계 유형과 구별됩니다. 대신 세 번째 클래스를 만들어 두 항목 간의 연결 항목(또는 상호 참조 테이블)을 나타냅니다. 상호 참조 테이블에는 테이블에 표시된 다대다 관계에 있는 각 항목의 기본 키 열이 있어야 합니다. 이 예에서 상호 참조 테이블의 각 행은 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();