我有一个由SQLiteDatabase
支持的内容提供商。请考虑以下两个调用:
//the following call results in "DELETE FROM DETAILS":
context.getContentResolver().delete(getContentUriOfDetailTable(), null, null); //line 1
//the following call results in "DELETE FROM MASTER":
context.getContentResolver().delete(getContentUriOfMasterTable(), null, null); //line 2
据我所知,内容提供商are thread safe, when backed by an SQLiteDatabase。但有没有保证,第1行触发的SQL的执行在第2行触发的SQL执行之前就完成了?
背景 从表DETAIL到MASTER有一个外键引用,我得到了一个bug报告
SQLiteConstraintException:外键约束失败(代码19)
来自第2行,但我无法重现它。
表:
CREATE TABLE MASTER
(_id PRIMARY KEY autoincrement,
VALUE text
);
CREATE TABLE DETAILS (
_id PRIMARY KEY autoincrement,
MASTERIDX integer not null,
VALUE text,
FOREIGN KEY(MASTERIDX) REFERENCES MASTER(_id)
);
我的ContentProvider没什么特别的:
public class MyDbContentProvider extends ContentProvider {
private MyDbOpenHelper mDbHelper;
//...
//...
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
int rowsDeleted = 0;
String where;
MyUriMatcher.MatchResult matchRes = mURIMatcher.matchTable(uri);
TableDef tableDef = matchRes.tableDef;
switch (matchRes.uriType) {
case ALL_ITEMS:
where = selection;
break;
case SINGLE_ITEM:
String idstr = uri.getLastPathSegment();
where = TableDef._ID + " = " + idstr;
if (!TextUtils.isEmpty(selection)) {
where += " AND (" + selection + ")";
}
break;
default:
throw new IllegalArgumentException("Unsupported URI for delete: " + uri);
}
try {
rowsDeleted = db.delete(tableDef.getTableName(), where, selectionArgs);
} catch (SQLException e) {
throw createCustomSqlException(e, "DELETE", uri, null, where, selectionArgs);
}
// notify all listeners of changes:
if (rowsDeleted > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsDeleted;
}
}
这是堆栈跟踪:
android.database.SQLException: SQLiteConstraintException: foreign key constraint failed (code 19) for DELETE for following URI: content://de.myapp.mobil.myappDbProvider/master and this selection: null
at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)
at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:858)
at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:754)
at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64)
at android.database.sqlite.SQLiteDatabase.delete(SQLiteDatabase.java:1494)
at de.myapp.mobil.MyDbContentProvider.delete(MyDbContentProvider.java:267)
at android.content.ContentProvider$Transport.delete(ContentProvider.java:228)
at android.content.ContentResolver.delete(ContentResolver.java:958)
at de.myapp.CommonDbContract$TableDef.deleteAll(CommonDbContract.java:119)
at de.myapp.mobil.MyDbContract.deleteAll(MyDbContract.java:1730)
at de.myapp.mobil.MyDbContract.recreateDbOrDeleteAll(MyDbContract.java:1761)
at de.myapp.mobil.SettingsActivity$ResetAllCommand.execute(SettingsActivity.java:77)
at de.myapp.mobil.SettingsActivity$ResetAllCommand.execute(SettingsActivity.java:1)
at de.myapp.DlgUtils$DataCommand.execute(DlgUtils.java:54)
at de.myapp.DlgUtils$CombinedCommand.execute(DlgUtils.java:116)
at de.myapp.DlgUtils$CommandWrapper.onClick(DlgUtils.java:157)
答案 0 :(得分:4)
ContentResolver
个对象在幕后使用Binder
(IBinder
)接口与ContentProvider
个实例进行通信。 Binder
来电被阻止,因此它们会在您的应用中按顺序执行。检查AOSP来源:ContentResolver和IContentProvider。 IContentProvider
是一个同步Binder接口,没有async
关键字应用于AIDL定义的接口。
另外,检查外键上的SQLite文档。如果您尝试从MASTER表中删除所有行,但在DETAIL表中仍然存在引用MASTER表ID的行,那么它将失败:sqlite.org/foreignkeys.html。
答案 1 :(得分:0)
而不是这个
CREATE TABLE MASTER
(_id PRIMARY KEY autoincrement,
VALUE text
);
CREATE TABLE DETAILS (
_id PRIMARY KEY autoincrement,
MASTERIDX integer not null,
VALUE text,
FOREIGN KEY(MASTERIDX) REFERENCES MASTER(_id)
);
只需更新此
CREATE TABLE MASTER
(_id PRIMARY KEY autoincrement,
VALUE text
);
CREATE TABLE DETAILS (
_id PRIMARY KEY autoincrement,
MASTERIDX integer not null,
VALUE text,
FOREIGN KEY(MASTERIDX) REFERENCES MASTER(_id) ON DELETE CASCADE
);
现在,这一行足以删除主表和详细信息表中的数据:
//the following call results in "DELETE FROM MASTER":
context.getContentResolver().delete(getContentUriOfMasterTable(), null, null); //line 2