5

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>)
RajV
  • 6,860
  • 8
  • 44
  • 62

3 Answers3

13

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();
}
RajV
  • 6,860
  • 8
  • 44
  • 62
3

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.

Nayan Wadekar
  • 11,444
  • 4
  • 50
  • 73
0

The compiler is complaining because amount is an int, not an Expression, see if you can figure out how to build an Expression that's a constant and use that and you should be good.

Bart
  • 19,692
  • 7
  • 68
  • 77
Francis Upton IV
  • 19,322
  • 3
  • 53
  • 57
  • Can you edit your post with the revised code and give the error message you are currently getting. I can't imagine you are getting the same error if you are using an arg that is a ParameterExpression. And also doing that is probably not what you want. – Francis Upton IV Apr 30 '12 at 23:32
  • I updated the code and the new error message. What I am doing is straight from various tutorials. I don't think criteria query can be done in JBoss 6. – RajV May 01 '12 at 13:39