Android room typeconverter list object kotlin

Урок 11. Room. Type converter

В этом уроке рассмотрим, как использовать конвертеры типов данных, чтобы Room мог сохранять не только поля-примитивы.

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

Иногда ваши Entity объекты могут содержать поля, которые не являются примитивами, и не могут быть сохранены в БД.

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

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

Room справедливо замечает, что понятия не имеет, как ему такое поле сохранить в базу, и предлагает использовать type converter.

Ок, давайте создадим конвертер. Он должен уметь конвертировать List в какой-нибудь простой тип, который может быть сохранен в базу, например, String. Также конвертер должен уметь конвертировать в обратную сторону, т.е. из String в List , чтобы Room мог прочесть данные из базы в поле Entity объекта.

Первый метод преобразует List в String. Второй — наоборот. Оба метода помечаем аннотацией TypeConverter.

Осталось указать этот конвертер для поля hobbies. Это делается аннотацией TypeConverters с указанием класса конвертера.

Теперь Room будет знать, что для поля hobbies он может использовать конвертер HobbiesConverter.

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

Бывают случаи, когда преобразование может быть необходимо не только для Entity объекта. Рассмотрим пример.

Есть Entity класс

У работника все поля являются простыми, и Room без проблем может их сохранить/прочесть. Этим полям не нужны конвертеры.

Но что если мы хотим в Dao сделать так:

Т.е. нам для поиска по полю birthday (с типом long) удобнее использовать объект Date.

При попытке собрать проект получаем ошибку: Query method parameters should either be a type that can be converted into a database column or a List / Array that contains such type. You can consider adding a Type Adapter for this.

Room сообщает, что типы не совпадают и снова предлагает использовать конвертеры.

В нашем случае необходимо Date конвертировать в long, чтобы Room мог выполнить query запрос. Создаем для этого метод dateToTimestamp.

Обратная конвертация нам не нужна. У Room нет необходимости конвертировать long в Date. Объект Employee будет содержать дату в формате long.

Конвертер прописываем в Dao, прямо для конкретного параметра конкретного метода

Теперь Room конвертирует Date в long и запрос будет выполнен.

Также конвертер можно прописать для всего метода, а не отдельного параметра

В этом случае Room сможет использовать конвертер для преобразования всех параметров метода.

Если же прописать конвертер для Dao, то он будет доступен всем методам этого Dao

Ну и самое глобальное решение — прописать конвертер для Database

В этом случае Room сможет использовать его во всех Entity и Dao.

Если у вас несколько конвертеров, указывайте их через запятую.

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

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

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

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

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

Источник

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.
Читайте также:  Asus безопасный режим android

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.
Читайте также:  Аим для standoff 2 андроид

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.
Читайте также:  Android marshmallow как установить

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!

Источник

What is TypeConverter in Room? How to use it properly

Nov 28, 2020 · 3 min read

As Android developers, we should be thankful for the existence of the Room persistence library. Befo r e Room, we had to write tons of lines of code to make SQLite work. It helped us clean those boilerplate codes, as well as making working with SQLite more fun and simple. It provides many functionalities, including conversion between primitive and boxed types, but it doesn’t support object references. Think about the few primitive types that are coming into your mind, such as int, long, float, and char, etc. But sometimes, we need a custom data type whose value you would like to store into a single database column. I’m talking about custom data types such as Date, Bitmap, etc. As I said earlier, this object reference is not possible in Room at the moment. But we don’t need to worry. Here comes TypeConverter for our rescue, which can convert a custom class to and from a known type which Room can persist.

Let’s get our hands dirty.

This is our POJO class called Run,

Look at the first item in the POJO.

It’s a custom object and will not fit into any of the primitive data types we talked about earlier.

And this is our Dao class,

You can see that we are running a query to get all the runs sorted by date, and it’s returning a list of Run, and it will include our custom data type, img, which is a Bitmap. And if you try running this code, you will get the following error message.

error: Cannot figure out how to save this field into database.

You can consider adding a type converter for it.

As the error message suggesting us, let us try creating a Type converter now and see if this error is showing again.

I’m going to create a new class called Converters.

It has two functions.

  1. fromBitmap: This function will take a Bitmap as a parameter and returns a ByteArray. A ByteArray is nothing but an array of bytes. As you have correctly guessed, a byte is a primitive type which is known for Room.
  2. toBitmap: This function will take a ByteArray as a parameter and returns a Bitmap. As you have correctly guessed again, it is our custom object in this case.

Also note that we have used an annotation called, “@TypeConverter” before the starting of each function.

Next, we have to make a small change in our database class.

Take a look into this line,

We have added the “@TypeConverters” annotation to our database class so that Room can use the converter we have defined earlier to convert our Bitmap to ByteArray and vice versa. You can put as many converters as you want.

Now try running that query again and see if there are any more errors popping up.

If you have followed the above steps correctly, it will work perfectly. And congrats. You have learned how to use TypeConverter. For more information about TypeConverter in Room, please check out the official documentation from the Android developers. I have learned more about TypeConverter by watching this guy’s(Philipp Lackner) YouTube channel. He talks in-depth about Android development and Kotlin.

Lastly, if you liked reading this post and genuinely thinking it helped you, please give a clap and share it among your fellow developer friends. Thank you. Be safe.

Источник

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