Room 2.x से Room 3.0 पर माइग्रेट करना

Room 3.0, वर्शन का एक बड़ा अपडेट है. इससे लाइब्रेरी को Kotlin-first में बदल दिया जाता है. यह Kotlin Multiplatform (KMP) के साथ काम करता है. इसके लिए, Kotlin Symbol Processing (KSP) की ज़रूरत होती है. साथ ही, एसिंक्रोनस कार्रवाइयों के लिए कोरूटीन लागू करता है.

Room 2.x के मौजूदा ऐप्लिकेशन और ट्रांज़िटिव डिपेंडेंसी के साथ काम न करने की समस्याओं को रोकने के लिए, Room 3.0 को एक नए पैकेज में रखा गया है: androidx.room3.

इस गाइड में, Room 2.x के मौजूदा वर्शन को Room 3.0 पर माइग्रेट करने का तरीका बताया गया है.

Room 3.0 में हुए मुख्य बदलाव

माइग्रेशन शुरू करने से पहले, इन मुख्य अंतरों के बारे में जान लें:

  • नया पैकेज और आर्टफ़ैक्ट: सभी क्लास androidx.room3 में मौजूद होती हैं. आर्टफ़ैक्ट, room3 प्रीफ़िक्स का इस्तेमाल करते हैं. जैसे, androidx.room3:room3-runtime.
  • सिर्फ़ Kotlin और KSP के लिए: Room 3.0, Java कोड जनरेट करने की सुविधा के साथ काम नहीं करता. KAPT या Java annotation processors के बजाय KSP का इस्तेमाल करें. Room 3.0 अब भी Java सोर्स को इनपुट के तौर पर इस्तेमाल करता है.
  • कोरूटीन पहले: DAO फ़ंक्शन suspend फ़ंक्शन होने चाहिए. हालांकि, ऐसा सिर्फ़ ऑब्ज़र्वेबल टाइप के लिए ज़रूरी नहीं है. CoroutineContext, एक्ज़ीक्यूटर की जगह लेता है.
  • No SupportSQLite: SQLiteDriver एपीआई, Room के साथ काम नहीं करते. कमरे से SupportSQLiteDatabase को मुख्य एपीआई से हटाया गया.
  • एपीआई में बदलाव: माइग्रेशन और डेटाबेस कॉलबैक, SupportSQLiteDatabase के बजाय SQLiteConnection का इस्तेमाल करते हैं.
  • रिएक्टिव टाइप के लिए कन्वर्टर: RxJava, LiveData, Guava, और Paging के रिटर्न टाइप के लिए, आपको @DaoReturnTypeConverters रजिस्टर करना होगा.

हमारा सुझाव है कि माइग्रेट करने की प्रोसेस को दो चरणों में पूरा करें: पहले चरण में, Room 2.x में अपने कोडबेस को तैयार करें और उसे आधुनिक बनाएं. इसके बाद, दूसरे चरण में Room 3.0 पर स्विच करें.


Room 2.x में तैयारी करना और उसे मॉडर्न बनाना

Room 3.0 पर माइग्रेट करने से पहले, Room 2.x की मौजूदा रिलीज़, जैसे कि Room 2.8 पर अपडेट करके, मॉडर्नाइज़ेशन से जुड़ा ज़्यादातर काम किया जा सकता है. Room 2.8, Kotlin Multiplatform या KMP के साथ काम करता है. इसमें कई ड्राइवर एपीआई शामिल हैं, जिनका इस्तेमाल Room 3.0 करता है.

Room को 2.8 और इसके बाद के वर्शन में अपडेट करें

Room 2.x के मौजूदा वर्शन का इस्तेमाल करने के लिए, अपने बिल्ड कॉन्फ़िगरेशन को अपडेट करें:

[versions]
room2 = "2.8.4" # Use the current Room 2.8 version

[libraries]
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room2" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room2" }

KAPT से KSP पर माइग्रेट करना

Room 3.0, Java एनोटेशन प्रोसेसर या KAPT के साथ काम नहीं करता. आपको Kotlin Symbol Processing (KSP) का इस्तेमाल करना होगा. Room 2.x का इस्तेमाल करते हुए भी, इस ट्रांज़िशन को किया जा सकता है.

  1. अपने मॉड्यूल के build.gradle.kts में, KSP प्लगिन लागू करें:

    plugins {
        id("com.google.devtools.ksp") version "<ksp_version>"
    }
    

    पक्का करें कि KSP का वर्शन, Kotlin के वर्शन के साथ काम करता हो.

  2. रूम कंपाइलर की डिपेंडेंसी के लिए, kapt या annotationProcessor को ksp से बदलें:

    dependencies {
        implementation(libs.androidx.room.runtime)
        ksp(libs.androidx.room.compiler)
    }
    

