• slidebg1

Persistencia y Android: ¿Problema o Solución?


Recientemente os hemos hablado de soluciones  para webs en las que se trabaja con una base de datos pero, ¿qué soluciones tenemos a la hora de trabajar con ellas en un app de Android? En esta plataforma existe desde hace años la posibilidad de trabajar con SQLite de manera rudimentaria y algo insegura, pero también se han ido publicando soluciones para que gestionar los datos en una app no sea difícil ni doloroso.

 

 

Comúnmente las bases de datos SQLite se utilizan en Android para guardar datos no triviales de manera que si se pierde la conexión a internet el usuario no se vea afectado por ello, sincronizando los datos locales con los datos de servidor en cuanto se recupera la conexión.

 

Hoy os vamos a hablar de Room Persistence Library una herramienta que recientemente ha visto su primera versión beta, y que ha sido desarrollada por Google para hacernos la vida más fácil sin quitarnos la libertad de poder realizar cualquier acción con nuestra base de datos.

 

Los componentes principales de esta librería son:

  • Database: Donde se definen las entidades (tablas) que van a componer la base de datos.

  • Entity: Representan las tuplas de las tablas de nuestra base de datos, para el desarrollador estos elementos no serán más que las clases de su modelo.

  • DAO (Data Access Object): Este componente será el que haga de puente entre la base de datos y el modelo, y en él se declararán las consultas que nos permitan obtener las entidades que hayamos definido.

 

A continuación los veremos en detalle, además de otros aspectos que hacen de esta herramienta una opción muy interesante.

 

 

Entity

 

Para crear una nueva entidad en nuestra base de datos tan sólo es necesario añadir la anotación @Entity a la clase que queramos almacenar y referenciarla en la propiedad entities de nuestra clase anotada con @Database. De esta manera tendremos lista una tabla con el mismo nombre que la clase que hemos anotado (este nombre lo podemos cambiar con la propiedad tableName).

 

En la misma clase anotada como entidad debemos declarar al menos 1 atributo como clave primaria de la tabla, pero podemos declarar más. De la misma manera podemos declarar índices, los nombres con los que queramos almacenar cada atributo de la clase e incluso ignorar los que no queramos almacenar.

 

Otra facilidad que nos ofrece esta herramienta la encontramos al abordar las relaciones entre tablas, y es que podemos declarar claves ajenas en nuestras entidades o incluso clases anidadas. En el último caso no se crea una entidad nueva sino que los atributos de la clase embebida se transforman en atributos de la clase contenedora, por lo que seguiremos teniendo una única tabla.

 

DAO

 

Este es el componente principal de Room ya que lo utilizaremos para interactuar con los datos de manera asíncrona (no nos permitirá realizar acción alguna en el hilo principal). Nos permitirá realizar inserciones, actualizar y eliminar datos de manera cómoda y sencilla sin necesidad de escribir ninguna sentencia SQL, tan sólo habrá que anotar los métodos con la acción deseada. Además podremos hacer varias operaciones en una sola acción, por lo que no tendremos que preocuparnos por hacerlas mediante batching, ya que la propia herramienta lo hará por nosotros.

 

Para realizar consultas a la base de datos y utilizar lo que tenemos almacenado sí que necesitaremos conocer SQL, ya que para ello anotaremos los métodos que definamos con una consulta que será verificada en tiempo de compilación, por lo que si nos hemos equivocado en algún momento el propio IDE nos lo avisará antes de que podamos ejecutar nuestra app. Aunque este modo de trabajar añade complejidad a la implementación, nos da total libertad para realizar consultas simples, parametrizadas, subconsultas

 

Otras ventajas de usar Room es la integración con LiveData, por lo que si trabajamos con este componente podremos obtener la información de nuestra base de datos y actualizar la interfaz de manera rápida y segura. Si por otro lado utilizamos RxJava también podremos trabajar con componentes de ésta directamente sin necesidad de hacer ninguna conversión que ralentice nuestra app.

 

