Yalnızca yeni tablo eklenirse oda veritabanı geçişi


112

Basit bir Oda veritabanım olduğunu varsayalım:

@Database(entities = {User.class}, version = 1)
abstract class AppDatabase extends RoomDatabase {
    public abstract Dao getDao();
}

Şimdi, yeni bir varlık ekliyorum: Petve sürümü 2'ye çarpıyorum:

@Database(entities = {User.class, Pet.class}, version = 2)
abstract class AppDatabase extends RoomDatabase {
    public abstract Dao getDao();
}

Elbette, Room bir istisna oluşturur: java.lang.IllegalStateException: A migration from 1 to 2 is necessary.

UserSınıfı değiştirmediğimi varsayarsak (bu nedenle tüm veriler güvende olur), sadece yeni bir tablo oluşturan geçiş sağlamam gerekiyor. Bu yüzden, Room tarafından oluşturulan sınıflara bakıyorum, yeni tablomu oluşturmak için oluşturulan sorguyu arıyorum, kopyalayıp geçişe yapıştırıyorum:

final Migration MIGRATION_1_2 =
        new Migration(1, 2) {
            @Override
            public void migrate(@NonNull final SupportSQLiteDatabase database) {
                database.execSQL("CREATE TABLE IF NOT EXISTS `Pet` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))");
            }
        };

Ancak bunu manuel olarak yapmayı uygunsuz buluyorum. Room'a söylemenin bir yolu var mı: Mevcut tablodan hiçbirine dokunmuyorum, bu yüzden veriler güvende. Lütfen benim için göç yaratır mısınız?


Buna bir çözüm buldunuz mu?
Mikkel Larsen

3
Ben de aynı sorunu yaşadım ve sizin yaptığınız gibi düzelttim ve bir çözüm bulamadım. O zaman yalnız olmadığıma sevindim. :)
Mikkel Larsen

3
Burada aynı. Odanın veritabanı_impl içinde oluşturma sorgusunu oluşturmasını çok uygunsuz buluyorum, ancak geçiş başladığında sadece tabloyu oluşturamıyorum ....
JacksOnF1re

1
Böyle bir özellik için çok şey
verirdim

3
Bunun yardımcı olup olmayacağından emin değilim, ancak Room, veritabanı şemasını bir JSON dosyasına aktarma seçeneğine sahip. developer.android.com/training/data-storage/room/… Tabii ki bu yine de taşıma komut dosyasını manuel olarak eklemek anlamına gelir, ancak SQL ifadenizi almak için otomatik oluşturulan sınıflar arasında yönlendirme yapmanız gerekmez.
James Lendrem

Yanıtlar:


89

Oda yok DEĞİL en azından kadar olmaz, bir iyi Göç Sistemi var 2.1.0-alpha03.

Bu nedenle, daha iyi bir Geçiş Sistemine sahip olana kadar, Odada kolay Geçişlere sahip olmak için bazı geçici çözümler vardır.

@Database(createNewTables = true) Ya MigrationSystem.createTable(User::class)da diye bir yöntem olmadığından, biri ya da diğeri olması gerektiği için, mümkün olan tek yol koşmaktır.

CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))

migrateyönteminizin içinde .

val MIGRATION_1_2 = object : Migration(1, 2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))")
    }
}

SQL betiğinin üzerine çıkmak için 4 yolunuz var

1. Kendi kendinize yazın

Temel olarak, yukarıdaki betiği Room'un oluşturduğu betiğe uyacak şekilde yazmanız gerekir. Bu yol mümkün, mümkün değil. (50 alanınız olduğunu düşünün)

2. Şemayı Dışa Aktar

Ek açıklamanızın exportSchema = trueiçine eklerseniz @Database, Room proje klasörünüzün / şemaları içinde veritabanı şeması oluşturacaktır. Kullanım

@Database(entities = [User::class], version = 2, exportSchema = true)
abstract class AppDatabase : RoomDatabase {
   //...
}

