将嵌入式实体与一对多实体组合

Combining embedded entities with one-to-many entities

我的运动情况如下:
每场比赛只有 2 支球队。每支球队都有很多球员。
由于我需要知道哪个团队是第一个,哪个团队是第二个,所以我创建了这样的实体:

@Entity
data class MatchEntity(
    @PrimaryKey
    val matchId: String,
    @Embedded(prefix = "firstTeam_")
    val firstTeam: TeamDetailedWithPlayers,
    @Embedded(prefix = "secondTeam_")
    val secondTeam: TeamDetailedWithPlayers,
)

data class TeamDetailedWithPlayers(
    @Embedded
    val team: TeamDetailedEntity,
    @Relation(
        parentColumn = "id",
        entityColumn = "teamId",
    )
    val players: List<PlayerEntity>
)

@Entity
data class TeamDetailedEntity(
    @PrimaryKey
    val id: String,
    /* ... */
)

@Entity
data class PlayerEntity(
    @PrimaryKey
    val id: String,
    val teamId: String,
    /* ... */
)

现在,当我尝试构建它时,由于前缀,我收到以下错误:
Cannot find the parent entity column `id` in TeamDetailedWithPlayers. Options: firstTeam_id, ...

有没有任何(其他)方法可以优雅地解决这个问题,而不涉及 MatchEntity 内部团队的显式外键?

我相信您正在混合应该是 POJO 的东西和应该是实体的东西,并且基本上是在尝试存储重复数据和存储需要不必要的类型转换器的数据。

  • 如果使用了@Entity那么它定义了一个table,如果没有那么它就是一个POJO

作为一个例子,Match 应该有一个 matchId 和可能的其他比赛特定数据以及对第一和第二球队的引用,而不是试图嵌入和复制整个时间信息,因此比赛实体中的球员。

所以 table(实体)你想要一个 Match table 引用 2 支球队中的每一支球队,一支球队 table 和一名球员 table对玩家所在团队的引用。

然后您就有了 POJO,例如 TeamWithPlayers 和 MatchWithTeams。例如对于 table 结构(Schema)你可以有:-

  • 注意 id的使用Longs(效率更高)
  • 注意 id 名称是唯一的而不仅仅是 id(重复的列会导致列不明确的问题)

团队实体

@Entity
data class TeamEntity(
    @PrimaryKey
    val teamId: Long,
    val teamName: String
    /*
        other Team detail here
     */
)

玩家实体

@Entity
data class PlayerEntity(
    @PrimaryKey
    val playerId: Long,
    @ForeignKey(entity = TeamEntity::class,parentColumns = ["teamId"],childColumns = ["teamReference"])
    val teamReference: Long,
    val playerName: String
    /*
        other player info here
     */
)

匹配实体

@Entity
data class MatchEntity(
    @PrimaryKey
    val matchId: Long,
    @ForeignKey(entity = TeamEntity::class, parentColumns = ["matchId"],childColumns = ["firstTeamReference"])
    val firstTeamReference: Long,
    @ForeignKey(entity = TeamEntity::class, parentColumns = ["matchId"],childColumns = ["secondTeamReference"])
    val secondTeamReference: Long
)

没有任何 POJO 的这将创建一个看起来像(通过数据库检查器)的数据库:-

  • 可以看出编译成功

然后您可以使用简单的 Dao 插入一些测试数据,然后处理 Dao 和 POJO 以获得您想要的数据。

Team 的 POJO 示例,玩家列表为 TeamWithPlayers :-

data class TeamWithPlayers(
    @Embedded
    val teamEntity: TeamEntity,
    @Relation(entity = PlayerEntity::class,parentColumn = "teamId",entityColumn = "teamReference")
    val playerlist: List<PlayerEntity>
)

与 2 个 TeamWithPlayers 匹配的 POJO 示例:-

data class MatchWithTeamsWithPlayers(
    @Embedded
    val matchEntity: MatchEntity,
    @Relation(entity = TeamEntity::class, parentColumn = "firstTeamReference",entityColumn = "teamId")
    val firstTeam: List<TeamWithPlayers>,
    @Relation(entity = TeamEntity::class,parentColumn = "secondTeamReference",entityColumn = "teamId")
    val secondTeam: List<TeamWithPlayers>
)

工作示例

首先是道在AllDao

@Dao
interface AllDao {

