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

Hibernate Criteria查询一对多关联获取子实体报错求助

问题:查询Installer子实体infoRges时出现SQL语法错误

实体类与DTO定义

Installer实体类

@Entity
@Table(name = "installer")
public class Installer implements Serializable {

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Set<InfoRge> infoRges;

    @Column(name = "enterprise_name", length = 50)
    private String name;
    // 包含getter和setter
}

InstallerListDto类

private Set<InfoRge> infoRges;
private String name;
// 构造方法:Long id, Set<InfoRge> infoRges, String name
// 包含setter和getter

查询代码(Criteria Query)

// 创建CriteriaBuilder
CriteriaBuilder cb = em.getCriteriaBuilder();

// 创建针对InstallerListDto的查询
CriteriaQuery<InstallerListDto> criteriaQuery = cb.createQuery(InstallerListDto.class);

Root<Installer> from = criteriaQuery.from(Installer.class);
criteriaQuery.select(cb.construct(InstallerListDto.class, from.get("id"),
                                      from.get("infoRges"),
                                      from.get("name")));

报错信息

org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet

Caused by: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '. as col_5_0_, installer0_.enterprise_name as col_6_0_ from installer installer0' at line 1

问题原因与解决方案

原因

JPA的CriteriaBuilder.construct()仅支持映射简单属性,无法直接处理@OneToMany这类关联集合。直接将from.get("infoRges")传入构造方法时,Hibernate无法生成合法的SQL语句,导致语法错误。

解决方法

方式1:先查实体再手动转DTO

// 查询Installer实体,并主动加载关联集合避免懒加载问题
CriteriaQuery<Installer> criteriaQuery = cb.createQuery(Installer.class);
Root<Installer> from = criteriaQuery.from(Installer.class);
from.fetch("infoRges", JoinType.LEFT);
criteriaQuery.select(from);

List<Installer> installers = em.createQuery(criteriaQuery).getResultList();

// 手动转换为DTO
List<InstallerListDto> dtos = installers.stream()
    .map(installer -> new InstallerListDto(installer.getId(), installer.getInfoRges(), installer.getName()))
    .collect(Collectors.toList());

该方式逻辑直观,适合数据量不大的场景,通过fetch提前加载关联集合可避免N+1查询问题。

方式2:用JPQL直接构造DTO

改用JPQL的构造函数表达式,Hibernate支持直接映射关联集合:

// 需替换为DTO的完整类路径
String jpql = "SELECT new com.yourpackage.InstallerListDto(i.id, i.infoRges, i.name) FROM Installer i LEFT JOIN FETCH i.infoRges";
List<InstallerListDto> dtos = em.createQuery(jpql, InstallerListDto.class).getResultList();

通过LEFT JOIN FETCH确保关联集合被正确加载,避免懒加载异常。

补充:完善关联映射配置

当前@OneToMany注解缺少mappedBy属性(除非InfoRge用的是单向@JoinColumn关联),建议补充配置以保证映射正确性:

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "installer")
private Set<InfoRge> infoRges;

其中mappedBy的值对应InfoRge实体中指向Installer的属性名。

内容的提问来源于stack exchange,提问作者Bader-Eddine Qodia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 08:15:52