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

    ...
}
Jcrowson
fonte

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
Phil
fonte
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
55

Desde Android 4.1 (API 16), SQLiteDatabase suporta:

public void setForeignKeyConstraintsEnabled (boolean enable)
e.shishkin
fonte
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);
}
Malcolm
fonte
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);
}
Codeversed
fonte
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

anand krish
fonte
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.

Thorsten Dittmar
fonte
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.

Dave.B
fonte
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

GBouerat
fonte
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.

Yar
fonte
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()
Krishnakumarp
fonte