【问题标题】:Android Content Provider SQLite Junction TablesAndroid 内容提供程序 SQLite 连接表
【发布时间】:2016-03-18 08:53:48
【问题描述】:

我正在实现一个内容提供程序,它由一个相当复杂的 SQLite DB 模式支持。数据库有一些联结表,我不确定它们是否应该对 Content Provider 的用户可见。

父表通过Contract 公开,每个表都有自己的内容URI,等等。现在,当通过ContentResolver#applyBatch() 方法插入数据时,我为每个表的内容URI 创建ContentProviderOperation。到目前为止,一切都清楚了。但我的问题是,应该如何填充联结表,因为它们没有自己的内容 URI?

为了说明这一点,这里有一个例子。我有 2 个“父”表,MoviesActors。它们之间的关系是多对多的,因此我有一个名为MoviesActors 的连接表。

要一次性插入,我执行以下操作:

List<ContentProviderOperation> operations = new ArrayList<>;
// movie
operations.add(ContentProviderOperation.newInsert(Contract.Movie.ContentUri).withValue("movie_id", "23asd2kwe0231123sa").build());
// actor
operations.add(ContentProviderOperation.newInsert(Contract.Actor.ContentUri).withValue("actor_id", "89asd02kjlwe081231a").build());
getContentResolver().applyBatch(authority, operations);

连接表MoviesActors 应该插入一行包含movie_idactor_id。在这种情况下我该如何处理连接表?

我想到的唯一一件事是扩展合同以使内容 URI 指向联结表并添加另一个 ContentProviderOperation,否则,您如何将 movie_idactor_idContentProvider#applyBatch() 通信?

我宁愿不将联结表暴露给 ContentProvider 的用户,但我在这里可能是错的......也许这就是它应该在 Android 上完成的方式?

我已经搜索这个主题好几天了,但还没有找到答案。 任何帮助将不胜感激。

额外问题:

是否有必要通过合约暴露每一个表?例如,当子表处于一对多关系时。我特别指的是插入/更新/删除,因为我知道使用查询我可以简单地进行连接,但也许我在这里也错了。

非常感谢!

注意:我对 3rd 方库解决方案不感兴趣。

【问题讨论】:

    标签: android database sqlite android-contentprovider junction-table


    【解决方案1】:

    我认为你从错误的角度解决问题。您正在尝试设计一个与您的数据库结构相匹配的接口,但接口应该是第一位的。

    首先,接口应该满足您的 ContentProvider 客户端的所有要求。如果您的 ContentProvider 客户端需要访问联结表,您必须公开它(以某种方式,见下文),否则您不必这样做。一个好的接口隐藏了实际的实现细节,因此 ContentProvider 客户端不需要关心 ContentProvider 是否由 SQLite 数据库、一堆内存映射甚至是 Web 服务支持。

    此外,您不应将 ContentProvider 视为数据库的接口,而将 Contract 视为数据库架构。 ContentProvider 比这更加通用和强大。主要区别在于 ContentProvider 由 URI 寻址,而在 SQL 中您只有表名。与表名相比,URI 具有结构。 URI 具有标识要操作的对象(或对象目录)的路径。您还可以将查询参数添加到 URI 以修改操作的行为。在这方面,可以将 ContentProvider 设计为非常类似于 RESTful 服务。

    请参阅下面的简单电影数据库合同的具体(但不完整)示例。这基本上就是设计 RESTful Web 服务的方式,除了一件事:就像在您的代码中一样,movie-idactor- id 由调用者提供。真正的 RESTful 服务会自动创建和分配这些,然后将它们返回给调用者。插入新对象时,ContentProvider 只能返回 long ID。

    插入新电影

    插入 /movies/

    值:{"movie_id": &lt;movie-id&gt;, "title": &lt;movie-title&gt;, "year": ...}

    插入一个新演员

    插入 /actors/

    值:{"actor_id": &lt;actor-id&gt;, "name": &lt;actor-name&gt;, "gender": ...}

    将现有演员添加到电影中

    插入 /movies/movie-id/actors/

    值:{"actor_id": &lt;actor-id&gt;}

    将现有电影添加到演员:

    插入 /actors/actor-id/movies/

    值:{"movie_id": &lt;movie-id&gt;}

    可选:直接向电影添加新演员:

    插入 /movies/movie-id/actors/

    值:{"actor_id": &lt;actor-id&gt;, "name": &lt;actor-name&gt;, "gender": ... }

    如果不存在具有给定 id 的演员,则此操作将创建新演员并将其链接到电影。如果具有此 ID 的参与者已经存在,则会引发异常。 反过来也可以这样做,为演员添加一部新电影。

    从电影中删除演员

    删除 /movies/movie-id/actors/actor-id em>

    删除 /actors/actors-id/movies/movie-id em>

    获取所有电影

    查询 /movies/

    获取特定电影

    查询 /movies/movie-id

    让所有演员在特定电影中演出

    查询 /movies/movie-id/actors/

    获取特定演员演过的所有电影

    查询 /actors/actor-id/movies/

    可选的查询选择语句可用于过滤结果。要获取特定演员在过去 10 年中出演的电影,您可以将选择 movies_year&gt;=2005 添加到最后一个查询中。

    通过使用这样的合同,您不会公开联结表,而是为您的数据库提供类似 REST 的接口。

    ContentProvider 的工作是将这些操作映射到数据库或任何其他后端。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多