Relaciones Uno A Muchos Con Room

ANUNCIO
Loading...

En este tutorial aprenderás a cómo usar la anotación @Relation para implementar relaciones uno a muchos con Room.

Implementar Relación Uno A Muchos

Una relación uno a muchos se da cuando una instancia de la entidad padre puede relacionarse con uno o más instancias de la entidad hija.

Puedes implementarlas en Room con los siguientes pasos:

Paso 1: Incluye una referencia a la clave primaria de la entidad padre en la entidad hija.

@Entity(tableName = "parent") public class Parent { @NonNull @PrimaryKey public long id; } @Entity(tableName = "child") public class Child { public long parentId; }
Lenguaje del código: PHP (php)

Paso 2: Crea una clase de relación con un campo del tipo del padre anotado con @Embedded. Y otro que sea una lista del tipo de la entidad hija, anotado con @Relation (exactamente igual que en relaciones 1:1):

public class ParentWithChilds{ @Embedded public Parent parent; @Relation( parentColumn="id", entityColumn="parentId" ) public List<Child> childs; }
Lenguaje del código: PHP (php)

Paso 3: Y para consultar los resultados, añade un método anotado con @Query que retorne el tipo de la clase de relación.

@Transaction @Query("SELECT * FROM parent") public List<ParentWithChilds> getParentWithChilds();
Lenguaje del código: CSS (css)

Proyecciones

Si deseas retornar solo algunas columnas de tu entidad que estén especificadas en un POJO, entonces agrega la propiedad entity especificando la entidad de la que se inferirá:

public class ChildId{ public long id; } public class ParentWithChilds{ @Embedded public Parent parent; @Relation( parentColumn="id", entityColumn="parentId", entity = Child.class ) public List<ChildId> childs; }
Lenguaje del código: PHP (php)

También existen las proyecciones desplegables, que consisten en especificar a Room el nombre de las columnas específicas a consultar con la propiedad projection.

public class ParentWithChilds{ @Embedded public Parent parent; @Relation( parentColumn="id", entityColumn="parentId", entity = Child.class, projection = {"id"} ) public List<Long> childIds; }
Lenguaje del código: PHP (php)

Ejemplo De Relaciones Uno A Muchos Con Room

Tomemos la relación entre las tablas shopping_list y collaborator del ejemplo de la app de listas de compras.

Diagrama uno a muchos con Room

Supón que quieres mostrar el nombre del colaborador en los ítems de la lista de la actividad principal.

Prototipo de relaciones uno a  muchos con Room

Ya que es una relación uno a muchos, veamos la solución aplicando lo aprendido previamente.

Puedes descargar el código completo desde el siguiente enlace:

1. Crear Entidad De Colaboradores

Crea una nueva clase llamada Collaborator. Anótala con @Entity y agrega como campos todas las columnas mostradas en el modelo relacional. También asegura la restricción foránea hacia ShoppingList.

@Entity(tableName = "collaborator", foreignKeys = @ForeignKey( entity = ShoppingList.class, parentColumns = "id", childColumns = "shopping_list_id") ) public class Collaborator { @NonNull @PrimaryKey public String id; public String name; @ColumnInfo(name = "shopping_list_id") public String shoppingListId; public Collaborator(@NonNull String id, String name, String shoppingListId) { this.id = id; this.name = name; this.shoppingListId = shoppingListId; } }
Lenguaje del código: Java (java)

Luego añade la entidad a la lista de @Database y aumenta la versión a 5.

@Database(entities = {ShoppingList.class, Info.class, Collaborator.class}, version = 5, exportSchema = false) public abstract class ShoppingListDatabase extends RoomDatabase { }
Lenguaje del código: Java (java)

2. Relacionar ShoppingList Y Collaborator

Crea otra clase para la relación de la lista de compras y colaboradores llamada ShoppingListWithCollaborators.

Ya que deseas solo el nombre del colaborador en el resultado, aplica una proyección desplegable con la columna collaborator.name.

Y no olvides incluir la relación 1:1 con Info que creaste en el tutorial anterior.

public class ShoppingListWithCollaborators { @Embedded public ShoppingListForList shoppingList; @Relation( entity = Collaborator.class, parentColumn = "id", entityColumn = "shopping_list_id", projection = {"name"} ) public List<String> collaboratorNames; @Relation( entity = Info.class, parentColumn = "id", entityColumn = "shopping_list_id", projection = {"created_date"} ) public String createdDate; }
Lenguaje del código: Java (java)

3. Obtener Resultados (1:*) En El DAO

Ahora ve a ShoppingListDao y actualiza los métodos getAll() y getShoppinhListsByCategories() para que retornen la entidad de relaciones que creaste.

@Transaction @Query("SELECT id, name, is_favorite FROM shopping_list") abstract LiveData<List<ShoppingListWithCollaborators>> getAll(); @Transaction @Query("SELECT id, name, is_favorite FROM shopping_list WHERE category IN(:categories)") abstract LiveData<List<ShoppingListWithCollaborators>> getShoppingListsByCategories(List<String> categories);
Lenguaje del código: Java (java)

Cuando termines estas acciones deberás actualizar el Adaptador, ViewModel y Repositorio para que acepten el nuevo tipo en sus métodos.

4. Actualizar Layout Del Item

Para que el layout quede igual al prototipo es necesario que agregues dos TextViews a shopping_list_item.xml.

Diseño UI para lista de compras con colaboradores

Usa la siguiente definición XML:

