Android Room: как моделировать отношения?

Я только начал работать с Room, и хотя все кажется довольно интуитивно понятным, в настоящее время я не совсем понимаю, как именно я могу справляться с отношениями.

Поскольку SQLite - это реляционная база данных, вы можете указать отношения между объектами. Несмотря на то, что большинство библиотек ORM позволяют объектам сущностей ссылаться друг на друга, Room явно запрещает это. Несмотря на то, что вы не можете использовать прямые отношения, Room по-прежнему позволяет вам определять ограничения внешнего ключа между объектами. (Источник: https://developer.android.com/topic/libraries/architecture/room.html#no-ссылкинаобъекты)

  1. Как вы должны смоделировать отношения Многие ко многим или Один ко многим?
  2. Как это будет выглядеть на практике (пример DAO + Entities)?

person Rene Ferrari    schedule 19.05.2017    source источник
comment
При использовании Room, а не таблиц базы данных моделирования POJO, наборы результатов запроса модели POJO. Это похоже на клиентов API веб-службы (например, Retrofit, Apollo-Android), которые не моделируют базу данных базовой веб-службы, а скорее моделируют результаты, которые вы запрашиваете. Следовательно, с Room нет отношений, потому что результат запроса не имеет отношения.   -  person CommonsWare    schedule 19.05.2017
comment
Значит, в основном результат обычного запроса преобразуется в POJO? И поэтому на уровне POJO отношений нет? Я правильно понял? И спасибо за ваш комментарий!   -  person Rene Ferrari    schedule 19.05.2017
comment
Я правильно понял? - Да, именно так я интерпретирую Room. Они освещают этот момент в презентации Google I | O 2017 на Room, ближе к концу. .   -  person CommonsWare    schedule 19.05.2017
comment
Еще раз спасибо! Кстати замечательная книга - обязательно прочтите ее когда-нибудь!   -  person Rene Ferrari    schedule 19.05.2017


Ответы (2)


Вы можете использовать аннотацию @Relation для обработки отношений в Room.

Удобная аннотация, которую можно использовать в Pojo для автоматической выборки сущностей отношений. Когда Pojo возвращается из запроса, Room также получает все его отношения.

См. документ.

(В документе Google есть запутанные примеры. Я написал шаги и некоторые базовые объяснения в своем другом ответе. Вы можете проверить)

person Devrim    schedule 07.06.2017
comment
Спасибо, это действительно очень помогает! Когда они представили Room на Google I / O, я подумал, что все будет очень просто. Теперь, увидев, как обрабатываются отношения, я думаю, это вас очень легко запутает, поскольку необходимо создать так много классов (возможно, начнется путаница, если у вас есть 5+ таблиц и много отношений между ними, ха-ха). - person Rene Ferrari; 10.06.2017
comment
@ReneFerrari: Я думаю, что сложность заключается в коде, чтобы обеспечить лучшую производительность и отсутствие мин при запуске приложения. Ленивая загрузка отлично подходит для кодирования, но это плохо, потому что вы никогда не знаете, в каком потоке будет запрашиваться база данных. Вы можете пропускать кадры или, что еще хуже, получать ошибки ANR, просто загружая поле в виде списка лениво. - person Benoit Duffez; 19.08.2017
comment
Как вы можете справиться со следующим с комнатой: у меня есть сущность Player, Game и PlayerInGame. Теперь я хочу загрузить игру, содержащую список объектов PlayerInGame, и каждый объект PlayerInGame также должен содержать файл Player. Это возможно с комнатой? - person julien-100000; 12.03.2019

Я создал простой удобный метод, который вручную заполняет отношения "один ко многим". Так, например, если у вас есть один ко многим между Country и City, вы можете использовать этот метод, чтобы вручную заполнить свойство cityList в Country.

/**
 * @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;
    });
person Muhammad Ahmed AbuTalib    schedule 10.09.2017