
مهاجرت (انتقال) دیتابیس همیشه باعث ترس من میشد جوری که انگار دارم بمب خنثی میکنم.با یه حرکت اشتباه انگار اپ من تو دستای کاربر منفجر میشد. اگه از کتابخونه Room برای کنترل عملیات دیتابیستون استفاده میکنین مهاجرت به آسونی فشردن یک کلید میمونه.
هنگام استفاده از Room اگر دیاگرامِ دیتابیس(database schema) رو عوض کنین ولی ورژن رو اپگرید نکنین اپ کرش میکنه اگرهم ورژن رو اپگریت کنین اما هیچ مهاجرتی رو فراهم نکنین یا اپ کرش میکنه یا جدول های دیتابیس پاک میشن و کاربرانتون تمام داده هاشون رو از دست میدن. با فکر کردن به اینکه چطوری مهاجرت روم رو پیاده سازی کنین وقتتون رو هدر ندین به جاش ساختار داخلی روم رو درک کنین تا با اعتماد به نفس دیتابیستون رو اپدیت کنین.
دیتابیس های SQLite تغییرات دیاگرام رو با کمک ورژن بندی دیتایس کنترل میکنن. به طور خلاصه هربار که دیاگرام خودتون رو با اضافه کردن ٬ حذف یا تغییرات جدول ها ٬ تغییر میدین شما ورژن دیتابیس رو افزایش میدین و متد SQLiteOpenHelper.onUpgrade رو اپدیت فراخونی میکنین. به این ترتیب به SQLite میگین که برای رفتن از یه ورژن قدیمی به ورژن جدید به چه چیزی نیاز داره.
همچنین این اولین فراخونی هست که اجرا میشه وقتی که اپ شما با دیتابیس شروع میکنه به تعامل
اس کیو ابتدا سغی میکن که اپگریت های ورزن رو هندل کنه و تنها اون موقع است که دیتابیس رو اجرا میکنه.
آنچه که روم انجام میده
روم یه لایه انتزاعی(Abstrack) فراهم میکنه تا کار مهاجرت SQLite رو در قالب کلاس Migrationآسون کنه.
کلاس Migration کارهایی که برای مهاجرت از یک ورژن به ورژن دیگه لازمه رو مشخص میکنه.
روم از پیاده سازی شخصی خودش از SQLiteOpenHelper استفاده میکنه و در متد onUpgrade مهاجرت هایی که شما مشخص کردین رو راه اندازی میکنه.
این ها اتفاقاتی است که وقتی برای بار اول به دیتایس دسترسی پیدا میکنین :
1. دیتابیس روم ساخته شده
2. متد SQLiteOpenHelper.onUpgrade فراخونی میشه و Room مهاجرت رو راه اندازی میکنه
3. دیتابیس باز میشه
اگه شما مهاجرتی تعریف نکرده باشین اما ورژن دیتابیس رو بالا ببرین بسته به موقعیت هایی که در زیر در نظر گرفتیم ممکنه اپ کرش کنه یا داده ها پاک بشه :
یک نقش مهم در داخل مهاجرت رو یک identity hash String بازی میکنه که Room از اون برای یکتا سازی هویت هر ورژن دیتابیس استفاده میکنه. این هش هویتی(identity hash) برای ورژن کنونی در جدول پیکربندی در دیتابیس شما نگهداری میشه که روم اون رو مدیریت میکنه. بنابراین تعجب نکنین اگه یه وقت جدول room_master_table توی دیتابیس دیدین.
یه مثال ساده : ما یک جدول users داریم با دو ستون :
جدول users بخشی از دیتابیس ما با ورژن 1 که با استفاده از SQLiteDatabase API پیاده سازی شده است.
درنظر میگیریم که کاربران ما در حال حاضر از این ورژن استفاده میکنن و شما میخواین از Room استفاده کنید. بیاین ببینیم که Room چطوری چند سناریو رو هندل میکنه :
مهاجرت از SQLite API به Room
بیاین فرض کنیم که همه کلاس های Room رو ساختیم و تمرکزمون رو میذاریم روی کلاس UsersDatabaseکه از RoomDatabase اکستند میشه :
@Database(entities = {User.class}, version = 1) public abstract class UsersDatabase extends RoomDatabase
اگر ورژن دیتابیس رو بدون تغییر نگه داشته باشیم Room در بکگراند کارهای زیر رو انجام میده :
گام اول : تلاش برای باز کردن دیتابیس
هویت دیتابیس رو با مقایسه هش هویت ورژن کنونی با ورژنی که درجدول room_master_table ذخیره شده چک میکنه. اما از اونجا که هیچ هش هویتیای ذخیره نشده اپ با خطای زیر کرش میکنه :
IllegalStateException ❌
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.
اگر شما دیاگرام دیتابیس رو تغییر بدین اما ورژن رو اپدیت نکنین روم همیشه یه خطایIllegalStateExceptionنشون میده.
بیاین خطا رو جدی بگیریم و ورژن دیتابیس رو افزایش بدیم :
@Database(entities = {User.class}, version = 2) public abstract class UsersDatabase extends RoomDatabase
تو این حالت اگه دوباره اپ رو اجرا کنیم روم کارهای زیر رو انجام میده :
گام اول : تلاش برای اپگریت ورژن 1( که روی دستگاه نصب شده) به ورژن 2
IllegalStateException کرش میکنه❌ java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables.
اگه مهاجرتی فراهم نکرده باشین روم خطای IllegalStateException رو نشون میده(پرتاپ میکنه).
fallbackToDestructiveMigration فعال شده ـــ دیتابیس پاک میشهاگه نمیخواین هیچ مهاجرتی فراهم کنین و حتما میخواین که وقتی ورژن دیتابیس بالا میره دیتابیستون پاک بشه متد fallbackToDestructiveMigration رو در بیلدر دیتابیس فراخونی کنین :
database = Room.databaseBuilder(context.getApplicationContext(), UsersDatabase.class, "Sample.db") .fallbackToDestructiveMigration() .build();
حالا وقتی شما برنامه رو دوباره اجرا میکنین روم کارهای زیر رو انجام میده :
گام اول : تلاش برای اپگریت ورژن1 (که روی دستگاه نصب شده) به ورژن 2
fallbackToDestructiveMigration رو فراخونی کردیم جدول ها پاک شدن و identity_hash در جدول نوشته شد.گام دوم : تلاش برای باز کردن دیتابیس
room_master_table ذخیره شده یکی هستن✅الان دیگه اپ ما کرش نمیکنه اما همه داده ها رو ازدست دادیم. بنابراین از قبل مطمئن باشین که این روش رو میخواین انجام بدین.
برای حفظ داده های کاربران ما باید یک مهاجرت پیاده سازی کنیم. از اونجایی که دیاگرام تغییری نمیکنه فقط نیاز داریم تا یک مهاجرت خالی رو پیاده سازی کنیم و به Room بگیم که ازش استفاده کنه.
@Database(entities = {User.class}, version = 2) public abstract class UsersDatabase extends RoomDatabase {…static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(SupportSQLiteDatabase database) { // Since we didn't alter the table, there's nothing else to do here. } };…database = Room.databaseBuilder(context.getApplicationContext(), UsersDatabase.class, "Sample.db") .addMigrations(MIGRATION_1_2) .build();
حالا وقتی اپ رو اجرا میکنیم روم کارهای زیر رو انجام میده :
گام اول : تلاش برای اپگریت ورژن 1(که روی دستگاه نصب شده) به ورژن 2
room_master_table ✅گام دوم : تلاش برای باز کردن دیتابیس
هش هویتی ورژن کنونی با هش هویتیای که در جدول room_master_table ذخیره شده یکی هستن✅
بنابراین اپ ما باز میشه و داده های کاربر هم مهاجرت کردن(منتقل شدن) به ورژن جدید :) ?
پایان بخش اول
ادامه دارد..
اگه این مطلب رو دوست داشتین میتونین با دیگران به اشتراک بذارین :)