使用ContentProvider插入多个表

时间:2012-07-15 13:02:54

标签: android android-contentprovider android-contentresolver android-sqlite

我正在实现一个私有ContentProvider,它有几个关系表(一对多,多对多)。在我当前的实现中,所有表都可以通过URI访问。 如何简化界面,以便URI不必访问内部的“通过”表?

例如,我有一个POSTS表,每个POST通过TAGGINGS表有很多TAGS。 我想只与POSTS URI进行交互,并在ContentProvider内进行“私人”工作。

对于query,简单地返回带有连接表的游标,但是如何使用insert执行此操作?是bulkInsert我应该研究什么?

4 个答案:

答案 0 :(得分:7)

这是ContentProvider的限制。如果您没有将数据暴露给其他应用程序,则可以使用自定义数据库适配器实现,方法和查询直接满足您的要求。

bulkInsert()在这种情况下无济于事,因为它一次只将行插入一个表中。但请查看ContentProvider.applyBatch()方法和ContentProviderOperationContentProviderOperation.Builder类(您可能需要withValueBackReference()进行一对多插入)。

这些链接可以帮助您了解如何使用它们:

http://www.grokkingandroid.com/better-performance-with-contentprovideroperation/ http://www.grokkingandroid.com/androids-contentprovideroperation-withbackreference-explained/ What are the semantics of withValueBackReference?

但请注意,如果您一次插入多行,则使用ContentProviderOperationbulkInsert()慢得多,因为每次操作将解析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();
    }
}

答案 1 :(得分:1)

只要提供了所需的值,您就可以自由插入乘法表。

例如:

ContentValues v = new ContentValues();
v.put("title","post1");
v.put("tag","tag1");
getProvider().insert(POST_URI,v);

insert的实现中,您可以检查字段(tag)是否属于其他表存在。如果存在,则表示您应该执行额外的工作 - 如果是,则先插入标记不存在,在标签和刚插入的帖子之间设置正确的关联。

您可以查看android联系人的源代码以供参考。

更新:

要插入乘法标记,一种hack-y方式是插入逗号分隔的字符串。这不是很优雅,但它确实有效。

答案 2 :(得分:1)

只是为了做到这一点:你想拥有一个URI并插入一个帖子及其所有标签,并对ContentProvider进行一次插入调用?正确的吗?

问题是,您需要拥有ContentValues对象中的所有值。数据库中存在规范化的原因。不过它可能是可行的。对于标签,这应该很容易。只需对所有标记使用一个字符串。例如“android,ios,bada,wp7”,并在插入方法中解析此字符串。

您还可以使用命名加整数约定。只要有tag1,tag2,... tagX,您就可以从ContentProvider的insert方法中读取这些值。

既不优雅,也会奏效。

在这种情况下,bulkInsert或applyBatch在您的代码中没有位置。如果您想在一次交易中同时使用对ContentProvider的多次调用,它们才会发挥作用。

但我认为更好的解决方案确实是实际使用biegleux所描述的多个操作。

答案 3 :(得分:0)

由于您要插入多个表,正常的SQLiteDatabase.insert辅助函数将无法正常工作。但这是完全可行的,性能良好。

您需要从即将插入ContentProvider的用户的端点查看此内容,即使只是您自己。因此,首先要为所有字段定义名称或键。由于您不使用SQLiteDatabase.insert,因此实际上不需要将它们命名为与数据库字段相同的名称。这些名称都不应该重复。例如,如果您在tagTableA中的TableB重叠了两个不同的表中的字段,则可以将这些字段的名称定义为TableA.tagTableB.tag 。或者使用语义命名来获得不会发生冲突的更具描述性的名称。

接下来,您需要使用SQLiteStatementanswer创建插入查询。确保您在createInsert中使用的名称与ContentProvider的调用者在ContentValues中用作键的名称相同。