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

Posted

技术标签:

【中文标题】Android中使用SQLite的外键约束?在删除级联【英文标题】:Foreign key constraints in Android using SQLite? on Delete cascade 【发布时间】:2011-02-02 11:39:14 【问题描述】:

我有两张表:tracks 和 waypoints,一个track 可以有很多waypoints,但是一个waypoint 只能分配给一个track。

在路点表中,我有一个名为“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"
                + " );"
              );

    ...

【问题讨论】:

【参考方案1】:

支持带有 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

【讨论】:

这意味着它只适用于具有 SQLite 3.6.22 的 Android 2.2 Froyo @RedPlanet - 这是因为只有在将某些内容写入数据库时​​才会强制执行此约束。 (如果你所做的一切都是从数据库中读取,你就不能打破这个约束)另外,Phil,而不是 onOpen 方法,在 onConfigure 方法中可能会更好。来源:developer.android.com/reference/android/database/sqlite/… Google 建议在 onConfigure() 中写入 PRAGMA 语句,但它需要 API 级别 16 (Android 4.1),到那时您可以简单地调用 setForeignKeyConstraintsEnabled 可能还需要考虑在onCreate/onDowngrade/onUpgrade 中启用外键约束,它们在onOpen 之前。见source code in Android 4.1.1。 @Natix 包括对 super 的调用可确保在实现的类与其父级之间引入中间类时功能正确。【参考方案2】:

自 Android 4.1 (API 16) SQLiteDatabase 支持:

public void setForeignKeyConstraintsEnabled (boolean enable)

【讨论】:

【参考方案3】:

正如 e.shishkin 的帖子所说,从 API 16 开始,您应该使用 db.setForeignKeyConstraintsEnabled(boolean)SqLiteOpenHelper.onConfigure(SqLiteDatabase) 方法中启用外键约束

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

【讨论】:

【参考方案4】:

一个问题永远不会太老而无法用更完整的答案来回答。

@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);

【讨论】:

【参考方案5】:

@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

【讨论】:

您发布的文档建议:A good time to call this method is right after calling openOrCreateDatabase(File, SQLiteDatabase.CursorFactory) or in the onConfigure(SQLiteDatabase) callback. 因此,onConfigure 似乎是正确的地方,而不是 onOpen【参考方案6】:

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

    创建事务 删除详细数据(示例中的航点) 删除主数据(示例中的轨道) 成功提交事务

这样我可以确定要么删除所有数据,要么不删除。

【讨论】:

但是您是否使用一种方法从两个表中删除? 是的,我非常喜欢 API 中的 Notes 示例。当我要删除您的情况下的轨道时,我会创建事务,删除轨道和航点并提交事务。一口气完成。【参考方案7】:

android 支持触发器,sqlite 不支持这种类型的级联删除。在 android 上使用触发器的示例可以在 here 找到。尽管使用 Thorsten 所说的事务可能就像触发器一样简单。

【讨论】:

【参考方案8】:

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版本..因为我们必须支持将软件版本支持到具有sqlite版本3.5.9的android 2.1如上 不,你必须自己处理一切:(【参考方案9】:

Android 2.2 及更高版本的 SQLite 支持带有“on delete cascade”的外键。但使用时要小心:有时在某一列上启动一个外键时会报错,但真正的问题在于子表中的另一列外键约束,或者其他一些引用该表的表。

看起来 SQLite 在启动其中一个约束时会检查所有约束。它实际上在文档中提到。 DDL 与 DML 约束检查。

【讨论】:

【参考方案10】:

如果您使用的是 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()

【讨论】:

以上是关于Android中使用SQLite的外键约束?在删除级联的主要内容,如果未能解决你的问题,请参考以下文章

如何在删除时处理 MySql + Laravel 中的外键约束?

数据库中的外键约束

Mysql的外键约束内外连接查询以及锁

小议Oracle外键约束修改行为(一)

从表 2 中的外键约束中删除表 1 中确实具有主键的记录

禁止级联删除的外键约束