Room (Kotlin 멀티플랫폼)

Room 지속성 라이브러리는 SQLite를 통해 추상화 레이어를 제공하여 을 사용하여 SQLite를 완벽히 활용하면서 더 강력한 데이터베이스 액세스를 제공합니다. 이 페이지는 Kotlin 멀티플랫폼 (KMP) 프로젝트에서 Room을 사용하는 데 중점을 둡니다. 자세한 내용은 Room 사용에 관한 자세한 내용은 Room을 사용하여 로컬 데이터베이스에 데이터 저장을 참고하세요. 또는 공식 샘플을 참고하세요.

종속 항목 설정

KMP를 지원하는 Room의 현재 버전은 다음과 같습니다. 2.7.0-alpha01 이상

KMP 프로젝트에서 Room을 설정하려면 build.gradle.kts 파일을 생성합니다.

  • androidx.room:room-gradle-plugin - Room 스키마를 구성하는 Gradle 플러그인
  • androidx.room:room-compiler - 코드를 생성하는 KSP 프로세서
  • androidx.room:room-runtime - 라이브러리의 런타임 부분
  • androidx.sqlite:sqlite-bundled - (선택사항) 번들 SQLite 라이브러리

또한 Room의 SQLite 드라이버를 구성해야 합니다. 이러한 요인은 기반으로 작동합니다 자세한 내용은 드라이버 구현 을 참조하세요.

추가 설정 정보는 다음을 참고하세요.

를 통해 개인정보처리방침을 정의할 수 있습니다.

데이터베이스 클래스 정의

DAO와 함께 @Database 주석이 달린 데이터베이스 클래스를 만들어야 함 및 개체와 함께 공유 KMP 모듈의 공통 소스 세트에 포함되어 있습니다. 배치 공통 소스에 있는 이러한 클래스를 사용하면 모든 대상 간에 공유될 수 있습니다. 지원합니다

인터페이스로 expect 객체를 선언하는 경우 RoomDatabaseConstructor: Room 컴파일러가 actual 있습니다. Android 스튜디오에서 경고가 발생할 수 있음 "Expected object 'AppDatabaseConstructor' has no actual declaration in module" @Suppress("NO_ACTUAL_FOR_EXPECT")를 사용하여 경고를 표시하지 않을 수 있습니다.

// shared/src/commonMain/kotlin/Database.kt

@Database(entities = [TodoEntity::class], version = 1)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
  abstract fun getDao(): TodoDao
}

// The Room compiler generates the `actual` implementations.
@Suppress("NO_ACTUAL_FOR_EXPECT")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
    override fun initialize(): AppDatabase
}

@Dao
interface TodoDao {
  @Insert
  suspend fun insert(item: TodoEntity)

  @Query("SELECT count(*) FROM TodoEntity")
  suspend fun count(): Int

  @Query("SELECT * FROM TodoEntity")
  fun getAllAsFlow(): Flow<List<TodoEntity>>
}

@Entity
data class TodoEntity(
  @PrimaryKey(autoGenerate = true) val id: Long = 0,
  val title: String,
  val content: String
)

실제 / 예상 선언을 선택적으로 사용할 수 있습니다. 을 사용하여 플랫폼별 Room 구현을 만듭니다. 예를 들어 expect를 사용하여 공통 코드에 정의된 플랫폼별 DAO 플랫폼별 추가 쿼리로 actual 정의 지정 소스 세트입니다.

데이터베이스 빌더 만들기

데이터베이스 빌더를 정의하여 각 플랫폼에서 Room을 인스턴스화해야 합니다. 이 API에서 플랫폼별 소스에 있어야 하는 유일한 부분임 파일 시스템 API의 차이로 인해 발생합니다. 예를 들어 Android에서 데이터베이스 위치는 일반적으로 Context.getDatabasePath() API, iOS의 경우 데이터베이스 위치는 NSFileManager를 사용하여 얻습니다.

Android

데이터베이스 인스턴스를 만들려면 데이터베이스와 함께 컨텍스트를 지정합니다. 있습니다.

// shared/src/androidMain/kotlin/Database.kt

fun getDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
  val appContext = ctx.applicationContext
  val dbFile = appContext.getDatabasePath("my_room.db")
  return Room.databaseBuilder<AppDatabase>(
    context = appContext,
    name = dbFile.absolutePath
  )
}

iOS

데이터베이스 인스턴스를 만들려면 다음을 사용하여 데이터베이스 경로를 제공합니다. NSFileManager. 일반적으로 NSDocumentDirectory에 있습니다.

// shared/src/iosMain/kotlin/Database.kt

fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
    val dbFilePath = documentDirectory() + "/my_room.db"
    return Room.databaseBuilder<AppDatabase>(
        name = dbFilePath,
    )
}

private fun documentDirectory(): String {
  val documentDirectory = NSFileManager.defaultManager.URLForDirectory(
    directory = NSDocumentDirectory,
    inDomain = NSUserDomainMask,
    appropriateForURL = null,
    create = false,
    error = null,
  )
  return requireNotNull(documentDirectory?.path)
}

JVM (데스크톱)

데이터베이스 인스턴스를 만들려면 Java 또는 Kotlin을 사용하여 데이터베이스 경로를 제공하세요. API에 액세스할 수 있습니다

// shared/src/jvmMain/kotlin/Database.kt

fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
    val dbFile = File(System.getProperty("java.io.tmpdir"), "my_room.db")
    return Room.databaseBuilder<AppDatabase>(
        name = dbFile.absolutePath,
    )
}

데이터베이스 인스턴스화

플랫폼별 중 하나에서 RoomDatabase.Builder를 가져온 후 Room 데이터베이스의 나머지 부분을 공통 코드로 구성할 수 있습니다. 실제 데이터베이스 인스턴스화와 함께 제공됩니다.