<?xml version="1.0" encoding="utf-8"?> <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_marginBottom="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_height="wrap_content"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="144dp" android:padding="@dimen/normal_padding"> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?attr/textAppearanceHeadline6" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/favorite_button" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.0" tools:text="Lista de ejemplo" /> <TextView android:id="@+id/created_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?textAppearanceCaption" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/name" app:layout_constraintVertical_bias="0.0" tools:text="26/05/2020 01:12:54" /> <com.google.android.material.checkbox.MaterialCheckBox android:id="@+id/favorite_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginRight="8dp" android:button="@drawable/sl_favorite_24" android:minWidth="0dp" android:minHeight="0dp" app:buttonTint="@color/favorite_color" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/delete_button" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.0" /> <ImageView android:id="@+id/delete_button" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.0" app:srcCompat="@drawable/ic_delete_24" /> <TextView android:id="@+id/collaborators_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="@string/collaborators_label" android:textAllCaps="true" android:textAppearance="?textAppearanceCaption" app:layout_constraintBottom_toTopOf="@+id/collaborator_names" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/created_date" app:layout_constraintVertical_bias="1.0" /> <TextView android:id="@+id/collaborator_names" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?textAppearanceBody1" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" tools:text="Cesar, Ramiro, Cristina" /> </androidx.constraintlayout.widget.ConstraintLayout> </com.google.android.material.card.MaterialCardView>
Lenguaje del código: HTML, XML (xml)

5. Bindear Colaboradores En Adaptador

Ahora muestra los nombres de los colaboradores en un String separado por comas en el ViewHolder del adaptador.

public class ShoppingListViewHolder extends RecyclerView.ViewHolder { private final TextView mNameText; private final CheckBox mFavoriteButton; private final ImageView mDeleteButton; private TextView mCreatedDateText; private TextView mCollaboratorsText; public ShoppingListViewHolder(@NonNull View itemView) { super(itemView); mNameText = itemView.findViewById(R.id.name); mCreatedDateText = itemView.findViewById(R.id.created_date); mFavoriteButton = itemView.findViewById(R.id.favorite_button); mDeleteButton = itemView.findViewById(R.id.delete_button); mCollaboratorsText = itemView.findViewById(R.id.collaborator_names); // Setear eventos mFavoriteButton.setOnClickListener(this::manageEvents); mDeleteButton.setOnClickListener(this::manageEvents); itemView.setOnClickListener(this::manageEvents); } private void manageEvents(View view) { if (mItemListener != null) { ShoppingListWithCollaborators clickedItem = mShoppingLists.get(getAdapterPosition()); // Manejar evento de click en Favorito if (view.getId() == R.id.favorite_button) { mItemListener.onFavoriteIconClicked(clickedItem); return; } else if (view.getId() == R.id.delete_button) { mItemListener.onDeleteIconClicked(clickedItem); return; } mItemListener.onClick(clickedItem); } } public void bind(ShoppingListWithCollaborators item) { mNameText.setText(item.shoppingList.name); mFavoriteButton.setChecked(item.shoppingList.favorite); mCreatedDateText.setText(item.createdDate); mCollaboratorsText.setText(TextUtils.join(",", item.collaboratorNames)); } }
Lenguaje del código: Java (java)

6. Insertar Colaboradores

Modifica el método del DAO de la inserción de listas de compras para que acepte una lista colaboradores.

@Transaction public void insertWithInfoAndCollaborators(ShoppingListInsert shoppingList, Info info, List<Collaborator> collaborators) { insertShoppingList(shoppingList); insertInfo(info); insertAllCollaborators(collaborators); } @Transaction public void insertAllWithInfosAndCollaborators(List<ShoppingListInsert> shoppingLists, List<Info> infos, List<Collaborator> collaborators) { insertAll(shoppingLists); insertAllInfos(infos); insertAllCollaborators(collaborators); } @Insert(onConflict = OnConflictStrategy.IGNORE) protected abstract void insertAllCollaborators(List<Collaborator> collaborators);
Lenguaje del código: PHP (php)

Seguido, abre ShoppingListDatabase e inserta cinco colaboradores en la escucha de apertura de la base de datos.

// Prepoblar base de datos con callback private static final RoomDatabase.Callback mRoomCallback = new Callback() { @Override public void onOpen(@NonNull SupportSQLiteDatabase db) { super.onCreate(db); dbExecutor.execute(this::prepopulate); } public void prepopulate() { ShoppingListDao dao = INSTANCE.shoppingListDao(); List<ShoppingListInsert> lists = new ArrayList<>(); List<Info> infos = new ArrayList<>(); List<Collaborator> collaborators = new ArrayList<>(); for (int i = 0; i < 5; i++) { String dummyId = String.valueOf((i + 1)); // Crear lista de compras ShoppingListInsert shoppingList = new ShoppingListInsert( dummyId, "Lista " + (i + 1) ); // Crear info String date = Utils.getCurrentDate(); Info info = new Info( shoppingList.id, date, date); // Crear colaborador Collaborator collaborator = new Collaborator(dummyId, "Colaborador " + dummyId, dummyId); lists.add(shoppingList); infos.add(info); collaborators.add(collaborator); } dao.insertAllWithInfosAndCollaborators(lists, infos, collaborators); } };
Lenguaje del código: Java (java)

Ya finalizando, ejecuta el aplicativo. Deberás ver la siguiente imagen:

Relaciones Uno A Muchos Con Room

Siguiente tutorial: Relaciones Muchos A Muchos Con Room

¿Ha sido útil esta publicación?

ANUNCIO
Loading...