    @Insert
    fun insertTeamEntity(teamEntity: TeamEntity): Long
    @Insert
    fun insertPlayerEntity(playerEntity: PlayerEntity): Long
    @Insert
    fun insertMatchEntity(matchEntity: MatchEntity): Long
    @Query("SELECT count(*) FROM teamentity")
    fun getTeamCount(): Long
    @Query("SELECT * FROM teamentity")
    fun getAllTeamsWithPlayers(): List<TeamWithPlayers>
    @Query("SELECT * FROM matchEntity")
    fun getAllMatchesWithTeamdsWithPlayers(): List<MatchWithTeamsWithPlayers>
}

接下来是数据库 SportsDatabase :-

@Database(entities = [TeamEntity::class,PlayerEntity::class,MatchEntity::class],version = 1)
abstract class SportsDatabase: RoomDatabase() {
    abstract fun getDao(): AllDao
}

最终通过 Activity MainActivity 进行测试(见评论)

  • 为了方便和简洁,请注意在主线程上进行的测试

:-

class MainActivity : AppCompatActivity() {
    lateinit var db: SportsDatabase
    lateinit var dao: AllDao
    val TAG = "SPORTDBINFO"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // STAGE 1 create and populate the data base using just the core schema no POJO's
        db = Room.databaseBuilder(this,SportsDatabase::class.java,"mysports.db")
            .allowMainThreadQueries()
            .build()
        dao = db.getDao()


        // Add some testing data but only if none exists (any Team row existing as the check)
        if (dao.getTeamCount() < 1) {
            
            // 3 teams
            dao.insertTeamEntity(TeamEntity(1, "Team 1"))
            dao.insertTeamEntity(TeamEntity(2, "Team 2"))
            dao.insertTeamEntity(TeamEntity(3, "Team 3"))

            // 3 players per team except team 3 with 2 playes
            dao.insertPlayerEntity(PlayerEntity(100, 1, "Player 100 in Team 1"))
            dao.insertPlayerEntity(PlayerEntity(101, 1, "Player 101 in Team 1"))
            dao.insertPlayerEntity(PlayerEntity(102, 1, "Player 102 in Team 1"))
            dao.insertPlayerEntity(PlayerEntity(200, 2, "Player 200 in Team 2"))
            dao.insertPlayerEntity(PlayerEntity(201, 2, "Player 201 in Team 2"))
            dao.insertPlayerEntity(PlayerEntity(202, 2, "Player 202 in Team 2"))
            dao.insertPlayerEntity(PlayerEntity(300, 3, "Player 300 in Team 3"))
            dao.insertPlayerEntity(PlayerEntity(301, 3, "Player 301 in Team 3"))

            // some matches each team plays each team home and away i.e. 6 matches
            dao.insertMatchEntity(MatchEntity(1000, 1, 2))
            dao.insertMatchEntity(MatchEntity(1010, 2, 3))
            dao.insertMatchEntity(MatchEntity(1020, 1, 3))
            dao.insertMatchEntity(MatchEntity(500, 3, 1))
            dao.insertMatchEntity(MatchEntity(510, 3, 2))
            dao.insertMatchEntity(MatchEntity(520, 2, 3))
        }

        // Stage 2 check the use of the TeamWithPlayers POJO using the getAllTeamWithPlayers PJOJO
        for (team: TeamWithPlayers in dao.getAllTeamsWithPlayers()) {
            logTeam( team.teamEntity)
            for (player: PlayerEntity in team.playerlist) {
                logPlayer(player)
            }
        }

        // Stage 3 check the use of the MatchWithTeamsWithPlayers POJO
        for(match: MatchWithTeamsWithPlayers in dao.getAllMatchesWithTeamdsWithPlayers()) {
            logMatch(match.matchEntity)
            // First team
            for(team: TeamWithPlayers in match.firstTeam) {
                logTeam(team.teamEntity)
                // players in the first team
                for(player: PlayerEntity in team.playerlist) {
                    logPlayer(player)
                }
            }
            // Second team
            for(team: TeamWithPlayers in match.secondTeam) {
                logTeam(team.teamEntity)
                // players in the second team
                for(player: PlayerEntity in team.playerlist) {
                    logPlayer(player)
                }
            }
        }
    }

    private fun logPlayer(playerEntity: PlayerEntity) {
        Log.d(TAG,"\t\tPlayer Name = " + playerEntity.playerName + " PlayerID =" + playerEntity.playerId + " TeamReference = " + playerEntity.teamReference)
    }
    private fun logTeam(teamEntity: TeamEntity) {
        Log.d(TAG,"\tTeam Name =" + teamEntity.teamName + " TeamID = " + teamEntity.teamId)
    }
    private fun logMatch(matchEntity: MatchEntity) {
        Log.d(TAG,"Match ID = " + matchEntity.matchId + " Team 1 reference = " + matchEntity.firstTeamReference + " Team 2 reference = " + matchEntity.secondTeamReference)
    }
}

