Ograniczenia klucza obcego w systemie Android przy użyciu oprogramowania SQLite? on Usuń kaskadę

91

Mam dwie tabele: ścieżki i punkty trasy, ścieżka może mieć wiele punktów, ale punkt drogi jest przypisany tylko do jednej ścieżki.

W tabeli punktów sposób mam kolumnę o nazwie "trackidfk", która wstawia track_ID po utworzeniu ścieżki, jednak nie ustawiłem ograniczeń klucza obcego w tej kolumnie.

Kiedy usuwam ślad Chcę usunąć przypisane punkty, czy jest to możliwe? Czytałem o używaniu wyzwalaczy, ale nie sądzę, aby były obsługiwane w systemie Android.

Aby utworzyć tabelę punktów trasy:

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"
                + " );"
              );

    ...
}
jcrowson
źródło

Odpowiedzi:

237

Ograniczenia klucza obcego z kaskadą przy usuwaniu są obsługiwane, ale należy je włączyć.
Właśnie dodałem następujące polecenie do mojego SQLOpenHelper , co wydaje się załatwiać sprawę .

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

Zadeklarowałem moją kolumnę odniesienia w następujący sposób.

mailbox_id INTEGER REFERENCES mailboxes ON DELETE CASCADE
Phil
źródło
59
Co oznacza, że ​​działa tylko od Androida 2.2 Froyo, który ma SQLite 3.6.22
Intrications
@RedPlanet - to dlatego, że to ograniczenie jest egzekwowane tylko wtedy, gdy coś jest zapisywane w bazie danych. (Nie możesz złamać tego ograniczenia, jeśli wszystko, co robisz, to odczytywanie z bazy danych). Ponadto Phil, zamiast metody onOpen, prawdopodobnie lepiej zrobić to w metodzie onConfigure. Źródło: developer.android.com/reference/android/database/sqlite/…
Aneem
12
Google zaleca pisanie PRAGMAoświadczeń w, onConfigure()ale wymaga to poziomu API 16 (Android 4.1), a wtedy możesz po prostu wywołać setForeignKeyConstraintsEnabled.
Pang
Może być również konieczne rozważenie włączenia ograniczeń klucza obcego w onCreate/ onDowngrade/ onUpgrade, które są wcześniej onOpen. Zobacz kod źródłowy w systemie Android 4.1.1 .
Pang
1
@Natix, w tym wywołanie super, zapewnia poprawną funkcjonalność, jeśli między zaimplementowaną klasą a jej rodzicem zostanie wprowadzona klasa pośrednia.
tbm
55

Od Androida 4.1 (API 16) SQLiteDatabase obsługuje:

public void setForeignKeyConstraintsEnabled (boolean enable)
e.shishkin
źródło
26

Jak mówi post z e.shishkin od API 16 w górę, powinieneś włączyć ograniczenia klucza obcego w SqLiteOpenHelper.onConfigure(SqLiteDatabase)metodzie używającejdb.setForeignKeyConstraintsEnabled(boolean)

@Override
public void onConfigure(SQLiteDatabase db){
    db.setForeignKeyConstraintsEnabled(true);
}
malcolm
źródło
10

Nigdy nie jest zbyt stare pytanie, by udzielić pełniejszej odpowiedzi.

@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);
}
Codeversed
źródło
6

Wszystko, o czym wspomniał @phil, jest dobre. Ale możesz użyć innej domyślnej metody dostępnej w samej bazie danych, aby ustawić klucz obcy. To jest 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)
    }
}

W przypadku dokumentów odwołaj się do SQLiteDatabase.setForeignKeyConstraintsEnabled

anand krish
źródło
3
Dokumentacja, którą opublikowałeś, sugeruje: A good time to call this method is right after calling openOrCreateDatabase(File, SQLiteDatabase.CursorFactory) or in the onConfigure(SQLiteDatabase) callback. Więc zamiast onOpen, onConfigurewydaje się, że to właściwe miejsce.
Paul Woitaschek
4

Nie sądzę, aby SQLite obsługiwał to po wyjęciu z pudełka. W moich aplikacjach robię:

  1. Utwórz transakcję
  2. Usuń dane szczegółowe (punkty trasy w przykładzie)
  3. Usuń dane podstawowe (ścieżki w Twoim przykładzie)
  4. Zatwierdź transakcję po sukcesie

W ten sposób mam pewność, że wszystkie dane zostaną usunięte lub żadne.

Thorsten Dittmar
źródło
Ale czy usuwasz dane z obu tabel przy użyciu jednej metody?
jcrowson
Tak, w zasadzie poszedłem za przykładem Notes z interfejsu API. Kiedy mam usunąć to, co w Twoim przypadku byłoby trasą, tworzę transakcję, usuwam trasę i punkty trasy oraz zatwierdzam transakcję. To wszystko za jednym zamachem.
Thorsten Dittmar
4

Wyzwalacze są obsługiwane przez system Android, a ten typ usuwania kaskadowego nie jest obsługiwany przez sqlite. Przykład użycia wyzwalaczy na Androidzie można znaleźć tutaj . Chociaż używanie transakcji, jak stwierdził Thorsten, jest prawdopodobnie tak samo łatwe jak wyzwalanie.

Dave B.
źródło
3

Wersja SQLite w systemie Android 1.6 to 3.5.9, więc nie obsługuje kluczy obcych ...

http://www.sqlite.org/foreignkeys.html „Ten dokument opisuje obsługę ograniczeń klucza obcego SQL wprowadzonych w SQLite 3.6.19.”

We Froyo jest to SQLite w wersji 3.6.22, więc ...

EDYCJA: aby zobaczyć wersję sqlite: adb shell sqlite3 -version

GBouerat
źródło
Czy jest więc jakiś sposób na wymuszenie takich ograniczeń ... Mam na myśli, czy istnieje sposób na aktualizację wersji sqlite ... ponieważ musimy obsługiwać wersję oprogramowania do Androida 2.1, która ma wersję 3.5.9 sqlite jak wyżej
NullPointerException
Nie, wszystko musisz załatwić sam :(
GBouerat
1

Klucze obce z kaskadą „przy usuwaniu” są obsługiwane w SQLite w systemie Android 2.2 i nowszych. Ale bądź ostrożny podczas ich używania: czasami zgłaszany jest błąd podczas odpalania jednego klucza obcego w jednej kolumnie, ale prawdziwy problem leży w ograniczeniu klucza obcego innej kolumny w tabeli potomnej lub w innej tabeli, która odwołuje się do tej tabeli.

Wygląda na to, że SQLite sprawdza wszystkie ograniczenia podczas uruchamiania jednego z nich. Faktycznie jest to wymienione w dokumentacji. Sprawdzanie ograniczeń DDL i DML.

Yar
źródło
0

Jeśli korzystasz z Android Room, wykonaj poniższe czynności.

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()
krishnakumarp
źródło