【问题标题】:Android Room: How to model relationships?Android Room:如何建模关系?
【发布时间】:2017-10-19 13:46:26
【问题描述】:

我刚刚开始使用 Room,虽然一切似乎都非常直观,但我目前并不真正了解我该如何处理人际关系。

因为 SQLite 是关系型数据库,所以可以指定对象之间的关系。尽管大多数 ORM 库允许实体对象相互引用,但 Room 明确禁止这样做。即使您不能使用直接关系,Room 仍然允许您定义实体之间的外键约束。(来源:https://developer.android.com/topic/libraries/architecture/room.html#no-object-references

  1. 您应该如何为多对多一对多关系建模?
  2. 这在实践中会是什么样子(例如 DAO + 实体)?

【问题讨论】:

  • 使用 Room,而不是 POJO 建模数据库表,POJO 建模查询结果集。这类似于 Web 服务 API 客户端(例如 Retrofit、Apollo-Android),它们不对底层 Web 服务的数据库进行建模,而是对您查询的结果进行建模。因此,对于 Room,没有关系,因为查询结果没有关系。
  • 所以基本上“常规查询结果”然后转换为POJO?因此在 POJO 级别上没有关系?我理解正确吗?感谢您的评论!
  • “我理解正确吗?” -- 是的,这就是我对 Room 的解释。他们在the Google I|O 2017 presentation on Room 中讨论了这一点,接近尾声。
  • 再次感谢您!顺便说一句,这本书很棒 - 一定要花些时间看看!

标签: android database sqlite orm android-room


【解决方案1】:

你可以使用@Relation注解来处理Room的关系。

一个方便的注解,可以在 Pojo 中自动使用 获取关系实体。当 Pojo 从查询返回时,所有 Room 也获取了它的关系。

See document.

(谷歌的文档有令人困惑的例子。我已经在我的另一个答案中写了步骤和一些基本解释。你可以check it out

【讨论】:

  • 谢谢,真的很有帮助!当他们在 Google I/O 上展示 Room 时,我认为一切都会变得非常简单。现在,在看到如何处理关系之后,我认为这很容易让您感到困惑,因为必须创建如此多的类(如果您有 5 个以上的表并且它们之间有很多关系,那么可能会开始混淆哈哈)。
  • @ReneFerrari:我认为复杂性在于代码,以便在运行应用程序时获得更好的性能和没有地雷。延迟加载非常适合编码,但它很糟糕,因为您永远不知道数据库将在哪个线程中被查询。您可以通过延迟加载列表视图中的字段来跳过帧或更糟的是获得 ANR。
  • 如何处理房间以下问题:我有一个 Player、Game 和一个 PlayerInGame 实体。现在我想加载包含 PlayerInGame 对象列表的游戏,每个 PlayerInGame 对象也应该包含 Player。房间可以吗?
【解决方案2】:

我创建了一个简单的便捷方法,可以手动填充一对多关系。 因此,例如,如果 Country 和 City 之间存在一对多的关系,则可以使用该方法手动填充 Country 中的 cityList 属性。

/**
 * @param tableOne The table that contains the PK. We are not using annotations right now so the pk should be exposed via a getter getId();
 * @param tableTwo The table that contains the FK. We are not using annotations right now so the Fk should be exposed via a getter get{TableOneName}Id(); eg. getCountryId();
 * @param <T1>     Table One Type
 * @param <T2>     Table Two Type
 * @throws NoSuchFieldException
 * @throws IllegalAccessException
 * @throws NoSuchMethodException
 * @throws InvocationTargetException
 */
private static <T1, T2> void oneToMany(List<T1> tableOne, List<T2> tableTwo) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

    String tableOneName = tableOne.get(0).getClass().getSimpleName();
    String tableTwoName = tableTwo.get(0).getClass().getSimpleName();
    for (T1 t1 :
            tableOne) {
        Method method = t1.getClass().getMethod("getId");
        Integer pkId = (Integer) method.invoke(t1);
        List<T2> listForCurrentId = new ArrayList<>();
        for (T2 t2 : tableTwo) {
            Method fkMethod = t2.getClass().getDeclaredMethod("get".concat(tableOneName).concat("Id"));
            Integer fkId = (Integer) fkMethod.invoke(t2);
            if (pkId == fkId) {
                listForCurrentId.add(t2);
            }
        }
        Method tableTwoList = t1.getClass().getMethod("set".concat(tableTwoName).concat("List"), List.class);
        tableTwoList.invoke(t1, listForCurrentId);
    }
}

这就是我使用它的方式。

   SystemDefaults systemDefaults = new SystemDefaults();
    return Single.zip(systemDao.getRoles(), systemDao.getCountries(), systemDao.getCities(), (roles, countries, cities) -> {
        systemDefaults.setRoles(roles);
        *ConvenienceMethods.oneToMany(countries,cities);*
        systemDefaults.setCountries(countries);
        return systemDefaults;
    });

【讨论】:

    猜你喜欢
    • 2022-08-18
    • 1970-01-01
    • 2022-07-08
    • 1970-01-01
    • 2017-11-23
    • 1970-01-01
    • 2018-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多