Внешний ключ не создан на Android Studio, но таблицы созданы - PullRequest
0 голосов
/ 27 октября 2019

У меня есть 2 таблицы: registereduser, listbands. listbands имеет внешний ключ, ссылающийся на registereduser (id)

Мне удалось создать обе таблицы в базах данных, однако при создании нового пользователя поля listbands пусты! У него должен быть идентификатор registereduser.

Вот мой код:

public class DatabaseHelper extends SQLiteOpenHelper {

    //User table
    public static final String DATABASE_NAME ="bands.db";
    public static final String TABLE_USER ="registereduser";
    public static final String COL_USER_ID ="BID";
    public static final String COL_USER_EMAIL ="email";
    public static final String COL_USER_PASS ="password";

    //bands table
    public static final String TABLE_LIST ="listbands";
    public static final String COL_LIST_ID ="LID";
    public static final String COL_LIST_FK ="BID";
    public static final String COL_LIST_NAME ="name";


    public DatabaseHelper(@Nullable Context context ) {
        super(context, DATABASE_NAME, null , 1);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {

        sqLiteDatabase.execSQL("CREATE TABLE registereduser (BID INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "email TEXT , password TEXT )");
        sqLiteDatabase.execSQL("CREATE TABLE listbands (LID INTEGER PRIMARY KEY AUTOINCREMENT ," +
                "BID INTEGER NOT NULL, name TEXT, FOREIGN KEY(BID) REFERENCES registereduser(BID) )");



    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {

        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_USER);
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_LIST);
        onCreate(sqLiteDatabase);

    }

    @Override
    public void onOpen(SQLiteDatabase sqLiteDatabase) {
        super.onOpen(sqLiteDatabase);

        //enable foreign key constraints like ON UPDATE CASCADE, ON DELETE CASCADE
        sqLiteDatabase.execSQL("PRAGMA foreign_keys=ON;");
    }

    public long addUser(String email, String password) {

        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put(" email ",email);
        contentValues.put(" password ",password);
        long res = db.insert("registereduser", null, contentValues);
        db.close();
        return res;
    }

    public long addList(String name) {

        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues contentLists = new ContentValues();
        contentLists.put(" name ",name);
        long rest = db.insert("listbands", null, contentLists);
        db.close();
        return rest;
    }

    public boolean checkUser ( String email, String password){

        String[] column = {COL_USER_ID};
        SQLiteDatabase db = getReadableDatabase();
        String choice = COL_USER_EMAIL + "=?" + " and " + COL_USER_PASS + "=?";
        String[] choiceArgs = { email, password};
        Cursor cursor = db.query(TABLE_USER, column, choice, choiceArgs, null, null, null);
        int count = cursor.getCount();
        cursor.close();
        db.close();

        if (count>0)
            return true;
        else
            return false;

    }
}

и это снимок экрана, на котором мои списки рассылки пусты: это снимок экрана из менеджера sqlite

1 Ответ

0 голосов
/ 27 октября 2019

Определение внешнего ключа не позволяет автоматически вставлять данные в дочернюю таблицу (таблица listbands) при добавлении строки в родительскую таблицу (таблица registereduser).

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

Вам необходимо добавить строки в дочернюю таблицу по мере необходимости.

На снимке экрана показано, что в таблице listBands теперь есть строки.

В настоящее время в вашем коде есть метод addList , который не будет работать, поскольку в результате возникнет конфликт ограничений NOT NULL. Однако это не приведет к ошибке, так как исключение перехватывается методом insert . Однако, если вы посмотрите на возвращаемое значение, оно будет равно -1, а не 1 или больше (идентификатор вставленной строки).

Вам потребуется использовать что-то вроде: -

public long addListProper(String name, long id) {
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues cv = new ContentValues();
    cv.put(COL_LIST_FK,id);
    cv.put(COL_LIST_NAME,name);
    return db.insert(TABLE_LIST,null,cv);
}

для вставки строки списка диапазонов.

Пример

Обратите внимание на следующее: -

  • Удаляет все строки из таблицы списков диапазонов.
  • Удаляет все строки из таблицы registereduser.
    • Обратите внимание, что, поскольку registereduser может включать в себя родительские элементы строк списка, сначала необходимо удалить списки.
    • Эти два шага предназначены только для того, чтобы таблицы были пустыми при каждом запуске демонстрации.
  • Добавляет пользователя
  • Добавляет список (который НЕ будет добавлен)
  • Добавляет список, используя альтернативную / правильную вставку, которая использует идентификаторпользователь
  • Регистрирует информацию, обновляя идентификаторы вставок.
  • Извлекает строки из таблиц в курсор, который выводится в журнал.

Код: -

    mDBHlpr = new DatabaseHelper(this);
    mDBHlpr.getWritableDatabase().delete(DatabaseHelper.TABLE_LIST,null,null);
    mDBHlpr.getWritableDatabase().delete(DatabaseHelper.TABLE_USER,null,null);
    long this_user = mDBHlpr.addUser("Fred","1234567890");
    long this_list = mDBHlpr.addList("Blah"); //<<<<<<<<<< WILL NOT WORK
    long alt_list = mDBHlpr.addListProper("blah blah",this_user); //<<<<<<<<< SHOULD WORK
    Log.d("INSERTIFO","ID of user was " + this_user + " ID of list was " + this_list);
    DatabaseUtils.dumpCursor(
            mDBHlpr.getWritableDatabase().query(DatabaseHelper.TABLE_USER,null,null,null,null,null,null)
    );
    DatabaseUtils.dumpCursor(
            mDBHlpr.getWritableDatabase().query(DatabaseHelper.TABLE_LIST,null,null,null,null,null,null)
    );

