Android 内容提供商 SQLite 连接表
Android Content Provider SQLite Junction Tables
我正在实施一个由相当复杂的 SQLite 数据库模式支持的内容提供程序。数据库有几个连接 table,我不确定它们是否应该对内容提供者的用户可见。
parent table 是通过 Contract
公开的,每个都有自己的内容 URI 等。现在,当通过 ContentResolver#applyBatch()
方法插入数据时,我为每个 table 的内容 URI 创建了 ContentProviderOperation
。到目前为止一切都很清楚。但我的问题是,应该如何填充联结 table,因为它们没有自己的内容 URI?
为了说明这一点,这里有一个例子。我有 2 "parent" table、Movies
和 Actors
。它们之间的关系是 many-to-many,因此我有一个名为 MoviesActors
.
的结点 table
要批量插入,我执行以下操作:
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);
结点 table MoviesActors
应插入包含 movie_id
和 actor_id
的行。在这种情况下,我该如何处理连接点 table?
我想到的唯一一件事是扩展合约,使内容 URI 指向联结点 tables 并添加另一个 ContentProviderOperation
,否则,您如何沟通 movie_id
和 actor_id
到 ContentProvider#applyBatch()
?
我宁愿不向 ContentProvider 的用户公开联结 table,但我在这里可能是错误的...也许这就是应该在 Android 上完成的方式?
我已经搜索这个主题好几天了,但没有找到答案。
任何帮助将不胜感激。
加分题:
是否有必要通过合约公开每一个table?例如,当在 one-to-many 关系中有 child table 时。我特别指的是 Insert/Update/Delete 因为我知道使用 Query 我可以简单地进行连接,但也许我在这里也错了。
非常感谢!
注意:我对第 3 方库解决方案不感兴趣。
我认为您从错误的角度解决问题。您正在尝试设计一个接口来匹配您的数据库结构,但接口应该放在第一位。
首先,界面应该满足您的ContentProvider 客户端的所有要求。如果您的 ContentProvider 客户端需要访问联结 table,您必须公开它(以某种方式,见下文),否则您不必公开。一个好的接口隐藏了实际的实现细节,因此 ContentProvider 客户端不需要关心 ContentProvider 是否由 SQLite 数据库、一堆 in-memory 映射甚至 web-service.
此外,您不应将 ContentProvider 仅视为数据库的接口,而将 Contract 视为数据库模式。 ContentProvider 比它更通用、更强大。主要区别在于 ContentProvider 由 URI 寻址,而在 SQL 中您只有 table 名称。与 table 名称相反,URI 具有结构。 URI 具有标识您要操作的对象(或对象目录)的路径。您还可以将查询参数添加到 URI 以修改操作的行为。在这方面,ContentProvider 可以设计得更像 RESTful 服务。
有关简单电影数据库合同的具体(但不完整)示例,请参见下文。这基本上就是人们设计 RESTful web-service 的方式,除了一件事:就像在您的代码中一样,movie-id 和actor-id由调用者提供。真正的 RESTful 服务会自动创建和分配这些,然后 return 将它们分配给调用者。 ContentProvider 在插入新对象时只能 return long
个 ID。
插入一部新电影
在 /movies/
上插入
值:{"movie_id": <movie-id>, "title": <movie-title>, "year": ...}
插入一个新演员
在 /actors/
上插入
值:{"actor_id": <actor-id>, "name": <actor-name>, "gender": ...}
将现有演员添加到电影
在/movies/movie-id/actors/[=上插入 17=]
值:{"actor_id": <actor-id>}
将现有电影添加到演员:
在/actors/actor-id/movies/[=上插入 17=]
值:{"movie_id": <movie-id>}
可选:将新演员直接添加到电影中:
在/movies/movie-id/actors/[=上插入 17=]
值:{"actor_id": <actor-id>, "name": <actor-name>, "gender": ... }
如果不存在具有给定 id 的演员,此操作将创建新演员并 link 一步到电影中。如果具有此 ID 的参与者已经存在,则会抛出异常。
反过来也可以这样做,为演员添加一部新电影。
从电影中删除演员
在 /movies/movie-id/actors/[= 上删除 87=]actor-id
或
删除 /actors/actors-id/movies/movie-id
获取所有电影
在 /movies/
上查询
获取特定电影
在 /movies/movie-id
上查询
获取特定电影中的所有演员
在 /movies/movie-id/actors/[= 上查询 17=]
获取特定演员演过的所有电影
在/actors/actor-id/movies/[=上查询 17=]
可选的查询选择语句可用于过滤结果。要获取特定演员过去 10 年出演的电影,您可以将选择项 movies_year>=2005
添加到最后一个查询。
通过使用这样的合同,您不会公开联结 table,而是为您的数据库提供一个 REST-like 接口。
ContentProvider 的工作是将这些操作映射到数据库或任何其他 back-end。
我正在实施一个由相当复杂的 SQLite 数据库模式支持的内容提供程序。数据库有几个连接 table,我不确定它们是否应该对内容提供者的用户可见。
parent table 是通过 Contract
公开的,每个都有自己的内容 URI 等。现在,当通过 ContentResolver#applyBatch()
方法插入数据时,我为每个 table 的内容 URI 创建了 ContentProviderOperation
。到目前为止一切都很清楚。但我的问题是,应该如何填充联结 table,因为它们没有自己的内容 URI?
为了说明这一点,这里有一个例子。我有 2 "parent" table、Movies
和 Actors
。它们之间的关系是 many-to-many,因此我有一个名为 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);
结点 table MoviesActors
应插入包含 movie_id
和 actor_id
的行。在这种情况下,我该如何处理连接点 table?
我想到的唯一一件事是扩展合约,使内容 URI 指向联结点 tables 并添加另一个 ContentProviderOperation
,否则,您如何沟通 movie_id
和 actor_id
到 ContentProvider#applyBatch()
?
我宁愿不向 ContentProvider 的用户公开联结 table,但我在这里可能是错误的...也许这就是应该在 Android 上完成的方式?
我已经搜索这个主题好几天了,但没有找到答案。 任何帮助将不胜感激。
加分题:
是否有必要通过合约公开每一个table?例如,当在 one-to-many 关系中有 child table 时。我特别指的是 Insert/Update/Delete 因为我知道使用 Query 我可以简单地进行连接,但也许我在这里也错了。
非常感谢!
注意:我对第 3 方库解决方案不感兴趣。
我认为您从错误的角度解决问题。您正在尝试设计一个接口来匹配您的数据库结构,但接口应该放在第一位。
首先,界面应该满足您的ContentProvider 客户端的所有要求。如果您的 ContentProvider 客户端需要访问联结 table,您必须公开它(以某种方式,见下文),否则您不必公开。一个好的接口隐藏了实际的实现细节,因此 ContentProvider 客户端不需要关心 ContentProvider 是否由 SQLite 数据库、一堆 in-memory 映射甚至 web-service.
此外,您不应将 ContentProvider 仅视为数据库的接口,而将 Contract 视为数据库模式。 ContentProvider 比它更通用、更强大。主要区别在于 ContentProvider 由 URI 寻址,而在 SQL 中您只有 table 名称。与 table 名称相反,URI 具有结构。 URI 具有标识您要操作的对象(或对象目录)的路径。您还可以将查询参数添加到 URI 以修改操作的行为。在这方面,ContentProvider 可以设计得更像 RESTful 服务。
有关简单电影数据库合同的具体(但不完整)示例,请参见下文。这基本上就是人们设计 RESTful web-service 的方式,除了一件事:就像在您的代码中一样,movie-id 和actor-id由调用者提供。真正的 RESTful 服务会自动创建和分配这些,然后 return 将它们分配给调用者。 ContentProvider 在插入新对象时只能 return long
个 ID。
插入一部新电影
在 /movies/
上插入值:{"movie_id": <movie-id>, "title": <movie-title>, "year": ...}
插入一个新演员
在 /actors/
上插入值:{"actor_id": <actor-id>, "name": <actor-name>, "gender": ...}
将现有演员添加到电影
在/movies/movie-id/actors/[=上插入 17=]
值:{"actor_id": <actor-id>}
将现有电影添加到演员:
在/actors/actor-id/movies/[=上插入 17=]
值:{"movie_id": <movie-id>}
可选:将新演员直接添加到电影中:
在/movies/movie-id/actors/[=上插入 17=]
值:{"actor_id": <actor-id>, "name": <actor-name>, "gender": ... }
如果不存在具有给定 id 的演员,此操作将创建新演员并 link 一步到电影中。如果具有此 ID 的参与者已经存在,则会抛出异常。 反过来也可以这样做,为演员添加一部新电影。
从电影中删除演员
在 /movies/movie-id/actors/[= 上删除 87=]actor-id
或
删除 /actors/actors-id/movies/movie-id
获取所有电影
在 /movies/
上查询获取特定电影
在 /movies/movie-id
上查询获取特定电影中的所有演员
在 /movies/movie-id/actors/[= 上查询 17=]
获取特定演员演过的所有电影
在/actors/actor-id/movies/[=上查询 17=]
可选的查询选择语句可用于过滤结果。要获取特定演员过去 10 年出演的电影,您可以将选择项 movies_year>=2005
添加到最后一个查询。
通过使用这样的合同,您不会公开联结 table,而是为您的数据库提供一个 REST-like 接口。
ContentProvider 的工作是将这些操作映射到数据库或任何其他 back-end。