Android kotlin sqlite room

Using and testing Room Kotlin APIs

Room is a wrapper over SQLite and it makes working with databases on Android so much easier and is by far my favorite Jetpack library. In this post I want to tell you how to use and test Room Kotlin APIs and while we do that, I’ll also share how things work under the hood.

We’re going to use the Room with a view codelab as a basis. Here we’re building a list of words that are saved in the database and displayed on the screen. The user can also add words to the list.

Define the database table

In our database we have only one table: the one containing words. The Word class represents an entry in the table and it needs to be annotated with @Entity . We use @PrimaryKey to define the primary key for the table. Based on this, Room will generate a SQLite table, with the same name as the class name. Each member of the class becomes a different column in the table. The column name and type is given by the name and type of each field. You can change it by using the @ColumnInfo annotation.

We recommend you always use the @ColumnInfo annotation as it gives you more flexibility to rename the members without having to change the database column names. Changing the column names leads to a change in the database schema and therefore you need to implement a migration.

Access the data in the table

To access the data in the table we create a data access object (DAO). This will be an interface called WordDao , annotated with @Dao . We want to insert, delete and get data from the table so these will be defined as abstract methods in the DAO. Working with the database is a time consuming I/O operation, so this needs to be done on the background thread. We’ll use Room integration with Kotlin coroutines and Flow to achieve this.

We’ve covered the basics of working with coroutines in this Kotlin vocabulary video. Check out this video for information about Flow.

Insert data

To insert data create an abstract suspend function that gets as parameter the Word to be inserted and annotate it with @Insert. Room will generate all the work that needs to be done to insert the data in the database and, because we made the function suspend, Room moves all the work to be done to a background thread. Thus, this suspend function is main-safe: safe to be called from the main thread.

Under the hood Room generates the implementation of the Dao. Here’s how the implementation of our insert method looks like:

CoroutinesRoom.execute() function is called, with 3 parameters: the database, a flag to indicate whether we’re in a transaction and a Callable object. Callable.call() contains the code that handles the database insertion.

If we check the CoroutinesRoom.execute() implementation, we see that Room moves callable.call() to a different CoroutineContext . This is derived from the executors you provide when building your database or by default will use the Architecture Components IO Executor.

Query data

To query the table, we’ll create an abstract function and annotate it with @Query , passing in the SQL query needed to get the data we want: all words from the word table, ordered alphabetically.

We want to be notified whenever the data in the database changes, so we return a Flow
> . Because of the return type, Room runs the query on the background thread.

Under the hood, Room generates the getAlphabetizedWords() for us:

Читайте также:  Андроид нет часового пояса

We see a CoroutinesRoom.createFlow() call, with 4 parameters: the database, a flag indicating whether we’re in a transaction, the list of tables that should be observed for changes (in our case just word_table ) and a Callback . Callback.call() contains the implementation of the query to be triggered.

If we check the CoroutinesRoom.createFlow() implementation, we see that here as well the query call is moved to a different CoroutineContext . Like with the insert call, this dispatcher is derived from the executors you provide when building your Database or by default will use the Architecture Components IO executor.

Create the database

We’ve defined the data to be stored in the database and how to access it, now it’s time to define the database itself. For this, we create an abstract class that extends RoomDatabase , annotate it with @Database , passing in Word as the entity to be stored, and 1 as database version.

We’ll define an abstract method that returns the WordDao . Everything is abstract because Room is the one that generates the implementation for us. Like this, there’s a lot of logic that we no longer need to implement.

The only step left is to build the database. We want to make sure we don’t have multiple database instances open at the same time, and we need the application context to initialise the database. So one way to handle this is to define a RoomDatabase instance in the companion object of our class, and a getDatabase function that builds the database. If we want Room queries to be executed on a different Executor than the IO Executor created by Room, we can pass it in the builder by calling setQueryExecutor() .

Testing the Dao

To test the Dao we need to implement an AndroidJUnit test as Room creates an SQLite database on the device.

When implementing the Dao test, before each test is run, we create the database. After each test is run, we close the database. As we don’t need to save the data on the device, when creating the database, we can use an in-memory one. As this is a test, we can allow queries to be run on the main thread.

To test if a word is inserted successfully, we’ll create a word, insert it, get the first word from the list of alphabetised words and ensure it’s the same as the word we created. As we’re calling suspend functions, we’ll run the test in a runBlocking block. Since this is a test, we don’t mind if the test blocks the test thread.

