[英]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]
我的问题:
为什么字符串绑定而数字文字没有绑定?
应该始终对字符串进行参数绑定(而不是将文字放入查询中)以避免 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 。
在问题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.