データベースをアップグレードしようとすると、Foreign Key Constraint Failed (code 787)
エラーに遭遇しました。私が行った唯一の変更は、InsertStatus
に4番目のエントリを追加することでした。私は周りを見回し、ON DELETE CASCADE
を使用して問題を解決できることを確認したので、すべてのFK参照に配置して、同じ問題をもう一度試しました。
Logcatは私のonUpgrade
とその中のすべてのDROP TABLES
を指しています(私は一度に1つずつ削除して、どれが不良で、すべてが明らかにそうであるかを確認しました)。
ON DELETE CASCADE
を間違って使用していますか?それとも私のコードの他の何かですか?
InsertStatus
void InsertStatus(SQLiteDatabase db) {
ContentValues cv = new ContentValues();
cv.put(colStatusID, 0);
cv.put(colStatClass, "Active");
db.insert(statTable, colStatusID, cv);
cv.put(colStatusID, 1);
cv.put(colStatClass, "Settled");
db.insert(statTable, colStatusID, cv);
cv.put(colStatusID, 2);
cv.put(colStatClass, "Terminated");
db.insert(statTable, colStatusID, cv);
cv.put(colStatusID, 3);
cv.put(colStatClass, "");
db.insert(statTable, colStatusID, cv);
}
DatabaseHelper
db.execSQL("CREATE TABLE " + termsTable + " (" + colTermsID + " INTEGER PRIMARY KEY , " + colTermsClass + " TEXT)");
db.execSQL("CREATE TABLE " + periodTable + " (" + colPeriodID + " INTEGER PRIMARY KEY , " + colPeriodClass + " TEXT)");
db.execSQL("CREATE TABLE " + statTable + " (" + colStatusID + " INTEGER PRIMARY KEY , " + colStatClass + " TEXT)");
db.execSQL("CREATE TABLE " + accountsTable + " (" + colID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
colName + " TEXT, " +
colAmount + " Integer, " +
colPurpose + " TEXT, " +
colTerms + " INTEGER NOT NULL, " +
colPeriod +" INTEGER NOT NULL, " +
colBalance +" INTEGER, "+
colStatus + " INTEGER DEFAULT '1'," +
colDate + " TEXT, " +
colEditDate + " TEXT, " +
"FOREIGN KEY (" + colTerms + ") REFERENCES " + termsTable + " (" + colTermsID + ") ON DELETE CASCADE," +
"FOREIGN KEY (" + colPeriod + ") REFERENCES " + periodTable + " (" + colPeriodID + ") ON DELETE CASCADE," +
"FOREIGN KEY (" + colStatus + ") REFERENCES " + statTable + " (" + colStatusID + ") ON DELETE CASCADE);");
db.execSQL("CREATE TABLE " + payTable + " (" + colPayID + " INTEGER PRIMARY KEY , " +
colGroupID + " INTEGER NOT NULL, " +
colPayBal + " TEXT, " +
colInterest + " TEXT, " +
colPayDue + " TEXT, " +
colDateDue + " TEXT, " +
colPaid + " Integer, " +
"FOREIGN KEY (" + colGroupID + ") REFERENCES " + accountsTable + " (" + colID + ") ON DELETE CASCADE);");
db.execSQL("CREATE VIEW " + viewAccs +
" AS SELECT " + accountsTable + "." + colID + " AS _id," +
" " + accountsTable + "." + colName + "," +
" " + accountsTable + "." + colAmount + "," +
" " + accountsTable + "." + colPurpose + "," +
" " + termsTable + "." + colTermsClass + "," +
" " + periodTable + "." + colPeriodClass + "," +
" " + accountsTable+ "." + colBalance + "," +
" " + statTable + "." + colStatClass + "," +
" " + accountsTable + "." + colDate + "," +
" " + accountsTable + "." + colEditDate + "" +
" FROM " + accountsTable +
" JOIN " + termsTable + " ON " + accountsTable + "." + colTerms + " = " + termsTable + "." + colTermsID +
" JOIN " + periodTable + " ON " + accountsTable + "." + colPeriod + " = " + periodTable + "." + colPeriodID +
" JOIN " + statTable + " ON " + accountsTable + "." + colStatus + " = " + statTable + "." + colStatusID );
db.execSQL("CREATE VIEW " + viewPmnts +
" AS SELECT " + payTable + "." + colPayID + " AS _id," +
" " + accountsTable + "." + colID + "," +
" " + payTable + "." + colGroupID + "," +
" " + payTable + "." + colPayBal + "," +
" " + payTable + "." + colInterest + "," +
" " + payTable + "." + colPayDue + "," +
" " + payTable + "." + colDateDue + "," +
" " + payTable + "." + colPaid + "" +
" FROM " + payTable +
" JOIN " + accountsTable + " ON " + payTable + "." + colGroupID + " = " + accountsTable + "." + colID );
InsertTerms(db);
InsertPeriods(db);
InsertStatus(db);
}
onUpgrade
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + accountsTable);
db.execSQL("DROP TABLE IF EXISTS " + termsTable);
db.execSQL("DROP TABLE IF EXISTS " + periodTable);
db.execSQL("DROP TABLE IF EXISTS " + statTable);
db.execSQL("DROP TABLE IF EXISTS " + payTable);
db.execSQL("DROP TRIGGER IF EXISTS acc_id_trigger");
db.execSQL("DROP TRIGGER IF EXISTS acc_id_trigger22");
db.execSQL("DROP TRIGGER IF EXISTS fk_accterm_termid");
db.execSQL("DROP TRIGGER IF EXISTS fk_accperiod_periodid");
db.execSQL("DROP TRIGGER IF EXISTS fk_accpay_payid");
db.execSQL("DROP TRIGGER IF EXISTS fk_accstat_statid");
db.execSQL("DROP VIEW IF EXISTS " + viewAccs);
db.execSQL("DROP VIEW IF EXISTS " + viewPmnts);
onCreate(db);
}
以下のリンクによると、外部キーの制約に失敗した値を挿入しますは、親テーブルに存在しない外部キーの値を追加したことを意味します
ここでのエラーは、親が存在する前に子エンティティを作成することに関連しています。
フローは次のようになります。
-親を作成し、親IDを取得します。 ----親IDへの参照を含む子エンティティを作成します。
したがって、最初に有効な親IDを持たずに子エンティティを作成すると、この致命的なエラーがスローされます。
私の場合、親テーブルにデータを入力するのを忘れていました。子テーブルにアイテムを追加すると、このエラーが発生しました。 Entity-fileのforeignKeys
行を削除すると、次のようになります。
foreignKeys = [
ForeignKey(entity = SomeEntity::class, parentColumns = ["id"], childColumns = ["parent_id"]),
...
]
挿入可能となりました。 (ただし、アプリのデータを消去せずにこれを行うと、例外Java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
が発生します)
したがって、必要なデータを親テーブルに追加するだけです。
[〜#〜]更新[〜#〜]
私は再びこの例外を受けました。
競合している外部キーをテストするには、それらのいくつかにコメントを付けて、アプリケーションを再コンパイルします。また、インストールされているものを削除するか、データを消去します。アプリを起動した後、通常どおりリクエストを実行します。例外が発生しなかった場合は、1つの外部キーのコメントを外します。次に、アプリを再度削除し、再コンパイルして、テーブルにデータを追加します。違反している外部キーがわかるまで繰り返します。
私の場合、列にnull
ではなく0
を挿入して、親テーブルに0
が含まれないようにしました。
私の状況では、問題は私が
@Insert(onConflict = OnConflictStrategy.REPLACE)
代わりに
@Update
@Dao。
特定のクエリの外部キーを無効にして、その後有効にすることができます。
public static void disableForeignKeys() {
database.getInstance().getOpenHelper().getReadableDatabase().execSQL("PRAGMA foreign_keys = off;");
}
public static void enableForeignKeys() {
database.getInstance().getOpenHelper().getReadableDatabase().execSQL("PRAGMA foreign_keys = on;");
}
そしてそれを次のように使用します:
disableForeignKeys();
//excute some queries
enableForeignKeys();
最初に親テーブルにあるオブジェクトを必ず挿入してください。いくつかのテストを行っているときに同じミスをしていました(対応する親を追加しないため、親テーブルのPKは存在しませんでした)。