Restrições de chave estrangeira no Android usando SQLite? em Excluir cascata


91

Eu tenho duas tabelas: trilhas e waypoints, uma trilha pode ter muitos waypoints, mas um waypoint é atribuído a apenas 1 trilha.

Na tabela de pontos de maneira, tenho uma coluna chamada "trackidfk" que insere o track_ID assim que uma trilha é feita, no entanto, não configurei restrições de chave estrangeira nesta coluna.

Quando apago uma trilha, quero apagar os waypoints atribuídos, isso é possível? Eu li sobre o uso de Triggers, mas não acho que eles sejam suportados no Android.

Para criar a tabela 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"
                + " );"
              );

    ...
}

Respostas:


237

As restrições de chave estrangeira com em cascata de exclusão são suportadas, mas você precisa habilitá-las.
Acabei de adicionar o seguinte ao meu 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;");
    }
}

Eu declarei minha coluna de referência da seguinte maneira.

mailbox_id INTEGER REFERENCES mailboxes ON DELETE CASCADE

59
O que significa que só funciona desde Android 2.2 Froyo que tem SQLite 3.6.22
Intrications

@RedPlanet - é porque a única vez que essa restrição é aplicada é quando algo é gravado no banco de dados. (Você não pode quebrar essa restrição se tudo o que você fizer for ler o banco de dados) Além disso, Phil, em vez do método onOpen, provavelmente é melhor fazê-lo no método onConfigure. Fonte: developer.android.com/reference/android/database/sqlite/…
Aneem

12
O Google recomenda escrever PRAGMAdeclarações em, onConfigure()mas requer nível de API 16 (Android 4.1), e então você pode simplesmente chamar setForeignKeyConstraintsEnabled.
Pang

Também pode ser necessário considerar a ativação de restrições de chave estrangeira em onCreate/ onDowngrade/ onUpgrade, que são anteriores onOpen. Veja o código-fonte no Android 4.1.1 .
Pang

1
@Natix incluindo a chamada para super garante a funcionalidade correta se uma classe intermediária for introduzida entre a classe implementada e seu pai.
tbm


26

Como a postagem de e.shishkin diz da API 16 em diante, você deve habilitar as restrições de chave estrangeira no SqLiteOpenHelper.onConfigure(SqLiteDatabase)método usando odb.setForeignKeyConstraintsEnabled(boolean)

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

10

Nunca é uma pergunta muito velha para responder com uma resposta mais 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

Tudo o que @phil mencionou é bom. Mas você pode usar outro método padrão disponível no próprio banco de dados para definir a chave estrangeira. Isso é 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)
    }
}

Para documentos, consulte SQLiteDatabase.setForeignKeyConstraintsEnabled


3
A documentação que você postou sugere: A good time to call this method is right after calling openOrCreateDatabase(File, SQLiteDatabase.CursorFactory) or in the onConfigure(SQLiteDatabase) callback. Então, em vez de onOpen, onConfigureparece ser o lugar certo.
Paul Woitaschek

4

Não acho que o SQLite suporte isso fora da caixa. O que estou fazendo em meus aplicativos é:

  1. Criar transação
  2. Exclua dados de detalhes (pontos de passagem em seu exemplo)
  3. Excluir dados mestre (faixas em seu exemplo)
  4. Confirmar transação em caso de sucesso

Dessa forma, tenho certeza de que todos os dados foram excluídos ou nenhum.


Mas você está excluindo de ambas as tabelas usando um método?
jcrowson

Sim, acompanhei o exemplo do Notes da API. Quando devo excluir o que seria uma trilha no seu caso, eu crio a transação, apago a trilha e os waypoints e confirmo a transação. Isso tudo de uma vez.
Thorsten Dittmar

4

Os gatilhos são compatíveis com o Android e esse tipo de exclusão em cascata não é compatível com o sqlite. Um exemplo de uso de gatilhos no Android pode ser encontrado aqui . Embora usar transações como Thorsten declarou, provavelmente seja tão fácil quanto um gatilho.


3

A versão do SQLite no Android 1.6 é 3.5.9, por isso não suporta chaves estrangeiras ...

http://www.sqlite.org/foreignkeys.html "Este documento descreve o suporte para restrições de chave estrangeira SQL introduzidas no SQLite versão 3.6.19."

No Froyo é SQLite versão 3.6.22, então ...

EDITAR: para ver a versão sqlite: adb shell sqlite3 -version


Então, existe alguma maneira de forçar tais restrições .. Quero dizer, existe alguma maneira de atualizar a versão sqlite .. porque devemos ter que oferecer suporte a nossa versão de software para android 2.1 que tem sqlite versão 3.5.9 como acima
NullPointerException

Não, você tem que cuidar de tudo sozinho :(
GBouerat

1

Chaves estrangeiras com "on delete cascade" são suportadas no SQLite no Android 2.2 e superior. Mas tenha cuidado ao usá-los: às vezes, um erro é relatado ao disparar uma chave estrangeira em uma coluna, mas o problema real está em outra restrição de chave estrangeira de coluna na tabela filha, ou em alguma outra tabela que faz referência a esta tabela.

Parece que o SQLite verifica todas as restrições ao iniciar um deles. Na verdade, é mencionado na documentação. Verificações de restrição DDL versus DML.


0

Se você estiver usando o Android Room, siga as instruções abaixo.

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()
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.