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

Spring Boot/JPA REST API控制器传递日期异常:无法转换PathVariable的java.sql.Date类型

解决Spring中java.util.Date与java.sql.Date的类型转换异常问题

这个问题我之前也碰到过,核心原因是Spring无法自动在java.util.Date和java.sql.Date之间完成转换,再加上旧日期API的类型混淆,就抛出了转换器找不到的错误。下面给你几个可行的解决方案,优先推荐使用Java 8+的新日期API,因为它能彻底避免这类类型混乱问题。

方案1:改用Java 8+的日期时间API(推荐)

旧的Date类(不管是util还是sql包下的)设计缺陷很多,Java 8引入的LocalDate、LocalDateTime是更安全、更清晰的选择,而且Spring和JPA对它们的支持非常完善。

步骤1:修改控制器参数类型

把控制器中的Date替换为LocalDate,并保留@DateTimeFormat注解:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@GetMapping(path = "/{startDate}/{endDate}")
public List<CarResponse> getCarsSoldBetween(
    @PathVariable("startDate") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
    @PathVariable("endDate") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
    // 把LocalDate转换为包含时间范围的LocalDateTime,确保覆盖当天所有时间点
    LocalDateTime startDateTime = startDate.atStartOfDay();
    LocalDateTime endDateTime = endDate.atTime(LocalTime.MAX);
    
    List<CarResponse> cars = myRepository.getCarsSoldBetween(startDateTime, endDateTime);
    return cars;
}

步骤2:修改Repository方法

因为你的soldDate是DB2的TIMESTAMP类型,对应实体类的字段应该是LocalDateTime,所以Repository的参数也要改成LocalDateTime:

import java.time.LocalDateTime;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;

@Query("select c from CarEntity c where c.carType = 'SPORT' and c.soldDate between ?1 and ?2")
List<CarEntity> getCarsSoldBetween(LocalDateTime startDateTime, LocalDateTime endDateTime);

方案2:统一使用java.sql.Date(兼容旧代码)

如果你暂时不想切换到新API,那要确保控制器和Repository中的日期类型完全一致,避免util.Date和sql.Date混用。

步骤1:修改控制器参数为java.sql.Date

明确指定参数类型为java.sql.Date,Spring就能直接将路径变量的字符串转换为该类型:

import java.sql.Date;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@GetMapping(path = "/{startDate}/{endDate}")
public List<CarResponse> getCarsSoldBetween(
    @PathVariable("startDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
    @PathVariable("endDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate) {
    // 注意:java.sql.Date会截断时间,所以要把结束日期加1天,确保包含当天所有时间
    Date endDatePlusOne = Date.valueOf(endDate.toLocalDate().plusDays(1));
    List<CarResponse> cars = myRepository.getCarsSoldBetween(startDate, endDatePlusOne);
    return cars;
}

步骤2:修改Repository参数为java.sql.Date

确保Repository的参数和控制器一致,同时调整查询逻辑适配时间截断问题:

import java.sql.Date;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;

@Query("select c from CarEntity c where c.carType = 'SPORT' and c.soldDate >= ?1 and c.soldDate < ?2")
List<CarEntity> getCarsSoldBetween(Date startDate, Date endDatePlusOne);

这里把between改成>=和<,是因为java.sql.Date对应的是当天00:00:00,加1天后的日期就能覆盖原结束日的所有时间点。

方案3:自定义转换器(灵活适配)

如果需要更灵活的转换逻辑,可以自定义一个Spring转换器,让它直接将字符串路径变量转换为java.sql.Date:

import java.sql.Date;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class StringToSqlDateConverter implements Converter<String, Date> {
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Override
    public Date convert(String source) {
        if (source == null || source.isBlank()) {
            return null;
        }
        LocalDate localDate = LocalDate.parse(source, DATE_FORMAT);
        return Date.valueOf(localDate);
    }
}

注册这个转换器后,控制器参数可以直接用java.sql.Date,甚至可以去掉@DateTimeFormat注解(如果格式固定的话)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 22:13:10