Перейти к содержимому

Что такое realm

  • автор:

Что такое realm sip?

Вообще, прохождение авторизации в Описание RFC протокола SIP протоколе зависит от «realm», различного для каждого защищаемого домена. Таким образом, в Хеш-сумма (digest — дайджест) авторизации, каждый защищаемый домен имеет свой набор имен пользователей и паролей.

Цитата из RFC 2617:

REALM: Это строка, отправляемая пользователю, который, собственно, знает какое имя пользователя и пароль следует использовать. Эта строка должна содержать, как минимум, имя хоста, производящего авторизацию, и может дополнительно содержать имя списка пользователей, которые могут иметь доступ. Например, эта строка может выглядеть так: «registered_users@gotham.news.com».

Перевод «realm» на русский

А государства, не обладающие ядерным оружием, должны ограничиваться лишь работой в сфере нераспространения.

This is how the problem manifested itself from the realm of programming into the realm of real life.
Именно так проблема проявила себя из сферы программирования в сферу реальной жизни.

Many people believed that spirits and deities of the spirit realm regularly interacted with the realm of the living.

Многие люди считали, что духи и божества духовной сферы регулярно взаимодействуют с царством живых.

A new technology has developed that could largely replace the realm of necessity with the realm of freedom.

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

This realm is known as the fourth dimension, or the realm of mind and knowledge and imagination.
Эта сфера известна как четвертая плотность или царство ума, знания и воображения.

In other words, the astrological realm of moving celestial bodies is like the realm of logical propositions.

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

A human rights-based approach moves development analysis and programming from the amorphous realm of charity to the more measurable and enforceable realm of obligation.

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

There is a heavenly realm but only the pope can communicate with that realm.
Существует небесное царство, но только Папа может общаться с этой сферой.

The Sangha was no longer purely occupied with the spiritual realm; it had crossed into the secular realm.

Сангха более не была занята чисто духовной сферой; она перешла в светскую сферу.

Human freedom, for Berdyaev, is rooted in the realm of spirit, a realm independent of scientific notions of causation.

Человеческая свобода, для Бердяева, внедрена в сфере духа, сфера, независимая от научных понятий причинной обусловленности.

Water flows downward because its natural realm is beneath the Earth realm.
Потоки воды идут вниз, потому что ее естественная сфера ниже Земной сферы.

That events that the government would rather delegate to the realm of the twilight zone or mythology often belong in the very real realm of fact.

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

Flesh belongs to the realm below; Spirit to the realm above.
Плоть принадлежит к земной сфере, Дух же — к царству свыше.
The question and answer fall outside the realm of science.
Считается, что и сам вопрос и ответ на него выходят за рамки научных поисков».
Возможно неприемлемое содержание

Примеры предназначены только для помощи в переводе искомых слов и выражений в различных контекстах. Мы не выбираем и не утверждаем примеры, и они могут содержать неприемлемые слова или идеи. Пожалуйста, сообщайте нам о примерах, которые, на Ваш взгляд, необходимо исправить или удалить. Грубые или разговорные переводы обычно отмечены красным или оранжевым цветом.

Реалистичный Realm. 1 год опыта

Realm давно известен в среде мобильных (и не только) разработчиков. К сожалению, в рунете почти нет статей об этой базе данных. Давайте исправим эту ситуацию.

Год назад в build.gradle нашего проекта появилась строчка:

classpath "io.realm:realm-gradle-plugin:0.87.5" 

За этот год код Realm вырос до версии 3.3, обзавелся множеством фич и починил кучу багов, реализовал новый функционал и получил облачный бекенд. Давайте поподробнее поговорим о Realm в реалиях Andoroid разработки и обсудим тонкие моменты, возникающие при его использовании.

Содержание

  • О нас
  • Realm как стартап
  • Realm как база данных
  • Hello world
  • Сравнение с другими базами данных
  • Live Objects — Синхронное чтение
  • Live Objects — Асинхронное чтение
  • Основные особенности Realm объектов
  • Транзакции
  • Синхронные транзакции
  • Асинхронные транзакции
  • Open/close realm
  • Notifications, RxJava
  • Многопоточность и асинхронность
  • Тестирование
  • Один Realm хорошо, а три лучше
  • Наследование и полиморфизм
  • Kotlin
  • Realm mobile platform
  • Отладка
  • Архитектура
  • Полезные ссылки
  • Заключение

О нас

Мы разрабатываем приложение для коммуникации внутри команды, что-то среднее между telegram и slack. Android приложение написано на Kotlin, с самого начала использовался подход offline-first, т.е. когда все данные, отображенные на экране, достаются из кэша. Попробовав несколько разных баз данных, мы остановились на Realm и в течении года активно использовали его. Данная статья выросла из внутреннего документа по использованию Realm. Статья не является переводом документации и не претендует на полноту описания, это скорее сборник рецептов и разбор тонких моментов. Для полного понимания строго рекомендуем прочитать официальную документацию. Мы же расскажем о своем опыте и какие шишки набили за этот год. Весь код для статьи написан на Kotlin, найти его вы можете на Github.

Realm как стартап

Если говорить о Realm как о компании, то это датский стартап основанный в 2011 году. Ранее проект назывался tight.db. За время существования привлечено 29M$ инвестиций. Зарабатывать компания планирует на основе Realm Mobile Platform, сама же база данных бесплатная и опенсорсная. Realm под Android появился в 2014 году и с тех пор постоянно развивается. Некоторые апдейты ломают обратную совместимость, однако фиксы можно сделать достаточно легко и быстро.

Realm как база данных

Realm — это база данных для нескольких платформ. О себе они пишут:

The Realm Mobile Platform is a next-generation data layer for applications. Realm is reactive, concurrent, and lightweight, allowing you to work with live, native objects.

