مهاجرت (انتقال) دیتابیس همیشه باعث ترس من میشد جوری که انگار دارم بمب خنثی میکنم.با یه حرکت اشتباه انگار اپ من تو دستای کاربر منفجر میشد. اگه از کتابخونه 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
ذخیره شده یکی هستن✅
بنابراین اپ ما باز میشه و داده های کاربر هم مهاجرت کردن(منتقل شدن) به ورژن جدید :) ?
پایان بخش اول
ادامه دارد..
اگه این مطلب رو دوست داشتین میتونین با دیگران به اشتراک بذارین :)