Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile error when using CriteriaBuilder

Tags:

java

generics

jpa

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>)
like image 994
RajV Avatar asked Apr 30 '12 20:04

RajV


2 Answers

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();
}
like image 196
RajV Avatar answered Sep 26 '22 01:09

RajV


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.

like image 25
Nayan Wadekar Avatar answered Sep 24 '22 01:09

Nayan Wadekar