Fremdschlüsseleinschränkungen in Android mit SQLite? auf Kaskade löschen

90

Ich habe zwei Tabellen: Spuren und Wegpunkte, eine Spur kann viele Wegpunkte haben, aber ein Wegpunkt ist nur einer Spur zugeordnet.

In der Wegpunktetabelle habe ich eine Spalte mit dem Namen "trackidfk", die die track_ID einfügt, sobald eine Spur erstellt wurde. Ich habe jedoch keine Fremdschlüsseleinschränkungen für diese Spalte eingerichtet.

Ist dies möglich, wenn ich eine Spur lösche, für die ich die zugewiesenen Wegpunkte löschen möchte? Ich habe über die Verwendung von Triggern gelesen, glaube aber nicht, dass sie in Android unterstützt werden.

So erstellen Sie die Wegpunkttabelle:

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
quelle

Antworten:

236

Fremdschlüsseleinschränkungen mit einer Löschkaskade werden unterstützt, müssen jedoch aktiviert werden.
Ich habe meinem SQLOpenHelper gerade Folgendes hinzugefügt , was den Trick zu tun scheint.

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

Ich habe meine Referenzierungsspalte wie folgt deklariert.

mailbox_id INTEGER REFERENCES mailboxes ON DELETE CASCADE
Phil
quelle
59
Das heißt , es funktioniert nur , seit Android 2.2 Froyo , die SQLite hat 3.6.22
Intrications
@RedPlanet - Dies liegt daran, dass diese Einschränkung nur dann erzwungen wird, wenn etwas in die Datenbank geschrieben wird. (Sie können diese Einschränkung nicht aufheben, wenn Sie nur aus der Datenbank lesen.) Phil, anstelle der onOpen-Methode ist es wahrscheinlich besser, dies in der onConfigure-Methode zu tun. Quelle: developer.android.com/reference/android/database/sqlite/…
Aneem
12
Google empfiehlt das Schreiben von PRAGMAAnweisungen onConfigure(), erfordert jedoch API-Level 16 (Android 4.1). Bis dahin können Sie einfach aufrufen setForeignKeyConstraintsEnabled.
Pang
Man kann auch in so dass Fremdschlüsselbeschränkungen beachten müssen , onCreate/ onDowngrade/ onUpgrade, die vor sind onOpen. Siehe Quellcode in Android 4.1.1 .
Pang
1
@Natix einschließlich des Aufrufs von Super stellt die korrekte Funktionalität sicher, wenn eine Zwischenklasse zwischen der implementierten Klasse und ihrer übergeordneten Klasse eingeführt wird.
tbm
54

Seit Android 4.1 (API 16) unterstützt SQLiteDatabase :

public void setForeignKeyConstraintsEnabled (boolean enable)
e.shishkin
quelle
26

Wie der Beitrag von e.shishkin ab API 16 besagt, sollten Sie Fremdschlüsseleinschränkungen in der SqLiteOpenHelper.onConfigure(SqLiteDatabase)Methode mithilfe von aktivierendb.setForeignKeyConstraintsEnabled(boolean)

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

Nie zu alt für eine Frage, um sie mit einer vollständigeren Antwort zu beantworten.

@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);
}
Codeversiert
quelle
6

Was auch immer @phil erwähnt hat, ist gut. Sie können jedoch eine andere Standardmethode verwenden, die in der Datenbank selbst verfügbar ist, um den Fremdschlüssel festzulegen. Das ist 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)
    }
}

Für Text & Tabellen beziehen SQLiteDatabase.setForeignKeyConstraintsEnabled

anand krish
quelle
3
Die Dokumentation , die Sie geschrieben schlägt vor: A good time to call this method is right after calling openOrCreateDatabase(File, SQLiteDatabase.CursorFactory) or in the onConfigure(SQLiteDatabase) callback. Anstatt also onOpen, onConfigurescheint der richtige Ort zu sein.
Paul Woitaschek
4

Ich glaube nicht, dass SQLite dies sofort unterstützt. Was ich in meinen Apps mache, ist:

  1. Transaktion erstellen
  2. Löschen Sie Detaildaten (Wegpunkte in Ihrem Beispiel).
  3. Stammdaten löschen (Spuren in Ihrem Beispiel)
  4. Transaktion bei Erfolg festschreiben

Auf diese Weise bin ich sicher, dass entweder alle Daten gelöscht werden oder keine.

Thorsten Dittmar
quelle
Aber löschen Sie mit einer Methode aus beiden Tabellen?
JCrowson
Ja, ich habe das Notes-Beispiel aus der API ziemlich genau mitgemacht. Wenn ich einen Track in Ihrem Fall löschen möchte, erstelle ich die Transaktion, lösche den Track und die Wegpunkte und schreibe die Transaktion fest. Das ist alles auf einmal.
Thorsten Dittmar
4

Trigger werden von Android unterstützt und diese Art der Kaskadenlöschung wird von SQLite nicht unterstützt. Ein Beispiel für die Verwendung von Triggern auf Android finden Sie hier . Die Verwendung von Transaktionen, wie Thorsten sagte, ist wahrscheinlich genauso einfach wie ein Auslöser.

Dave.B
quelle
3

Die SQLite-Version in Android 1.6 ist 3.5.9 und unterstützt daher keine Fremdschlüssel ...

http://www.sqlite.org/foreignkeys.html "Dieses Dokument beschreibt die Unterstützung für SQL-Fremdschlüsseleinschränkungen, die in SQLite Version 3.6.19 eingeführt wurden."

In Froyo ist es SQLite Version 3.6.22, also ...

BEARBEITEN: um die SQLite-Version zu sehen: adb shell sqlite3 -version

GBouerat
quelle
Gibt es eine Möglichkeit, solche Einschränkungen zu erzwingen
?
Nein, Sie müssen alles selbst
erledigen
1

Fremdschlüssel mit "On Delete Cascade" werden in SQLite ab Android 2.2 unterstützt. Seien Sie jedoch vorsichtig, wenn Sie sie verwenden: Manchmal wird ein Fehler gemeldet, wenn ein Fremdschlüssel in einer Spalte ausgelöst wird. Das eigentliche Problem liegt jedoch entweder in einer anderen Spalten-Fremdschlüsseleinschränkung in der untergeordneten Tabelle oder in einer anderen Tabelle, die auf diese Tabelle verweist.

Anscheinend überprüft SQLite alle Einschränkungen, wenn eine davon gestartet wird. Es wird tatsächlich in der Dokumentation erwähnt. DDL- und DML-Einschränkungsprüfungen.

Yar
quelle
0

Wenn Sie Android Room verwenden, gehen Sie wie unten gezeigt vor.

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
quelle