我正在实现一个 PRIVATE ContentProvider
,它的关系表很少(一对多,多对多)。在我当前的实现中,所有表都可以通过 URI 访问。如何简化界面,使 URI 不必访问内部“直通”表?
比如我有一个POSTS表,每个POST通过TAGGINGS表有很多TAGS。我只想与 POSTS URI 交互并在ContentProvider
.
因为query
它很容易返回一个带有连接表的游标,但是我该怎么做insert
呢?bulkInsert
我应该调查什么?
我正在实现一个 PRIVATE ContentProvider
,它的关系表很少(一对多,多对多)。在我当前的实现中,所有表都可以通过 URI 访问。如何简化界面,使 URI 不必访问内部“直通”表?
比如我有一个POSTS表,每个POST通过TAGGINGS表有很多TAGS。我只想与 POSTS URI 交互并在ContentProvider
.
因为query
它很容易返回一个带有连接表的游标,但是我该怎么做insert
呢?bulkInsert
我应该调查什么?
这是一个限制ContentProvider
。如果您不将数据暴露给其他应用程序,则可以使用自定义数据库适配器实现,方法和查询直接满足您的要求。
bulkInsert()
在这种情况下无济于事,因为它一次只将行插入到一个表中。但是看看ContentProvider.applyBatch()方法和ContentProviderOperation,ContentProviderOperation.Builder类(您可能需要withValueBackReference()
一对多插入)。
这些链接应该可以帮助您了解如何使用它们:
http://www.grokkingandroid.com/better-performance-with-contentprovideroperation/ http://www.grokkingandroid.com/androids-contentprovideroperation-withbackreference-explained/ withValueBackReference 的语义是什么?
但是请注意,使用ContentProviderOperation
比bulkInsert()
一次插入多行要慢得多,因为它Uri
会在每次执行操作时解析(字符串比较)。这样做您仍然必须公开 Uri 才能插入到子表中。
如果您决定使用applyBatch()
,请在您的提供程序中覆盖它,以便它在一个事务中执行所有操作,从而保持数据的一致性并加快数据库操作:
@Override
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
try {
ContentProviderResult[] results = super.applyBatch(operations);
db.setTransactionSuccessful();
return results;
} finally {
db.endTransaction();
}
}
只要提供了所需的值,您就可以自由地插入到乘法表中。
例如:
ContentValues v = new ContentValues();
v.put("title","post1");
v.put("tag","tag1");
getProvider().insert(POST_URI,v);
在 实现 中insert
, 可以 检查 是否 存在tag
属于 其他 表 的 字段 ( ) 是否 存在 , 如果 存在 , 则 意味着 你 应该 做 额外 的 工作 -- 如果 标签 不 存在 则 先 插入 , 建立 标签 与 帖子 之间 的 正确 关联 .刚插入。
您可以查看android联系人的源代码以供参考。
更新:
要插入多个标签,一种 hack-y 方法是插入逗号分隔的字符串。这并不优雅,但它有效。
只是为了做到这一点:您想拥有一个 URI 并通过对 ContentProvider 的一次插入调用插入一篇文章及其所有标签?正确的?
问题是,您需要在 ContentValues 对象中包含所有值。数据库标准化是有原因的。尽管如此,它可能是可行的。对于标签,这应该很容易。只需对所有标签使用一个字符串。例如“android、ios、bada、wp7”并在您的插入方法中解析此字符串。
您还可以使用命名加整数约定。只要存在 tag1、tag2、... tagX,您就可以从 ContentProvider 的 insert 方法中读取这些值。
两者都不优雅,但会起作用。
在这种情况下,bulkInsert 或 applyBatch 在您的代码中没有位置。如果您想在一个事务中一次使用多个对 ContentProvider 的调用,它们只会发挥作用。
但我认为更好的解决方案确实是实际使用 biegleux 描述的多个操作。
由于您将要插入多个表,因此正常的SQLiteDatabase.insert
辅助函数将不起作用。但这以一种高性能和好的方式是完全可行的。
您需要从将要插入您的 ContentProvider 的用户的端点查看这一点,即使它只是您自己。因此,首先为所有字段定义名称或键。由于您不会使用SQLiteDatabase.insert
,因此实际上不需要将它们命名为与数据库字段相同的名称。所有名称都不应重复。例如,如果您有两个不同表中的字段可能tag
在TableA
和 中重叠,TableB
您可以将这些字段的名称定义为TableA.tag
和TableB.tag
。或者对不冲突的更具描述性的名称使用语义命名。
接下来,您需要使用SQLiteStatement
per this answer创建插入查询。确保您使用的名称createInsert
与 ContentProvider 的调用者用作 ContentValues 中的键的名称相同。