Khi thêm và thay đổi các tính năng trong ứng dụng của mình, bạn cần sửa đổi các lớp thực thể Room và bảng cơ sở dữ liệu cơ bản để phản ánh những thay đổi này. Bạn cần lưu giữ dữ liệu người dùng đã có trong cơ sở dữ liệu trên thiết bị khi bản cập nhật ứng dụng thay đổi giản đồ cơ sở dữ liệu.
Room hỗ trợ cả các lựa chọn tự động và thủ công để di chuyển dần dần. Quá trình di chuyển tự động hoạt động với hầu hết các thay đổi cơ bản về giản đồ, nhưng bạn có thể cần phải xác định các đường dẫn di chuyển theo cách thủ công cho những thay đổi phức tạp hơn.
Di chuyển tự động
Để khai báo quá trình di chuyển tự động giữa hai phiên bản cơ sở dữ liệu, hãy thêm chú giải @AutoMigration
vào thuộc tính autoMigrations
trong @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 { ... }
Thông số kỹ thuật di chuyển tự động
Nếu Room phát hiện các thay đổi giản đồ không rõ ràng và không thể tạo kế hoạch di chuyển nếu không có thêm thông tin, thì ứng dụng sẽ gửi lỗi thời gian biên dịch và yêu cầu bạn triển khai AutoMigrationSpec
.
Thông thường, trường hợp này xảy ra khi quá trình di chuyển liên quan đến một trong những thao tác sau:
- Xoá hoặc đổi tên bảng.
- Xoá hoặc đổi tên cột.
Bạn có thể sử dụng AutoMigrationSpec
để cung cấp cho Room những thông tin bổ sung cần thiết nhằm tạo đường dẫn di chuyển chính xác. Xác định một lớp tĩnh triển khai AutoMigrationSpec
trong lớp RoomDatabase
của bạn và chú giải lớp đó bằng một hoặc nhiều lệnh sau:
Để sử dụng hoạt động triển khai AutoMigrationSpec
cho quá trình di chuyển tự động, hãy đặt thuộc tính spec
trong chú giải @AutoMigration
tương ứng:
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 { } ... }
Nếu ứng dụng của bạn cần làm nhiều việc hơn sau khi quá trình di chuyển tự động hoàn tất, bạn có thể triển khai
onPostMigrate()
.
Nếu bạn triển khai phương thức này trong AutoMigrationSpec
, thì Room sẽ gọi phương thức này sau khi quá trình di chuyển tự động hoàn tất.
Di chuyển thủ công
Trong trường hợp quá trình di chuyển kéo theo những thay đổi phức tạp của giản đồ, có thể Room sẽ không tự động tạo được một đường dẫn di chuyển thích hợp. Ví dụ: Nếu bạn quyết định chia dữ liệu trong một bảng thành hai bảng, thì Room không thể thực hiện quá trình chia này. Trong những trường hợp này, bạn phải dùng cách thủ công là triển khai một lớp Migration
để xác định đường dẫn di chuyển.
Một lớp Migration
xác định rõ một đường dẫn di chuyển giữa startVersion
và endVersion
bằng cách ghi đè phương thức Migration.migrate()
. Thêm các lớp Migration
vào trình tạo cơ sở dữ liệu bằng phương thức 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();
Khi xác định đường dẫn di chuyển, bạn có thể sử dụng quá trình di chuyển tự động cho một số phiên bản và quá trình di chuyển thủ công cho những phiên bản khác. Nếu bạn xác định cả một quá trình di chuyển tự động lẫn di chuyển thủ công cho cùng một phiên bản, thì Room sẽ sử dụng quá trình di chuyển thủ công.
Kiểm thử các quá trình di chuyển
Quá trình di chuyển thường phức tạp và nếu được xác định không chính xác có thể khiến ứng dụng của bạn gặp sự cố. Để duy trì độ ổn định của ứng dụng, hãy kiểm thử quá trình di chuyển. Room cung cấp cấu phần phần mềm Maven room-testing
để hỗ trợ quá trình kiểm thử cho cả quá trình di chuyển tự động và thủ công. Để cấu phần phần mềm này hoạt động, trước tiên, bạn phải xuất giản đồ của cơ sở dữ liệu.
Xuất giản đồ
Room có thể xuất thông tin giản đồ cơ sở dữ liệu của bạn vào tệp JSON tại thời gian biên dịch. Các tệp JSON đã xuất sẽ đại diện cho nhật ký giản đồ cơ sở dữ liệu của bạn. Cửa hàng các tệp này trong hệ thống quản lý phiên bản của bạn để Room có thể tạo các phiên bản thấp hơn cơ sở dữ liệu cho mục đích kiểm tra và để cho phép tự động tạo quá trình di chuyển.
Đặt vị trí giản đồ bằng trình bổ trợ Room cho Gradle
Nếu đang dùng Room phiên bản 2.6.0 trở lên, bạn có thể áp dụng
Trình bổ trợ Room cho Gradle và sử dụng
Tiện ích room
để chỉ định thư mục giản đồ.
Groovy
plugins {
id 'androidx.room'
}
room {
schemaDirectory "$projectDir/schemas"
}
Kotlin
plugins {
id("androidx.room")
}
room {
schemaDirectory("$projectDir/schemas")
}
Nếu giản đồ cơ sở dữ liệu của bạn khác nhau tuỳ theo biến thể, phiên bản hoặc bản dựng
loại, bạn phải chỉ định các vị trí khác nhau bằng cách sử dụng schemaDirectory()
cấu hình này nhiều lần, mỗi lần có variantMatchName
là lần đầu tiên
đối số. Mỗi cấu hình có thể khớp với một hoặc nhiều biến thể dựa trên
so với tên biến thể.
Hãy đảm bảo những nội dung này đầy đủ và bao gồm tất cả các biến thể. Bạn cũng có thể thêm
schemaDirectory()
không có variantMatchName
để xử lý các biến thể không khớp
theo bất kỳ cấu hình nào khác. Ví dụ: trong một ứng dụng có hai bản dựng
phiên bản demo
và full
cùng hai loại bản dựng debug
và release
,
sau đây là các cấu hình hợp lệ:
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")
}
Đặt vị trí giản đồ bằng tuỳ chọn trình xử lý chú giải
Nếu bạn đang sử dụng phiên bản Room 2.5.2 trở xuống hoặc nếu bạn không sử dụng
Trình bổ trợ Room cho Gradle, đặt vị trí giản đồ bằng room.schemaLocation
tuỳ chọn trình xử lý chú giải.
Các tệp trong thư mục này được dùng làm dữ liệu đầu vào và đầu ra cho một số tác vụ Gradle.
Để đảm bảo tính chính xác và hiệu suất của các bản dựng gia tăng và được lưu vào bộ nhớ đệm, bạn phải sử dụng
của Gradle
CommandLineArgumentProvider
để thông báo cho Gradle về thư mục này.
Trước tiên, hãy sao chép lớp RoomSchemaArgProvider
hiển thị dưới đây vào tệp
Tệp bản dựng Gradle. Phương thức asArguments()
trong lớp mẫu truyền
room.schemaLocation=${schemaDir.path}
đến KSP
. Nếu bạn đang sử dụng KAPT
và
javac
, hãy thay đổi giá trị này thành -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}")
}
}
Sau đó, hãy định cấu hình các tuỳ chọn biên dịch để sử dụng RoomSchemaArgProvider
với
thư mục giản đồ được chỉ định:
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"))
)
}
}
}
}
Kiểm thử một quá trình di chuyển
Để có thể kiểm thử các quá trình di chuyển, bạn hãy thêm cấu phần phần mềm Maven androidx.room:room-testing
từ Room vào các phần phụ thuộc kiểm thử và thêm vị trí của giản đồ đã xuất làm thư mục tài sản:
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") }
Gói kiểm thử cung cấp một lớp MigrationTestHelper
, có thể đọc các tệp giản đồ được xuất. Gói này cũng triển khai giao diện TestRule
JUnit4, nhờ đó có thể quản lý các cơ sở dữ liệu đã tạo.
Ví dụ sau đây minh hoạ hoạt động kiểm thử cho một quá trình di chuyển:
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. } }
Kiểm thử tất cả quá trình di chuyển
Mặc dù có thể kiểm thử một quá trình di chuyển dần dần, nhưng bạn nên kiểm thử mọi quá trình di chuyển được xác định cho cơ sở dữ liệu của ứng dụng. Việc này giúp đảm bảo rằng không có sự khác biệt giữa phiên bản cơ sở dữ liệu được tạo gần đây và phiên bản cũ hơn đi theo đường dẫn di chuyển đã xác định.
Ví dụ sau đây minh hoạ một hoạt động kiểm thử cho tất cả các quá trình di chuyển đã xác định:
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}; }
Xử lý linh hoạt đường dẫn di chuyển bị thiếu
Nếu Room không tìm thấy đường dẫn di chuyển để nâng cấp cơ sở dữ liệu hiện có trên thiết bị lên phiên bản hiện tại, thì IllegalStateException
sẽ xảy ra. Nếu bạn có thể chấp nhận mất dữ liệu hiện có khi thiếu đường dẫn di chuyển, hãy gọi phương thức trình tạo fallbackToDestructiveMigration()
khi bạn tạo cơ sở dữ liệu:
Kotlin
Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name") .fallbackToDestructiveMigration() .build()
Java
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name") .fallbackToDestructiveMigration() .build();
Phương thức này sẽ yêu cầu Room tạo lại toàn bộ các bảng trong cơ sở dữ liệu của ứng dụng (có thể gây thiệt hại) khi cần thực hiện quá trình di chuyển dần dần và không có đường dẫn di chuyển đã xác định nào.
Nếu bạn chỉ muốn Room tìm đến hoạt động tạo lại gây thiệt hại ở một số tình huống nhất định, thì có một vài phương án thay thế cho fallbackToDestructiveMigration()
:
- Nếu các phiên bản cụ thể của nhật ký giản đồ gây ra các lỗi mà bạn không thể giải quyết bằng đường dẫn di chuyển, hãy sử dụng
fallbackToDestructiveMigrationFrom()
thay thế. Phương thức này cho biết rằng bạn chỉ muốn Room tìm đến hoạt động tạo lại gây thiệt hại khi di chuyển từ các phiên bản cụ thể. - Nếu bạn chỉ muốn Room tìm đến hoạt động tạo lại gây thiệt hại khi di chuyển từ phiên bản cơ sở dữ liệu cao hơn sang phiên bản cơ sở dữ liệu thấp hơn, hãy sử dụng
fallbackToDestructiveMigrationOnDowngrade()
thay thế.
Xử lý các giá trị mặc định của cột khi nâng cấp lên Room 2.2.0
Ở Room 2.2.0 trở lên, bạn có thể xác định giá trị mặc định cho cột bằng cách sử dụng chú giải @ColumnInfo(defaultValue = "...")
.
Trong các phiên bản thấp hơn 2.2.0, cách duy nhất để xác định giá trị mặc định cho cột là xác định trực tiếp giá trị đó trong câu lệnh SQL được thực thi. Thao tác này sẽ tạo ra giá trị mặc định mà Room không biết. Nói cách khác, nếu ban đầu, cơ sở dữ liệu được tạo bằng phiên bản Room thấp hơn 2.2.0, thì khi nâng cấp ứng dụng của bạn để dùng Room 2.2.0, có thể bạn sẽ phải cung cấp đường dẫn di chuyển đặc biệt cho giá trị mặc định hiện có mà bạn đã xác định khi không sử dụng API Room.
Ví dụ: Giả sử phiên bản 1 của cơ sở dữ liệu xác định thực thể 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; }
Giả sử thêm rằng phiên bản 2 của cùng một cơ sở dữ liệu đó thêm một cột NOT NULL
mới và xác định đường dẫn di chuyển từ phiên bản 1 sang phiên bản 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 ''"); } };
Điều này gây ra sự khác biệt trong bảng cơ bản giữa các bản cập nhật và lượt cài đặt mới của ứng dụng. Vì giá trị mặc định cho cột tag
chỉ được khai báo trong đường dẫn di chuyển từ phiên bản 1 sang phiên bản 2, nên mọi người dùng cài đặt ứng dụng kể từ phiên bản 2 sẽ không có giá trị mặc định cho tag
trong giản đồ cơ sở dữ liệu của họ.
Ở các phiên bản Room thấp hơn 2.2.0, sự khác biệt này không gây hại. Tuy nhiên, nếu sau đó ứng dụng nâng cấp để sử dụng Room 2.2.0 trở lên và thay đổi lớp thực thể Song
để có thể chứa giá trị mặc định cho tag
bằng cách sử dụng chú giải @ColumnInfo
, thì lúc đó Room có thể thấy sự khác biệt này. Điều này dẫn đến việc xác thực giản đồ không thành công.
Để giúp đảm bảo rằng giản đồ cơ sở dữ liệu nhất quán cho mọi người dùng khi khai báo các giá trị mặc định của cột trong các đường dẫn di chuyển trước đó của bạn, hãy làm như sau trong lần đầu tiên bạn nâng cấp ứng dụng của mình để sử dụng Room 2.2.0 trở lên:
- Khai báo giá trị mặc định của cột trong các lớp thực thể tương ứng bằng chú giải
@ColumnInfo
. - Tăng số phiên bản cơ sở dữ liệu lên một số.
- Xác định đường dẫn di chuyển đến phiên bản mới triển khai chiến lược xoá (drop) và tạo lại (recreate) để thêm các giá trị mặc định cần thiết vào các cột hiện có.
Ví dụ sau đây minh hoạ quá trình này:
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"); } };