আপনার অ্যাপে ফিচার যোগ ও পরিবর্তন করার সাথে সাথে, এই পরিবর্তনগুলো প্রতিফলিত করার জন্য আপনার Room এনটিটি ক্লাস এবং অন্তর্নিহিত ডেটাবেস টেবিলগুলো সংশোধন করতে হবে। যখন কোনো অ্যাপ আপডেটের ফলে ডেটাবেস স্কিমা পরিবর্তিত হয়, তখন ডিভাইসের ডেটাবেসে আগে থেকে থাকা ব্যবহারকারীর ডেটা সংরক্ষণ করা গুরুত্বপূর্ণ।
রুম ইনক্রিমেন্টাল মাইগ্রেশনের জন্য স্বয়ংক্রিয় এবং ম্যানুয়াল উভয় বিকল্পই সমর্থন করে। বেশিরভাগ সাধারণ স্কিমা পরিবর্তনের জন্য স্বয়ংক্রিয় মাইগ্রেশন কাজ করে, কিন্তু আরও জটিল পরিবর্তনের জন্য আপনাকে ম্যানুয়ালি মাইগ্রেশন পাথ নির্ধারণ করতে হতে পারে।
স্বয়ংক্রিয় স্থানান্তর
দুটি ডাটাবেস সংস্করণের মধ্যে স্বয়ংক্রিয় মাইগ্রেশন ঘোষণা করতে, @Database এর autoMigrations প্রপার্টিতে একটি @AutoMigration অ্যানোটেশন যোগ করুন:
কোটলিন
// Database class before the version update. @Database( version = 1, entities = [User::class] ) abstract class AppDatabase : RoomDatabase() { ... } // Database class after the version update. @Database( version = 2, entities = [User::class], autoMigrations = [ AutoMigration (from = 1, to = 2) ] ) abstract class AppDatabase : RoomDatabase() { ... }
জাভা
// Database class before the version update. @Database( version = 1, entities = {User.class} ) public abstract class AppDatabase extends RoomDatabase { ... } // Database class after the version update. @Database( version = 2, entities = {User.class}, autoMigrations = { @AutoMigration (from = 1, to = 2) } ) public abstract class AppDatabase extends RoomDatabase { ... }
স্বয়ংক্রিয় মাইগ্রেশন স্পেসিফিকেশন
যদি Room অস্পষ্ট স্কিমা পরিবর্তন শনাক্ত করে এবং আরও ইনপুট ছাড়া একটি মাইগ্রেশন প্ল্যান তৈরি করতে না পারে, তবে এটি একটি কম্পাইল-টাইম ত্রুটি দেখায় এবং আপনাকে একটি AutoMigrationSpec প্রয়োগ করতে বলে। সাধারণত, এটি তখন ঘটে যখন একটি মাইগ্রেশনে নিম্নলিখিতগুলির মধ্যে একটি অন্তর্ভুক্ত থাকে:
- টেবিল মুছে ফেলা বা নাম পরিবর্তন করা।
- একটি কলাম মুছে ফেলা বা তার নাম পরিবর্তন করা।
মাইগ্রেশন পাথ সঠিকভাবে তৈরি করার জন্য Room-এর প্রয়োজনীয় অতিরিক্ত তথ্য সরবরাহ করতে আপনি AutoMigrationSpec ব্যবহার করতে পারেন। আপনার RoomDatabase ক্লাসে AutoMigrationSpec ইমপ্লিমেন্ট করে এমন একটি স্ট্যাটিক ক্লাস সংজ্ঞায়িত করুন এবং এটিকে নিম্নলিখিত এক বা একাধিক অ্যানোটেশন দিয়ে চিহ্নিত করুন:
স্বয়ংক্রিয় মাইগ্রেশনের জন্য AutoMigrationSpec ইমপ্লিমেন্টেশন ব্যবহার করতে, সংশ্লিষ্ট @AutoMigration অ্যানোটেশনে spec প্রপার্টিটি সেট করুন:
কোটলিন
@Database( version = 2, entities = [User::class], autoMigrations = [ AutoMigration ( from = 1, to = 2, spec = AppDatabase.MyAutoMigration::class ) ] ) abstract class AppDatabase : RoomDatabase() { @RenameTable(fromTableName = "User", toTableName = "AppUser") class MyAutoMigration : AutoMigrationSpec ... }
জাভা
@Database( version = 2, entities = {AppUser.class}, autoMigrations = { @AutoMigration ( from = 1, to = 2, spec = AppDatabase.MyAutoMigration.class ) } ) public abstract class AppDatabase extends RoomDatabase { @RenameTable(fromTableName = "User", toTableName = "AppUser") static class MyAutoMigration implements AutoMigrationSpec { } ... }
স্বয়ংক্রিয় মাইগ্রেশন সম্পন্ন হওয়ার পর যদি আপনার অ্যাপের আরও কোনো কাজ করার প্রয়োজন হয়, তাহলে আপনি onPostMigrate() ইমপ্লিমেন্ট করতে পারেন। আপনি যদি আপনার AutoMigrationSpec এ এই মেথডটি ইমপ্লিমেন্ট করেন, তাহলে স্বয়ংক্রিয় মাইগ্রেশন সম্পন্ন হওয়ার পর Room এটিকে কল করে।
ম্যানুয়াল মাইগ্রেশন
যেসব ক্ষেত্রে মাইগ্রেশনে জটিল স্কিমা পরিবর্তন জড়িত থাকে, সেখানে Room স্বয়ংক্রিয়ভাবে একটি উপযুক্ত মাইগ্রেশন পাথ তৈরি করতে সক্ষম নাও হতে পারে। উদাহরণস্বরূপ, যদি আপনি একটি টেবিলের ডেটা দুটি টেবিলে ভাগ করার সিদ্ধান্ত নেন, তবে এই বিভাজনটি কীভাবে করতে হবে তা Room বুঝতে পারে না। এই ধরনের ক্ষেত্রে, আপনাকে অবশ্যই একটি Migration ক্লাস ইমপ্লিমেন্ট করার মাধ্যমে ম্যানুয়ালি একটি মাইগ্রেশন পাথ নির্ধারণ করতে হবে।
একটি Migration ক্লাস Migration.migrate() মেথডটি ওভাররাইড করার মাধ্যমে startVersion এবং endVersion মধ্যে একটি মাইগ্রেশন পাথ সুস্পষ্টভাবে নির্ধারণ করে। addMigrations() মেথডটি ব্যবহার করে আপনার ডেটাবেস বিল্ডারে Migration ক্লাসগুলো যোগ করুন:
কোটলিন
val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, `name` TEXT, " + "PRIMARY KEY(`id`))") } } val MIGRATION_2_3 = object : Migration(2, 3) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE Book ADD COLUMN pub_year INTEGER") } } Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name") .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build()
জাভা
static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, " + "`name` TEXT, PRIMARY KEY(`id`))"); } }; static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE Book " + " ADD COLUMN pub_year INTEGER"); } }; Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name") .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();
যখন আপনি আপনার মাইগ্রেশন পাথ নির্ধারণ করেন, তখন আপনি কিছু ভার্সনের জন্য স্বয়ংক্রিয় মাইগ্রেশন এবং অন্যগুলোর জন্য ম্যানুয়াল মাইগ্রেশন ব্যবহার করতে পারেন। যদি আপনি একই ভার্সনের জন্য একটি স্বয়ংক্রিয় মাইগ্রেশন এবং একটি ম্যানুয়াল মাইগ্রেশন উভয়ই নির্ধারণ করেন, তাহলে Room ম্যানুয়াল মাইগ্রেশনটি ব্যবহার করবে।
পরীক্ষার স্থানান্তর
মাইগ্রেশন প্রায়শই জটিল হয়, এবং ভুলভাবে সংজ্ঞায়িত মাইগ্রেশনের কারণে আপনার অ্যাপ ক্র্যাশ করতে পারে। আপনার অ্যাপের স্থিতিশীলতা বজায় রাখতে, আপনার মাইগ্রেশনগুলো পরীক্ষা করুন। স্বয়ংক্রিয় এবং ম্যানুয়াল উভয় প্রকার মাইগ্রেশনের টেস্টিং প্রক্রিয়ায় সহায়তা করার জন্য Room একটি room-testing Maven আর্টিফ্যাক্ট প্রদান করে। এই আর্টিফ্যাক্টটি কাজ করার জন্য, আপনাকে প্রথমে আপনার ডাটাবেসের স্কিমা এক্সপোর্ট করতে হবে।
স্কিমা রপ্তানি করুন
Room কম্পাইল করার সময় আপনার ডাটাবেসের স্কিমা তথ্য একটি JSON ফাইলে এক্সপোর্ট করতে পারে। এক্সপোর্ট করা JSON ফাইলগুলো আপনার ডাটাবেসের স্কিমা ইতিহাসকে উপস্থাপন করে। এই ফাইলগুলো আপনার ভার্সন কন্ট্রোল সিস্টেমে সংরক্ষণ করুন, যাতে Room পরীক্ষার উদ্দেশ্যে ডাটাবেসের নিম্নতর ভার্সন তৈরি করতে পারে এবং স্বয়ংক্রিয় মাইগ্রেশন জেনারেশন সক্ষম করতে পারে।
Room Gradle Plugin ব্যবহার করে স্কিমা অবস্থান নির্ধারণ করুন
আপনি যদি Room সংস্করণ 2.6.0 বা তার উচ্চতর সংস্করণ ব্যবহার করেন, তাহলে আপনি Room Gradle Plugin প্রয়োগ করতে পারেন এবং স্কিমা ডিরেক্টরি নির্দিষ্ট করার জন্য room এক্সটেনশনটি ব্যবহার করতে পারেন।
গ্রুভি
plugins {
id 'androidx.room'
}
room {
schemaDirectory "$projectDir/schemas"
}
কোটলিন
plugins {
id("androidx.room")
}
room {
schemaDirectory("$projectDir/schemas")
}
যদি আপনার ডাটাবেস স্কিমা ভ্যারিয়েন্ট, ফ্লেভার বা বিল্ড টাইপের উপর ভিত্তি করে ভিন্ন হয়, তবে আপনাকে অবশ্যই schemaDirectory() কনফিগারেশনটি একাধিকবার ব্যবহার করে ভিন্ন ভিন্ন অবস্থান নির্দিষ্ট করতে হবে, যেখানে প্রতিটির প্রথম আর্গুমেন্ট হিসেবে একটি variantMatchName থাকবে। প্রতিটি কনফিগারেশন ভ্যারিয়েন্টের নামের সাথে সাধারণ তুলনার ভিত্তিতে এক বা একাধিক ভ্যারিয়েন্টকে মেলাতে পারে।
নিশ্চিত করুন যে এগুলো সম্পূর্ণ এবং সমস্ত ভ্যারিয়েন্টকে অন্তর্ভুক্ত করে। অন্য কোনো কনফিগারেশনের সাথে মেলে না এমন ভ্যারিয়েন্টগুলো পরিচালনা করার জন্য আপনি variantMatchName ছাড়াই একটি schemaDirectory() অন্তর্ভুক্ত করতে পারেন। উদাহরণস্বরূপ, demo এবং full দুটি বিল্ড ফ্লেভার এবং debug এবং release দুটি বিল্ড টাইপ সহ একটি অ্যাপে, নিম্নলিখিতগুলো বৈধ কনফিগারেশন:
গ্রুভি
room {
// Applies to 'demoDebug' only
schemaDirectory "demoDebug", "$projectDir/schemas/demoDebug"
// Applies to 'demoDebug' and 'demoRelease'
schemaDirectory "demo", "$projectDir/schemas/demo"
// Applies to 'demoDebug' and 'fullDebug'
schemaDirectory "debug", "$projectDir/schemas/debug"
// Applies to variants that aren't matched by other configurations.
schemaDirectory "$projectDir/schemas"
}
কোটলিন
room {
// Applies to 'demoDebug' only
schemaDirectory("demoDebug", "$projectDir/schemas/demoDebug")
// Applies to 'demoDebug' and 'demoRelease'
schemaDirectory("demo", "$projectDir/schemas/demo")
// Applies to 'demoDebug' and 'fullDebug'
schemaDirectory("debug", "$projectDir/schemas/debug")
// Applies to variants that aren't matched by other configurations.
schemaDirectory("$projectDir/schemas")
}
অ্যানোটেশন প্রসেসর অপশন ব্যবহার করে স্কিমা অবস্থান সেট করুন
আপনি যদি Room-এর 2.5.2 বা তার নিম্ন সংস্করণ ব্যবহার করেন, অথবা যদি Room Gradle Plugin ব্যবহার না করেন, তাহলে room.schemaLocation অ্যানোটেশন প্রসেসর অপশনটি ব্যবহার করে স্কিমা লোকেশন সেট করুন।
এই ডিরেক্টরির ফাইলগুলি কিছু গ্রেডল টাস্কের ইনপুট এবং আউটপুট হিসাবে ব্যবহৃত হয়। ইনক্রিমেন্টাল এবং ক্যাশড বিল্ডের সঠিকতা ও পারফরম্যান্সের জন্য, আপনাকে অবশ্যই গ্রেডলের CommandLineArgumentProvider ব্যবহার করে এই ডিরেক্টরিটি সম্পর্কে গ্রেডলকে জানাতে হবে।
প্রথমে, নিচে দেখানো RoomSchemaArgProvider ক্লাসটি আপনার মডিউলের Gradle বিল্ড ফাইলে কপি করুন। স্যাম্পল ক্লাসের asArguments() মেথডটি KSP তে room.schemaLocation=${schemaDir.path} পাস করে। আপনি যদি KAPT এবং javac ব্যবহার করেন, তাহলে এই ভ্যালুটি পরিবর্তন করে -Aroom.schemaLocation=${schemaDir.path} করুন।
গ্রুভি
class RoomSchemaArgProvider implements CommandLineArgumentProvider {
@InputDirectory
@PathSensitive(PathSensitivity.RELATIVE)
File schemaDir
RoomSchemaArgProvider(File schemaDir) {
this.schemaDir = schemaDir
}
@Override
Iterable<String> asArguments() {
// Note: If you're using KAPT and javac, change the line below to
// return ["-Aroom.schemaLocation=${schemaDir.path}".toString()].
return ["room.schemaLocation=${schemaDir.path}".toString()]
}
}
কোটলিন
class RoomSchemaArgProvider(
@get:InputDirectory
@get:PathSensitive(PathSensitivity.RELATIVE)
val schemaDir: File
) : CommandLineArgumentProvider {
override fun asArguments(): Iterable<String> {
// Note: If you're using KAPT and javac, change the line below to
// return listOf("-Aroom.schemaLocation=${schemaDir.path}").
return listOf("room.schemaLocation=${schemaDir.path}")
}
}
এরপর, নির্দিষ্ট স্কিমা ডিরেক্টরির সাথে RoomSchemaArgProvider ব্যবহার করার জন্য কম্পাইল অপশনগুলো কনফিগার করুন:
গ্রুভি
// For KSP, configure using KSP extension:
ksp {
arg(new RoomSchemaArgProvider(new File(projectDir, "schemas")))
}
// For javac or KAPT, configure using android DSL:
android {
...
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
compilerArgumentProviders(
new RoomSchemaArgProvider(new File(projectDir, "schemas"))
)
}
}
}
}
কোটলিন
// For KSP, configure using KSP extension:
ksp {
arg(RoomSchemaArgProvider(File(projectDir, "schemas")))
}
// For javac or KAPT, configure using android DSL:
android {
...
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
compilerArgumentProviders(
RoomSchemaArgProvider(File(projectDir, "schemas"))
)
}
}
}
}
একটি একক মাইগ্রেশন পরীক্ষা করুন
আপনার মাইগ্রেশনগুলো পরীক্ষা করার আগে, Room থেকে androidx.room:room-testing Maven আর্টিফ্যাক্টটি আপনার টেস্ট ডিপেন্ডেন্সিতে যোগ করুন এবং এক্সপোর্ট করা স্কিমার অবস্থানটিকে একটি অ্যাসেট ফোল্ডার হিসেবে যুক্ত করুন:
বিল্ড.গ্রেডল
গ্রুভি
android { ... sourceSets { // Adds exported schema location as test app assets. androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } } dependencies { ... androidTestImplementation "androidx.room:room-testing:2.8.4" }
কোটলিন
android { ... sourceSets { // Adds exported schema location as test app assets. getByName("androidTest").assets.srcDir("$projectDir/schemas") } } dependencies { ... testImplementation("androidx.room:room-testing:2.8.4") }
টেস্টিং প্যাকেজটিতে একটি MigrationTestHelper ক্লাস রয়েছে, যা এক্সপোর্ট করা স্কিমা ফাইলগুলো পড়তে পারে। প্যাকেজটি JUnit4-এর TestRule ইন্টারফেসও ইমপ্লিমেন্ট করে, ফলে এটি তৈরি করা ডাটাবেসগুলোও পরিচালনা করতে পারে।
নিম্নলিখিত উদাহরণটি একটি একক মাইগ্রেশনের জন্য একটি পরীক্ষা প্রদর্শন করে:
কোটলিন
@RunWith(AndroidJUnit4::class) class MigrationTest { private val TEST_DB = "migration-test" @get:Rule val helper: MigrationTestHelper = MigrationTestHelper( InstrumentationRegistry.getInstrumentation(), MigrationDb::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory() ) @Test @Throws(IOException::class) fun migrate1To2() { var db = helper.createDatabase(TEST_DB, 1).apply { // Database has schema version 1. Insert some data using SQL queries. // You can't use DAO classes because they expect the latest schema. execSQL(...) // Prepare for the next version. close() } // Re-open the database with version 2 and provide // MIGRATION_1_2 as the migration process. db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2) // MigrationTestHelper automatically verifies the schema changes, // but you need to validate that the data was migrated properly. } }
জাভা
@RunWith(AndroidJUnit4.class) public class MigrationTest { private static final String TEST_DB = "migration-test"; @Rule public MigrationTestHelper helper; public MigrationTest() { helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(), MigrationDb.class.getCanonicalName(), new FrameworkSQLiteOpenHelperFactory()); } @Test public void migrate1To2() throws IOException { SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1); // Database has schema version 1. Insert some data using SQL queries. // You can't use DAO classes because they expect the latest schema. db.execSQL(...); // Prepare for the next version. db.close(); // Re-open the database with version 2 and provide // MIGRATION_1_2 as the migration process. db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2); // MigrationTestHelper automatically verifies the schema changes, // but you need to validate that the data was migrated properly. } }
সমস্ত মাইগ্রেশন পরীক্ষা করুন
যদিও একটিমাত্র ইনক্রিমেন্টাল মাইগ্রেশন পরীক্ষা করা সম্ভব, আমরা সুপারিশ করি যে আপনি আপনার অ্যাপের ডাটাবেসের জন্য সংজ্ঞায়িত সমস্ত মাইগ্রেশন অন্তর্ভুক্ত করে একটি পরীক্ষা করুন। এটি নিশ্চিত করতে সাহায্য করে যে, সম্প্রতি তৈরি করা একটি ডাটাবেস ইনস্ট্যান্স এবং সংজ্ঞায়িত মাইগ্রেশন পথ অনুসরণকারী একটি পুরোনো ইনস্ট্যান্সের মধ্যে কোনো অমিল নেই।
নিম্নলিখিত উদাহরণটি সমস্ত সংজ্ঞায়িত মাইগ্রেশনের জন্য একটি পরীক্ষা প্রদর্শন করে:
কোটলিন
@RunWith(AndroidJUnit4::class) class MigrationTest { private val TEST_DB = "migration-test" // Array of all migrations. private val ALL_MIGRATIONS = arrayOf( MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4) @get:Rule val helper: MigrationTestHelper = MigrationTestHelper( InstrumentationRegistry.getInstrumentation(), AppDatabase::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory() ) @Test @Throws(IOException::class) fun migrateAll() { // Create earliest version of the database. helper.createDatabase(TEST_DB, 1).apply { close() } // Open latest version of the database. Room validates the schema // once all migrations execute. Room.databaseBuilder( InstrumentationRegistry.getInstrumentation().targetContext, AppDatabase::class.java, TEST_DB ).addMigrations(*ALL_MIGRATIONS).build().apply { openHelper.writableDatabase.close() } } }
জাভা
@RunWith(AndroidJUnit4.class) public class MigrationTest { private static final String TEST_DB = "migration-test"; @Rule public MigrationTestHelper helper; public MigrationTest() { helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(), AppDatabase.class.getCanonicalName(), new FrameworkSQLiteOpenHelperFactory()); } @Test public void migrateAll() throws IOException { // Create earliest version of the database. SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1); db.close(); // Open latest version of the database. Room validates the schema // once all migrations execute. AppDatabase appDb = Room.databaseBuilder( InstrumentationRegistry.getInstrumentation().getTargetContext(), AppDatabase.class, TEST_DB) .addMigrations(ALL_MIGRATIONS).build(); appDb.getOpenHelper().getWritableDatabase(); appDb.close(); } // Array of all migrations. private static final Migration[] ALL_MIGRATIONS = new Migration[]{ MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4}; }
অনুপস্থিত মাইগ্রেশন পাথগুলি সুন্দরভাবে পরিচালনা করুন
যদি Room কোনো ডিভাইসে থাকা ডেটাবেসকে বর্তমান সংস্করণে আপগ্রেড করার জন্য কোনো মাইগ্রেশন পাথ খুঁজে না পায়, তাহলে একটি IllegalStateException ঘটে। যদি মাইগ্রেশন পাথ না থাকার কারণে বিদ্যমান ডেটা হারিয়ে যাওয়াটা গ্রহণযোগ্য হয়, তাহলে ডেটাবেস তৈরি করার সময় fallbackToDestructiveMigration() বিল্ডার মেথডটি কল করুন:
কোটলিন
Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name") .fallbackToDestructiveMigration() .build()
জাভা
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name") .fallbackToDestructiveMigration() .build();
এই পদ্ধতিটি Room-কে নির্দেশ দেয়, যখন একটি ইনক্রিমেন্টাল মাইগ্রেশন করার প্রয়োজন হয় এবং কোনো নির্ধারিত মাইগ্রেশন পাথ থাকে না, তখন যেন এটি আপনার অ্যাপের ডাটাবেসে থাকা টেবিলগুলোকে ডেস্ট্রাকটিভভাবে পুনরায় তৈরি করে।
যদি আপনি শুধুমাত্র নির্দিষ্ট পরিস্থিতিতেই রুমকে ধ্বংসাত্মক বিনোদনে ফিরে যেতে দিতে চান, তাহলে fallbackToDestructiveMigration() এর কয়েকটি বিকল্প রয়েছে:
- আপনার স্কিমা হিস্টোরির নির্দিষ্ট সংস্করণগুলির কারণে যদি এমন ত্রুটি দেখা দেয় যা আপনি মাইগ্রেশন পাথ ব্যবহার করে সমাধান করতে পারছেন না, তাহলে তার পরিবর্তে
fallbackToDestructiveMigrationFrom()ব্যবহার করুন। এই মেথডটি নির্দেশ করে যে, আপনি চান Room শুধুমাত্র নির্দিষ্ট সংস্করণ থেকে মাইগ্রেট করার সময় ডেস্ট্রাকটিভ রিক্রিয়েশনে ফিরে যাক। - আপনি যদি চান যে Room শুধুমাত্র উচ্চতর ডাটাবেস সংস্করণ থেকে নিম্নতর সংস্করণে স্থানান্তরের সময়ই ধ্বংসাত্মক পুনর্গঠন পদ্ধতিতে ফিরে যাক, তাহলে এর পরিবর্তে
fallbackToDestructiveMigrationOnDowngrade()ব্যবহার করুন।
Room 2.2.0-তে আপগ্রেড করার সময় কলামের ডিফল্ট মানগুলো পরিচালনা করুন।
Room 2.2.0 এবং এর পরবর্তী সংস্করণগুলিতে, আপনি @ColumnInfo(defaultValue = "...") অ্যানোটেশনটি ব্যবহার করে একটি কলামের জন্য ডিফল্ট মান নির্ধারণ করতে পারেন। 2.2.0-এর পূর্ববর্তী সংস্করণগুলিতে, একটি কলামের জন্য ডিফল্ট মান নির্ধারণ করার একমাত্র উপায় হলো সরাসরি একটি এক্সিকিউটেড SQL স্টেটমেন্টে তা নির্ধারণ করা, যা এমন একটি ডিফল্ট মান তৈরি করে যা সম্পর্কে Room অবগত থাকে না। এর অর্থ হলো, যদি কোনো ডাটাবেস মূলত Room 2.2.0-এর পূর্ববর্তী কোনো সংস্করণ দ্বারা তৈরি করা হয়ে থাকে, তবে আপনার অ্যাপটিকে Room 2.2.0 ব্যবহার করার জন্য আপগ্রেড করতে গেলে, Room API ব্যবহার না করে আপনার নির্ধারণ করা বিদ্যমান ডিফল্ট মানগুলির জন্য একটি বিশেষ মাইগ্রেশন পাথ প্রদান করার প্রয়োজন হতে পারে।
উদাহরণস্বরূপ, ধরা যাক একটি ডাটাবেসের সংস্করণ ১-এ একটি Song এনটিটি সংজ্ঞায়িত করা আছে:
কোটলিন
// Song entity, database version 1, Room 2.1.0. @Entity data class Song( @PrimaryKey val id: Long, val title: String )
জাভা
// Song entity, database version 1, Room 2.1.0. @Entity public class Song { @PrimaryKey final long id; final String title; }
আরও ধরুন যে, একই ডাটাবেসের ভার্সন ২ একটি নতুন NOT NULL কলাম যোগ করে এবং ভার্সন ১ থেকে ভার্সন ২-তে যাওয়ার জন্য একটি মাইগ্রেশন পাথ নির্ধারণ করে:
কোটলিন
// Song entity, database version 2, Room 2.1.0. @Entity data class Song( @PrimaryKey val id: Long, val title: String, val tag: String // Added in version 2. ) // Migration from 1 to 2, Room 2.1.0. val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL( "ALTER TABLE Song ADD COLUMN tag TEXT NOT NULL DEFAULT ''") } }
জাভা
// Song entity, database version 2, Room 2.1.0. @Entity public class Song { @PrimaryKey final long id; final String title; @NonNull final String tag; // Added in version 2. } // Migration from 1 to 2, Room 2.1.0. static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL( "ALTER TABLE Song ADD COLUMN tag TEXT NOT NULL DEFAULT ''"); } };
এর ফলে অ্যাপের আপডেট এবং নতুন ইনস্টলের মধ্যে অন্তর্নিহিত টেবিলে একটি অমিল দেখা দেয়। যেহেতু tag কলামের ডিফল্ট মান শুধুমাত্র ভার্সন ১ থেকে ভার্সন ২-এ মাইগ্রেশন পাথে ঘোষিত থাকে, তাই যে সকল ব্যবহারকারী ভার্সন ২ থেকে অ্যাপটি ইনস্টল করেন, তাদের ডাটাবেস স্কিমাতে tag জন্য ডিফল্ট মানটি থাকে না।
Room-এর 2.2.0-এর পূর্ববর্তী সংস্করণগুলিতে এই অমিলটি ক্ষতিকর নয়। তবে, যদি অ্যাপটি পরবর্তীতে Room 2.2.0 বা তার উচ্চতর সংস্করণ ব্যবহার করার জন্য আপগ্রেড করে এবং @ColumnInfo অ্যানোটেশন ব্যবহার করে tag জন্য একটি ডিফল্ট মান অন্তর্ভুক্ত করতে Song এনটিটি ক্লাসটি পরিবর্তন করে, তাহলে Room এই অমিলটি দেখতে পায়। এর ফলে স্কিমা ভ্যালিডেশন ব্যর্থ হয়।
আপনার পূর্ববর্তী মাইগ্রেশন পাথগুলিতে কলামের ডিফল্ট মান ঘোষণা করা থাকলে, সমস্ত ব্যবহারকারীর জন্য ডাটাবেস স্কিমা যেন সামঞ্জস্যপূর্ণ থাকে তা নিশ্চিত করতে, প্রথমবার আপনার অ্যাপটিকে Room 2.2.0 বা তার উচ্চতর সংস্করণে আপগ্রেড করার সময় নিম্নলিখিতগুলি করুন:
-
@ColumnInfoঅ্যানোটেশন ব্যবহার করে কলামের ডিফল্ট মানগুলো তাদের নিজ নিজ এনটিটি ক্লাসে ঘোষণা করুন। - ডাটাবেস ভার্সন নম্বরটি ১ বৃদ্ধি করুন।
- নতুন সংস্করণে স্থানান্তরের জন্য এমন একটি পথ নির্ধারণ করুন, যা বিদ্যমান কলামগুলিতে প্রয়োজনীয় ডিফল্ট মান যোগ করার জন্য ড্রপ ও রিক্রিয়েট কৌশলটি প্রয়োগ করে।
নিম্নলিখিত উদাহরণটি এই প্রক্রিয়াটি প্রদর্শন করে:
কোটলিন
// Migration from 2 to 3, Room 2.2.0. val MIGRATION_2_3 = object : Migration(2, 3) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL(""" CREATE TABLE new_Song ( id INTEGER PRIMARY KEY NOT NULL, name TEXT, tag TEXT NOT NULL DEFAULT '' ) """.trimIndent()) database.execSQL(""" INSERT INTO new_Song (id, name, tag) SELECT id, name, tag FROM Song """.trimIndent()) database.execSQL("DROP TABLE Song") database.execSQL("ALTER TABLE new_Song RENAME TO Song") } }
জাভা
// Migration from 2 to 3, Room 2.2.0. static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL("CREATE TABLE new_Song (" + "id INTEGER PRIMARY KEY NOT NULL," + "name TEXT," + "tag TEXT NOT NULL DEFAULT '')"); database.execSQL("INSERT INTO new_Song (id, name, tag) " + "SELECT id, name, tag FROM Song"); database.execSQL("DROP TABLE Song"); database.execSQL("ALTER TABLE new_Song RENAME TO Song"); } };