【翻译】CriteriaBuilder and Dynamic Queries in JPA 2.0

A major new feature of Java EE 6 is JPA 2.0 and in particular the addition of the Criteria API which provides the ability to dynamically construct object-based queries.

This resolves some of the problems which arise when building dynamic native queries. The below example shows how to find customer entities with two search parameters:


public List<CustomerEntity> findCustomers(
final String firstName, final String surname) {

StringBuilder queryBuilder = new StringBuilder(
"select c from Customer where ");
List<String> paramList = new ArrayList<String>();
paramList.add(" upper(c.firstName) like '%?%'"
.replace("?", firstName.toUpperCase()));
paramList.add(" upper(c.surname) like '%?%'"
.replace("?", surname.toUpperCase()));

Iterator itr = paramList.iterator();
while(itr.hasNext()) {
queryBuilder.append(itr.next());
if (itr.hasNext()) {
queryBuilder.append(" and ");
}
}

final Query query = entityManager.createNativeQuery(
queryBuilder.toString());

List<Object> resultList = (List<Object>)query.getResultList();

// iterate, cast, populate and return a list
}


The problem with the above is that it is not type safe and involves iterating over a List of Object where those Objects are themselves Object arrays. Also should Customer contain any child elements, these would have to be retrieved in a separate call.

Using the CriteriaBuilder, the same results can be achieved as shown below:


public List<CustomerEntity> findCustomers(final String firstName, final String surname) {

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<CustomerEntity> query =
builder.createQuery(CustomerEntity.class);
Root<CustomerEntity> cust = query.from(CustomerEntity.class);
query.select(cust);

List<Predicate> predicateList = new ArrayList<Predicate>();

Predicate firstNamePredicate, surnamePredicate;

if ((firstName != null) && (!(firstName.isEmpty()))) {
firstNamePredicate = builder.like(
builder.upper(cust.<String>get("firstName")),
"%"+firstName.toUpperCase()+"%");
predicateList.add(firstNamePredicate);
}

if ((surname != null) && (!(surname.isEmpty()))) {
surnamePredicate = builder.like(
builder.upper(cust.<String>get("surname")),
"%"+surname.toUpperCase()+"%");
predicateList.add(surnamePredicate);
}

Predicate[] predicates = new
Predicate[predicateList.size()];
PredicateList.toArray(predicates);
query.where(predicates);

return entityManager.createQuery(query).getResultList();
}


There is some type safety in the above but it can be furthered tied down by using the metamodel class for the entity, by using the metamodel class's public static members instead of text strings for the entity's attributes. The code would now look like this:


firstNamePredicate = builder.like(
builder.upper(cust.get(CustomerEntity_.firstName)),
"%"+firstName.toUpperCase()+"%");

surnamePredicate = builder.like(
builder.upper(cust.get(CustomerEntity_.surname)),
"%"+surname.toUpperCase()+"%");


Having built metamodel classes using Maven, it's questionable whether it's a worthwhile exercise as any mistakes in the text based approach to finding attribute names should be flagged up by comprehensive unti testing.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值