结果 即输出到日志

2021-04-11 13:26:06.555 D/SPORTDBINFO:  Team Name =Team 1 TeamID = 1
2021-04-11 13:26:06.555 D/SPORTDBINFO:      Player Name = Player 100 in Team 1 PlayerID =100 TeamReference = 1
2021-04-11 13:26:06.555 D/SPORTDBINFO:      Player Name = Player 101 in Team 1 PlayerID =101 TeamReference = 1
2021-04-11 13:26:06.555 D/SPORTDBINFO:      Player Name = Player 102 in Team 1 PlayerID =102 TeamReference = 1
2021-04-11 13:26:06.555 D/SPORTDBINFO:  Team Name =Team 2 TeamID = 2
2021-04-11 13:26:06.555 D/SPORTDBINFO:      Player Name = Player 200 in Team 2 PlayerID =200 TeamReference = 2
2021-04-11 13:26:06.555 D/SPORTDBINFO:      Player Name = Player 201 in Team 2 PlayerID =201 TeamReference = 2
2021-04-11 13:26:06.555 D/SPORTDBINFO:      Player Name = Player 202 in Team 2 PlayerID =202 TeamReference = 2
2021-04-11 13:26:06.555 D/SPORTDBINFO:  Team Name =Team 3 TeamID = 3
2021-04-11 13:26:06.555 D/SPORTDBINFO:      Player Name = Player 300 in Team 3 PlayerID =300 TeamReference = 3
2021-04-11 13:26:06.555 D/SPORTDBINFO:      Player Name = Player 301 in Team 3 PlayerID =301 TeamReference = 3




