SQLite - 删除引用为外键的行

时间:2018-04-11 10:24:44

标签: android sqlite android-sqlite sqliteopenhelper

在尝试删除引用为外键的行时(出于实验目的),我得到了运行时异常。我知道我试图违反外键约束,我只是对logcat消息感到困惑,因为它引用了我的代码中完全不同的部分。

这是我的SQLiteOpenHelper onCreate方法:

@Override
public void onCreate(SQLiteDatabase db) {
    this.db = db;

    final String SQL_CREATE_CATEGORIES_TABLE = "CREATE TABLE " +
            CategoriesTable.TABLE_NAME + "( " +
            CategoriesTable._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
            CategoriesTable.COLUMN_NAME + " TEXT " +
            ")";

    final String SQL_CREATE_QUESTIONS_TABLE = "CREATE TABLE " +
            QuestionsTable.TABLE_NAME + " ( " +
            QuestionsTable._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
            QuestionsTable.COLUMN_QUESTION + " TEXT, " +
            QuestionsTable.COLUMN_OPTION1 + " TEXT, " +
            QuestionsTable.COLUMN_OPTION2 + " TEXT, " +
            QuestionsTable.COLUMN_OPTION3 + " TEXT, " +
            QuestionsTable.COLUMN_ANSWER_NR + " INTEGER, " +
            QuestionsTable.COLUMN_DIFFICULTY + " TEXT, " +
            QuestionsTable.COLUMN_CATEGORY + " INTEGER, " +
            "FOREIGN KEY(" + QuestionsTable.COLUMN_CATEGORY + ") REFERENCES " +
            CategoriesTable.TABLE_NAME + "(" + QuestionsTable._ID + ")" + 
            ")";

    db.execSQL(SQL_CREATE_CATEGORIES_TABLE);
    db.execSQL(SQL_CREATE_QUESTIONS_TABLE);
    fillCategoriesTable();
    fillQuestionsTable();
}

这里我用初始问题填充表格,然后我尝试从类别表中删除一行以故意违反外键约束(以测试它):

private void fillQuestionsTable() {
    Question q1 = new Question("Easy: A is correct",
            "A", "B", "C", 1, Question.DIFFICULTY_EASY, 1);
    addQuestion(q1);
    Question q2 = new Question("Medium: B is correct",
            "A", "B", "C", 2, Question.DIFFICULTY_MEDIUM, 2);
    addQuestion(q2);
    Question q3 = new Question("Medium: C is correct",
            "A", "B", "C", 3, Question.DIFFICULTY_MEDIUM, 3);
    addQuestion(q3);
    Question q4 = new Question("Hard: A is correct",
            "A", "B", "C", 1, Question.DIFFICULTY_HARD, 4);
    addQuestion(q4);
    Question q5 = new Question("Hard: B is correct",
            "A", "B", "C", 2, Question.DIFFICULTY_HARD, 5);
    addQuestion(q5);
    Question q6 = new Question("Hard: C is correct",
            "A", "B", "C", 3, Question.DIFFICULTY_HARD, 6);
    addQuestion(q6);

    db.execSQL("DELETE FROM " + CategoriesTable.TABLE_NAME + " WHERE ID = 1");
}

我收到运行时异常。 请注意我是故意违反外键约束而只是想了解异常。

我的第一个问题是:

1)崩溃是因为删除,但为什么logcat只讨论将问题添加到类别4,5和6(首先不在我的类别表中)?

  

插入难度时出错=硬选项1 =问题=硬:C是正确的category_id = 6 answer_nr = 3 option3 = C option2 = B                                                                                     android.database.sqlite.SQLiteConstraintException:FOREIGN KEY约束失败(代码787)

为什么不像我期望的那样谈论删除引用的列?

2)为什么它会抛出一个运行时异常,而试图添加一个类别在类别表中不存在的问题,只是忽略了SQLite命令,但是不会使应用程序崩溃? 通过尝试删除引用的列来违反外键约束时,运行时异常是预期的行为吗?

1 个答案:

答案 0 :(得分:1)

  

1)崩溃是因为删除,但为什么logcat只能说话   关于将问题添加到类别4,5和6(不在我的   类别表在第一位)?

没有崩溃或者至少显示的消息是因为它表示FOREIGN KEY约束违规,这发生在删除之前(addQuestion(q6);),因此删除永远不会发生。

解释FOREIGN KEY约束及其失败的原因有点困难,因为看起来你有混合表列。但我的猜测是,这本身不是问题。

更具体地说,您可以使用

定义FOREIGN KEY约束
FOREIGN KEY(column_in_this_table_which_you_have_right)
    REFERENCES the_other_table_which_you_have_right
        (column_in_the_other_table_????)

对于另一个表中的列,您QuestionsTable._ID应为CategoriesTable._ID。但是,它们可能被称为相同。所以这不会引起问题。

错误消息包括 category_id = 6

假设他们都解析为ID,那么你实际上是在说

如果** question 表的 category_id *列与中的行匹配(可以引用),则只允许在问题表中插入一行>类别表格 ID 列与 category_id 相同(即6)。

简而言之,显示的消息显示没有ID为6的类别。

  

为什么不像我那样谈论删除引用的列   期望?

因为它在到达db.execSQL("DELETE FROM " + CategoriesTable.TABLE_NAME + " WHERE ID = 1");

之前崩溃了
  

