Несоответствие внешнего ключа при вставке данных - Android Studio SQLite - PullRequest
0 голосов
/ 06 августа 2020

Я пытаюсь вставить значения в базу данных, где первичный ключ ссылается на другую таблицу. Поскольку указанная таблица содержит все возможные значения, которые можно вставить в новую таблицу, несоответствия внешнего ключа быть не должно. Возможно, вы, ребята, увидите это там, где я этого не вижу.

package com.example.guviolinapp

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper

class DBHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, 1) {

    private lateinit var db: SQLiteDatabase

    companion object{
        //Companion object that holds the name of everything in the database
        private val DATABASE_NAME = "violininfo.db"
        private val GAME_TABLE = "game_table"
        private val COL_GAME_NUMBER = "game_number"
        private val COL_GAME_SCORE = "game_score"
        private val SCORE_TABLE = "score_table"
        private val COL_SCORE_LEVEL = "quiz_level"
        private val COL_QUIZ_SCORE = "quiz_score"
        private val QUESTION_TABLE = "questions_table"
        private val COL_QUIZ_LEVEL = "quiz_level"
        private val COL_QUESTION_NUM = "question_number"
        private val COL_QUIZ_QUESTION = "quiz_questions"
        private val COL_QUIZ_OPTION_ONE = "quiz_options_one"
        private val COL_QUIZ_OPTION_TWO = "quiz_options_two"
        private val COL_QUIZ_OPTION_THREE = "quiz_options_three"
        private val COL_QUIZ_OPTION_FOUR = "quiz_options_four"
        private val COL_QUIZ_ANSWER = "quiz_answer"
    }

    //On create of the Application creating the actual database in the device
    override fun onCreate(db: SQLiteDatabase) {
        this.db = db

        //Variable that holds the SQL query to create the table
        val CREATE_GAME_TABLE =
                "CREATE TABLE ${GAME_TABLE}" +
                "($COL_GAME_NUMBER INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "$COL_GAME_SCORE INTEGER" +
                ")"
        //Variable that holds the SQL query to create the table
        val CREATE_QUESTION_TABLE =
                "CREATE TABLE ${QUESTION_TABLE}" +
                "($COL_QUIZ_LEVEL INTEGER, " +
                "$COL_QUESTION_NUM INTEGER, " +
                "$COL_QUIZ_QUESTION TEXT, " +
                "$COL_QUIZ_OPTION_ONE TEXT, " +
                "$COL_QUIZ_OPTION_TWO TEXT, " +
                "$COL_QUIZ_OPTION_THREE TEXT, " +
                "$COL_QUIZ_OPTION_FOUR TEXT, " +
                "$COL_QUIZ_ANSWER INTEGER, " +
                "PRIMARY KEY ($COL_QUIZ_LEVEL, $COL_QUESTION_NUM)" +
                ")"
        //Variable that holds the SQL query to create the table
        val CREATE_SCORE_TABLE =
                "CREATE TABLE ${SCORE_TABLE}" +
                "($COL_SCORE_LEVEL INTEGER PRIMARY KEY, " +
                "$COL_QUIZ_SCORE INTEGER, " +
                "FOREIGN KEY ($COL_SCORE_LEVEL) REFERENCES $QUESTION_TABLE($COL_QUIZ_LEVEL) ON DELETE CASCADE" +
                ")"

        //Functions that execute the sql script. The first one prevents null in the db
        db.execSQL(CREATE_QUESTION_TABLE)
        db.execSQL(CREATE_GAME_TABLE)
        db.execSQL(CREATE_SCORE_TABLE)
        fillQuestionsTable()
    }

    //Function that drops the database and remakes it for when the database is updated. It can be done two ways.
    //The app can be deleted, or the version can be increased
    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        val DROP_SCORE_TABLE = "DROP TABLE IF EXISTS $SCORE_TABLE"
        val DROP_GAME_TABLE = "DROP TABLE IF EXISTS $GAME_TABLE"
        val DROP_QUESTION_TABLE = "DROP TABLE IF EXISTS $QUESTION_TABLE"

