سعید غفاری
سعید غفاری
خواندن ۴ دقیقه·۴ ماه پیش

مهاجرت از sqlite به room در اندروید

سلام شاید شما هم یه برنامه قدیمی دارید که تو یه مارکت منتشر کردید و از ابزار های قدیمی توی برنامه تون استفاده کردین، مثلا با جاوا کد زدین از volley استفاده کردین یا کلی چیز دیگه که بعضی از اینا رو میشه به راحتی تغییر داد .مثلا کد جدید که ریفکتور کردین رو با retrofit بزنین. اما تو بعضی موارد نیازه یه کارایی بکنین تا از ابزار جدید بشه استفاده کرد. یکی از همین موارد اینه که بخواین از دیتابیس sqlite به room مهاجرت کنین . تو این مورد باید حواستون باشه اطلاعات قدیمی کاربر بعد به روز رسانی حذف نشن یه وقت و یه عالمه فیدبک منفی نگیرین . مستقیما بریم سمت راه حل و اینکه جیکار باید بکنیم.

مرحله اول: مدل‌ها و DAOهای جدیدتون رو تو کاتلین تعریف کنید. به عنوان مثال:

//model

@Entity(tableName = &quotyour_table&quot) data class YourEntity( @PrimaryKey(autoGenerate = true) @ColumnInfo(name = &quotid&quot) var id: Int, @ColumnInfo(name = &quotname&quot) var name:String , @ColumnInfo(name = &quotphone&quot) var phone:String? )


//DAO

@Dao interface YourDao { @Query(&quotSELECT * FROM your_table&quot) fun getAll(): List<YourEntity> }

مرحله دوم: ایجاد کلاس RoomDatabase

نکته: برای نوشتن کد مهاجرت (Migration) از SQLite به Room، باید مطمئن شید که ساختار جدول و نام ستون‌ها تو هر دو نسخه یکسان هستند. با توجه به اسکیمای دیتابیس قدیمی و اینتیتی جدید، می‌تونید کد مهاجرت رو به صورت زیر بنویسید:

اگر از hilt به عنوان dependency injection استفاده می کنید کلاس AppDatabase خودتون را به شکل زیر provide کنید:

@Singleton @Provides fun provideDb(@ApplicationContext context: Context, ): AppDatabase { return Room.databaseBuilder(context, AppDatabase::class.java, AppDatabase.DATABASE_NAME) .allowMainThreadQueries() .fallbackToDestructiveMigration() .addMigrations(AppDatabase.MIGRATION_1_2) .build() }

و کلاس AppDataBase رو به شکل زیر بسازید:

@Database( entities = [ YourEntity::class ], version = 2 ) abstract class AppDatabase : RoomDatabase() { abstract fun yourDao(): YourDao companion object { //حواستون باشه اسم دیتابیس همون چیزی باشه که تو ورژن قبلی بود val DATABASE_NAME: String = &quotنام دیتابیس&quot val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { //پیاده سازی در ادامه توضیح داده خواهد شد } }


اما اگه از hilt استفاده نمی کنید کلاس AppDatabsae شما به این شکل میشه:


@Database(entities = [YourEntity::class], version = 2) abstract class AppDatabase : RoomDatabase() { abstract fun yourDao(): YourDao companion object { @Volatile private var INSTANCE: AppDatabase? = null fun getDatabase(context: Context): AppDatabase { return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, AppDatabase::class.java, //حواستون باشه اسم دیتابیس همون چیزی باشه که تو ورژن قبلی بود &quotنام دیتابیس&quot ).addMigrations(MIGRATION_1_2).build() INSTANCE = instance instance } } val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { //پیاده سازی در ادامه } }

نکته اول: آبجکتی که از نوع Migration ساختیم دوتا int توی کانسترکتور خودش می گیره که اولی نسخه قبلی دیتابیستونه و دومی نسخییه که الان داریم اگه این دوتا درست نباشن مهاجرت اتفاق نمیفته.


نکته دوم:کلمه کلیدی @Volatile برای تضمین این استفاده می‌شه که متغیر INSTANCE همیشه به‌روزترین مقدار خودش رو برای تمام نخ‌ها (threads) به اشتراک بگذاره. با این کار، از شرایط رقابتی (race conditions) جلوگیری می‌شه که ممکنه باعث شه چندین نمونه از کلاس AppDatabase ایجاد شه.


مرحله سوم : override کردن فانکشن migrate


تو این مرحله که تقریبا مرحله آخرمونه توی فانکشن migrate که override کردیم کوئری هی خودمون رو می نویسیم مثل این مثال :

override fun migrate(database: SupportSQLiteDatabase) { // Create the new table database.execSQL(&quot&quot&quotCREATE TABLE IF NOT EXISTS `your_new_table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `phone` TEXT ) &quot&quot&quot.trimIndent()) // Copy the data database.execSQL(&quot&quot&quot INSERT INTO `your_new_table` (`id`, `name`, `phone`) SELECT `id`, `name`, `phone` FROM `your_table` &quot&quot&quot.trimIndent()) // Remove the old table database.execSQL(&quotDROP TABLE `your_table`&quot) // Rename the new table to the old table name database.execSQL(&quotALTER TABLE `your_new_table` RENAME TO `your_table`&quot) }

خب کاری که کردیم اینه :

  • یه table جدید ساختیم با یه اسم جدید( مثلا به آخر همون اسم table قدیمی یه new اضافه کنید.)
  • اطلاعات table قدیمی رو کپی کردیم تو table جدید.
  • بعد table قدیمی رو پاک کردیم
  • در آخر اسم table جدید رو همونی کردیم که تو ورژن قبلی بود (حواستون به اسم table باشه با ورژن قدیمی متفاوت نباشه )

تمام شد به همین راحتی فقط یه مورد دیگه اضافه کنم

اگر table جدیدی به دیتابیستون اضافه شده تو متد migrate باید بسازیدش (فقط CREATE TABLE IF NOT EXISTS)

امیدوارم این مطلب براتون مفید باشه و لذت برده باشید.
تا دیداری دیگر بدرود...

sqliteroommigratesqlite room
شاید از این پست‌ها خوشتان بیاید