2)为什么它会抛出运行时异常,而试图添加一个   具有类别中不存在的类别的问题   表,只是忽略SQLite命令,但不会使应用程序崩溃?

因为在所有可能性中,您的应用程序将在许多地方崩溃而没有。你说这对设计来说非常重要。我毫不怀疑,如果忽略选项就是这种情况,那么你就会更加相反(即最好的选择,没有人强迫你定义约束)。虽然如果你想要的话,你可以查看4.2 Deferred Foreign Key Constraints

  

运行时异常是违反外部时的预期行为   尝试删除引用列的键约束?

重申你没有那么远。

但是,您可以查看4.3 ON DELETE and ON UPDATE Actions

编辑 - 混乱解释。

简而言之,崩溃实际上是由于DELETE约束,INSERT约束被捕获但显示为正在使用标准insert方法。如果您仔细查看,日志将显示此信息,如下所述。

以下是基于您汇总的代码的示例: -

04-11 21:47:10.241 1927-1927/? E/SQLiteDatabase: Error inserting option1=A category_id=6 option2=B option3=C difficulty=Hard answer_nr=3 question=Hard: C is correct
    android.database.sqlite.SQLiteConstraintException: foreign key constraint failed (code 19)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:775)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1469)
        at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1339)
        at askit.questionaire.DatabaseHelper.addQuestion(DatabaseHelper.java:70)
        at askit.questionaire.DatabaseHelper.fillQuestionsTable(DatabaseHelper.java:97)
        at askit.questionaire.DatabaseHelper.onCreate(DatabaseHelper.java:52)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:252)
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
        at askit.questionaire.DatabaseHelper.<init>(DatabaseHelper.java:17)
        at askit.questionaire.MainActivity.onCreate(MainActivity.java:14)
        at android.app.Activity.performCreate(Activity.java:5008)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
        at android.app.ActivityThread.access$600(ActivityThread.java:130)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4745)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
        at dalvik.system.NativeStart.main(Native Method)
04-11 21:47:10.245 1927-1927/? D/AndroidRuntime: Shutting down VM
04-11 21:47:10.245 1927-1927/? W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0xa6294288)
04-11 21:47:10.245 1927-1927/? E/AndroidRuntime: FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{askit.questionaire/askit.questionaire.MainActivity}: android.database.sqlite.SQLiteConstraintException: foreign key constraint failed (code 19)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
        at android.app.ActivityThread.access$600(ActivityThread.java:130)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4745)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
        at dalvik.system.NativeStart.main(Native Method)
     Caused by: android.database.sqlite.SQLiteConstraintException: foreign key constraint failed (code 19)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:727)
        at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:754)
        at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64)
        at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1665)
        at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1594)
        at askit.questionaire.DatabaseHelper.fillQuestionsTable(DatabaseHelper.java:99)
        at askit.questionaire.DatabaseHelper.onCreate(DatabaseHelper.java:52)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:252)
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
        at askit.questionaire.DatabaseHelper.<init>(DatabaseHelper.java:17)
        at askit.questionaire.MainActivity.onCreate(MainActivity.java:14)
        at android.app.Activity.performCreate(Activity.java:5008)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084) 
        at android.app.ActivityThread.access$600(ActivityThread.java:130) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:137) 
        at android.app.ActivityThread.main(ActivityThread.java:4745) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:511) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 
        at dalvik.system.NativeStart.main(Native Method) 

您看到的相当于: -

04-11 21:47:10.241 1927-1927/? E/SQLiteDatabase: Error inserting option1=A category_id=6 option2=B option3=C difficulty=Hard answer_nr=3 question=Hard: C is correct
    android.database.sqlite.SQLiteConstraintException: foreign key constraint failed (code 19)

然而,这并不会导致应用程序崩溃。

  • 如果您运行应用程序,删除数据库后,删除DELETE并查看日志,即使应用程序没有崩溃,您也会在日志中看到相同内容。

您需要做的是继续完成日志,您将找到相应的: -

04-11 21:47:10.245 1927-1927/? D/AndroidRuntime: Shutting down VM
04-11 21:47:10.245 1927-1927/? W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0xa6294288)
04-11 21:47:10.245 1927-1927/? E/AndroidRuntime: FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{askit.questionaire/askit.questionaire.MainActivity}: android.database.sqlite.SQLiteConstraintException: foreign key constraint failed (code 19)

这是崩溃,即 FATAL EXCEPTION:

如果你继续,你会看到由引起的,这是你看的地方所以: -

 Caused by: android.database.sqlite.SQLiteConstraintException: foreign key constraint failed (code 19)
    at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)
    at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:727)
    at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:754)
    at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64)
    at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1665)
    at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1594)
    at askit.questionaire.DatabaseHelper.fillQuestionsTable(DatabaseHelper.java:99)
    at askit.questionaire.DatabaseHelper.onCreate(DatabaseHelper.java:52)
    at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:252)
    at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
    at askit.questionaire.DatabaseHelper.<init>(DatabaseHelper.java:17)
    at askit.questionaire.MainActivity.onCreate(MainActivity.java:14)
    at android.app.Activity.performCreate(Activity.java:5008) 

如果您查看由引起的日志,您会看到提及您的软件包的第一行是: -

at askit.questionaire.DatabaseHelper.fillQuestionsTable(DatabaseHelper.java:99)

第99行(您的行号与包装不同)指向

enter image description here

  • 注意我更改了行以使用CONSTANT(CategoriesTable._ID)作为类别ID列。