如何将位置/命名参数动态设置为JPA标准查询? [英] How to set positional/named parameters dynamically to JPA criteria query?

查看:156
本文介绍了如何将位置/命名参数动态设置为JPA标准查询?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

除非设置为 entityManager.createQuery(criteriaQuery).setParameter(Parameter p,T t),否则Hibernate提供程序不会为非字符串类型参数生成预准备语句。 默认情况下由EclipseLink完成。



设置这些参数的方法是什么,如果它们是在运行时动态提供的。例如,

  CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); 
CriteriaQuery< Long> criteriaQuery = criteriaBuilder.createQuery(Long.class);
Metamodel metamodel = entityManager.getMetamodel();
EntityType< Product> entityType = metamodel.entity(Product.class);
Root<产品> root = criteriaQuery.from(entityType);
criteriaQuery.select(criteriaBuilder.count(root));

List< Predicate> predicates = new ArrayList< Predicate>(); (Map.Entry< String,String> entry:filters.entrySet()){
if(entry.getKey()。equalsIgnoreCase(prodId)){$ b $(

) b predicates.add(criteriaBuilder.equal(root.get(Product_.prodId),Long.parseLong(entry.getValue()。trim())));
} else if(entry.getKey()。equalsIgnoreCase(prodName)){
predicates.add(criteriaBuilder.like(root.get(Product_.prodName),%+ entry.getValue ().trim()+%));
} else if(entry.getKey()。equalsIgnoreCase(marketPrice)){
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(Product_.marketPrice),new BigDecimal(entry.getValue )。修剪())));
} else if(entry.getKey()。equalsIgnoreCase(salePrice)){
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(Product_.salePrice),new BigDecimal(entry.getValue )。修剪())));
} else if(entry.getKey()。equalsIgnoreCase(quantity)){
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(Product_.quantity),Integer.valueOf(entry.getValue ()。修剪()))); (!predicates.isEmpty()){
criteria $



$ b $ predicate.toArray(new Predicate [0]));
}

Long count = entityManager.createQuery(criteriaQuery).getSingleResult();

在这种情况下, prodName 是一个String类型的参数。因此,它自动绑定到位置参数。其余(非字符串类型)不是。它们都被它们的值所取代。



通过使用,它们被设置为 createQuery()例如,< / p>

  ParameterExpression< BigDecimal> EXP = criteriaBuilder.parameter(BigDecimal.class); 
entityManager.createQuery(criteriaQuery).setParameter(exp,new BigDecimal(1000));

如何设置这样的动态参数,以便可以生成准备好的查询?






这在EclipseLink中确实不需要,它们会自动映射到位置参数。



这可以通过Hibernate提供程序完成吗?



我使用Hibernate 4.2.7提供的JPA 2.0如果设置了所有参数,则上述标准查询生成的语句将如下所示。


$ b

  SELECT count(product0_.prod_id)AS col_0_0_ 
FROM projectdb.product product0_
WHERE product0_.market_price> = 1000
AND(product0_.prod_name LIKE?)
AND product0_.prod_id = 1
AND product0_.quantity> = 1
AND product0_.sale_price> = 1000






在EclipseLink(2.3.2)下运行上面的条件查询,导致生成以下SQL语句。


$ b

  SELECT count(prod_id)
FROM projectdb.product
WHERE(((((market_price> =?)
AND prod_name LIKE? )
AND(prod_id =?))
AND(quantity> =?))
AND(sale_price> =?))

bind => [1000,%product1%,1,1,1000]

即参数化查询。






不可能这样做。

  // ... 

TypedQuery< Long> typedQuery = entityManager.createQuery(criteriaQuery); (Map.Entry< String,String> entry:filters.entrySet()){
if(entry.getKey()。equalsIgnoreCase(discountId)){$ b $(

) b ParameterExpression< Long> parameterExpression = criteriaBuilder.parameter(Long.class);
predicates.add(criteriaBuilder.equal(root.get(Discount_.discountId),parameterExpression));
typedQuery.setParameter(parameterExpression,Long.parseLong(entry.getValue()。trim()));
}

// .. if-else-if的其余部分。
}

// ...
// ...
// ...
Long count = typedQuery.setFirstResult(first)。 setMaxResults(pageSize的).getSingleResult();

这样做会导致 java.lang.IllegalArgumentException:参数的名称定位不能为空要抛出的异常。

解决方案

为什么不使用 CriteriaBuilder.parameter ?为什么要在生成的SQL中插入参数,如位置参数?你想通过在SQL服务器上准备它来优化你的查询,然后只发送参数吗?我想在这种情况下,最好明确指定 CriteriaBuilder.parameter