कोरूटीन का इस्तेमाल करना

Room 3.0 में, एसिंक्रोनस ऑपरेशन के लिए कोरूटीन की ज़रूरत होती है.

  • अपने DAO अपडेट करें: जब तक वे Flow या RxJava टाइप जैसे ऑब्ज़र्वेबल रिएक्टिव टाइप नहीं दिखाते, तब तक सभी DAO फ़ंक्शन suspend फ़ंक्शन होने चाहिए.
// Before (Blocking)
@Dao
interface UserDao {
    @Query("SELECT * FROM User")
    fun getAll(): List<User>
}

// After (Suspend)
@Dao
interface UserDao {
    @Query("SELECT * FROM User")
    suspend fun getAll(): List<User>
}
  • अगर आपने डेटाबेस से जुड़ी कार्रवाइयां करने के लिए, कस्टम Executor के साथ RoomDatabase को कॉन्फ़िगर किया है, तो बिल्डर पर setQueryCoroutineContext का इस्तेमाल करके, CoroutineContext पर माइग्रेट करें:
Room.databaseBuilder<AppDatabase>(context, "db")
    .setQueryCoroutineContext(Dispatchers.IO)
    .build()

ड्राइवर एपीआई का इस्तेमाल करें और Support SQLite से बचें

Room 3.0 को SQLiteDriver का पूरा सपोर्ट मिलता है. साथ ही, यह अपने मुख्य एपीआई में SupportSQLiteDatabase का इस्तेमाल नहीं करता.

अगर आपने अपने डेटाबेस बिल्डर पर SQLiteDriver सेट करने के लिए setDriver को कॉल नहीं किया है, तो Room 2.8, कंपैटिबिलिटी मोड में काम करता है. इसमें Support SQLite और Driver, दोनों एपीआई काम करते हैं. इस कंपैटिबिलिटी मोड की मदद से, ड्राइवर को चालू करने से पहले अपने कोडबेस को धीरे-धीरे बदला जा सकता है.

  • कन्वर्ज़न माइग्रेशन: SupportSQLiteDatabase के बजाय SQLiteConnection का इस्तेमाल करने के लिए, अपनी Migration और AutoMigrationSpec सबक्लास माइग्रेट करें.
// Before (SupportSQLiteDatabase)
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(db: SupportSQLiteDatabase) {
        db.execSQL("ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL")
    }
}

// After (SQLiteConnection - Room 2.8)
import androidx.sqlite.SQLiteConnection
import androidx.sqlite.execSQL

val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(connection: SQLiteConnection) {
        connection.execSQL(
            "ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL"
        )
    }
}
  • डेटाबेस कॉलबैक को बदलना: RoomDatabase.Callback को लागू करने के तरीके को अपडेट करके SQLiteConnection का इस्तेमाल करें:
// Before (SupportSQLiteDatabase)
val callback = object : RoomDatabase.Callback() {
    override fun onCreate(db: SupportSQLiteDatabase) {
        // ...
    }
}

// After (SQLiteConnection - Room 2.8)
val callback = object : RoomDatabase.Callback() {
    override fun onCreate(connection: SQLiteConnection) {
        // ...
    }
}
  • @RawQuery डीएओ फ़ंक्शन बदलें: @RawQuery के साथ एनोटेट किए गए फ़ंक्शन के लिए, SupportSQLiteQuery के बजाय RoomRawQuery का इस्तेमाल करें:
// Before (SupportSQLiteQuery)
@Dao
interface UserDao {
    @RawQuery
    fun getUser(query: SupportSQLiteQuery): User
}

// After (RoomRawQuery)
@Dao
interface UserDao {
    @RawQuery
    suspend fun getUser(query: RoomRawQuery): User
}

रनटाइम के दौरान RoomRawQuery बनाया जा सकता है:

