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

Luk*_*sko 10 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
}
Run Code Online (Sandbox Code Playgroud)

查询:

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();
Run Code Online (Sandbox Code Playgroud)

日志:

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] 
Run Code Online (Sandbox Code Playgroud)

我的问题:

  1. 为什么Integer列表的元素直接内联到sql,String列表的元素作为预处理语句参数处理?
  2. 这个功能是Hibernate特有还是由JPA保证?
  3. 从DB的角度来看,哪两个应该是首选?
  4. 这是int-yes字符串 - 没有内联与sql注入有关吗?
  5. 这是否与RDMBS可以处理的sql IN子句中的值数限制有关?
  6. 如何编写一个条件查询,它将以与String参数列表相同的方式处理Integer参数列表.

Ale*_*rin 6

在问题HHH-9576中添加了一个新参数来解决此问题,自版本5.2.12 (?)

<property name="hibernate.criteria.literal_handling_mode" value="bind"/>
Run Code Online (Sandbox Code Playgroud)

如果您使用此参数,则不再需要 Pace 提出的详细解决方案。

来自literal_handling_mode的hibernate文档:

该枚举定义了 JPA Criteria 如何处理文字。默认情况下(自动),Criteria 查询对任何非数字值的文字使用绑定参数。但是,为了增加 JDBC 语句缓存的可能性,您可能还需要对数值使用绑定参数。BIND 模式将为任何文字值使用绑定变量。INLINE 模式将按原样内联文字值。为了防止 SQL 注入,切勿将 INLINE 与字符串变量一起使用。始终在 INLINE 模式中使用常量。


Pac*_*ace 5

为什么字符串绑定和数字文字没有绑定?

应该总是对字符串进行参数绑定(而不是在查询中放置文字)以避免SQL注入.

但是,真正的问题是,为什么要将文字直接插入查询而不是使用绑定.最初的原因是:

所以iirc导致我在这里使用文字的问题与规模和操作有关.意思(再次,iirc)一些数据库需要知道类型信息才能正确处理类似......的东西?+?...等等.所以选择是在CAST函数调用中包装所有这些参数并希望/祈祷db实现了正确的CAST函数或使用文字.最后,我选择了文字路线,因为这就是用户预先要求的内容.包装函数调用将限制数据库在相当多的数据库中利用索引的能力.

对于db来说哪个更好?

这取决于数据库和查询,可能不会产生巨大的差异.例如,Oracle只能在值为文字时执行某些分区,而其他数据库只能在值为绑定参数时执行某些优化.如果它成为一个问题(例如你描述它,你知道这会减慢你的速度),那么只需切换到另一个方法.

这是JPA规范吗?

没有.

这是否与in语句中允许的值相关?

没有.

我可以使用数字文字绑定而不是直接插入查询

是的,但有点冗长.

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);
Run Code Online (Sandbox Code Playgroud)

那将长期使用参数绑定.它只是一个参数,但可以从这里轻松推断出多个参数,辅助方法可以清理.

参考文献:

HHH-6280中解释和讨论了大多数推理.有关渲染的特定方法是LiteralExpression.render.