顺便说一下,你看到 QueryDSL


Hibernate provider does not generate prepared statement for non-string type parameters unless they are set to entityManager.createQuery(criteriaQuery).setParameter(Parameter p, T t); as done by EclipseLink, by default.

What is the way to set such parameters, if they are supplied dynamically at run time. For example,

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Long>criteriaQuery=criteriaBuilder.createQuery(Long.class);
Metamodel metamodel=entityManager.getMetamodel();
EntityType<Product> entityType = metamodel.entity(Product.class);
Root<Product> root = criteriaQuery.from(entityType);
criteriaQuery.select(criteriaBuilder.count(root));

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

for (Map.Entry<String, String> entry : filters.entrySet()) {
    if (entry.getKey().equalsIgnoreCase("prodId")) {
        predicates.add(criteriaBuilder.equal(root.get(Product_.prodId), Long.parseLong(entry.getValue().trim())));
    } else if (entry.getKey().equalsIgnoreCase("prodName")) {
        predicates.add(criteriaBuilder.like(root.get(Product_.prodName), "%" + entry.getValue().trim() + "%"));
    } else if (entry.getKey().equalsIgnoreCase("marketPrice")) {
        predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(Product_.marketPrice), new BigDecimal(entry.getValue().trim())));
    } else if (entry.getKey().equalsIgnoreCase("salePrice")) {
        predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(Product_.salePrice), new BigDecimal(entry.getValue().trim())));
    } else if (entry.getKey().equalsIgnoreCase("quantity")) {
        predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(Product_.quantity), Integer.valueOf(entry.getValue().trim())));
    }
}

if (!predicates.isEmpty()) {
    criteriaQuery.where(predicates.toArray(new Predicate[0]));
}

Long count = entityManager.createQuery(criteriaQuery).getSingleResult();

In this case, the parameter to prodName is a String type parameter. Hence, it is automatically bound to a positional parameter ?. The rest (non-string type) are however not. They all are just replaced by their values.

They are required to be set to createQuery() by using, for example,

ParameterExpression<BigDecimal> exp=criteriaBuilder.parameter(BigDecimal.class);
entityManager.createQuery(criteriaQuery).setParameter(exp, new BigDecimal(1000));

How to set such dynamic parameters so that a prepared query can be generated?


This is indeed not required in EclipseLink where they are automatically mapped to positional parameters.

Can this be done with Hibernate provider?

I'm using JPA 2.0 provided by Hibernate 4.2.7 final.


If all the parameters are set then, the statement generated by above criteria query would be like as shown below.

SELECT count(product0_.prod_id) AS col_0_0_ 
FROM   projectdb.product product0_ 
WHERE  product0_.market_price >= 1000 
       AND ( product0_.prod_name LIKE ? ) 
       AND product0_.prod_id = 1 
       AND product0_.quantity >= 1 
       AND product0_.sale_price >= 1000 


I have just run the above criteria query under EclipseLink (2.3.2) that resulted in producing the following SQL statement.

SELECT count(prod_id) 
FROM   projectdb.product 
WHERE  ( ( ( ( ( market_price >= ? ) 
           AND prod_name LIKE ? ) 
         AND ( prod_id = ? ) ) 
       AND ( quantity >= ? ) ) 
     AND ( sale_price >= ? ) ) 

bind => [1000, %product1%, 1, 1, 1000]

i.e a parameterized query.


Doing like the following is not possible.

//...

TypedQuery<Long> typedQuery = entityManager.createQuery(criteriaQuery);

for (Map.Entry<String, String> entry : filters.entrySet()) {
    if (entry.getKey().equalsIgnoreCase("discountId")) {
        ParameterExpression<Long> parameterExpression = criteriaBuilder.parameter(Long.class);
        predicates.add(criteriaBuilder.equal(root.get(Discount_.discountId), parameterExpression));
        typedQuery.setParameter(parameterExpression, Long.parseLong(entry.getValue().trim()));
    }

    //...The rest of the if-else-if ladder.
}

//...
//...
//...
Long count = typedQuery.setFirstResult(first).setMaxResults(pageSize).getSingleResult();

Doing so would cause the java.lang.IllegalArgumentException: Name of parameter to locate cannot be null exception to be thrown.

解决方案

Why don't use CriteriaBuilder.parameter? And why do you want parameters to be inserted like positional parameters in the generated SQL? Do you want optimize you query by prepairing it on SQL server and then sending parameters only? I suppose in this case it is better to explicitly specify CriteriaBuilder.parameter.

And BTW did you see QueryDSL?

这篇关于如何将位置/命名参数动态设置为JPA标准查询?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