Batasan kunci asing di Android menggunakan SQLite? pada Hapus cascade

91

Saya memiliki dua tabel: trek dan titik arah, sebuah trek dapat memiliki banyak titik lewat, tetapi titik jalan ditetapkan hanya untuk 1 trek.

Dalam tabel poin cara saya memiliki kolom bernama "trackidfk" yang menyisipkan track_ID setelah trek dibuat, namun saya belum menyiapkan batasan Kunci Asing pada kolom ini.

Saat saya menghapus lintasan, saya ingin menghapus titik lewat yang telah ditetapkan, apakah ini mungkin ?. Saya membaca tentang menggunakan Pemicu tetapi saya rasa mereka tidak didukung di Android.

Untuk membuat tabel titik jalan:

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.dll
sumber

Jawaban:

237

Batasan kunci asing dengan penghapusan kaskade didukung, tetapi Anda harus mengaktifkannya.
Saya baru saja menambahkan yang berikut ini ke SQLOpenHelper saya , yang sepertinya berhasil.

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

Saya menyatakan kolom referensi saya sebagai berikut.

mailbox_id INTEGER REFERENCES mailboxes ON DELETE CASCADE
Phil
sumber
59
Yang artinya hanya berfungsi sejak Android 2.2 Froyo yang memiliki SQLite 3.6.22
Intrications
@RedPlanet - ini karena satu-satunya saat batasan ini diterapkan adalah ketika sesuatu ditulis ke database. (Anda tidak dapat merusak batasan ini jika semua yang Anda lakukan adalah membaca dari db) Selain itu, Phil, daripada metode onOpen, mungkin lebih baik melakukannya dalam metode onConfigure. Sumber: developer.android.com/reference/android/database/sqlite/…
Aneem
12
Google merekomendasikan penulisan PRAGMApernyataan onConfigure()tetapi itu membutuhkan API level 16 (Android 4.1), dan saat itu Anda cukup menelepon setForeignKeyConstraintsEnabled.
Pang
Seseorang mungkin juga perlu mempertimbangkan untuk mengaktifkan batasan kunci asing di onCreate/ onDowngrade/ onUpgrade, yang sebelumnya onOpen. Lihat kode sumber di Android 4.1.1 .
Pang
1
@Natix termasuk panggilan ke super memastikan fungsionalitas yang benar jika kelas perantara diperkenalkan antara kelas yang diimplementasikan dan induknya.
tbm
55

Sejak Android 4.1 (API 16) SQLiteDatabase mendukung:

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

Seperti yang dikatakan oleh posting dari e.shishkin dari API 16 ke atas, Anda harus mengaktifkan batasan kunci asing dalam SqLiteOpenHelper.onConfigure(SqLiteDatabase)metode menggunakandb.setForeignKeyConstraintsEnabled(boolean)

@Override
public void onConfigure(SQLiteDatabase db){
    db.setForeignKeyConstraintsEnabled(true);
}
malcolm.dll
sumber
10

Tidak pernah ada pertanyaan yang terlalu tua untuk dijawab dengan jawaban yang lebih lengkap.

@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
sumber
6

Apapun @phil yang disebutkan itu bagus. Tetapi Anda dapat menggunakan metode default lain yang tersedia di Database itu sendiri untuk menyetel kunci asing. Yaitu 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)
    }
}

Untuk Dokumen, lihat SQLiteDatabase.setForeignKeyConstraintsEnabled

anand krish
sumber
3
Dokumentasi yang Anda posting menyarankan: A good time to call this method is right after calling openOrCreateDatabase(File, SQLiteDatabase.CursorFactory) or in the onConfigure(SQLiteDatabase) callback. Jadi, alih-alih onOpen, onConfiguretampaknya tempat yang tepat.
Paul Woitaschek
4

Saya tidak berpikir SQLite mendukung ini di luar kotak. Apa yang saya lakukan di aplikasi saya adalah:

  1. Buat transaksi
  2. Hapus data detail (titik lewat dalam contoh Anda)
  3. Hapus data master (trek dalam contoh Anda)
  4. Lakukan transaksi demi kesuksesan

Dengan cara itu saya yakin bahwa semua data akan dihapus atau tidak ada.

Thorsten Dittmar
sumber
Tetapi apakah Anda menghapus dari kedua tabel menggunakan satu metode?
jcrowson
Ya, saya cukup banyak mengikuti sampel Notes dari API. Ketika saya menghapus apa yang akan menjadi lintasan dalam kasus Anda, saya membuat transaksi, menghapus lintasan dan titik jalan dan melakukan transaksi. Itu semua dalam sekali jalan.
Thorsten Dittmar
4

Pemicu didukung oleh android dan jenis penghapusan kaskade tersebut tidak didukung oleh sqlite. Contoh penggunaan pemicu di android dapat ditemukan di sini . Meskipun menggunakan transaksi seperti yang dinyatakan Thorsten mungkin semudah pemicu.

Dave.B
sumber
3

Versi SQLite di android 1.6 adalah 3.5.9 sehingga tidak mendukung kunci asing ...

http://www.sqlite.org/foreignkeys.html "Dokumen ini menjelaskan dukungan untuk kendala kunci asing SQL yang diperkenalkan di SQLite versi 3.6.19."

Di Froyo itu SQLite versi 3.6.22, jadi ...

EDIT: untuk melihat versi sqlite: adb shell sqlite3 -version

GBouerat
sumber
Jadi apakah ada cara untuk memaksa kendala seperti itu .. Maksud saya apakah ada cara untuk mengupgrade versi sqlite .. karena kita harus mendukung versi perangkat lunak ke android 2.1 yang memiliki versi sqlite 3.5.9 seperti di atas
NullPointerException
Tidak, Anda harus menangani semuanya sendiri :(
GBouerat
1

Kunci asing dengan "on delete cascade" didukung di SQLite di Android 2.2 dan lebih tinggi. Namun berhati-hatilah saat menggunakannya: terkadang kesalahan dilaporkan saat mengaktifkan satu kunci asing pada satu kolom, tetapi masalah sebenarnya terletak pada batasan kunci asing kolom lain di tabel anak, atau tabel lain yang mereferensikan tabel ini.

Sepertinya SQLite memeriksa semua batasan saat mengaktifkan salah satunya. Itu sebenarnya disebutkan dalam dokumentasi. Pemeriksaan kendala DDL versus DML.

Yar
sumber
0

Jika Anda menggunakan Android Room, lakukan seperti yang ditunjukkan di bawah ini.

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
sumber