- Урок 5. Room. Основы
- Подключение к проекту
- Entity
- Database
- Практика
- UI поток
- Переход на Room
- Basic Implementation of Room Database With Repository and ViewModel | Android Jetpack
- Advantages
- Room has three main components:
- 1. Entity
- 2. DAO
- 3. Database
- Implementation of RoomDB
- 7 шагов к использованию Room. Пошаговое руководство по миграции приложения на Room
- Шаг 1. Обновление зависимостей gradle
- Шаг 2. Обновление классов модели до сущностей
- Шаг 3. Создание объектов доступа к данным (DAO)
- Шаг 4. Создание базы данных
- Шаг 5. Обновление репозитория для использования Room
- Шаг 6. Тестирование на устройстве
- Тестирование UserDao
- Тестирование использования UserDao в LocalUserDataSource
- Тестирование миграции базы данных
- Шаг 7. Удаление всего ненужного
Урок 5. Room. Основы
Библиотека Room предоставляет нам удобную обертку для работы с базой данных SQLite. В этом уроке рассмотрим основы. Как подключить к проекту. Как получать, вставлять, обновлять и удалять данные.
Полный список уроков курса:
Подключение к проекту
В build.gradle файл проекта добавьте репозитарий google()
В build.gradle файле модуля добавьте dependencies:
Если у вас студия ниже 3.0 и старые версии Gradle и Android Plugin, то подключение будет выглядеть так:
Room имеет три основных компонента: Entity, Dao и Database. Рассмотрим их на небольшом примере, в котором будем создавать базу данных для хранения данных по сотрудникам (англ. — employee).
При работе с Room нам необходимо будет писать SQL запросы. Если вы не знакомы с ними, то имеет смысл прочесть хотя бы основы.
Entity
Аннотацией Entity нам необходимо пометить объект, который мы хотим хранить в базе данных. Для этого создаем класс Employee, который будет представлять собой данные сотрудника: id, имя, зарплата:
Класс помечается аннотацией Entity. Объекты класса Employee будут использоваться при работе с базой данных. Например, мы будем получать их от базы при запросах данных и отправлять их в базу при вставке данных.
Этот же класс Employee будет использован для создания таблицы в базе. В качестве имени таблицы будет использовано имя класса. А поля таблицы будут созданы в соответствии с полями класса.
Аннотацией PrimaryKey мы помечаем поле, которое будет ключом в таблице.
В следующих уроках мы рассмотрим возможности Entity более подробно.
В объекте Dao мы будем описывать методы для работы с базой данных. Нам нужны будут методы для получения списка сотрудников и для добавления/изменения/удаления сотрудников.
Описываем их в интерфейсе с аннотацией Dao.
Методы getAll и getById позволяют получить полный список сотрудников или конкретного сотрудника по id. В аннотации Query нам необходимо прописать соответствующие SQL-запросы, которые будут использованы для получения данных.
Обратите внимание, что в качестве имени таблицы мы используем employee. Напомню, что имя таблицы равно имени Entity класса, т.е. Employee, но в SQLite не важен регистр в именах таблиц, поэтому можем писать employee.
Для вставки/обновления/удаления используются методы insert/update/delete с соответствующими аннотациями. Тут никакие запросы указывать не нужно. Названия методов могут быть любыми. Главное — аннотации.
В следующих уроках мы рассмотрим возможности Dao и его аннотаций более подробно.
Database
Аннотацией Database помечаем основной класс по работе с базой данных. Этот класс должен быть абстрактным и наследовать RoomDatabase.
В параметрах аннотации Database указываем, какие Entity будут использоваться, и версию базы. Для каждого Entity класса из списка entities будет создана таблица.
В Database классе необходимо описать абстрактные методы для получения Dao объектов, которые вам понадобятся.
Практика
Все необходимые для работы объекты созданы. Давайте посмотрим, как использовать их для работы с базой данных.
Database объект — это стартовая точка. Его создание выглядит так:
Используем Application Context, а также указываем AppDatabase класс и имя файла для базы.
Учитывайте, что при вызове этого кода Room каждый раз будет создавать новый экземпляр AppDatabase. Эти экземпляры очень тяжелые и рекомендуется использовать один экземпляр для всех ваших операций. Поэтому вам необходимо позаботиться о синглтоне для этого объекта. Это можно сделать с помощью Dagger, например.
Если вы не используете Dagger (или другой DI механизм), то можно использовать Application класс для создания и хранения AppDatabase:
Не забудьте добавить App класс в манифест
В коде получение базы будет выглядеть так:
Из Database объекта получаем Dao.
Теперь мы можем работать с Employee объектами. Но эти операции должны выполняться не в UI потоке. Иначе мы получим Exception.
Добавление нового сотрудника в базу будет выглядеть так:
Метод getAll вернет нам всех сотрудников в List
Получение сотрудника по id:
Обновление данных по сотруднику.
Room будет искать в таблице запись по ключевому полю, т.е. по id. Если в объекте employee не заполнено поле id, то по умолчанию в нашем примере оно будет равно нулю и Room просто не найдет такого сотрудника (если, конечно, у вас нет записи с >
Аналогично обновлению, Room будет искать запись по ключевому полю, т.е. по id
Давайте для примера добавим еще один тип объекта — Car.
Описываем Entity объект
Теперь Dao для работы с Car объектом
Будем считать, что нам надо только читать все записи, добавлять новые и удалять старые.
В Database необходимо добавить Car в список entities и новый метод для получения CarDao
Т.к. мы добавили новую таблицу, изменилась структура базы данных. И нам необходимо поднять версию базы данных до 2. Но об этом мы подробно поговорим в Уроке 12. А пока можно оставить версию равной 1, удалить старую версию приложения и поставить новую.
UI поток
Повторюсь, операции по работе с базой данных — синхронные, и должны выполняться не в UI потоке.
В случае с Query операциями мы можем сделать их асинхронными используя LiveData или RxJava. Об этом еще поговорим в следующих уроках.
В случае insert/update/delete вы можете обернуть эти методы в асинхронный RxJava. В моем блоге есть статья на эту тему.
Также, вы можете использовать allowMainThreadQueries в билдере создания AppDatabase
В этом случае вы не будете получать Exception при работе в UI потоке. Но вы должны понимать, что это плохая практика, и может добавить ощутимых тормозов вашему приложению.
Переход на Room
Если вы надумали с SQLite мигрировать на Room, то вот пара полезных ссылок по этой теме:
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Basic Implementation of Room Database With Repository and ViewModel | Android Jetpack
In this article, I will explain the Room Persistence Library, and we will try to implement our first database.
Android Jetpack is a collection of components for Android development. Room database is a component for databases. Let me explain the Room in detail. We will be using Kotlin and Android Studio.
The Room persistence library provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite.
Room database allows you to create a data cache to keep the application’s data. The cache serves as a single source of data and allows you to keep consistent data to use. That component works regardless of whether users have an internet connection.
Advantages
- Compile-time verification of SQL queries
- Less boilerplate code
- Easily integrated with other architecture components
Room has three main components:
1. Entity
Represents a table within the database. Room creates a table for each class that has @Entity an annotation, and the fields in the class correspond to columns in the table. Therefore, the entity classes are small model classes that don’t contain any logic.
2. DAO
DAO’s are responsible for defining the methods that access the database. DAO’s are places where we add our queries.
3. Database
Contains the database and serves as the main access point for the underlying connection to your app’s data.
Implementation of RoomDB
Adding coroutines, lifecycle, and roomdb dependencies to build.gradle .
Creating a Model Class
- Annotate the class with @Entity and use the tableName property to set the name of the table.
- Set the primary key by adding the @PrimaryKey annotation to the correct fields.
- Set the name of the columns for the class fields using the @ColumnInfo(name = “column_name”) annotation. If you have the correct column name, you can skip this annotation.
Creating DAO’s (Data Access Objects)
- DAO’s needs to be created with annotation @Dao and an interface.
Creating a Database
- We need to create an abstract class that extends the Room Database.
- A repository class abstract access to multiple data sources. The repository is not part of the Architecture Component libraries but is a suggested best practice for code separation and architecture.
Creating a View Model
- ViewModel is like a bridge between UI and repository. The role of the VM is to provide data to UI.
After creating these classes, you can use the View Model in your activity and do several operations. Also, you can add different operations to View Model for using it as the update and delete as we coded in the repository above.
I hope this article helps you to understand Room DB and it’s implementation. See you in another article. Bye!
Источник
7 шагов к использованию Room. Пошаговое руководство по миграции приложения на Room
Room — это библиотека, которая является частью архитектурных компонентов Android. Она облегчает работу с объектами SQLiteDatabase в приложении, уменьшая объём стандартного кода и проверяя SQL-запросы во время компиляции.
У вас уже есть Android-проект, который использует SQLite для хранения данных? Если это так, то вы можете мигрировать его на Room. Давайте посмотрим, как взять уже существующий проект и отрефакторить его для использования Room за 7 простых шагов.
TL;DR: обновите зависимости gradle, создайте свои сущности, DAO и базу данных, замените вызовы SQLiteDatabase вызовами методов DAO, протестируйте всё, что вы создали или изменили, и удалите неиспользуемые классы. Вот и всё!
В нашем примере приложения для миграции мы работаем с объектами типа User . Мы использовали product flavors для демонстрации различных реализаций уровня данных:
- sqlite — использует SQLiteOpenHelper и традиционные интерфейсы SQLite.
- room — заменяет реализацию на Room и обеспечивает миграцию.
Каждый вариант использует один и тот же слой пользовательского интерфейса, который работает с классом UserRepository благодаря паттерну MVP.
В варианте sqlite вы увидите много кода, который часто дублируется и использует базу данных в классах UsersDbHelper и LocalUserDataSource . Запросы строятся с помощью ContentValues , а данные, возвращаемые объектами Cursor , читаются столбец за столбцом. Весь этот код способствует появлению неявных ошибок. Например, можно пропустить добавление столбца в запрос или неправильно собрать объект из базы данных.
Давайте посмотрим, как Room улучшит наш код. Изначально мы просто копируем классы из варианта sqlite и постепенно будем изменять их.
Шаг 1. Обновление зависимостей gradle
Зависимости для Room доступны через новый Google Maven-репозиторий. Просто добавьте его в список репозиториев в вашем основном файле build.gradle :
Определите версию библиотеки Room в том же файле. Пока она находится в альфа-версии, но следите за обновлениями версий на страницах для разработчиков:
В вашем файле app/build.gradle добавьте зависимости для Room:
Чтобы мигрировать на Room, нам нужно увеличить версию базы данных, а для сохранения пользовательских данных нам потребуется реализовать класс Migration. Чтобы протестировать миграцию, нам нужно экспортировать схему. Для этого добавьте следующий код в файл app/build.gradle :
Шаг 2. Обновление классов модели до сущностей
Room создаёт таблицу для каждого класса, помеченного @Entity. Поля в классе соответствуют столбцам в таблице. Следовательно, классы сущностей, как правило, представляют собой небольшие классы моделей, которые не содержат никакой логики. Наш класс User представляет модель для данных в базе данных. Итак, давайте обновим его, чтобы сообщить Room, что он должен создать таблицу на основе этого класса:
- Аннотируйте класс с помощью @Entity и используйте свойство tableName , чтобы задать имя таблицы.
- Задайте первичный ключ, добавив аннотацию @PrimaryKey в правильные поля — в нашем случае это идентификатор пользователя.
- Задайте имя столбцов для полей класса, используя аннотацию @ColumnInfo(name = «column_name») . Этот шаг можно пропустить, если ваши поля уже названы так, как следует назвать столбец.
- Если в классе несколько конструкторов, добавьте аннотацию @Ignore , чтобы указать Room, какой следует использовать, а какой — нет.
Примечание: для плавной миграции обратите пристальное внимание на имена таблиц и столбцов в исходной реализации и убедитесь, что вы правильно устанавливаете их в аннотациях @Entity и @ColumnInfo .
Шаг 3. Создание объектов доступа к данным (DAO)
DAO отвечают за определение методов доступа к базе данных. В первоначальной реализации нашего проекта на SQLite все запросы к базе данных выполнялись в классе LocalUserDataSource , где мы работали с объектами Cursor . В Room нам не нужен весь код, связанный с курсором, и мы можем просто определять наши запросы, используя аннотации в классе UserDao .
Например, при запросе всех пользователей из базы данных Room выполняет всю «тяжелую работу», и нам нужно только написать:
Шаг 4. Создание базы данных
Мы уже определили нашу таблицу Users и соответствующие ей запросы, но мы ещё не создали базу данных, которая объединит все эти составляющие Room. Для этого нам нужно определить абстрактный класс, который расширяет RoomDatabase . Этот класс помечен @Database , в нём перечислены объекты, содержащиеся в базе данных, и DAO, которые обращаются к ним. Версия базы данных должна быть увеличена на 1 в сравнении с первоначальным значением, поэтому в нашем случае это будет 2.
Поскольку мы хотим сохранить пользовательские данные, нам нужно реализовать класс Migration , сообщающий Room, что он должен делать при переходе с версии 1 на 2. В нашем случае, поскольку схема базы данных не изменилась, мы просто предоставим пустую реализацию:
Создайте объект базы данных в классе UsersDatabase , определив имя базы данных и миграцию:
Чтобы узнать больше о том, как реализовать миграцию баз данных и как они работают под капотом, посмотрите этот пост.
Шаг 5. Обновление репозитория для использования Room
Мы создали нашу базу данных, нашу таблицу пользователей и запросы, так что теперь пришло время их использовать. На этом этапе мы обновим класс LocalUserDataSource для использования методов UserDao . Для этого мы сначала обновим конструктор: удалим Context и добавим UserDao . Конечно, любой класс, который создаёт экземпляр LocalUserDataSource , также должен быть обновлен.
Далее мы обновим методы LocalUserDataSource , которые делают запросы с помощью вызова методов UserDao . Например, метод, который запрашивает всех пользователей, теперь выглядит так:
А теперь время запустить то, что у нас получилось.
Одна из лучших функций Room — это то, что если вы выполняете операции с базой данных в главном потоке, то ваше приложение упадёт со следующим сообщением об ошибке:
Один надёжный способ переместить операции ввода-вывода из основного потока — это создать новый Runnable , который будет создавать новый поток для каждого запроса к базе данных. Поскольку мы уже используем этот подход в варианте sqlite, никаких изменений не потребовалось.
Шаг 6. Тестирование на устройстве
Мы создали новые классы — UserDao и UsersDatabase и изменили наш LocalUserDataSource для использования базы данных Room. Теперь нам нужно их протестировать.
Тестирование UserDao
Чтобы протестировать UserDao , нам нужно создать тестовый класс AndroidJUnit4 . Потрясающая особенность Room — это возможность создавать базу данных в памяти. Это исключает необходимость очистки после каждого теста.
Нам также нужно убедиться, что мы закрываем соединение с базой данных после каждого теста.
Например, чтобы протестировать вход пользователя, мы добавим пользователя, а затем проверим, сможем ли мы получить этого пользователя из базы данных.
Тестирование использования UserDao в LocalUserDataSource
Убедиться, что LocalUserDataSource по-прежнему работает правильно, легко, поскольку у нас уже есть тесты, которые описывают поведение этого класса. Всё, что нам нужно сделать, это создать базу данных в памяти, получить из нее объект UserDao и использовать его в качестве параметра для конструктора LocalUserDataSource .
Опять же, нам нужно убедиться, что мы закрываем базу данных после каждого теста.
Тестирование миграции базы данных
Подробнее почитать о том, как реализовать тесты миграции баз данных, а также, как работает MigrationTestHelper , можно в этом посте.
Вы также можете посмотреть код из более детального примера приложения миграции.
Шаг 7. Удаление всего ненужного
Удалите все неиспользуемые классы и строки кода, которые теперь заменены функциональностью Room. В нашем проекте нам просто нужно удалить класс UsersDbHelper , который расширял класс SQLiteOpenHelper .
Если у вас есть большая и более сложная база данных, и вы хотите постепенно перейти на Room, то рекомендуем этот пост.
Теперь количество стандартного кода, подверженного ошибкам, уменьшилось, запросы проверяются во время компиляции, и всё тестируется. За 7 простых шагов мы смогли мигрировать наше существующее приложение на Room. Пример приложения можете посмотреть здесь.
Источник