This code
criteriaBuilder.literal(LocalDateTime.now().minusDays( (long) root.get("person").get("birthDay").as(Long.class)))
has not compile error but at run time gets this error:
Cannot cast from Expression to int
I tried using String:
criteriaBuilder.literal(LocalDateTime.now().minusDays( Long.parseValue( root.get("person").get("birthDay").toString())))
This also gets error:
ERROR: For input string: “org.hibernate.query.criteria.internal.path.SingularAttributePath@2ed2d35d”; nested exception is java.lang.NumberFormatException: For input string: “org.hibernate.query.criteria.internal.path.SingularAttributePath@2ed2d35d”
Advertisement
Answer
In your first error, you are mixing LocalDateTime.now().minusDays
with the Expression
root.get("person").get("birthDay").as(Long.class)
, and it is not possible.
The error is not detected at compile time because you are actually casting the Expression
root.get("person").get("birthDay").as(Long.class)
to long
:
(long) root.get("person").get("birthDay").as(Long.class)
So everything is fine for the compiler, but at runtime an error will be raised because an Expression
is not a long
value.
In your second error you are trying using:
root.get("person").get("birthDay").toString()
As a result of the evaluation of this code you will get a String
representation of the JPA criteria path you are defining, org.hibernate.query.criteria.internal.path.SingularAttributePath@2ed2d35d
in your use case.
Then, if you compute Long.parseValue
of org.hibernate.query.criteria.internal.path.SingularAttributePath@2ed2d35d
if will raise an error because org.hibernate.query.criteria.internal.path.SingularAttributePath@2ed2d35d
doesn’t represent a valid number.
Under the hood, the problem is that you are trying to apply the operation LocalDateTime.now().minusDays
to an Expression
, but it is not possible, you need to perform the computation in some other suitable way.
It is unclear by your question what you are actually trying to achieve, but the JPA criteria API provides a rich set of builtin expressions you can use as Predicate
s to deal with dates, such as, assuming root.get("person").get("birthDay")
represent a Date
like object:
cb.greaterThanOrEqualTo(root.get("person").get("birthDay"), cb.literal(LocalDate.now())); //... cb.greaterThan(root.get("person").get("birthDay"), cb.literal(LocalDate.now())); //... cb.lessThanOrEqualTo(root.get("person").get("birthDay"), cb.literal(LocalDate.now())); //... cb.lessThan(root.get("person").get("birthDay"), cb.literal(LocalDate.now()));
You can create your own function using cb.function
as well and use that function in your predicates. Consider for that purpose defining a helper method somewhere in your code, for example:
public static Expression<Long> daysBetween( CriteriaBuilder cb, Expression<LocalDate> startDate, Expression<LocalDate> endDate) { return cb.function("DAYS_BETWEEN", long.class, startDate, endDate); }
Where DAYS_BETWEEN
is the name, you can choose the one you consider most appropriate, of a database level predefined or user defined function. In Oracle for example it exists per se, but it should be straight forward to implement in other database systems:
create or replace function days_between...
Then, use this helper method in your code. For example, give me the persons that turns years in the following numberOfDaysToTurnYears
days:
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Person> query = cb.createQuery(Person.class); Root<Person> root = query.from(Person.class); ParameterExpression<Integer> numberOfDaysToTurnYears = cb.parameter(Integer.class); query.where( cb.lessThan( // Here you can use LocalDate.now(), because it is actually a pure literal value daysBetween(cb, root.get("person").get("birthDay"), cb.literal(LocalDate.now())), numberOfDaysToTurnYears ) ); List<Person> people = entityManager.createQuery(query) .setParameter(numberOfDaysToTurnYears, 10) .getResultList();