چگونه migration را در کتابخانه room پیاده‌سازی کنیم
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 5 دقیقه

چگونه migration را در کتابخانه room پیاده‌سازی کنیم

migration با پایگاه‌داده محلی در اپلیکیشن‌های موبایلی همیشه برای من شبیه به خنثی کردن یک بمب بوده. اما کتابخانه room این کار را تاحدی راحت‌تر کرده است.

پایگاه‌داده محلی براساس منطق تجاری شخصی(business logic) طراحی شده است، و فقط مسئله زمان است که قبل از اینکه با یک موقعیت مواجه شوید باید با انواع‌ مختلف مهاجرت آشنا شده باشید.

به‌جای اینکه دنبال راه حل باشید، سعی کنید یاد بگیرید migration چگونه در کتابخانه room کار می‌کند.  بنابراین می‌توانید کار درست را در جای درست انجام دهید.

با room، اگر شمای پایگاه‌داده را عوض کنید یا جدول جدید اضافه کنید و نسخه پایگاه‌داده را بروز نکنید، اپلیکیشن شما crash می‌کند. همچنین اگر نسخه پایگاه‌داده را بروز کنید و هیچ‌گونه قاعده migrationای برای آن قرار ندهید اپلیکیشن crash خواهد کرد.

درصورتی‌که قاعده‌ی migration مناسب نباشد، جداول پایگاه‌داده ازبین می‌رود، و کاربران شما داده‌هایشان را از دست خواهند داد.

می‌دانم که پردازش بسیار زیادی است، اما اگر یک بار یادبگیرید که migration چگونه کار می‌کند، مطمئن هستم که دیگر  احساسی نخواهید کرد.

پس بیایید شروع کنیم :)

تاریخچه

Room یک لایه‌ی انتزاعی روی پایگاه‌داده SQLite است، که یعنی room باید با SQLite توافق کند.

بنابراین، اگر می‌دانید که migration در SQLite چگونه کار می‌کند؛ می‌دانید که چرا نیاز دارید که کار های خاصی مانند بروزرسانی نسخه، قاعده migration، و موارد دیگری که در ادامه خواهید دید در پایگاه‌داده room انجام دهید.

پایگاه‌داده SQLite تغییرات شِما را با نسخه پایگاه‌داده کنترل می‌کند. برای خاص‌تر بودن، هر بار که جداول پایگاه‌داده حذف، اضافه یا شِمای آن‌ها تغییر کرد باید نسخه پایگاه‌داده اضافه شود.

بنابراین وقتی اپلیکیشن را باز می‌کنید، Sqlite ابتدا migration نسخه را مدنظر قرار می‌دهد، و سپس پایگاه‌داده را باز خواهد کرد.

موارد اجباری که در room باید با آن مقابله کنیم

بیایید که یک مثال ساده را در نظر بگیریم. ما یک پایگاه‌داده داریم که یک جدول به نام Movies که شامل سه ستون ID(primary key)، name و cover pic که نوع داده‌هایشان به ترتیب Int, String, String می‌باشد است.

@Entity(tableName = "MOVIES TABLE")
data class Movies(
        @PrimaryKey @ColumnInfo(name = "id")
        var id: Int,
        @ColumnInfo(name = "name")
        var name: String,
        @ColumnInfo(name = "coverpic")
        var coverPic: String,
      ) {
    constructor() : this("-1", "", "")
}

اکنون به یک ستون جدید در جدول movies نیاز داریم، بنابراین یک متغییر جدید در کلاس movies همانند زیر اضافه می‌کنیم.

@Entity(tableName = "MOVIES TABLE")
data class Movies(
        @PrimaryKey @ColumnInfo(name = "id")
        var id: Int,
        @ColumnInfo(name = "name")
        var name: String,
        @ColumnInfo(name = "coverpic")
        var coverPic: String,
        @ColumnInfo(name = "rating")
        var rating: String
      ) {
    constructor() : this("-1", "", "","0")
}

مورد اول نسخه بروزرسانی نشده است

اکنون کار شما با تغییرات پایگاه‌داده تمام شده است و اپلیکیشن را اجرا می‌کنید، حالا زمانی که می‌خواهید به room دسترسی داشته باشید به خطایی همانند زیر برمی‌خورید.

java.lang.IllegalStateException: Room cannot verify the data integrity.
Looks like you've changed schema but forgot to update the version number. 
You can simply fix this by increasing the version number.

اکنون room سعی در شناسایی شناسه پایگاه‌داده با مقایسه با شناسه‌ی hash شده‌ی نسخه کنونی، که در room_master_table ذخیره شده است. اما هیچ‌گونه شناسه‌‌ی hash شده‌ای وجود ندارد، بنابراین اپلیکیشن با illegalStateException کرش خواهد کرد.

حال شما علت اولین crash را می‌دانید، پس بیاید این رو داخل یک چک لیست قرار بدیم که در نسخه از اپلیکیشن آن را بررسی کنیم.

