रूम को Kotlin मल्टीप्लैटफ़ॉर्म पर माइग्रेट करें

इस दस्तावेज़ में बताया गया है कि लागू किए गए मौजूदा रूम को कैसे माइग्रेट किया जाता है जो Kotlin मल्टीप्लैटफ़ॉर्म (KMP) का इस्तेमाल करती है.

किसी मौजूदा Android कोड बेस में, रूम के इस्तेमाल को शेयर किए गए सामान्य केएमपी में माइग्रेट करना मॉड्यूल में इस्तेमाल किए जाने वाले रूम एपीआई के आधार पर कठिनाई का लेवल काफ़ी अलग हो सकता है या कोड बेस पहले से ही कोरूटीन का इस्तेमाल करता है. इस सेक्शन में कुछ दिशा-निर्देश और सलाह दी गई है रूम के इस्तेमाल को सामान्य मॉड्यूल में माइग्रेट करने की कोशिश करते समय.

सबसे पहले, इनके बीच के अंतर और कमियों के बारे में जान लेना ज़रूरी है रूम के Android वर्शन और KMP वर्शन के बीच की सुविधाएँ किस तरह इस्तेमाल किया जा सकता है. संक्षेप में, एक सफल माइग्रेशन में रीफ़ैक्टरिंग की ज़रूरत है SupportSQLite* एपीआई का इस्तेमाल और उन्हें SQLite ड्राइवर एपीआई से बदलना साथ ही, मूविंग रूम की जानकारी (@Database एनोटेट की गई क्लास, डीएओ, इकाइयों, और इसी तरह के एक जैसे).

जारी रखने से पहले, नीचे दी गई जानकारी को फिर से देखें:

अगले सेक्शन में, कैंपेन को सफल बनाने के लिए ज़रूरी अलग-अलग चरणों के बारे में बताया गया है माइग्रेशन.

SQLite के साथ काम करने वाले टूल से SQLite ड्राइवर पर माइग्रेट करना

androidx.sqlite.db के एपीआई सिर्फ़ Android पर इस्तेमाल किए जा सकते हैं. इनके लिए किसी भी तरीके का इस्तेमाल करना ज़रूरी है SQLite ड्राइवर एपीआई की मदद से रीफ़ैक्टरिंग किए गए. पुराने सिस्टम के साथ काम करने की सुविधा के लिए और तब तक RoomDatabase को SupportSQLiteOpenHelper.Factory (यानी कि कोई SQLiteDriver सेट नहीं है), तो रूम 'कंपैटबिलिटी मोड' में काम करता है जहां SQLite और SQLite ड्राइवर के साथ काम करने वाले एपीआई, दोनों उम्मीद के मुताबिक काम करते हैं. यह चालू करता है ताकि आपको अपने सभी Support SQLite को कन्वर्ट करने की ज़रूरत न पड़े एक ही बार में SQLite ड्राइवर का इस्तेमाल करता है.

नीचे दिए गए उदाहरण में SQLite और उनके SQLite के सामान्य इस्तेमाल के बारे में बताया गया है ड्राइवर के जैसे:

SQLite के साथ काम करता है (इससे)

बिना किसी नतीजे वाली क्वेरी एक्ज़ीक्यूट करें

val database: SupportSQLiteDatabase = ...
database.execSQL("ALTER TABLE ...")

क्वेरी को एक्ज़ीक्यूट करें, लेकिन उसमें कोई आर्ग्युमेंट न हो

val database: SupportSQLiteDatabase = ...
database.query("SELECT * FROM Pet").use { cursor ->
  while (cusor.moveToNext()) {
    // read columns
    cursor.getInt(0)
    cursor.getString(1)
  }
}

नतीजे और आर्ग्युमेंट के साथ क्वेरी एक्ज़ीक्यूट करें

database.query("SELECT * FROM Pet WHERE id = ?", id).use { cursor ->
  if (cursor.moveToNext()) {
    // row found, read columns
  } else {
    // row not found
  }
}

SQLite ड्राइवर (तक)

बिना किसी नतीजे वाली क्वेरी एक्ज़ीक्यूट करें

val connection: SQLiteConnection = ...
connection.execSQL("ALTER TABLE ...")

क्वेरी को एक्ज़ीक्यूट करें, लेकिन उसमें कोई आर्ग्युमेंट न हो

val connection: SQLiteConnection = ...
connection.prepare("SELECT * FROM Pet").use { statement ->
  while (statement.step()) {
    // read columns
    statement.getInt(0)
    statement.getText(1)
  }
}

नतीजे और आर्ग्युमेंट के साथ क्वेरी एक्ज़ीक्यूट करें

connection.prepare("SELECT * FROM Pet WHERE id = ?").use { statement ->
  statement.bindInt(1, id)
  if (statement.step()) {
    // row found, read columns
  } else {
    // row not found
  }
}

