J'ai rencontré l'erreur Foreign Key Constraint Failed (code 787)
lorsque j'ai essayé de mettre à niveau ma base de données. Le seul changement que j'ai fait a été d'essayer d'ajouter une quatrième entrée à mon InsertStatus
. J'ai regardé autour de moi et j'ai lu qu'en utilisant ON DELETE CASCADE
devrait résoudre mon problème, j'ai donc essayé de le placer sur toutes mes références FK et j'ai réessayé mais toujours le même problème.
Logcat pointe vers mon onUpgrade
et tous les DROP TABLES
dedans (j'ai essayé de le retirer un par un pour voir lesquels étaient mauvais et apparemment tous).
Suis-je en utilisant ON DELETE CASCADE
faux? Ou est-ce autre chose dans mon code?
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);
}
Selon le lien ci-dessous, vous insérez la valeur qui a échoué aux contraintes de clé étrangère signifie que vous avez ajouté une valeur de clé de départ qui n'existe pas dans la table parent
L'erreur ici est liée à la création d'une entité enfant avant que leur parent existe.
Le flux doit être:
--Créez Parent et obtenez l'ID parent. ---- Créer une entité enfant contenant une référence à l'ID parent.
Donc, si vous créez une entité enfant sans avoir au préalable un ID parent valide, cette erreur fatale vous sera renvoyée.
Dans mon cas, j'ai oublié de remplir une table parent avec des données. Lors de l'ajout d'un élément à la table enfant, cette erreur s'est produite. En cas de suppression des lignes foreignKeys
dans le fichier d'entité:
foreignKeys = [
ForeignKey(entity = SomeEntity::class, parentColumns = ["id"], childColumns = ["parent_id"]),
...
]
l'insertion est devenue possible. (Mais si vous le faites sans effacer les données de l'application, vous obtiendrez une exception 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.
)
Donc, ajoutez simplement les données nécessaires aux tables parentes.
MISE À JOUR
J'ai de nouveau cette exception.
Afin de tester quelle clé étrangère est confictive, commentez plusieurs d'entre elles et recompilez l'application. Supprimez également un installé ou effacez ses données. Après avoir démarré l'application, exécutez les demandes comme d'habitude. Si aucune exception ne s'est produite, décommentez une clé étrangère. Ensuite, supprimez à nouveau l'application, recompilez, ajoutez des données à la table. Faites-le jusqu'à ce que vous sachiez quelle clé étrangère est violée.
Dans mon cas, j'ai inséré 0
au lieu de null
dans une colonne, de sorte qu'une table parent ne contienne pas 0
.
Dans ma situation, le problème était que j'utilisais
@Insert(onConflict = OnConflictStrategy.REPLACE)
au lieu
@Update
dans @Dao.
vous pouvez désactiver la clé étrangère pour une requête spécifique et l'activer ensuite:
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;");
}
et l'utiliser comme:
disableForeignKeys();
//excute some queries
enableForeignKeys();
Assurez-vous que vous insérez d'abord l'objet qui se trouve dans la table parent. Je faisais la même erreur lors de certains tests (sans ajouter le parent correspondant, de sorte que PK de la table parent n'existait pas.)