En este artículo te quiero mostrar cuáles son los pasos para empezar a utilizar la librería de Room.

Room es una librería de base de datos creada por el equipo de Android en Google y que simplifica la tarea de trabajar con bases de datos en Android.

Room es tan fácil de usar, que en unos minutos tendrás una base de datos lista para ser usada.

Cómo funciona

La librería de Room actúa de intermediaria con la App. Lo que hacemos es pedirle el DAO que queremos usar, y el DAO nos devuelve una serie de Entities, que podemos modificar para actualizar la base de datos.

Tampoco te líes mucho con los conceptos porque lo veremos en el ejemplo

Añadir las dependencias de Room

Necesitas incluir el plugin kotlin-kapt y las dos siguientes dependencias:

apply plugin: 'kotlin-kapt'

dependencies {
    implementation "androidx.room:room-ktx:2.2.5"
    kapt "androidx.room:room-compiler:2.2.5"
}

Como siempre, recuerda revisar las últimas versiones en la sección de releases de AndroidX.

Creamos las entidades que va a tener la base de datos

Si lo relacionamos con los conceptos típicos de una base de datos, estas entidades vendrían a ser las tablas.

Por ejemplo, si tenemos una entidad tipo persona como esta:

data class Person(
    val name: String,
    val age: Int,
    val address: String
)

Lo único que necesitamos es añadir la anotación @Entity para convertirlo en una entidad de Room:

@Entity
data class Person(
    val name: String,
    val age: Int,
    val address: String
)

Además, podemos añadirle un id que se autogenere, para identificar las distintas filas de la tabla:

@Entity
data class Person(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    val name: String,
    val age: Int,
    val address: String
)

Y ya está, esto nos creará una tabla de base de datos, ahora necesitaríamos poder hacer peticiones sobre esta tabla.

DAOs (o Data Access Objects)

Son los objetos que nos van a permitir acceder a los datos. Utilizando la anotación @Dao es muy sencillo:

@Dao
interface PersonDao {
}

Los DAOs son interfaces, y cada método representa una operación de acceso o modificación de base de datos. Mediante anotaciones le indicamos qué operación van a realizar.

Acceso a datos

@Dao
interface PersonDao {

    @Query("SELECT * from Person")
    fun getAll(): List<Person>
}

Una cosa súper interesante es que Android Studio nos permite autocompletar y hacer validaciones sencillas de las queries que definimos:

A las queries también podemos pasarles argumentos. Solo tienes que indicar el argumento en la función, y lo podrás usar desde la query:

@Query("SELECT * FROM Person WHERE id = :id")
fun findById(id: Int): Person

Creación de nuevos registros

Se realizan mediante la anotación @Insert:

@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(people: List<Person>)

Gracias a onConflict, le podemos definir la estrategia de inserción si hay conflictos: IGNORE, ABORT o REPLACE

Actualización de registros

Es igual de fácil, pero con la anotación @Update:

@Update
fun update(person: Person)

Borrado de registros

Como ya te puedes imaginar a estas alturas, se haría con la anotación @Delete:

@Delete
fun delete(person: Person)

Creación de la base de datos

Este paso es muy sencillo. Solo tienes que crear la base de datos de Room, e identificar qué entidades va a utilizar y que DAOs puede proveer.

Hay que definirla como una clase abstracta que extienda de RoomDatabse, y utilizar una serie de anotaciones que harán todo el trabajo sucio por nosotros:

@Database(
    entities = [Person::class],
    version = 1
)
abstract class PeopleDb : RoomDatabase() {

    abstract fun personDao(): PersonDao
}

Para crear la instancia de Room, en el Application por ejemplo (si no usas inyección de dependencias):

    val room: PeopleDb = Room
        .databaseBuilder(this, PeopleDb::class.java, "people")
        .build()

Y para usarlo:

val people = room.personDao().getAll()

Cómo evitar que Room se ejecute en el hilo principal

Todas estas operaciones de base de datos deberían hacerse fuera del hilo principal. Esto lo podemos hacer manualmente nosotros, o usar alguna de las integraciones que ya se nos dan implementadas.

Integración con corrutinas

Lo único que necesitamos es añadir la palabra suspend delante de las funciones del DAO, ¡y listo!

    @Query("SELECT * from Person")
    suspend fun getAll(): List<Person>

Soluciones reactivas

Quizá preferimos que se nos informe cada vez que se modifiquen los elementos de la Query realizada. Para ello, lo que podemos hacer es usar alguna de las integraciones que tenemos con:

  • LiveData
  • Flow de Corrutinas
  • RxJava
  • Guava

Por ejemplo, con LiveData, es tan sencillo como devolver el objeto envuelto en un LiveData:

    @Query("SELECT * from Person")
    suspend fun getAll(): LiveData<List<Person>>

Lo único que tendríamos que hacer sería observar ese LiveData.

Déjame en los comentarios si quieres que haga otro artículo hablando sobre alguna de las demás integraciones.

Y aquí puedes acceder al Gist con los distintos archivos.

Author: Antonio Leiva

Soy un apasionado de Kotlin. Hace ya más de dos años que estudio el lenguaje y su aplicación a Android para ayudarte a ti a aprenderlo de la forma más sencilla posible.