Room offers a lot more functionality and flexibility than what we’ve covered — you can define how Room should handle database conflicts, you can store types that otherwise, natively with SQLite can’t be stored, like Date , by creating TypeConverters , you can implement complex queries, using JOIN and other SQL functionality, create database views, pre-populate your database or trigger certain database actions whenever the database is created or opened.

Check out our Room documentation for more information and the Room with a view codelab for hands-on learning.

Источник

Room: Хранение данных на Android для всех и каждого

Room — это новый способ сохранить данные приложений в Android-приложении, представленный в этом году на Google I/O. Это часть новойAndroid Architecture, группа библиотек от Google, которые поддерживают уместную архитектуру приложений. Room предлагается в качестве альтернативы Realm, ORMLite, GreenDao и многим другим.

Room — это высокоуровневый интерфейс для низкоуровневых привязок SQLite, встроенных в Android, о которых вы можете узнать больше в документации. Он выполняет большую часть своей работы во время компиляции, создавая API-интерфейс поверх встроенного SQLite API, поэтому вам не нужно работать с Cursor или ContentResolver.

Использование Room

Во-первых, добавьте Room в свой проект. После этого вам нужно будет передать в Room, как выглядят ваши данные. Предположим, имеется простой класс модели, который выглядит следующим образом:

Чтобы рассказать Room о классе Person, добавляем аннотицию Entity к классу и @PrimaryKey к ключу:

Благодаря этим двум аннотациям Room теперь знает, как создать таблицу для хранения экземпляров Person.

Важная вещь, которую следует учитывать при настройке ваших моделей: каждое поле, которое хранится в базе данных, должно быть общедоступным или иметь геттер и сеттер в стандартном стиле Java Beans (например, getName () и setName (имя строки)).

Читайте также:  Android test lan speed test

В классе Person теперь есть вся информация, которая требуется Room для создания таблиц, но у вас нет способа фактически добавлять, запрашивать или удалять данные из базы данных. Вот почему вам нужно будет сделать объект доступа к данным (DAO). DAO предоставляет интерфейс в самой базе данных и занимается манипулированием хранимыми данными Person.

Вот простой интерфейс DAO для класса Person:

Первое, что нужно заметить, это то, что PersonDaoэто интерфейс, а не класс. Другая интересная деталь — это инструкции SQL в аннотациях Query (). Операторы SQL говорят Room, какую информацию вы хотите получить из базы данных. Они также проверяются во время компиляции. Поэтому, если вы измените подпись метода List getAllPeopleWithFavoriteColor ( название цвета ) на List getAllPeopleWithFavoriteColor ( int color ), Room выдаст ошибку во время компиляции:

И если вы сделаете опечатку в выражении SQL, например, напишите favoriteColors ( множественное число ) вместо favoriteColor ( единственное число ), Room также выдаст ошибку компиляции:

Вы не можете получить экземпляр PersonDao, потому что это интерфейс. Чтобы иметь возможность использовать классы DAO, вам необходимо создать класс базы данных. За кулисами этот класс будет отвечать за ведение самой базы данных и предоставление экземпляров DAO.

Вы можете создать свой класс базы данных всего за пару строк:

Это лишь описание структуры базы данных, но сама база данных будет жить в одном файле. Чтобы получить экземпляр AppDatabase, сохраненный в файле с именем populus-database, вы должны написать:

Если вы хотите получить все данные обо всех Person, которые находятся в базе данных, вы могли бы написать:

Преимущества использования Room

В отличие от большинства ORM, Room использует обработчик аннотации для выполнения всей своей манеры сохранения данных. Это означает, что ни ваши классы приложений, ни классы моделей не должны ничего расширять в Room, в отличие от многих других ORM, включая Realm и SugarORM. Как вы видели при ошибках с аннотациями Query () выше, вы также получаете возможность проверки корректности SQL-запросов во время компиляции, что может сэкономить вам много хлопот.

Room также позволяет вам наблюдать за изменениями данных, интегрируя их как с API LiveData Архитектурных Компонентов, так и с RxJava 2. Это означает, что если у вас сложная схема, где изменения в базе данных должны появляться в нескольких местах вашего приложения, Room делает уведомления об изменениях. Это мощное дополнение может быть включено одной строкой. Все, что вам нужно сделать, это изменить тип возвращаемых значений.

Например, этот метод:

Самое большое ограничение в Room: взаимосвязи

