1. 简介
本文将介绍如何在 Spring Data JPA 中基于日期和时间字段查询实体。
我们先回顾一下 JPA 中日期与时间的映射方式,然后定义一个包含日期时间字段的实体类,并创建对应的 Spring Data 仓库接口来实现查询功能。内容简单粗暴,直奔主题,适合有经验的开发者快速查阅和踩坑参考。
2. JPA 中的日期与时间映射
在正式查询前,先温习一下 JPA 如何映射日期和时间类型。你需要明确字段表示的是:
- 仅日期(年月日)
- 仅时间(时分秒)
- 还是日期+时间(时间戳)
除了可选的 @Column
注解外,还需要使用 @Temporal
注解来指定字段的语义类型。该注解接收一个 TemporalType
枚举值作为参数,包括:
- ✅
TemporalType.DATE
—— 只保留年月日(如:2025-04-05) - ✅
TemporalType.TIME
—— 只保留时分秒(如:14:30:00) - ✅
TemporalType.TIMESTAMP
—— 保留完整的时间戳(如:2025-04-05 14:30:00)
⚠️ 注意:虽然 Java 8 引入了
LocalDateTime
、ZonedDateTime
等新时间类型,可以直接被 JPA 2.2+ 支持而无需@Temporal
,但如果你还在用java.util.Date
或java.util.Calendar
,那就必须加上@Temporal
,否则可能踩坑。
更详细的 JPA 时间类型映射说明可参考 这篇文章。
3. 实战示例
一旦实体类正确配置,Spring Data JPA 的查询机制就能无缝支持日期时间字段。你可以使用:
- ✅ 方法名推导(Query Methods)
- ✅
@Query
注解写原生或 JPQL 查询
下面通过一个实际例子演示。
3.1. 定义实体类
假设我们有一个 Article
文章实体,包含发布日期、发布时间以及创建时间戳:
@Entity
public class Article {
@Id
@GeneratedValue
Integer id;
@Temporal(TemporalType.DATE)
Date publicationDate;
@Temporal(TemporalType.TIME)
Date publicationTime;
@Temporal(TemporalType.TIMESTAMP)
Date creationDateTime;
}
📌 说明:这里特意把“发布时间”拆成 publicationDate
和 publicationTime
两个字段,是为了覆盖三种 TemporalType
类型,便于演示。
3.2. 创建 Repository 查询接口
接下来定义 Spring Data 的 ArticleRepository
,实现三个典型的时间查询场景:
public interface ArticleRepository
extends JpaRepository<Article, Integer> {
List<Article> findAllByPublicationDate(Date publicationDate);
List<Article> findAllByPublicationTimeBetween(
Date publicationTimeStart,
Date publicationTimeEnd);
@Query("select a from Article a where a.creationDateTime <= :creationDateTime")
List<Article> findAllWithCreationDateTimeBefore(
@Param("creationDateTime") Date creationDateTime);
}
逐个解释:
方法名 | 查询逻辑 | 使用机制 |
---|---|---|
findAllByPublicationDate |
查询指定发布日期的文章 | Query Method(按日期匹配) |
findAllByPublicationTimeBetween |
查询发布时间在某个时间段内的文章 | Query Method(时间区间) |
findAllWithCreationDateTimeBefore |
查询创建时间早于某时刻的文章 | @Query + JPQL |
✅ 关键点:
- 第一个方法只比较 日期部分(忽略时分秒)
- 第二个方法只比较 时间部分(忽略年月日)
- 第三个方法则完整比较 日期+时间
也就是说,Spring Data JPA 会根据字段上的 @Temporal
类型自动截取参数中的对应部分进行匹配,无需手动处理。
3.3. 编写测试验证查询
最后写几个集成测试,验证查询是否按预期工作。
@RunWith(SpringRunner.class)
@DataJpaTest
public class ArticleRepositoryIntegrationTest {
@Autowired
private ArticleRepository repository;
@Test
public void whenFindByPublicationDate_thenArticles1And2Returned() throws Exception {
List<Article> result = repository.findAllByPublicationDate(
new SimpleDateFormat("yyyy-MM-dd").parse("2018-01-01"));
assertEquals(2, result.size());
assertTrue(result.stream()
.map(Article::getId)
.allMatch(id -> Arrays.asList(1, 2).contains(id)));
}
@Test
public void whenFindByPublicationTimeBetween_thenArticles2And3Returned() throws Exception {
List<Article> result = repository.findAllByPublicationTimeBetween(
new SimpleDateFormat("HH:mm").parse("15:15"),
new SimpleDateFormat("HH:mm").parse("16:30"));
assertEquals(2, result.size());
assertTrue(result.stream()
.map(Article::getId)
.allMatch(id -> Arrays.asList(2, 3).contains(id)));
}
@Test
public void givenArticlesWhenFindWithCreationDateThenArticles2And3Returned() throws Exception {
List<Article> result = repository.findAllWithCreationDateTimeBefore(
new SimpleDateFormat("yyyy-MM-dd HH:mm").parse("2017-12-15 10:00"));
assertEquals(2, result.size());
assertTrue(result.stream()
.map(Article::getId)
.allMatch(id -> Arrays.asList(2, 3).contains(id)));
}
}
📌 测试要点:
- 所有
SimpleDateFormat
都需正确设置格式,避免解析错误 - 注意时区问题(本例默认使用系统时区)
@DataJpaTest
提供了内存数据库环境,适合做轻量级集成测试
测试通过说明:三个查询都能准确按日期、时间或时间戳过滤数据,在真实项目中可直接套用。
4. 总结
本文演示了如何在 Spring Data JPA 中基于日期和时间字段进行查询:
- 使用
@Temporal
明确字段语义(DATE / TIME / TIMESTAMP) - 利用 Query Method 或
@Query
实现灵活查询 - Spring Data 会自动根据
TemporalType
截取参数的有效部分进行比较 - 测试验证是确保逻辑正确的关键步骤
✅ 推荐实践:
- 新项目建议使用 Java 8 时间 API(如
LocalDateTime
),避免Date
类型的坑 - 若必须用
Date
,务必加上@Temporal
注解 - 时间查询容易受时区影响,建议统一使用 UTC 存储
本文完整源码已发布在 GitHub:https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-jpa-query-3