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

如何将JPA Repository查询结果转换为自定义StatisticDto类型

问题背景

现有可正常执行的JPA Repository查询代码如下:

public interface GameRepository extends JpaRepository<Game,Integer> {
@Query(value="SELECT Cast(bifnb as varchar) bifnb , count(*) FROM (SELECT  count(fk_game) as nb FROM public.game INNER JOIN score s on game.id_game = s.fk_game WHERE fk_board_game = 2014 GROUP BY fk_game) as bifnb group by bifnb", nativeQuery = true)
List<StatisticDto> nbplayer();
}

使用StatisticDto接收原生查询结果时抛出如下异常:

org.springframework.core.convert.ConverterNotFoundException: 找不到可将类型[org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap]转换为类型[****.statistics.payload.StatisticDto]的转换器

将方法返回值改为List<Object>后查询可正常执行,但期望直接使用StatisticDto类型接收结果。


报错原因

使用nativeQuery = true标记的原生SQL查询,Spring Data JPA默认会将结果封装为TupleBackedMap结构,不会自动完成到自定义POJO类的类型转换,必须显式配置映射规则才能直接返回自定义DTO类型。


可行解决方案

方案1:接口投影(改动最小,无需额外映射配置)

这是原生查询场景下最简便的实现方式,不需要修改原有查询逻辑,只需定义和返回字段匹配的投影接口即可:

  1. 定义投影接口,方法名和查询字段别名严格对应:
    public interface StatisticProjection {
        // 对应SQL里的bifnb字段
        String getBifnb();
        // 对应count(*)的返回值,注意count聚合结果为Long类型,不要用Integer接收
        Long getTotal();
    }
    
  2. 修改原SQL,给无别名的count(*)加上和投影接口匹配的别名,调整Repository返回值类型:
    public interface GameRepository extends JpaRepository<Game,Integer> {
    @Query(value="SELECT Cast(bifnb as varchar) bifnb , count(*) as total FROM (SELECT  count(fk_game) as nb FROM public.game INNER JOIN score s on game.id_game = s.fk_game WHERE fk_board_game = 2014 GROUP BY fk_game) as bifnb group by bifnb", nativeQuery = true)
    List<StatisticProjection> nbplayer();
    }
    

使用时直接调用接口的getBifnb()、getTotal()方法即可获取值,使用体验和普通DTO无差异。

方案2:构造函数结果映射(适合必须使用具体DTO类的场景)

如果一定要返回StatisticDto具体类而非接口,可通过JPA的@SqlResultSetMapping显式定义结果和DTO的映射关系:

  1. 给StatisticDto添加字段顺序、类型完全匹配的全参构造函数,补充对应getter/setter:
    public class StatisticDto {
        private String bifnb;
        private Long total;
    
        // 必须保留无参构造(部分JPA实现需要)
        public StatisticDto() {}
    
        // 和查询返回字段顺序、类型一一对应的全参构造
        public StatisticDto(String bifnb, Long total) {
            this.bifnb = bifnb;
            this.total = total;
        }
    
        // 省略getter、setter
    }
    
  2. 在Game实体类上配置命名原生查询和结果集映射:
    @Entity
    @NamedNativeQuery(
        name = "Game.nbplayer",
        query = "SELECT Cast(bifnb as varchar) bifnb , count(*) as total FROM (SELECT  count(fk_game) as nb FROM public.game INNER JOIN score s on game.id_game = s.fk_game WHERE fk_board_game = 2014 GROUP BY fk_game) as bifnb group by bifnb",
        resultSetMapping = "statisticDtoMap"
    )
    @SqlResultSetMapping(
        name = "statisticDtoMap",
        classes = @ConstructorResult(
            targetClass = StatisticDto.class,
            columns = {
                @ColumnResult(name = "bifnb", type = String.class),
                @ColumnResult(name = "total", type = Long.class)
            }
        )
    )
    public class Game {
        // 实体原有字段、方法不变
    }
    
  3. 移除Repository方法上的@Query注解,方法名和命名查询的name对应即可:
    public interface GameRepository extends JpaRepository<Game,Integer> {
        List<StatisticDto> nbplayer();
    }
    

方案3:高版本JPA直接映射(Spring Boot 2.6+可用)

如果项目使用的Spring Data JPA版本在2.6及以上,只需保证:

  • StatisticDto有对应字段的getter/setter、无参构造
  • 原生SQL的所有返回字段都有和StatisticDto属性名完全一致的别名
  • 字段类型严格匹配(count结果用Long接收,不要用Integer)
    即可直接返回List<StatisticDto>,不需要额外配置。

避坑提示
  • 数据库count(*)聚合函数返回的是Long/BigInteger类型,不要用Integer类型接收,否则会抛出类型转换异常
  • 原生查询的所有返回字段必须显式指定别名,且别名要和投影/DTO的属性名严格匹配,否则会出现字段值为null的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:57:21