java - 为什么 Hibernate 内联传递给 JPA Criteria Query 的 Integer 参数列表?

标签 java hibernate jpa prepared-statement criteria-api

我正在使用 JPA Criteria API 构建查询。当我使用 javax.persistence.criteria.Path#in(Collection<?>) 创建两个限制谓词时方法生成的 SQL 查询与我预期的略有不同。

int 上构建的第一个谓词属性生成的 SQL 内联参数集合的所有元素:in (10, 20, 30) .

String 上构建的第二个谓词属性生成的参数化 SQL:in (?, ?, ?) .

让我展示一下:

实体:

@Entity
public class A {
    @Id 
    private Integer id;
    private int intAttr;
    private String stringAttr;
    //getter/setters
}

查询:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<A> q = cb.createQuery(A.class);
Root<A> root = q.from(A.class);
q.where(
    root.get("intAttr").in(Arrays.asList(10, 20, 30)),
    root.get("stringAttr").in(Arrays.asList("a", "b", "c"))
);
entityManager.createQuery(q).getResultList();

日志:

select
    a0_.id as id1_0_,
    a0_.intAttr as intAttr2_0_,
    a0_.stringAttr as stringAt3_0_ 
from
    A a0_ 
where
    (
        a0_.intAttr in (
            10 , 20 , 30
        )
    ) 
    and (
        a0_.stringAttr in (
            ? , ? , ?
        )
    ) 
org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - [a] 
org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - [b] 
org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [3] as [VARCHAR] - [c] 

我的问题:

  1. 为什么 Integer 列表的元素直接内联到 sql 而 String 列表的元素作为准备语句参数处理?
  2. 此功能是 Hibernate 特有的还是由 JPA 保证?
  3. 从数据库的角度来看,应该首选两者中的哪一个?
  4. 这个 int-yes string-no 内联是否以某种方式与 sql 注入(inject)有关?
  5. 这是否与 RDMBS 可以处理的 sql IN 子句中值的数量限制有某种关系?
  6. 如何编写一个条件查询,以与处理字符串参数列表相同的方式处理整数参数列表。

最佳答案

为什么要绑定(bind)字符串而不绑定(bind)数字文字?

应该始终对字符串进行参数绑定(bind)(而不是将文字放入查询中)以避免 SQL 注入(inject)。

然而,真正的问题是为什么要将文字直接插入查询而不是使用绑定(bind)。原来的原因是:

So iirc the issue that lead me to use literals here had to do with scale and operations. Meaning (again, iirc) some databases needed to know type information to be able to properly handle something like ... ? + ? ..., etc. So the choice was to either wrap all such params in CAST function calls and hope/pray the db implemented a proper CAST function or use literals. In the end I opted for the literal route because, well, thats what the user asked for up front. Wrapping in function calls will limit the databases ability to leverage indexes in quite a few databases.

哪个对数据库更好?

这取决于数据库和查询,可能不会产生巨大差异。例如,Oracle 只能在值为字面量时进行某些分区,而其他数据库只能在值为绑定(bind)参数时进行某些优化。如果它成为一个问题(例如,您对它进行分析,并且您知道这是让您变慢的原因),那么只需切换到另一种方法。

这在 JPA 规范中吗?

没有。

这与 in 语句中允许的值的数量有关吗?

没有。

我可以绑定(bind)数字文字而不是直接插入到查询中吗

是的,但是有点冗长。

CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Foo> query = cb.createQuery(Foo.class);
Root<Foo> root = query.from(Foo.class);
ParameterExpression<Long> paramOne = cb.parameter(Long.class);
Predicate versionPredicate = root.get("bar").in(paramOne);
query.select(root).where(versionPredicate);
TypedQuery<Foo> typedQuery = getEntityManager().createQuery(query);
typedQuery.setParameter(paramOne, 1L);

这将长期使用参数绑定(bind)。它只有一个参数,但可以很容易地从这里推断出多个参数,辅助方法可以清理一切。

引用资料:

大部分推理在 HHH-6280 中进行了解释和讨论。 . 执行此渲染的特定方法是 LiteralExpression.render .

关于java - 为什么 Hibernate 内联传递给 JPA Criteria Query 的 Integer 参数列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36811521/

相关文章:

java - 如何获取先科的ZoneId?

Java + Selenium : ElementNotVisibleException

java - Hibernate 从数据库中获取列表

java - 从 4.2.7/4.3.0.CR1 开始,Hibernate JPA OneToOne 孤立删除仍然不起作用

java - Order By 之后的 SQL 注入(inject)

java - 人们是否提供多种机制来在 API 中做同样的事情?

java - 在 onDestroy 中执行长时间运行的操作

java - 在 JPA/Hibernate 中删除然后查询失败(已删除的实体传递到持久化)

jpa - 在合并/持久化之前指定 bean 验证组?

MongoDB 根据最新日期查询多个不同的值