Nie zakładajmy, że mam prostą bazę danych Pokoju:
@Database(entities = {User.class}, version = 1)
abstract class AppDatabase extends RoomDatabase {
public abstract Dao getDao();
}
Teraz dodaję nową jednostkę: Pet
i wbijam wersję na 2:
@Database(entities = {User.class, Pet.class}, version = 2)
abstract class AppDatabase extends RoomDatabase {
public abstract Dao getDao();
}
Oczywiście Room rzuca wyjątek: java.lang.IllegalStateException: A migration from 1 to 2 is necessary.
Zakładając, że nie zmieniłem User
klasy (więc wszystkie dane są bezpieczne), muszę zapewnić migrację, która po prostu tworzy nową tabelę. Tak więc patrzę na klasy generowane przez Room, szukam wygenerowanego zapytania, aby utworzyć nową tabelę, kopiując ją i wklejając do migracji:
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`))");
}
};
Jednak wykonanie tego ręcznie jest dla mnie niewygodne. Czy istnieje sposób, aby powiedzieć Roomowi: nie dotykam żadnej z istniejącej tabeli, więc dane są bezpieczne. Proszę utworzyć migrację dla mnie?
java
android
database-migration
android-room
Piotr Aleksander Chmielowski
źródło
źródło
Odpowiedzi:
Pokój ma nie mieć dobry system migracji, przynajmniej nie do czasu
2.1.0-alpha03
.Tak więc, dopóki nie będziemy mieć lepszego systemu migracji, istnieją pewne obejścia umożliwiające łatwe migracje w pokoju.
Ponieważ nie ma takiej metody jak
@Database(createNewTables = true)
lubMigrationSystem.createTable(User::class)
, która powinna być taka czy inna, jedyną możliwą drogą jest bieganieCREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))
wewnątrz twojej
migrate
metody.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`))") } }
Aby uzyskać powyższy skrypt SQL , masz 4 sposoby
1. Napisz sam
Zasadniczo musisz napisać powyższy skrypt, który będzie pasował do skryptu generowanego przez Room. Ten sposób jest możliwy, niewykonalny. (Weź pod uwagę, że masz 50 pól)
2. Schemat eksportu
Jeśli umieścisz
exportSchema = true
wewnątrz@Database
adnotacji, Room wygeneruje schemat bazy danych w / schemas folderu projektu. Użycie jest@Database(entities = [User::class], version = 2, exportSchema = true) abstract class AppDatabase : RoomDatabase { //... }
Upewnij się, że w
build.grade
module aplikacji uwzględniono poniższe wierszekapt { arguments { arg("room.schemaLocation", "$projectDir/schemas".toString()) } }
Kiedy uruchomisz lub zbudujesz projekt, otrzymasz plik JSON
2.json
, który zawiera wszystkie zapytania w Twojej bazie danych Room."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 },
Możesz więc uwzględnić powyższe
createSql
w swojejmigrate
metodzie.3. Uzyskaj zapytanie z AppDatabase_Impl
Jeśli nie chcesz eksportować schematu, nadal możesz uzyskać zapytanie, uruchamiając lub budując projekt, który wygeneruje
AppDatabase_Impl.java
plik. i w określonym pliku, który możesz mieć.@Override public void createAllTables(SupportSQLiteDatabase _db) { _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))");
W ramach
createAllTables
metody zostaną utworzone skrypty wszystkich podmiotów. Możesz to zdobyć i uwzględnić w swojejmigrate
metodzie.4. Przetwarzanie adnotacji.
Jak można się domyślać, Pokój generuje wszystkie wyżej wymienione
schema
, aAppDatabase_Impl
pliki w czasie kompilacji oraz z dopiskiem przetworzeniu, które dodasz zkapt "androidx.room:room-compiler:$room_version"
Oznacza to, że możesz również zrobić to samo i stworzyć własną bibliotekę przetwarzania adnotacji, która generuje wszystkie niezbędne zapytania tworzenia.
Chodzi o to, aby utworzyć bibliotekę przetwarzania adnotacji dla adnotacji pokojów
@Entity
i@Database
. Weźmy na przykład zajęcia oznaczone adnotacją@Entity
. Oto kroki, które będziesz musiał wykonaćStringBuilder
i dołącz „UTWÓRZ TABELĘ, JEŚLI NIE ISTNIEJE”class.simplename
lub wedługtableName
pola@Entity
. Dodaj to do swojegoStringBuilder
@ColumnInfo
adnotacji. Dla każdego pola musisz dodaćid INTEGER NOT NULL
styl kolumny do swojegoStringBuilder
.@PrimaryKey
ForeignKey
iIndices
jeśli istnieje.public final class UserSqlUtils { public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))"; }
Następnie możesz go użyć jako
val MIGRATION_1_2 = object : Migration(1, 2){ override fun migrate(database: SupportSQLiteDatabase) { database.execSQL(UserSqlUtils().createTable) } }
Zrobiłem dla siebie taką bibliotekę, którą możesz sprawdzić, a nawet wykorzystać w swoim projekcie. Zwróć uwagę, że biblioteka, którą utworzyłem, nie jest pełna i po prostu spełnia moje wymagania dotyczące tworzenia tabel.
RoomExtension dla lepszej migracji
Aplikacja korzystająca z RoomExtension
Mam nadzieję, że to było przydatne.
AKTUALIZACJA
W chwili pisania tej odpowiedzi wersja pokoju była
2.1.0-alpha03
i kiedy wysłałem e-mail do programistów, otrzymałem odpowiedźNiestety wciąż brakuje nam lepszego systemu migracji.
źródło
Przepraszamy, Room nie obsługuje automatycznego tworzenia tabel bez utraty danych.
Zapis migracji jest obowiązkowy. W przeciwnym razie usunie wszystkie dane i utworzy nową strukturę tabeli.
źródło
Możesz to zrobić w ten sposób-
@Database(entities = {User.class, Pet.class}, version = 2) abstract class AppDatabase extends RoomDatabase { public abstract Dao getDao(); public abstract Dao getPetDao(); }
Pozostałe będą takie same, jak wspomniałeś powyżej-
db = Room.databaseBuilder(this, AppDatabase::class.java, "your_db") .addMigrations(MIGRATION_1_2).build()
Odniesienie - więcej
źródło
Możesz dodać następujące polecenie gradle do domyślnej konfiguracji w pliku app.gradle:
javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] } }
Po uruchomieniu skompiluje listę nazw tabel z odpowiednimi instrukcjami CREATE TABLE, z których można po prostu skopiować i wkleić do obiektów migracji. Może być konieczna zmiana nazw tabel.
Na przykład to jest z mojego wygenerowanego schematu:
"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`))"
I tak kopiuję, wklejam instrukcję createSql i zmieniam „$ {TABLE_NAME}” na „asset” nazwę tabeli i voila, automatycznie generowane instrukcje Room create.
źródło
W takim przypadku nie musisz wykonywać migracji, możesz wywołać .fallbackToDestructiveMigration () podczas tworzenia instancji bazy danych.
Przykład:
instance = Room.databaseBuilder(context, AppDatabase.class, "database name").fallbackToDestructiveMigration().build();
I nie zapomnij zmienić wersji bazy danych.
źródło
Może w tym przypadku (jeśli utworzyłeś tylko nową tabelę bez zmiany innych) możesz to zrobić bez tworzenia żadnych migracji?
źródło