ما می‌توانیم این مشکل را تنها با بروزرسانی نسخه‌ی پایگاه‌داده در کلاس پایگاه‌داده و یا کلاسی که از RoomDatabase() ارث برده است رفع کنیم.

@Database(entities = arrayOf(DashboardCircularCategory::class,
                             version = 2)
@TypeConverters(Converters::class)
abstract class ApplicationDatabase : RoomDatabase()  {

مورد دوم هیچگونه migration ای ارائه نشده است

اکنون نسخه پایگاه‌داده را افزایش داده‌ایم و و سعی می‌کنیم به پایگاه‌داده دسترسی داشته‌ باشیم، اپلیکیشن همانند زیر با crash مواجه می‌شود.

java.lang.IllegalStateException: A migration from 1 to 2 was required but not found. 
Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) 
or allow for destructive migrations via one of the RoomDatabase.Builder.
fallbackToDestructiveMigration* methods.

Romm یک کلاس به نام Migration دارد، که با تغییرات در شِما توافق کرده است، که مسئول بروزرسانی SQLite است. بنابراین اگر هر تغییری در شِما ایجاد شود و هیچگونه migrationای ارائه نشود، اپلیکیشن crash خواهد کرد.

مورد سوم مهاجرت مخرب

این نوع خطا کاملا واضح است، ما باید یا مهاجرت را ارائه کنیم و یا از fallbackToDestructiveMigration در سازنده‌ی پایگاه‌داده room استفاده کنیم. برای استفاده ساده‌تر، بیایید مانند زیر از آن استفاده کنیم:

Room.databaseBuilder(context.getApplicationContext(),
        ApplicationDatabase::class.java, "applicationDatabase.db")
        .fallbackToDestructiveMigration()
        .build()

بنابراین وقتی که می‌خواهید به پایگاه‌داده دسترسی داشته باشید، همانطور که نسخه را ارتقا داده‌اید، room را در migration بررسی می‌کند. هیچ migrationای پیدا نمی‌کند، بنابراین حالت fallbackToDestructiveMigration همه‌ی جدول‌ها را حذف می‌کند و شناسه‌ی hash شده را اضافه می‌کند.

نتیجه این خواهد بود که اپلیکیشن crash نمی‌کند ولی کاربر همه‌ی داده‌هایش را از دست ‌می‌دهد.

مهاجرت همراه با تغییر شِما

بیایید موقعیتی را در نظر بگیریم که نیاز داریم داده‌ها را نگه داریم و همچنین شِمای جدیدی را اضافه کنیم. اینجاست که مفهوم migration آشکار می‌شود.

بیایید کارهایی که نیاز به انجام است را قدم به قدم انجام دهیم.

1- افزایش نسخه‌ی پایگاه‌داده به 3

@Database(entities = arrayOf(DashboardCircularCategory::class,
                             version = 2)
@TypeConverters(Converters::class)
abstract class ApplicationDatabase : RoomDatabase() 

2- ایجاد migration از نسخه 2 به 3، که شامل همه‌ی تغییراتی که از نسخه 2 به 3 داشته‎‌اید می‌باشد.

val MIGRATION_2_3: Migration = object : Migration(2, 3) {
    override fun migrate(database: SupportSQLiteDatabase) 
        database.execSQL("ALTER TABLE `Movies`"
                + " ADD COLUMN rating TEXT")
    }
}

3- migration را همانند زیر به سازنده پایگاه‌داده‌ی room اضافه کنید.

Room.databaseBuilder(context.getApplicationContext(),
        ApplicationDatabase::class.java, "applicationDatabase.db")
        .addMigrations(MIGRATION_2_3)
        .build()

اکنون، اگر سعی در دسترسی به پایگاه‌داده room داشته باشد:

  1. تغییرات بر روی جدول اعمال می‌شود
  2. Identity_hash بروزرسانی می‌شود
  3. بعد از آن سعی می‌شود پایگاه‌داده باز شود. بخاطر آنکه شناسه‌ی hash شده نسخه کنونی و نسخه ذخیره شده یکی هستند. بنابراین مشکلی ایجاد نخواهد شد.

نتیجه 

در این مقاله سعی شد مواردی که در migration کردن در پایگاه‌داده room وجود دارد بررسی شود امیدوارم براتون مفید واقع شده باشد.

منبع

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

/@pouryasharifi78
پوریا شریفی
توسعه‌دهنده‌ی اندروید

ابتدا که با برنامه‌نویسی آشنا شدم به سمت php و طراحی وب رفتم، بعد از اون به توسعه‌ی اندروید علاقه‌مند شدم و تقریبا ۲ سال است که مشغول به برنامه‌نویسی اندروید هستم، همچنین عاشق یادگیری چیزهای جدید هستم.

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

پوریا شریفی

توسعه‌دهنده‌ی اندروید