[解決済み] 新しいテーブルが追加されただけで、ルームデータベースが移行される
質問
簡単なRoomデータベースがあるとします。
@Database(entities = {User.class}, version = 1)
abstract class AppDatabase extends RoomDatabase {
public abstract Dao getDao();
}
さて、新しいエンティティを追加します。
Pet
を追加し、バージョンを 2 に上げています。
@Database(entities = {User.class, Pet.class}, version = 2)
abstract class AppDatabase extends RoomDatabase {
public abstract Dao getDao();
}
もちろん、Room は例外を投げます。
java.lang.IllegalStateException: A migration from 1 to 2 is necessary.
仮に変更していないとすると
User
クラスを変更していないと仮定すると (したがって、すべてのデータは安全です)、私は単に新しいテーブルを作成する移行を提供する必要があります。そこで、私は Room によって生成されたクラスを調べ、新しいテーブルを作成するために生成されたクエリを検索し、それをコピーしてマイグレーションに貼り付けます。
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`))");
}
};
しかし、それを手動で行うのは不便だと思います。 ルームに伝える方法はありますか。 既存のテーブルを一切触らないので、データは安全です。私のために移行を作成してください。
どのように解決するのですか?
部屋
は
ではない
は良いマイグレーションシステムを持っていません。
2.1.0-alpha03
.
そのため、より良いマイグレーションシステムができるまでは、ルームで簡単にマイグレーションを行うためのいくつかの回避策があります。
のようなメソッドは存在しないので
@Database(createNewTables = true)
または
MigrationSystem.createTable(User::class)
のどちらかであるべきで、唯一の可能な方法は、実行することです。
CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))
の中に
migrate
メソッドの中で
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 スクリプトを取得するために、4つの方法があります。
1. 自分で書く
基本的には、Room が生成するスクリプトと一致するような上記のスクリプトを書く必要があります。この方法は可能ですが、実現は不可能です。(50個のフィールドがあると仮定して)
2. スキーマをエクスポートする
もしあなたが
exportSchema = true
の中に
@Database
というアノテーションを付けると、Room はプロジェクトフォルダの /schemas にデータベーススキーマを生成します。使用法は次のとおりです。
@Database(entities = [User::class], version = 2, exportSchema = true)
abstract class AppDatabase : RoomDatabase {
//...
}
の中に、以下の行が含まれていることを確認してください。
build.grade
に記述していることを確認してください。
kapt {
arguments {
arg("room.schemaLocation", "$projectDir/schemas".toString())
}
}
プロジェクトを実行またはビルドすると、JSONファイルが生成されます。
2.json
を取得します。このファイルには、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
},
つまり、上記のような
createSql
の中に
migrate
メソッドを使用します。
3. AppDatabase_Implからクエリを取得する
スキーマをエクスポートしたくない場合でも、プロジェクトを実行またはビルドすることでクエリを取得することができ、その結果
AppDatabase_Impl.java
ファイルを生成し、指定されたファイル内でクエリを実行することができます。
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))");
内
createAllTables
メソッド内に、すべてのエンティティの作成スクリプトがあります。これを取得して
migrate
メソッドに含めることができます。
4. アノテーションの処理。
ご推察の通り、Room では、上記のようなすべての
schema
を生成し、さらに
AppDatabase_Impl
といったファイルをコンパイル時に追加するアノテーション処理と
kapt "androidx.room:room-compiler:$room_version"
つまり、同じようにして、必要なcreate queryを全て生成してくれるアノテーション処理ライブラリを自作することもできるのです。
のルームアノテーションのためのアノテーション処理ライブラリを作るということです。
@Entity
と
@Database
. でアノテーションされたクラスを例にとると
@Entity
を例とします。以下は、その手順です。
-
新しい
StringBuilder
を作成し、"CREATE TABLE IF NOT EXISTS "を追加します。 -
テーブル名は
class.simplename
によって、あるいはtableName
のフィールド@Entity
. それをあなたのStringBuilder
-
次に、クラスの各フィールドについて、SQLのカラムを作成します。フィールドの名前、型、nullability をフィールド自身か
@ColumnInfo
アノテーションを使用します。 すべてのフィールドに対してid INTEGER NOT NULL
のスタイルで、カラムのStringBuilder
. -
による主キーの追加
@PrimaryKey
-
追加
ForeignKey
とIndices
が存在する場合 - 終了後、文字列に変換し、使用したい新しいクラスに保存します。例えば、以下のように保存します。
public final class UserSqlUtils {
public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))";
}
次に,これを
val MIGRATION_1_2 = object : Migration(1, 2){
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(UserSqlUtils().createTable)
}
}
私は自分用にこのようなライブラリを作りましたので、それをチェックアウトし、あなたのプロジェクトで使用することもできます。なお、私が作ったライブラリは完全なものではなく、テーブル作成に関する私の必要条件を満たすだけのものです。
お役に立ったでしょうか。
最新情報
この回答を書いている時点では、ルームバージョンは
2.1.0-alpha03
で、開発者にメールしたところ、次のような回答がありました。
でのマイグレーションシステムがより良くなることが期待されます。
2.2.0
残念ながら、私たちはより良いマイグレーション・システムをまだ持っていません。
関連
-
SpringBootApplication を型解決できない。
-
エラーの解決方法 jarfile XXX.jarにアクセスできません。
-
Methodのinvokeメソッド実装のJavaリフレクション
-
スレッド "main" での例外 java.lang.ArrayIndexOutOfBoundsException: 1
-
node js npm gruntインストール、elasticsearch-head 5.Xインストール
-
linux run jarfile Invalid or corrupt jarfile error.
-
スレッド "main" で例外発生 java.net.BindException: アドレスは既に使用中です。NET_Bind
-
javax.net.ssl.SSLException: 読み取りエラー: ssl=0xdeae5100: システムコール中の I/O エラー、接続 res
-
起動時にEclipseエラーが発生しました。起動中に内部エラーが発生しました。java.lang.NullPoin: "Javaツーリングの初期化 "中に内部エラーが発生しました。
-
Java(1)仕上げの基本概念+eclipseのインストール構成
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
スレッド "main "での例外をEclipseで解決 java.lang.Error: 未解決のコンパイル問題、コンパイラとパッケージの不整合
-
javaの模造品QQ WeChatのチャットルーム
-
スキャナは、タイプに解決することはできません最もルーキー初心者の質問
-
Eclipseプロンプトを実行する java仮想マシンを使用しない
-
サーブレットクラスのインスタンス化エラーの解決法
-
マスキング このリソースにアクセスするには、完全な認証が必要です。
-
spring aop アドバイスからの Null 戻り値が、サマリーのプリミティブ戻り値と一致しない。
-
javaでよく使われる英単語
-
java.lang.NoClassDefFoundError: org.apache.jasper.el.ELContextImpl クラスを初期化できませんでした。
-
IDEA パッケージステートメントの欠落