build.gradeUygulama modülünüze aşağıdaki satırları eklediğinizden emin olun

kapt {
    arguments {
        arg("room.schemaLocation", "$projectDir/schemas".toString())
    }
} 

Projeyi çalıştırdığınızda veya 2.jsonoluşturduğunuzda, Oda veritabanınızdaki tüm sorguları içeren bir JSON dosyası alacaksınız .

  "formatVersion": 1,
  "database": {
    "version": 2,
    "identityHash": "325bd539353db508c5248423a1c88c03",
    "entities": [
      {
        "tableName": "User",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, PRIMARY KEY(`id`))",
        "fields": [
          {
            "fieldPath": "id",
            "columnName": "id",
            "affinity": "INTEGER",
            "notNull": true
          },

Böylece yukarıdakileri createSqlkendi migrateyönteminize dahil edebilirsiniz .

3. AppDatabase_Impl'den sorgu alın

Şemayı dışa aktarmak istemiyorsanız, AppDatabase_Impl.javadosyayı oluşturacak projeyi çalıştırarak veya oluşturarak sorguyu yine de alabilirsiniz . ve belirtilen dosya içinde sahip olabilirsiniz.

@Override
public void createAllTables(SupportSQLiteDatabase _db) {
  _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))");

createAllTablesYöntem içinde , tüm varlıkların oluşturma komut dosyaları olacaktır. Onu alabilir ve kendi migrateyönteminize dahil edebilirsiniz.

4. Ek Açıklama İşleme.

Tahmin edebileceğiniz gibi, Oda yukarıda belirtilenlerin tümünü schemave AppDatabase_Impldosyaları derleme süresi içinde ve eklediğiniz Ek Açıklama İşleme ile oluşturur.

kapt "androidx.room:room-compiler:$room_version"

Bu, sizin için gerekli tüm sorguları oluşturan kendi ek açıklama işleme kitaplığınızı da yapabileceğiniz anlamına gelir.

Fikir Odası ait ek açıklamalar için bir açıklama işleme kütüphanesi yapmaktır @Entityve @Database. Örneğin not alan bir sınıfı ele @Entityalalım. Bunlar takip etmeniz gereken adımlar

  1. Yeni bir tane oluşturun StringBuilderve "MEVCUT DEĞİLSE TABLO OLUŞTUR" seçeneğini ekleyin
  2. Tablo adını alanından class.simplenameveya tableNamealanına göre alın @Entity. EkleyinStringBuilder
  3. Daha sonra sınıfınızın her alanı için SQL sütunları oluşturun. Alanın adını, türünü, boş verilebilirliğini ya alanın kendisine göre ya da @ColumnInfoek açıklama ile alın. Her alan için, id INTEGER NOT NULLbir sütun stilini StringBuilder.
  4. Birincil anahtarları şu şekilde ekleyin: @PrimaryKey
  5. Varsa ForeignKeyve ekleyin Indices.
  6. Bitirdikten sonra onu dizeye dönüştürün ve kullanmak istediğiniz yeni bir sınıfa kaydedin. Örneğin, aşağıdaki gibi kaydedin
public final class UserSqlUtils {
  public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))";
}

O zaman bunu şu şekilde kullanabilirsiniz:

val MIGRATION_1_2 = object : Migration(1, 2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL(UserSqlUtils().createTable)
    }
}

Kendime öyle bir kitaplık yaptım ki siz de inceleyebilir, hatta projenizde kullanabilirsiniz. Yaptığım kitaplığın dolu olmadığını ve sadece tablo oluşturma gereksinimlerimi karşıladığını unutmayın.

Daha iyi Geçiş için RoomExtension

RoomExtension kullanan uygulama

Umarım yararlı olmuştur.

GÜNCELLEME

Bu cevabı yazdığım zaman, oda versiyonu öyleydi 2.1.0-alpha03ve geliştiricilere e-posta gönderdiğimde bir yanıt aldım

Daha iyi bir Göç Sistemine sahip olması beklenmektedir. 2.2.0

