Flash.itsportsbetDocsFinance & Crypto
Related
Optimizing docs.rs Builds: A Guide to Reducing Default TargetsCrypto Markets Under Pressure: Tariff Turmoil and Tokenization MilestonesHow to Interpret Cisco's Earnings Beat and Job Cuts in the Age of AIAnthropic Cracks Down on Unauthorized Secondary Market Share Sales5 Critical Factors Behind PayPal's Post-Earnings Stock Drop and What Investors Should KnowNavigating the Evolving Crypto Landscape: A Step-by-Step Guide to Market and Institutional SignalsFord's Strong Q1 Performance: Tariff Refund and Plant Recovery Drive Forecast UpgradeRecord Preschool Funding Masks Growing Quality Gap, Report Warns

Mastering Date Range Queries in Hibernate: A Step-by-Step How-To Guide

Last updated: 2026-05-15 16:56:30 · Finance & Crypto

Introduction

Querying records between two specific dates is a routine task in many enterprise applications—whether you're generating monthly reports, filtering user activity logs, or retrieving orders within a billing cycle. Hibernate, as a popular JPA implementation, offers multiple ways to perform these temporal queries: HQL, the Criteria API, and native SQL. This step-by-step guide walks you through each approach, highlighting common pitfalls and best practices to ensure accurate results.

Mastering Date Range Queries in Hibernate: A Step-by-Step How-To Guide
Source: www.baeldung.com

What You Need

  • Java 8 or later (to use java.time types)
  • Hibernate 5+ (supports LocalDateTime natively)
  • An entity class with a date/time field (e.g., Order with creationDate)
  • A configured SessionFactory and database with a date column
  • Basic familiarity with Hibernate setup and queries

Step-by-Step Instructions

Step 1: Prepare Your Entity Class for Date Fields

Start by defining an entity that holds a date-time field. For modern Hibernate (5+), you can directly use Java 8's LocalDateTime without extra annotations. For example, an Order entity:

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String trackingNumber;
    private LocalDateTime creationDate;
    // getters and setters
}

If you're still using legacy java.util.Date, you must annotate it with @Temporal(TemporalType.TIMESTAMP) to specify the date+time storage type.

Step 2: Understand the BETWEEN Operator Pitfall

The BETWEEN operator in HQL is inclusive on both ends. When using LocalDateTime, a query like WHERE o.creationDate BETWEEN :start AND :end includes records whose timestamps are exactly equal to :start or :end. But if :end is set to 2024-01-31T00:00:00, any order placed after that midnight—say at 10:30 AM on Jan 31—will be excluded. To capture an entire day, you would need to set the time to 23:59:59.999, which is fragile. A safer alternative is the half-open interval pattern.

Step 3: Write a Date Range Query with HQL Using BETWEEN

If your use case truly requires inclusive bounds and you control the endpoint timestamps precisely, you can still use BETWEEN. Example:

String hql = "FROM Order o WHERE o.creationDate BETWEEN :startDate AND :endDate";
List<Order> orders = session.createQuery(hql, Order.class)
  .setParameter("startDate", startDate)
  .setParameter("endDate", endDate)
  .getResultList();

This works fine when endDate is already set to the last millisecond of the day. However, for most day-based filters, proceed to Step 4.

Step 4: Write a Robust HQL Query Using Comparison Operators (Half-Open Interval)

The most reliable pattern for querying full calendar days or months is the half-open interval: inclusive on start (>=) and exclusive on end (<). This avoids time-of-day miscalculations. For example, to get all orders from January 2024:

String hql = "FROM Order o WHERE o.creationDate >= :startDate AND o.creationDate < :endDate";
LocalDateTime start = LocalDateTime.of(2024, 1, 1, 0, 0);
LocalDateTime end = LocalDateTime.of(2024, 2, 1, 0, 0);
List<Order> orders = session.createQuery(hql, Order.class)
  .setParameter("startDate", start)
  .setParameter("endDate", end)
  .getResultList();

This captures every order from Jan 1 00:00:00 up to (but not including) Feb 1 00:00:00, effectively covering the entire month of January. You can apply this pattern for any boundary (days, weeks, years).

Mastering Date Range Queries in Hibernate: A Step-by-Step How-To Guide
Source: www.baeldung.com

Step 5: Query Dates with the Criteria API

The Criteria API offers a type-safe alternative. Use cb.between() or cb.greaterThanOrEqualTo() with cb.lessThan(). Example for the half-open interval:

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
Root<Order> root = cq.from(Order.class);
Predicate startPred = cb.greaterThanOrEqualTo(root.get("creationDate"), start);
Predicate endPred = cb.lessThan(root.get("creationDate"), end);
cq.where(startPred, endPred);
List<Order> orders = session.createQuery(cq).getResultList();

The same half-open principle applies here.

Step 6: Fallback to Native SQL for Complex Database-Specific Functions

Sometimes you need database-specific date functions (e.g., DATE() to truncate time). Use native SQL with createNativeQuery():

String sql = "SELECT * FROM orders WHERE creation_date >= :startDate AND creation_date < :endDate";
List<Order> orders = session.createNativeQuery(sql, Order.class)
  .setParameter("startDate", start)
  .setParameter("endDate", end)
  .getResultList();

Be aware that this ties your code to a specific database dialect.

Tips for Success

  • Always prefer half-open intervals (>= and <) when filtering by full days or months. This avoids the fragile 23:59:59 hack.
  • Consider time zones if your dates are stored in UTC and you query from a local time zone. Convert to UTC before passing parameters.
  • Test edge cases: midnight boundaries, leap seconds (rare but possible depending on your DB), and records with null date fields.
  • Use HQL for readability and portability; the Criteria API for type safety; native SQL only when you need database-specific features.
  • Parameterize your queries to avoid SQL injection and improve performance via query plan caching.