val query = RoomRawQuery(
    sql = "SELECT * FROM User WHERE id = ?",
    onBindStatement = { statement ->
        statement.bindInt(1, userId)
    }
)
  • लेन-देन वाले एपीआई बदलना: Android के लिए उपलब्ध withTransaction और runInTransaction ब्लॉक को withWriteTransaction या withReadTransaction से बदलें:
// Before (withTransaction)
db.withTransaction {
    // perform database operations
}

// After (withWriteTransaction - Room 2.8)
import androidx.room.withWriteTransaction

db.withWriteTransaction {
    // perform database operations
}

अगर आपको लेन-देन के कनेक्शन का सीधे तौर पर लो-लेवल ऐक्सेस चाहिए, तो useWriterConnection के साथ immediateTransaction का इस्तेमाल भी किया जा सकता है.

  • SupportSQLiteDatabase का सीधे तौर पर इस्तेमाल करने से बचें: अगर आपके पास ऐसा लेगसी कोड है जिसके लिए अब भी SupportSQLiteDatabase की ज़रूरत है और उसे अभी माइग्रेट नहीं किया जा सकता, तो androidx.room:room-sqlite-wrapper के साथ काम करने वाले आर्टफ़ैक्ट का इस्तेमाल करें:
dependencies {
    implementation("androidx.room:room-sqlite-wrapper:$roomVersion")
}

इसके बाद, अपने रूम डेटाबेस इंस्टेंस से SupportSQLiteDatabase पाने के लिए, getSupportWrapper का इस्तेमाल करें:

import androidx.room.support.getSupportWrapper

val legacyDb: SupportSQLiteDatabase = roomDatabase.getSupportWrapper()
  • SQLite ड्राइवर सेट करना: Room API के सभी इस्तेमाल को ड्राइवर एपीआई पर माइग्रेट करने के बाद, ड्राइवर को कॉन्फ़िगर करें. जैसे, BundledSQLiteDriver या AndroidSQLiteDriver. इसके लिए, अपने RoomDatabase बिल्डर में setDriver को कॉल करें:
import androidx.sqlite.driver.bundled.BundledSQLiteDriver

val db = Room.databaseBuilder<AppDatabase>(context, "db")
    .setDriver(BundledSQLiteDriver())
    .build()

फ़्लो पर आधारित अमान्य होने की ट्रैकिंग को अपनाना

Room 2.8 में InvalidationTracker.createFlow एपीआई उपलब्ध है. Room 2.x पर रहते हुए, InvalidationTracker.Observer के पुराने वर्शन से माइग्रेट करने के लिए, इस एपीआई का इस्तेमाल करें. इससे आपका कोडबेस, Room 3.0 के लिए तैयार हो जाता है. Room 3.0 में, Observer को पूरी तरह से हटा दिया गया है.

// Before (InvalidationTracker.Observer)
val observer = object : InvalidationTracker.Observer("User") {
    override fun onInvalidated(tables: Set<String>) {
        // reload user data
    }
}
db.invalidationTracker.addObserver(observer)

// After (createFlow - Room 2.8)
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
    userDao.getAllUsers()
}

Room 3.0 पर माइग्रेट करना

Room 2.x पर अपने ऐप्लिकेशन को मॉडर्न बनाने के बाद, Room 3.0 पर ट्रांज़िशन करने के लिए, आपको डिपेंडेंसी, पैकेज इंपोर्ट, और डेटाबेस कॉलबैक अपडेट करने होंगे.

डिपेंडेंसी और पैकेज इंपोर्ट अपडेट करना

  • अपने बिल्ड कॉन्फ़िगरेशन में, androidx.room डिपेंडेंसी को androidx.room3 से बदलें:
[versions]
room3 = "3.0.0" # Use the current Room 3.0 version

[libraries]
androidx-room3-runtime = { module = "androidx.room3:room3-runtime", version.ref = "room3" }
androidx-room3-compiler = { module = "androidx.room3:room3-compiler", version.ref = "room3" }
  • डिपेंडेंसी ब्लॉक को अपडेट करें:
dependencies {
    implementation(libs.androidx.room3.runtime)
    ksp(libs.androidx.room3.compiler)
}
  • अपने पैकेज इंपोर्ट अपडेट करें. import androidx.room.* को import androidx.room3.* से बदलें.

टाइप कन्वर्टर एपीआई अपडेट करना

