أثناء إضافة الميزات وتغييرها في تطبيقك، عليك تعديل كيان الغرفة. والفئات وجداول قاعدة البيانات الأساسية لتعكس هذه التغييرات. من المهم بيانات المستخدم المتوفرة بالفعل في قاعدة البيانات على الجهاز عند تشغيل تطبيق يؤدي تحديث إلى تغيير مخطط قاعدة البيانات.
تتيح الغرفة كلا الخيارين التلقائيين واليدويين لنقل البيانات بشكل تدريجي. تتوافق عمليات نقل البيانات التلقائية مع معظم التغييرات الأساسية في المخطط، ولكن قد تحتاج إلى إجراء ما يلي: تحديد مسارات نقل البيانات يدويًا لإجراء تغييرات أكثر تعقيدًا.
عمليات نقل البيانات المبرمَجة
للإعلان عن عملية نقل مبرمَج بين نسختَين من قاعدة البيانات، أضِف
@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 { ... }
مواصفات نقل البيانات التلقائي
إذا رصدت الغرفة تغييرات غامضة في المخطط ولا يمكنها إنشاء
بدون مزيد من المدخلات، فإنه يعرض خطأ وقت التجميع ويسألك
لتنفيذ
AutoMigrationSpec
يحدث هذا غالبًا عندما تتضمن عملية النقل أيًا مما يلي:
- حذف جدول أو إعادة تسميته.
- حذف عمود أو إعادة تسميته.
يمكنك استخدام "AutoMigrationSpec
" لمنح الغرفة المعلومات الإضافية التي
إنشاء مسارات نقل البيانات بشكل صحيح. حدد فئة ثابتة
لتنفيذ 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
، سيتم طلبها من الغرفة بعد
يتم اكتمال النقل التلقائي.
عمليات نقل البيانات اليدوية
وفي الحالات التي تتضمن فيها عملية نقل البيانات تغييرات معقّدة في المخطط، قد لا يتم
إنشاء مسار ترحيل مناسب تلقائيًا. على سبيل المثال، إذا
فقررت تقسيم البيانات الموجودة في جدول إلى جدولين، فلن تستطيع الغرفة معرفة
كيفية إجراء هذا التقسيم. في مثل هذه الحالات، يجب عليك
تحديد مسار ترحيل من خلال تنفيذ
صف واحد (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 الدليل وترحيلها.
اختبار عمليات نقل البيانات
غالبًا ما تكون عمليات النقل معقدة، ويمكن أن يتسبب تحديد الترحيل بشكل غير صحيح في
تعطُّل تطبيقك. للحفاظ على استقرار تطبيقك، اختبِر
وعمليات الترحيل. توفر الغرفة عناصر Maven من room-testing
للمساعدة في
لعملية اختبار لكل من عمليات النقل التلقائية واليدوية. لهذا العنصر إلى
يجب عليك أولاً تصدير مخطط قاعدة البيانات لديك.
تصدير المخططات
يمكن للغرفة تصدير معلومات مخطّط قاعدة البيانات إلى ملف JSON عند التجميع. الوقت. تمثّل ملفات JSON التي تم تصديرها سجلّ مخطّط قاعدة البيانات. متجر هذه الملفات في نظام التحكم في الإصدار بحيث يمكن لـ Room إنشاء إصدارات أقل من قاعدة البيانات لأغراض الاختبار ولتمكين إنشاء الترحيل التلقائي.
ضبط موقع المخطط باستخدام مكوّن Gradle الإضافي للغرفة
إذا كنت تستخدم الإصدار 2.6.0 من الغرفة أو إصدارًا أحدث، يمكنك تطبيق
المكوّن الإضافي لنظام Gradle المتوافق مع الغرف واستخدام
room
لتحديد دليل المخطط.
Groovy
plugins {
id 'androidx.room'
}
room {
schemaDirectory "$projectDir/schemas"
}
Kotlin
plugins {
id("androidx.room")
}
room {
schemaDirectory("$projectDir/schemas")
}
إذا كان مخطط قاعدة البيانات يختلف بناءً على الصيغة أو النكهة أو الإصدار
النوع، يجب تحديد مواقع مختلفة باستخدام schemaDirectory()
التكوين عدة مرات، ولكل منها 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 أو إصدارًا أقدم من الغرفة، أو إذا كنت لا تستخدم
مكوّن Gradle الإضافي للغرفة، اضبط موقع المخطط باستخدام room.schemaLocation
معالج التعليقات التوضيحية.
تُستخدم الملفات في هذا الدليل كمدخلات ومخرجات لبعض مهام Gradle.
ومن أجل صحة وأداء الإصدارات الإضافية والمخزنة مؤقتًا، يجب استخدام
من غرادل
CommandLineArgumentProvider
لإبلاغ 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 من الغرفة في الاختبار
الإضافية وإضافة موقع المخطط الذي تم تصديره كمجلد أصل:
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.6.1" }
Kotlin
android { ... sourceSets { // Adds exported schema location as test app assets. getByName("androidTest").assets.srcDir("$projectDir/schemas") } } dependencies { ... testImplementation("androidx.room:room-testing:2.6.1") }
توفر حزمة الاختبار
MigrationTestHelper
التي يمكنها قراءة ملفات المخطط التي تم تصديرها. تنفذ الحزمة أيضًا
الوحدة الرابعة
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}; }
التعامل بسلاسة مع مسارات نقل البيانات المفقودة
في حال لم تتمكّن الغرفة من العثور على مسار نقل بيانات لترقية قاعدة بيانات حالية على
إلى الإصدار الحالي،
IllegalStateException
. في حال حذف
فمن المقبول فقدان البيانات الحالية عند فقد مسار نقل، أو
الـ
fallbackToDestructiveMigration()
عند إنشاء قاعدة البيانات:
Kotlin
Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name") .fallbackToDestructiveMigration() .build()
Java
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name") .fallbackToDestructiveMigration() .build();
تطلب هذه الطريقة من الغرفة إعادة إنشاء الجداول في تطبيقك بشكل مدمر قاعدة البيانات عندما تحتاج إلى إجراء ترحيل تزايدي ولا توجد مسار الترحيل المحدد.
إذا كنت تريد فقط أن تعود إلى لعبة Room للاسترخاء في
هناك بعض البدائل لـ fallbackToDestructiveMigration()
:
- إذا كانت إصدارات معيّنة من سجلّ المخططات تتسبب في أخطاء لا يمكنك حلها
مع مسارات الترحيل، واستخدام
fallbackToDestructiveMigrationFrom()
بدلاً من ذلك. تشير هذه الطريقة إلى أنك تريد من الغرفة العودة إلى الحالة المدمرة. إعادة التحميل فقط عند نقل البيانات من إصدارات معيّنة. - إذا كنت تريد من "غرفة" العودة إلى وضع الترفيه المدمر فقط عند نقل البيانات
من إصدار قاعدة بيانات أعلى إلى إصدار أقل، استخدم
fallbackToDestructiveMigrationOnDowngrade()
بدلاً من ذلك.
التعامل مع القيم التلقائية للعمود عند الترقية إلى الغرفة 2.2.0
في الغرفة 2.2.0 والإصدارات الأحدث، يمكنك تحديد قيمة تلقائية للعمود باستخدام
التعليق التوضيحي
@ColumnInfo(defaultValue = "...")
وفي الإصدارات الأقل من 2.2.0، تكون الطريقة الوحيدة لتحديد قيمة تلقائية
من خلال تعريفه مباشرةً في عبارة SQL تم تنفيذها، مما يؤدي إلى إنشاء
القيمة الافتراضية التي لا تعرفها الغرفة. هذا يعني أنه إذا كانت قاعدة البيانات
تم إنشاؤه في الأصل باستخدام إصدار Room أقل من 2.2.0، ما يؤدي إلى ترقية تطبيقك إلى
استخدام الغرفة 2.2.0 قد تتطلب منك توفير مسار نقل بيانات خاص
القيم التلقائية الحالية التي حدّدتها بدون استخدام واجهات برمجة تطبيقات الغرف.
على سبيل المثال، لنفترض أنّ الإصدار 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
في مخطط قاعدة البيانات لديها.
في إصدارات الغرفة الأقل من 2.2.0، لا يكون هذا التناقض ضارًا. ومع ذلك، إذا
ستتم ترقية التطبيق لاحقًا لاستخدام الغرفة 2.2.0 أو إصدار أحدث وتغيير كيان Song
.
على تضمين قيمة تلقائية لـ tag
باستخدام
التعليق التوضيحي @ColumnInfo
، الغرفة
يمكننا حينئذٍ أن نرى هذا التناقض. ينتج عن ذلك تعذُّر المخطط.
عمليات التحقق من الصحة.
للمساعدة في ضمان أن مخطط قاعدة البيانات متسق عبر جميع المستخدمين عند العمود تم الإعلان عن القيم الافتراضية في مسارات النقل السابقة، قم بما يلي في المرة الأولى التي تتم فيها ترقية تطبيقك لاستخدام الغرفة 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"); } };