我正在实现一个私有ContentProvider
,它有几个关系表(一对多,多对多)。在我当前的实现中,所有表都可以通过URI访问。
如何简化界面,以便URI不必访问内部的“通过”表?
例如,我有一个POSTS表,每个POST通过TAGGINGS表有很多TAGS。
我想只与POSTS URI进行交互,并在ContentProvider
内进行“私人”工作。
对于query
,简单地返回带有连接表的游标,但是如何使用insert
执行此操作?是bulkInsert
我应该研究什么?
答案 0 :(得分:7)
这是ContentProvider
的限制。如果您没有将数据暴露给其他应用程序,则可以使用自定义数据库适配器实现,方法和查询直接满足您的要求。
bulkInsert()
在这种情况下无济于事,因为它一次只将行插入一个表中。但请查看ContentProvider.applyBatch()方法和ContentProviderOperation,ContentProviderOperation.Builder类(您可能需要withValueBackReference()
进行一对多插入)。
这些链接可以帮助您了解如何使用它们:
http://www.grokkingandroid.com/better-performance-with-contentprovideroperation/ http://www.grokkingandroid.com/androids-contentprovideroperation-withbackreference-explained/ What are the semantics of 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();
}
}
答案 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
,因此实际上不需要将它们命名为与数据库字段相同的名称。这些名称都不应该重复。例如,如果您在tag
和TableA
中的TableB
重叠了两个不同的表中的字段,则可以将这些字段的名称定义为TableA.tag
和TableB.tag
。或者使用语义命名来获得不会发生冲突的更具描述性的名称。
接下来,您需要使用SQLiteStatement
按answer创建插入查询。确保您在createInsert
中使用的名称与ContentProvider的调用者在ContentValues中用作键的名称相同。