Vincoli di chiave esterna in Android utilizzando SQLite? su Elimina cascata


91

Ho due tabelle: tracce e waypoint, una traccia può avere molti waypoint, ma un waypoint è assegnato solo a 1 traccia.

Nella tabella way points ho una colonna chiamata "trackidfk" che inserisce il track_ID una volta creata una traccia, tuttavia non ho impostato i vincoli di Foreign Key su questa colonna.

Quando elimino una traccia, desidero eliminare i waypoint assegnati, è possibile ?. Ho letto sull'utilizzo dei trigger ma non credo che siano supportati in Android.

Per creare la tabella dei waypoint:

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

    ...
}

Risposte:


237

I vincoli di chiave esterna con cascata all'eliminazione sono supportati, ma è necessario abilitarli.
Ho appena aggiunto quanto segue al mio SQLOpenHelper , che sembra fare il trucco.

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

Ho dichiarato la mia colonna di riferimento come segue.

mailbox_id INTEGER REFERENCES mailboxes ON DELETE CASCADE

59
Il che significa che funziona solo da Android 2.2 Froyo che ha SQLite 3.6.22
Intrications

@RedPlanet - è perché l'unica volta che questo vincolo viene applicato è quando viene scritto qualcosa nel database. (Non puoi infrangere questo vincolo se tutto ciò che fai è leggere dal db) Inoltre, Phil, invece del metodo onOpen, è probabilmente meglio farlo nel metodo onConfigure. Fonte: developer.android.com/reference/android/database/sqlite/…
Aneem

12
Google consiglia di scrivere PRAGMAdichiarazioni in, onConfigure()ma richiede il livello API 16 (Android 4.1), ea quel punto puoi semplicemente chiamare setForeignKeyConstraintsEnabled.
Pang

Potrebbe anche essere necessario considerare l'abilitazione dei vincoli di chiave esterna in onCreate/ onDowngrade/ onUpgrade, che sono precedenti onOpen. Vedi il codice sorgente in Android 4.1.1 .
Pang

1
@Natix inclusa la chiamata a super garantisce la corretta funzionalità se viene introdotta una classe intermedia tra la classe implementata e la sua genitrice.
tbm


26

Come dice il post di e.shishkin dall'API 16 in su, dovresti abilitare i vincoli di chiave esterna nel SqLiteOpenHelper.onConfigure(SqLiteDatabase)metodo usando ildb.setForeignKeyConstraintsEnabled(boolean)

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

10

Mai una domanda troppo vecchia per rispondere con una risposta più completa.

@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

Qualunque cosa abbia menzionato @phil è buona. Ma puoi usare un altro metodo predefinito disponibile nel database stesso per impostare la chiave esterna. Questo è 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)
    }
}

Per i documenti fare riferimento a SQLiteDatabase.setForeignKeyConstraintsEnabled


3
La documentazione che hai pubblicato suggerisce: A good time to call this method is right after calling openOrCreateDatabase(File, SQLiteDatabase.CursorFactory) or in the onConfigure(SQLiteDatabase) callback. quindi, invece di onOpen, onConfiguresembra essere il posto giusto.
Paul Woitaschek

4

Non penso che SQLite lo supporti fuori dagli schemi. Quello che sto facendo nelle mie app è:

  1. Crea transazione
  2. Elimina i dati di dettaglio (waypoint nel tuo esempio)
  3. Elimina i dati principali (tracce nel tuo esempio)
  4. Impegnare la transazione in caso di successo

In questo modo sono sicuro che tutti i dati vengono eliminati o nessuno.


Ma stai eliminando da entrambe le tabelle usando un metodo?
jcrowson

Sì, sono andato più o meno con l'esempio di Notes dall'API. Quando devo eliminare quella che sarebbe una traccia nel tuo caso, creo la transazione, elimino traccia e waypoint e metto in atto la transazione. È tutto in una volta.
Thorsten Dittmar

4

I trigger sono supportati da Android e quel tipo di eliminazione a cascata non è supportato da sqlite. Un esempio di utilizzo dei trigger su Android può essere trovato qui . Anche se usare le transazioni come ha affermato Thorsten è probabilmente facile come un trigger.


3

La versione di SQLite in Android 1.6 è 3.5.9 quindi non supporta chiavi esterne ...

http://www.sqlite.org/foreignkeys.html "Questo documento descrive il supporto per i vincoli di chiave esterna SQL introdotti nella versione 3.6.19 di SQLite."

In Froyo è la versione SQLite 3.6.22, quindi ...

EDIT: per vedere la versione sqlite: adb shell sqlite3 -version


Quindi esiste un modo per forzare tali vincoli .. Voglio dire, c'è un modo per aggiornare la versione sqlite .. perché dobbiamo supportare la versione del software ad Android 2.1 che ha la versione 3.5.9 di sqlite come sopra
NullPointerException

No, devi gestire tutto da solo :(
GBouerat

1

Le chiavi esterne con "all'eliminazione a cascata" sono supportate in SQLite in Android 2.2 e versioni successive. Ma fai attenzione quando li usi: a volte viene segnalato un errore quando si attiva una chiave esterna su una colonna, ma il vero problema risiede nel vincolo di chiave esterna di un'altra colonna nella tabella figlia o in qualche altra tabella che fa riferimento a questa tabella.

Sembra che SQLite controlli tutti i vincoli quando ne attiva uno. In realtà è menzionato nella documentazione. Controlli dei vincoli DDL e DML.


0

Se stai usando Android Room, fai come mostrato di seguito.

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()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.