📜 ⬆️ ⬇️

Room: Android data storage for everyone

Room is a new way to save application data in an Android application introduced this year on Google I / O. This is part of the new Android Architecture , a group of libraries from Google that support the relevant application architecture. Room is offered as an alternative to Realm, ORMLite, GreenDao and more.


Room is a high-level interface for the low-level SQLite bindings built into Android, which you can learn more about in the documentation . It performs most of its work at compile time, creating the API over the embedded SQLite API, so you don’t need to work with Cursor or ContentResolver .


Use Room


First, add Room to your project. After that you will need to transfer to the Room what your data looks like. Suppose there is a simple model class that looks like this:


public class Person { String name; int age; String favoriteColor; } 

To tell Room about the Person class, add the Entity annotation to the class and @PrimaryKey to the key:


 @Entity public class Person { @PrimaryKey String name; int age; String favoriteColor; } 

Thanks to these two annotations, Room now knows how to create a table to hold instances of Person .


An important thing to consider when setting up your models: each field that is stored in the database must be public or have a getter and setter in the standard Java Beans style (for example, getName () and setName (string name) ).


The Person class now has all the information Room needs to create tables, but you have no way to actually add, query, or delete data from the database. That is why you will need to make a data access object (DAO). DAO provides an interface to the database itself and handles the handling of stored Person data.


Here is a simple DAO interface for the Person class:


 @Dao public interface PersonDao { //  Person   @Insert void insertAll(Person... people); //  Person   @Delete void delete(Person person); //   Person   @Query("SELECT * FROM person") List<Person> getAllPeople(); //   Person     @Query("SELECT * FROM person WHERE favoriteColor LIKE :color") List<Person> getAllPeopleWithFavoriteColor(String color); } 

The first thing to notice is that PersonDao is an interface, not a class . Another interesting detail is the SQL statements in the Query () annotations. SQL statements tell Room what information you want to retrieve from the database. They are also checked at compile time. Therefore, if you change the signature of the getAllPeopleWithFavoriteColor ( color name ) method to List getAllPeopleWithFavoriteColor ( int color ), Room will generate an error at compile time:

 incompatible types: int cannot be converted to String 

And if you make a typo in the SQL statement, for example, write favoriteColors ( plural ) instead of favoriteColor ( singular ) , Room will also generate a compilation error:


 There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: favoriteColors) 

You cannot get an instance of PersonDao, because this is an interface. To be able to use DAO classes, you need to create a database class. Behind the scenes, this class will be responsible for maintaining the database itself and providing DAO instances.


You can create your own database class in just a couple of lines:


 @Database(entities = {Person.class /*, AnotherEntityType.class, AThirdEntityType.class */}, version = 1) public abstract class AppDatabase extends RoomDatabase { public abstract PersonDao getPersonDao(); } 

This is just a description of the database structure, but the database itself will live in one file. To get an AppDatabase instance stored in a file called populus-database , you would write:


 AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "populus-database").build(); 

If you want to get all the data about all the Person that are in the database, you could write:


 List<Person> everyone = db.getPersonDao().getAllPeople(); 

Benefits of using Room


Unlike most ORMs, Room uses an annotation handler to perform all of its data storage. This means that neither your application classes, nor the model classes should expand anything in the Room , unlike many other ORMs, including Realm and SugarORM. As you saw in the errors with the Query () annotations above, you also get the ability to validate SQL queries during compilation, which can save you a lot of trouble.


Room also allows you to monitor data changes, integrating them with both the LiveData API of Architectural Components and RxJava 2. This means that if you have a complicated scheme where changes in the database should appear in several places in your application, Room makes notifications about changes. This powerful add-on can be included in one line. All you need to do is change the type of the returned values.


For example, this method:


 @Query("SELECT * FROM person") List<Person> getAllPeople(); 

Becomes as follows:


 @Query("SELECT * FROM person") LiveData<List<Person>> /* or Observable<List<Person>> */ getAllPeople(); 

Room's biggest limitation: interconnections


The biggest limitation in Room is that it will not handle relationships with other types of entities for you automatically, like other ORMs. This means that if you want to track pets:


 @Entity public class Person { @PrimaryKey String name; int age; String favoriteColor; List<Pet> pets; } @Entity public class Pet { @PrimaryKey String name; String breed; } 

That Room will generate a compilation error, since it does not know how to keep the relationship between Person and Pet:


 Cannot figure out how to save this field into database. You can consider adding a type converter for it. 

Compile error offers a type converter that converts objects to primitives that can be directly stored in SQL. Since the List cannot be reduced to a primitive, you need to do something else. This is a one-to-many relationship, where one Person can have a lot of Pet. Room cannot model such relationships, but she can cope with inverse relationships — each Pet has one Person. To model this, remove the field for Pet in Person and add the ownerId field to the Pet class:


 @Entity public class Person { @PrimaryKey String name; int age; String favoriteColor; } @Entity(foreignKeys = @ForeignKey( entity = Person.class, parentColumns = "name", childColumns = "ownerId")) public class Pet { @PrimaryKey String name; String breed; String ownerId; // this ID points to a Person } 

This will cause Room to provide a foreign key constraint between objects. Room will not call a one-to-many and many-to-one relationship, but it gives you the tools to express these relationships.


To get all pets that belong to a particular person, you can use a query that finds all pets with a given owner ID. For example, you can add the following method to your DAO:


 @Query("SELECT * FROM pet WHERE ownderId IS :ownerId") List<Pet> getPetsForOwner(String ownerId); 

Should I use the Room?


If you have already configured to save data in your application and are happy with it, then do not change anything. Each ORM and embedded implementation of SQLite will continue to work the same way as before. Room is just another option for saving data.


If you are using SQLite or are going to use it, you should try Room. It has all the capabilities necessary to perform advanced queries, at the same time eliminating the need to write SQL queries to support the database on its own.


')

Source: https://habr.com/ru/post/336196/


All Articles