首页 文章

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

提问于
浏览
9

我正在使用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]

我的问题:

  • 为什么整数列表的元素直接内联到sql,而字符串列表的元素作为预处理语句参数处理?

  • 这个功能特定于Hibernate还是由JPA保证?

  • 从DB角度来看,哪两个应该是首选?

  • 这是int-yes字符串 - 没有内联与sql注入有关吗?

  • 这是否与RDMBS可以处理的sql IN子句中的值数限制有关?

  • 如何编写一个条件查询,它将以与String参数列表相同的方式处理Integer参数列表 .

4 回答

  • 5

    Why are strings bound and numeric literals not bound?

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

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

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

    Which is better for the db?

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

    Is this in the JPA spec?

    没有 .

    Is this related to the # of values allowed in an in statement?

    没有 .

    Can I have a numeric literal bound instead of inserted directly into the query

    是的,但它有点冗长 .

    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);
    

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

    References:

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

  • -1
    • 因为字符串可以包含SQL和整数不能,所以不需要从安全方面(SQL注入) .

    • JPA规范没有像您希望的那样明确指定它 . 这似乎是一个实现细节 .

    • String参数的Prepared语句参数 . 对于int参数,它并不重要,因为它们不会被黑客滥用 .

    • 是的

    • 您应该在您正在使用的特定数据库的文档中查找 . JPA并不关心这些事情 .

    • 为什么?有什么好处?唐't try to improve things when you don't知道你在改进什么 .

  • 1

    我完全同意Niels的意思,不应该内联字符串参数以防止SQL注入 .

    但我用DataNucleus 4.1.9和Derby db检查了它,令我惊讶的是日志也显示了字符串的内联 . 此外,它还显示DataNucleus使用“OR”条件的组合实现“IN”条件查询 . 可能这不如Hibernate,可能存在安全风险 . 更高级别抽象可能存在危险的示例 . 你不能太谨慎:-) .

    日志:

    Begin compiling prepared statement: 
    SELECT 'pack.entities.I' AS NUCLEUS_TYPE,DN_THIS.ID,DN_THIS.INTATTR,DN_THIS.STRINGATTR FROM I DN_THIS 
    WHERE (((DN_THIS.INTATTR = 10) OR (DN_THIS.INTATTR = 20)) OR (DN_THIS.INTATTR = 30)) 
    AND (((DN_THIS.STRINGATTR = 'a') OR (DN_THIS.STRINGATTR = 'b')) OR (DN_THIS.STRINGATTR = 'c')) :End prepared statement
    
    Tue Apr 26 15:46:01 CEST 2016 Thread[DRDAConnThread_3,5,derby.daemons] 
    End compiling prepared statement: 
    SELECT 'pack.entities.I' AS NUCLEUS_TYPE,DN_THIS.ID,DN_THIS.INTATTR,DN_THIS.STRINGATTR FROM I DN_THIS 
    WHERE (((DN_THIS.INTATTR = 10) OR (DN_THIS.INTATTR = 20)) OR (DN_THIS.INTATTR = 30)) 
    AND (((DN_THIS.STRINGATTR = 'a') OR (DN_THIS.STRINGATTR = 'b')) OR (DN_THIS.STRINGATTR = 'c')) :End prepared statement
    
    Tue Apr 26 15:46:01 CEST 2016 Thread[DRDAConnThread_3,5,derby.daemons] Executing prepared statement: 
    SELECT 'pack.entities.I' AS NUCLEUS_TYPE,DN_THIS.ID,DN_THIS.INTATTR,DN_THIS.STRINGATTR FROM I DN_THIS 
    WHERE (((DN_THIS.INTATTR = 10) OR (DN_THIS.INTATTR = 20)) OR (DN_THIS.INTATTR = 30)) 
    AND (((DN_THIS.STRINGATTR = 'a') OR (DN_THIS.STRINGATTR = 'b')) OR (DN_THIS.STRINGATTR = 'c')) :End prepared statement
    
  • 3

    为什么整数列表的元素直接内联到sql,而String列表的元素作为预处理语句参数处理?

    这可能是因为你使用的整数是原语 .

    尝试以这种方式使用它

    Arrays.asList(Integer.value(10),Integer.value(20),Integer.value(30))

相关问题