I am trying convert this JPA QL to criteria builder. JBoss 6.0.
"SELECT ba FROM BankAccount ba WHERE ba.balance >= :amt ORDER BY ba.ownerName ASC"
I wrote this code according to several tutorials.
public List<BankAccount> findWithBalance(int amount) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<BankAccount> cq = cb.createQuery(BankAccount.class);
Root<BankAccount> from = cq.from(BankAccount.class);
ParameterExpression<Integer> balance = cb.parameter(Integer.class);
cq.select(from);
Predicate predicate = cb.gt(from.get("balance"), balance);
cq.where(predicate);
cq.orderBy(cb.asc(from.get("ownerName")));
TypedQuery<BankAccount> query = em.createQuery(cq);
return query.getResultList();
}
But, I am getting a compile error in the line:
Predicate predicate = cb.gt(from.get("balance"), balance);
The error is:
The method gt(Expression<? extends Number>, Expression<? extends Number>) in the type CriteriaBuilder is not applicable for the arguments (Path<Object>, ParameterExpression<Integer>)
Well, I finally found the right way to call the gt() method. Here is the complete solution. Fully tested in JBoss 6.
public List<BankAccount> findWithBalance(int amount) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<BankAccount> cq = cb.createQuery(BankAccount.class);
Root<BankAccount> from = cq.from(BankAccount.class);
ParameterExpression<Integer> balance = cb.parameter(Integer.class);
cq.select(from);
//Here is the trick!
Predicate predicate = cb.gt(from.<Integer> get("balance"), balance);
cq.where(predicate);
cq.orderBy(cb.asc(from.get("ownerName")));
TypedQuery<BankAccount> query = em.createQuery(cq);
query.setParameter(balance, amount);
return query.getResultList();
}
The type safety feature in JPA restricts such comparisons with incompatible types, the compiler itself will raise error.
Here, from.get("balance")
returns the Path<Object>
, but the method can accept parameter of type java.lang.Number
, therefore results in error.
You can try the below code.
//--
Metamodel metamodel = em.getMetamodel();
EntityType<BankAccount> pClass = metamodel.entity(BankAccount.class);
Predicate predicate = cb.gt(from.get(pClass.getSingularAttribute("balance", Integer.class)), balance);
//--
If you are using Metamodel API, then you can retieve directly by specifying ClassName_.field as cb.gt(from.get(BankAccount_.balance), balance)
which is much cleaner & easy to debug.
But if you are having many entities, then it may be difficult to write their Metamodel classes manually, if the JPA provider doesn't generate them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With