2021-04-11 13:26:06.562 D/SPORTDBINFO: Match ID = 500 Team 1 reference = 3 Team 2 reference = 1
2021-04-11 13:26:06.562 D/SPORTDBINFO:  Team Name =Team 3 TeamID = 3
2021-04-11 13:26:06.562 D/SPORTDBINFO:      Player Name = Player 300 in Team 3 PlayerID =300 TeamReference = 3
2021-04-11 13:26:06.562 D/SPORTDBINFO:      Player Name = Player 301 in Team 3 PlayerID =301 TeamReference = 3
2021-04-11 13:26:06.562 D/SPORTDBINFO:  Team Name =Team 1 TeamID = 1
2021-04-11 13:26:06.562 D/SPORTDBINFO:      Player Name = Player 100 in Team 1 PlayerID =100 TeamReference = 1
2021-04-11 13:26:06.562 D/SPORTDBINFO:      Player Name = Player 101 in Team 1 PlayerID =101 TeamReference = 1
2021-04-11 13:26:06.562 D/SPORTDBINFO:      Player Name = Player 102 in Team 1 PlayerID =102 TeamReference = 1
2021-04-11 13:26:06.562 D/SPORTDBINFO: Match ID = 510 Team 1 reference = 3 Team 2 reference = 2
2021-04-11 13:26:06.563 D/SPORTDBINFO:  Team Name =Team 3 TeamID = 3
2021-04-11 13:26:06.563 D/SPORTDBINFO:      Player Name = Player 300 in Team 3 PlayerID =300 TeamReference = 3
2021-04-11 13:26:06.563 D/SPORTDBINFO:      Player Name = Player 301 in Team 3 PlayerID =301 TeamReference = 3
2021-04-11 13:26:06.563 D/SPORTDBINFO:  Team Name =Team 2 TeamID = 2
2021-04-11 13:26:06.563 D/SPORTDBINFO:      Player Name = Player 200 in Team 2 PlayerID =200 TeamReference = 2
2021-04-11 13:26:06.563 D/SPORTDBINFO:      Player Name = Player 201 in Team 2 PlayerID =201 TeamReference = 2
2021-04-11 13:26:06.563 D/SPORTDBINFO:      Player Name = Player 202 in Team 2 PlayerID =202 TeamReference = 2
2021-04-11 13:26:06.563 D/SPORTDBINFO: Match ID = 520 Team 1 reference = 2 Team 2 reference = 3
2021-04-11 13:26:06.563 D/SPORTDBINFO:  Team Name =Team 2 TeamID = 2
2021-04-11 13:26:06.563 D/SPORTDBINFO:      Player Name = Player 200 in Team 2 PlayerID =200 TeamReference = 2
2021-04-11 13:26:06.563 D/SPORTDBINFO:      Player Name = Player 201 in Team 2 PlayerID =201 TeamReference = 2
2021-04-11 13:26:06.564 D/SPORTDBINFO:      Player Name = Player 202 in Team 2 PlayerID =202 TeamReference = 2
2021-04-11 13:26:06.564 D/SPORTDBINFO:  Team Name =Team 3 TeamID = 3
2021-04-11 13:26:06.564 D/SPORTDBINFO:      Player Name = Player 300 in Team 3 PlayerID =300 TeamReference = 3
2021-04-11 13:26:06.564 D/SPORTDBINFO:      Player Name = Player 301 in Team 3 PlayerID =301 TeamReference = 3
2021-04-11 13:26:06.564 D/SPORTDBINFO: Match ID = 1000 Team 1 reference = 1 Team 2 reference = 2
2021-04-11 13:26:06.564 D/SPORTDBINFO:  Team Name =Team 1 TeamID = 1
2021-04-11 13:26:06.564 D/SPORTDBINFO:      Player Name = Player 100 in Team 1 PlayerID =100 TeamReference = 1
2021-04-11 13:26:06.564 D/SPORTDBINFO:      Player Name = Player 101 in Team 1 PlayerID =101 TeamReference = 1
2021-04-11 13:26:06.564 D/SPORTDBINFO:      Player Name = Player 102 in Team 1 PlayerID =102 TeamReference = 1
2021-04-11 13:26:06.564 D/SPORTDBINFO:  Team Name =Team 2 TeamID = 2
2021-04-11 13:26:06.564 D/SPORTDBINFO:      Player Name = Player 200 in Team 2 PlayerID =200 TeamReference = 2
2021-04-11 13:26:06.565 D/SPORTDBINFO:      Player Name = Player 201 in Team 2 PlayerID =201 TeamReference = 2
2021-04-11 13:26:06.565 D/SPORTDBINFO:      Player Name = Player 202 in Team 2 PlayerID =202 TeamReference = 2
2021-04-11 13:26:06.565 D/SPORTDBINFO: Match ID = 1010 Team 1 reference = 2 Team 2 reference = 3
2021-04-11 13:26:06.565 D/SPORTDBINFO:  Team Name =Team 2 TeamID = 2
2021-04-11 13:26:06.565 D/SPORTDBINFO:      Player Name = Player 200 in Team 2 PlayerID =200 TeamReference = 2
2021-04-11 13:26:06.565 D/SPORTDBINFO:      Player Name = Player 201 in Team 2 PlayerID =201 TeamReference = 2
2021-04-11 13:26:06.565 D/SPORTDBINFO:      Player Name = Player 202 in Team 2 PlayerID =202 TeamReference = 2
2021-04-11 13:26:06.565 D/SPORTDBINFO:  Team Name =Team 3 TeamID = 3
2021-04-11 13:26:06.565 D/SPORTDBINFO:      Player Name = Player 300 in Team 3 PlayerID =300 TeamReference = 3
2021-04-11 13:26:06.565 D/SPORTDBINFO:      Player Name = Player 301 in Team 3 PlayerID =301 TeamReference = 3
2021-04-11 13:26:06.565 D/SPORTDBINFO: Match ID = 1020 Team 1 reference = 1 Team 2 reference = 3
2021-04-11 13:26:06.565 D/SPORTDBINFO:  Team Name =Team 1 TeamID = 1
2021-04-11 13:26:06.566 D/SPORTDBINFO:      Player Name = Player 100 in Team 1 PlayerID =100 TeamReference = 1
2021-04-11 13:26:06.566 D/SPORTDBINFO:      Player Name = Player 101 in Team 1 PlayerID =101 TeamReference = 1
2021-04-11 13:26:06.566 D/SPORTDBINFO:      Player Name = Player 102 in Team 1 PlayerID =102 TeamReference = 1
2021-04-11 13:26:06.566 D/SPORTDBINFO:  Team Name =Team 3 TeamID = 3
2021-04-11 13:26:06.566 D/SPORTDBINFO:      Player Name = Player 300 in Team 3 PlayerID =300 TeamReference = 3
2021-04-11 13:26:06.566 D/SPORTDBINFO:      Player Name = Player 301 in Team 3 PlayerID =301 TeamReference = 3