4

我正在实施PRIVATE ContentProvider,该表格包含很少关系的表格(一对多,多对多)。在我目前的实现中,所有的表都可以通过URI访问。 我该如何简化接口,以便不必通过URI来访问内部'through'表?使用ContentProvider插入到多个表格

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

对于query它简单地返回与连接表的游标,但我该怎么做与insert?是bulkInsert我应该看看?

回答

1

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

例如:

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

insert实现,您可以检查是否字段(tag)属于其它表exists.If这样做,意味着你应该做一些额外的工作 - 第一次插入标签如果它不存在,请在标签和刚刚插入的帖子之间建立正确的关联。

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

UPDATE:

要插入乘标签,一个黑客-Y的办法是插入一个逗号分隔的字符串。这不是优雅,但它的作品。

+0

一个帖子有很多标签,所以我需要传递一个数组或一组标签。我没有看到它可能与ContentValues。 – 2012-07-15 13:48:25

+0

@Gal:要插入多个标记,一种hack-y方法是插入逗号分隔的字符串。这不是优雅,但它的作品。 – pierrotlefou 2012-07-17 01:15:13

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?

但注意,使用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(); 
    } 
} 
+0

所以不暴露子表的唯一方法是使用'applyBatch'? – 2012-07-15 14:05:17

+0

你仍然需要定义uri来插入子表,这就是ContentProvider被处理的方式(如果你想禁止查询子表,你可以在子表上使用IllegalArgumentException对子查询进行操作),对内容提供者的访问是基于Uris并且提供者主要用于一个表,但在涉及表之间的关系时它们有局限性,但公开单独的uri以插入子表没有什么错误,发布的applyBatch()方法可帮助您准备操作并在一个表中执行它们事务 – biegleux 2012-07-15 14:18:11

+0

这是一个使用内容提供者而不是自己的数据库适配器实现的折衷 – biegleux 2012-07-15 14:21:51

1

只是为了得到这个权利:你想拥有一个URI并通过对ContentProvider的一次插入调用来插入一个帖子及其所有标签?正确?

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

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

既不优雅,但会工作。

在这种情况下,bulkInsert或applyBatch在您的代码中没有位置。只有在一次和一次事务内使用多个对您的ContentProvider的调用时,它们才会发挥作用。

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

0

由于您将要插入多个表中,所以普通的SQLiteDatabase.insert辅助函数将不起作用。但是,这是完全可行的,性能优良的方式。

您需要从将要插入ContentProvider的用户的端点来看待此问题,即使它只是您自己。所以首先为你们所有的字段定义名字或者键。由于您不会使用SQLiteDatabase.insert,因此实际上并不需要将它们命名为与数据库字段相同。没有一个名称应该重复。例如,如果您有两个不同表中的字段重叠,则TableA中的tagTableB可能与TableA.tagTableB.tag中的那些字段的名称重叠。或者使用语义命名来获得更多不会相互冲突的描述性名称。

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