Realm es la mejor solución de base de datos de Android
Publicado: 2022-03-11Desde que se creó Android, los desarrolladores de aplicaciones hemos estado usando SQLite para almacenar nuestros datos locales. A veces directamente con declaraciones SQL, a veces usando un mapeador relacional de objetos (ORM) como una capa de abstracción, pero de cualquier manera, hemos estado usando SQLite al final del día.
Sin embargo, a pesar de todas las ventajas de SQLite, hubo momentos en los que deseábamos tener alternativas a un modelo relacional: algo que nos evitara tener que agregar código repetitivo para convertir valores hacia y desde la base de datos, o que nos permitiera omitir la configuración de asignaciones. entre clases y tablas, campos y columnas, claves foráneas, etc.
En otras palabras, una base de datos con estructuras de datos más parecidas a las que realmente usamos a nivel de aplicación. Mejor aún, si pudiera ser eficiente en memoria por diseño, lo que permitiría mejores experiencias en dispositivos con recursos limitados, sería increíble.
Estos son, de hecho, algunos de los beneficios listos para usar que obtenemos con Realm, una plataforma de base de datos con una arquitectura distinta, que surgió como una nueva alternativa a SQLite.
Este artículo presenta algunas de las razones principales por las que Realm ha llamado tanto la atención y por las que quizás quieras considerar probarlo. Analiza algunas de las ventajas clave que Realm brinda a los desarrolladores de Android sobre SQLite.
Dado que Realm está disponible en múltiples plataformas, parte de lo que se tratará en este artículo también tiene relevancia para otras plataformas móviles, como iOS, Xamarin y React Native.
SQLite: funciona, pero no es lo que necesita la mayor parte del tiempo
La mayoría de los desarrolladores móviles probablemente estén familiarizados con SQLite. Ha existido desde el año 2000, y podría decirse que es el motor de base de datos relacional más utilizado en el mundo.
SQLite tiene una serie de beneficios que todos reconocemos, uno de los cuales es su compatibilidad nativa con Android.
El hecho de que sea una base de datos relacional SQL estándar también minimiza la curva de aprendizaje para aquellos que provienen de una base de datos relacional. También proporciona un rendimiento razonablemente bueno si se utiliza en todo su potencial (aprovechando funciones, como declaraciones preparadas, operaciones masivas con transacciones, etc.). Aunque es posible que SQLite no se adapte muy bien a todas sus necesidades.
Sin embargo, tratar directamente con sentencias SQL tiene varias desventajas.
De acuerdo con la documentación oficial de Android, estos son los pasos necesarios para comenzar a leer/escribir en SQLite:
- Describa su esquema en términos de clases de contrato.
- Defina sus comandos de crear/soltar tabla en cadenas.
- Extienda
SQLiteOpenHelper
para ejecutar comandos de creación y administrar actualizaciones/reversiones.
Una vez que haya hecho esto, estará listo para leer y escribir en su base de datos. Sin embargo, deberá realizar conversiones entre los objetos de su aplicación y los valores de la base de datos. Para resumir: ¡Es mucho código repetitivo!
Otro tema es la mantenibilidad. A medida que su proyecto crezca y surja la necesidad de escribir consultas más complejas, terminará con grandes porciones de consultas SQL sin procesar en cadenas. Si más adelante necesita cambiar la lógica de esas consultas, puede ser bastante complicado.
A pesar de sus desventajas, hay casos en los que usar SQL sin formato es la mejor opción. Un ejemplo es cuando está desarrollando una biblioteca donde el rendimiento y el tamaño son factores críticos y, si es posible, se debe evitar agregar una biblioteca de terceros.
Asignador relacional de objetos: la curita para los desafíos de SQL
Para salvarnos de lidiar con SQL sin procesar, los ORM vinieron al rescate.
Algunos de los ORM de Android más famosos son DBFlow, greenDAO y OrmLite.
El mayor valor que aportan es la abstracción de SQLite, lo que nos permite mapear entidades de base de datos a objetos Java con relativa facilidad.
Entre otros beneficios, los desarrolladores de aplicaciones pueden trabajar con objetos, una estructura de datos mucho más familiar. También ayuda con la mantenibilidad, ya que ahora estamos manejando objetos de alto nivel con una tipificación más fuerte y dejando el trabajo sucio a las bibliotecas. Menos problemas con la creación de consultas mediante la concatenación de cadenas o el manejo manual de la conexión con la base de datos. Menos errores tipográficos.
Aunque es un hecho que estos ORM han subido el listón en las bases de datos de Android, también tienen sus inconvenientes. En muchos casos, terminas cargando datos innecesarios.
Aquí hay un ejemplo.
Digamos que tiene una tabla con 15 columnas, y en una determinada pantalla de su aplicación, se muestra una lista de objetos de esta tabla. Esta lista muestra valores de solo tres columnas. Por lo tanto, al cargar todos los datos de la fila de la tabla, obtiene cinco veces más datos de los que realmente necesita para esa pantalla.
A decir verdad, en algunas de estas bibliotecas puede especificar qué columnas desea recuperar por adelantado, pero para eso necesita agregar más código y, aun así, eso no será suficiente en caso de que solo sepa exactamente qué columnas recuperará. Úselo después de mirar los datos en sí: algunos datos pueden cargarse innecesariamente de todos modos.
Además, a menudo hay escenarios en los que tiene que realizar consultas complejas, y su biblioteca ORM simplemente no le ofrece una forma de describir estas consultas con su API. Eso puede hacer que escriba consultas ineficientes que hacen más cálculos de los que necesita, por ejemplo.
La consecuencia es una pérdida de rendimiento, lo que lo lleva a recurrir a SQL sin formato. Si bien esto no es un factor decisivo para muchos de nosotros, perjudica el propósito principal del mapeo relacional de objetos y nos lleva de regreso a algunos de los problemas mencionados anteriormente con respecto a SQLite.
Realm: una alternativa perfecta
Realm Mobile Database es una base de datos diseñada para dispositivos móviles desde cero.
La diferencia clave entre Realm y ORM es que Realm no es una abstracción construida sobre SQLite, sino un motor de base de datos completamente nuevo. En lugar de un modelo relacional, se basa en un almacén de objetos. Su núcleo consiste en una biblioteca C++ independiente. Actualmente es compatible con Android, iOS (Objective-C y Swift), Xamarin y React Native.
Realm se lanzó en junio de 2014, por lo que actualmente tiene dos años y medio (¡bastante nuevo!).
Si bien las tecnologías de bases de datos de servidor estaban experimentando una revolución desde 2007, con la aparición de muchas nuevas, la tecnología de bases de datos para dispositivos móviles se quedó estancada con SQLite y sus contenedores. Esta fue una de las motivaciones clave para crear algo desde cero. Además, como veremos, algunas de las funciones de Realm requerían cambios fundamentales en la forma en que se comporta una base de datos a un nivel bajo, y eso simplemente no era posible construir algo sobre SQLite.
¿Pero Realm realmente vale la pena? Estas son las principales razones por las que debería considerar agregar Realm a su cinturón de herramientas.
Fácil modelado
Aquí hay un ejemplo de algunos modelos creados con Realm:
public class Contact extends RealmObject { @PrimaryKey String id; protected String name; String email; @Ignore public int sessionId; //Relationships private Address address; private RealmList<Contact> friends; //getters & setter left out for brevity }
public class Address extends RealmObject { @PrimaryKey public Long id; public String name; public String address; public String city; public String state; public long phone; }
Sus modelos se extienden desde RealmObject. Realm acepta todos los tipos primitivos y sus tipos en caja (excepto char
), String
, Date
y byte[]
. También admite subclases de RealmObject
y RealmList<? extends RealmObject>
RealmList<? extends RealmObject>
para modelar relaciones.
Los campos pueden tener cualquier nivel de acceso (privado, público, protegido, etc). Todos los campos se conservan de forma predeterminada y solo necesita anotar los campos "especiales" (p. ej., @PrimaryKey
para su campo de clave principal, @Ignore
para establecer campos no persistentes, etc.).
Lo interesante de este enfoque es que mantiene las clases menos "contaminadas por anotaciones" en comparación con los ORM, ya que en la mayoría de ellos necesita anotaciones para asignar clases a tablas, campos regulares a columnas de bases de datos, campos de clave externa a otras tablas, etc. en.
Relaciones
Cuando se trata de relaciones, hay dos opciones:
Agregue un modelo como el campo de otro modelo. En nuestro ejemplo, la clase
Contact
contiene un campoAddress
y eso define su relación. Un contacto puede tener una dirección, pero nada impide que esta misma dirección se agregue a otros contactos. Eso permite relaciones de uno a uno y de uno a muchos.Agregue una
RealmList
de los modelos a los que se hace referencia.RealmLists
se comporta como las buenas y antiguas JavaLists
, actuando como un contenedor de objetos Realm. Podemos ver que nuestro modeloContact
tiene unaRealmList
de contactos, que son sus amigos en este ejemplo. Las relaciones de uno a muchos y de muchos a muchos se pueden modelar con este enfoque.
Me gusta esta forma de representar las relaciones porque nos parece muy natural a los desarrolladores de Java. Al agregar estos objetos (o listas de estos objetos) directamente como campos de nuestra clase, tal como lo haríamos con otras clases que no son modelos, no necesitamos lidiar con la configuración de SQLite para claves externas.
Advertencia: no hay soporte para la herencia de modelos. La solución actual es utilizar la composición. Entonces, si, por ejemplo, tiene un modelo Animal
y esperaba crear un modelo Dog
que se extienda desde Animal
, tendrá que agregar una instancia Animal
como un campo en Dog
. Hay un gran debate sobre Composición vs. Herencia. Si le gusta usar la herencia, esto es definitivamente algo que necesita saber sobre Realm. Con SQLite, esto podría implementarse usando dos tablas (una para el padre y otra para el hijo) conectadas por una clave externa. Algunos ORM tampoco imponen esta restricción, como DBFlow.
¡Recupere solo los datos que necesita! Diseño de copia cero
Esta es una característica asesina.
Realm aplica el concepto de diseño de copia cero, lo que significa que los datos nunca se copian en la memoria. Los resultados que obtiene de una consulta son en realidad solo indicadores de los datos reales. Los datos en sí se cargan perezosamente a medida que accede a ellos.
Por ejemplo, tiene un modelo con 10 campos (columnas en SQL). Si consulta los objetos de este modelo para mostrarlos en una pantalla y solo necesita tres de los 10 campos para completar los elementos de la lista, esos serán los únicos campos recuperados.
Como consecuencia, las consultas son increíblemente rápidas (consulte aquí y aquí algunos resultados de referencia).
Esta es una gran ventaja sobre los ORM que generalmente cargan todos los datos de las filas de SQL seleccionadas por adelantado.
Como resultado, la carga de pantalla se vuelve mucho más eficiente sin requerir ningún esfuerzo adicional por parte del desarrollador: es solo el comportamiento predeterminado de Realm.
Además, esto también significa que las aplicaciones consumen menos memoria y, considerando que estamos hablando de un entorno con recursos limitados, como los dispositivos móviles, eso puede marcar una gran diferencia.
Otra consecuencia del enfoque de copia cero es que los objetos administrados por Realm se actualizan automáticamente.
Los datos nunca se copian en la memoria. Si tiene resultados de una consulta y otro hilo ha actualizado estos datos en la base de datos después de su consulta, los resultados que posee ya reflejarán estos cambios. Sus resultados son solo indicadores de los datos reales. Entonces, cuando accede a los valores de los campos, se devuelven los datos más actualizados.
Si ya leyó datos de objetos Realm y los mostró en la pantalla, por ejemplo, y desea recibir actualizaciones para cuando cambien los datos subyacentes, puede agregar un oyente:
final RealmResults<Contact> johns = realm.where(Contact.class).beginsWith("name", "John ").findAll(); johns.addChangeListener(new RealmChangeListener<RealmResults<Contact>>() { @Override public void onChange(RealmResults<Contact> results) { // UPDATE UI } });
No es solo un envoltorio
Aunque tenemos docenas de opciones para los ORM, son envoltorios y todo se reduce a SQLite por debajo, lo que limita hasta dónde pueden llegar. Por el contrario, Realm no es solo otro contenedor de SQLite. Tiene la libertad de proporcionar características que los ORM simplemente no pueden ofrecer.
Uno de los cambios fundamentales con Realm es poder almacenar datos como un almacén de gráficos de objetos.
Esto significa que Realm son objetos en toda su extensión, desde el nivel del lenguaje de programación hasta la base de datos. En consecuencia, se realizan muchas menos conversiones de ida y vuelta a medida que escribe y lee valores, en comparación con una base de datos relacional.
Las estructuras de bases de datos reflejan más de cerca las estructuras de datos que utilizan los desarrolladores de aplicaciones. De hecho, esta es una de las principales razones por las que hay un movimiento que se aleja del modelado relacional y se dirige hacia modelos agregados en el desarrollo del lado del servidor. Realm finalmente trae algunas de estas ideas al mundo del desarrollo móvil.
Si pensamos en los componentes de la arquitectura de Realm, en la parte inferior está su núcleo con la implementación más fundamental de la plataforma. Además, tendremos bibliotecas vinculantes para cada plataforma compatible.
Al usar un envoltorio para alguna tecnología sobre la que no tiene control, eventualmente necesita proporcionar algún tipo de capa de abstracción a su alrededor.
Las bibliotecas de enlace de reino están diseñadas para ser lo más delgadas posible, para eliminar la complejidad de la abstracción. En su mayoría, propagan la idea de diseño de Core. Al tener el control de toda la arquitectura, estos componentes funcionan mejor sincronizados entre sí.
Un ejemplo práctico es el acceso a otros objetos referenciados (claves foráneas en SQL). La estructura de archivos de Realm se basa en enlaces nativos, por lo que cuando consulta relaciones, en lugar de tener que traducir una abstracción ORM a relacional y/o unir varias tablas, obtiene enlaces sin procesar a los objetos a nivel de sistema de archivos en el formato de archivo.
Son objetos que apuntan directamente a otros objetos. Por lo tanto, consultar una relación es lo mismo que consultar una columna de enteros, por ejemplo. No hay necesidad de operaciones costosas atravesando claves foráneas. Se trata de seguir los punteros.
Soporte comunitario
Realm está en desarrollo activo y ha estado lanzando versiones actualizadas con bastante frecuencia.
Todos los componentes de Realm Mobile Database son de código abierto. Son muy receptivos en su rastreador de problemas y Stack Overflow, por lo que puede esperar un soporte bueno y rápido en estos canales.
Además, los comentarios de la comunidad se tienen en cuenta al priorizar problemas (errores, mejoras, solicitudes de funciones, etc.). Siempre es bueno saber que puede opinar sobre el desarrollo de las herramientas que utiliza.
Empecé a usar Realm en 2015 y, desde entonces, me he encontrado con varias publicaciones en la web con varias opiniones sobre Realm. Hablaremos sobre sus limitaciones pronto, pero una cosa que he notado es que muchas de las quejas hechas en el momento de la publicación se han solucionado desde entonces.
Cuando conocí Realm, por ejemplo, aún no había soporte para métodos personalizados en modelos y llamadas asincrónicas. Estos fueron factores decisivos para muchos en ese momento, pero ambos son compatibles actualmente.
Tal velocidad de desarrollo y capacidad de respuesta nos hace más seguros de que no esperaremos mucho tiempo para funciones importantes.
Limitaciones
Como todo en la vida, Realm no es todo rosas. Además de la limitación hereditaria comentada anteriormente, existen otras carencias a tener en cuenta:
Aunque es posible tener varios subprocesos leyendo y escribiendo en la base de datos al mismo tiempo, los objetos Realm no se pueden mover entre subprocesos . Entonces, si, por ejemplo, recupera un objeto de reino usando doInBackground
doInBackground()
de AsyncTask, que se ejecuta en un subproceso en segundo plano, no puede pasar esta instancia a los métodosonPostExecute()
, ya que se ejecutan en el subproceso principal. Las posibles soluciones para esta situación serían hacer una copia del objeto y pasarla o pasar la identificación del objeto y recuperar el objeto nuevamente enonPostExecute()
. Realm ofrece métodos síncronos y asíncronos para lectura/escritura.No hay soporte para claves primarias de incremento automático , por lo que deberá manejar la generación de estas usted mismo.
No es posible acceder a la base de datos desde distintos procesos al mismo tiempo . De acuerdo con su documentación, el soporte multiproceso llegará pronto.
Realm es el futuro de las soluciones de bases de datos móviles
SQLite es un motor de base de datos sólido, robusto y probado, y las bases de datos relacionales no van a desaparecer pronto. Hay una serie de buenos ORM que también funcionarán en muchos escenarios.
Sin embargo, es importante mantenerse actualizado sobre las tendencias actuales.
En este sentido, creo que Realm es una de las mayores tendencias de los últimos años en lo que respecta al desarrollo de bases de datos móviles.
Realm trae consigo un enfoque único para manejar datos que son valiosos para los desarrolladores, no solo porque puede ser una mejor opción que las soluciones existentes, sino también porque amplía nuestros horizontes en términos de nuevas posibilidades y eleva el nivel de la tecnología de bases de datos móviles.
¿Ya tienes experiencia con Realm? Por favor, siéntase libre de compartir sus pensamientos.