迁移至Hibernate 6后,多对一关联的Join Fetch失效问题咨询
Hibernate 6中嵌入式ID内多对一关联的JOIN FETCH行为变化
问题场景
迁移至Spring Boot 3.0.3(对应Hibernate 6.1.7)后,在Spring Data JPA中使用JOIN FETCH查询嵌入式ID内的多对一关联时出现异常:
- 实体结构:
HotelRoomEntity的嵌入式IDHotelRoomId中包含RoomTypeEntity的多对一关联 - 原有JPQL查询:
select hotelRoom from HotelRoomEntity hotelRoom join fetch hotelRoom.id.roomType where hotelRoom.id.hotel.code = :hotelCode - 现象:Hibernate 6生成的SQL仅查询
HotelRoomEntity的字段,未抓取RoomTypeEntity;而Hibernate 6之前版本会自动包含关联实体的字段,正常获取关联数据。 - 临时解决:将SELECT子句修改为
select hotelRoom, roomType(或hotelRoom.id.roomType)后,关联实体可正常被抓取。
原因分析
这是Hibernate 6对JPQL解析和抓取策略的严格化调整,而非强制要求:
- JPA规范本身并不推荐在嵌入式ID(
@Embeddable)中包含实体关联,嵌入式ID的设计初衷是由简单值类型(如String、Long)组成的复合主键。 - Hibernate 6之前的版本对这种非规范场景做了宽松处理,自动识别嵌入式ID内的关联并执行抓取;但6.x版本收紧了逻辑,仅当关联对象被显式包含在SELECT子句中,或者关联路径属于主实体的直接属性时,才会触发抓取。
解决方案
方案1:显式在SELECT子句中指定关联对象
修改JPQL查询,将关联实体加入SELECT:
@Query(""" select hotelRoom, hotelRoom.id.roomType from HotelRoomEntity hotelRoom join fetch hotelRoom.id.roomType where hotelRoom.id.hotel.code = :hotelCode """) List<HotelRoomEntity> find(@Param("hotelCode") final int hotelCode);
方案2:调整实体结构(推荐)
将嵌入式ID内的多对一关联移至主实体,贴合JPA规范:
// 修改HotelRoomEntity @Entity @Table(name = "hotel_room") @Getter @Setter public class HotelRoomEntity implements Serializable { @EmbeddedId private HotelRoomId id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "room_type_code", referencedColumnName = "room_type_code", insertable = false, updatable = false) private RoomTypeEntity roomType; } // 修改HotelRoomId为纯值类型 @Embeddable @Data public class HotelRoomId implements Serializable { @Column(name = "hotel_code") private String hotelCode; @Column(name = "room_type_code") private String roomTypeCode; }
调整后原有JOIN FETCH hotelRoom.roomType的查询即可正常工作。
内容的提问来源于stack exchange,提问作者Santiago Ordax
相关产品推荐
相关产品推荐

