Android room unique column

Урок 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.

Читайте также:  Запуск android под windows

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

Индекс

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

В аннотации 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 для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

7 Pro-tips for Room

Room is an abstraction layer on top of SQLite that makes it easier and nicer to persist data. If you’re new to Room then check out this primer:

7 Steps To Room

A step by step guide on how to migrate your app to Room

In this article, I’d like to share some pro-tips on getting the most out of Room:

1. Pre-populate the database

Do you need to add default data to your database, right after it was created or when the database is opened? Use RoomDatabase#Callback ! Call the addCallback method when building your RoomDatabase and override either onCreate or onOpen .

onCreate will be called when the database is created for the first time, after the tables have been created. onOpen is called when the database was opened. Since the DAOs can only be accessed once these methods return, we‘re creating a new thread where we’re getting a reference to the database, get the DAO, and then insert the data.

See a full example here.

Note: When using the ioThread approach, if your app crashes at the first launch, in between database creation and insert, the data will never be inserted.

2. Use DAO’s inheritance capability

Do you have multiple tables in your database and find yourself copying the same Insert , Update and Delete methods? DAOs support inheritance, so create a BaseDao class, and define your generic @Insert , @Update and @Delete methods there. Have each DAO extend the BaseDao and add methods specific to each of them.

See more details here.

The DAOs have to be interfaces or abstract classes because Room generates their implementation at compile time, including the methods from BaseDao .

3. Execute queries in transactions with minimal boilerplate code

Annotating a method with @Transaction makes sure that all database operations you’re executing in that method will be run inside one transaction. The transaction will fail when an exception is thrown in the method body.

Читайте также:  Андроид не включается будильник

You might want to use the @Transaction annotation for @Query methods that have a select statement, in the following cases:

  • When the result of the query is fairly big. By querying the database in one transaction, you ensure that if the query result doesn’t fit in a single cursor window, it doesn’t get corrupted due to changes in the database between cursor window swaps.
  • When the result of the query is a POJO with @Relation fields. The fields are queries separately so running them in a single transaction will guarantee consistent results between queries.

@Delete , @Update and @Insert methods that have multiple parameters are automatically run inside a transaction.

4. Read only what you need

When you’re querying the database, do you use all the fields you return in your query? Take care of the amount of memory used by your app and load only the subset of fields you will end up using. This will also improve the speed of your queries by reducing the IO cost. Room will do the mapping between the columns and the object for you.

Consider this complex User object:

On some screens we don’t need to display all of this information. So instead, we can create a UserMinimal object that holds only the data needed.

In the DAO class, we define the query and select the right columns from the users table.

5. Enforce constraints between entities with foreign keys

Even though Room doesn’t directly support relations, it allows you to define Foreign Key constraints between entities.

Room has the @ForeignKey annotation, part of the @Entity annotation, to allow using the SQLite foreign key features. It enforces constraints across tables that ensure the relationship is valid when you modify the database. On an entity, define the parent entity to reference, the columns in it and the columns in the current entity.

Consider a User and a Pet class. The Pet has an owner, which is a user id referenced as foreign key.

Optionally, you can define what action to be taken when the parent entity is deleted or updated in the database. You can choose one of the following: NO_ACTION , RESTRICT , SET_NULL , SET_DEFAULT or CASCADE , that have same behaviors as in SQLite.

Note: In Room, SET_DEFAULT works as SET_NULL , as Room does not yet allow setting default values for columns.

6. Simplify one-to-many queries via @Relation

In the previous User — Pet example, we can say that we have a one-to-many relation: a user can have multiple pets. Let’s say that we want to get a list of users with their pets: List .

To do this manually, we would need to implement 2 queries: one to get the list of all users and another one to get the list of pets based on a user id.

We would then iterate through the list of users and query the Pets table.

To make this simpler, Room’s @Relation annotation automatically fetches related entities. @Relation can only be applied to a List or Set of objects. The UserAndAllPets class has to be updated:

In the DAO, we define a single query and Room will query both the Users and the Pets tables and handle the object mapping.

7. Avoid false positive notifications for observable queries

Let’s say that you want to get a user based on the user id in an observable query:

You’ll get a new emission of the User object whenever that user updates. But you will also get the same object when other changes (deletes, updates or inserts) occur on the Users table that have nothing to do with the User you’re interested in, resulting in false positive notifications. Even more, if your query involves multiple tables, you’ll get a new emission whenever something changed in any of them.

Here’s what’s going on behind the scenes:

  1. SQLite supports triggers that fire whenever a DELETE , UPDATE or INSERT happens in a table.
  2. Room creates an InvalidationTracker that uses Observers that track whenever something has changed in the observed tables.
  3. Both LiveData and Flowable queries rely on the )» target=»_blank» rel=»noopener ugc nofollow»>InvalidationTracker.Observer#onInvalidated notification. When this is received, it triggers a re-query.

Room only knows that the table has been modified but doesn’t know why and what has changed. Therefore, after the re-query, the result of the query is emitted by the LiveData or Flowable . Since Room doesn’t hold any data in memory and can’t assume that objects have equals() , it can’t tell whether this is the same data or not.

Читайте также:  Sprint htc evo 4g android

You need to make sure that your DAO filters emissions and only reacts to distinct objects.

If the observable query is implemented using Flowables , use Flowable#distinctUntilChanged .

If your query returns a LiveData , you can use a MediatorLiveData that only allows distinct object emissions from a source.

In your DAOs, make method that returns the distinct LiveData public and the method that queries the database protected .

See more of the code here.

Note: if you’re returning a list to be displayed, consider using the Paging Library and returning a LivePagedListBuilder since the library will help with automatically computing the diff between list items and updating your UI.

New to Room? Check out our previous articles:

Источник

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.

Источник

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