Android中使用SQLite的外键约束?在删除级联上


91

我有两个表:航迹和航路点,一个航迹可以有多个航路点,但是航路点仅分配给1条航迹。

在点表的方式中,我有一个名为“ trackidfk”的列,一旦创建了轨道,该列就会插入track_ID,但是我没有在此列上设置外键约束。

删除轨道时,我想删除分配的航点,这可能吗?我读过有关使用触发器的信息,但我认为Android不支持它们。

要创建航点表:

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

    ...
}

Answers:


237

支持带有on delete级联的外键约束,但是您需要启用它们。
我只是将以下内容添加到我的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最好使用onConfigure方法代替onOpen方法,而不是onOpen方法。来源: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中的源代码

1
如果在实现的类及其父级之间引入了中间类,则@Natix包括对super的调用可确保正确的功能。
tbm 2015年


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. 因此onOpenonConfigure似乎不是正确的地方。
Paul Woitaschek '17

4

我不认为SQLite开箱即用。我在应用程序中所做的是:

  1. 创建交易
  2. 删除详细数据(示例中的航点)
  3. 删除主数据(示例中的轨道)
  4. 成功提交交易

这样,我确定所有数据都将被删除或全部都不会被删除。


但是,您是否使用一种方法从两个表中删除?
jcrowson 2010年

是的,我与API中的Notes示例一起进行了很多工作。当我要删除您的情况下的航迹时,我会创建交易,删除航迹和航路点并提交交易。一劳永逸。
Thorsten Dittmar

4

触发器受android支持,而sqlite不支持这种类型的级联删除。在此处可以找到在Android上使用触发器的示例。尽管像Thorsten所说的那样使用事务可能像触发一样容易。


3

android 1.6中的SQLite版本是3.5.9,因此它不支持外键...

http://www.sqlite.org/foreignkeys.html “此文档描述了对SQLite 3.6.19版中引入的SQL外键约束的支持。”

在Froyo中,它是SQLite版本3.6.22,所以...

编辑:查看sqlite版本:adb shell sqlite3 -version


所以有没有办法强制这种约束。.我的意思是有任何办法可以升级sqlite版本..因为我们必须支持软件版本到android 2.1,而后者具有sqlite版本3.5.9
NullPointerException 2012年

不,您必须自己处理所有事情:(
GBouerat,2012年

1

Android 2.2及更高版本中的SQLite支持带有“删除级联”的外键。但是使用它们时要小心:有时在一个列上激发一个外键时会报告一个错误,但真正的问题在于子表中的另一列外键约束或其他引用该表的表。

看起来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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.