Ограничения внешнего ключа в Android с использованием SQLite? на Удалить каскад


91

У меня есть две таблицы: треки и путевые точки, у трека может быть много путевых точек, но путевая точка назначается только одному треку.

В таблице точек пути у меня есть столбец с именем «trackidfk», который вставляет track_ID после создания трека, однако я не устанавливал ограничения внешнего ключа для этого столбца.

Когда я удаляю трек, я хочу удалить назначенные путевые точки, возможно ли это? Я читал об использовании триггеров, но не думаю, что они поддерживаются в Android.

Чтобы создать таблицу путевых точек:

public void onCreate(SQLiteDatabase db) {
    db.execSQL( "CREATE TABLE " + TABLE_NAME 
                + " (" 
                + _ID         + " INTEGER PRIMARY KEY AUTOINCREMENT, " 
                + LONGITUDE   + " INTEGER," 
                + LATITUDE    + " INTEGER," 
                + TIME        + " INTEGER,"
                + TRACK_ID_FK + " INTEGER"
                + " );"
              );

    ...
}

Ответы:


237

Ограничения внешнего ключа с каскадом удаления поддерживаются, но вам необходимо включить их.
Я только что добавил в свой SQLOpenHelper следующее , что, похоже, помогает .

@Override
public void onOpen(SQLiteDatabase db) {
    super.onOpen(db);
    if (!db.isReadOnly()) {
        // Enable foreign key constraints
        db.execSQL("PRAGMA foreign_keys=ON;");
    }
}

Я объявил свой ссылочный столбец следующим образом.

mailbox_id INTEGER REFERENCES mailboxes ON DELETE CASCADE

59
А это значит, работает только с Android 2.2 Froyo , которая имеет SQLite 3.6.22
— Intrications

@RedPlanet - это потому, что это ограничение применяется только тогда, когда что-то записывается в базу данных. (Вы не можете нарушить это ограничение, если все, что вы делаете, читается из базы данных) Кроме того, Фил, вместо метода onOpen, вероятно, лучше сделать это в методе onConfigure. Источник: developer.android.com/reference/android/database/sqlite/…
— Aneem

12
Google рекомендует писать PRAGMAоператоры, onConfigure()но для этого требуется уровень API 16 (Android 4.1), и к тому времени вы можете просто позвонить setForeignKeyConstraintsEnabled.
— Pang

Также может потребоваться рассмотреть возможность включения ограничений внешнего ключа в onCreate/ onDowngrade/ onUpgrade, которые были раньше onOpen. См. Исходный код в Android 4.1.1 .
— Pang

1
@Natix, включая вызов super, гарантирует правильную функциональность, если промежуточный класс вводится между реализованным классом и его родителем.
— tbm


26

Как говорится в сообщении от e.shishkin, начиная с API 16, вы должны включить ограничения внешнего ключа в SqLiteOpenHelper.onConfigure(SqLiteDatabase)методе, используяdb.setForeignKeyConstraintsEnabled(boolean)

@Override
public void onConfigure(SQLiteDatabase db){
    db.setForeignKeyConstraintsEnabled(true);
}

10

Никогда не бывает слишком старым вопросом, чтобы дать более полный ответ.

@Override public void onOpen(SQLiteDatabase db) {
    super.onOpen(db);
    if (!db.isReadOnly()) {
        setForeignKeyConstraintsEnabled(db);
    }
    mOpenHelperCallbacks.onOpen(mContext, db);
}

private void setForeignKeyConstraintsEnabled(SQLiteDatabase db) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        setForeignKeyConstraintsEnabledPreJellyBean(db);
    } else {
        setForeignKeyConstraintsEnabledPostJellyBean(db);
    }
}

private void setForeignKeyConstraintsEnabledPreJellyBean(SQLiteDatabase db) {
    db.execSQL("PRAGMA foreign_keys=ON;");
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void setForeignKeyConstraintsEnabledPostJellyBean(SQLiteDatabase db) {
    db.setForeignKeyConstraintsEnabled(true);
}

6

Все, что упомянул @phil, хорошо. Но вы можете использовать другой метод по умолчанию, доступный в самой базе данных, для установки внешнего ключа. Это setForeignKeyConstraintsEnabled (true).

@Override
public void onOpen(SQLiteDatabase db) {
    super.onOpen(db);
    if (!db.isReadOnly()) {
        // Enable foreign key constraints
        db.execSQL("PRAGMA foreign_keys=ON;"); 
              //(OR)
        db.setForeignKeyConstraintsEnabled (true)
    }
}

Для документов см. SQLiteDatabase.setForeignKeyConstraintsEnabled


3
Документация, которую вы опубликовали, предлагает: A good time to call this method is right after calling openOrCreateDatabase(File, SQLiteDatabase.CursorFactory) or in the onConfigure(SQLiteDatabase) callback. Итак, вместо onOpen, onConfigureпохоже, правильное место.
— Paul Woitaschek 02

4

Я не думаю, что SQLite поддерживает это из коробки. В своих приложениях я делаю следующее:

  1. Создать транзакцию
  2. Удалить подробные данные (путевые точки в вашем примере)
  3. Удалить основные данные (треки в вашем примере)
  4. Зафиксировать транзакцию в случае успеха

Таким образом, я уверен, что либо все данные будут удалены, либо нет.


Но вы удаляете из обеих таблиц одним методом?
— jcrowson

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

4

Триггеры поддерживаются Android, и этот тип каскадного удаления не поддерживается sqlite. Пример использования триггеров на android можно найти здесь . Хотя использование транзакций, как заявил Торстен, вероятно, так же просто, как триггер.


3

Версия SQLite в android 1.6 - 3.5.9, поэтому внешние ключи не поддерживаются ...

http://www.sqlite.org/foreignkeys.html «В этом документе описывается поддержка ограничений внешнего ключа SQL, представленных в SQLite версии 3.6.19».

Во Froyo это SQLite версии 3.6.22, так что ...

РЕДАКТИРОВАТЬ: чтобы увидеть версию sqlite: adb shell sqlite3 -version


Итак, есть ли способ навязать такие ограничения ... Я имею в виду, есть ли способ обновить версию sqlite ... потому что мы должны поддерживать версию программного обеспечения до Android 2.1, которая имеет версию sqlite 3.5.9, как указано выше
— NullPointerException

Нет, ты должен со всем справиться самостоятельно :(
— GBouerat

1

Внешние ключи с «каскадом удаления» поддерживаются в SQLite в Android 2.2 и выше. Но будьте осторожны при их использовании: иногда при запуске одного внешнего ключа в одном столбце появляется сообщение об ошибке, но настоящая проблема заключается либо в ограничении внешнего ключа другого столбца в дочерней таблице, либо в какой-либо другой таблице, которая ссылается на эту таблицу.

Похоже, SQLite проверяет все ограничения при запуске одного из них. Это действительно упоминается в документации. DDL по сравнению с проверками ограничений DML.


0

Если вы используете Android Room, сделайте, как показано ниже.

Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
    .addCallback(object : RoomDatabase.Callback() {
        // Called when the database has been opened.
        override fun onOpen(db: SupportSQLiteDatabase) {
            super.onOpen(db)
            //True to enable foreign key constraints
            db.setForeignKeyConstraintsEnabled(true)
        }

        // Called when the database is created for the first time. 
        override fun onCreate(db: SupportSQLiteDatabase) {
            super.onCreate(db)
        }
    }).build()
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.