Room — Kotlin, Android Architecture Components
When we talk about storing information in a persistent way on Android, the most important and “easy” option is to use plain SQLite, if you do a good implementation, this implies:
- Creating a contract class which contains the constants for the URIs, tables and columns name (which should extends from BaseColumns ).
- Creating a class that extends SQLiteOpenHelper which will create the database the first time it is called. To do this, you need to manually create a query string indicating the name of the tables and columns you want to create, using the contract defined on the above step. This is difficult to maintain and can introduce a bunch of errors.
- Once you have created your DB, if you want to insert, update or delete information you have to instantiate your subclass of SQLiteOpenHelper , get a writable version of the DB, also you need to create a ContentValues using the contract class created on the first step, to set the keys and the values with the information you need to save. In the case of update and delete operations, you can also define a projection to select specific information base on the value of a column.
- In the case you want to read information, you have to instantiate your subclass of SQLiteOpenHelper , get a readable version of the DB and create a projection. As a result, you will get a Cursor which then you have to go through, column by column, to get the information you need.
It sounds like too much work, doesn’t it? If you want to do this easier, you can use an ORM but you still need to manually defined and create the DB, subclassing SQLiteOpenHelper and creating the contract classes. So, the question is: Is there any way to simplify all this process?, the answer is: Yes, Room is your solution.
What is Room?
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 is part of the Android Architecture Components presented in the Google I/O 2016. It is not an ORM, it is a whole library that allows us to create and manipulate SQLite databases easier, by using annotations we can define our databases, its tables and operations; Room will automatically translate these annotations into SQLite instructions/queries to perform the correspondent operations into the DB engine.
Room architecture looks like follow:
The three major components of Room are:
- Database: It represents the DB, it is an object that holds a connection to the SQLite DB and all the operations are executed through it. It is annotated with @Database .
- Entity: Represents a table within the Room Database. It should be annotated with @Entity .
- DAO: An interface that contains the methods to access the Database. It is annotated with @Dao .
Too much talk, let see an example:
Let’s imagine that we need to create an app to store your gym routine, we are going to have 4 entities in our database as we’ll show below. All the sample codes are written using kotlin, if you don’t know Kotlin or you want to learn more, I would like to invite you to visit this link and read my article about it.
The first thing we need to do is to update our gradle file. It should look like the following
As we mentioned, Room is composed of three major components: Entities, DAO’s and DataBase. Let’s see and analyze each of them:
Entities
For our example we are going to use four entities: Gender, Exercise, Routine and Trainee.
It represents the gender of the trainee.
- All the classes that represent an entity of the database have to be annotated with @Entity .
- With the annotation @PrimaryKey(autoGenerate = true) we are indicating that id is the primary key of the entity and should be autoGenerate by the database engine.
It represents an exercise that it is part of a routine.
Things to notice:
- By default, Room uses the field names as the column names in the database. If you want a column to have a different name, add the @ColumnInfo annotation to a field.
It is basically a container of exercises that together create an exercise routine.
Things to notice:
- When a class is annotated with @Entity the name of the tablet will be the name of the class, if we want to use a different one we have to add the tableName property along with the @Entity annotation.
- @TypeConverters annotation has to be used when we declare a property which type is a custom class, a list, date type or any other type that Room and SQL don’t know how to serialize. In this case, we are using the annotation at the class field level whereby only that field will be able to use it. Depending on where the annotation is placed it will behave differently as explained here.
It represents the owner of the routine.
There are several things to be noticed here:
- The indices property at @Entity annotation is used to index certain fields in the database to speed up the queries. In this case, we are indicating that the columns: name and age have to be indexed.
- The property called foreignKeys is used to indicate a relationship between two or more tables, it receives an array of ForeignKeys . entity property is the name of the father class, parentColumns is the name of the column at the father class and childColumns is the name of the column at the class where it is used.
- We use @Embedded annotation when we want to represent an object that we’d like to decompose into its subfields within a table. We can, then, query the embedded fields just as you would for other individual columns. This allow us to express an entity or plain old Java object (POJO) as a cohesive whole in our database logic, even if the object contains several fields.
Data Access Objects or DAOs are used to access our data when we implement Room. Each DAO have to include a set of methods to manipulate the data (insert, update, delete or get).
A DAO can be implemented as an interface or as an abstract class, in our case we are going to use an interface. Due to all the DAOs are basically identical we will show only one.
Some things to notice:
- All the DAOs have to be annotated with @Dao .
- A function annotated with @Insert , @Update or @Delete have to receive an instance of the desired class as a parameter, which represents the object that we want to insert, update or delete respectively.
- In the case of insert or update operations, we can use the property onConflict to indicate what to do when a conflict performing the operation happens. The strategies available to use are: REPLACE , ABORT , FAIL , IGNORE and ROLLBACK .
- If we want to get specific information from one or more entities we can annotate a function with @Query and provide a SQL script as parameter. Another advantage of use Room is: if the SQL query provided in the annotation is invalid, it will fail on compilation time and not in runtime.
Type Converters
As we said before, Type Converters are used when we declare a property which Room and SQL don’t know how to serialize. Let’s see an example of how to serialize date data type.
DataBase
It represents the DB, it holds a connection to the actual SQLite DB.
Things to notice here:
- This is an abstract class that has to extend from RoomDatabase .
- It has to be annotated with @Database , it receives a list of entities with all the classes that compose the database (all these classes have to be annotated with @Entity ). We also have to provide a database version.
- We have to declare an abstract function for each of the entities included in the @Database annotation, this function has to return the correspondent DAO (A class annotated with @Dao ).
- Finally, we declare a companion object to get static access to the method getAppDataBase which gives us a singleton instance of the database.
Using the Room database
Now let’s see a very simple example of how to use the Room database we just created
What are we doing?
- We’re getting the instance of the database and GenderDao .
- We’re creating two Gender instances: Male and Female.
- We’re inserting the two instances created into de DB.
- We’re querying the DB to get all the genders store on it.
- We’re merging the name of all the genders we got from the DB and we’re setting the text of the TextView with that value.
And that’s it 🙂 This is almost everything you need to know to create and use a Database on Android with Room. The whole code of the project can be gotten from here. Thanks for reading!
Источник
Room Database with Kotlin
In this article, i would like to explain Room Database. Firstly, let’s talk about room..
The room persistence library it is an abstraction layer over SQLite.
The room is an ORM ( Object Relational Mapper ) for SQLite database in Android. It is part of the Architecture Components.
The room makes using SQLite much easier for you by implementing annotations.
. Compile Time Verification
In SQLite database if query has any error then it will cause Run-time exception. If we use Room then it will check the query at compile time itself. So that we can prevent the app from Run- time exception.
To convert the data from SQLite to DTO ( Data Transfer Object ) we need to write lot of codes. Those are internally managed by ROOM. So we don’t need to write the convertion code.
.Easily integrated with other Architecture components
Room is an Android Architecture component. So it can provide the data as observable like LiveData or RxJava’s Single object directly.
There are 3 main components are there in Room database.
- Represents a table within the database. Room creates a table for each class that has entity annotation, the fields in the class correspond to columns in the table. Therefore, the entity classes tend to be small model classes that don’t contain any logic.
- Some useful annotations and their attributes :
— Foreign keys : names of foreign keys
— Indices : list of indicates on the table
— Primary keys : names of entity primary keys
Primary Key as its name indicates, this annotation points the primary key of the entity.autoGenerate — if set to true, then sqlite will be generating a unique id for the column
ColumnInfo allows specifying custom information about column
Ignore field will not be persisted by Room
Embeded nested fields can be referenced directly in the SQL queries.
To create a database in Room, we need to create an class and annotate it with database. To create the database we need to give what are all the tables should be there in this db and db version in the annotation.
To access database content we need to create data access object using an interface with Dao annotation. Inside this interface we need to create the methods required for db operations.
Here we can user 4 annotation :
Query to run raw query we need to user this annotation. Inside we need to pass the query.
Insert the data into database
Update the data into database
Delete the record from Database
Why Room Database over SQLite Database ?
- Compile — time verification of Sql queries, so there is no more syntax errors at run time.
- Very less code to write.
- Support to integrate with other Architecture components.
Now, let’s implement it with an example.
Step 1 : Create a new project in Android Studio with empty activity.
Step 2 : Add dependencies.
Step 3 : Now let’s create an Entity.
- It must to declare one field as primary key. You need to annotate a field with PrimaryKey with attribute autoGenerate by default it is false
- The class is annotated with Entity and name of the table
Step 4 : Create a Dao ( Data access object ) using an interface.
- This class is annotated with Dao annotation
- There are four annotations Query, Insert, Update, Delete to perform CRUD operations.
Step 5 : Create a database class an NoteDatabase extends RoomDatabase.
Источник
Урок 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 для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник