오늘도 더 나은 코드를 작성하였습니까?

객체 간 관계 정의 본문

Android Jetpack Architecture/Room

객체 간 관계 정의

hik14 2020. 8. 23. 19:18

SQLite는 관계형 데이터베이스이므로 entity 간 관계를 지정할 수 있습니다.

 

대부분의 객체 관계 매핑(ORM) 라이브러리에서는 entity객체가 서로를 참조할 수 있지만, Room은 이러한 상호 참조를 명시적으로 금지합니다.  즉, 어떤 Entity가 다른 Entity의 참조를 가지고 있으면 안 된다.

 

Room에서 객체 참조를 허용하지 않는 이유 이해 (앱에 필요한 데이터를 명시적으로 요청해야 합니다.)

 

내장된 Entity 생성하기

개발자는 객체에 여러 필드가 포함되어 있는 경우에도 데이터베이스 로직에서 Entity 또는 데이터 객체를 응집된 전체로 표현하려고 합니다.

@Embedded주석을 사용하여 테이블 내의 하위 필드로 분해하려고 하는 객체를 나타낼 수 있습니다. 그러면 다른 개별 열을 쿼리 하듯 삽입된 필드를 쿼리 할 수 있습니다.

 

예를 들어 User 클래스에는 Address 유형의 필드를 포함할 수 있으며 이 유형의 필드는 street, city, state  postCode라는 필드의 구성을 나타냅니다. 구성된 열을 테이블에 별도로 저장하려면 다음 코드와 같이 @Embedded로 주석 처리된 Address 필드를 User 클래스에 포함합니다.

 

data class Address(
        val street: String?,
        val state: String?,
        val city: String?,
        @ColumnInfo(name = "post_code") val postCode: Int
    )

    @Entity
    data class User(
        @PrimaryKey val id: Int,
        val firstName: String?,
        @Embedded val address: Address?
    )

 

User 객체를 나타내는 테이블에는 id, firstName, street, state, city  post_code라는 이름의 열이 포함됩니다.

 

 @Embedded는 중첩해서 사용할 수 있다.

 

Entity에 동일한 유형의 삽입된 필드가 여러 개 있으면 prefix 속성을 설정하여 각 열을 고유하게 유지할 수 있습니다

그러면 Room은 제공된 값을 삽입된 객체의 각 열 이름 시작 부분에 추가합니다.

일대일 관계 정의

Entity 간의일대일 관계는 상위 항목의 각 인스턴스가 정확히 하나의 하위 항목 인스턴스에 상응하는 관계이며,

그 반대의 경우도 마찬가지입니다.

 

예를 들어 사용자가 소유한 노래 라이브러리가 있는 음악 스트리밍 앱을 생각해 보세요. 각 사용자에게는 하나의 라이브러리만 있으며 각 라이브러리는 정확히 한 명의 사용자에 상응합니다. 따라서 User 항목과 Library 항목 간에 일대일 관계가 있어야 합니다.

    @Entity
    data class User(
        @PrimaryKey val userId: Long,
        val name: String,
        val age: Int
    )

    @Entity
    data class Library(
        @PrimaryKey val libraryId: Long,
        val userOwnerId: Long
    )

parentColumn을 상위 항목(User)의 기본 키 열 이름으로 설정하고

entityColumn을 상위 항목의 기본 키를 참조하는 하위 항목(Library)의 열 이름으로 설정하여 

@Relation 주석을 하위 항목 인스턴스에 추가합니다.

data class UserAndLibrary(
        @Embedded val user: User,
        @Relation(
             parentColumn = "userId",
             entityColumn = "userOwnerId"
        )
        val library: Library
    )

마지막으로, 상위 항목과 하위 항목을 쌍으로 하는 데이터 클래스의 모든 인스턴스를 반환하는 메서드를 DAO 클래스에 추가합니다.

 

이 메서드를 사용하려면 Room에서 두 개의 쿼리를 실행해야 하므로 이 메서드에 @Transaction 주석을 추가하여 전체 작업이 Atomic 하게 실행되도록 해야 합니다

    @Transaction
    @Query("SELECT * FROM User")
    fun getUsersAndLibraries(): List<UserAndLibrary>

일대다 관계 정의

Entity 간의일대다 관계는 상위 항목의 각 인스턴스가 0개 이상의 하위 항목 인스턴스에 상응하지만,  하위 항목의 각 인스턴스는 정확히 하나의 상위 항목 인스턴스에만 상응할 수 있는 관계입니다.

 

