Spring Data如何将@Param标注的YearMonth类型转为起止LocalDate
业务背景
我所维护的产品费率数据按月存储在数据库中,费率生效起始日固定为上月最后一天,失效结束日固定为当月最后一天。例如2020年3月的费率,对应的startDate为2020-02-29,endDate为2020-03-31。
当前实现
现有Rate实体类定义如下:
@Entity @Data public class Rate { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @ManyToOne @JoinColumn(name="productId") @JsonBackReference private Product product; private LocalDate startDate; private LocalDate endDate; private BigDecimal value; }
对应Spring Data Repository的查询方法如下:
Rate findByProductIdAndStartDateAndEndDate( @Param("productId") Long productId, @Param("startDate") @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) LocalDate startDate, @Param("endDate") @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) LocalDate endDate);
需求目标
希望对调用方屏蔽日期计算逻辑,将上述Repository方法改写为直接接收年月参数的形式,可配合@Query注解写JPQL实现:
//OK with writing JPQL here via @Query Rate findByProductIdAndYearMonth( @Param("productId") Long productId, @Param("yearMonth") @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) YearMonth yearMonth);
已尝试方案
参考相关资料后考虑过使用AttributeConverter实现转换,但该组件仅支持单字段互转,无法实现单个yearMonth参数映射startDate、endDate两个数据库字段的需求。也考虑过新增Service层封装日期转换逻辑后调用Repository,但当前项目使用Spring Data REST,需要支持客户端直接通过/api/rates/search/findByProductIdAndYearMonth路径调用仓库查询接口,因此需要在Repository层直接实现该能力。
解决方案
推荐使用SpEL表达式在查询生成阶段直接计算出对应的起止日期,完全规避不同数据库的日期函数兼容性问题,实现代码如下:
@Query("SELECT r FROM Rate r WHERE r.product.id = :productId " + "AND r.startDate = :#{#yearMonth.minusMonths(1).atEndOfMonth()} " + "AND r.endDate = :#{#yearMonth.atEndOfMonth()}") Rate findByProductIdAndYearMonth( @Param("productId") Long productId, @Param("yearMonth") YearMonth yearMonth);
上述写法直接调用YearMonth自带的方法计算日期,不需要额外适配数据库,Spring Data REST可以直接暴露该查询接口给客户端调用,完全符合你的需求。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

