쿼리가 UI를 차단하지 않도록 Room은 기본 스레드에서 데이터베이스 액세스를 지원하지 않습니다. 이 제한사항은 DAO 쿼리 를 비동기식으로 만들어야 함을 의미합니다. Room 라이브러리에는 비동기 쿼리 실행을 제공하는 여러 프레임워크와의 통합이 포함되어 있습니다.
DAO 쿼리는 세 가지 카테고리로 분류됩니다.
- 원샷 쓰기 쿼리: 데이터베이스에서 데이터를 삽입하거나 업데이트하거나 삭제합니다.
- 원샷 읽기 쿼리: 데이터베이스에서 데이터를 한 번만 읽고 그 시점의 데이터베이스 스냅샷과 함께 결과를 반환합니다.
- 관찰 가능한 읽기 쿼리: 기본 데이터베이스 테이블이 변경될 때마다 데이터베이스에서 데이터를 읽고 이러한 변경사항을 반영하는 새 값을 내보냅니다.
언어 및 프레임워크 옵션
Room은 특정 언어 기능 및 라이브러리와의 상호 운용성을 위한 통합 지원을 제공합니다. 다음 표는 쿼리 유형 및 프레임워크에 기반한 관련 반환 유형을 보여줍니다.
| 쿼리 유형 | Kotlin 언어 기능 (네이티브) | RxJava | Guava | Jetpack 수명 주기* |
|---|---|---|---|---|
| 원샷 쓰기 | 코루틴(suspend) |
Single<T>, Maybe<T>,
Completable |
ListenableFuture<T> |
해당 사항 없음 |
| 원샷 읽기 | 코루틴(suspend) |
Single<T>, Maybe<T> |
ListenableFuture<T> |
해당 사항 없음 |
| 관찰 가능한 읽기 | Flow<T> |
Flowable<T>, Publisher<T>,
Observable<T> |
해당 사항 없음 | LiveData<T> |
이 가이드에서는 이러한 통합을 사용하여 DAO에서 비동기 쿼리를 구현하는 세 가지 방법을 보여줍니다.
Flow 및 코루틴을 사용하는 Kotlin
Kotlin은 서드 파티 프레임워크 없이 비동기 쿼리를 작성할 수 있는 기본 제공 언어 기능을 제공합니다.
- Room은 관찰 가능한 쿼리를 작성하기 위해 Kotlin's Flow 를 직접 지원합니다.
- Room은
suspend키워드를 요구하여 원샷 DAO 쿼리를 Kotlin 코루틴으로 비동기식으로 만듭니다.
코루틴 및 Flow 지원은 핵심 Room 런타임에 직접 빌드되므로 추가 아티팩트가 필요하지 않습니다.
Kotlin 및 Java용 RxJava
Room 3.0은 RxJava 3 반환 유형을 지원합니다. RxJava 반환 유형을 사용하려면 데이터베이스 또는 DAO에서 RxJava 반환 유형 변환기를 등록해야 합니다.
- 빌드 구성에
androidx.room3:room3-rxjava3아티팩트를 포함합니다. @Database또는@Dao선언에@DaoReturnTypeConverters(RxDaoReturnTypeConverters::class)주석을 답니다.
Room은 다음 RxJava 3 반환 유형을 지원합니다.
- 원샷 쿼리:
Completable,Single<T>, andMaybe<T> - 관찰 가능한 쿼리:
Publisher<T>,Flowable<T>, 및Observable<T>
LiveData 및 Guava
Room 3.0은 변환기를 사용하여 LiveData 및 Guava ListenableFuture 반환 유형을 지원합니다.
- LiveData:
androidx.room3:room3-livedata아티팩트를 포함하고 데이터베이스 또는 DAO에@DaoReturnTypeConverters(LiveDataDaoReturnTypeConverter::class)주석을 답니다. - Guava:
androidx.room3:room3-guava아티팩트를 포함하고 데이터베이스 또는 DAO에@DaoReturnTypeConverters(GuavaDaoReturnTypeConverter::class)주석을 답니다.
비동기 원샷 쿼리 작성
원샷 쿼리는 데이터베이스 작업으로, 한 번만 실행되며 실행 시점의 데이터 스냅샷을 가져옵니다. 다음은 비동기 원샷 쿼리를 보여주는 예입니다.
@Dao interface UserDao { @Query("SELECT * FROM user WHERE id = :id") suspend fun loadUserById(id: Int): User @Query("SELECT * from user WHERE region IN (:regions)") suspend fun loadUsersByRegion(regions: List<String>): List<User> }
관찰 가능한 쿼리 작성
관찰 가능한 쿼리는 참조된 테이블이 변경될 때마다 새 값을 내보내는 읽기 작업입니다. 예를 들어 이 동작을 사용하여 데이터베이스가 변경될 때 표시된 항목 목록을 업데이트할 수 있습니다. 다음은 관찰 가능한 쿼리를 보여주는 예입니다.
@Dao interface ObservableUserDao { @Query("SELECT * FROM user WHERE id = :id") fun loadUserById(id: Int): Flow<User> @Query("SELECT * from user WHERE region IN (:regions)") fun loadUsersByRegion(regions: List<String>): Flow<List<User>> }
데이터베이스 무효화 수동 추적
관찰 가능한 데이터베이스 작업을 수동으로 빌드해야 하는 경우
createFlow API를 사용할 수 있습니다.InvalidationTracker 이 API를 사용하면 특정 테이블의 수정을 추적하고 이러한 테이블이 변경될 때마다 알림을 내보내는 Flow를 만들 수 있습니다.
fun getArtistTours(db: RoomDatabase, from: Date, to: Date): Flow<Map<Artist, TourState>> { return db.invalidationTracker.createFlow("Artist").map { _ -> val artists = artistsDao.getAllArtists() val tours = tourService.fetchStates(artists.map { it.id }) associateTours(artists, tours, from, to) } }
기본적으로 반환된 Flow는 스트림을 시작하기 위해 등록된 모든 테이블이 포함된 초기 값을 내보냅니다. emitInitialState 매개변수를 false로 설정하여 이 동작을 사용 중지할 수 있습니다.
맞춤 DAO 반환 유형 변환기
Room 또는 확장 프로그램 라이브러리에서 직접 지원하지 않는 유형의 경우 맞춤 DAO 반환 유형 변환기를 정의하여 추가 반환 유형을 지원할 수 있습니다. DAO 함수의 결과를 맞춤 유형으로 변환하려면 변환기 함수에
@DaoReturnTypeConverter 주석을 답니다.
예를 들어 androidx.tracing을 사용하여 쿼리 실행 주위에 추적 섹션을 추가하는 변환기를 정의하여 실행을 맞춤 TracedQuery 유형으로 래핑하여 성능에 민감한 쿼리를 모니터링할 수 있습니다.
class TracedQuery<T>(val result: T) object TracingDaoReturnTypeConverter { @DaoReturnTypeConverter([OperationType.READ]) suspend fun <T> convert( rawQuery: RoomRawQuery, executeAndConvert: suspend () -> T ): TracedQuery<T> { val result = trace("TracedQuery: ${rawQuery.sql}") { executeAndConvert() } return TracedQuery(result) } }
변환기를 사용하려면 데이터베이스 또는 DAO에
@DaoReturnTypeConverters 주석을 답니다.
@Dao @DaoReturnTypeConverters(TracingDaoReturnTypeConverter::class) interface MusicDao { @Query("SELECT * FROM Song") suspend fun getAllSongs(): TracedQuery<List<Song>> }
DAO 반환 유형 변환기 초기화 제어
Room은 일반적으로 DAO 반환 유형 변환기의 인스턴스화를 처리합니다.
그러나 변환기 클래스에 추가 종속 항목을 전달해야 하는 경우 앱에서 초기화를 직접 제어해야 합니다. 이 경우 변환기 클래스에
@ProvidedDaoReturnTypeConverter 주석을 답니다.
@ProvidedDaoReturnTypeConverter class TracingDaoReturnTypeConverter(val tracer: Tracer) { @DaoReturnTypeConverter([OperationType.READ]) suspend fun <T> convert( rawQuery: RoomRawQuery, executeAndConvert: suspend () -> T ): TracedQuery<T> { val result = tracer.trace("TracedQuery: ${rawQuery.sql}") { executeAndConvert() } return TracedQuery(result) } }
그런 다음
@DaoReturnTypeConverters에서 변환기 클래스를 선언하는 것 외에도
RoomDatabase.Builder.addDaoReturnTypeConverter 함수를 사용하여 변환기 클래스
인스턴스를 RoomDatabase 빌더에 전달합니다.
val db = Room.databaseBuilder<MyDatabase>(applicationContext, "database-name") .addDaoReturnTypeConverter(TracingDaoReturnTypeConverter(myLoggerInstance)) .build()
변환기 함수 요구사항
@DaoReturnTypeConverter 함수는 다음과 같은 여러 요구사항을 충족해야 합니다.
- 일반적으로
executeAndConvert라는 기능 매개변수를 마지막 인수로 포함해야 합니다. 이 매개변수는 Room에서 쿼리를 실행하고 결과를 파싱하기 위해 생성하는suspend람다입니다.- 변환기에서 페이징과 같은 쿼리를 변환해야 하는 경우 람다는
RoomRawQuery매개변수를 사용할 수 있습니다.
- 변환기에서 페이징과 같은 쿼리를 변환해야 하는 경우 람다는
- 선택적으로 람다 앞에 다음 매개변수를 허용할 수 있습니다.
db: RoomDatabase: 코루틴 범위를 가져오거나 추가 작업을 실행하는 데 유용한 데이터베이스 인스턴스에 액세스합니다.tableNames: Array<String>또는List<String>: 관찰 가능한 유형에 유용한 쿼리에서 액세스하는 테이블의 이름을 제공합니다.rawQuery: RoomRawQuery: 쿼리의 런타임 인스턴스를 제공합니다.inTransaction: Boolean: 쿼리가 트랜잭션 내에서 실행되는지 여부를 나타냅니다.