Room: Relaciones entre tablas #AndroidMeetsKotlin
Francisco Manuel López Jurado Colaboraciones 20/04/2018
Una de las principales características por la que te puedes decantar entre una librería que facilita la gestión de BD en Android es su capacidad / facilidad de realizar relaciones entre las tablas de la misma sin penalizar el rendimiento. En primer lugar, vamos a describir nuestro modelo de datos, dicho modelo parte del ya definido en el anterior artículo sobre Room:
- Desarrollador.
- Móvil de desarrollo.
- Mascota. id -> String, name -> String
Relaciones posibles:
- 1 móvil de desarrollo sólo puede estar asignado a 1 desarrollador. Relación 1 a 1.
- 1 desarrollador puede tener N teléfonos de desarrollo. Relación 1 a N.
- 1 desarrollador puede tener N mascotas. Relación 1 a N.
- 1 mascota puede tener N dueños. Relación M a N entre las tablas “Developer” y “Pet”.
Dichas relaciones las podemos ver en el siguiente esquema:
En base a esto, vamos a definir cómo podemos implementar estas relaciones:
Relación 1 a N.
Aunque esta relación ya se vió por encima en el anterior post sobre Room seguimos ampliando su documentación. Esta relación necesita que uno de los campos tenga la anotación @ForeignKey especificando que dicho objeto es clave foránea de la otra tabla. Sin embargo, no precisa de la anotación @Relation. Además, podemos especificar el comportamiento frente a borrado / actualización del id que tenemos asociado desde la otra tabla.
Por ejemplo, hemos creado los siguientes usuarios y móviles de desarrollo:
La configuración de la relación es la siguiente:
Por lo que, al borrar el usuario con id 1 que tiene asociado el teléfono de desarrollo con id 1, éste se borrará automáticamente también. Puesto que no siempre queremos esto, podemos especificar dicho comportamiento en el parámetro “onDelete” de la anotación @ForeignKey.
Tenemos los siguientes valores:
- NO_ACTION. Es la opción por defecto, indica que no se ejecutará ninguna acción y no se hará nada por solventar el posible error o pérdida en la clave foránea.
- CASCADE. Indica que el borrado será en cascada, por lo que implica que si borramos un elemento Desarrollador que tenga su id en N objetos de tipo Mascota, se borrarán también.
- RESTRICT. No se podrá borrar o actualizar ningún id ya asignado a otro elemento.
- SET_NULL. Establecerá un NULL en el id que se haya borrado.
- SET_DEFAULT. Igual que SET_NULL, pero en este caso asignará a la clave foránea un valor por defecto.
Una vez configurada nuestra relación y su comportamiento frente a actualizaciones en las claves foráneas, vamos a crear un método que retorne el Desarrollador que tiene asignado un Móvil de Desarrollo a partir del id del mismo, dicho método estará en DeveloperDao:
Hemos usado un “INNER JOIN” para obtener a partir de la relación anterior tanto los parámetros de la tabla “Developer” como la de “DevelopmentPhone” pudiendo así obtener el desarrollador que tiene asociado el móvil de desarrollo.
Relación M - N.
En este caso ya no necesitamos el uso de claves foráneas, si no que, hacemos uso de una tabla de relación donde se especificarán los ids de ambos elementos para indicar su relación.
Por ejemplo:
El Desarrollador d1 tiene las mascotas con ids m1 y m2.
El Desarrollador d2 tiene las mascotas con ids m2 y m3.
El Desarrollador d3 no tiene mascotas, por lo que no aparecerá en la tabla.
La tabla quedaría tal que así:
id |
id_developer |
id_pet |
0 |
d1 |
m1 |
1 |
d1 |
m2 |
2 |
d2 |
m2 |
3 |
d2 |
m3 |
Esta tabla se puede gestionar con 2 anotaciones aportadas por Room:
- @Embedded. Es capaz de embeber un objeto dentro de una clase a partir de una referencia.
- @Relation. Define una relación entre dos tablas, se puede configurar con tres parámetros:
-
- “parentColum”. Hace referencia a los campos del objeto embebido. Si por ejemplo queremos acceder a otra subpropiedad del mismo podemos hacer uso del ".". Por ejemplo: desarrollador.conocimiento.nivel
- “entityColum”. Hace referencia a los campos de la tabla especificada en "entity".
- “entity”. Indica la entidad a la que hace referencia la relación, en nuestro caso sería la clase Mascota.
A continuación vamos a ver la implementación de la clase DevelopersWithPets, es importante señalar que debemos inicializar la variable anotada con @Embedded:
En cuanto a su DAO, tenemos los siguientes métodos:
- getDevelopersWithPets(). Retorna una lista de desarrolladores que tienen mascotas.
- getPetListByDeveloperId(developerId: Long). Retorna la lista de mascotas (con todos sus campos) asociadas a un desarrollador con el id pasado por parámetros.
- getPetNameListByDeveloperId(developerId: Long). Retorna la lista de los nombres de las mascotas asociadas a un desarrollador cuyo id ha sido pasado por parámetros.
La implementación de la clase DevelopersWithPetsDao sería el siguiente:
Como hemos visto, la implementación de las relaciones se realiza con relativa facilidad, por lo que no serán un inconveniente a la hora de crear nuestra BD. Room nos proporciona una alternativa a otros ORMs con fácil aprendizaje y con un alto rendimiento, todo ello sin tener que hacer uso de elementos externos a Google. Espero que os haya gustado.