Spring Data JPA 4.2.0-RC1 在启动时校验静态 DTO 投影重写后的 @Query。DTO 属性名打错时,进程起不来,日志写出 Query validation failed 与重写后的 select new …(p.firstName_WITH_TYPO)。同一工程换 4.2.0-M2 能启动,调用接口才 500。

相对 Spring Data 2026.1.0-M2:JPA 4.0 初兼容与 Redis JsonOperations,本文只记 RC1 相对 M2 的这一处实测差。官方 JPA 4.2.0-RC1 发行说明列出 #4362 / #4363:校验时纳入 query rewriting 与静态投影。

版本:JDK 21.0.12.1、Spring Boot 4.2.0-RC1(BOM 内 spring-data-bom.version=2026.1.0-RC1)、Spring Data JPA 4.2.0-RC1 / Commons 4.2.0-RC1、Hibernate 7.4.13.Final、H2 内存库。对照跑使用同一源码与 4.2.0-M2 jar。

实体 Person 有 firstName。@Query("select p from Person p where p.id = :id") 返回 record DTO。坏样例 DTO 字段写成 firstName_WITH_TYPO;好样例为 firstName。

public record PersonDto(Long id, String firstName_WITH_TYPO) { }

public interface PersonRepository extends JpaRepository<Person, Long> {
  @Query("select p from Person p where p.id = :id")
  PersonDto findDto(@Param("id") Long id);
}

JPA 4.2.0-RC1 + 坏 DTO,启动失败(Application run failed):

Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'badProjectionApp': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'personRepository' defined in com.example.bad.PersonRepository defined in @EnableJpaRepositories declared on BadProjectionApp: Query validation failed for 'select new com.example.bad.PersonDto(p.id, p.firstName_WITH_TYPO) from Person p where p.id = :id'
Caused by: org.springframework.data.repository.query.QueryCreationException: Cannot create query for method [PersonRepository.findDto(java.lang.Long)]; Query validation failed for 'select new com.example.bad.PersonDto(p.id, p.firstName_WITH_TYPO) from Person p where p.id = :id'
Caused by: org.hibernate.query.sqm.UnknownPathException: Could not resolve attribute 'firstName_WITH_TYPO' of 'com.example.Person' [select new com.example.bad.PersonDto(p.id, p.firstName_WITH_TYPO) from Person p where p.id = :id]

同一坏 DTO 换 classpath 上的 spring-data-jpa 4.2.0-M2,应用启动成功:

2026-10-25T08:13:16.432+08:00  INFO 203135 --- [data-rc1-jpa-query-validation] [           main] o.s.boot.tomcat.TomcatWebServer          : Tomcat started on port 8088 (http) with context path '/'
2026-10-25T08:13:16.436+08:00  INFO 203135 --- [data-rc1-jpa-query-validation] [           main] com.example.BadProjectionApp             : Started BadProjectionApp in 3.461 seconds (process running for 3.746)

GET /bad/1 返回 HTTP 500。日志在调用时才出现同一属性错误:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.query.sqm.UnknownPathException: Could not resolve attribute 'firstName_WITH_TYPO' of 'com.example.Person' [select new com.example.bad.PersonDto(p.id, p.firstName_WITH_TYPO) from Person p where p.id = :id]] with root cause
org.hibernate.query.sqm.PathElementException: Could not resolve attribute 'firstName_WITH_TYPO' of 'com.example.Person'

RC1 + 正确 DTO 字段 firstName 可启动。GET /good/1 返回 {"id":1,"firstName":"Ada"},HTTP 200:

2026-10-25T08:13:31.187+08:00  INFO 204733 --- [data-rc1-jpa-query-validation] [           main] o.s.boot.tomcat.TomcatWebServer          : Tomcat started on port 8088 (http) with context path '/'
2026-10-25T08:13:31.194+08:00  INFO 204733 --- [data-rc1-jpa-query-validation] [           main] com.example.GoodProjectionApp            : Started GoodProjectionApp in 2.939 seconds (process running for 3.187)

源码对照(据 4.2.0-M2 / 4.2.0-RC1 sources):M2 的 SimpleJpaQuery 用原始 query 字符串做 validateQuery;RC1 改为 getSortedQuery(Sort.unsorted(), method.getResultProcessor()) 后再校验,并经 potentiallyRewriteQuery。注释写明考虑 QueryRewriter 与 DTO projection rewriting。默认 validateQueries=true(@EnableJpaRepositories)。

依据:Spring Data BOM 2026.1.0-RC1(HTTP 200)、Spring Data JPA 4.2.0-RC1(HTTP 200)、issue #4362 / PR #4363(HTTP 200)。

— 感谢阅读 —
广告

一起交流

分享你的思考,让讨论更进一步。