Эффективное проектирование схемы Realm и другие советы Realm

Я уже написал несколько советов о том, как в целом использовать Realm, но я видел конкретную RealmModel, написанную кем-то на Stack Overflow, и мне казалось, что мне, вероятно, следует поделиться несколькими советами о том, как спроектировать правильное Realm схемы, или хотя бы упростить вещи на будущее.

Это было следующее:

С этим набором объектов Realm связано много боли.

Прежде чем мы перейдем к этому, мы должны понять несколько вещей:

  • Realm не является реляционным (это NoSQL). В результате фактических «присоединений» не бывает. Столбцы на самом деле не «столбцы», это свойства объектов. И хотя между объектами существуют «ссылки на объекты» как «отношения», это не работает, как соединения - это, по сути, свойство, указывающее на другой объект.
  • Поскольку Realm не является реляционным, «нормализация» не является обязательной. Имеет смысл вручную создавать «вычисляемые свойства», переопределив установщики для нескольких свойств и вычислив значение другого свойства.
  • RealmObject s не поддерживает каскадное удаление и / или удаление ненужных объектов, поэтому добавление ненужных объектов значительно затрудняет их удаление.
  • Модели Realm не поддерживают целый набор вещей - абстрактные классы, которые сами являются абстрактными, обобщенные типы, коллекции, которые не являются RealmList. Хотя они поддерживают интерфейсы.

Советы

- Вы всегда должны стремиться получить свой список RealmObjects в качестве результатов, возвращаемых непосредственно запросом.

Конкретный пример.

Если у вас есть класс Product, в котором есть поля количество и цена, и вы хотите отсортировать на основе (количество * цена ), то вы, очевидно, не сможете выразить это с помощью RealmQuery, потому что подобные настраиваемые предикаты не поддерживаются.

Однако вы МОЖЕТЕ определить ТРЕТЬЕ свойство с именем «total» и обновить его значение в установщиках.

Потому что тогда ты можешь сделать

И, конечно же, если вы используете поле в запросе, вам обязательно нужно добавить к нему индекс.

- Вместо того, чтобы определять свои собственные «общедоступные статические конечные» константы для полей, используйте помощник по полям имени области.

Есть замечательная сторона (неофициально) для Realm от C. Melchior, которая называется RealmNamesFieldHelper, которая запускает обработчик аннотаций и создает для вас эти классы ___Fields.

annotationProcessor 'dk.ilios:realmfieldnameshelper:1.1.0'

Теперь вам не нужно создавать свои собственные константы, и вы определенно не захотите напрямую писать имена полей вручную. Кто вообще это делает?

- Объединяйте объекты в один для создания однонаправленных ссылок на объекты, чтобы избежать запросов ссылок и ручного «каскадного удаления».

Класс, который я показал в начале, является прекрасным примером того, как усложнить себе жизнь при использовании Realm.

Разделение этого одного класса Email на такое количество классов заставляет вас знать их родителей (или даже их идентификаторы) для эффективного запроса, и это отношение придется настраивать вручную, потому что обратные ссылки не нужны. пока не поддерживается.

Если вы запрашиваете электронную почту по их отправителю, вы в конечном итоге будете использовать запросы ссылок, которые медленнее и несколько сложнее в использовании (и вы не можете сортировать через ссылки пока нет).

Добавление ненужных ссылок на объекты также заставляет вас управлять их удалением. Чтобы удалить все объекты, принадлежащие одному Email, вам нужно будет пройти по ним и вызвать deleteFromRealm () вручную для каждого из них.

Итак, чтобы освежить наш разум, вот он снова:

А вот упрощенная версия:

Бум, управлять намного проще! Теперь вам не нужно возиться с запросами ссылок, беспокоиться о сортировке ссылок и управлять каскадным удалением вручную!

Наличие отдельных объектов однонаправленных ссылок нам не дало.

Это, конечно, невозможно сделать, если между объектами существует множество отношений (RealmList). К сожалению для нас, нам все равно придется их разделять.

- Обработка запросов для родительских объектов без обратных ссылок (до 3.4.0)

Это применимо только до того, как будут введены запросы через @LinkingObjects.

