ข้อ จำกัด ของคีย์ต่างประเทศใน Android โดยใช้ SQLite? บนลบน้ำตก


91

ฉันมีสองตาราง: แทร็กและจุดอ้างอิงแทร็กสามารถมีจุดอ้างอิงได้หลายจุด แต่เวย์พอยต์ถูกกำหนดให้กับแทร็ก 1 แทร็กเท่านั้น

ในตารางชี้ทางฉันมีคอลัมน์ชื่อ "trackidfk" ซึ่งแทรก track_ID เมื่อสร้างแทร็กแล้วอย่างไรก็ตามฉันไม่ได้ตั้งค่าข้อ จำกัด Foreign Key ในคอลัมน์นี้

เมื่อฉันลบแทร็กฉันต้องการลบจุดอ้างอิงที่กำหนดเป็นไปได้หรือไม่? ฉันอ่านเกี่ยวกับการใช้ทริกเกอร์ แต่ไม่คิดว่าแอนดรอยด์รองรับ

ในการสร้างตารางจุดอ้างอิง:

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

    ...
}

คำตอบ:


237

รองรับข้อ จำกัด ของคีย์ต่างประเทศในการลบน้ำตก แต่คุณต้องเปิดใช้งาน
ฉันเพิ่งเพิ่มสิ่งต่อไปนี้ในSQLOpenHelperของฉันซึ่งดูเหมือนจะทำเคล็ดลับ

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

ฉันประกาศคอลัมน์อ้างอิงของฉันดังนี้

mailbox_id INTEGER REFERENCES mailboxes ON DELETE CASCADE

59
ซึ่งหมายความว่าใช้งานได้เฉพาะกับ Android 2.2 Froyo ซึ่งมี SQLite 3.6.22
Intrications

@RedPlanet - เป็นเพราะมีการบังคับใช้ข้อ จำกัด นี้เพียงครั้งเดียวเมื่อมีการเขียนบางสิ่งลงในฐานข้อมูล (คุณไม่สามารถทำลายข้อ จำกัด นี้ได้หากคุณอ่านทั้งหมดจาก db) นอกจากนี้ Phil แทนที่จะใช้วิธี onOpen ก็น่าจะดีกว่าถ้าทำในเมธอด onConfigure ที่มา: developer.android.com/reference/android/database/sqlite/…
Aneem

12
Google แนะนำให้เขียนPRAGMAคำสั่งonConfigure()แต่ต้องใช้ API ระดับ 16 (Android 4.1) จากนั้นคุณก็สามารถโทรsetForeignKeyConstraintsEnabledได้
ปาง

คุณอาจต้องพิจารณาเปิดใช้งานข้อ จำกัด ของคีย์ต่างประเทศในonCreate/ onDowngrade/ onUpgradeซึ่งก่อนหน้าonOpenนี้ ดูรหัสแหล่งที่มาใน Android 4.1.1
Pang

1
@Natix รวมถึงการเรียกใช้ super ช่วยให้มั่นใจได้ว่าฟังก์ชันการทำงานถูกต้องหากมีการแนะนำคลาสระดับกลางระหว่างคลาสที่ใช้งานและพาเรนต์
tbm


26

ดังที่โพสต์จาก e.shishkin กล่าวว่าตั้งแต่ API 16 ขึ้นไปคุณควรเปิดใช้งานข้อ จำกัด ของคีย์ต่างประเทศในSqLiteOpenHelper.onConfigure(SqLiteDatabase)วิธีการโดยใช้ไฟล์db.setForeignKeyConstraintsEnabled(boolean)

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

10

อย่าเก่าเกินไปสำหรับคำถามที่จะตอบด้วยคำตอบที่สมบูรณ์กว่านี้

@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

สิ่งที่ @phil กล่าวถึงเป็นสิ่งที่ดี แต่คุณสามารถใช้วิธีการเริ่มต้นอื่นที่มีอยู่ในฐานข้อมูลเพื่อตั้งค่าคีย์ต่างประเทศ นั่นคือ 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)
    }
}

สำหรับเอกสารอ้างอิงSQLiteDatabase.setForeignKeyConstraintsEnabled


