Spring Boot中基于日期字段查询SQL汇率数据的正确方法及日期转换
问题解析与解决方案
问题背景
我创建了包含date(日期)和rate(货币汇率)字段的EmployeeExchangeRate实体,想要编写服务层方法,通过日、月、年参数查询对应日期的汇率数据。目前已完成实体类、Repository和服务层方法的编写,但日期字符串解析失败,代码无法正常运行。需要解决两个问题:
- 基于日期查询SQL数据的正确方式
- 如何将日、月、年参数转换为匹配数据库存储的日期格式
现有代码
实体类
@Entity @Getter @Setter @Table(name = "ExchangeRate") public class EmployeeExchangeRate extends BaseEntity { @Basic @Temporal(TemporalType.DATE) @Column(name = "date", nullable = false) private Date date; @Column(name = "rate", nullable = false) private double rate; }
Repository
public interface EmployeeExchangeRateRepository extends JpaRepository<EmployeeExchangeRate, Long> { EmployeeExchangeRate findByDate(Date date); }
存在问题的服务层方法
private void findRate(int day, int month, int year){ String s = day + "-" + month + "-" + year; Optional<EmployeeExchangeRate> foundExchangeRate = exchangeRateRepository.findByDate(new SimpleDateFormat("yyyy/dd/MM").parse(s)); System.out.println(foundExchangeRate.get()); }
核心问题分析
代码解析失败的直接原因:
- 拼接出的字符串格式是
日-月-年(比如15-08-2024),但用SimpleDateFormat("yyyy/dd/MM")去解析,格式完全不匹配,触发ParseException。 SimpleDateFormat是线程不安全类,直接使用存在并发风险。- 直接调用
Optional.get()会在数据不存在时抛出空指针异常。
解决方案
方案一:使用Java 8+ LocalDate(推荐)
JPA 2.2及以上原生支持java.time包下的日期类,这是目前最规范的做法:
- 修改实体类日期字段
@Entity @Getter @Setter @Table(name = "ExchangeRate") public class EmployeeExchangeRate extends BaseEntity { // 替换为LocalDate,无需@Temporal注解 @Column(name = "date", nullable = false) private LocalDate date; @Column(name = "rate", nullable = false) private double rate; }
- 更新Repository方法
public interface EmployeeExchangeRateRepository extends JpaRepository<EmployeeExchangeRate, Long> { // 返回Optional避免空指针 Optional<EmployeeExchangeRate> findByDate(LocalDate date); }
- 重构服务层方法
直接用LocalDate.of构造日期,无需字符串拼接,完全避免解析错误:
private void findRate(int day, int month, int year) { LocalDate targetDate = LocalDate.of(year, month, day); Optional<EmployeeExchangeRate> foundExchangeRate = exchangeRateRepository.findByDate(targetDate); // 安全处理Optional结果 foundExchangeRate.ifPresentOrElse( exchangeRate -> System.out.println(exchangeRate), () -> System.out.println("未找到对应日期的汇率数据") ); }
方案二:兼容旧版Java(使用java.util.Date)
如果必须使用Date类型,修复解析逻辑即可:
private void findRate(int day, int month, int year) { // 按目标格式拼接字符串:yyyy/dd/MM String dateStr = String.format("%d/%02d/%02d", year, day, month); try { // 用匹配的格式解析字符串 Date targetDate = new SimpleDateFormat("yyyy/dd/MM").parse(dateStr); EmployeeExchangeRate foundExchangeRate = exchangeRateRepository.findByDate(targetDate); if (foundExchangeRate != null) { System.out.println(foundExchangeRate); } else { System.out.println("未找到对应日期的汇率数据"); } } catch (ParseException e) { e.printStackTrace(); // 这里可以添加异常处理逻辑,比如返回错误提示 } }
注意:
SimpleDateFormat线程不安全,如果是在多线程环境下使用,建议每次创建新实例,或者用ThreadLocal包装来保证线程安全。
关于MySQL日期存储的说明
MySQL的DATE类型默认以YYYY-MM-DD格式存储,但JPA会自动处理实体类日期类型与数据库存储格式的转换,你不需要手动将日期转换为yyyy/dd/mm格式存入数据库。只要实体类字段与数据库字段映射正确,查询时传入正确的日期对象,JPA会自动生成匹配的SQL查询语句。
内容的提问来源于stack exchange,提问作者GiviXbo
相关产品推荐
相关产品推荐

