我正在使用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 回答
Why are strings bound and numeric literals not bound?
应该总是对字符串进行参数绑定(而不是将文字放在查询中)以避免SQL注入 .
但是,真正的问题是,为什么要将文字直接插入查询而不是使用绑定 . 最初的原因是:
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
是的,但它有点冗长 .
这将使用参数绑定为long . 它只是一个参数,但可以从这里轻松推断出多个参数,辅助方法可以清理 .
References:
大多数推理都在HHH-6280中进行了解释和讨论 . 执行此渲染的特定方法是LiteralExpression.render .
因为字符串可以包含SQL和整数不能,所以不需要从安全方面(SQL注入) .
JPA规范没有像您希望的那样明确指定它 . 这似乎是一个实现细节 .
String参数的Prepared语句参数 . 对于int参数,它并不重要,因为它们不会被黑客滥用 .
是的
您应该在您正在使用的特定数据库的文档中查找 . JPA并不关心这些事情 .
为什么?有什么好处?唐't try to improve things when you don't知道你在改进什么 .
我完全同意Niels的意思,不应该内联字符串参数以防止SQL注入 .
但我用DataNucleus 4.1.9和Derby db检查了它,令我惊讶的是日志也显示了字符串的内联 . 此外,它还显示DataNucleus使用“OR”条件的组合实现“IN”条件查询 . 可能这不如Hibernate,可能存在安全风险 . 更高级别抽象可能存在危险的示例 . 你不能太谨慎:-) .
日志:
为什么整数列表的元素直接内联到sql,而String列表的元素作为预处理语句参数处理?
这可能是因为你使用的整数是原语 .
尝试以这种方式使用它
Arrays.asList(Integer.value(10),Integer.value(20),Integer.value(30))