为什么Hibernate内联传递给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 q = cb.createQuery(A.class); Root 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,而String列表的元素作为预处理语句参数处理?
  2. 这个function是Hibernate特有还是由JPA保证?
  3. 从DB的角度来看哪两个应该是首选?
  4. 这是int-yes字符串 – 没有内联与sql注入有关吗?
  5. 这是否与RDMBS可以处理的sql IN子句中的值数限制有关?
  6. 如何编写一个条件查询,它将以与String参数列表相同的方式处理Integer参数列表。

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

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

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

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

对于db来说哪个更好?

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

这是JPA规范吗?

没有。

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

没有。

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

是的,但它有点冗长。

 CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); CriteriaQuery query = cb.createQuery(Foo.class); Root root = query.from(Foo.class); ParameterExpression paramOne = cb.parameter(Long.class); Predicate versionPredicate = root.get("bar").in(paramOne); query.select(root).where(versionPredicate); TypedQuery typedQuery = getEntityManager().createQuery(query); typedQuery.setParameter(paramOne, 1L); 

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

参考文献:

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

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

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

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

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

尝试以这种方式使用它

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