เมื่อเพิ่มและเปลี่ยนแปลงฟีเจอร์ในแอป คุณจะต้องแก้ไขเอนทิตีห้อง และตารางฐานข้อมูลที่สำคัญเพื่อแสดงการเปลี่ยนแปลงเหล่านี้ เป็นสิ่งสำคัญ เพื่อเก็บรักษาข้อมูลผู้ใช้ที่อยู่ในฐานข้อมูลในอุปกรณ์อยู่แล้วเมื่อแอป อัปเดตเปลี่ยนแปลงสคีมาของฐานข้อมูล
Room รองรับทั้งตัวเลือกแบบอัตโนมัติและด้วยตนเองสําหรับการย้ายข้อมูลเพิ่มขึ้น การย้ายข้อมูลอัตโนมัติใช้ได้กับการเปลี่ยนแปลงสคีมาขั้นพื้นฐานส่วนใหญ่ แต่คุณอาจต้องดำเนินการต่อไปนี้ กำหนดเส้นทางการย้ายข้อมูลด้วยตนเองสำหรับการเปลี่ยนแปลงที่ซับซ้อนยิ่งขึ้น
การย้ายข้อมูลอัตโนมัติ
หากต้องการประกาศการย้ายข้อมูลอัตโนมัติระหว่างฐานข้อมูล 2 เวอร์ชัน ให้เพิ่ม
@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
เพื่อให้ข้อมูลเพิ่มเติมแก่ห้องแชทได้
ต้องสร้างเส้นทางการย้ายข้อมูลอย่างถูกต้อง กำหนดคลาสแบบคงที่ที่
นำ 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 จะเรียกใช้หลังจาก
การย้ายข้อมูลอัตโนมัติเสร็จสมบูรณ์
การย้ายข้อมูลด้วยตนเอง
ในกรณีที่การย้ายข้อมูลเกี่ยวข้องกับการเปลี่ยนแปลงสคีมาที่ซับซ้อน ห้องแชทอาจ
สามารถสร้างเส้นทางการย้ายข้อมูลที่เหมาะสมโดยอัตโนมัติ ตัวอย่างเช่น หาก
คุณตัดสินใจที่จะแบ่งข้อมูลในตารางให้เป็นสองตาราง ห้องไม่สามารถบอกได้
วิธีทำการแยก ในกรณีเช่นนี้ คุณต้อง
กำหนดเส้นทางการย้ายข้อมูลโดยใช้
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-testing
Maven ที่จะช่วยอำนวยความสะดวก
ทั้งการย้ายข้อมูลอัตโนมัติและการย้ายข้อมูลด้วยตนเอง สำหรับอาร์ติแฟกต์นี้เพื่อ
คุณต้องส่งออกสคีมาของฐานข้อมูลก่อน
ส่งออกสคีมา
ห้องแชทสามารถส่งออกข้อมูลสคีมาของฐานข้อมูลเป็นไฟล์ JSON ขณะคอมไพล์ได้ ไฟล์ JSON ที่ส่งออกจะแสดงประวัติสคีมาของฐานข้อมูล ร้านค้า ไฟล์เหล่านี้ไว้ในระบบควบคุมเวอร์ชัน เพื่อให้ห้องแชทสามารถสร้างเวอร์ชันที่ต่ำกว่า ฐานข้อมูลสำหรับวัตถุประสงค์ในการทดสอบและเปิดใช้การสร้างการย้ายข้อมูลอัตโนมัติ
ตั้งค่าตำแหน่งสคีมาโดยใช้ปลั๊กอิน Room Gradle
หากใช้ห้องแชทเวอร์ชัน 2.6.0 ขึ้นไป คุณจะใช้
ปลั๊กอิน Room Gradle และใช้
room
เพื่อระบุไดเรกทอรีสคีมา
ดึงดูด
plugins {
id 'androidx.room'
}
room {
schemaDirectory "$projectDir/schemas"
}
Kotlin
plugins {
id("androidx.room")
}
room {
schemaDirectory("$projectDir/schemas")
}
หากสคีมาฐานข้อมูลแตกต่างกันไปตามตัวแปร เวอร์ชัน หรือบิลด์
คุณต้องระบุสถานที่ตั้งอื่นโดยใช้แอตทริบิวต์ schemaDirectory()
กำหนดค่าหลายครั้ง โดยที่แต่ละรายการมี variantMatchName
เป็นรายการแรก
อาร์กิวเมนต์ การกำหนดค่าแต่ละรายการอาจตรงกับตัวแปรอย่างน้อย 1 รายการโดยอิงจาก
เทียบกับชื่อตัวแปร
โปรดตรวจสอบว่าข้อมูลเหล่านี้ครบถ้วนและครอบคลุมตัวแปรทั้งหมด นอกจากนี้ คุณยังใส่
schemaDirectory()
ที่ไม่มี variantMatchName
เพื่อจัดการกับตัวแปรที่ไม่ตรงกัน
การกำหนดค่าอื่นๆ อีก เช่น ในแอปที่มี 2 บิลด์
เวอร์ชัน demo
และ full
รวมถึงบิลด์ 2 ประเภทคือ 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"
}
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 Gradle ตั้งค่าตำแหน่งสคีมาโดยใช้ room.schemaLocation
ตัวประมวลผลคำอธิบายประกอบ
ระบบใช้ไฟล์ในไดเรกทอรีนี้เป็นอินพุตและเอาต์พุตสำหรับงาน Gradle บางงาน
เพื่อความถูกต้องและประสิทธิภาพของบิลด์ที่เพิ่มขึ้นและบิลด์ที่แคชไว้ คุณต้องใช้
สำหรับ Gradle
CommandLineArgumentProvider
เพื่อแจ้ง Gradle เกี่ยวกับไดเรกทอรีนี้
ขั้นแรก ให้คัดลอกคลาส RoomSchemaArgProvider
ที่แสดงด้านล่างลงใน
ไฟล์บิลด์ Gradle เมธอด asArguments()
ในบัตรคลาสตัวอย่าง
room.schemaLocation=${schemaDir.path}
ถึง KSP
หากคุณกำลังใช้ 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()]
}
}
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
ร่วมกับ
ไดเรกทอรีสคีมาที่ระบุ:
ดึงดูด
// 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 จากห้องแชทไปยังการทดสอบ
ทรัพยากร Dependency และเพิ่มตำแหน่งของสคีมาที่ส่งออกเป็นโฟลเดอร์ชิ้นงาน โดยทำดังนี้
ดึงดูด
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
ซึ่งอ่านไฟล์สคีมาที่ส่งออกได้ แพ็กเกจยังใช้
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}; }
จัดการเส้นทางการย้ายข้อมูลที่ขาดหายไปอย่างค่อยเป็นค่อยไป
หากห้องแชทไม่พบเส้นทางการย้ายข้อมูลเพื่ออัปเกรดฐานข้อมูลที่มีอยู่ใน
อุปกรณ์เป็นเวอร์ชันปัจจุบัน
IllegalStateException
เกิดขึ้น ถ้า
การที่ข้อมูลที่มีอยู่สูญหายได้ หากเส้นทางการย้ายข้อมูลหายไป ให้เรียก
เวลา
fallbackToDestructiveMigration()
ของเครื่องมือสร้างเมื่อคุณสร้างฐานข้อมูล
Kotlin
Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name") .fallbackToDestructiveMigration() .build()
Java
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name") .fallbackToDestructiveMigration() .build();
วิธีนี้จะทำให้ Room สร้างตารางขึ้นใหม่ในแอป ฐานข้อมูลเมื่อจำเป็นต้องดำเนินการย้ายข้อมูลเพิ่มและไม่มี เส้นทางการย้ายข้อมูลที่กำหนดไว้
หากคุณแค่ต้องการใช้ห้องเพื่อกลับไปใช้กิจกรรมนันทนาการที่ทำลายล้างในบางสถานการณ์
มีทางเลือกอีก 2-3 อย่างนอกเหนือจาก fallbackToDestructiveMigration()
ดังนี้
- หากประวัติสคีมาบางเวอร์ชันเกิดข้อผิดพลาดที่คุณแก้ไขไม่ได้
กับเส้นทางการย้ายข้อมูล ให้ใช้
fallbackToDestructiveMigrationFrom()
แทน วิธีนี้ระบุว่าคุณต้องการเปลี่ยนห้องแชทกลับไปเป็นโหมดที่เป็นอันตราย ใหม่ก็ต่อเมื่อย้ายข้อมูลจากเวอร์ชันที่เฉพาะเจาะจงเท่านั้น - หากต้องการให้ห้องแชทกลับไปใช้กิจกรรมนันทนาการที่ก่อให้เกิดความเสียหายกับอุปกรณ์เฉพาะตอนที่ย้ายข้อมูลเท่านั้น
จากฐานข้อมูลเวอร์ชันสูงกว่าไปยังเวอร์ชันที่ต่ำกว่า
fallbackToDestructiveMigrationOnDowngrade()
แทน
จัดการค่าเริ่มต้นของคอลัมน์เมื่ออัปเกรดเป็นห้อง 2.2.0
ในห้องแชท 2.2.0 ขึ้นไป คุณกำหนดค่าเริ่มต้นสำหรับคอลัมน์ได้โดยใช้
คำอธิบายประกอบ
@ColumnInfo(defaultValue = "...")
ในเวอร์ชันที่ต่ำกว่า 2.2.0 นี่เป็นวิธีเดียวในการกำหนดค่าเริ่มต้นสำหรับ
คือการระบุโดยตรงในคำสั่ง SQL ที่ดำเนินการแล้ว ซึ่งจะสร้าง
ค่าเริ่มต้นที่ห้องแชทไม่ทราบ ซึ่งหมายความว่าหากฐานข้อมูล
เดิมสร้างขึ้นโดยห้องแชทเวอร์ชันต่ำกว่า 2.2.0 ซึ่งจะอัปเกรดแอปเป็น
เมื่อใช้ห้อง 2.2.0 คุณอาจต้องระบุเส้นทางการย้ายข้อมูลพิเศษสำหรับ
ค่าเริ่มต้นที่มีอยู่ซึ่งคุณได้กำหนดไว้โดยไม่ต้องใช้ API สำหรับห้อง
ตัวอย่างเช่น สมมติว่าฐานข้อมูลเวอร์ชัน 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"); } };