// shared/src/commonMain/kotlin/Database.kt

fun getRoomDatabase(
    builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
  return builder
      .addMigrations(MIGRATIONS)
      .fallbackToDestructiveMigrationOnDowngrade()
      .setDriver(BundledSQLiteDriver())
      .setQueryCoroutineContext(Dispatchers.IO)
      .build()
}

SQLiteDriver 선택

이전 코드 스니펫은 BundledSQLiteDriver를 사용합니다. 이것은 SQLite를 포함하는 것이 좋습니다. 이 드라이버는 가장 일관성 있는 최신 버전의 SQLite를 시킵니다. 만약 OS에서 제공하는 SQLite를 사용하려면 플랫폼별 setDriver API를 사용하세요. 플랫폼별 드라이버를 지정하는 소스 세트를 생성합니다. Android의 경우 AndroidSQLiteDriver, iOS의 경우 NativeSQLiteDriver를 사용할 수 있습니다. 받는사람 NativeSQLiteDriver를 사용하는 경우 iOS 기기에서 앱이 시스템 SQLite와 동적으로 링크됩니다.

// shared/build.gradle.kts

kotlin {
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "TodoApp"
            isStatic = true
            // Required when using NativeSQLiteDriver
            linkerOpts.add("-lsqlite3")
        }
    }
}

차이점

Room은 원래 Android 라이브러리로 개발되었으며 나중에 API 호환성에 중점을 둔 KMP Room의 KMP 버전은 약간 다릅니다. Android 전용 버전에서 달라질 수 있습니다 이러한 차이점은 나열되고 설명되어 있습니다.

DAO 함수 차단

KMP용 Room을 사용할 때 Android가 아닌 플랫폼용으로 컴파일된 모든 DAO 함수 suspend 함수여야 합니다. 단, 반응형 반환 유형은 예외입니다. Flow 형식으로 전송합니다.

// shared/src/commonMain/kotlin/MultiplatformDao.kt

@Dao
interface MultiplatformDao {
  // ERROR: Blocking function not valid for non-Android targets
  @Query("SELECT * FROM Entity")
  fun blockingQuery(): List<Entity>

  // OK
  @Query("SELECT * FROM Entity")
  suspend fun query(): List<Entity>

  // OK
  @Query("SELECT * FROM Entity")
  fun queryFlow(): Flow<List<Entity>>

  // ERROR: Blocking function not valid for non-Android targets
  @Transaction
  fun blockingTransaction() { // … }

  // OK
  @Transaction
  suspend fun transaction() { // … }
}

Room에서 제공하는 기능이 풍부한 비동기 kotlinx.coroutines 라이브러리의 이점 다양한 플랫폼에서 사용할 수 있습니다 최적의 기능을 위해 suspend 함수가 KMP 프로젝트에서 컴파일된 DAO에 적용되지만 기존 DAO와의 하위 호환성을 유지하기 위한 Android 관련 DAO 제공합니다

KMP와의 기능 차이

이 섹션에서는 KMP와 Android 플랫폼 간 기능의 차이점을 설명합니다. 맞춤설정할 수 있습니다.

@RawQuery DAO 함수

Android가 아닌 플랫폼용으로 컴파일된 @RawQuery 주석이 달린 함수 는RoomRawQuery SupportSQLiteQuery입니다.

@Dao
interface TodoDao {
  @RawQuery
  suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}

그런 다음 RoomRawQuery를 사용하여 런타임에 쿼리를 만들 수 있습니다.

suspend fun getTodosWithLowercaseTitle(title: String): List<TodoEntity> {
  val query = RoomRawQuery(
    sql = "SELECT * FROM TodoEntity WHERE title = ?"
    onBindStatement = {
      it.bindText(1, title.lowercase())
    }
  )
  return todosDao.getTodos(query)
}

콜백 쿼리

쿼리 콜백 구성을 위한 다음 API는 일반적으로 사용할 수 없습니다. 따라서 Android 이외의 플랫폼에서는 사용할 수 없습니다.

  • RoomDatabase.Builder.setQueryCallback
  • RoomDatabase.QueryCallback

향후 Room 버전에서 쿼리 콜백 지원을 추가할 예정입니다.

쿼리 콜백으로 RoomDatabase를 구성하는 API RoomDatabase.Builder.setQueryCallback 및 콜백 인터페이스 RoomDatabase.QueryCallback는 공통으로 사용할 수 없으므로 사용할 수 없습니다. 다른 플랫폼에서도 작동합니다.

데이터베이스 자동 닫기

제한 시간 후 자동 닫기를 사용 설정하는 API RoomDatabase.Builder.setAutoCloseTimeout은(는) Android에서만 사용 가능하며 다른 플랫폼에서는 이용할 수 없습니다.

사전 패키지 데이터베이스

다음 API는 기존 데이터베이스 (예:RoomDatabase 사전 패키징된 데이터베이스)는 공통으로 사용할 수 없으므로 다른 플랫폼에서도 사용할 수 있습니다. 이러한 API는 다음과 같습니다.

  • RoomDatabase.Builder.createFromAsset
  • RoomDatabase.Builder.createFromFile
  • RoomDatabase.Builder.createFromInputStream
  • RoomDatabase.PrepackagedDatabaseCallback

Google은 방.

멀티 인스턴스 무효화

멀티 인스턴스 무효화를 사용 설정하는 API RoomDatabase.Builder.enableMultiInstanceInvalidation은(는) 다음에서만 사용할 수 있습니다. 일반적인 플랫폼이나 다른 플랫폼에서는 사용할 수 없습니다.