为什么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]
我的问题:
- 为什么整数列表的元素直接内联到sql,而String列表的元素作为预处理语句参数处理?
- 这个function是Hibernate特有还是由JPA保证?
- 从DB的角度来看哪两个应该是首选?
- 这是int-yes字符串 – 没有内联与sql注入有关吗?
- 这是否与RDMBS可以处理的sql IN子句中的值数限制有关?
- 如何编写一个条件查询,它将以与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 。
- 因为Strings可以包含SQL和Integers,所以不需要从安全方面(SQL注入)。
- JPA规范没有像您希望的那样明确指定它。 这似乎是一个实现细节。
- String参数的预处理语句参数。 对于int参数,它并不重要,因为它们不会被黑客滥用。
- 是
- 您应该在您正在使用的特定数据库的文档中查找。 JPA并不关心这些事情。
- 为什么? 有什么好处? 当你不知道自己在改进什么时,不要试图改进。
我完全同意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))