Ограничения внешнего ключа в 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.