Room: Storage of data on Android for everyone
- Transfer
- Tutorial
Room - a new way to save data applications in Android-application submitted this year for the Google I of / About . 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 low-level SQLite bindings built into Android, which you can learn more about in the documentation . It does most of its work at compile time, creating an API on top of the built-in SQLite API, so you don't need to work with Cursor or ContentResolver .
Using Room
First, add Room to your project. After that, you will need to transfer to 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;
}With 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 is that each field that is stored in the database must be public or have a getter and setter in the standard Java Beans style (e.g. getName () and setName (string name) ).
The Person class now has all the information that Room needs to create tables, but you have no way to actually add, query, or delete data from the database. This is why you will need to make a data access object (DAO). DAO provides an interface in the database itself and is involved in manipulating Person stored 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 getAllPeople();
// Получение всех Person из бд с условием
@Query("SELECT * FROM person WHERE favoriteColor LIKE :color")
List 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 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 List method getAllPeopleWithFavoriteColor ( color name ) to List getAllPeopleWithFavoriteColor ( int color ), Room will throw an error at compile time:
incompatible types: int cannot be converted to StringAnd if you make a typo in the SQL expression, for example, write favoriteColors ( plural ) instead of favoriteColor ( singular ) , Room will also throw 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 it 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 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 a single file. To get an instance of AppDatabase stored in a file called populus-database , you should write:
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "populus-database").build();If you want to get all the data about all Person that are in the database, you could write:
List everyone = db.getPersonDao().getAllPeople(); Benefits of Using Room
Unlike most ORMs, Room uses an annotation handler to perform its entire manner of storing data. This means that neither your application classes nor model classes should extend anything in Room , unlike many other ORMs, including Realm and SugarORM. As you saw with errors with the Query () annotations above, you also get the option of checking the correctness of SQL queries at compile time, which can save you a lot of trouble.
Room also allows you to observe data changes, integrating them with both the LiveData API of Architectural Components and RxJava 2. This means that if you have a complex scheme where changes in the database should appear in several places of your application, Room makes notifications about changes. This powerful add-on can be included in a single line. All you have to do is change the return type.
For example, this method:
@Query("SELECT * FROM person")
List getAllPeople(); Becomes as follows:
@Query("SELECT * FROM person")
LiveData> /* or Observable> */ getAllPeople();
The biggest limitation in Room: relationships
The biggest limitation in Room is that it will not automatically process relationships with other types of entities for you, like other ORMs. This means that if you want to track pets:
@Entity
public class Person {
@PrimaryKey String name;
int age;
String favoriteColor;
List pets;
}
@Entity
public class Pet {
@PrimaryKey String name;
String breed;
} That Room will throw a compilation error, because it does not know how to maintain 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.A compilation error offers a type converter that converts objects to primitives that can be directly stored in SQL. Since 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 many Pet. Room cannot model such relationships, but it can handle inverse relationships - each Pet has one Person. To simulate this, delete 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 restrict the foreign key between objects. Room will not invoke a one-to-many and many-to-one relationship, but it gives you the tools to express that relationship.
To get all the pets belonging 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 getPetsForOwner(String ownerId); Should I use Room?
If you have already configured to save data in your application and are satisfied with it, then do not change anything. Each ORM and embedded SQLite implementation will continue to work as before. Room is just another way to save data.
If you use SQLite or intend to use it, you should try Room. It has all the capabilities necessary to perform advanced queries, while eliminating the need to write SQL queries to support the database on its own.