Persistencia de datos con Room — Parte 1
Esta es una presentación que hice para el MeetUp#29 de Android Dev Perú sobre cómo implementar Room en nuestros proyectos, si desean ver la presentación completa la pueden encontrar en YouTube.
Trataremos de cubrir varios conceptos que nos brinda esta librería:
- Entity
- ForeignKey
- TypeConverter
- Embedded
- Relation
- Migration
- TypeConverter
- Database Inspector 🤩
En esta parte revisaremos conceptos teóricos pero muy importantes, si deseas continuar con el demo puedes pasar a la Parte 2
¿Que es Room?
Podemos encontrar diferentes definiciones y mucha teoría sobre Room pero en resumen es una biblioteca de persistencia de datos que nos brinda una capa de abstracción para SQLite (fácil, rápido y sencillo).
Un poco de código previo
Regresemos unos años y revisemos la implementación de este método para guardar los datos de un estudiante utilizando SQLite, para hacerlo más dramático está escrito en Java 🙈
public void addStudent(Student student) {
SQLiteDatabase db = this.getWritableDatabase(); //Content values use KEY-VALUE pair concept
ContentValues values = new ContentValues();
values.put(KEY_STUDENT_ID, student.getStudentID());
values.put(KEY_STUDENT_NAME, student.getStudentName());
values.put(KEY_STUDENT_EMAIL, student.getStudentEmail()); db.insert(TABLE_STUDENTS, null, values);
db.close();
}
Ahora veamos este método utilizando Room donde simplemente escribimos la anotación @Insert para implementar la misma funcionalidad, obviamente en Kotlin 💜
@Insert
fun addStudent(student: Student)
Ventajas de utilizar Room
- Eliminar todo el código repetitivo.
- Verificación de consultas en tiempo de compilación.
- Nuestro código es muy legible y fácil de mantener.
- Si bien es una capa adicional para trabajar con SQLite, a nivel de performance tienen mucha similitud.
Componentes de Room
- Entity: Clase que representa nuestra tabla en la base de datos.
- DAO: Interfaz que define los métodos de acceso a la base de datos.
- RoomDatabase: Clase abstracta para inicializar nuestra base de datos.
¿Cómo lo implementamos?
Los componentes de Room interactúan para que podamos acceder a la base de datos SQLite de manera sencilla, su implementación básica es la siguiente:
1) Primero para implementar una tabla en base de datos debemos crear una clase con la anotación @Entity, sus atributos con la anotación @ColumnInfo y uno de ellos debe tener la anotación @PrimaryKey para definir la llave primaria.
2) Luego para implementar el Dao creamos una interface con la anotación @Dao y definimos sus métodos de acceso a la base de datos con las anotaciones @Insert, @Update, @Delete o @Query para las consultas.
3) Finalmente declaramos una clase abstracta que herede de RoomDatabase donde declararemos los Entity y Dao que creamos anteriormente, además de implementar un singleton para obtener instancia de la base de datos.
Android Architecture Components
Algo muy importante es que podemos utilizar los siguientes componentes de arquitectura en conjunto con Room con el objetivo de construir aplicaciones robustas, testeables y mantenibles (esta frase la saqué de Android Jetpack)
- LifeCycles: Administra el ciclo de vida de nuestros fragmentos o actividades.
- LiveData: Contenedor de datos observable que notifica a la UI cuando la base de datos cambia, tomando en cuenta el ciclo de vida.
- ViewModel: Administra los datos relacionados con la IU de manera optimizada para los ciclos de vida
- Room: Acceso a la base de datos SQLite “fácil, rápido y sencillo” 💜
Consultas asíncronas vs hilo principal
Room no admite el acceso a la base de datos en el hilo principal
Las consultas que hagamos en nuestra base de datos pueden tomar cierta cantidad de tiempo y si estas se hacen desde el hilo principal de la aplicación, se podría congelar la UI hasta que la consulta termine. Obviamente no queremos esto por eso Room no lo permite por defecto sin embargo podemos activar las consultas desde el hilo principal utilizando el método allowMainThreadQueries() al instanciar la base de datos.
Tenemos diferentes alternativas para trabajar las consultas de manera asíncrona:
- Consultas reactivas: Utilizando RxJava (Completable, Maybe, Flowable, etc)
- Consultas observables: Utilizando LiveData
- Coroutines: Utilizando funciones suspend
Tengamos en cuenta que las consultas que retornan instancias de LiveData o Flowable se ejecutan asincrónicamente en un subproceso en segundo plano y retornan su resultado cuando es necesario.
Eso es todo por ahora, vimos una revisión teórica sobre Room. A continuación en la Parte 2 implementaremos una aplicación demo donde entraremos en detalle sobre la mayoría de funcionalidades que nos trae esta esta librería.