
当在 spring data jpa 中通过 `@query` 原生 sql 或 jpql 查询多列并期望返回 `tuple` 时,若配置不当(如误用原生 sql + `tuple`),会触发 `indexoutofboundsexception`;根本原因在于 spring boot 1.5.22+ 对原生查询与 `tuple` 的兼容性限制增强,需改用构造器表达式或自定义 dto。
在 Spring Boot 升级至 1.5.22.RELEASE 及更高版本后,@Query 注解对原生 SQL(nativeQuery = true)与 Tuple 类型的组合支持被严格限制:原生 SQL 不支持直接映射为 Tuple(即使使用别名),这导致 SELECT q.id as someId, q.name as someName 这类多列查询在执行时无法正确构建 Tuple 实例,最终抛出 IndexOutOfBoundsException: Index: 0, Size: 0 —— 表明结果集为空或元数据解析失败。
✅ 正确做法是:改用 JPQL(非原生) + 构造器表达式(constructor expression),显式调用目标类的构造方法。例如,定义一个轻量级 DTO:
// src/main/java/com/example/demo/TupleDto.java
public class TupleDto {
private final Long someId;
private final String someName;
public TupleDto(Long someId, String someName) {
this.someId = someId;
this.someName = someName;
}
// getters (required for projection usage)
public Long getSomeId() { return someId; }
public String getSomeName() { return someName; }
}然后在 Repository 中使用 JPQL 构造器语法(注意:不能加 value =,且必须是合法 JPQL,非 SQL):
@Repository public interface QuoteRepository extends JpaRepository{ @Query("SELECT new com.example.demo.TupleDto(q.id, q.name) " + "FROM Quote q WHERE q.id IN :quoteIds") ListselectSomeThings(@Param("quoteIds") List quoteIds); }
⚠️ 注意事项:
❌ @Query(value = "...", nativeQuery = true) + List
不适用于多列原生查询(单列可能偶然成功,属未定义行为); ✅ Tuple 仅可靠用于 JPQL 查询 + select new org.hibernate.query.Tuple(...)(Hibernate 特有)或更推荐的 接口投影(Interface-based Projection);
-
✅ 若坚持用 Tuple,应改用 JPQL 并启用 Hibernate 的 Tuple 构造(需确保使用 Hibernate 5.2+):
@Query("SELECT NEW org.hibernate.query.Tuple(q.id AS someId, q.name AS someName) FROM Quote q WHERE q.id IN :quoteIds") ListselectAsTuple(@Param("quoteIds") List quoteIds); // 需确认 Hibernate 版本兼容性 -
? 替代方案:使用 接口投影(推荐),更类型安全、无需手动建类:
interface QuoteProjection { Long getSomeId(); // 对应 SELECT 中的别名 String getSomeName(); } @Query("SELECT q.id AS someId, q.name AS someName FROM Quote q WHERE q.id IN :quoteIds") ListselectProjections(@Param("quoteIds") List quoteIds);
? 总结:该问题本质是 Spring Boot 1.5.22+ 加强了对原生查询与 Tuple 组合的校验。解决核心在于——放弃原生 SQL + Tuple 多列组合,转向 JPQL 构造器、DTO 投影或接口投影。三者中,接口投影最简洁,DTO 最灵活可控,而 Tuple 仅建议在动态列场景下配合 JPQL 谨慎使用。










