Spring Data - 如何将 YearMonth 类型的 @Param 转换为 LocalDate 的开始和结束?
Posted
技术标签:
【中文标题】Spring Data - 如何将 YearMonth 类型的 @Param 转换为 LocalDate 的开始和结束?【英文标题】:Spring Data - How to Convert @Param of type YearMonth to start and end LocalDate? 【发布时间】:2021-10-24 05:31:30 【问题描述】:后台域
我有每月存储在数据库中的产品的费率。开始日期始终是上个月的最后一天,结束日期是当月的最后一天。例如,2020 年 3 月汇率的开始日期为 2020 年 2 月 29 日,结束日期为 2020 年 3 月 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 存储库,它具有以下方法:
Rate findByProductIdAndStartDateAndEndDate(
@Param("productId") Long productId,
@Param("startDate") @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) LocalDate startDate,
@Param("endDate") @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) LocalDate endDate);
目标
我想通过(例如)通过重写findByProductIdAndStartDateAndEndDate
来调用Rate
存储库来从客户那里抽象出这个细节,如下所示:
//OK with writing JPQL here via @Query
Rate findByProductIdAndYearMonth(
@Param("productId") Long productId,
@Param("yearMonth") @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) YearMonth yearMonth);
我尝试过/考虑过的事情
在阅读this post 后,我考虑过AttributeConverter
。但我正在尝试从一个字段 yearMonth
转换为两个数据库列 startDate
和 endDate
。
我知道我可以编写一个服务类来获取 YearMonth,然后在确定开始和结束日期后调用 repo。但我正在使用 Spring Data REST 并希望允许客户端通过以下方式直接调用此 repo:
/api/rates/search/findByProductIdAndYearMonth
【问题讨论】:
用@Query
注释findByProductIdAndYearMonth
怎么样?然后编写您的自定义查询。只是一个提示。我不擅长 JPQL。
【参考方案1】:
如果您的数据库支持虚拟列,那么我会考虑创建一个名为 period
的虚拟列,它计算为结束日期的月/年。
如果该虚拟列在您的实体中映射为任何其他属性,则查询变得微不足道。您可以将 JPA 属性设为只读。一般来说,这里的查询可能有优势,因为任何查询都可以简单地写为 period = '0320' 或其他任何内容,而不必担心开始和结束日期。
如果您的数据库不支持虚拟列,那么您可以通过 2 列数据库视图(rate_id、period)实现类似的效果,然后通过 @SecondaryTable
将其连接到 Rate 实体。
在任一解决方案中,您的 Rate 实体都有一个新属性 period
,您可以像查询任何其他列一样对其进行查询和排序。
【讨论】:
好建议。由于 OP 显然使用 Hibernate,org.hibernate.annotations.Formula
可用于映射(虚拟)列。【参考方案2】:
您始终可以做的是使用 SPeL,它调用 @Query
中的自定义方法,为您进行转换。例如创建一个实用程序类,如
package com.acme.util;
import java.time.LocalDate;
import java.time.YearMonth;
public class RateDateUtil
public static LocalDate toEndDate(YearMonth ym)
return ym.atEndOfMonth();
public static LocalDate toStartDate(YearMonth ym)
return ym.minusMonths(1).atEndOfMonth();
然后在你的@Query
中使用它
@Query("from Rate r join r.product p where p.id = :##productId and r.startDate = :#T(com.acme.util.RateDateUtil).toStartDate(#yearMonth) and r.endDate = :#T(com.acme.util.RateDateUtil).toEndDate(#yearMonth)")
Rate findByProductIdAndYearMonth(@Param("productId") Long productId, @Param("yearMonth") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) YearMonth yearMonth);
请注意,在较旧的 spring-data-jpa 版本中,您不能混合使用 SPeL 和 JPA 方式,这就是 productId
也需要包装的原因。这可能已在较新的版本中得到修复(我使用 1.1.x 版本对此进行了测试)。有关更多信息,请参阅 this answer 以了解其他问题。
【讨论】:
以上是关于Spring Data - 如何将 YearMonth 类型的 @Param 转换为 LocalDate 的开始和结束?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 spring-data-rest 与 spring websocket 混合到一个实现中
如何将多个日期之间的搜索与 Spring Data JPA 的 CrudRepository 结合起来?