        db.execSQL(DROP_SCORE_TABLE)
        db.execSQL(DROP_GAME_TABLE)
        db.execSQL(DROP_QUESTION_TABLE)
        onCreate(db)
    }

    override fun onConfigure(db: SQLiteDatabase) {
        super.onConfigure(db)
        db.setForeignKeyConstraintsEnabled(true)
    }


    //Function to insert values into the Score table
    fun insertScoreTable(quizLevel: Int, quizScore: Int): Boolean? {
        //Initializing the db
        val db = this.writableDatabase
        //Function to insert values that will be inserted into the database
        val cv = ContentValues()
        //Inserting the values with a specific identifier in which the value needs to be inserted
        cv.put(COL_SCORE_LEVEL, quizLevel)
        cv.put(COL_QUIZ_SCORE, quizScore)
        //Executing the actual insert database
        val res = db.insert(SCORE_TABLE, null, cv)
        return !res.equals(-1)
    }

    //Function to insert values into the Game table
    fun insertGameTable(gameNum: Int, gameScore: Int): Boolean? {
        //Initializing the db
        val db = this.writableDatabase
        //Function to insert values that will be inserted into the database
        val cv = ContentValues()
        //Inserting the values with a specific identifier in which the value needs to be inserted
        cv.put(COL_GAME_NUMBER, gameNum)
        cv.put(COL_GAME_SCORE, gameScore)
        //Executing the actual insert database
        val res = db.insert(GAME_TABLE, null, cv)
        return !res.equals(-1)
    }

    //Function to insert values into the Questions table
    private fun insertQuestionsTable(questionTable: QuestionsTable): Boolean? {
        //Function to insert values that will be inserted into the database
        val cv = ContentValues()
        //Inserting the values with a specific identifier in which the value needs to be inserted
        cv.put(COL_QUIZ_LEVEL, questionTable.quiz_level)
        cv.put(COL_QUESTION_NUM, questionTable.question_number)
        cv.put(COL_QUIZ_QUESTION, questionTable.quiz_questions)
        cv.put(COL_QUIZ_OPTION_ONE, questionTable.quiz_option_one)
        cv.put(COL_QUIZ_OPTION_TWO, questionTable.quiz_option_two)
        cv.put(COL_QUIZ_OPTION_THREE, questionTable.quiz_option_three)
        cv.put(COL_QUIZ_OPTION_FOUR, questionTable.quiz_option_four)
        cv.put(COL_QUIZ_ANSWER, questionTable.quiz_answer)
        //Executing the actual insert database
        val res = db.insert(QUESTION_TABLE, null, cv)
        return !res.equals(-1)
    }

    //Function to insert all the questions into the database
    private fun fillQuestionsTable() {
        //Variable with the data class holding the values for each question
        val l1q1 = QuestionsTable(1, 1, "What note is this?", "Minim",
            "Semibreve", "Crochet", "Quaver", 4)
        //Calling the insertQuestionsTable to insert the question
        insertQuestionsTable(l1q1)

        val l1q2 = QuestionsTable(1, 2, "What note is this?", "Quaver",
            "Minim", "Crochet", "Semibreve", 3)
        insertQuestionsTable(l1q2)

        val l1q3 = QuestionsTable(1, 3, "What rest is this?", "Whole",
            "Eighth", "Half", "Quarter", 2)
        insertQuestionsTable(l1q3)

        val l1q4 = QuestionsTable(1, 4, "What rest is this?", "Quarter",
            "Eighth", "Whole", "Half", 1)
        insertQuestionsTable(l1q4)

        val l1q5 = QuestionsTable(1, 5, "What rest is this?", "Whole",
        "Eighth", "Quarter", "Half", 1)
        insertQuestionsTable(l1q5)

        val l1q6 = QuestionsTable(1, 6, "What rest is this?", "Quarter",
            "Eighth", "Whole", "Half", 4)
        insertQuestionsTable(l1q6)

        val l1q7 = QuestionsTable(1, 7, "What note is this?", "Minim",
            "Quaver", "Semibreve", "Crochet", 1)
        insertQuestionsTable(l1q7)

        val l1q8 = QuestionsTable(1, 8, "What note is this?", "Quaver",
            "Semibreve", "Crochet", "Minim", 2)
        insertQuestionsTable(l1q8)

        val l1q9 = QuestionsTable(1, 9, "How many beats in this rest?", "Half Beat",
            "One Beat", "Two Beats", "Four Beats", 3)
        insertQuestionsTable(l1q9)

        val l1q10 = QuestionsTable(1, 10, "How many beats in this rest?", "Four Beats",
            "One Beat", "Two Beats", "Half Beat", 4)
        insertQuestionsTable(l1q10)

        val l1q11 = QuestionsTable(1, 11, "How many beats in this rest?", "One Beat",
            "Half Beat", "Two Beats", "Four Beats", 4)
        insertQuestionsTable(l1q11)

        val l1q12 = QuestionsTable(1, 12, "How many beats in this note?", "Four Beats",
            "One Beat", "Half Beat", "Two Beats", 2)
        insertQuestionsTable(l1q12)

        val l1q13 = QuestionsTable(1, 13, "How many beats in this rest?", "Two Beats",
            "One Beat", "Four Beats", "Half Beat", 2)
        insertQuestionsTable(l1q13)

        val l1q14 = QuestionsTable(1, 14, "How many beats in this note?", "Two Beats",
            "Half Beat", "Four Beats", "One Beat", 3)
        insertQuestionsTable(l1q14)

        val l1q15 = QuestionsTable(1, 15, "How many beats in this note?", "One Beat",
            "Four Beats", "Half Beat", "Two Beats", 3)
        insertQuestionsTable(l1q15)

        val l2q1 = QuestionsTable(2, 1, "What string is this?", "D",
            "A", "G", "E", 1)
        insertQuestionsTable(l2q1)

        val l2q2 = QuestionsTable(2, 2, "What note is this?", "F sharp",
            "E", "G sharp", "A", 1)
        insertQuestionsTable(l2q2)

        val l2q3 = QuestionsTable(2, 3, "What note is this?", "G",
            "B", "A", "C", 2)
        insertQuestionsTable(l2q3)

        val l2q4 = QuestionsTable(2, 4, "What note is this?", "E",
            "F sharp", "A", "G sharp", 4)
        insertQuestionsTable(l2q4)

        val l2q5 = QuestionsTable(2, 5, "What note is this?", "G",
            "A", "B", "C", 4)
        insertQuestionsTable(l2q5)

        val l2q6 = QuestionsTable(2, 6, "What string is this?", "A",
            "D", "E", "G", 3)
        insertQuestionsTable(l2q6)

        val l2q7 = QuestionsTable(2, 7, "What note is this?", "B",
            "A", "C", "G", 2)
        insertQuestionsTable(l2q7)

        val l2q8 = QuestionsTable(2, 8, "What note is this?", "E",
            "D", "G", "F sharp", 4)
        insertQuestionsTable(l2q8)

        val l2q9 = QuestionsTable(2, 9, "What note is this?", "A",
            "B", "C sharp", "D", 4)
        insertQuestionsTable(l2q9)

        val l2q10 = QuestionsTable(2, 10, "What note is this?", "F sharp",
            "D", "E", "G", 3)
        insertQuestionsTable(l2q10)

        val l2q11 = QuestionsTable(2, 11, "What note is this?", "A",
            "B", "C sharp", "D", 2)
        insertQuestionsTable(l2q11)

        val l2q12 = QuestionsTable(2, 12, "What string is this?", "D",
            "G", "E", "A", 4)
        insertQuestionsTable(l2q12)

        val l2q13 = QuestionsTable(2, 13, "What note is this?", "C sharp",
            "D", "B", "A", 1)
        insertQuestionsTable(l2q13)

        val l2q14 = QuestionsTable(2, 14, "What note is this?", "E",
            "G", "F sharp", "D", 2)
        insertQuestionsTable(l2q14)

        val l2q15 = QuestionsTable(2, 15, "What string is this?", "A",
            "D", "G", "E", 3)
        insertQuestionsTable(l2q15)

        val l3q1 = QuestionsTable(3, 1, "What is this called?", "Treble Clef",
            "Tie", "Stave", "Flat", 3)
        insertQuestionsTable(l3q1)

        val l3q2 = QuestionsTable(3, 2, "What is this called?", "Stave",
            "Bass Clef", "Sharp", "Treble Clef", 3)
        insertQuestionsTable(l3q2)

        val l3q3 = QuestionsTable(3, 3, "What is this called?", "Bar",
            "Stave", "Sharp", "Bass Clef", 1)
        insertQuestionsTable(l3q3)

        val l3q4 = QuestionsTable(3, 4, "What is this called?", "Time Signature",
            "Treble Clef", "Natural", "Bass Clef", 2)
        insertQuestionsTable(l3q4)

        val l3q5 = QuestionsTable(3, 5, "What is this called?", "Bar",
            "Key Signature", "Sharp", "Tie", 2)
        insertQuestionsTable(l3q5)

        val l3q6 = QuestionsTable(3, 6, "What is this called?", "Treble Clef",
            "Key Signature", "Bar", "Tie", 4)
        insertQuestionsTable(l3q6)

        val l3q7 = QuestionsTable(3, 7, "What is this called?", "Flat",
            "Sharp", "Bar", "Natural", 1)
        insertQuestionsTable(l3q7)

        val l3q8 = QuestionsTable(3, 8, "What is this called?", "Treble Clef",
            "Flat", "Sharp", "Bass Clef", 4)
        insertQuestionsTable(l3q8)

        val l3q9 = QuestionsTable(3, 9, "What is this called?", "Sharp",
            "Time Signature", "Flat", "Tie", 2)
        insertQuestionsTable(l3q9)

        val l3q10 = QuestionsTable(3, 10, "What is this called?", "Bass Clef",
            "Bar", "Natural", "Sharp", 3)
        insertQuestionsTable(l3q10)

        val l3q11 = QuestionsTable(3, 11, "What is this called?", "Decrescendo",
            "Accent", "Forte", "Crescendo", 1)
        insertQuestionsTable(l3q11)

        val l3q12 = QuestionsTable(3, 12, "What is this called?", "Decrescendo",
            "Forte", "Accent", "Piano", 4)
        insertQuestionsTable(l3q12)

        val l3q13 = QuestionsTable(3, 13, "What is this called?", "Forte",
            "Accent", "Crescendo", "Decrescendo", 1)
        insertQuestionsTable(l3q13)

        val l3q14 = QuestionsTable(3, 14, "What is this called?", "Decrescendo",
            "Accent", "Crescendo", "Forte", 2)
        insertQuestionsTable(l3q14)

        val l3q15 = QuestionsTable(3, 15, "What is this called?", "Forte",
            "Piano", "Accent", "Crescendo", 4)
        insertQuestionsTable(l3q15)


    }

    //Function get the questions back from the database.
    fun getLevelQuestions(quizLevel: Int): ArrayList<QuestionsTable> {
        //Initializing db
        val db = this.readableDatabase
        //Selection variable that holds the attribute we need
        val selection = "$COL_QUIZ_LEVEL = ? "
        //Argument variable that holds which row we need
        val selectionArgs = arrayOf(quizLevel.toString())
        //Creating the ArrayList that holds the questions retrieved from the database
        val rv = ArrayList<QuestionsTable>()
        //Query that gets the values from the database
        val csr = db.query(QUESTION_TABLE,null /* ALL columns */,selection,selectionArgs,null,null,null)

        //While loop that insert the values into the array list while there is a next value
        while (csr.moveToNext()) {
            //Adding values to the array list
            rv.add(
                //Inserting the values into the data class
                QuestionsTable(
                    //Getting the actual values from the query
                    csr.getInt(csr.getColumnIndex(COL_QUIZ_LEVEL)),
                    csr.getInt(csr.getColumnIndex(COL_QUESTION_NUM)),
                    csr.getString(csr.getColumnIndex(COL_QUIZ_QUESTION)),
                    csr.getString(csr.getColumnIndex(COL_QUIZ_OPTION_ONE)),
                    csr.getString(csr.getColumnIndex(COL_QUIZ_OPTION_TWO)),
                    csr.getString(csr.getColumnIndex(COL_QUIZ_OPTION_THREE)),
                    csr.getString(csr.getColumnIndex(COL_QUIZ_OPTION_FOUR)),
                    csr.getInt(csr.getColumnIndex(COL_QUIZ_ANSWER))
                )
            )

        }

        //Closing the database query
        csr.close()
        //Returning the ArrayList
        return rv
    }

    //Function get the scores back from the database.
    fun getAllScores(): ArrayList<ScoreTable> {
        val db = this.readableDatabase
        //Creating the ArrayList that holds the questions retrieved from the database
        val rv = ArrayList<ScoreTable>()
        //While loop that insert the values into the array list while there is a next value
        val csr = db.query(SCORE_TABLE,null /* ALL columns */,null,null,null,null,null)

        //While loop that insert the values into the array list while there is a next value
        while (csr.moveToNext()) {
            //Adding values to the array list
            rv.add(
                //Inserting the values into the data class
                ScoreTable(
                    //Getting the actual values from the query
                    csr.getInt(csr.getColumnIndex(COL_SCORE_LEVEL)),
                    csr.getInt(csr.getColumnIndex(COL_QUIZ_SCORE))
                )
            )
        }

        //Closing the database query
        csr.close()
        //Returning the ArrayList
        return rv
    }

    //Function get the Game scores back from the database.
    fun getAllGameScores(): ArrayList<GameTable> {
        val db = this.readableDatabase
        //Creating the ArrayList that holds the questions retrieved from the database
        val rv = ArrayList<GameTable>()
        //While loop that insert the values into the array list while there is a next value
        val csr = db.query(GAME_TABLE,null /* ALL columns */,null,null,null,null,null)

        //While loop that insert the values into the array list while there is a next value
        while (csr.moveToNext()) {
            //Adding values to the array list
            rv.add(
                //Inserting the values into the data class
                GameTable(
                    //Getting the actual values from the query
                    csr.getInt(csr.getColumnIndex(COL_GAME_NUMBER)),
                    csr.getInt(csr.getColumnIndex(COL_GAME_SCORE))
                )
            )
        }

        //Closing the database query
        csr.close()
        //Returning the ArrayList
        return rv
    }
}