Результаты

Первым в журнале будет: -

2019-10-27 12:45:00.152 32338-32338/aso.so58575523foreignkeys E/SQLiteDatabase: Error inserting  name =Blah
    android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: listbands.BID (code 1299 SQLITE_CONSTRAINT_NOTNULL)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:879)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:790)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:88)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1599)
        at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1468)
        at aso.so58575523foreignkeys.DatabaseHelper.addList(DatabaseHelper.java:76)
        at aso.so58575523foreignkeys.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:7802)
        at android.app.Activity.performCreate(Activity.java:7791)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

т. Е. Ошибка addList, поскольку столбец BID равен нулю и, следовательно, строка являетсяне вставлено из-за ограничения NOT NULL. Хотя трассировка стека находится в журнале, исключение было перехвачено, и поэтому обработка продолжается.

В этом случае журнал будет иметь: -

2019-10-27 12:45:00.165 D/INSERTIFO: ID of user was 3 ID of list was -1 ID of alternative list item was 2
  • Посмотрите, как попытка использовать addList привел к идентификатору -1 .

затем: -

2019-10-27 12:45:00.165 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@561c44
2019-10-27 12:45:00.166 I/System.out: 0 {
2019-10-27 12:45:00.166 I/System.out:    BID=3
2019-10-27 12:45:00.166 I/System.out:    email=Fred
2019-10-27 12:45:00.166 I/System.out:    password=1234567890
2019-10-27 12:45:00.166 I/System.out: }
2019-10-27 12:45:00.166 I/System.out: <<<<<
2019-10-27 12:45:00.167 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@1def12d
2019-10-27 12:45:00.167 I/System.out: 0 {
2019-10-27 12:45:00.167 I/System.out:    LID=2
2019-10-27 12:45:00.167 I/System.out:    BID=3
2019-10-27 12:45:00.167 I/System.out:    name=blah blah
2019-10-27 12:45:00.167 I/System.out: }
2019-10-27 12:45:00.167 I/System.out: <<<<<
  • Как видно на второмпопытка (с использованием addListProper ) добавила строку в списки диапазонов, и не было никакого конфликта ограничений внешнего ключа или конфликта ограничений NOT NULL, который первоначально остановил добавляемую строку.

Extra

рассмотрите эту строку кода: -

long otheralter_list = mDBHlpr.addListProper("not blah",100); //<<<<<<< FK constraint as no user with an ID of 100.
  • Попытка добавления строки, которая ссылается на пользователя с идентификатором 100 (такого пользователя нет). Строка не будет вставлена ​​(но опять-таки не приведет к сбою) из-за ограничения внешнего ключа, поскольку ни одна строка в таблице registereduser (родительская таблица внешнего ключа) не имеет 100 в столбце BID (родительский столбец).

Результат в журнале будет: -

2019-10-27 13:12:39.272 32564-32564/? E/SQLiteDatabase: Error inserting BID=100 name=not blah
    android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:879)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:790)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:88)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1599)
        at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1468)
        at aso.so58575523foreignkeys.DatabaseHelper.addListProper(DatabaseHelper.java:86)
        at aso.so58575523foreignkeys.MainActivity.onCreate(MainActivity.java:24)
        at android.app.Activity.performCreate(Activity.java:7802)
        at android.app.Activity.performCreate(Activity.java:7791)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Дополнительный комментарий Re

Поэтому я должен использовать CONSTRAINT в таблице listbands для созданиявнешний ключ?

Нет, вы правильно определили внешний ключ. ОГРАНИЧЕНИЕ ???? FOREIGN KEY только дает Ограничению имя. Он не меняет действия ограничения.

Таблица списков диапазонов также не имеет идентификатора, даже если она создана как AUTOINCREMENT

Нет. Снимки экрана показывают, что в таблице listbands вообще нет данных (из-за ограничения NOT NULL, как описано выше).

AUTOINCREMENT делает очень мало INTEGER PRIMARY KEY, что позволяет автоматически сгенерированному значению бытьпри условии. Хотя на самом деле это неверно, поскольку все, что делает INTEGER PRIMARY KEY, - это делает столбец псевдонимом обычно скрытого столбца rowid , который является автоматически генерируемым столбцом для всех таблиц, кроме тех, которые определены с использованием WITHOUT ROWID.

AUTOINCREMENT дополняет псевдоним дополнительным правилом, которого SQLite будет придерживаться, если возможно, говоря, что автоматически сгенерированное значение больше любого ранее выделенного. Без AUTOINCREMENT и IF было использовано максимально возможное значение rowid (9223372036854775807), тогда можно использовать более низкое значение. Без AUTOINCREMENT SQLIte вместо этого выдаст исключение SQLITE_FULL.

AUTOINCREMENT использует дополнительную таблицу sqlite_sequence для хранения последнего назначенного значения rowid. Поскольку такая неэффективность вносится в управление этой дополнительной таблицей.

Если AUTOINCREMENT WERE удален, то не будет заметной разницы.

...