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


91

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

У таблиці пунктів шляху у мене є стовпець "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 - це тому, що це обмеження застосовується лише тоді, коли щось записується в базу даних. (Ви не можете порушити це обмеження, якщо все, що ви робите, читаєте з db). Крім того, Філ, замість методу onOpen, можливо, краще це робити в методі onConfigure. Джерело: developer.android.com/reference/android/database/sqlite/…
Aneem

12
Google рекомендує писати PRAGMAзаяви, onConfigure()але для цього потрібен рівень API 16 (Android 4.1), і до того часу ви можете просто зателефонувати setForeignKeyConstraintsEnabled.
Панг

Можливо, також доведеться розглянути можливість включення обмежень зовнішнього ключа в 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 (істина).

@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здається, саме там.
Пол Войташек,

4

Я не думаю, що SQLite підтримує це нестандартно. Що я роблю в своїх програмах:

  1. Створити транзакцію
  2. Видалити деталізовані дані (шляхові точки у вашому прикладі)
  3. Видалити основні дані (доріжки у вашому прикладі)
  4. Здійснити транзакцію на успіх

Таким чином я впевнений, що або всі дані видаляються, або жодні.


Але чи видаляєте ви з обох таблиць одним методом?
jcrowson

Так, я майже повністю пішов разом із зразком Notes із API. Коли я хочу видалити те, що було б доріжкою у вашому випадку, я створюю транзакцію, видаляю доріжку та шляхові точки та фіксую транзакцію. Це все одним рухом.
Торстен Діттмар,

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, тому ...

EDIT: побачити версію sqlite: оболонка adb sqlite3 -версія


Тож чи є спосіб примусити такі обмеження .. Я маю на увазі, чи є спосіб оновити версію 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.