PAYATHON 2026

Hibernate 6 中固定值 @JoinFormula 导致原生查询报错,如何修复?

数据小许

建议保留 attachmentType 和现有的固定值关联映射,先将返回实体的原生查询改为 Spring Data JPA 派生查询。Hibernate 会根据映射生成 SQL,避开手写 SELECT A.* 时公式关联所需结果列的映射问题。

仅凭异常信息,不能确定这是 Hibernate 6.6.42 的某个已知缺陷,也不能认定所有固定值 @JoinFormula 都不可用。

为什么会找不到 codCategory?

你的关联条件是:

ATTACHMENT.TYPE = ATTACHMENT_TYPE.COD_TYPE
AND ATTACHMENT_TYPE.COD_CATEGORY = 'AF'

在 @JoinFormula(value = "'AF'", referencedColumnName = "COD_CATEGORY") 中,'AF' 是 SQL 字符串常量,COD_CATEGORY 是目标表的关联列,不需要在 ATTACHMENT 表中新增同名字段。Hibernate 支持用 @JoinColumnOrFormula 组合普通列和公式条件,参见 Hibernate 关联映射说明。

原生查询中的 A.* 只返回 ATTACHMENT 的实际列,不会自动补充公式表达式。异常表明,Hibernate 在读取结果集、组装实体时尝试查找名为 codCategory 的列,但结果集中没有这个列标签。结合当前映射,应优先检查原生查询结果是否满足公式关联的读取要求;准确的触发路径仍需通过完整堆栈或最小复现来确认。

Hibernate 对返回实体的原生查询有结果列要求,关联所需的数据也必须能从结果中读取。相关规则见 原生查询的关联处理。

首选方案:用派生查询获取最新记录

原来的 SQL 用于排序并取第一条记录,可以改用 Spring Data JPA 派生查询:

import java.math.BigInteger;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AttachmentRepository
        extends JpaRepository<Attachment, BigInteger> {

    Optional<Attachment> findFirstByOrderByTmsUltAggLegacyDesc();

    default Optional<Attachment> findMostRecentAttachment() {
        return findFirstByOrderByTmsUltAggLegacyDesc();
    }
}

删除原来 findMostRecentAttachment() 上的原生 @Query,用上述默认方法保留旧方法名。attachmentType 字段及其注解可以先保持原样。

派生方法使用 Java 属性名 tmsUltAggLegacy,不是数据库列名 TMS_ULT_AGG_LEGACY。Hibernate 会根据实体映射处理关联公式,并由数据库方言生成限制结果数量的 SQL。

原始代码中的实体类名是 Attachment,查询返回类型却是 AttachmentEntity。示例按 Attachment 编写;如果实际实体名是 AttachmentEntity,应统一替换 Repository 中的类型。

必须保留原生 SQL 时:先查主键,再加载实体

如果必须保留原生 SQL,可以先让查询只返回主键,再通过 findById() 加载实体,避免直接用原生结果集组装带公式关联的实体。

在 Repository 中声明:

@Query(nativeQuery = true, value = "SELECT A.ID " +
    "FROM ATTACHMENT A " +
    "ORDER BY A.TMS_ULT_AGG_LEGACY DESC " +
    "FETCH FIRST 1 ROW ONLY")
Optional<BigInteger> findMostRecentAttachmentId();

在 Service 中组合调用:

@Transactional(readOnly = true)
public Optional<Attachment> findMostRecentAttachment() {
    return attachmentRepository.findMostRecentAttachmentId()
        .flatMap(attachmentRepository::findById);
}

这里假设 Service 已注入 attachmentRepository,且 ID 的 JDBC 返回值能够映射为实体主键类型 BigInteger。该方案通常会增加一次数据库查询,但保留了原生排序语句和实体关联字段。

不要仅根据报错就在 SQL 中添加一个名为 codCategory 的常量列,并将其当作通用修复。公式关联具体需要哪些结果标签,应结合当前版本的实际映射确认。

保留关联时需要检查什么?

@NotFound(action = NotFoundAction.IGNORE) 在目标记录不存在时将关联视为 null,但不能忽略结果集缺列异常。它还会使 to-one 关联采用非延迟加载行为,因此即使声明了 FetchType.LAZY,也不能假定 attachmentType 会延迟加载。参见 NotFound API 说明。

还应检查未提供的 AttachmentTypeEntityPK:它需要包含 String codType 和 String codCategory,与实体中两个 @Id 属性同名、同类型,并正确实现 equals()、hashCode(),符合 @IdClass 的要求。

验证时至少覆盖以下情况:

  • 存在匹配的 COD_TYPE,且 COD_CATEGORY = 'AF',应取得对应类型。
  • 同一个 COD_TYPE 只有其他分类的记录,应按 IGNORE 语义得到 null。
  • ATTACHMENT 没有记录,应返回 Optional.empty()。

如果派生查询和普通 findById() 也出现相同异常,需要继续检查完整实体映射、复合主键类及生成的 SQL;此时问题已不只涉及原来的原生查询。

备注:内容仅供参考。