for persisting data - One of Architecture Components - Eliminates boilerplate code - Compile-time validation for SQL query - Provide Observability - Fully support SQLite
id; public String name; public Int age; } Person.java @Entity - At least one of the field must be annotated with @PrimaryKey - Fields must be public or have getter/setter - autoGenerate = true AUTO_INCREMENT Room SQL
"person_name") val name: String, @ColumnInfo(name = "person_age")val age: Int) { } @Entity If name is not specified, by default class name is used as the Table name and field name is used as a column name of a table id person_name person_age 1 Steve 22 … … … Table “Persons”
@ColumnInfo(name = "person_name") val name: String, val street: String, val city: String ) @Embedded Nested objects You do not need to create multiple tables to express one object as multiple entities Person Name Age …. Street City Person Name Age …. Address Street City Address
@Entity(tableName = "Persons") class Person(@PrimaryKey(autoGenerate = true) val id: Long @ColumnInfo(name = "person_name") val name: String, … @Embedded val address: Address @Embedded (prefix = "second_") val addressSecondary: Address) Person.kt @Embedded @Embedded annotation to represent an object that you’d like to decompose into its subfields within a table @Embedded annotation can be used on a POJO or Entity only, not for a List There is no @Entity because we don’t want to create a separate table in the database If you have multiple embedded objects you must annotate object @Embedded(prefix = “some_name”)
name: String = "", … var addressList: RealmList<Adress> ) : RealmObject() {} Relations Most Object Relational Mapping (ORM) libraries allow entity objects to reference each other. Room does not support entities being directly related to other entities Realm @Entity(tableName = "Persons") class Person( @ColumnInfo(name = "person_name") val name: String, … var addressList: List<Address> ) {} Room Cannot figure out how to save this field into database. You can consider adding a type converter for it. If you want to do it with Room… It is OK
from a unknown type into a known type in terms of database types. - @Relation Is for having relation with other model class. Creates a one-to-many relation - @ForeignKey To define the relationship between tables. Creates a one-to-one, one-to-many, many-to-many relation - @Ignore Add annotation if you don’t want to include some field for the table but want to keep it in your model class
if (value == null) null else Date(value) } @TypeConverter fun dateToTimestamp(date: Date?): Long? { return date?.time ?: return null } } @TypeConverter Object TEXT, REAL INTEGER… Converts from a unknown type into a known database type @TypeConverters
= arrayOf("personId"), onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE))) class Address(@PrimaryKey val id: Long, val personId : Long, …) @ForeignKey Person id Name Age …. It specifies that the child data is deleted Or updated when the parent data is deleted or updated - Entity may have multiple composite primary key for declaring unique values - Entity may have multiple ForeignKey @ForeignKey @Query("SELECT * FROM Person" + "INNER JOIN Address ON Person.id = Address.id WHERE Person.id= :id") Your pojo join class Return data
fun update(person: Person) @Delete fun delete(person: Person) @Query("SELECT * FROM Persons") fun allPerson(): List<Person> } @Insert fun insertPersons(vararg persons: Person) @Insert fun insertPersons(person1: Person, (person2: Person) @Insert fun insertPersons(persons: List<Person>) @Dao Dao interface which contains Basic functions of persistent storage If you want you can get the return value which is the new row id @DAO @Query("SELECT * FROM Persons WHERE person_name = :name“ ) fun person(name: String): Person Room only supports named bind parameter :name to avoid any confusion between the method parameters and the query bind parameters
- Collection of entity - Cursor - LiveData - RxJava(Single, Maybe, Flowablle ) Each @Query method is verified at compile time. It allow avoid runtime failure. @Query("SELECT * FROM Persons") fun allPerson(): List<Person>
abstract class AppDatabase : RoomDatabase() { abstract fun personDao(): PersonDao } We have to create an abstract method for every DAO class that we create Database version array of the Entity classes You should create a database class and define some necessary parameters Initialization database @Database Add a type converter to your database class
{ instance = it } } Database instance A good practice is to use singleton approach for the database Create a database instance Database name Also we can define by builder a migration strategy or working with a database in the main stream @Database
main thread since it may potentially lock the UI for a long period of time. We must perform operations with the database not in the main thread Don`t perform operations on the Room in the UI thread Run it in: - AsyncTask - Executor - Rx - Kotlin co-routines - RxJava2 - LiveData Observable queries:
allPerson(): List<Person> @Query("SELECT * FROM Persons") fun allPerson(): Flowable<List<Person>> @Query("SELECT * FROM Persons") fun allPerson(): LiveData<List<Person>> } RxJava 2 Android Architecture Components Observable Async queries return LiveData or RxJava’s Maybe, Single or Flowable. It allow you to get updates whenever the data changes Manually call this method if data is modified Observable
override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE Persons ADD COLUMN 'birthDate' INTEGER") } } Room.databaseBuilder(this, AppDatabase::class.java, "app-database") .addMigrations(Migration_1_2, //AnotherMigration) .build() Migration Room provides an abstraction layer to ease SQLite migrations with the Migration class. Migration class defines the actions that should be performed when migrating from one version to another Identity hash String used by Room to uniquely identify every database version and stores it in the room_master_table Database versions Add migrations to builder @Database(entities = arrayOf(Person::class),version = 2) Don’t forget to change a version in your database class
version not increased 2. Version not increased but no migration provided 3. Version increased, fallback to destructive migration enabled 4. Version increased, migration provided IllegalStateException IllegalStateException Database is cleaned Room.databaseBuilder(this, AppDatabase::class.java, "app-database") .fallbackToDestructiveMigration() .build() Data is kept Room.databaseBuilder(this, AppDatabase::class.java, "app-database") .addMigrations(Migration_1_2, //AnotherMigration) .build() Observable
} Database initialization @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() Testing inMemoryDatabaseBuilder() - it clears the database on closing the connection to the database allowMainThreadQueries() – can allow room to execute query on UI thread
database?.personDao()?.insert(person) … assertEquals(person.name, dbPerson?.name) assertEquals(person.age, dbPerson?.age) } @After fun closeDatabase(){ database?.close() } Testing Any data that has been added to the database will be cleared once the process is killed
VS SQLite table (tracks) which describes a collection of music tracks where each contains an id, title, duration, lyrics, etc https://hackernoon.com/squeezing-performance-from-sqlite-insertions-with-room-d769512f8330