Y para los que echéis de menos trabajar con Cursor o para cuando la ocasión lo requiera, también podréis extraer vuestros datos en un objeto de este tipo y trabajar con ellos directamente. Aunque se recomienda no seguir este procedimiento ya que vulnera la robustez de la app al poder causar problemas de tuplas o atributos que no existan que nos lleven al lanzamiento de una excepción.

 

 

Conversores

 

Esta herramienta ya realiza una conversión automática para todos los tipos primitivos y sus clases envolventes, pero además nos ofrece la posibilidad de definir conversores para campos como fechas de manera que, cada vez que se escriba o lea un atributo de este tipo de una entidad se utilizarán los correspondientes conversores para almacenar un tipo primitivo y leer un objeto más complejo (lo hayamos definido nosotros o no).

 

Migraciones

 

Una complicación a la que nos enfrentábamos anteriormente era la de actualizar nuestra base de datos a medida que nuestra app crecía. Room no nos va a hacer todo el trabajo pero sí que nos va a poner algunas facilidades, ya que podemos definir objetos de tipo Migration donde haremos los cambios pertinentes en nuestra base de datos para cada versión de ésta.

 

Algo que tenemos que tener muy en cuenta es que si no definimos las migraciones y actualizamos la versión de la base de datos, ésta se reconstruirá eliminando todos los datos que hubiese anteriormente, ya que pondrá la consistencia de su estructura por delante de los datos que tenga almacenados.

 

Si en algún momento nuestra migración falla provocará un crash, por lo que debe probarse de antemano. Para ello tenemos a nuestra disposición un asistente de pruebas para migraciones que nos permitirá poner a punto una versión simulada de nuestra base de datos y así realizar pruebas para poder observar los resultados de la migración sin comprometer la base de datos real.

 

 

Eficiencia

 

Con tal de conseguir que nuestra app sea rápida, eficiente y no hagamos consultas innecesarias que puedan perjudicar al rendimiento de nuestras interfaces, Room no nos permite que hagamos referencias directas entre las entidades que hayamos definido ya que, si queremos acceder a una entidad a través de otra estaremos realizando dos consultas a nuestra base de datos y, posiblemente no necesitemos todos los datos de la segunda entidad (se podría dar el caso de que sí). Por tanto si queremos obtener un dato específico deberíamos definir un método que lo obtenga dentro de nuestra primera entidad.

 

Como ejemplo, si tuviéramos una interfaz con un listado de ciudades donde figura el nombre del país al que pertenecen normalmente tendríamos algo como “city.getCountry().getName()”. Para esto cada vez que se obtiene una ciudad de la base de datos se haría una consulta a la tabla de países para obtener su relacionado, independientemente de si se va a utilizar este objeto o no. Por tanto, para evitar este uso de recursos innecesario tendríamos que implementar un método para obtener sólo la información que necesitamos; algo como “city.getCountryName()” de manera que sólo se hará la consulta en caso de necesitar realmente este dato. Aún así siempre podemos implementar “city.getCountry()” si de verdad necesitamos el objeto completo.

 

Conclusión

 

Después de abordar todos lo puntos importantes de esta librería podemos afirmar que a pesar de que existen otras herramientas que llevan más tiempo funcionando o que son más rápidas a la hora de realizar una operación en la base de datos, Room apuesta por la robustez y la eficiencia, además de otorgarnos total libertad a la hora de realizar consultas aunque esto requiera un mayor conocimiento de SQL. Otra de las ventajas de utilizar esta herramienta es que es más ligera que otras de su misma índole, por lo que no recargará demasiado los índices de nuestro proyecto.

 

 

Para poder utilizar Room en nuestro proyecto Android sólo tenemos que añadir las siguientes dependencias a nuestro archivo “build.gradle”:

 

compile "android.arch.persistence.room:runtime:1.0.0-beta1"

annotationProcessor "android.arch.persistence.room:compiler:1.0.0-beta1"

 

O, en el caso de que estemos trabajando con Kotlin:

 

compile "android.arch.persistence.room:runtime:1.0.0-beta1"

kapt "android.arch.persistence.room:compiler:1.0.0-beta1"

 

Públicado el 11/10/2017

Comparte este post: