Uygulamanızda özellik ekleyip değiştirirken Oda varlığınızı da değiştirmeniz gerekir temel veritabanı tablolarını ve bu değişiklikleri yansıtacak şekilde düzenleyin. Bu önemli veri tabanında bulunan kullanıcı verilerini korumak için bir uygulama güncelleme, veritabanı şemasını değiştirir.
Oda, artımlı taşıma için hem otomatik hem de manuel seçenekleri destekler. Otomatik taşıma işlemleri çoğu temel şema değişikliği için kullanılabilir ancak Daha karmaşık değişiklikler için taşıma yollarını manuel olarak tanımlayabilirsiniz.
Otomatik taşıma işlemleri
İki veritabanı sürümü arasında otomatik taşıma bildirmek için
@AutoMigration
ek açıklaması
autoMigrations
@Database
konumundaki mülk:
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 { ... }
Otomatik taşıma özellikleri
Oda, belirsiz şema değişiklikleri algılar ve
yeni bir taşıma planına izin verdiğinizde, derleme zamanı hatası vererek
bir
AutoMigrationSpec
.
Bu durum genellikle taşıma işlemi aşağıdakilerden birini içerdiğinde gerçekleşir:
- Tablo silme veya yeniden adlandırma.
- Sütun silme veya yeniden adlandırma
Odaya eklemek istediğiniz ek bilgileri AutoMigrationSpec
taşıma yollarının doğru şekilde oluşturulması gerekir. Şu özelliklere sahip statik bir sınıf tanımlayın:
RoomDatabase
sınıfınıza AutoMigrationSpec
uygular ve buna ek açıklama ekler
aşağıdakilerden biri veya daha fazlası:
AutoMigrationSpec
uygulamasını otomatik taşıma işleminde kullanmak için
ilgili @AutoMigration
ek açıklamasındaki spec
özelliği:
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 { } ... }
Otomatik taşıma işlemi tamamlandıktan sonra uygulamanızın daha fazla işlem yapması gerekiyorsa
kullanabileceğimiz
onPostMigrate()
.
Bu yöntemi AutoMigrationSpec
içinde uygularsanız Oda, şunu sonrasında çağırır:
otomatik taşıma tamamlanır.
Manuel taşıma işlemleri
Taşıma işleminin karmaşık şema değişiklikleri içerdiği durumlarda, Oda
uygun bir taşıma yolunu otomatik olarak
oluşturabilir. Örneğin,
bir tablodaki verileri iki tabloya bölmeye karar verdiniz. Oda,
nasıl yapılacağını göstereceğim. Bu gibi durumlarda
şunu uygulayarak bir taşıma yolu tanımlayın:
Migration
sınıfı.
Migration
sınıfı,
geçersiz kılarak startVersion
ve endVersion
Migration.migrate()
yöntemidir. Migration
sınıflarınızı veritabanı oluşturucunuza eklemek için şunu kullanın:
"the"
addMigrations()
yöntem:
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();
Taşıma yollarınızı tanımlarken bazı taşıma işlemleri için otomatik taşıma diğer kullanıcılar için manuel taşıma yapabilirsiniz. Hem otomatik hem de için manuel taşıma gerçekleştirir. Ardından Oda, manuel olarak taşıma.
Taşıma işlemlerini test etme
Taşıma işlemleri genellikle karmaşıktır ve yanlış tanımlanan bir taşıma işlemi
kilitlenmesine neden olabilir. Uygulamanızın kararlılığını korumak için
taşıma işlemleri. Oda, şu işlemlerde yardımcı olmak için bir room-testing
Maven yapısı sağlar:
test süreci için de kullanılabilir. Bu yapı için
veritabanınızın şemasını dışa aktarmanız gerekir.
Şemaları dışa aktarma
Oda, veritabanınızın şema bilgilerini derleme sırasında bir JSON dosyasına aktarabilir gerekir. Dışa aktarılan JSON dosyaları, veritabanınızın şema geçmişini temsil eder. Mağaza bu dosyaları sürüm kontrol sisteminizde kullanabilirsiniz. Böylece Oda, veritabanını test etmek ve otomatik taşıma oluşturmayı etkinleştirmek için kullanabilirsiniz.
Room Gradle Eklentisini kullanarak şema konumunu ayarlama
Oda 2.6.0 veya daha yeni bir sürüm kullanıyorsanız
Room Gradle Eklentisi'ni ziyaret edin ve
Şema dizinini belirten room
uzantısı.
Eski
plugins {
id 'androidx.room'
}
room {
schemaDirectory "$projectDir/schemas"
}
Kotlin
plugins {
id("androidx.room")
}
room {
schemaDirectory("$projectDir/schemas")
}
Veritabanı şemanız varyant, tür veya derlemeye göre farklılık gösteriyorsa
kullanmak istiyorsanız, schemaDirectory()
öğesini kullanarak farklı konumlar belirtmeniz gerekir
birden fazla kez yapılandır ve her biri ilk sırada variantMatchName
olacak şekilde
bağımsız değişkeninin önüne geçer. Her yapılandırma, modelin basit değerine göre bir veya daha fazla varyantla eşleşebilir
karşılaştırabilirsiniz.
Bu koşulların kapsamlı olduğundan ve tüm varyantları kapsadığından emin olun. İsterseniz
Eşleşmeyen varyantları işlemek için variantMatchName
içermeyen schemaDirectory()
değiştirebilirsiniz. Örneğin, iki derleme içeren bir uygulamada
demo
ve full
aromaları ile debug
ve release
iki derleme türü,
Aşağıdaki geçerli yapılandırmalar verilmiştir:
Eski
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")
}
Ek açıklama işlemci seçeneğini kullanarak şema konumunu ayarlama
Room'un 2.5.2 veya daha eski bir sürümünü kullanıyorsanız ya da
Room Gradle Eklentisi, şema konumunu room.schemaLocation
kullanarak ayarlayın
ek açıklama işlemcisi seçeneği olarak tanımlanmalıdır.
Bu dizindeki dosyalar bazı Gradle görevleri için giriş ve çıkış olarak kullanılır.
Artımlı ve önbelleğe alınmış derlemelerin doğruluğu ve performansı için
Gradle's
CommandLineArgumentProvider
Gradle'ı bu dizin hakkında bilgilendirdim.
İlk olarak, aşağıda gösterilen RoomSchemaArgProvider
sınıfını modülünüzün'e kopyalayın.
Gradle derleme dosyası. Örnek sınıftaki asArguments()
yöntemi başarılı
room.schemaLocation=${schemaDir.path}
- KSP
. KAPT
ve
javac
yerine bu değeri -Aroom.schemaLocation=${schemaDir.path}
olarak değiştirin.
Eski
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}")
}
}
Ardından derleme seçeneklerini, RoomSchemaArgProvider
öğesini
belirtilen şema dizini:
Eski
// 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"))
)
}
}
}
}
Tek bir taşıma işlemini test etme
Taşıma işlemlerinizi test etmeden önce
Oda'daki androidx.room:room-testing
Maven yapısı testinize eklendi
ve dışa aktarılan şemanın konumunu bir öğe klasörü olarak ekleyin:
derleme.gradle
Eski
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") }
Test paketi,
MigrationTestHelper
sınıfını kullanır. Paket ayrıca
JÜnite4
TestRule
arayüzü sayesinde, oluşturulan veritabanlarını yönetebilir.
Aşağıdaki örnekte tek bir taşıma işlemine yönelik bir test gösterilmektedir:
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. } }
Tüm taşıma işlemlerini test et
Tek bir artımlı taşımayı test etmek mümkün olsa da uygulamanız için tanımlanan tüm taşıma işlemlerini kapsayan bir test eklemeniz Bu, yakın zamanda oluşturulan ve en son yayınlanan ve tanımlanan taşıma işlemini izleyen daha eski bir örnek yollar.
Aşağıdaki örnekte tanımlı tüm taşıma işlemlerine yönelik bir test gösterilmektedir:
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}; }
Eksik taşıma yollarının sorunsuz bir şekilde ele alınması
Oda, mevcut bir veritabanını
cihazın mevcut sürümüne
IllegalStateException
gerçekleşir. Eğer
bir taşıma yolu eksik olduğunda mevcut verilerin kaybedilmesi kabul edilebilir, çağrı
"the"
fallbackToDestructiveMigration()
oluşturucu yöntemini eklemeniz gerekir:
Kotlin
Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name") .fallbackToDestructiveMigration() .build()
Java
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name") .fallbackToDestructiveMigration() .build();
Bu yöntem, Room'a uygulamanızın veri tabanınız için artımlı taşıma gerçekleştirmesi gerektiğinde ve tanımlanmış taşıma yolunu izleyin.
Yalnızca belirli durumlarda yıkıcı eğlence faaliyetlerinin bir parçası
durumlarda, fallbackToDestructiveMigration()
yerine birkaç alternatif vardır:
- Şema geçmişinizin belirli sürümleri, çözemediğiniz hatalara neden oluyorsa
taşıma yollarında
fallbackToDestructiveMigrationFrom()
. Bu yöntem, "Oda" özelliğinin yıkıcı içeriklere geri dönmesini istediğinizi belirtir yalnızca belirli sürümlerden geçiş yaparken yeniden oluşturma. - Odanın yalnızca taşıma sırasında yıkıcı yeniden oluşturma işlemlerine yedeklenmesini istiyorsanız
daha düşük bir sürüme geçmek için
fallbackToDestructiveMigrationOnDowngrade()
.
Oda 2.2.0'a yükseltme yaparken sütun varsayılan değerlerini işleme
Oda 2.2.0 ve sonraki sürümlerde, bir sütun için
ek açıklama
@ColumnInfo(defaultValue = "...")
.
2.2.0'dan önceki sürümlerde, bir
sütunu, doğrudan yürütülen bir SQL deyimi içinde tanımlanarak
odanın bilmediği varsayılan değere sahiptir. Bu, veri tabanının
İlk olarak, Odanızın 2.2.0'dan daha eski bir sürümü tarafından oluşturulmuş, uygulamanızı
Oda 2.2.0'ı kullanmak için
Oda API'lerini kullanmadan tanımladığınız mevcut varsayılan değerlerdir.
Örneğin, veritabanı sürüm 1'in bir Song
varlığını tanımladığını varsayalım:
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; }
Aynı veritabanına ait sürüm 2'nin, yeni bir NOT NULL
sütunu eklediğini varsayalım
ve sürüm 1'den sürüm 2'ye bir taşıma yolunu tanımlar:
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 ''"); } };
Bu durum, temel tabloda güncellemeler ile yenilenen değerler arasında bir tutarsızlığa neden olur
en az 24 saat sürer. Çünkü tag
sütunu için varsayılan değer yalnızca
sürüm 1'den sürüm 2'ye geçiş yolunda belirtilen tüm kullanıcıları
sürüm 2'den başlayan uygulamayı yüklediğinizde tag
için varsayılan değer yok
eklememiz gerekir.
Odanın 2.2.0'dan önceki sürümlerinde, bu tutarsızlık zararsızdır. Ancak,
Uygulama daha sonra Oda 2.2.0 veya sonraki bir sürümü kullanacak şekilde yükseltilir ve Song
varlığını değiştirir
sınıfını kullanarak tag
için varsayılan bir değer eklemek üzere
@ColumnInfo
ek açıklaması, Oda
bu tutarsızlığı görebilirsiniz. Bu durum, şemanın başarısız olmasına yol açar
doğrulamaları gerekir.
Sütun aşağıdaki durumlarda veritabanı şemasının tüm kullanıcılar arasında tutarlı olduğundan emin olmak için: önceki taşıma yollarınızda bildirilen varsayılan değerler için aşağıdakileri yapın: Oda 2.2.0 veya sonraki bir sürümü kullanmak için uygulamanızı ilk kez yükselttiğinizde:
- Aşağıdaki komutu kullanarak ilgili varlık sınıflarındaki sütun varsayılan değerlerini bildirin:
@ColumnInfo
ek açıklaması. - Veritabanı sürüm numarasını 1 artırın.
- Drop ve stratejiyi yeniden oluşturma değerini girin.
Aşağıdaki örnekte bu işlem gösterilmektedir:
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"); } };