Если вы не можете избежать множества связей между вашими объектами и не можете использовать связывающие объекты, вам все равно следует подумать о возможности создания RealmResults для обеих сторон отношения.

Для этого вы должны настроить идентификаторы с обеих сторон, вроде как объединения (за исключением того, что, как вы знаете, здесь нет объединений).

Не очень конкретный пример:

Таким образом, вы можете запрашивать теги, принадлежащие конкретному сообщению, и вам не нужно вручную искать в списке в объекте Post.

- Обработка запросов для родительских объектов с обратными ссылками (3.4.0+)

Поскольку были введены запросы по @LinkingObjects, вам не нужно устанавливать двунаправленные ссылки. Одна ссылка на другую всегда создает обратную ссылку, которая работает так же, как обычная ссылка.

Другие волшебные вещи, о которых стоит упомянуть

- Я хочу сохранить карту, но карты не поддерживаются

Нет, но вы можете сохранить поле, содержащее «ключ» (это может быть строка или первичный ключ другого объекта). Запрос этого «ключевого» поля даст вам запись, установленную как RealmResults для этого конкретного «ключа».

Как правило, вы можете избегать карт в своих объектах Realm и преобразовывать их в поля, доступные для запроса.

- Обходной путь: если вам действительно нужен полиморфизм, вы можете поделиться полями RealmObject через интерфейс.

Конкретный пример.

Ранг сообщений мне возвращал сервер, и эти ранги были указаны сервером. Он также был разбит на страницы, поэтому вы не могли просто вручную оценить эти ранги на клиенте.

Вы должны были показать оба сообщения в «обычном» фиде, и вы должны были иметь возможность показывать почти те же объекты через «избранный» фид.

Излишне говорить, что первичные ключи совпадают. Звания были бы перезаписаны. И способ управления «категориями» тоже не облегчил эту задачу.

Я решил, что просто разделю свои объекты на две разные «таблицы». Итак, теперь есть FeedItem и FavoriteFeedItem. У меня также была логика фида, которая работала со свойствами FeedItem, но я точно не хотел бы дублировать ее и создавать «FavoriteFeedView» и « FavoriteFeedAdapter ». Ни за что.

Решение, поделитесь полями через интерфейс.

Создание этого интерфейса позволило мне использовать RealmResults ‹FeedItemInterface› после некоторой магии в моем репозитории.

Ву! Теперь я мог использовать FeedItemInterface везде, без дублирования в реальном коде бизнес-логики!

- «Мне действительно не нужен полиморфизм, мне просто нужно несколько классов, которые немного отличаются друг от друга, но имеют одни и те же элементы»

Вы можете использовать квалификатор String для определения типа объекта, к которому относится ваш RealmObject из набора возможных объектов, и использовать поля, которые имеют смысл в контексте данного подтипа. (Этот метод также называется «наследование одной таблицы»).

В моем реальном случае это был выбор его параметра RealmList. Я покажу объект целиком для полноты картины.

ResultType имел возможное значение FIRST, SECOND, THIRD или FOURTH. И в зависимости от этого значения имеет смысл использовать именно этот RealmList. Остальные были пусты. Но как таковой, этот ResultPost имел в основном унифицированную логику.

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

Но эта часть становится очень специфичной для предметной области, поэтому я оставлю все как есть.

Заключение

Надеюсь, это немного помогло в определении схемы Realm. Главный вывод - постарайтесь свести к минимуму однонаправленные ссылки на объекты, если это действительно не необходимо.

Однонаправленные ссылки на объекты являются обычным явлением в обычно используемом обходном пути для примитивных списков, для которого вы можете прочитать дополнительную статью ( о том, как RealmList<RealmString> плохо, и вам не следует его использовать) .

И, как упоминалось ранее, для получения дополнительной информации, связанной с Realm, вы всегда можете проверить мои первую и вторую статьи о рекомендуемом использовании Realm, а также сопровождающий репозиторий« учебников .

(Тема обсуждения Reddit: https://www.reddit.com/r/androiddev/comments/576fl5/designing_the_schema_of_realm_effectively_and/)