डेटाबेस ट्रांज़ैक्शन एपीआई सीधे SupportSQLiteDatabase में उपलब्ध हैं, जिनके साथ beginTransaction(), setTransactionSuccessful(), और endTransaction(). यह कमरा runInTransaction() का इस्तेमाल करके, कमरा बुक करने के लिए भी उपलब्ध है. इन्हें माइग्रेट करें SQLite ड्राइवर एपीआई का इस्तेमाल करता है.

SQLite के साथ काम करता है (इससे)

RoomDatabase का इस्तेमाल करके लेन-देन करें

val database: RoomDatabase = ...
database.runInTransaction {
  // perform database operations in transaction
}

SupportSQLiteDatabase का इस्तेमाल करके लेन-देन करें

val database: SupportSQLiteDatabase = ...
database.beginTransaction()
try {
  // perform database operations in transaction
  database.setTransactionSuccessful()
} finally {
  database.endTransaction()
}

SQLite ड्राइवर (तक)

RoomDatabase का इस्तेमाल करके लेन-देन करें

val database: RoomDatabase = ...
database.useWriterConnection { transactor ->
  transactor.immediateTransaction {
    // perform database operations in transaction
  }
}

SQLiteConnection का इस्तेमाल करके लेन-देन करें

val connection: SQLiteConnection = ...
connection.execSQL("BEGIN IMMEDIATE TRANSACTION")
try {
  // perform database operations in transaction
  connection.execSQL("END TRANSACTION")
} catch(t: Throwable) {
  connection.execSQL("ROLLBACK TRANSACTION")
}

कई तरह के कॉलबैक ओवरराइड को भी उनके ड्राइवर वाले वर्शन पर माइग्रेट करने की ज़रूरत होती है:

SQLite के साथ काम करता है (इससे)

माइग्रेशन सब-क्लास

object Migration_1_2 : Migration(1, 2) {
  override fun migrate(db: SupportSQLiteDatabase) {
    // ...
  }
}

अपने-आप होने वाले माइग्रेशन के स्पेसिफ़िकेशन की सब-क्लास

class AutoMigrationSpec_1_2 : AutoMigrationSpec {
  override fun onPostMigrate(db: SupportSQLiteDatabase) {
    // ...
  }
}

डेटाबेस कॉलबैक सब-क्लास

object MyRoomCallback : RoomDatabase.Callback {
  override fun onCreate(db: SupportSQLiteDatabase) {
    // ...
  }

  override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
    // ...
  }

  override fun onOpen(db: SupportSQLiteDatabase) {
    // ...
  }
}

SQLite ड्राइवर (तक)

माइग्रेशन सब-क्लास

object Migration_1_2 : Migration(1, 2) {
  override fun migrate(connection: SQLiteConnection) {
    // ...
  }
}

अपने-आप होने वाले माइग्रेशन के स्पेसिफ़िकेशन की सब-क्लास

class AutoMigrationSpec_1_2 : AutoMigrationSpec {
  override fun onPostMigrate(connection: SQLiteConnection) {
    // ...
  }
}

डेटाबेस कॉलबैक सब-क्लास

object MyRoomCallback : RoomDatabase.Callback {
  override fun onCreate(connection: SQLiteConnection) {
    // ...
  }

  override fun onDestructiveMigration(connection: SQLiteConnection) {
    // ...
  }

  override fun onOpen(connection: SQLiteConnection) {
    // ...
  }
}

खास जानकारी देने के लिए, SQLiteDatabase को SQLiteConnection से बदलें. ऐसा तब करें, जब RoomDatabase उपलब्ध नहीं है, जैसे कि कॉलबैक ओवरराइड (onMigrate, onCreate वगैरह). अगर RoomDatabase उपलब्ध है, तो इसे ऐक्सेस करें RoomDatabase.useReaderConnection और का इस्तेमाल करके डेटाबेस कनेक्शन के बजाय RoomDatabase.useWriterConnection RoomDatabase.openHelper.writtableDatabase.

ब्लॉक करने वाले डीएओ फ़ंक्शन को सस्पेंड फ़ंक्शन में बदलें

रूम का केएमपी वर्शन, I/O करने के लिए कोरूटीन पर निर्भर करता है कॉन्फ़िगर किए गए CoroutineContext पर कार्रवाइयां. इसका मतलब है कि फ़ंक्शन को निलंबित करने के लिए, ब्लॉक करने वाले किसी डीएओ फ़ंक्शन को माइग्रेट करना होगा.

डीएओ फ़ंक्शन को ब्लॉक करना (यहां से)

@Query("SELECT * FROM Todo")
fun getAllTodos(): List<Todo>

डीएओ फ़ंक्शन को निलंबित करना (तक)

@Query("SELECT * FROM Todo")
suspend fun getAllTodos(): List<Todo>