3
เอกสารที่คุณโพสต์แนะนำ: A good time to call this method is right after calling openOrCreateDatabase(File, SQLiteDatabase.CursorFactory) or in the onConfigure(SQLiteDatabase) callback. ดังนั้นแทนที่จะonOpen, onConfigureน่าจะเป็นสถานที่ที่เหมาะสม
Paul Woitaschek

4

ฉันไม่คิดว่า SQLite รองรับสิ่งนี้นอกกรอบ สิ่งที่ฉันทำในแอปของฉันคือ:

  1. สร้างธุรกรรม
  2. ลบข้อมูลรายละเอียด (จุดอ้างอิงในตัวอย่างของคุณ)
  3. ลบข้อมูลหลัก (แทร็กในตัวอย่างของคุณ)
  4. ทำธุรกรรมกับความสำเร็จ

ด้วยวิธีนี้ฉันแน่ใจว่าข้อมูลทั้งหมดถูกลบหรือไม่มีเลย


แต่คุณกำลังลบออกจากตารางทั้งสองโดยใช้วิธีเดียวหรือไม่?
jcrowson

ใช่ฉันไปพร้อมกับตัวอย่าง Notes จาก API ได้ดี เมื่อฉันจะลบสิ่งที่จะเป็นแทร็กในกรณีของคุณฉันจะสร้างธุรกรรมลบแทร็กและเวย์พอยท์และทำธุรกรรม ทั้งหมดนี้ทำได้ในครั้งเดียว
Thorsten Dittmar

4

android รองรับทริกเกอร์และ sqlite ไม่รองรับการลบน้ำตกประเภทนั้น ตัวอย่างของการใช้ทริกเกอร์เกี่ยวกับหุ่นยนต์ที่สามารถพบได้ที่นี่ แม้ว่าการใช้ธุรกรรมตามที่ Thorsten ระบุไว้นั้นอาจทำได้ง่ายพอ ๆ กับทริกเกอร์


3

เวอร์ชัน SQLite ใน Android 1.6 คือ 3.5.9 ดังนั้นจึงไม่รองรับคีย์ต่างประเทศ ...

http://www.sqlite.org/foreignkeys.html "เอกสารนี้อธิบายการสนับสนุนข้อ จำกัด ของคีย์ต่างประเทศของ SQL ที่แนะนำใน SQLite เวอร์ชัน 3.6.19"

ใน Froyo เป็น SQLite เวอร์ชัน 3.6.22 ดังนั้น ...

แก้ไข: เพื่อดูเวอร์ชัน sqlite: adb shell sqlite3 -version


มีวิธีใดบ้างที่จะบังคับใช้ข้อ จำกัด ดังกล่าว .. ฉันหมายความว่ามีวิธีใดบ้างในการอัปเกรดเวอร์ชัน sqlite .. เพราะเราต้องรองรับซอฟต์แวร์เวอร์ชันเป็น android 2.1 ซึ่งมี sqlite เวอร์ชัน 3.5.9 ตามด้านบน
NullPointerException

ไม่คุณต้องจัดการทุกอย่างด้วยตัวเอง :(
GBouerat

1

คีย์ต่างประเทศที่มี "on delete cascade" รองรับ SQLite ใน Android 2.2 ขึ้นไป แต่โปรดใช้ความระมัดระวังในการใช้: บางครั้งจะมีการรายงานข้อผิดพลาดเมื่อเริ่มใช้คีย์ต่างประเทศหนึ่งรายการในคอลัมน์เดียว แต่ปัญหาที่แท้จริงอยู่ที่ข้อ จำกัด ของคีย์ต่างประเทศของคอลัมน์อื่นในตารางลูกหรือตารางอื่น ๆ ที่อ้างอิงตารางนี้

ดูเหมือนว่า SQLite จะตรวจสอบข้อ จำกัด ทั้งหมดเมื่อเริ่มทำงานอย่างใดอย่างหนึ่ง มีการกล่าวถึงจริงในเอกสาร DDL เทียบกับการตรวจสอบข้อ จำกัด ของ DML


0

หากคุณใช้ Android Room ให้ทำตามที่แสดงด้านล่าง

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()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.