Room 3.0, टाइप कन्वर्टर एपीआई के नाम बदलता है, ताकि कॉलम की वैल्यू को बदलने के लिए उनके इस्तेमाल के बारे में साफ़ तौर पर बताया जा सके. साथ ही, DAO के रिटर्न टाइप कन्वर्टर के साथ भ्रम से बचा जा सके.

अपने कोडबेस में, यहां दी गई एनोटेशन और फ़ंक्शन को अपडेट करें:

  • @TypeConverter का नाम बदलकर @ColumnTypeConverter करें.
  • @TypeConverters का नाम बदलकर @ColumnTypeConverters करें.
  • @ProvidedTypeConverter का नाम बदलकर @ProvidedColumnTypeConverter करें.
  • RoomDatabase.Builder.addTypeConverter का नाम बदलकर addColumnTypeConverter करें.

उदाहरण:

// Before
@ProvidedTypeConverter
class Converters {
  @TypeConverter
  fun fromTimestamp(value: Long?): Date? = ...
}

@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase()

val db = Room.databaseBuilder<AppDatabase>(...)
  .addTypeConverter(convertersInstance)
  .build()
// After
import androidx.room3.ColumnTypeConverter
import androidx.room3.ColumnTypeConverters
import androidx.room3.ProvidedColumnTypeConverter

@ProvidedColumnTypeConverter
class Converters {
  @ColumnTypeConverter
  fun fromTimestamp(value: Long?): Date? = ...
}

@Database(entities = [User::class], version = 1)
@ColumnTypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase()

val db = Room.databaseBuilder<AppDatabase>(...)
  .addColumnTypeConverter(convertersInstance)
  .build()

सस्पेंड फ़ंक्शन के लिए अपडेट कॉलबैक

Room 3.0 में, डेटाबेस के कॉलबैक और माइग्रेशन, SQLiteConnection और suspend फ़ंक्शन का इस्तेमाल करते हैं.

  • मैन्युअल तरीके से बनाई गई Migration क्लास अपडेट करें:
import androidx.sqlite.SQLiteConnection
import androidx.sqlite.async.executeSQL

val MIGRATION_1_2 = object : Migration(1, 2) {
    override suspend fun migrate(connection: SQLiteConnection) {
        connection.executeSQL(
            "ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL"
        )
    }
}
  • RoomDatabase.Callback को लागू करने के तरीके अपडेट करें:
val callback = object : RoomDatabase.Callback() {
    override suspend fun onCreate(connection: SQLiteConnection) {
        // ...
    }
}

डीएओ के रिटर्न टाइप कन्वर्टर रजिस्टर करना

Room 3.0 में, RxJava, LiveData, Guava, और Paging जैसे रिएक्टिव रिटर्न टाइप के लिए, आपको @DaoReturnTypeConverters का इस्तेमाल करके DAO रिटर्न टाइप कन्वर्टर रजिस्टर करने होंगे.

import androidx.room3.paging.PagingSourceDaoReturnTypeConverter

@Dao
@DaoReturnTypeConverters(PagingSourceDaoReturnTypeConverter::class)
interface UserDao {
    @Query("SELECT * FROM User")
    fun getAllPaginated(): PagingSource<Int, User>
}
  • पेजिंग (PagingSource): androidx.room3:room3-paging आर्टफ़ैक्ट से PagingSourceDaoReturnTypeConverter रजिस्टर करें.
  • RxJava (Observable, Flowable, Single, Maybe, Completable): androidx.room3:room3-rxjava3 आर्टफ़ैक्ट से RxDaoReturnTypeConverters रजिस्टर करें.
  • Guava (ListenableFuture): androidx.room3:room3-guava आर्टफ़ैक्ट से GuavaDaoReturnTypeConverter रजिस्टर करें.
  • LiveData (LiveData): androidx.room3:room3-livedata आर्टफ़ैक्ट से LiveDataDaoReturnTypeConverter रजिस्टर करें.

InvalidationTracker Observer को हटाने की पुष्टि करना

Room 3.0, InvalidationTracker.Observer और इससे जुड़े रजिस्ट्रेशन के तरीकों को पूरी तरह से हटा देता है. जैसे, addObserver और removeObserver.

अगर आपने पहले चरण में, को-रूटीन फ़्लो पर स्विच नहीं किया है, तो आपको Observer के सभी इस्तेमाल को createFlow पर माइग्रेट करना होगा:

val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
    userDao.getAllUsers()
}