عند إضافة ميزات وتغييرها في تطبيقك، عليك تعديل فئات عناصر Room وجداول قاعدة البيانات الأساسية لعكس هذه التغييرات. من المهم الاحتفاظ ببيانات المستخدمين الموجودة في قاعدة البيانات على الجهاز عند تعديل مخطط قاعدة البيانات من خلال تحديث التطبيق.
تتيح Room خيارات مبرمَجة ويدوية لنقل البيانات بشكل تدريجي. تعمل عمليات نقل البيانات التلقائية مع معظم التغييرات الأساسية في المخطط، ولكن قد تحتاج إلى تحديد مسارات نقل البيانات يدويًا لإجراء تغييرات أكثر تعقيدًا.
عمليات نقل البيانات المبرمَجة
للإشارة إلى عملية نقل بيانات تلقائية بين إصدارَين من قاعدة البيانات، أضِف التعليق التوضيحي @AutoMigration
إلى السمة autoMigrations
في @Database
:
Kotlin
// 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() { ... }
Java
// 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
.
يحدث ذلك غالبًا عندما تتضمّن عملية نقل البيانات أحد الإجراءات التالية:
- حذف جدول أو إعادة تسميته
- حذف عمود أو إعادة تسميته
يمكنك استخدام AutoMigrationSpec
لتزويد Room بالمعلومات الإضافية التي يحتاجها لإنشاء مسارات نقل البيانات بشكل صحيح. حدِّد فئة ثابتة تنفّذ AutoMigrationSpec
في فئة RoomDatabase
وأضِف إليها واحدًا أو أكثر مما يلي:
لاستخدام عملية تنفيذ AutoMigrationSpec
لنقل البيانات تلقائيًا، اضبط السمة spec
في التعليق التوضيحي @AutoMigration
ذي الصلة:
Kotlin
@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 ... }
Java
@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
مسار نقل البيانات بشكل صريح بين startVersion
وendVersion
من خلال إلغاء طريقة Migration.migrate()
. أضِف فئات Migration
إلى أداة إنشاء قاعدة البيانات باستخدام الطريقة addMigrations()
:
Kotlin
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()
Java
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
إذا كنت تستخدم الإصدار 2.6.0 أو إصدارًا أحدث من Room، يمكنك تطبيق
إضافة Room Gradle واستخدام الإضافة
room
لتحديد دليل المخطط.
Groovy
plugins {
id 'androidx.room'
}
room {
schemaDirectory "$projectDir/schemas"
}
Kotlin
plugins {
id("androidx.room")
}
room {
schemaDirectory("$projectDir/schemas")
}
إذا كان مخطط قاعدة البيانات يختلف استنادًا إلى نوع المنتج أو الإصدار أو الإصدار، يجب تحديد مواقع مختلفة باستخدام schemaDirectory()
configuration عدة مرات، مع استخدام variantMatchName
كمعلَمة أولى في كل مرة. يمكن أن يتطابق كل إعداد مع صيغة واحدة أو أكثر استنادًا إلى مقارنة بسيطة مع اسم الصيغة.
تأكَّد من أنّ هذه القائمة شاملة وتغطّي جميع الصيغ. يمكنك أيضًا تضمين
schemaDirectory()
بدون variantMatchName
للتعامل مع الصيغ التي لا تتطابق مع أي من الإعدادات الأخرى. على سبيل المثال، في تطبيق يتضمّن نوعَين من إصدارات الإنشاء demo
وfull
ونوعَين من أنواع الإنشاء debug
وrelease
، تكون الإعدادات التالية صالحة:
Groovy
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"
}
Kotlin
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")
}
ضبط موقع المخطط باستخدام خيار معالج التعليقات التوضيحية
إذا كنت تستخدم الإصدار 2.5.2 أو إصدارًا أقدم من Room، أو إذا كنت لا تستخدم
Room Gradle Plugin، اضبط موقع المخطط باستخدام خيار room.schemaLocation
معالج التعليقات التوضيحية.
تُستخدَم الملفات في هذا الدليل كمدخلات ومخرجات لبعض مهام Gradle.
لضمان صحة وأداء عمليات الإنشاء التزايدي والمخزَّن مؤقتًا، عليك استخدام CommandLineArgumentProvider
من Gradle لإعلام Gradle بهذا الدليل.
أولاً، انسخ فئة RoomSchemaArgProvider
الموضّحة أدناه إلى ملف Gradle الخاص بوحدة التطبيق. تُمرِّر الطريقة asArguments()
في فئة النموذج القيمة room.schemaLocation=${schemaDir.path}
إلى KSP
. إذا كنت تستخدم KAPT
وjavac
، غيِّر هذه القيمة إلى -Aroom.schemaLocation=${schemaDir.path}
بدلاً من ذلك.
Groovy
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()]
}
}
Kotlin
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
مع دليل المخطط المحدّد:
Groovy
// 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"))
)
}
}
}
}
Kotlin
// 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"))
)
}
}
}
}
اختبار عملية نقل بيانات واحدة
قبل أن تتمكّن من اختبار عمليات النقل، أضِف
androidx.room:room-testing
Maven artifact من Room إلى
التبعيات الخاصة بالاختبار، وأضِف موقع المخطط الذي تم تصديره كمجلد مواد عرض:
build.gradle
Groovy
android { ... sourceSets { // Adds exported schema location as test app assets. androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } } dependencies { ... androidTestImplementation "androidx.room:room-testing:2.7.2" }
Kotlin
android { ... sourceSets { // Adds exported schema location as test app assets. getByName("androidTest").assets.srcDir("$projectDir/schemas") } } dependencies { ... testImplementation("androidx.room:room-testing:2.7.2") }
توفر حزمة الاختبار
MigrationTestHelper
فئة يمكنها قراءة ملفات المخطط التي تم تصديرها. تتضمّن الحزمة أيضًا واجهة
JUnit4
TestRule
،
وبالتالي يمكنها إدارة قواعد البيانات التي تم إنشاؤها.
يوضّح المثال التالي اختبارًا لعملية نقل واحدة:
Kotlin
@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. } }
Java
@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. } }
اختبار جميع عمليات نقل البيانات
على الرغم من إمكانية اختبار عملية نقل تدريجية واحدة، ننصحك بتضمين اختبار يشمل جميع عمليات النقل المحدّدة لقاعدة بيانات تطبيقك. يساعد ذلك في ضمان عدم حدوث تفاوت بين نسخة قاعدة بيانات تم إنشاؤها مؤخرًا ونسخة أقدم اتّبعت مسارات نقل البيانات المحدّدة.
يوضّح المثال التالي اختبارًا لجميع عمليات نقل البيانات المحدّدة:
Kotlin
@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() } } }
Java
@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()
عند إنشاء قاعدة البيانات:
Kotlin
Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name") .fallbackToDestructiveMigration() .build()
Java
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name") .fallbackToDestructiveMigration() .build();
تطلب هذه الطريقة من Room إعادة إنشاء الجداول بشكل مدمر في قاعدة بيانات تطبيقك عندما يحتاج إلى إجراء عملية نقل بيانات تدريجية ولا يتوفّر مسار نقل بيانات محدّد.
إذا كنت تريد أن يعود 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.
على سبيل المثال، لنفترض أنّ الإصدار 1 من قاعدة البيانات يحدّد الكيان Song
:
Kotlin
// Song entity, database version 1, Room 2.1.0. @Entity data class Song( @PrimaryKey val id: Long, val title: String )
Java
// Song entity, database version 1, Room 2.1.0. @Entity public class Song { @PrimaryKey final long id; final String title; }
لنفترض أيضًا أنّ الإصدار 2 من قاعدة البيانات نفسها يضيف عمود NOT NULL
جديدًا
ويحدّد مسار نقل البيانات من الإصدار 1 إلى الإصدار 2:
Kotlin
// 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 ''") } }
Java
// 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
يتم تحديدها فقط في مسار نقل البيانات من الإصدار 1 إلى الإصدار 2، وبالتالي فإنّ أي مستخدمين يثبّتون التطبيق بدءًا من الإصدار 2 لن تتوفّر لهم القيمة التلقائية للعمود tag
في مخطط قاعدة البيانات.
في إصدارات Room الأقل من 2.2.0، لا يشكّل هذا التناقض أي مشكلة. ومع ذلك، إذا تمت ترقية التطبيق لاحقًا لاستخدام Room 2.2.0 أو إصدار أحدث وتغيير فئة كيان Song
لتضمين قيمة تلقائية لـ tag
باستخدام التعليق التوضيحي @ColumnInfo
، يمكن أن يرصد Room بعد ذلك هذا التناقض. ويؤدي ذلك إلى تعذُّر التحقّق من صحة المخطط.
للمساعدة في ضمان اتّساق مخطط قاعدة البيانات لدى جميع المستخدمين عند تحديد القيم التلقائية للأعمدة في مسارات النقل السابقة، اتّبِع الخطوات التالية في المرة الأولى التي ترقّي فيها تطبيقك لاستخدام Room 2.2.0 أو الإصدارات الأحدث:
- يمكنك تحديد القيم التلقائية للأعمدة في فئات الكيانات الخاصة بها باستخدام التعليق التوضيحي
@ColumnInfo
. - زيادة رقم إصدار قاعدة البيانات بمقدار 1
- حدِّد مسار نقل إلى الإصدار الجديد الذي ينفّذ استراتيجية الحذف وإعادة الإنشاء لإضافة القيم التلقائية اللازمة إلى الأعمدة الحالية.
يوضّح المثال التالي هذه العملية:
Kotlin
// 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") } }
Java
// 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"); } };