You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 15:19:53