在 Core Data 中镜像远程数据库的最佳策略是什么?
假设我在数据库中有两个表:费用和帐户。费用是我感兴趣的数据,该表有一个帐户的外键。该数据库是远程的,通过 Restful 式的命令访问,我只想将我的应用程序所需的数据镜像到 iPhone 上的 Core Data 数据存储中。我正在使用的实际数据库比这个示例大得多。约 30 个表,费用表有约 7 个 FK。我正在与进行 API 设计的人员密切合作,因此如有必要,我可以修改发出请求或返回数据的方式。
将这些数据加载到核心数据中的最佳策略是什么?
我的第一个想法是让费用请求带回 FK 的 ID。
<expense>
<date>1/1/2011</date>
<cost>1.50</cost>
<account_id>123</account_id>
</expense>
如果我的数据存储中已经有一个 ID 为“123”的帐户,则此方法可以正常工作。如果我不这样做,那么每次遇到我没有的 ID 时,我都必须发出额外的网络请求……这将非常慢。我可以通过按特定顺序发出请求来解决这个问题,即在请求费用之前请求所有新帐户,这样我就知道所有 FK 行都存在。我觉得一旦数据库开始达到中等复杂性,这就会变得太麻烦了。
我的第二个想法是让请求返回的数据遵循 FK,并从 FK 返回数据。
<expense>
<date>1/1/2011</date>
<cost>1.50</cost>
<account>
<id>123</id>
<name>Bob's Big Boy</name>
<address>1234 Main Street</address>
</account>
</expense>
这看起来更好,并保证我在需要时可以获得所需的所有数据。如果我还没有帐户“123”,我可以从该 XML 创建一个新帐户对象。不过,我对这种方法的担忧是,随着数据库复杂性的增加,这些 XML 文件可能会变得过大。 Expenses 表有大约 7 个外键,每个表都有多个 FK。感觉就像对一笔费用的简单请求最终可能会返回大量数据。
其他人是如何解决这个问题的?
Let's say that I have two tables in a DB: Expenses and Account. Expenses is the data that I'm interested in and that table has a foreign key to Account. This DB is remote, accessed via Restful-esque commands, and I want to mirror just the data I need for my app in a Core Data data store on the iPhone. The actual DB I'm working with is much bigger than this example. ~30 tables and the Expenses table has ~7 FKs. I'm working closely with the person doing the API design, so I can modify the way I make my requests or the data returned, if necessary.
What is the best strategy for loading this data into Core Data?
My first thought was to have the request for the expense bring back the ids for the FK.
<expense>
<date>1/1/2011</date>
<cost>1.50</cost>
<account_id>123</account_id>
</expense>
This works fine if I already have an account with id '123' in my data store. If I don't, then I've got to make additional web requests every time I encounter an id I don't have… which is going to be incredibly slow. I can get around this by making requests in a specific order, i.e. request all new accounts before requesting expenses, so that I way I know all the FK rows exist. I feel this would become much too cumbersome once the DB starts reaching moderate complexity.
My second thought was to have the data returned from the request follow FKs and return data from the FK.
<expense>
<date>1/1/2011</date>
<cost>1.50</cost>
<account>
<id>123</id>
<name>Bob's Big Boy</name>
<address>1234 Main Street</address>
</account>
</expense>
This looks better and guarantees that I'll have all the data I need when I need it. If I don't already have an account '123' I can create a new account object from that XML. My concern with this method, though, is that as the database grows in complexity, these XML files could become excessively large. The Expenses table has ~7 foreign keys, each of those tables has multiple FKs. It feels like a simple request for just a single Expense could end up returning a huge chunk of data.
How have other people solved this issue?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我假设在任何给定时间您只想在本地应用程序中缓存服务器数据库的一部分,并且缓存的数据可能会随着时间的推移而改变。
您可能想使用“存根”实体来表示您尚未实际下载的相关对象。您可以像这样设置实体:
AccountStub 实体具有根据费用表提供的信息识别服务器数据库中的帐户所需的最低限度信息。它充当完整的 Account 对象的对象图中的占位符(如果您愿意,您可以将其视为一种错误。)
由于 Expenses 与 AccountStub 存在关系,并且 Account 继承自 AccountStub,因此您可以将 Account 替换为根据需要创建 AccountStub(反之亦然)。
您需要为 AccountStub 和 Account 提供自定义子类,以便 AccountStub 可以在实际需要该数据时触发帐户数据的下载和 Account 对象的创建。然后,新的 Account 对象应在其所有关系中替换为 AccountStub(这可能需要相当多的代码)。
要使用,您首先需要获取 Expense 对象的数据并创建该对象。您将尝试使用费用表数据中提供的 ID 获取 AccountStub。设置提取以包含子条目。如果存在具有该 ID 的 AccountStub 或 Account 对象,您将把 Expense 对象添加到关系中。如果没有,您可以使用该 ID 创建一个 AccountStub 对象并将其添加到关系中。现在您有了一个基本的对象图,显示了 Expense 对象与 AccountStub 对象的关系。要访问费用的帐户数据,您首先要检查相关帐户是存根帐户还是完整帐户。如果它是存根,那么您需要在之前加载完整的帐户数据。
该系统的优点是您可以维护相当复杂的对象图,而不必在本地实际拥有所有数据。例如,您可以维持多种关系并处理这些关系。例如,您可以这样扩展您的模型:
如果您想查找费用对象的帐户所有者的名称,您只需使用
account.owner.name
遍历存根中的关系,帐户对象本身就会将仍然只是一个存根。如果您需要在本地节省空间,可以将对象恢复为存根,而不会影响图形。
这需要一些工作,并且您必须密切关注存根,但它可以让您镜像复杂的外部数据库,而不必保留手头的所有数据。
I am assuming that at any given time you only want to cache part of the server DB in the local app and that the data cached may change overtime.
You probably want to use "stub" entities to represent related objects that you haven't actually downloaded yet. You would set up the entities like this:
The AccountStub entity has the bare minimum info needed to identify the Account in the server DB based on info provided from the Expense table. It serves as a placeholder in the object graph for the full fledged Account object (you can think of it as a type of fault if you like.)
Since Expenses has the relationship with AccountStub and Account inherits from AccountStub you can swap out an Account for an AccountStub (and vice versa) as needed.
You will need to provide a custom subclass for AccountStub and Account such that AccountStub can trigger the downloading of account data and the creation of an Account object when that data is actually required. Then the new Account object should be swapped out for AccountStub in all its relationships (that may take rather a lot of code.)
To use, you would first obtain the data for an Expense object and create that object. You would attempt to fetch for an AccountStub with the ID provided from the Expense table data. Set the fetch to include subentries. If an AccountStub or Account object exist with that ID you will add the Expense object to the relationship. If not, the you create an AccountStub object with that ID and add it to the relationship. Now you have a basic object graph showing the relationship of an Expense object to an AccountStub object. To access the account data of an Expense, you would first check if the related account is a stub or a full account. If it is a stub, then you need to load the full account data before preceding.
The advantage of this system is that you can maintain a fairly complex object graph without having to actually have all the data locally. E.g. you can maintain several relationships and walk those relationships. E.g you could expand your model like this:
If you wanted to find the name of an Expense object's account owner, you would just walk the relationship across the stubs with
account.owner.name
the Account object itself would would remain just a stub.If you need to conserve room locally, you can revert an object back to a stub without compromising the graph.
This would take some work and you would have to keep an eye on the stubs but it would let you mirror a complex external DB without having to keep all the data on hand.