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

Spring Data JPA使用@Query返回DTO触发Druid SQL解析异常

问题原因与解决方案

核心问题分析

抛出com.alibaba.druid.sql.parser.ParserException的根本原因是原生SQL与JPQL语法混用,同时存在两处辅助问题:

  • 开启了nativeQuery=true(表示使用原生SQL),但查询语句却用了JPQL特有的new 全限定类名.DTO(...)构造器语法,Druid解析原生SQL时无法识别该语法,直接抛出解析异常。
  • Repository泛型错误:MzArenaRepository继承JpaRepository时指定的泛型是MzArena,但实际查询的是MzContentArena实体,上下文不匹配。
  • 参数类型不匹配:方法参数是String id,但数据库中arena_id是Integer类型,会引发后续类型转换问题。

解决方案

方案一:使用JPQL(推荐,简单直接)

修改Repository代码,去掉nativeQuery=true,改用JPQL语法操作实体类与属性:

@Repository
// 修正泛型为要操作的MzContentArena实体,主键类型为Integer
public interface MzContentArenaRepository extends JpaRepository<MzContentArena, Integer> {
    // 去掉nativeQuery=true,JPQL引用实体的属性名(arenaId而非数据库字段arena_id)
    @Query("select new com.qiguliuxing.dts.db.entity.MzContentArenaDTO(car.id, car.arenaId, car.contentId) from MzContentArena car where car.arenaId = ?1")
    // 参数类型修正为Integer,匹配数据库字段类型
    List<MzContentArenaDTO> findByArenaId(Integer arenaId);
}

方案二:使用原生SQL(需手动处理结果映射)

如果必须使用原生SQL,不能直接用new DTO()语法,需要通过结果映射实现:

  1. 先在DTO类上添加无参构造器(JPA需要实例化对象后赋值):
@Data
@AllArgsConstructor
public class MzContentArenaDTO implements Serializable {
    private Integer id;
    private Integer arenaId;
    private Integer contentId;
    private LocalDateTime createDate;

    public MzContentArenaDTO(Integer id, Integer arenaId, Integer contentId) {
        this.id = id;
        this.arenaId = arenaId;
        this.contentId = contentId;
    }

    // 添加无参构造器
    public MzContentArenaDTO() {}
}
  1. 修改Repository,使用原生SQL并指定结果集映射:
@Repository
public interface MzContentArenaRepository extends JpaRepository<MzContentArena, Integer> {
    @Query(value = "select id, arena_id as arenaId, content_id as contentId from mz_content_arena where arena_id = ?1", 
           nativeQuery = true,
           // 指定结果映射到DTO的属性
           resultSetMapping = "MzContentArenaDTOMapping")
    List<MzContentArenaDTO> findByArenaId(Integer arenaId);
}
  1. 在实体类MzContentArena上添加结果集映射注解:
@Entity
@Table(name = "mz_content_arena")
// 定义结果集映射,将原生SQL的字段映射到DTO属性
@SqlResultSetMapping(
        name = "MzContentArenaDTOMapping",
        classes = @ConstructorResult(
                targetClass = MzContentArenaDTO.class,
                columns = {
                        @ColumnResult(name = "id", type = Integer.class),
                        @ColumnResult(name = "arenaId", type = Integer.class),
                        @ColumnResult(name = "contentId", type = Integer.class)
                }
        )
)
public class MzContentArena {
    @Id
    private Integer id;
    private Integer arenaId;
    private Integer contentId;
}

关键注意事项

  • JPQL操作的是实体类和属性名,原生SQL操作的是数据库表和字段名,二者语法不能混用。
  • Repository的泛型必须与要操作的实体类一致,否则JPA无法正确识别实体上下文。
  • 方法参数类型要与数据库字段类型匹配,避免不必要的类型转换异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 07:35:24