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

JPA原生查询获取下周到期条目时出现SQL语法错误求助

问题分析与解决方案

你的报错核心原因不是CURRENT_DATE和INTERVAL的语法问题,而是你在nativeQuery = true的原生SQL中使用了JPQL特有的new model.dto.EntryDTO(...)构造DTO的语法——PostgreSQL原生SQL根本不认识这种Java类构造的写法,所以解析到model.dto里的.时直接抛出语法错误,对应报错里的Position:21。

两种修复方案

方案1:改用JPQL(推荐,更符合JPA规范)

去掉nativeQuery = true,使用JPQL语法查询,同时调整日期逻辑(JPQL支持CURRENT_DATE,加一周可以直接用CURRENT_DATE + 7):

@Query(value = "SELECT new model.dto.EntryDTO(v.name, e.eNumber, e.dueDate, e.amount, eScan.scanTimestamp) " +
               "FROM Entry e " +
               "JOIN EntryScan eScan ON e.eNumber = eScan.eNumber " +
               "JOIN Ve v ON e.vId = v.id " +
               "WHERE e.dueDate BETWEEN CURRENT_DATE AND CURRENT_DATE + 7")
List<EntryDTO> findAllDueNextWeek();

注意:

  • JPQL中要使用Java实体类的属性名(比如e.eNumber而非数据库字段e.e_number),确保和你的实体类定义一致
  • 如果Entry和EntryScan实体间有关联关系,可以简化JOIN写法:JOIN e.entryScan eScan,无需手动写ON条件

方案2:保持原生查询,通过结果映射获取DTO

如果必须用原生SQL,需要去掉JPQL的new语法,通过以下方式转换为DTO:

方式A:手动转换Object[]

先查询返回原始数组,再在业务层转换为DTO:

@Query(value = "SELECT v.name, e.e_number, e.due_date, e.amount, es.scan_timestamp " +
               "FROM entry e " +
               "JOIN entry_scan es ON e.e_number = es.e_number " +
               "JOIN ve v ON e.v_id = v.id " +
               "WHERE e.due_date BETWEEN CURRENT_DATE AND CURRENT_DATE + INTERVAL '1 week'",
       nativeQuery = true)
List<Object[]> findAllDueNextWeekRaw();

// 业务层转换逻辑
public List<EntryDTO> getDueNextWeekEntries() {
    return findAllDueNextWeekRaw().stream()
        .map(row -> new EntryDTO(
            (String) row[0],
            (String) row[1],
            (Date) row[2],
            (BigDecimal) row[3],
            (Timestamp) row[4]
        ))
        .collect(Collectors.toList());
}

方式B:使用@SqlResultSetMapping自动映射

在DTO类上定义结果集映射规则:

@SqlResultSetMapping(
    name = "EntryDTOMapping",
    classes = @ConstructorResult(
        targetClass = EntryDTO.class,
        columns = {
            @ColumnResult(name = "name", type = String.class),
            @ColumnResult(name = "e_number", type = String.class),
            @ColumnResult(name = "due_date", type = Date.class),
            @ColumnResult(name = "amount", type = BigDecimal.class),
            @ColumnResult(name = "scan_timestamp", type = Timestamp.class)
        }
    )
)
public class EntryDTO {
    // 匹配映射规则的构造函数
    public EntryDTO(String name, String eNumber, Date dueDate, BigDecimal amount, Timestamp scanTimestamp) {
        // 构造逻辑
    }
    // 其他属性、方法
}

然后查询时指定映射:

@Query(value = "SELECT v.name, e.e_number, e.due_date, e.amount, es.scan_timestamp " +
               "FROM entry e " +
               "JOIN entry_scan es ON e.e_number = es.e_number " +
               "JOIN ve v ON e.v_id = v.id " +
               "WHERE e.due_date BETWEEN CURRENT_DATE AND CURRENT_DATE + INTERVAL '1 week'",
       nativeQuery = true,
       resultSetMapping = "EntryDTOMapping")
List<EntryDTO> findAllDueNextWeek();

补充说明

你原来的PostgreSQL日期语法CURRENT_DATE AND (CURRENT_DATE + INTERVAL '1 week')是完全正确的,不需要修改——问题出在原生查询和JPQL语法的混用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 02:34:56