Micronaut Data JDBC查询Tournament时关联表未加载问题求助
解决方案
你的问题核心是Micronaut Data JDBC默认不会自动加载关联实体,默认的CrudRepository方法仅查询主表数据。以下是几种可行的解决方式:
方案1:修改关联关系的FetchType为EAGER
直接将所有需要立即加载的关联字段的fetch属性设置为FetchType.EAGER,强制查询时加载关联数据:
修改TournamentEntity:
@MappedEntity(value = "tournament") data class TournamentEntity( @field:Id @GeneratedValue val id: Int? = null, @JoinTable(name = "tournament_player", joinColumns = [JoinColumn(name = "tournament_id", referencedColumnName = "id")], inverseJoinColumns = [JoinColumn(name = "player_id", referencedColumnName = "id")] ) @ManyToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER) val players: List<PlayerEntity>, @OneToMany(mappedBy = "tournament", orphanRemoval = true, cascade = [CascadeType.ALL], fetch = FetchType.EAGER) val tournamentRounds: List<TournamentRoundEntity>, )
修改TournamentRoundEntity的matches字段:
@OneToMany(mappedBy = "tournamentRound", orphanRemoval = true, cascade = [CascadeType.ALL], fetch = FetchType.EAGER) val matches: List<MatchEntity>,
修改MatchEntity的选手关联字段:
@OneToOne(fetch = FetchType.EAGER) @JoinColumn(name = "player_a_id", nullable = false, columnDefinition = "int") val playerA: PlayerEntity, @OneToOne(fetch = FetchType.EAGER) @JoinColumn(name = "player_b_id", nullable = false, columnDefinition = "int") val playerB: PlayerEntity,
注意:此方式会每次查询主实体时都加载所有关联数据,适合数据量较小的场景,避免大数量级下的性能问题。
方案2:自定义仓库查询,使用JOIN FETCH显式加载
通过自定义JPQL查询,使用JOIN FETCH一次性加载所有层级的关联数据,避免N+1查询:
@JdbcRepository(dialect = Dialect.MYSQL) @Transactional(Transactional.TxType.MANDATORY) interface TournamentRepository : CrudRepository<TournamentEntity, Int>, JpaSpecificationExecutor<TournamentEntity> { @Query("SELECT t FROM TournamentEntity t JOIN FETCH t.players JOIN FETCH t.tournamentRounds tr JOIN FETCH tr.matches m JOIN FETCH m.playerA JOIN FETCH m.playerB") override fun findAll(): List<TournamentEntity> @Query("SELECT t FROM TournamentEntity t JOIN FETCH t.players JOIN FETCH t.tournamentRounds tr JOIN FETCH tr.matches m JOIN FETCH m.playerA JOIN FETCH m.playerB WHERE t.id = :id") override fun findById(id: Int): Optional<TournamentEntity> }
此方式更灵活,可按需指定加载的关联,性能可控性更强。
方案3:使用@EntityGraph指定关联加载路径
利用Micronaut Data支持的@EntityGraph注解,通过路径声明要加载的关联,写法更简洁:
@JdbcRepository(dialect = Dialect.MYSQL) @Transactional(Transactional.TxType.MANDATORY) interface TournamentRepository : CrudRepository<TournamentEntity, Int>, JpaSpecificationExecutor<TournamentEntity> { @EntityGraph(attributePaths = ["players", "tournamentRounds", "tournamentRounds.matches", "tournamentRounds.matches.playerA", "tournamentRounds.matches.playerB"]) override fun findAll(): List<TournamentEntity> @EntityGraph(attributePaths = ["players", "tournamentRounds", "tournamentRounds.matches", "tournamentRounds.matches.playerA", "tournamentRounds.matches.playerB"]) override fun findById(id: Int): Optional<TournamentEntity> }
attributePaths通过点分隔层级关联,清晰定义需要加载的所有子级数据。
内容的提问来源于stack exchange,提问作者noko
相关产品推荐
相关产品推荐