मौजूदा डीएओ ब्लॉक करने वाले फ़ंक्शन को सस्पेंड फ़ंक्शन में माइग्रेट किया जा सकता है यह मुश्किल है, अगर मौजूदा कोड बेस में पहले से कोरूटीन शामिल न हों. कोरूटीन का इस्तेमाल शुरू करने के लिए, Android में कोरूटीन देखें कोड बेस में रहेगा.

रिटर्न के रिस्पॉन्स टाइप को फ़्लो में बदलें

यह ज़रूरी नहीं है कि सभी डीएओ फ़ंक्शन, सस्पेंड फ़ंक्शन हों. वापस आने वाले डीएओ फ़ंक्शन LiveData या RxJava के Flowable जैसे रीऐक्टिव टाइप को कन्वर्ट नहीं किया जाना चाहिए का इस्तेमाल करें. हालांकि, LiveData जैसे कुछ टाइप केएमपी नहीं हैं साथ काम करता है. रिऐक्टिव रिटर्न टाइप वाले डीएओ फ़ंक्शन को कोरूटीन फ़्लो.

असंगत KMP प्रकार (से)

@Query("SELECT * FROM Todo")
fun getTodosLiveData(): LiveData<List<Todo>>

साथ काम करने वाला केएमपी टाइप (पाने वाले)

@Query("SELECT * FROM Todo")
fun getTodosFlow(): Flow<List<Todo>>

अपने ऐप्लिकेशन में फ़्लो का इस्तेमाल शुरू करने के लिए, Android की नीतियां देखें कोड बेस में एक्सपोर्ट करें.

कोरूटीन कॉन्टेक्स्ट सेट करें (ज़रूरी नहीं)

RoomDatabase को शेयर किए गए ऐप्लिकेशन के साथ कॉन्फ़िगर किया जा सकता है. हालांकि, ऐसा करना ज़रूरी नहीं है डेटाबेस करने के लिए RoomDatabase.Builder.setQueryExecutor() का इस्तेमाल करने वाले एक्ज़िक्यूटर कार्रवाइयां. प्लान बनाने वाले लोग केएमपी में शामिल नहीं हो सकते, इसलिए रूम की setQueryExecutor() सामान्य सोर्स के लिए एपीआई उपलब्ध नहीं है. इसके बजाय, RoomDatabase को यह करना चाहिए इसे CoroutineContext के साथ कॉन्फ़िगर किया जाना चाहिए. कॉन्टेक्स्ट सेट करने के लिए RoomDatabase.Builder.setCoroutineContext(), अगर कोई भी सेट नहीं है, तो RoomDatabase, डिफ़ॉल्ट रूप से Dispatchers.IO का इस्तेमाल करेगा.

SQLite ड्राइवर सेट करें

जब SQLite के इस्तेमाल से जुड़े फ़ंक्शन को SQLite ड्राइवर एपीआई पर माइग्रेट किया जाता है, तो ड्राइवर को RoomDatabase.Builder.setDriver का इस्तेमाल करके कॉन्फ़िगर करना होगा. कॉन्टेंट बनाने BundledSQLiteDriver ड्राइवर सुझाया गया है. इनके लिए Driver लागू करना देखें उपलब्ध ड्राइवर लागू करने के बारे में जानकारी.

कस्टम SupportSQLiteOpenHelper.Factory को इसका इस्तेमाल करके कॉन्फ़िगर किया गया RoomDatabase.Builder.openHelperFactory() केएमपी में काम नहीं करते हैं, कस्टम ओपन हेल्पर के ज़रिए उपलब्ध कराई गई सुविधाओं को फिर से लागू करना होगा. SQLite ड्राइवर के इंटरफ़ेस.

कमरे को एक जगह से दूसरी जगह ले जाने से जुड़े एलान

माइग्रेशन के ज़्यादातर चरण पूरे होने के बाद, रूम की जगह बदली जा सकती है की परिभाषाएं. ध्यान दें कि expect / actual रणनीतियां ये काम कर सकती हैं का इस्तेमाल रूम से जुड़ी परिभाषाओं को तेज़ी से मूव करने के लिए किया जा सकता है. उदाहरण के लिए, अगर सभी नहीं ब्लॉक करने वाले डीएओ फ़ंक्शन को सस्पेंड फ़ंक्शन पर माइग्रेट किया जा सकता है, इसलिए expect @Dao की व्याख्या करने वाला ऐसा इंटरफ़ेस बताएं जो कॉमन कोड में खाली हो, लेकिन में ब्लॉक करने वाले फ़ंक्शन मौजूद हैं.

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

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

@Dao
interface TodoDao {
    @Query("SELECT count(*) FROM TodoEntity")
    suspend fun count(): Int
}

@Dao
expect interface BlockingTodoDao
// shared/src/androidMain/kotlin/BlockingTodoDao.kt

@Dao
actual interface BlockingTodoDao {
    @Query("SELECT count(*) FROM TodoEntity")
    fun count(): Int
}