65 votes

Comment puis-je représenter plusieurs relations avec Android Room?

Comment puis-je représenter un nombre à beaucoup de rapport avec la Pièce? par exemple, j'ai "Invité" et "Réservation". La réservation peut avoir de nombreux Invités et les Hôtes peuvent faire partie de plusieurs Réserves.

Voici mon entité définitions:

@Entity data class Reservation(
    @PrimaryKey val id: Long,
    val table: String,
    val guests: List<Guest>
)

@Entity data class Guest(
    @PrimaryKey val id: Long,
    val name: String,
    val email: String
)

Tout en regardant dans les docs, je suis tombé sur @Relation. J'ai trouvé ça vraiment bien que déroutante.

En fonction de cela, je veux créer un POJO et ajoutez les relations de là. Donc, dans mon exemple je n'ai la suite

data class ReservationForGuest(
    @Embedded val reservation: Reservation,
    @Relation(
        parentColumn = "reservation.id", 
        entityColumn = "id", 
        entity = Guest::class
    ) val guestList: List<Guest>
)

Ci-dessus j'obtiens l'erreur de compilation:

Ne peut pas comprendre comment lire ce domaine à partir d'un curseur.

Je n'ai pas pu trouver un exemple de travail d' @Relation.

91voto

aegean Points 3854

J'ai eu un problème similaire. Voici ma solution.

Vous pouvez utiliser une autre entité (ReservationGuest) qui maintient la relation entre Guest et Reservation.

@Entity data class Guest(
    @PrimaryKey val id: Long,
    val name: String,
    val email: String
)

@Entity data class Reservation(
    @PrimaryKey val id: Long,
    val table: String
)

@Entity data class ReservationGuest(
    @PrimaryKey(autoGenerate = true) val id: Long,
    val reservationId: Long,
    val guestId: Long
)

Vous pouvez obtenir des réservations avec leur liste d' guestIds. (Pas l'invité d'objets)

data class ReservationWithGuests(
    @Embedded val reservation:Reservation,
    @Relation(
        parentColumn = "id",
        entityColumn = "reservationId",
        entity = ReservationGuest::class,
        projection = "guestId"
    ) val guestIdList: List<Long>
)

Vous pouvez également obtenir des clients avec leur liste d' reservationIds. (Pas la réservation des objets)

data class GuestWithReservations(
    @Embedded val guest:Guest,
    @Relation(
        parentColumn = "id",
        entityColumn = "guestId",
        entity = ReservationGuest::class,
        projection = "reservationId"
   ) val reservationIdList: List<Long>
)

Puisque vous pouvez obtenir de l' guestIds et reservationIds, vous pouvez interroger Reservation et Guest entités avec ceux-ci.

Je vais mettre à jour ma réponse si je trouve un moyen facile de récupérer de Réservation et Invité liste d'objets au lieu de leur id.

Similaire répondre

20voto

Nominalista Points 864

En fait, il ya une possibilité d'obtenir de l' Guest la liste, non seulement l'id est comme dans @Devrim réponse.

Tout d'abord, définir la classe qui représente la connexion entre Guest et Reservation.

@Entity(primaryKeys = ["reservationId", "guestId"],
        foreignKeys = [
            ForeignKey(entity = Reservation::class,
                    parentColumns = ["id"],
                    childColumns = ["reservationId"]),
            ForeignKey(entity = Guest::class,
                    parentColumns = ["id"],
                    childColumns = ["guestId"])
        ])
data class ReservationGuestJoin(
    val reservationId: Long,
    val guestId: Long
)

Chaque fois que vous serez l'insertion de nouveaux Reservation, vous devrez insérer ReservationGuestJoin objet afin de répondre à la contrainte de clé étrangère. Et maintenant, si vous voulez obtenir de l' Guest la liste, vous pouvez utiliser la puissance de la requête SQL:

@Dao
interface ReservationGuestJoinDao {

    @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
    @Query("""
        SELECT * FROM guest INNER JOIN reservationGuestJoin ON
        guest.id = reservationGuestJoin.guestId WHERE
        reservationGuestJoin.reservationId = :reservationId
        """)
    fun getGuestsWithReservationId(reservationId: Long): List<Guest>
}

Pour voir plus de détails visitez ce blog.

9voto

Anthony Points 1697

Voici une façon d'interroger un modèle d'objet complet par l'intermédiaire d'un M:N table de jonction dans une seule requête. Les sous-requêtes ne sont probablement pas le moyen le plus efficace pour ce faire, mais il fonctionne jusqu'à ce qu'ils obtiennent @Relation de bien marcher à travers ForeignKey. J'ai la main bloquée par le client/Réservation cadre dans mon code de travail, donc il y a peut être des fautes de frappe.

Entité (Ce qui a été couverte)

@Entity data class Guest(
    @PrimaryKey val id: Long,
    val name: String,
    val email: String
)

@Entity data class Reservation(
    @PrimaryKey val id: Long,
    val table: String
)

@Entity data class ReservationGuest(
    @PrimaryKey(autoGenerate = true) val id: Long,
    val reservationId: Long,
    val guestId: Long
)

Dao (Note nous tirer dans les M:N par l'intermédiaire d'une sous-requête et de réduire les extra - Reservation des lignes avec un GROUP_CONCAT

@Query("SELECT *, " +
            "(SELECT GROUP_CONCAT(table) " +
                "FROM ReservationGuest " +
                "JOIN Reservation " +
                "ON Reservation.id = ReservationGuest.reservationId " +
                "WHERE ReservationGuest.guestId = Guest.id) AS tables, " +
        "FROM guest")
abstract LiveData<List<GuestResult>> getGuests();

GuestResult (Cela gère la cartographie du résultat de la requête, la note que nous convertir la chaîne concaténée retour à une liste @TypeConverter)

@TypeConverters({ReservationResult.class})
public class GuestResult extends Guest {
    public List<String> tables;

    @TypeConverter
    public List<String> fromGroupConcat(String reservations) {
        return Arrays.asList(reservations.split(","));
    }
}

-1voto

Allan Veloso Points 881

Pour la table de jointure entité, je suggère l'utilisation d'un composite ID indexé:

@Entity(
    primaryKeys = ["reservationId", "guestId"],
    indices = [Index(value =["reservationId", "guestId"], unique = true)]
)
data class ReservationGuestJoin(
    @PrimaryKey(autoGenerate = true) var id: Long,
    var reservationId: Long = 0,
    var guestId: Long = 0
)

Le GuestDao.kt:

@Dao
@TypeConverters(GuestDao.Converters::class)
interface GuestDao {

    @Query(QUERY_STRING)
    fun listWithReservations(): LiveData<List<GuestWithReservations>>

    data class GuestWithReservations(
        var id: Long? = null,
        var name: String? = null,
        var email: String? = null,
        var reservations: List<Reservation> = emptyList()
    )

    class Converters{
        @TypeConverter
        fun listReservationFromConcatString(value: String?): List<Reservation>? = value?.let { value ->
                .split("^^")
                .map { it.split("^_") }
                .map { Reservation(id = it.getOrNull(0)?.toLongOrNull(), name = it.getOrNull(1)) }
        } ?: emptyList()
    }
}

L' QUERY_STRING. Nous faisons un jointures internes pour produire une grande table avec des données provenant de deux entités, nous concaténer les données à partir d' Reservation comme une colonne de chaîne et enfin nous group_concat les lignes par le code d'invité, la concaténation de la réservation chaînes avec différents séparateurs, notre convertisseur prendra soin de le reconstruire comme une entité:

SELECT 
    t.id, t.name, t.email, GROUP_CONCAT(t.reservation, '^^') as reservations 
FROM (
    SELECT 
        guestId as id, name, email, (reservationId || '^_' || reservationTable) as reservation 
    FROM  
        GuestReservationJoin
        INNER JOIN Guest ON Guest.id = GuestReservationJoin.guestId 
        INNER JOIN Reservation ON Reservation.id = GuestReservationJoin.reservationId
    ) as t 
GROUP BY t.id

Notez que j'ai changé votre colonne table nom parce que je pense que la Salle ne vous permettent pas d'utiliser SQLite noms réservés.

Je n'ai pas tester les performances de tout cela par rapport à avoir plus de plat entité (une autre option sans les concaténations). Si je le fais, je vais mettre à jour ma réponse.

-1voto

andu Points 116

Basé sur la réponse ci-dessus: https://stackoverflow.com/a/44428451/4992598 seulement en conservant séparément les noms de champ entre les entités vous pouvez avoir des modèles de retour (et pas seulement les id). Tout ce que vous devez faire est de:

@Entity data class ReservationGuest(
    @PrimaryKey(autoGenerate = true) val id: Long,
    val reservationId: Long,
    @Embedded
    val guest: Guest
)

Et oui, les entités peuvent être incorporés dans l'un de l'autre tant que vous ne gardez pas des champs en double. Donc, en conséquence, le ReservationWithGuests classe peut ressembler à ceci.

data class ReservationWithGuests(
    @Embedded val reservation:Reservation,
    @Relation(
        parentColumn = "id",
        entityColumn = "reservationId",
        entity = ReservationGuest::class,
        projection = "guestId"
    ) val guestList: List<Guest>
)

Donc, à ce stade, vous pouvez utiliser le val guestIdList: Liste parce que votre ReservationGuest entité fait les cartes id avec des modèles d'entité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X