Hibernate为何生成两条查询语句而非执行关联查询?
解决OneToMany关联生成两条SELECT而非JOIN查询的问题
问题回顾
在使用Blaze Persistence EntityView + Hibernate时,遇到OneToMany关联(Truth -> TruthOption)生成两条独立SELECT查询的问题,而其他类似关联能正常生成JOIN查询。相关信息如下:
技术栈
- Blaze Persistence Core + EntityView 1.6.11
- Hibernate 6.4.4 final
- Spring Boot 3.2.5
关键代码片段
Truth 实体
@Entity @Table( uniqueConstraints = { @UniqueConstraint(name = Truth.UNIQUE_CONSTRAINT_NAME, columnNames = {"slugified_name", "rules_package_id"}) } ) @NoArgsConstructor @Setter @Getter public class Truth implements NonCollectableNode { public static final String UNIQUE_CONSTRAINT_NAME = "UC_truth_name_rules_package_id"; @OneToMany(mappedBy = "truth", fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) private List<TruthOption> options = new ArrayList<>(); // 其他字段省略 }
TruthOption 实体
@Entity @Getter @Setter @NoArgsConstructor public class TruthOption { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "truth_id", nullable = false) private Truth truth; private String summary; // 其他字段省略 }
实体视图
@EntityView(Truth.class) public interface TruthResponseView { @IdMapping UUID getId(); List<TruthOptionResponseView> getOptions(); // 其他方法省略 } @EntityView(TruthOption.class) public interface TruthOptionResponseView { String getSummary(); }
仓库查询方法
public Optional<TruthResponseView> findDtoById(UUID id) { CriteriaBuilder<Truth> criteriaBuilder = criteriaBuilderFactory .create(entityManager, Truth.class) .where("id").eq(id); CriteriaBuilder<TruthResponseView> truthResponseViewCriteriaBuilder = entityViewManager.applySetting(EntityViewSetting.create(TruthResponseView.class), criteriaBuilder); TruthResponseView truthResponseView; try { truthResponseView = truthResponseViewCriteriaBuilder.getSingleResult(); } catch (NoResultException e) { truthResponseView = null; } return Optional.ofNullable(truthResponseView); }
生成的SQL
Hibernate: select t1_0.id,t1_0.canonical_name,t1_0.color,t1_0.dice,t1_0.name,t1_0.replaces,t1_0.rules_package_id,t1_0.slugified_name,t1_0.your_character from truth t1_0 where t1_0.id=? Hibernate: select o1_0.truth_id,o1_0.id,o1_0.summary from truth_option o1_0 where o1_0.truth_id=?
解决方案
1. 显式配置EntityView的JOIN获取策略
Blaze Persistence EntityView默认可能对集合关联采用批量获取(N+1查询),可通过两种方式指定JOIN策略:
方法一:在实体视图集合属性上添加@Fetch注解
修改TruthResponseView:
@EntityView(Truth.class) public interface TruthResponseView { @IdMapping UUID getId(); @Fetch(FetchMode.JOIN) List<TruthOptionResponseView> getOptions(); // 其他方法省略 }
方法二:查询时通过EntityViewSetting配置获取模式
调整仓库方法中的EntityViewSetting:
EntityViewSetting<TruthResponseView, CriteriaBuilder<TruthResponseView>> setting = EntityViewSetting.create(TruthResponseView.class) .fetch("options", FetchMode.JOIN); CriteriaBuilder<TruthResponseView> truthResponseViewCriteriaBuilder = entityViewManager.applySetting(setting, criteriaBuilder);
2. 检查关联映射与表结构一致性
确认TruthOption的truth属性与Truth的options属性映射完全匹配,同时核对数据库中truth_option表的truth_id字段类型与truth表主键类型(UUID)一致,确保外键约束正常。
3. 验证版本兼容性
检查Blaze Persistence 1.6.11与Hibernate 6.4.4的兼容性,若存在版本适配问题,升级Blaze Persistence到支持Hibernate 6.4.x的版本(如最新1.6.x系列或2.x系列)可解决潜在的策略生成问题。
原因分析
Blaze Persistence EntityView的集合获取策略会根据关联元数据、版本兼容性或查询上下文自动调整。当框架判断JOIN查询可能导致主表数据重复,或未显式指定JOIN策略时,会默认采用批量获取(N+1查询)。而其他类似关联正常生成JOIN,大概率是因为那些关联的实体视图或查询配置了显式JOIN策略,或元数据细节存在差异。
内容的提问来源于stack exchange,提问作者user26464197
相关产品推荐
相关产品推荐