Ne yazık ki, hala daha iyi bir Göç Sisteminden yoksun.


3
Oda 2.2.x'in daha iyi bir geçişe sahip olacağını okuduğunuz yeri gösterebilir misiniz? Bu iddiayı yapan hiçbir şey bulamıyorum ve şu anda 2.1.0 beta üzerinde çalıştığımız için, 2.2.0'da ne olduğu şu anda bilinmiyor gibi görünüyor.
jkane001

1
@ jkane001 Oda geliştiricilerinden birine e-posta gönderdim ve bir yanıt aldım. 2.2.x (henüz?)
İle

6
Şu anda 2.2.2 sürümünde ve hala daha iyi bir geçiş yok :( Ancak, bu mükemmel bir cevap ve bunun için bana bir ton işten tasarruf
ettirdi

@androiddeveloper Ek Açıklama İşleme olan # 4 hariç tümü
musooff

1
@musooff Aslında tablo oluşturma eklemenin sorun olmadığını düşünüyorum. Kodu "createAllTables" işlevinden kopyalamanın en güvenli yoludur.
android geliştiricisi

5

Maalesef Room veri kaybı olmadan otomatik tablo oluşturmayı desteklemiyor.

Göçün yazılması zorunludur. Aksi takdirde, tüm verileri siler ve yeni tablo yapısını oluşturur.


0

App.gradle'ınızda defaultConfig'inize aşağıdaki gradle komutunu ekleyebilirsiniz:

javaCompileOptions {
        annotationProcessorOptions {
            arguments = ["room.schemaLocation":
                                 "$projectDir/schemas".toString()]
        }
    }

Bunu çalıştırdığınızda, geçiş nesnelerinize kopyalayıp yapıştırabileceğiniz ilgili CREATE TABLE ifadeleriyle birlikte bir tablo adları listesi derleyecektir. Tablo adlarını değiştirmeniz gerekebilir.

Örneğin bu benim oluşturduğum şemadan:

"tableName": "assets",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` INTEGER NOT NULL, `type` INTEGER NOT NULL, `base` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`asset_id`))"

Ve bu yüzden createSql ifadesini kopyalayıp yapıştırıyorum ve '$ {TABLE_NAME}' ifadesini tablo adı olarak 'assets' olarak değiştiriyorum ve işte otomatik olarak oluşturulan Room create ifadeleri.


-1

Bu şekilde yapabilirsin-

@Database(entities = {User.class, Pet.class}, version = 2)

abstract class AppDatabase extends RoomDatabase {
public abstract Dao getDao();
public abstract Dao getPetDao();
}

Kalan, yukarıda bahsettiğinizle aynı olacaktır.

 db = Room.databaseBuilder(this, AppDatabase::class.java, "your_db")
        .addMigrations(MIGRATION_1_2).build()

Referans - Daha fazlası için


-1

Bu durumda, bir geçiş yapmanız gerekmez, veritabanı örneği oluştururken .fallbackToDestructiveMigration () 'ı çağırabilirsiniz.

Misal:

    instance = Room.databaseBuilder(context, AppDatabase.class, "database name").fallbackToDestructiveMigration().build();

Ve veritabanı versiyonunu değiştirmeyi unutmayın.


Bu çözüm tüm verilerimi mevcut tablolardan kaldıracak.
Piotr Aleksander Chmielowski

Ne yazık ki evet. "Çökme yerine veritabanını yeniden oluşturmak için bu davranışı değiştirmek için bu yöntemi çağırabilirsiniz. Bunun Oda tarafından yönetilen veritabanı tablolarındaki tüm verileri sileceğini unutmayın."
rudicjovan

-2

Belki bu durumda (diğerlerini değiştirmeden yalnızca yeni bir tablo oluşturduysanız) bunu herhangi bir geçiş oluşturmadan yapabilirsiniz?


1
Hayır, bu durumda oda günlükleri atar: java.lang.IllegalStateException: {old_version} sürümünden {new_version} sürümüne geçiş gerekli
Max Makeichik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.