Самым большим ограничением в Room является то, что он не будет обрабатывать отношения с другими типами сущностей для вас автоматически, как и другие ORM. Это означает, что если вы хотите отслеживать домашних животных:

То Room выдаст ошибку компиляци, так как не знает, как сохранить отношения между Person и Pet:

Ошибка при компиляции предлагает конвертер типов, который преобразует объекты в примитивы, которые могут быть непосредственно сохранены в SQL. Поскольку List нельзя свести к примитиву, вам нужно сделать что-то другое. Это отношения «один ко многим», где у одного Person может быть много Pet. Room не может моделировать такие отношения, но она может справиться с обратными отношениями — у каждого Pet есть один Person. Чтобы смоделировать это, удалите поле для Pet в Person и добавьте поле ownerId в класс Pet:

Это приведет к тому, что Room обеспечит ограничение внешнего ключа между объектами. Room не будет вызывать отношения «один-ко-многим» и «много-к-одному», но она дает вам инструменты для выражения этих отношений.

Чтобы получить всех домашних животных, принадлежащих конкретному человеку, вы можете использовать запрос, который находит всех домашних животных с данным идентификатором владельца. Например, вы можете добавить в свой DAO следующий метод:

Стоит ли использовать Room?

Если вы уже настроили сохранение данных в своем приложении и довольны им, то ничего не изменяйте. Каждая ORM и встроенная реализация SQLite будут продолжать работать так же, как и раньше. Room — это всего лишь еще один вариант сохранения данных.

Читайте также:  Где находятся системные файлы андроид

Если вы используете SQLite или собираетесь использовать его, вы должны попробовать Room. Он обладает всеми возможностями, необходимыми для выполнения расширенных запросов, одновременно устраняя необходимость писать SQL-запросы для поддержки базы данных самостоятельно.

Источник

Android Room using Kotlin

Feb 3, 2019 · 3 min read

Room is a part of the Android Architecture components which provides an abstraction layer over SQlite which allows for a more robust database acces while still providing the full power of SQlite.

Today we are going to discuss the basics of using the Room library and how it can enhance your workflow as a developer.

Room Basics

The Room library consists of 3 major components:

Entity:

The Entity represents a table within the database and has to be annotated with @Entity. Each Entity consist of a minimum of one field has to define a primary key.

DAO (Database Access Object):

In Room you use data access objects to access and manage your data. The DAO is the main component of Room and includes methodes that offer access to your apps database it has to be annotated with @Dao. DAOs are used instead of query builders and let you seperate different components of your database e.g. current data and statistics, which allows you to easily test your database.

Database:

Serves as the database holder an is the main accesspoint to your relational data. It has to be annotated with @Database and extents the RoomDatabase. It also contains and returns the Dao (Database Access Object).

Adding the needed dependencies

First, we need to add the needed dependencies that will enable us to use the Room library. We can do so with some simple lines in the build.gradle (Module:app) file

We also need to create a variable for the room version in our build.gradle (Project) file

Entity

After importing the dependency we can start defining the Entity which represents a table in the database. In this example we just have a simple todo list item with a title and a content field.

Now you should start to relise that Room is based on annotations. Now let’s look on how you can autogenerate a Primarykey and change the Tablename.

Next, we can start building our DAO which will contain our data queries.

Here we just define basic SQL database functionality like inserting and deleting entries. You can see that the @Query annotation is used to annotate functions which are using queries. You can also use parameters in your queries using :parametername as you can see in the findByTitle function.

You can also make your queries observable using LiveData as you can see in this example.

Here, you are returning a LiveData Object holding a list of TodoEntries which you can observer in your activity.

Database

After that we can start writing the database which contains all your DAOs as abstract methods.

Here we define the version of the database and the entity and DAO that we are using.

You can also use more than one entity and define a custome build and invoke methode as you can see in this example.

Accessing the Database

After defining the database we can get an instance in our activity using the Room.databaseBuilder() methode.

We don’t need the Room.databaseBuilder() to get an instance of the second database example defined above. We just need to call the invoke() methode and pass the activity context.

Now we can start using the database instance to access our DAO object.

Database operations cannot run on the UI thread so we need to create another one. In this example we are using Kotlin coroutines to launch a thread for the database operations.

Testing your database

Now we can start testing our database to make sure that the read and write functionality is functioning correctly.

Here we insert an object into the database and read it and check if the two objects are the same.

Источник

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