음악 스트리밍 앱 예에서 사용자가 노래를 재생목록으로 구성할 수 있다고 가정해 보겠습니다. 각 사용자는 원하는 만큼 재생목록을 만들 수 있지만 각 재생목록은 정확히 한 명의 사용자가 만듭니다. 따라서 User 항목과 Playlist 항목 간에 일대다 관계가 있어야 합니다.

 

먼저, 두 항목 각각의 클래스를 만듭니다. 이전 예에서와 같이 하위 항목은 상위 항목의 기본 키에 대한 참조인 변수를 포함해야 합니다.

    @Entity
    data class User(
        @PrimaryKey val userId: Long,
        val name: String,
        val age: Int
    )

    @Entity
    data class Playlist(
        @PrimaryKey val playlistId: Long,
        val userCreatorId: Long,
        val playlistName: String
    )

parentColumn을 상위 항목(User)의 기본 키 열 이름으로 설정하고 

entityColumn을 상위 항목의 기본 키를 참조하는 하위 항목의 열 이름으로 설정하여 

@Relation 주석을 하위 항목 인스턴스에 추가합니다.

data class UserWithPlaylists(
        @Embedded val user: User,
        @Relation(
              parentColumn = "userId",
              entityColumn = "userCreatorId"
        )
        val playlists: List<Playlist>
    )

이 메서드를 사용하려면 Room에서 두 개의 쿼리를 실행해야 하므로 이 메서드에 @Transaction 주석을 추가하여 전체 작업이 Atomic 하게 실행되도록 해야 합니다

    @Transaction
    @Query("SELECT * FROM User")
    fun getUsersWithPlaylists(): List<UserWithPlaylists>

다대다 관계 정의

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

 

음악 스트리밍 앱 예에서 다시 사용자 정의 재생목록을 생각해 보세요.

각 재생목록에는 많은 노래가 포함될 수 있으며 각 노래는 여러 다양한 재생목록에 속할 수 있습니다.

따라서 Playlist 항목과 Song 항목 간에 다대다 관계가 있어야 합니다.

 

먼저, 두 항목 각각의 클래스를 만듭니다. 다대다 관계는 일반적으로 하위 항목에 상위 항목에 대한 참조가 없기 때문에 다른 관계 유형과 구별됩니다.

 

대신 세 번째 클래스를 만들어 두 entity 간의 연결 entity(또는 상호 참조 테이블)을 나타냅니다. 상호 참조 테이블에는 테이블에 표시된 다대다 관계에 있는 각 항목의 기본 키 열이 있어야 합니다.

 

이 예에서 상호 참조 테이블의 각 행은 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"])
    data class PlaylistSongCrossRef(
        val playlistId: Long,
        val songId: Long
    )
  • 재생목록 및 각 재생목록에 상응하는 노래 목록을 쿼리 하려면 단일 Playlist 객체 및 재생목록에 포함된 모든 Song 객체 목록을 포함하는 새 데이터 클래스를 만듭니다.
  • 노래 및 각 노래에 상응하는 재생목록을 쿼리하려면 단일 Song 객체 및 노래가 포함된 모든 Playlist 객체 목록을 포함하는 새 데이터 클래스를 만듭니다.

어느 경우든 이러한 각 클래스의 @Relation 주석에서 associateBy 속성을 사용하여 entity 간의 관계를 모델링함으로써 Playlist entity와Song entity 간의 관계를 제공하는 상호 참조 항목을 식별합니다

    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>
    )

 DAO 클래스에 메서드를 추가하여 앱에 필요한 쿼리 기능을 노출합니다.

 

  • getPlaylistsWithSongs: 이 메서드는 데이터베이스를 쿼리하고 결과 PlaylistWithSongs 객체를 모두 반환합니다.
  • getSongsWithPlaylists: 이 메서드는 데이터베이스를 쿼리하고 결과 SongWithPlaylists 객체를 모두 반환합니다.

이러한 메서드를 사용하려면 Room에서 두 개의 쿼리를 실행해야 하므로 두 메서드 모두에 @Transaction 주석을 추가하여 전체 작업이 원자적으로 실행되도록 해야 합니다.

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

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

 @Relation 주석이 특정 사용 사례를 충족하지 않으면 SQL 쿼리에서 JOIN 키워드를 사용하여 적절한 관계를 수동으로 정의해야 할 수 있습니다.

'Android Jetpack Architecture > Room' 카테고리의 다른 글

뷰 만들기  (0) 2020.08.24
중첩된 관계 정의  (0) 2020.08.23
테이블 검색 지원  (0) 2020.08.23
Entity 생성하기  (0) 2020.08.23
Room 개요  (0) 2020.08.22