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

Spring Boot单向One-To-Many关联查询实现及原生SQL报错解决

问题根因与解决方案

1. 现有配置下优先使用JPQL查询(无需修改实体、无需关心中间表)

你当前的实体配置已经在Compania中定义了office关联属性,直接用JPQL查询即可,Hibernate会自动关联中间表生成正确的SQL,完全不需要手动写原生SQL处理中间表逻辑:

// 写在CompaniaRepository或者OfficeRepository中都可以
@Query("select o from Compania c join c.office o where c.id = :companiaId and lower(o.name) like lower(concat('%', :officeName, '%')) order by o.idRef asc")
List<Office> findMatchOffices(@Param("companiaId") Long companiaId, @Param("officeName") String officeName);

甚至可以直接用Spring Data JPA的命名查询,无需手写JPQL:

// 写在CompaniaRepository中
List<Office> findByIdAndOffice_NameContainingIgnoreCaseOrderByOffice_IdRefAsc(Long companiaId, String officeName);

2. 原生SQL报错的原因

你遇到的SQLGrammarException是SQL语法错误,核心是字段名不匹配:

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

你的Office实体类中idRef字段的@Column(name = "idRef"),数据库Office表的排序字段实际是idRef,但你的SQL中写的是o.id_ref,下划线命名和实际字段名不符导致语法报错。另外你SQL中匹配的是title字段,和需求中要匹配officeName的逻辑不一致,模糊查询也没有加%通配符,相当于精确匹配。
修正后的原生查询如下:

@Query(
    value = "select o.* from office o " +
        "inner join compania_office co on o.id = co.office_id " +
        "inner join compania c on c.id = co.compania_id " +
        "where c.id = :companiaId and lower(o.name) like lower(concat('%', :officeName, '%')) " +
        "order by o.\"idRef\" asc;",
    nativeQuery = true
)
List<Office> findOfficesByNameLike(@Param("companiaId")Long companiaId, @Param("officeName") String officeName);

3. 实体映射优化建议

你当前的@OneToMany配置存在矛盾:你加了mappedBy = "compania"代表这是双向关联,由Office类的compania属性维护关联,但你的Office类中并没有定义Compania类型的compania字段,所以Hibernate才自动生成了中间表COMPANIA_OFFICE。如果你不需要中间表,可以调整关联配置:

方案A:改成单向OneToMany无需中间表

修改Compania的office属性配置:

@OneToMany(
    cascade = CascadeType.ALL,
    fetch = FetchType.LAZY,
    orphanRemoval = true
)
@JoinColumn(name = "compania_id") // 会在Office表新增compania_id外键字段,无需中间表
private List<Office> office;

方案B:改成双向OneToMany

在Office类中新增关联属性:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "compania_id")
private Compania compania;

两种方案都可以去掉中间表,后续查询会更简单。

内容的提问来源于stack exchange,提问作者Varox

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 13:45:02