使用@Query投影至自定义类时无法正常工作的问题
解决Spring Data JPA @Query投影时的ConverterNotFoundException问题
我之前也踩过这个坑!当用Spring Data JPA做投影时,派生查询(比如findPersonSummaryById(long id))不管是接口投影还是带兼容构造方法的类投影都能正常工作,但一旦用@Query写自定义字段查询,就会出现ConverterNotFoundException,本质是Spring Data处理这两种查询的投影逻辑不一样。
问题根源
- 派生查询(由方法名自动生成SQL的那种):Spring Data会自动识别投影类型,如果是类投影,会直接调用对应的构造方法来实例化对象;如果是接口,会动态生成代理类实现接口并映射字段。
- 自定义
@Query查询:当你写SELECT name AS name, age AS age FROM Person...时,JPA默认会返回一个HashMap<String, Object>来存储字段名和对应的值,这时候Spring Data不知道要把这个Map转换成你的PersonSummary类,所以会抛出转换器找不到的异常。
几种解决方案
1. 改用接口投影(最省心)
如果你的投影只是用来获取数据,不需要修改,直接把PersonSummary改成接口形式:
public interface PersonSummary { String getName(); int getAge(); }
不管是派生查询还是自定义@Query查询,Spring Data都会动态生成代理类实现这个接口,自动把查询结果映射到接口的getter方法上,完美解决问题。
2. 在@Query中直接调用类的构造方法(适合类投影)
如果一定要用类投影,直接在JPQL语句里显式调用PersonSummary的构造方法,注意要写全类名:
@Query("SELECT new com.example.PersonSummary(p.name, p.age) FROM Person p WHERE p.id = :id") PersonSummary findPersonSummaryByIdQuery(@Param("id") long id);
需要确保构造方法的参数顺序、类型和SELECT中字段的顺序、类型完全匹配,这样JPA会直接创建PersonSummary实例,不会返回Map。
3. 注册自定义转换器(复杂场景适用)
如果不想修改JPQL语句,可以自定义一个转换器,把HashMap转换成PersonSummary,然后注册到Spring容器中:
import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; import java.util.Map; @Component public class MapToPersonSummaryConverter implements Converter<Map<String, Object>, PersonSummary> { @Override public PersonSummary convert(Map<String, Object> source) { PersonSummary summary = new PersonSummary(); summary.setName((String) source.get("name")); summary.setAge((Integer) source.get("age")); return summary; } }
这样Spring Data在遇到需要把Map转成PersonSummary的场景时,会自动使用这个转换器。
补充说明
这个问题其实是Spring Data JPA的一个已知问题,对应编号DATAJPA-1003。虽然后续版本优化了部分投影场景,但自定义@Query的类投影还是需要通过上面的方法来处理。
内容的提问来源于stack exchange,提问作者Alo
相关产品推荐
相关产品推荐