Если кратко, то это нативная no-sql база данных для Android (Java, Kotlin), iOS (Objective-C, Swift), Xamarin (C#) и JavaScript (React Native, Node.js).
Так же есть backend, который позволяет синхронизировать данные из всех источников.

Из ключевых особенностей стоит отметить zero copy, MVCC и ACID. Встроенного механизма устаревания и очистки данных нет.

Hello world

Hello world под Android выглядит следующим образом:

Добавим в build.gradle

build.gradle (Project level) classpath "io.realm:realm-gradle-plugin:3.3.0" build.gradle (App level) apply plugin: 'realm-android'

В Application настроим Realm Configuration

Realm.init(this) val config = RealmConfiguration.Builder() .build() Realm.setDefaultConfiguration(config) 

И можно начинать работать с базой данных:

val realm = Realm.getDefaultInstance() realm.executeTransaction < realm ->val dataObject = realm.createObject(DataObject::class.java) dataObject.name = "A" dataObject.id = 1 > val dataObject = realm.where(DataObject::class.java).equalTo("id", 1).findFirst() dataObject.name // => A realm.executeTransaction < realm ->val dataObjectTransaction = realm.where(DataObject::class.java).equalTo("id", 1).findFirst() dataObjectTransaction.name = "B" > dataObject.name // => B 

Сравнение с другими базами данных

На хабре есть статья от 8 апреля 2016 года, где сравниваются 9 ORM под Android, в том числе Realm. Realm там в лидерах, вот графики:

Сравнение с другими ORM

image

image

На своем сайте Realm приводит следующую статистику:

Графики с сайта Realm


Можно выделить три главные особенности, которые необходимо учитывать:
Live Objects — Все объекты, полученные из Realm, являются, по сути, прокси к базе данных. За счет этого достигается zero copy (объекты не копируются из базы)
Transactions — Все изменения привязанных объектов данных нужно проводить внутри транзакции
Open\Close — Необходимость открытия\закрытия instance базы данных

Live Objects

Все объекты из Realm можно получить синхронно или асинхронно.

Синхронное чтение
fun getFirstObject(realm: Realm, id: Long): DataObject?

Вызываем метод Realm и блокируем поток, пока не получим объект или null. Использовать объекты, полученные в других потоках, нельзя, поэтому для использования в главном потоке, нужно блокировать ui или использовать асинхронные запросы. К счастью, Realm предоставляет нам прокси, а не сам объект, поэтому все происходит достаточно быстро. С объектом можно работать сразу после получения.

Асинхронное чтение

Весьма неочевидный кейс. Как вы думаете, что произойдет в этом коде:

val firstObject = realm.where(DataObject::class.java).findFirstAsync() log(firstObject.id)

Правильный ответ: получим ошибку java.lang.IllegalStateException
При асинхронном чтении мы хоть и получаем объект сразу, но работать с ним не можем, пока он не загрузится. Проверять это нужно с помощью функции isLoaded() или вызвать блокирующую функцию load(). Выглядит достаточно неудобно, поэтому тут лучше использовать rx. Преобразуем в observable и получаем загруженный объект в OnNext. Асинхронные операции доступны только в потоках с Looper.

fun getObjectObservable(realm: Realm, id: Long): Observable

Основные особенности Realm объектов
  • Получение объектов из базы очень быстрое, десериализации как таковой нет, чтение с диска происходит только при обращении к конкретному полю
  • Именно для этого существует требование делать все поля приватными и обращаться через геттеры
  • Метод copyFromRealm() — позволяет получать отвязанные, полностью собранные объекты, прямо как обычная ORM. Правда и все фишки Realm становятся недоступны. На вход принимается глубина десериализации, по умолчанию MAX_INT
  • В дебагере все поля будут null. Для получения какого-либо значения нам нужно обращаться через геттер
  • Все объекты Live, т.е живые. Изменения распространяются моментально в рамках одного потока. Более сложные кейсы смотрите ниже (многопоточность).
  • Фильтрация объектов осуществляется по полям, причем названия полей вы указываете руками в виде строки. Например так: .equalTo(«id», 1). Это усложняет рефакторинг и приводит к ошибкам в наименовании полей для фильтрации. К сожалению Realm не генерирует переменные с названиями полей, поэтому все выборки лучше прятать внутри функции:

fun findFirstDataObject(id: Long, realm: Realm) : DataObject = realm.where(DataObject::class.java).equalTo("id", id).findFirst() 

RealmObjectChangeListener

Person p = realm.where(Person.class).findFirst(); p.addChangeListener(new RealmObjectChangeListener() < @Override public void onChange(Person person, ObjectChangeSet changeSet) < if (changeSet.isDeleted()) < hide(); // Object was deleted >else < // Use information about which fields changed to only update part of the UI if (changeSet.isFieldChanged("name")) < updateName(person.getName()); >> > >); 
  • Получение списков очень быстрое, по сути мы получаем только count. Все запросы lazy, получить большой список из сложных объектов мы можем очень быстро
  • Списки доступны только для чтения, любые методы изменения приводят к исключению
  • Т.к. мы можем быстро и дешево получать все элементы, можно забыть о проблеме пагинации. Мы всегда отдаем полный список элементов, при скролле обращаемся к объектам, и они быстро получаются из базы. Если нам нужно подгрузить данные, мы запускаем загрузку, получаем данные, сохраняем их в Realm, снова получаем полный список с загруженными элементами и отображаем его
  • До недавнего времени (до версии 3.0) была проблема с перерисовкой всех элементов списка. Если мы используем список для адаптера, то при изменении одного элемента происходит полная перерисовка всего списка. Использовать DiffUtils и сравнивать какие объекты изменились, не получится, т.к. это live объекты. В Realm 3.0 появились OrderedCollectionChangeSet, который сообщает нам DeletionRanges, InsertionRange, ChangeRanges. Стало наконец возможно понять какие объекты и как изменились.

Пример CollectionChangeListener

 private final OrderedRealmCollectionChangeListener> changeListener = new OrderedRealmCollectionChangeListener() < @Override public void onChange(RealmResultscollection, OrderedCollectionChangeSet changeSet) < // `null` means the async query returns the first time. if (changeSet == null) < notifyDataSetChanged(); return; >// For deletions, the adapter has to be notified in reverse order. OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges(); for (int i = deletions.length - 1; i >= 0; i--) < OrderedCollectionChangeSet.Range range = deletions[i]; notifyItemRangeRemoved(range.startIndex, range.length); >OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges(); for (OrderedCollectionChangeSet.Range range : insertions) < notifyItemRangeInserted(range.startIndex, range.length); >OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges(); for (OrderedCollectionChangeSet.Range range : modifications) < notifyItemRangeChanged(range.startIndex, range.length); >> >; 

Транзакции

Изменять привязанные к Realm объекты можно только внутри транзакции, при изменении вне транзакции получим ошибку. С одной стороны, не очень удобно, с другой стороны — дисциплинирует и не дает изменять объекты в любой части кода, только в определенном слое (database). Также нужно помнить, что транзакции внутри другой транзакции запрещены.

Как нельзя делать:

val user = database.getUser(1) button.setOnClickListener
val user = database.getUser(1) button.setOnClickListener

Транзакции можно производить синхронно и асинхронно. Давайте подробнее рассмотрим каждый из вариантов:

Синхронные транзакции:
fun syncTransaction() < Realm.getDefaultInstance().use < it.executeTransaction < val dataObject = DataObject() it.insertOrUpdate(dataObject) >> >

Также можно выполнять транзакции между beginTransaction и commitTransaction, однако рекомендуется использовать именно executeTransaction.

К сожалению, синхронные транзакции не поддерживают onError callback, так что обработка ошибок остается на вашей совести. Есть issue на добавление onError callback c июня 2016 года.

Асинхронные транзакции

Асинхронные транзакции запускаются методом asyncTransaction. На вход отдаем саму transaction и callback onSuccess и onError, на выходе получаем объект RealmAsyncTask, с помощью которого мы можем проверить статус или отменить транзакцию. Асинхронные транзакции запускаются только в тредах с Looper. Пример асинхронной транзакции:

Realm.getDefaultInstance().use < it.executeTransactionAsync(< it.insertOrUpdate(DataObject(0)) >, < log("OnSuccess") >, < log("onError") it.printStackTrace() >) >

Пара важных нюансов:

Вы не сможете присвоить через сеттер объект, не привязанный к Realm. Необходимо сначала положить объект в базу, а потом прикрепить привязанную копию. Пример:

val realm = Realm.getDefaultInstance() val parent = realm.where(Parent::class.java).findFirst() val children = Children() // parent.setChildren(children) 

Много транзакций лучше объединять в одну. В Realm есть внутренняя очередь на транзакции (размером 100) и если вы превысите ее, упадет исключение.

Все асинхронные транзакции работают на одном executor’e

// Thread pool for all async operations (Query & transaction) static final RealmThreadPoolExecutor asyncTaskExecutor = RealmThreadPoolExecutor.newDefaultExecutor();

Если у вас будет много асинхронных операций за короткое время, получите ошибку RejectedExecutionException. Выходом из данной ситуации будет использование отдельного потока и запуск в нем синхронных транзакций или объединение нескольких транзакций в одну.

Open / close realm

Все объекты из базы данных мы получаем, используя конкретный instance Realm-a, и можем работать с ними пока открыт этот instance. Как только мы вызовем realm.close(), любая попытка чтения объекта обернется для нас исключением. Если мы не будем вовремя закрывать Realm, то это приведет к утечкам памяти, т.к. сборщик мусора не умеет корректно работать с ресурсами, используемыми Realm.

В официальной документации рекомендуется открывать / закрывать Realm:

  • для Activity: onCreate / onDestroy
  • для Fragment: onCreateView / onDestroyView

В случае если вам нужно как-то изменить данные или добавить новые, проще всего получить новый instance, записать данные и затем закрыть его. В Kotlin для этого можно использовать .use()

Realm.getDefaultInstance().use < // it = realm instance>

Для чтение объектов с помощью Rx можно использовать “изолированные” instance и закрывать их в doOnUnsubscribe (или использовать Observable.using)

// Use doOnUnsubscribe val realm = Realm.getDefaultInstance() realm.where(DataObject::class.java).findAllSorted("id").asObservable().doOnUnsubscribe < realm.close() >// Use Observable.using Observable.using(Realm.getDefaultInstance(), realm -> realm.where(DataObject::class.java).equalTo("id", id) .findFirstAsync() .asObservable() .filter(realmObject -> realmObject.isLoaded()) .cast(DataObject::class.java), Realm::close);

Также есть особенность, связанная с закрытием Realm в onDestroy\onDestroyView. Иногда после закрытия Realm происходит вызов FragmentManagerImpl.moveToState → ViewGroup.removeView →… → RecyclerViewAdapter.getItemCount() и вызывается метод list.size() от невалидной коллекции. Так что тут нужно проверять isValid() или отвязывать adapter от recyclerView

Если вы используете Kotlin Android Extensions, то работать с view (из kotlinx.android.synthetic.*) из Fragment можно только начиная с метода onViewCreated(), лучше настраивать все listeners в этом методе, чтобы не получить NPE.

После разбора трех самых важных особенностей, пробежимся по менее важным:

Notifications, RxJava

Realm поддерживает уведомления об изменении данных, причем как самого объекта, так и вложенных объектов (всех залинкованных объектов). Реализовано это с помощью RealmChangeListener (нам приходит сам объект), RealmObjectChangeListener ( приходит измененный объект и ObjectChangeSet для него, можно понять какие поля изменились) или с помощью RxJava (в onNext получаем объект, в случае асинхронного запроса необходимо проверять isLoaded(), работает только в потоках с Looper).

RxJava2 пока не завезли, issue висит с сентября 2016 года, когда реализуют — неизвестно, используйте Interop.

Аналогично можно слушать изменения коллекций или всего instance Realm. Слушать изменения внутри транзакций запрещено.

fun getObjectObservable(realm: Realm, id: Long): Observable < return realm.where(DataObject::class.java).equalTo("id", id).findFirstAsync() .asObservable().filter(< it?.isLoaded >).filter < it?.isValid >>

Многопоточность и асинхронность

Realm это MVCC база данных. Википедия говорит про MVCC:

“Управление параллельным доступом с помощью многоверсионности (англ. MVCC — MultiVersion Concurrency Control) — один из механизмов обеспечения параллельного доступа к БД, заключающийся в предоставлении каждому пользователю так называемого «снимка» БД, обладающего тем свойством, что вносимые пользователем изменения в БД невидимы другим пользователям до момента фиксации транзакции. Этот способ управления позволяет добиться того, что пишущие транзакции не блокируют читающих, и читающие транзакции не блокируют пишущих.”

На практике это выглядит следующим образом: мы можем слушать изменения объекта или с помощью RxJava получать измененные объекты в onNext. В случае, если изменения происходят в потоке А, а мы работаем с объектом в потоке B, то поток B узнает об изменениях после закрытия Realm instance в потоке A. Изменения передаются посредством Looper. Если в потоке B Looper-a нет, то изменения не дойдут (можно проверить методом isAutoRefresh()). Выход из данной ситуации — использовать метод waitForChange().

Что касается асинхронных вызовов и транзакций, то их лучше не использовать вовсе. Удобнее переводить действия на отдельный поток и там выполнять синхронные операции. Причин несколько:

  • Нельзя смешивать асинхронные транзакции и синхронные, если смешать, все транзакции станут синхронными
  • Нельзя использовать асинхронные вызовы в потоках без Looper
  • Для длительных транзакций нужно открыть отдельный instance realm, иначе realm может быть закрыт во время транзакции и вы получите исключение
  • Все действия в асинхронной транзакции происходят на отдельном внутреннем executor-е, как следствие вы не можете пользоваться внешними realm объектами, возможно переполнение executor-а, изменения realm object не распространяются между потоками и прочие неудобства

Тестирование

Раньше Realm.java — был final и для тестирования нужен был powerMock или другие подобные инструменты. В данный момент Realm.java перестал быть final и можно спокойно использовать обычный mockito. Примеры тестов в демо проекте или на официальном репозитории

Один Realm хорошо, а три лучше

Работая с Realm мы всегда имеем ввиду стандартный realm, однако существуют еще In-Memory Realm и Dynamic Realm.

Стандартный Realm — можно получить методами Realm.getDefaultInstance() или с помощью конкретной конфигурации Realm.getInstance(config), конфигураций может быть неограниченное количество, это по сути отдельные базы данных.

In-Memory Realm — это Realm, который все записанные данные хранит в памяти, не записывая их на диск. Как только мы закроем этот instance, все данные пропадут. Подходит для кратковременного хранения данных.

Dynamic Realm — используется в основном при миграции, позволяет работать с realm — объектами без использования сгенерированных классов RealmObject, доступ осуществляется по именам полей.

Наследование и полиморфизм

Realm не поддерживает наследование. Любой realm-объект должен или наследоваться от RealmObject или реализовывать интерфейс маркер RealmModel и быть помеченным аннотацией @RealmClass. Наследоваться от существующих Realm объектов нельзя. Рекомендуется использовать композицию вместо наследования. Весьма серьезная проблема, issue висит с января 2015 года, но воз и ныне там.

Kotlin

Realm из коробки работает c Kotlin.
Не работают data class-ы, нужно использовать обычные open class.
Также стоит отметить Kotlin-Realm-Extensions, удобные расширения для работы с RealmObject.

Realm mobile platform

  • Realm Mobile Database – база для хранения данных
  • Realm Object Server – сервер, отвечающий за автоматическую синхронизацию и обработку событий
  • Realm Data Integration API – для подключения и синхронизации данных с существующими БД (Oracle, MongoDB, Hadoop, SAP HANA, Postgres и Redis)

Иллюстрация работы mobile platform

Отладка

Для отладки у нас есть несколько инструментов:

  • RealmLog — выводит лог, есть разные уровни логирования
  • Realm браузер — нужен просмотра базы данных с компьютера. Работает только под Mac. Для просмотра базы на Windows можно использовать Stetho Realm
    Также существуют несколько Android библиотек для удобного просмотра данных на девайсе.
  • WriteCopyTo() — позволяет скопировать базу в файл и отправить ее на анализ.
  • NDK Debugging — для анализа ошибок в нативном коде можно использовать Crashlytics NDK Crash Reporting

Архитектура

Realm отлично подходит для MV* архитектур, когда вся реализация прячется за интерфейсом базы данных. Все обращения и выборки происходят в модуле базы данных (repository), наверх отдаются Observable c автоматически закрываемым realm при unsubscribe. Или принимаем на вход instance realm и производим все действия с ним. При записи объектов мы открываем realm, записываем данные и закрываем его, на вход подается только объект для сохранения. Оба примера смотрите на github.
Увы, использование Realm (без copyFromRealm) накладывает серьезные ограничения на использование clean architecture. Использовать разные модели данных для разных слоев не получится, пропадает весь смысл live объектов и прокси списков. Также сложности возникнут при создании независимых слоев и открытии\закрытии Realm, тк эта операция привязана к жизненному циклу Activity\Fragment. Хорошим вариантом будет изолированный слой получения данных, преобразование объектов и сохранение их в базе данных.

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

Полезные ссылки

Для продолжения знакомства и разбора тонких моментов, рекомендуем следующие статьи:

Заключение

Realm сложнее, чем кажется на первый взгляд. Однако все недостатки с лихвой покрываются его мощностью и удобством. Live объекты, нотфикации и Rx, удобное API и множество других вещей упрощают создание приложений. Из конкурентов можно выделить requery, ObjectBox и GreenDao. Полностью же Realm раскрывает себя при построении offline-first приложений, когда все данные мы получаем из кэша и нам необходимы сложные выборки, а также постоянное обновление данных.
Весь приведенный код вы можете найти на Github

  • android
  • android development
  • android database
  • realm
  • Разработка мобильных приложений
  • Разработка под Android

База данных Realm на Android: Начало

Realm, библиотека хранения от MongoDB, позволяет легко хранить объекты локально. В этом туториале вы создадите приложение с именем PetRealm. Оно использует базу данных Realm для хранения информации о домашних животных и владельцах. Приложение позволяет пользователю добавлять домашних животных для поиска нового дома, добавлять владельцев и показывать список домашних животных, уже взятых из приюта. У него есть возможность назначать для питомца своего владельца.

В ходе этого туториала вы узнаете о:

  • База данных Realm
  • Схема и сущности
  • Вставка объектов
  • Запрос и удаление объектов
  • Отношения между сущностями
  • Realm Studio
  • Сравнение Realm и Room

Начало Работы

Загрузите материалы, нажав кнопку “Скачать Материалы”. Откройте Android Studio Arctic Fox или новее и импортируйте стартовый проект.

Ниже краткое описание работы каждого пакета:

  • common: содержит классы, используемые в разных разделах приложения.
  • di: Здесь вы найдете классы для внедрения зависимостей.
  • owners: содержит классы, связанные с владельцами.
  • pets: содержит классы, связанные с домашними животными.
  • realm: Здесь вы найдете все классы, относящиеся к Realm.

Соберите и запустите приложение. Вы увидите экран с панелью навигации внизу, кнопку для добавления животных и спиннер (или селектор) вверху.

Нажмите на кнопку и вы увидите экран для добавления животных, как показано ниже:

Закройте экран и нажмите на иконку Adopted pets (Принятые питомцы) на панели навигации. Вы увидите следующий пустой экран:

Наконец, нажмите на иконку Owners (Владельцы) на нижней панели навигации. Она откроет экран для владельцев. Нажмите кнопку add (Добавить) и вы увидите следующий экран:

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

Представляем Realm

Пришло время начать свое путешествие по Realm (в пер.: 1. Королевство, Царство; 2. Область, Сфера).

Хоть это может прозвучать как-то магически…, база данных Realm - это система управления базой данных объектов. Этот тип базы данных представляет свои сущности как объекты.

Важно: Realm ACID-совместимый. Это означает, что база данных Realm устанавливает:

  • Атомарность: путем группировки всех операций в транзакции. Если какая-либо из операций завершается неудачно, база данных откатывает все операции.
  • Последовательность: проверяя все изменения с помощью схемы.
  • Изоляция: разрешая только одну операцию записи за раз.
  • Долговечность: за счет немедленного сохранения любых изменений на диск.

Чтобы начать пользоваться Realm, важно изучить некоторые концепции, чтобы понять, как он работает.

Понимание Realm

Realm - это набор связанных объектов, имеющих предопределенную схему. В одном приложении может быть несколько таких наборов. Каждый realm может иметь разные разрешения и может включать разные типы данных или объекты.

Схема - это набор правил, определяющий типы данных, которые может хранить база данных. Вы определяете эту схему как набор полей и их типов данных в одном или нескольких объектах, называемых объектами Realm.

Объект Realm считывает и записывает данные в базу данных и из нее. База данных хранит эти объекты. Они всегда содержат самые свежие значения.

У каждой схемы есть версия. Изменение схемы состоит из добавления или удаления поля или добавления нового realm объекта. Чтобы сообщить базе данных, что изменилось в схеме, вы должны увеличить версию схемы и создать миграцию.

Теперь, когда вы знаете эти основные концепции, пора начать использовать Realm. В следующем разделе вы добавите Realm в пробный проект.

Инициализация Realm

Откройте build.gradle файл проекта. В разделе зависимостей добавьте следующую строку после зависимости navigation-safe-args-gradle-plugin :

classpath "io.realm:realm-gradle-plugin:10.6.0"

Откройте build.gradle файл модуля, чтобы применить плагин. Добавьте следующую строку после последнего примененного плагина:

apply plugin: 'realm-android'
Заметка

При данной установке Realm будут добавлены все зависимости Realm. Однако можно добавить только те, которые необходимы вашему приложению. Вы можете обратиться к официальной документации , чтобы узнать, как это реализовать.

Нажмите sync now (синхронизировать сейчас), чтобы синхронизировать проект. После завершения синхронизации откройте PetApplication.kt и добавьте следующую строку в onCreate() , импортировав io.realm.Realm :

Realm.init(this)

Эта строка инициализирует Realm и делает его доступным во всем приложении. Теперь вы можете получить экземпляр Realm и начать добавлять объекты и делать запросы.

Realm также нуждается в RealmConfiguration для настройки базы данных. Откройте PetsModule.kt из di - пакета и, импортировав io.realm.RealmConfiguration , добавьте следующий код:

// 1. private val realmVersion = 1L @Singleton @Provides fun providesRealmConfig(): RealmConfiguration = // 2. RealmConfiguration.Builder() .schemaVersion(realmVersion) .build()
  1. Объявите переменную с версией для этой схемы.
  2. Используйте RealmConfiguration.Builder() , чтобы установить версию схемы и установить RealmConfiguration .

Пришло время создать и запустить приложение. Оно работает, но по-прежнему показывает пустые экраны. Создание схемы будет первым шагом в добавлении функциональности в приложение.

Создание Схемы

PetRealm использует две сущности: домашние животные и владельцы. Эти сущности создают схему базы данных. На следующей диаграмме показаны эти две сущности вместе с их полями.

У обеих сущностей есть id для идентификации каждого элемента, что делает его первичным ключом. Как видите, существует сочетание типов данных String , int и boolean , а также поля NULL и NOT NULL . Вам необходимо определять эту информацию в каждом realm объекте.

Заметка

Realm поддерживает разные типы полей. Полный список этих полей можно найти в официальной документации .

Для начала вы создадите объект с питомцами. В пакете realm создайте новый класс и назовите его PetRealm.kt. Добавьте следующий код:

// 1. open class PetRealm( @PrimaryKey // 2. var id: String = ObjectId().toHexString(), // 3. @Required // 4. var name: String = "", @Required var petType: String = "", var age: Int = 0, var isAdopted: Boolean = false, @DrawableRes var image: Int? = null // 5. ): RealmObject() // 6.

Импортируйте androidx.annotation.DrawableRes , io.realm.RealmObject , io.realm.annotations.PrimaryKey , io.realm.annotations.Required и org.bson.types.ObjectId .

  1. Вам нужно объявить каждый класс сущности open , а каждое поле как var .
  2. @PrimaryKey указывает, что поле ID является первичным ключом.
  3. Realm предоставляет ObjectId() для создания уникальных идентификаторов для каждого объекта. Вы будете использовать его шестнадцатеричное представление.
  4. @Required указывает, что для поля требуется значение. Добавьте эту аннотацию к name и petType .
  5. Для полей, допускающих значения null , можно использовать типы, допускающие значение null , и назначить null в качестве значения по умолчанию.
  6. Каждая сущность должна быть RealmObject() .

Создайте класс в пакете realm и назовите его OwnerRealm.kt. Добавьте следующий код:

open class OwnerRealm( @PrimaryKey var id: String = ObjectId().toHexString(), @Required var name: String = "", @DrawableRes var image: Int? = null ) : RealmObject()

Импортируйте androidx.annotation.DrawableRes , io.realm.RealmObject , io.realm.annotations.PrimaryKey , io.realm.annotations.Required и org.bson.types.ObjectId .

С помощью этого кода вы добавляете сущность OwnerRealm в базу данных Realm.

Скомпилируйте и запустите приложение. За кулисами Realm добавляет в схему PetRealm и OnwerRealm . Однако вы по-прежнему будете видеть пустой экран.

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

Вставка объектов в Realm

Вставка объектов в базу данных - часть записывающих транзакций Realm. Все операции должны происходить в транзакции. Транзакция - это группа операций чтения и записи, которые Realm выполняет как одну операцию. Каждая операция в транзакции должна быть успешной для успешного завершения транзакции. Если какая-либо операция завершается неудачно, то вся транзакция завершается неудачно.

Откройте файл PetDatabaseOperations.kt в realm пакете. Добавьте в конструктор класса следующий параметр:

private val config: RealmConfiguration

С помощью этой строки вы предоставляете RealConfiguration этому классу.

Измените insertPet() следующим образом:

suspend fun insertPet(name: String, age: Int, type: String, image: Int?) < // 1. val realm = Realm.getInstance(config) // 2. realm.executeTransactionAwait(Dispatchers.IO) < realmTransaction ->// 3. val pet = PetRealm(name = name, age = age, petType = type, image = image) // 4. realmTransaction.insert(pet) > >

Импортируйте io.realm.Realm , io.realm.kotlin.executeTransactionAwait и kotlinx.coroutines.Dispatchers .

  1. Получаете экземпляр Realm, используя RealmConfiguration .
  2. Используете executeTransactionAwait() с Dispatchers.IO , чтобы выполнить транзакцию в фоновом потоке и дождаться ее завершения.
  3. Создаете новый объект PetRealm .
  4. Вставляете новый объект PetRealm .

Решающим фактором является использование экземпляра Realm, который в приведенном выше коде называется realmTransaction , предоставленный в лямбда-функции для вставки вновь созданного объекта.

Скомпилируйте и запустите приложение. Нажмите кнопку Add и добавьте нового питомца следующим образом:

Нажмите Add Pet (Добавить питомца) и вы добавите нового питомца в базу данных. Здесь вы должны мне поверить, т.к. приложение еще ничего не отображает.

Добавьте код, чтобы вставить владельца. Откройте OwnerDatabaseOperations.kt и добавьте в его конструктор следующий параметр:

private val config: RealmConfiguration

Эта строка предоставляет RealmConfiguration этому классу.

Измените insertOwner() следующим образом:

suspend fun insertOwner(name: String, image: Int?) < // 1. val realm = Realm.getInstance(config) // 2. realm.executeTransactionAwait(Dispatchers.IO) < realmTransaction ->// 3. val owner = OwnerRealm(name = name, image = image) // 4. realmTransaction.insert(owner) > >

Этот код выполняет те же действия, что и при добавлении питомца:

  1. Получает экземпляр Realm.
  2. Использует executeTransactionAwait() .
  3. Создает новый объект.
  4. Вставляет его в базу данных.

Скомпилируйте и запустите приложение. Нажмите кнопку Owners (Владельцы). Добавьте имя и выберите изображение долгим нажатием на него, как показано ниже:

Нажмите Add Owner (Добавить владельца). И вам снова придется поверить мне, что вы добавили нового владельца в базу данных.

Не волнуйтесь. Следующий шаг - это научиться запрашивать базу данных и отображать информацию.

Запрос Realm

Realm предоставляет механизм запросов, который позволяет находить, фильтровать и сортировать объекты. Каждый результат запроса - это живой объект. Это означает, что он содержит самые свежие данные, и, если вы решите изменить результат, он изменит сохраненный объект.

Откройте PetDatabaseOperations.kt. Добавьте следующий код в конец класса:

private fun mapPet(pet: PetRealm): Pet

mapPet() сопоставляет объект PetRealm с UI объектом Pet .

Измените retrievePetsToAdopt() следующим образом:

suspend fun retrievePetsToAdopt(): List < // 1. val realm = Realm.getInstance(config) val petsToAdopt = mutableListOf() // 2. realm.executeTransactionAwait(Dispatchers.IO) < realmTransaction ->petsToAdopt.addAll(realmTransaction // 3. .where(PetRealm::class.java) // 4. .findAll() // 5. .map < mapPet(it) >) > return petsToAdopt >

Чтобы получить питомцев, готовых к новому дому, вы:

  1. Получаете экземпляр Realm.
  2. Используете executeTransactionAwait() .
  3. Используете where (PetRealm :: class.java) для получения объектов PetRealm .
  4. findAll() выполняет запрос и возвращает каждый объект PetRealm .
  5. Сопоставляете PetRealm с объектами Pet .

Теперь, когда вы в ударе, откройте OwnerDatabaseOperations.kt и измените retrieveOwners() следующим образом:

suspend fun retrieveOwners(): List < // 1. val realm = Realm.getInstance(config) val owners = mutableListOf() // 2. realm.executeTransactionAwait(Dispatchers.IO) < realmTransaction ->owners.addAll(realmTransaction // 3. .where(OwnerRealm::class.java) // 4. .findAll() // 5. .map < owner ->Owner( name = owner.name, image = owner.image, ) > ) > return owners >

Чтобы получить владельцев, вы:

  1. Получаете экземпляр Realm.
  2. Используете executeTransactionAwait() .
  3. Используйте where (OwnerRealm :: class.java) для получения объектов OwnerRealm .
  4. findAll() выполняет запрос.
  5. Сопоставляете OwnerRealm с объектами Owner .

Теперь соберите и запустите приложение. Вы увидите животных, которых вы добавили ранее. Если же вы перейдете к экрану Owners, то увидите добавленного вами владельца:

Отличная работа! Теперь вы можете видеть данные из базы данных. Однако, есть ошибка, и вы можете сделать пару улучшений.

Фильтрация Результатов

Ошибка в получении домашних животных заключается в том, что запрос возвращает всех домашних животных: которых уже забрали (Adopted Pets) и которые в процессе ожидания своих новых владельцев (Pets to Adopt). Но Realm предоставляет операторы фильтрации , которые помогают фильтровать результаты на основе определенных значений.

В PetDatabaseOperations.kt измените retrievePetsToAdopt() , добавив следующую строку после .where() :

.equalTo("isAdopted", false)

Это действие вернет только те экземпляры PetRealm , которые содержат isAdopted как false .

Теперь измените retrieveAdoptedPets() следующим образом:

suspend fun retrieveAdoptedPets(): List < val realm = Realm.getInstance(config) val adoptedPets = mutableListOf() realm.executeTransactionAwait(Dispatchers.IO) < realmTransaction ->adoptedPets.addAll(realmTransaction .where(PetRealm::class.java) .equalTo("isAdopted", true) .findAll() .map < mapPet(it) >) > return adoptedPets >

Чтобы получить уже отданных владельцам питомцев, isAdopted должно быть true .

Теперь вы можете реализовать фильтр по типу Pet в списке “Pets to Adopt”. Измените retrieveFilteredPets() следующим образом:

suspend fun retrieveFilteredPets(petType: String): List < val realm = Realm.getInstance(config) val filteredPets = mutableListOf() realm.executeTransactionAwait(Dispatchers.IO) < realmTransaction ->filteredPets.addAll(realmTransaction .where(PetRealm::class.java) // 1. .equalTo("isAdopted", false) // 2. .and() // 3. .beginsWith("petType", petType) .findAll() .map < mapPet(it) >) > return filteredPets >

Этот код выполняет транзакцию для получения объектов PetRealm . Фильтрация работает следующим образом:

  1. Условие для фильтрации объектов PetRealm , для которых isAdopted установлено как false .
  2. and() - логический оператор, указывающий, что результат должен соответствовать обоим условиям. Здесь вы можете найти список всех логических операторов, поддерживаемых Realm.
  3. Условие для фильтрации объектов PetRealm , у которых есть поле petType с указанным значением.

После выполнения запроса и сопоставления результатов с объектами Pet , метод возвращает список домашних животных выбранного petType.

Соберите и запустите приложение. Добавьте в список различные виды домашних животных, как показано на изображении ниже:

В селекторе вверху выберите тип питомца:

Отлично! Вы исправили ошибку и реализовали функцию фильтрации. Теперь вы можете добавить небольшое улучшение для списка владельцев.

Сортировка Результатов

Иногда вам требуется отсортировать результаты запросов до того, как они отобразятся на экране. Перейдите к OwnerDatabaseOperations.kt и добавьте следующую строку в запрос в retrieveOwners() после .findAll() :

.sort("name", Sort.ASCENDING)

Эта операция сортирует результаты по имени в возрастающем порядке.

Соберите и запустите приложение. Добавьте других владельцев в список. Вы увидите владельцев, отсортированных по именам, как на следующем изображении:

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

Выполнение Расчетов

Еще одно значение, которое вам нужно отобразить, - это количество домашних животных, которые есть у каждого владельца. У Realm есть разные агрегатные операторы . Данные операторы просматривают список объектов Realm и вычисляют значение.

Откройте OwnerDatabaseOperations.kt и добавьте следующий код:

private fun getPetCount(realm: Realm, ownerId: String): Long < // 1. return realm.where(PetRealm::class.java) // 2. .equalTo("owner.id", ownerId) // 3. .count() >
  1. Запрашиваете realm, чтобы получить объекты PetRealm .
  2. Используете owner.id для фильтрации по идентификатору владельца.
  3. Считаете количество питомцев у владельца с помощью .count()

Измените создание Owner в retrieveOwner() , добавив numberOfPets :

Owner( name = owner.name, image = owner.image, numberOfPets = getPetCount(realmTransaction, owner.id) )

Теперь у вас есть домашние животные, готовые обрести новый дом и сами владельцы. Пора позволить хозяевам взять себе домашних животных.

Добавление Отношений

Realm предоставляет способ реализации отношений «один к одному», «один ко многим» и обратных отношений.

Определение Отношений “Один к Одному”

Отношения «один к одному» - это когда объект связан не более чем с одним экземпляром другого объекта. В PetRealm это происходит с питомцем, у которого может быть не более одного владельца, как показано на следующей диаграмме:

Откройте PetRealm.kt и добавьте в конструктор класса следующий параметр:

var owner: OwnerRealm? = null

Эта строка сообщает объекту PetRealm , что у него может быть не более одного OwnerRealm . Но в этой базе данных есть еще одна взаимосвязь: один владелец может иметь несколько домашних животных.

Определение Отношений "Один ко Многим"

Отношение «один ко многим» - это когда один объект связан с несколькими объектами. Это сценарий, когда один владелец может иметь несколько домашних животных, как показано на следующей диаграмме:

Откройте OwnerRealm.kt и добавьте в конструктор класса следующий параметр:

var pets: RealmList = RealmList()

RealmList позволяет OwnerRealm иметь отношение «один ко многим» с PetRealm .

Отличная работа! Вы реализовали две взаимосвязи в PetRealm . Однако Realm предлагает третий тип отношений, который может облегчить вашу жизнь.

Использование Обратных Отношений

Реализованные вами отношения однонаправленные. Это означает, что когда вы получите результаты запроса для домашних животных, их владельцы не будут отображаться в результатах. Это небольшая проблема, потому что вы должны указать владельца питомца в списке “Adopted pets”.

Для решения этой проблемы Realm предоставляет обратные отношения. Перейдите на PetRealm.kt и замените параметр owner в конструкторе следующими строками:

@LinkingObjects("pets") // 1. val owner: RealmResults? = null // 2.

Импортируйте io.realm.annotations.LinkingObjects и io.realm.RealmResults .

Чтобы добавить обратную связь, вам необходимо:

  1. Добавить аннотацию @LinkingObjects , передав в качестве параметра имя поля, к которому вы добавляете связь. Поле в OwnerRealm , которое вы хотите связать, - это pets .
  2. Поле должно быть val и иметь тип RealmResults .

Теперь, благодаря этому улучшению, вы можете получать информацию об OwnerRealm в запросе PetRealm .

Пришло время собрать и запустить приложение. О, нет! Приложение вылетает. Взглянув на Logcat, вы обнаружите следующую ошибку:

Process: com.raywenderlich.android.petrealm, PID: 16049 io.realm.exceptions.RealmMigrationNeededException: Migration is required due to the following errors: - Property 'OwnerRealm.pets' has been added.

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

Перенос Базы Данных

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

Создайте новый файл в пакете realm и назовите его Migrations.kt. Добавьте следующий код:

// 1. val migration = RealmMigration < realm, oldVersion, newVersion ->// 2. if (oldVersion == 1L) < // 3. val ownerSchema = realm.schema.get("OwnerRealm") val petSchema = realm.schema.get("PetRealm") // 4. petSchema?.let < ownerSchema?.addRealmListField("pets", it) >> >

Чтобы добавить миграцию, вам необходимо:

  1. Создать val типа RealmMigration .
  2. Определите, что делать при каждом изменении версии. oldVersion будет содержать значение предыдущей версии схемы.
  3. Получите каждую схему, которую нужно изменить.
  4. ownerSchema требуется новое поле RealmList типа PetRealm . Используйте addRealmListField() с именем поля и типом схемы, который нужен полю.

Откройте PetsModule.kt в пакете di. Увеличьте версию схемы как здесь:

private val realmVersion = 2L

Измените provideRealmConfig() следующим образом:

RealmConfiguration.Builder() .schemaVersion(realmVersion) .migration(migration) .build()

Используйте migration() , чтобы добавить миграцию. Если вы соберете и запустите приложение, оно снова запустится. Все работает как и ожидалось. Пришло время для самого важного шага: забрать домой братьев наших меньших.

Обновление Объектов

Чтобы обновить объекты Realm, вам необходимо запросить объект, который вы хотите обновить, и присвоить новые значения.

Чтобы забрать питомца, isAdopted должно измениться на true , и он будет назначен владельцу. Откройте OwnerDatabaseOperations.kt и измените updatePets() следующим образом:

suspend fun updatePets(petId: String, ownerId: String) < val realm = Realm.getInstance(config) // 1. realm.executeTransactionAwait(Dispatchers.IO) < realmTransaction ->// 2. val pet = realmTransaction .where(PetRealm::class.java) .equalTo("id", petId) .findFirst() // 3. val owner = realmTransaction .where(OwnerRealm::class.java) .equalTo("id", ownerId) .findFirst() // 4. pet?.isAdopted = true // 5. owner?.pets?.add(pet) > >
  1. Добавляете транзакцию, чтобы запустить операцию записи.
  2. Делаете запрос на счастливого питомца.
  3. Делаете запрос хозяина, который заберет питомца.
  4. Обновите значение isAdopted .
  5. Добавляете счастливчика в список питомцев владельца.

Откройте PetDatabaseOperations.kt и измените mapPet() следующим образом:

private fun mapPet(pet: PetRealm): Pet

Вы можете воспользоваться преимуществом ранее использованных отношений и добавить имя владельца к каждому принятому питомцу.

Соберите и запустите приложение. Нажмите Adopt Me (Прими меня) у любого питомца и выберите хозяина. Позвольте одному владельцу забрать несколько питомцев. Перейдите к экрану Adopted Pets (Принято питомцев), и вы увидите этих счастливчиков:

Откройте экран Owners. Вы можете увидеть, сколько каждый владелец забрал животных.

Прекрасная работа! Вы помогли домашним животным найти хозяина. Однако бывают случаи, когда домашнее животное убегает или хозяин больше не хочет держать животное. В этом случае у вас должна быть возможность удалить их из приложения.

Удаление Объектов

Откройте PetDatabaseOperations.kt и измените removePet() следующим образом:

suspend fun removePet(petId: String) < val realm = Realm.getInstance(config) realm.executeTransactionAwait(Dispatchers.IO) < realmTransaction ->// 1. val petToRemove = realmTransaction .where(PetRealm::class.java) .equalTo("id", petId) .findFirst() // 2. petToRemove?.deleteFromRealm() > >

Чтобы удалить объект, вам необходимо:

  1. Запросите объект, который вы хотите удалить. Этот запрос должен быть в транзакции.
  2. Используйте deleteFromRealm() в этом объекте, чтобы удалить его из базы данных.

Для удаления взаимосвязанных объектов требуется дополнительный шаг. Откройте OwnerDatabaseOperations.kt и обновите removeOwner() следующим образом:

suspend fun removeOwner(ownerId: String) < val realm = Realm.getInstance(config) realm.executeTransactionAwait(Dispatchers.IO) < realmTransaction ->// 1. val ownerToRemove = realmTransaction .where(OwnerRealm::class.java) .equalTo("id", ownerId) .findFirst() // 2. ownerToRemove?.pets?.deleteAllFromRealm() // 3. ownerToRemove?.deleteFromRealm() > >
  1. Запросить владельца, которого вы хотите удалить.
  2. Использовать deleteAllFromRealm() , чтобы удалить всех домашних животных, которые есть у владельца.
  3. Наконец, удалите объект Владелец.

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

Теперь PetRealm готов. Однако, каким образом можно выполнить отладку в базе данных Realm?

Использование Realm Studio

Realm Studio - это визуальный инструмент, который помогает просматривать, редактировать и создавать базы данных Realm. Зайдите на сайт Realm Studio , скачайте и установите версию 11.1.0.

В Android Studio перейдите в меню Вид Инструменты Windows Проводник Устройства. В раскрывшемся списке выберите эмулятор, который вы используете. Перейдите к файлам data data com.raywenderlich.android.petrealm files. Здесь вы можете найти базу данных с именем default.realm, как показано на следующем изображении:

Щелкните правой кнопкой мыши имя файла Realm и выберите Сохранить как. Выберите место на вашем компьютере и сохраните файл. Теперь откройте Realm Studio и нажмите Open Realm file. Перейдите к сохраненному файлу и выберите его. Первой вы увидите схему OwnerRealm:

В нем вы можете увидеть текущие значения ID, имени, изображения и домашних животных. Как видите, значение pets - это список идентификаторов питомцев.

Выберите PetRealm на левой боковой панели, и вы увидите следующее:

Нажмите Создать PetRealm в правом верхнем углу и добавьте следующего питомца:

Нажмите Создать, и вы увидите, что новое домашнее животное добавлено в базу данных.

Другие операции, которые вы можете выполнять в Realm Studio:

  • Создание новых классов
  • Добавление свойств к существующим классам
  • Запрос классов

Если вы хотите узнать больше о Realm Studio, посмотрите официальную документацию .

Теперь, когда вы узнали о Realm, пора поговорить о Room, одной из самых популярных библиотек хранения в Android. В чем разница между Room и Realm?

Сравнение Realm с Room

Jetpack предоставляет Room, библиотеку хранения данных, которая негласно использует SQLite.

В Room можно хранить только примитивные типы и строки. Вы можете использовать type converters для преобразования объекта в примитивные типы и сохранения его в базе данных. Например, если вам нужно сохранить Date , сначала вы должны преобразовать ее в Long .

Room не позволяет объектам сущностей ссылаться друг на друга. Это означает, что при работе с отношениями необходимо создавать промежуточные классы для моделирования этих отношений между сущностями. Здесь вы можете найти дополнительную информацию об отношениях в Room.

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

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

В Realm есть и другие расширенные функции , такие как:

  • Создание приложений Realm для синхронизации данных в реальном времени между разными платформами.
  • Использование служб приложений для проверки подлинности и управления разрешениями.
  • Безупречная работа с MongoDB.

База данных Realm позволяет хранить объекты и получать к ним доступ локально. Вы можете вставлять данные, запрашивать, фильтровать и сортировать результаты, обновлять и удалять объекты. Она также обеспечивает простой способ создания отношений между сущностями.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *