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

DAO를 사용하여 데이터 액세스 본문

Android Jetpack Architecture/Room

DAO를 사용하여 데이터 액세스

hik14 2020. 8. 25. 20:28

앱 데이터에 액세스 하려면데이터 액세스 객체 또는 DAO를 사용해야 된다.

각 DAO에는 앱 데이터베이스의 추상 액세스를 제공하는 메서드가 포함되어 있으므로 이 Dao 객체 세트는 Room의 기본 구성요소를 구성한다.

 

쿼리 빌더나 직접 쿼리 대신 DAO 클래스를 사용하면 데이터 베이스 구성 요소를 분리하고, 단위 테스트가 수월하다.

 

DAO는 인터페이스 또는 추상 클래스일 수 있습니다.

추상 클래스라면 RoomDatabase를 유일한 매개변수로 사용하는 생성자를 선택적으로 가질 수 있습니다.

Room은 컴파일 시간에 각 DAO 구현을 생성합니다.

 

Room은 UI스레드(메인 스레드)에서 데이터 베이스 접근을 허용하지 않는다. (UI를 오랫동안 잠글 수 있기 때문입니다. )

allowMainThreadQueries()를 호출하면 UI 스레드에서도 접근 가능하다.

 LiveData 또는 Flowable의 인스턴스를 반환하는 비동기 쿼리는 필요한 경우 백그라운드 스레드에서 쿼리를 비동기식으로 실행하므로 이 규칙에서 제외됩니다.

 

INSERT

DAO 메서드를 만들고 @Insert 주석을 달 때 Room은 단일 트랜잭션으로 모든 매개변수를 데이터베이스에 삽입하는 구현을 생성합니다

    @Dao
    interface MyDao {
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        fun insertUsers(vararg users: User)

        @Insert
        fun insertBothUsers(user1: User, user2: User)

        @Insert
        fun insertUsersAndFriends(user: User, friends: List<User>)
    }

@Insert 메서드는 매개변수를 하나만 수신하면 삽입된 항목의 새 rowId long을 반환할 수 있습니다. 매개변수가 배열 또는 컬렉션이면 대신 long[] 또는 List<Long>List <Long>을 반환해야 합니다.

 

UPDATE

Update 편의 메서드는 매개변수로 제공된 항목 세트를 데이터베이스에서 수정합니다.

이 메서드는 각 entity의 기본 키와 일치하는 쿼리를 사용합니다.

    @Dao
    interface MyDao {
        @Update
        fun updateUsers(vararg users: User)
    }

일반적으로 필요하지는 않지만 이 메서드가 int 값을 대신 반환하도록 하여 데이터베이스에서 업데이트된 행 수를 나타낼 수 있습니다.

 

DELETE

Delete 편의 메서드는 매개변수로 제공된 항목 세트를 데이터베이스에서 삭제합니다.

이 메서드는 기본 키를 사용하여 삭제할 항목을 찾습니다.

    @Dao
    interface MyDao {
        @Delete
        fun deleteUsers(vararg users: User)
    }

 

QUERY

@Query는 DAO 클래스에서 사용하는 기본 주석입니다.

이 주석으로 데이터베이스에서 읽기/쓰기 작업을 실행할 수 있습니다.

@Query 메서드는 컴파일 시간에 확인되므로 쿼리에 문제가 있으면 런타임 오류 대신 컴파일 오류가 발생합니다.

 

- 필드 이름이 일치하지 않는 경우 경고를 출력한다.

- 필드 이름이 전부 일치하지 않으면 에러를 출력한다.

 

simple query

    @Dao
    interface MyDao {
        @Query("SELECT * FROM user")
        fun loadAllUsers(): Array<User>
    }

모든 사용자를 로드하는 매우 단순한 쿼리입니다. 컴파일 타임에 Room은 사용자 테이블의 모든 열을 쿼리하고 있음을 알고 있습니다. 쿼리에 구문 오류가 포함되어 있거나 데이터베이스에 사용자 테이블이 없으면 앱이 컴파일될 때 Room은 적절한 메시지와 함께 오류를 표시합니다.

 

매개변수 전달

 

대부분의 경우 특정 연령보다 나이가 더 많은 사용자만 표시하는 식의 필터링 작업을 실행하려면 매개변수를 쿼리에 전달해야 합니다.

    @Dao
    interface MyDao {
        @Query("SELECT * FROM user WHERE age > :minAge")
        fun loadAllUsersOlderThan(minAge: Int): Array<User>
    }

 컴파일 시간에 처리되면 Room은 :minAge 바인드 매개변수를 minAge 메서드 매개변수와 일치시킵니다. Room은 매개변수 이름을 사용하여 일치를 실행합니다. 불일치가 있으면 앱이 컴파일될 때 오류가 발생합니다.

 

여러 개의 매개변수를 전달 가능하다.

    @Dao
    interface MyDao {
        @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
        fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User>

        @Query("SELECT * FROM user WHERE first_name LIKE :search " +
               "OR last_name LIKE :search")
        fun findUserWithName(search: String): List<User>
    }

 

열의 일부만 반환

대부분의 경우 Entity의 몇 가지 필드만 가져와야 합니다.

예를 들어 UI에는 사용자에 관한 모든 세부 정보가 아닌 사용자의 이름 및 성만 표시될 수 있습니다.

앱 UI에 표시되는 열만 가져오면 귀중한 리소스가 절약되고 쿼리도 더 빨리 완료될 수 있습니다.

 

    data class NameTuple(
        @ColumnInfo(name = "first_name") val firstName: String?,
        @ColumnInfo(name = "last_name") val lastName: String?
    )
    
    @Dao
    interface MyDao {
        @Query("SELECT first_name, last_name FROM user")
        fun loadFullName(): List<NameTuple>
    }

Room은 쿼리가 firstName과 lastName 칼럼 값을 반환하며 user entity에서 NameTuple클래스에 맵핑을 할 수 있다.

쿼리가 너무 많은 칼럼을 반환하거나 NameTuple클래스에 없는 칼럼을 반환하면 경고 메시지가 출력된다.

 

컬렉션을 매개변수로 전달하기

일부 쿼리에서는 런타임까지 알려지지 않는 정확한 수의 매개변수와 함께 가변적인 수의 매개변수를 전달해야 할 수도 있습니다.

예를 들어 특정지역에 거주하는 모든 사용자에 관한 정보를 검색하려고 할 수 있습니다.

Room은 매개변수가 언제 컬렉션을 나타내는지 인식하고 제공된 매개변수 수에 따라 런타임 시에 자동으로 확장합니다.

    @Dao
    interface MyDao {
        @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
        fun loadUsersFromRegions(regions: List<String>): List<NameTuple>
    }

직접 커서로 데이터 접근하기

앱의 로직에서 직접 반환 행에 액세스해야 한다면 다음 코드 스니펫과 같이 쿼리에서 Cursor 객체를 반환할 수 있습니다.

    @Dao
    interface MyDao {
        @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
        fun loadRawUsersOlderThan(minAge: Int): Cursor
    }

* Cursor API를 사용하여 작업하지 않는 것이 좋습니다. 행의 존재 여부 또는 행에 포함된 값을 보장하지 않기 때문입니다. 커서를 예상하는 코드 및 쉽게 리팩터링 할 수 없는 코드가 이미 있는 경우에만 이 기능을 사용하세요.

여러 테이블 쿼리

일부 쿼리는 결과를 계산하기 위해 여러 테이블에 액세스해야 할 수 있습니다.

Room을 사용하면 어떤 쿼리든 작성할 수 있으므로 테이블을 조인할 수도 있습니다.

 

반환 값이  Flowable 또는 LiveData와 같은 식별 가능한 데이터 유형이면 Room은 쿼리에서 참조된 모든 테이블의 무효화를 관찰합니다.----> 무슨 말인지 모르겠다.

 

테이블 조인을 실행하여 책을 빌리는 사용자가 포함된 테이블과 현재 대출 중인 책에 관한 데이터가 포함된 테이블 간에 정보를 통합하는 방법을 보여줍니다.

    @Dao
    interface MyDao {
        @Query(
            "SELECT * FROM book " +
            "INNER JOIN loan ON loan.book_id = book.id " +
            "INNER JOIN user ON user.id = loan.user_id " +
            "WHERE user.name LIKE :userName"
        )
        fun findBooksBorrowedByNameSync(userName: String): List<Book>
    }
    
    
    @Dao
    interface MyDao {
        @Query(
            "SELECT user.name AS userName, pet.name AS petName " +
            "FROM user, pet " +
            "WHERE user.id = pet.user_id"
        )
        fun loadUserAndPetNames(): LiveData<List<UserPet>>

        // You can also define this class in a separate file.
        data class UserPet(val userName: String?, val petName: String?)
    }

 

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

Room은 특정 프레임워크나 API와의 상호 운용성  (0) 2020.08.25
뷰 만들기  (0) 2020.08.24
중첩된 관계 정의  (0) 2020.08.23
객체 간 관계 정의  (0) 2020.08.23
테이블 검색 지원  (0) 2020.08.23