繁体   English   中英

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

[英]Why Hibernate inlines Integer parameter list passed to JPA Criteria Query?

我正在使用 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. 为什么整数列表的元素直接内联到 sql 而字符串列表的元素作为准备好的语句参数处理?
  2. 此功能是特定于 Hibernate 的还是由 JPA 保证的?
  3. 从数据库的角度来看,应该首选哪两个?
  4. 这个 int-yes string-no 内联是否与 sql 注入有某种关系?
  5. 这是否与 RDMBS 可以处理的 sql IN 子句中的值数量限制有关?
  6. 如何编写一个标准查询,它将以与字符串参数列表相同的方式处理整数参数列表。

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

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

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

所以 iirc 导致我在这里使用文字的问题与规模和操作有关。 这意味着(再次,iirc)某些数据库需要知道类型信息才能正确处理诸如...之类的事情? + ? ...,等等。所以选择是将所有这些参数包装在 CAST 函数调用中,并希望/祈祷数据库实现正确的 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);

这将长期使用参数绑定。 它只是一个参数,但可以很容易地从这里推断出多个参数,而辅助方法可以清理这些内容。

参考资料:

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

  1. 因为字符串可以包含 SQL 而整数不能包含,所以从安全方面(SQL 注入)没有必要。
  2. JPA 规范没有像您希望的那样明确指定它。 这似乎是一个实现细节。
  3. 为字符串参数准备的语句参数。 对于 int 参数并不重要,因为它们不会被黑客滥用。
  4. 是的
  5. 您应该在您使用的特定数据库的文档中查找。 JPA 不关心这些事情。
  6. 为什么? 有什么好处? 当你不知道自己在改进什么时,不要试图改进。

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

<property name="hibernate.criteria.literal_handling_mode" value="bind"/>

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

literal_handling_mode的休眠文档:

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

我完全同意 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM