我怎样才能代表Android Room的多对多关系?

bon*_*ond 43 android kotlin android-room android-architecture-components

我怎样才能代表与房间的多对多关系?例如,我有"访客"和"预订".预订可以有很多客人,客人可以参加许多预订.

这是我的实体定义:

@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
)
Run Code Online (Sandbox Code Playgroud)

在调查我遇到的文档时@Relation.我发现它真的很混乱.

根据这个,我想创建一个POJO并在那里添加关系.所以,以我的例子,我做了以下

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

上面我得到编译器错误:

无法弄清楚如何从游标中读取此字段.

我无法找到工作样本@Relation.

Dev*_*rim 71

我有类似的问题.这是我的解决方案.

您可以使用一个额外的实体(ReservationGuest),它保留之间的关系GuestReservation.

@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
)
Run Code Online (Sandbox Code Playgroud)

您可以使用他们的guestIds 列表进行预订.(不是客人对象)

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

您还可以获取客人的reservationIds 列表.(不是预订对象)

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

既然你可以得到guestIds和reservationIds,你就可以查询ReservationGuest实体.

如果我找到一种简单的方法来获取Reservation和Guest对象列表而不是它们的id,我会更新我的答案.

类似的答案

  • 你有没有找到一种方法来获取整个对象而不是id?我担心在具有M:N关系的单个查询中无法实现这一点. (17认同)
  • 您是否在示例应用程序中有一个工作实现或类似的东西?那简直太好了! (3认同)

Nis*_*hal 42

通过介绍Junction in room,您可以轻松处理多对多关系。

正如@Devrim 所说,您可以使用一个额外的实体 (ReservationGuest) 来保持 Guest 和 Reservation 之间的关系(也称为关联表或连接表或连接表)。

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

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

@Entity(
  primaryKeys = ["reservationId", "guestId"]
)
data class ReservationGuest(     
  val reservationId: Long,
  val guestId: Long
)
Run Code Online (Sandbox Code Playgroud)

现在您可以使用此模型向客人预订:

data class ReservationWithGuests (
    @Embedded
    val reservation: Reservation,
    @Relation(
            parentColumn = "rId",
            entity = Guest::class,
            entityColumn = "gId",
            associateBy = Junction(
                    value = ReservationGuest::class,
                    parentColumn = "reservationId",
                    entityColumn = "guestId"
            )
    )
    val guests: List<Guest>
)
Run Code Online (Sandbox Code Playgroud)

您还可以让客人获得他们的预订清单。

data class GuestWithReservations (
  @Embedded
  val guest: Guest,
  @Relation(
        parentColumn = "gId",
        entity = Reservation::class,
        entityColumn = "rId",
        associateBy = Junction(
                value = ReservationGuest::class,
                parentColumn = "guestId",
                entityColumn = "reservationId"
        )
  )
  val reservations: List<Reservation>
)
Run Code Online (Sandbox Code Playgroud)

现在您可以查询数据库的结果为:

@Dao
interface GuestReservationDao {
  @Query("SELECT * FROM Reservation")
  fun getReservationWithGuests(): LiveData<List<ReservationWithGuests>>

  @Query("SELECT * FROM Guest")
  fun getGuestWithReservations(): LiveData<List<GuestWithReservations>>

}
Run Code Online (Sandbox Code Playgroud)

  • 从现在开始,这应该被认为是公认的答案,Junctions 解决了这个问题+你得到了整个对象,而不仅仅是 ID。这也是Android文档提供的解决方案。如果您使用 Java,则必须使用 @Junction 注释。https://developer.android.com/training/data-storage/room/relationships#many-to-many。 (7认同)
  • 这个答案比当前的官方文档更有用,因为文档在实体之间重复相同的字段名称(songId 和 playlistId),所以有点令人困惑。谢谢! (2认同)

Nom*_*sta 10

实际上,还有另一种获取Guest列表的可能性,不仅是@Devrim答案中的id 。

先定义类将代表之间的连接GuestReservation

@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
)
Run Code Online (Sandbox Code Playgroud)

每次插入new时Reservation,都必须插入ReservationGuestJoinobject才能满足外键约束。现在,如果要获取Guest列表,则可以使用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>
}
Run Code Online (Sandbox Code Playgroud)

要查看更多详细信息,请访问此博客


Ant*_*ony 7

这是一种通过单个查询中的M:N联结表查询完整对象模型的方法.子查询可能不是最有效的方法,但它确实有效,直到他们@Relation正确地完成ForeignKey. 我将Guest/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
)
Run Code Online (Sandbox Code Playgroud)

Dao(注意我们通过子查询拉入M:N并Reservation用a 减少额外的行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();
Run Code Online (Sandbox Code Playgroud)

GuestResult(它处理查询结果的映射,请注意我们将连接的字符串转换回列表@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(","));
    }
}
Run Code Online (Sandbox Code Playgroud)