Java – Uses LocalDate between JPA 2.1 Criteria APIs

Uses LocalDate between JPA 2.1 Criteria APIs… here is a solution to the problem.

Uses LocalDate between JPA 2.1 Criteria APIs

In my entity, I have two fields:

private LocalDate startDate = LocalDate.of(1900, 1, 1);
private LocalDate endDate = LocalDate.of(3000, 1, 1);

Using the JPA Criteria API, I want to select the entity where LocalDate.now() > startDate is located and LocalDate.now() < endDate

I tried the following:

predicates.add(builder.greaterThan(LocalDate.now(), path.<LocalDate> get(Entity_.startDate)));
predicates.add(builder.lessThan(builder.currentDate(), path.<LocalDate> get(Entity_.endDate)));

But I get this error :

The method greaterThan(Expression<? extends Y>, Expression<? extends Y>) in the type CriteriaBuilder is not applicable for the arguments (LocalDate, Path< LocalDate>)

I tried it too:

predicates.add(builder.between(builder.currentDate(), path.<LocalDate> get(Entity_.startDate), path.<LocalDate> get(Entity_.endDate)));

Where the following error occurs:

The method between(Expression<? extends Y>, Expression<? extends Y>, Expression<? extends Y>) in the type CriteriaBuilder is not applicable for the arguments (Expression<Date>, Path<LocalDate>, Path<LocalDate>)

How do I fix this?

Solution

It seems that you need an AttributeConverter, since JPA 2.1 does not directly support LocalDate. Suppose you have an Entity

@Entity
@Getter
public class LocalDateEntity {
   @Id
   @GeneratedValue
   private Long id;
   @Setter
   private LocalDate startDate = LocalDate.of(1900, 1, 1);
   @Setter
   private LocalDate endDate = LocalDate.of(3000, 1, 1);
}

You can use AttributeConverter like this

// import java.sql.Date not java.util.Date;
@Converter(autoApply = true) // this makes it to apply anywhere there is a need
public class LocalDateConverter implements AttributeConverter<LocalDate, Date> {

@Override
   public Date convertToDatabaseColumn(LocalDate date) {
      return Date.valueOf(date);
   }

@Override
   public LocalDate convertToEntityAttribute(Date value) {      
      return value.toLocalDate();
   }
}

After that, you can make CriteriaQuery look like

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<LocalDateEntity> cq = cb.createQuery(LocalDateEntity.class);
Root<LocalDateEntity> from = cq.from(LocalDateEntity.class);
Expression<Date> expCurrDate = cb.currentDate();
cq.where(
      cb.and(
            cb.greaterThan(from.get("endDate"), expCurrDate)
            ,cb.lessThan(from.get("startDate"), expCurrDate)
            OR for example
            cb.lessThan(expCurrDate, from.get("endDate"))
            ,cb.greaterThan(expCurrDate, from.get("startDate"))
            both are expressions no matter in what order
            but note the change in Predicate lt vs. gt
      )
);
TypedQuery<LocalDateEntity> tq = em.createQuery(cq);

Note: While Predicate between(..) also works, it’s a bit different. It includes start and end dates.

Related Problems and Solutions