Это класс, который создает базу данных и в котором выполняется функция вставки. Далее идет код ошибки, который также содержит информацию, которая может вызвать несоответствие внешнего ключа

2020-08-06 11:10:44.662 24432-24432/com.example.guviolinapp E/SQLiteLog: (1) foreign key mismatch - "score_table" referencing "questions_table"
2020-08-06 11:10:44.664 24432-24432/com.example.guviolinapp E/SQLiteDatabase: Error inserting quiz_score=3 quiz_level=1
    android.database.sqlite.SQLiteException: foreign key mismatch - "score_table" referencing "questions_table" (code 1): , while compiling: INSERT INTO score_table(quiz_score,quiz_level) VALUES (?,?)
        at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
        at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:889)
        at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:500)
        at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
        at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
        at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:31)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1472)
        at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1343)
        at com.example.guviolinapp.DBHelper.insertScoreTable(DBHelper.kt:99)
        at com.example.guviolinapp.ResultActivity.onCreate(ResultActivity.kt:31)
        at android.app.Activity.performCreate(Activity.java:6662)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2599)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6077)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)

1 Ответ

1 голос
/ 06 августа 2020

Это оператор CREATE таблицы score_table:

CREATE TABLE score_table(
  quiz_level INTEGER PRIMARY KEY, 
  quiz_score INTEGER, 
  FOREIGN KEY (quiz_level) REFERENCES questions_table(quiz_level) ON DELETE CASCADE
)

, где вы определили, что столбец quiz_level ссылается на столбец quiz_level таблицы questions_table.

Также в методе onConfigure() у вас есть:

db.setForeignKeyConstraintsEnabled(true)

, который включает ограничения внешнего ключа для базы данных.

Это означает, что любое значение в столбце quiz_level таблицы score_table должно существовать в столбце quiz_level таблицы questions_table.

Эта ошибка говорит о том, что вы пытались вставить новую строку в таблицу score_table со значением в quiz_level, которого нет в questions_table.

Итак, прежде чем вставлять эту строку, вы должны вставить строку в questions_table с тем же quiz_level.

Также SQLite требует, чтобы указанный столбец quiz_level в questions_table был либо PRIMARY KEY таблицы, либо UNIQUE INDEX или UNIQUE constraint, (подробнее здесь: Поддержка внешнего ключа SQLite ), но все это неверно в определении таблицы questions_table.

...