Realm — лучшее решение для баз данных Android
Опубликовано: 2022-03-11С момента создания Android мы, разработчики приложений, использовали SQLite для хранения наших локальных данных. Иногда напрямую с операторами SQL, иногда с использованием Object-Relational Mapper (ORM) в качестве уровня абстракции, но в любом случае мы использовали SQLite в конце дня.
Однако, несмотря на все преимущества SQLite, были моменты, когда нам хотелось иметь альтернативы реляционной модели: что-то, что избавило бы нас от необходимости добавлять шаблонный код для преобразования значений в базу данных и из базы данных или позволило бы нам пропустить настройку сопоставлений. между классами и таблицами, полями и столбцами, внешними ключами и т. д.
Другими словами, база данных со структурами данных, более похожими на те, которые мы фактически используем на уровне приложения. А еще лучше, если бы он мог эффективно использовать память по дизайну, позволяя улучшить работу на устройствах с ограниченными ресурсами, это было бы здорово.
Это, по сути, некоторые из готовых преимуществ, которые мы получаем с Realm, платформой базы данных с отличной архитектурой, которая появилась как новая альтернатива SQLite.
В этой статье представлены некоторые из основных причин, по которым Realm привлекла такое внимание, и почему вы, возможно, захотите попробовать его. В нем обсуждаются некоторые ключевые преимущества, которые Realm предоставляет разработчикам Android по сравнению с SQLite.
Поскольку Realm доступен на нескольких платформах, то, что будет рассмотрено в этой статье, имеет отношение и к другим мобильным платформам, таким как iOS, Xamarin и React Native.
SQLite: работает, но в большинстве случаев это не то, что вам нужно
Большинство мобильных разработчиков, вероятно, знакомы с SQLite. Он существует с 2000 года и, возможно, является наиболее часто используемым движком реляционной базы данных в мире.
SQLite имеет ряд преимуществ, которые мы все признаем, одним из которых является встроенная поддержка Android.
Тот факт, что это стандартная реляционная база данных SQL, также сводит к минимуму кривую обучения для тех, кто имеет опыт работы с реляционными базами данных. Он также обеспечивает достаточно хорошую производительность, если использовать весь свой потенциал (используя такие функции, как подготовленные операторы, массовые операции с транзакциями и т. д.). Хотя SQLite может не очень хорошо масштабироваться для всех ваших нужд.
Однако работа непосредственно с операторами SQL имеет ряд недостатков.
Согласно официальной документации Android, вот шаги, необходимые для начала чтения/записи в SQLite:
- Опишите свою схему с точки зрения классов контрактов.
- Определите команды создания/удаления таблицы в строках.
- Расширение
SQLiteOpenHelper
для выполнения команд создания и управления обновлениями/откатами.
Как только вы это сделаете, вы будете готовы к чтению и записи в вашу базу данных. Однако вам нужно будет конвертировать туда и обратно между объектами в вашем приложении и значениями в базе данных. Короче говоря: это много стандартного кода!
Другой вопрос — ремонтопригодность. По мере того, как ваш проект становится больше и возникает необходимость писать более сложные запросы, вы получите большие куски необработанных SQL-запросов в строках. Если позже вам понадобится изменить логику этих запросов, это может быть довольно хлопотно.
Несмотря на свои недостатки, есть случаи, когда использование необработанного SQL является лучшим вариантом. Одним из примеров является разработка библиотеки, где производительность и размер являются критическими факторами, и по возможности следует избегать добавления сторонней библиотеки.
Объектно-реляционный картограф: средство для решения задач SQL
Чтобы избавить нас от работы с необработанным SQL, на помощь пришли ORM.
Одними из самых известных ORM для Android являются DBFlow, greenDAO и OrmLite.
Наибольшая ценность, которую они приносят, — это абстракция SQLite, позволяющая нам относительно легко сопоставлять объекты базы данных с объектами Java.
Среди других преимуществ разработчики приложений получают возможность работать с объектами, гораздо более привычной структурой данных. Это также помогает с удобством сопровождения, поскольку теперь мы обрабатываем высокоуровневые объекты с более строгой типизацией и оставляем грязную работу библиотекам. Меньше проблем с созданием запросов путем объединения строк или ручной обработки соединения с базой данных. Меньше опечаток.
Хотя это факт, что эти ORM подняли планку для баз данных Android, у них также есть свои недостатки. Во многих случаях вы загружаете ненужные данные.
Вот пример.
Допустим, у вас есть таблица с 15 столбцами, и на определенном экране вашего приложения отображается список объектов из этой таблицы. В этом списке отображаются значения только из трех столбцов. Таким образом, загрузив все данные из строки таблицы, вы получите в пять раз больше данных, чем вам действительно нужно для этого экрана.
По правде говоря, в некоторых из этих библиотек вы можете указать, какие столбцы вы хотите получить заранее, но для этого вам нужно добавить дополнительный код, и даже в этом случае этого будет недостаточно, если вы можете только точно знать, какие столбцы вы хотите получить. использовать после просмотра самих данных: некоторые данные все равно могут быть загружены без необходимости.
Кроме того, часто бывают ситуации, когда вам нужно делать сложные запросы, а ваша библиотека ORM просто не предлагает способа описать эти запросы с помощью своего API. Например, это может заставить вас писать неэффективные запросы, которые выполняют больше вычислений, чем вам нужно.
Следствием этого является потеря производительности, заставляющая вас прибегать к необработанному SQL. Хотя это не является препятствием для многих из нас, это вредит основной цели объектно-реляционного сопоставления и возвращает нас к некоторым из вышеупомянутых проблем, связанных с SQLite.
Царство: идеальная альтернатива
Realm Mobile Database — это база данных, разработанная для мобильных устройств с нуля.
Ключевое различие между Realm и ORM заключается в том, что Realm — это не абстракция, построенная поверх SQLite, а совершенно новый механизм базы данных. Вместо реляционной модели она основана на хранилище объектов. Его ядро состоит из автономной библиотеки C++. В настоящее время он поддерживает Android, iOS (Objective-C и Swift), Xamarin и React Native.
Realm был запущен в июне 2014 года, так что на данный момент ему два с половиной года (довольно новый!).
В то время как технологии серверных баз данных пережили революцию с 2007 года, появилось много новых, технология баз данных для мобильных устройств осталась привязанной к SQLite и его оболочкам. Это было одним из ключевых мотивов создать что-то с нуля. Кроме того, как мы увидим, некоторые функции Realm требовали фундаментальных изменений в поведении базы данных на низком уровне, а построить что-то поверх SQLite было просто невозможно.
Но действительно ли Realm того стоит? Вот основные причины, по которым вам следует подумать о добавлении Realm в свой набор инструментов.
Простое моделирование
Вот пример некоторых моделей, созданных с помощью Realm:
public class Contact extends RealmObject { @PrimaryKey String id; protected String name; String email; @Ignore public int sessionId; //Relationships private Address address; private RealmList<Contact> friends; //getters & setter left out for brevity }
public class Address extends RealmObject { @PrimaryKey public Long id; public String name; public String address; public String city; public String state; public long phone; }
Ваши модели расширяются от RealmObject. Realm принимает все примитивные типы и их упакованные типы (кроме char
), String
, Date
и byte[]
. Он также поддерживает подклассы RealmObject
и RealmList<? extends RealmObject>
RealmList<? extends RealmObject>
для моделирования отношений.
Поля могут иметь любой уровень доступа (частный, общедоступный, защищенный и т.д.). Все поля сохраняются по умолчанию, и вам просто нужно аннотировать «специальные» поля (например, @PrimaryKey
для вашего поля первичного ключа, @Ignore
для установки несохраняемых полей и т. д.).
Интересная вещь в этом подходе заключается в том, что он сохраняет классы менее «загрязненными аннотациями» по сравнению с ORM, так как в большинстве из них вам нужны аннотации для сопоставления классов с таблицами, обычных полей со столбцами базы данных, полей внешнего ключа с другими таблицами и так далее. на.
Отношения
Когда дело доходит до отношений, есть два варианта:
Добавьте модель как поле из другой модели. В нашем примере класс
Contact
содержит полеAddress
, которое определяет их отношения. У контакта может быть адрес, но ничто не мешает добавить этот же адрес к другим контактам. Это позволяет устанавливать отношения «один к одному» и «один ко многим».Добавьте
RealmList
моделей, на которые ссылаются.RealmLists
ведут себя как старые добрыеLists
Java, действуя как контейнер объектов Realm. Мы видим, что наша модельContact
имеетRealmList
контактов, которые в этом примере являются ее друзьями. С помощью этого подхода можно моделировать отношения «один ко многим» и «многие ко многим».
Мне нравится этот способ представления отношений, потому что он кажется нам, разработчикам Java, очень естественным. Добавляя эти объекты (или списки этих объектов) непосредственно в качестве полей нашего класса, точно так же, как мы делали бы это для других классов, не являющихся моделями, нам не нужно иметь дело с настройками SQLite для внешних ключей.
Предостережение: Наследование модели не поддерживается. Текущий обходной путь заключается в использовании композиции. Так что, если, например, у вас есть модель Animal
и вы надеялись создать модель Dog
, расширяющуюся от Animal
, вместо этого вам придется добавить экземпляр Animal
в качестве поля в Dog
. Существует большая дискуссия о композиции и наследовании. Если вы используете наследование, это определенно то, что вам нужно знать о Realm. В SQLite это можно реализовать с помощью двух таблиц (одна для родительской и одна для дочерней), связанных внешним ключом. Некоторые ORM также не накладывают это ограничение, например DBFlow.
Получайте только те данные, которые вам нужны! Дизайн с нулевым копированием
Это убийственная функция.
Realm применяет концепцию нулевого копирования, что означает, что данные никогда не копируются в память. Результаты, которые вы получаете от запроса, на самом деле являются просто указателями на реальные данные. Сами данные лениво загружаются при доступе к ним.
Например, у вас есть модель с 10 полями (столбцами в SQL). Если вы запрашиваете объекты этой модели, чтобы отобразить их в списке на экране, и вам нужны только три из 10 полей для заполнения элементов списка, это будут единственные поля, которые будут извлечены.
Как следствие, запросы невероятно быстры (см. здесь и здесь некоторые результаты тестов).
Это большое преимущество по сравнению с ORM, которые обычно загружают все данные из выбранных строк SQL заранее.
В результате загрузка экрана становится намного более эффективной, не требуя дополнительных усилий со стороны разработчика: это просто поведение Realm по умолчанию.
Кроме того, это также означает, что приложения потребляют меньше памяти, и, учитывая, что мы говорим о среде с ограниченными ресурсами, такой как мобильные устройства, это может иметь большое значение.
Еще одним следствием подхода с нулевым копированием является то, что объекты, управляемые Realm, автоматически обновляются.
Данные никогда не копируются в память. Если у вас есть результаты запроса, и другой поток обновил эти данные в базе данных после вашего запроса, полученные вами результаты уже будут отражать эти изменения. Ваши результаты являются лишь указателями на фактические данные. Таким образом, когда вы получаете доступ к значениям из полей, возвращаются самые последние данные.
Если вы уже прочитали данные из объектов Realm и отобразили их, например, на экране и хотите получать обновления при изменении базовых данных, вы можете добавить прослушиватель:
final RealmResults<Contact> johns = realm.where(Contact.class).beginsWith("name", "John ").findAll(); johns.addChangeListener(new RealmChangeListener<RealmResults<Contact>>() { @Override public void onChange(RealmResults<Contact> results) { // UPDATE UI } });
Это не просто обертка
Хотя у нас есть десятки вариантов ORM, они являются обертками, и все сводится к SQLite внутри, который ограничивает их возможности. Напротив, Realm — это не просто еще одна оболочка SQLite. У него есть свобода предоставления функций, которые ORM просто не могут предложить.
Одним из фундаментальных изменений в Realm является возможность хранить данные в виде хранилища графа объектов.
Это означает, что Realm является объектами на всем пути вниз, от уровня языка программирования до базы данных. Следовательно, при записи и чтении значений выполняется гораздо меньше преобразований по сравнению с реляционной базой данных.
Структуры базы данных более точно отражают структуры данных, используемые разработчиками приложений. На самом деле, это одна из основных причин перехода от реляционного моделирования к агрегатным моделям при разработке на стороне сервера. Realm, наконец, привносит некоторые из этих идей в мир мобильных разработок.
Если мы думаем о компонентах в архитектуре Realm, внизу находится ее ядро с наиболее фундаментальной реализацией платформы. Кроме того, у нас будут библиотеки привязки к каждой поддерживаемой платформе.
При использовании оболочки для какой-либо технологии, которую вы не можете контролировать, вам в конечном итоге необходимо обеспечить некий уровень абстракции вокруг нее.
Библиотеки привязки Realm спроектированы так, чтобы быть как можно более тонкими, чтобы исключить сложность абстракции. В основном они пропагандируют дизайнерскую идею Core. Имея контроль над всей архитектурой, эти компоненты лучше синхронизируются друг с другом.
Одним из практических примеров является доступ к другим объектам, на которые есть ссылки (внешние ключи в SQL). Файловая структура Realm основана на собственных ссылках, поэтому, когда вы запрашиваете связи, вместо того, чтобы преобразовывать абстракцию ORM в реляционную и/или объединять несколько таблиц, вы получаете необработанные ссылки на объекты на уровне файловой системы в формате файла.
Это объекты, указывающие непосредственно на другие объекты. Таким образом, запрос отношения аналогичен, например, запросу целочисленного столбца. Нет необходимости в дорогостоящих операциях по обходу внешних ключей. Все дело в следовании указателям.
Сообщество и поддержка
Realm находится в активной разработке и довольно часто выпускает обновленные версии.
Все компоненты базы данных Realm Mobile имеют открытый исходный код. Они очень отзывчивы в своем трекере проблем и Stack Overflow, поэтому вы можете рассчитывать на хорошую и быструю поддержку по этим каналам.
Кроме того, отзывы сообщества учитываются при определении приоритетности проблем (ошибки, улучшения, запросы функций и т. д.). Всегда приятно знать, что вы можете внести свой вклад в разработку инструментов, которые используете.
Я начал использовать Realm в 2015 году, и с тех пор я наткнулся на несколько сообщений в Интернете с различными мнениями о Realm. Мы скоро поговорим о его ограничениях, но я заметил одну вещь: многие жалобы, сделанные во время публикации, с тех пор были исправлены.
Когда я узнал о Realm, например, еще не было поддержки пользовательских методов в моделях и асинхронных вызовов. В то время это было препятствием для многих, но в настоящее время поддерживаются оба.
Такая скорость разработки и отзывчивость вселяют в нас уверенность в том, что мы не будем долго ждать важных функций.
Ограничения
Как и все в жизни, Царство не сплошь розы. Помимо ранее упомянутого ограничения наследования, есть и другие недостатки, о которых следует помнить:
Хотя возможно одновременное чтение и запись в базу данных несколькими потоками, объекты Realm нельзя перемещать между потоками . Поэтому, если, например, вы извлекаете объект области с помощью
doInBackground()
, который выполняется в фоновом потоке, вы не можете передать этот экземплярonPostExecute()
, поскольку они выполняются в основном потоке. Возможные обходные пути для этой ситуации могут состоять в том, чтобы либо сделать копию объекта и передать ее, либо передать идентификатор объекта и снова получить объект вonPostExecute()
. Realm предлагает синхронные и асинхронные методы чтения/записи.Автоинкрементные первичные ключи не поддерживаются , поэтому вам придется самостоятельно их генерировать.
Невозможно получить доступ к базе данных из разных процессов одновременно . Согласно их документации, скоро появится поддержка нескольких процессов.
Realm — будущее решений для мобильных баз данных
SQLite — это надежный, надежный и проверенный механизм базы данных, а реляционные базы данных не исчезнут в ближайшее время. Существует ряд хороших ORM, которые также подойдут для многих сценариев.
Однако важно быть в курсе современных тенденций.
В связи с этим я думаю, что Realm — одна из самых больших тенденций последних лет, когда речь идет о разработке мобильных баз данных.
Realm предлагает уникальный подход к работе с ценными для разработчиков данными не только потому, что он может быть лучшим вариантом, чем существующие решения, но и потому, что он расширяет наши горизонты с точки зрения новых возможностей и поднимает планку технологии мобильных баз данных.
У вас уже есть опыт работы с Realm? Пожалуйста, не стесняйтесь делиться своими мыслями.