Una de las opciones que tenemos disponible en Android para realizar persistencia de datos es a través de una base de datos, SQlite para más señas.

Hasta no hace mucho, el realizar el guardado de datos a través de este medio era bastante tedioso, había que escribir una gran cantidad de código para hacer un trabajo repetitivo y aburrido.

Cierto es que existían una serie de librerías que facilitaban este trabajo mediante el acceso a nuestra base de datos mediante el uso de ORM. 

Por suerte el equipo de Google añadió, no hace mucho, una nueva API al SDK de Android, mediante la cual y utilizando un ORM, podemos realizar nuestro trabajo con persistencias en bases de datos de manera mucho más fácil, rápida y sencilla. Esto es Room.

En post anteriores vimos que podíamos realizar también otro tipo de persistencia en Android mediante la API DataStore, un tipo de persistencia que nos permite persistir datos en el formato clave:value, muy útiles en ciertas situaciones.

Para trabajar con Room en Android tenemos que prestar atención a tres componentes principales:

  • Base de datos: Es la base de datos propiamente hablando. Consta básicamente de una clase donde configuramos una serie de datos, como la versión, el nombre de la misma o los DAOS con los que tendrá acceso, entre otros y que es la encargada de crear la base de datos y de realizar las migraciones cuando sea necesario.
  • Entidades: Son objetos que representan las tablas en Base de Datos.
  • DAO: Son clases que contienen los métodos de acceso a la Base de Datos.

En este primer capítulo sobre Room para Android, vamos a iniciar nuestro recorrido por las entidades, como crearlas, los diferentes tipos de anotaciones que nos ofrece la API y cual es su significado. En siguientes entradas del blog iremos viendo el resto de componentes de Room para Android así como la forma de realizar relaciones entre tablas.

Capítulos Persistencia en Android con Room

Instalación de Room en Android

Lo primero de todo es añadir la librería de Room a nuestro proyecto. Para eso añadimos en el archivo gradle de nuestro proyecto las dependencias necesarias:

dependencies {
  def room_version = "2.2.5"

  implementation "androidx.room:room-runtime:$room_version"
  kapt "androidx.room:room-compiler:$room_version"

  // optional - Kotlin Extensions and Coroutines support for Room
  implementation "androidx.room:room-ktx:$room_version"

  // optional - Test helpers
  testImplementation "androidx.room:room-testing:$room_version"
}

Entidades en Room

Como dijimos antes, las entidades son clases que representan y modelan las tablas que componen nuestra base de datos. Un ejemplo básico sería:

   @Entity
    data class User(
        @PrimaryKey var id: Int,
        var firstName: String?,
        var lastName: String?
    )

Aquí tenemos una data class que va a representar a un tabla usuario que consta de tres campos, un id, que es además la primary key de la tabla, un campo firstName de tipo String y que puede ser nulo, y un campo lastName también de tipo String y que puede ser nulo.

Vemos que para “decirle” a Room que esta data class representa una entidad de nuestra base de datos, añadimos la anotación @Entity sobre el nombre de la clase.

Toda la configuración que podemos realizar sobre una entidad para indicar funcionalidades o especificaciones de cada campo en la tabla se realiza mediante anotaciones.

Si queremos indicar que el nombre del campo sea diferente al nombre de la variable utilizada en la clase, utilizaremos lo siguiente:

@ColumnInfo(name = "first_name") val firstName: String?

Si lo que queremos es asignar un nombre diferente a la tabla y que no sea el de la clase, entonces debemos de añadirlo como propiedad a la anotación @Entity

@Entity(tableName = "users")
    data class User (
        // ...
    )

Cada entidad debe de tener al menos un campo que sea su clave primaria aunque esta tabla solo contenga un campo, esa anotación debe de estar presente siempre. 

Si la primary key está compuesta por dos campos de la tabla entonces la forma de crearlas sería así:

@Entity(primaryKeys = arrayOf("firstName", "lastName"))
    data class User(
        val firstName: String?,
        val lastName: String?
    )

Si deseamos que la tabla contenga algún campo con id autogenerados por la propia Base de datos utilizaremos la propiedad autogenerate de la anotación @PrimaryKey.

@Entity
    data class User(
        @PrimaryKey(autogenerate = true) var id: Int,
        var firstName: String?,
        var lastName: String?
    )

Por defecto Room creará una columna por cada propiedad que posea la clase, si deseamos que alguna de estas propiedades no formen parte de la tabla, marcaremos dicha propiedad con la anotación @Ignore.

@Entity
    data class User(
        @PrimaryKey val id: Int,
        val firstName: String?,
        val lastName: String?,
        @Ignore val picture: Bitmap?
    )

Creando Indices en las tablas de Room

En el caso de que queramos añadir índices a algún campo de la tabla para que después las búsquedas sean más optimizadas, lo podemos hacer añadiendo la propiedad índices a la anotación @Entity, propiedad que recibe un array de columnas a las que se les añadirá un índice.

@Entity(indices = arrayOf(Index(value = ["last_name", "address"])))
    data class User(
        @PrimaryKey val id: Int,
        val firstName: String?,
        val address: String?,
        @ColumnInfo(name = "last_name") val lastName: String?,
        @Ignore val picture: Bitmap?
    )

Si queremos que algún campo sea único también lo indicamos con propiedades sobre la anotación @Entity, tenemos que añadir un índice al campo único y añadir la propiedad unique a true sobre este índice, tal que así.

@Entity(indices = arrayOf(Index(value = ["first_name", "last_name"],
            unique = true)))
    data class User(
        @PrimaryKey val id: Int,
        @ColumnInfo(name = "first_name") val firstName: String?,
        @ColumnInfo(name = "last_name") val lastName: String?,
        @Ignore var picture: Bitmap?
    )

En un siguiente post versemos las relaciones que podemos hacer en tres las tablas o Entidades. Con lo que hemos visto hasta ahora tenemos todo lo necesario para ir creando nuestras tablas o Entidades en Room de una manera fácil. La persistencia en Android es mucho más amigable con Room.