¿Restricciones de clave externa en Android usando SQLite? en Eliminar cascada


91

Tengo dos tablas: tracks y waypoints, un track puede tener muchos waypoints, pero un waypoint se asigna a solo 1 track.

En la tabla de puntos de ruta, tengo una columna llamada "trackidfk" que inserta el track_ID una vez que se crea una pista, sin embargo, no he configurado las restricciones de clave externa en esta columna.

Cuando borro un track quiero borrar los waypoints asignados, ¿es posible ?. Leí sobre el uso de Triggers, pero no creo que sean compatibles con Android.

Para crear la tabla de waypoints:

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

    ...
}

Respuestas:


237

Se admiten las restricciones de clave externa con cascada de eliminación, pero debe habilitarlas.
Acabo de agregar lo siguiente a mi SQLOpenHelper , que parece funcionar.

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

Declaré mi columna de referencia de la siguiente manera.

mailbox_id INTEGER REFERENCES mailboxes ON DELETE CASCADE

59
Lo que significa que sólo funciona desde Android 2.2 Froyo que tiene SQLite 3.6.22
Intrications

@RedPlanet: es porque la única vez que se aplica esta restricción es cuando se escribe algo en la base de datos. (No puede romper esta restricción si todo lo que hace es leer desde la base de datos) Además, Phil, en lugar del método onOpen, probablemente sea mejor hacerlo en el método onConfigure. Fuente: developer.android.com/reference/android/database/sqlite/…
Aneem

12
Google recomienda escribir PRAGMAdeclaraciones, onConfigure()pero requiere API nivel 16 (Android 4.1), y para entonces simplemente puede llamar setForeignKeyConstraintsEnabled.
Pang

También puede ser necesario considerar la habilitación de restricciones de clave externa en onCreate/ onDowngrade/ onUpgrade, que son anteriores onOpen. Consulte el código fuente en Android 4.1.1 .
Pang

1
@Natix, incluida la llamada a super, garantiza la funcionalidad correcta si se introduce una clase intermedia entre la clase implementada y su padre.
tbm


26

Como dice la publicación de e.shishkin de API 16 en adelante, debe habilitar las restricciones de clave externa en el SqLiteOpenHelper.onConfigure(SqLiteDatabase)método utilizando eldb.setForeignKeyConstraintsEnabled(boolean)

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

10

Nunca una pregunta demasiado vieja para responder con una respuesta más 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

Lo que sea que @phil mencione es bueno. Pero puede usar otro método predeterminado disponible en la base de datos para configurar la clave externa. Eso es setForeignKeyConstraintsEnabled (verdadero).

@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)
    }
}

Para documentos, consulte SQLiteDatabase.setForeignKeyConstraintsEnabled


3
La documentación que publicó sugiere: A good time to call this method is right after calling openOrCreateDatabase(File, SQLiteDatabase.CursorFactory) or in the onConfigure(SQLiteDatabase) callback. Entonces, en lugar de onOpen, onConfigureparece ser el lugar correcto.
Paul Woitaschek

4

No creo que SQLite admita esto de inmediato. Lo que estoy haciendo en mis aplicaciones es:

  1. Crear transacción
  2. Eliminar datos detallados (waypoints en su ejemplo)
  3. Eliminar datos maestros (pistas en su ejemplo)
  4. Comprometer la transacción en caso de éxito

De esa manera, estoy seguro de que se eliminan todos los datos o ninguno.


¿Pero está eliminando de ambas tablas usando un método?
jcrowson

Sí, seguí más o menos el ejemplo de Notes de la API. Cuando debo eliminar lo que sería una pista en su caso, creo la transacción, elimino la ruta y los puntos de referencia y confirmo la transacción. Eso es todo de una vez.
Thorsten Dittmar

4

Los activadores son compatibles con Android y ese tipo de eliminación en cascada no es compatible con sqlite. Aquí puede encontrar un ejemplo del uso de activadores en Android . Aunque usar transacciones como dijo Thorsten es probablemente tan fácil como un disparador.


3

La versión de SQLite en Android 1.6 es 3.5.9, por lo que no admite claves externas ...

http://www.sqlite.org/foreignkeys.html "Este documento describe la compatibilidad con las restricciones de clave externa de SQL introducidas en la versión 3.6.19 de SQLite".

En Froyo es SQLite versión 3.6.22, así que ...

EDITAR: para ver la versión de sqlite: adb shell sqlite3 -version


Entonces, ¿hay alguna forma de forzar tales restricciones? Quiero decir, ¿hay alguna forma de actualizar la versión de sqlite ... porque debemos tener que admitir la versión de software para Android 2.1 que tiene la versión de sqlite 3.5.9 como la anterior
NullPointerException

No, tienes que manejar todo tú mismo :(
GBouerat

1

Las claves externas con "en cascada de eliminación" son compatibles con SQLite en Android 2.2 y versiones posteriores. Pero tenga cuidado al usarlos: a veces se informa un error al activar una clave externa en una columna, pero el problema real radica en otra restricción de clave externa de columna en la tabla secundaria, o en alguna otra tabla que hace referencia a esta tabla.

Parece que SQLite comprueba todas las restricciones al activar una de ellas. De hecho, se menciona en la documentación. Verificaciones de restricciones DDL versus DML.


0

Si está utilizando Android Room, haga lo que se muestra a continuación.

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()
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.