Android room foreignkey kotlin

Урок 6. Room. Entity

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

Полный список уроков курса:

Имя таблицы

Entity класс используется для создания таблицы. По умолчанию в качестве имени таблицы используется имя этого класса. Но мы можем указать свое имя, используя параметр tableName.

Для хранения объектов Employee будет создана таблица с именем employees.

Имя поля

По умолчанию в качестве имени полей в таблице используются имена полей Entity класса. Но мы можем указать свое имя, используя параметр name в аннотации ColumnInfo.

Для fullName в таблице будет создано поле с именем full_name.

Тип поля

По умолчанию Room определяет тип данных для поля в таблице по типу данных поля в Entity классе. Но мы можем явно указать свой тип.

В таблице поле salary будет с типом TEXT.

Модификаторы доступа

Чтобы Room мог добраться до полей класса Entity, мы делаем их public.

Но есть возможность использовать private поля. Для этого надо добавить set/get методы.

Все поля — private. Но каждое имеет set/get методы.

В Android Studio эти методы добавляются парой кликов. Жмете в коде ALT+INSERT, выбираете пункт Getter and Setter, затем выбираете поля, для которых надо создать методы.

Вместо set-методов мы также можем использовать конструктор.

Поле id здесь — private и имеет get-метод. А вместо set-метода, Room будет использовать конструктор.

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

Я для упрощения примеров везде буду использовать public поля.

Первичный ключ

Мы уже знаем, как с помощью @PrimaryKey назначить какое-либо поле ключом. Каждый Entity класс должен содержать хотя бы одно такое поле. Даже если в классе всего одно поле.

У PrimaryKey есть параметр autoGenerate. Он позволяет включить для поля режим autoincrement, в котором база данных сама будет генерировать значение, если вы его не укажете.

Теперь при создании Entity объекта вы можете не заполнять поле id. База сама найдет ближайшее свободное значение и использует его.

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

Внешний ключ

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

В выше рассмотренных примерах у нас есть класс Employee для хранения данных по сотрудникам. Давайте создадим класс Car для хранения данных по машинам. И каждая машина должна быть прикреплена к какому-либо сотруднику.

В поле employee_id будет храниться id сотрудника, к которому прикреплена эта машина.

Используем параметр foreignKeys для создания внешнего ключа. Указываем, что значения поля employee_id (параметр childColumns) должно обязательно быть равно какому-либо значению поля id (параметр parentColumns) в таблице сотрудников Employee (параметр entity).

Т.е. если у нас есть три сотрудника с id 1,2 и 3, мы не сможем добавить в базу данных машину с employee_id = 4. Потому что в базе нет такого родительского ключа, т.е. сотрудника с >

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

Для подобных случаев удаления или изменения родительского ключа, вы можете настроить поведение базы данных. По умолчанию она возвращает ошибку, но это можно поменять с помощью параметров onDelete и onUpdate в аннотации ForeignKey.

Добавим параметр onDelete

Его значение = CASCADE. Это означает, что при удалении родительского ключа, будут удалены, связанные с ним дочерние ключи. Т.е. при удалении сотрудника, удалится и его машина.

Список возможных значений для параметра onDelete можно посмотреть здесь. А подробнее почитать о них на русском здесь.

Еще один параметр аннотации ForeignKey — это deferred, имеющий по умолчанию значение false. Если задать этому параметру значение true, то внешний ключ станет отложенным. Это может быть полезно при вставке данных в разные таблицы в рамках одной транзакции. Вы сможете внести все необходимые изменения, и проверка на корректность внешних ключей будет выполнена в самом конце, при выполнении commit.

Читайте также:  Тайный орден 2 для андроид

Подробнее об этом можно почитать здесь.

Индекс

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

В аннотации Entity есть параметр indicies, который позволяет задавать индексы.

Создаем два индекса: один по полю salary, а другой по двум полям first_name и last_name.

Индекс дает возможность установить для его полей проверку на уникальность. Это делается параметром unique = true.

В этом случае база будет следить, чтобы в этой таблице не было записи с повторящейся парой значений first_name и last_name.

Индекс для одного поля также может быть настроен через параметр index аннотации ColumnInfo

Будет создан индекс для поле salary.

Вложенные объекты

Пусть у нас есть класс Address, с данными о адресе. Это обычный класс, не Entity.

И мы хотим использовать его в Entity классе Employee

Если мы сделаем так, то Room будет ругаться, т.к. он не знает, как сохранить такой объект в базу:
Cannot figure out how to save this field into database. You can consider adding a type converter for it.

Но есть простое решение — использовать аннотацию Embedded.

Embedded подскажет Room, что надо просто взять поля из Address и считать их полями таблицы Employee.

Т.е. в базе будет создана таблица Employee с полями id, name, salary, city, street, number.

Добавление новой записи будет выглядеть так:

Мы создаем вложенный объект Address, но Room разберется, и запишет все в таблицу, как плоскую структуру.

Embedded объекты могут включать в себя другие Embedded объекты.

Если у вас получается так, что совпадают имена каких-то полей в основном объекте и в Embedded объекте, то используйте префикс для Embedded объекта.

В этом случае к именам полей Embedded объекта в таблице будет добавлен указанный префикс.

Ignore

Аннотация Ignore позволяет подсказать Room, что это поле не должно записываться в базу или читаться из нее.

Нам не нужно хранить Bitmap в базе, поэтому добавляем Ignore к этому полю.

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

Room: Один ко многим

Один ко многим

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

и связываем их в единую сущность:

Для получения новой сущности типа DialogWithTags нужно использовать Dao, которое будет загружать данные из таблицы DialogPojo, при этом автоматически загрузятся tags из связанной таблицы (entity = TagPojo.class):

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

Целостность

Как это ни странно, но запрос на получение DialogWithTags не гарантирует целостность данных. Т.е., возможна ситуация, когда DialogPojo уже загружен, а список TagPojo нет. Предупреждение о возможных проблемах появляется на этапе компиляции программы.А кто их читает, эти предупреждения? Для обеспечения целостности данных, в запрос нужно добавить аннотацию Transaction.

Сохранение

К сожалению, сохранить модель DialogWithTags просто так не получится. Сохранять данные нужно отдельно и, желательно, в одной транзакции, например:

Live Data

Самое большое разочарование ждет при использовании LiveData. Данные будут живыми только для Embedded поля dialog. Изменения для tags отслеживаться не будут. Конечно можно объявить поле tags как LiveData, но, не стоит забывать, что LiveData вернет данные только в том случае, если зарегистрирован хотя бы один обсервер.

Источник

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:

  1. Creating a contract class which contains the constants for the URIs, tables and columns name (which should extends from BaseColumns ).
  2. 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.
  3. 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.
  4. 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.
Читайте также:  Код ios для андроид

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.
Читайте также:  Обход frp samsung a50 android 11

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!

Источник

Оцените статью