我正在使用JPA Criteria API构建查询。当我使用javax.persistence.criteria.Path#in(Collection<?>)方法创建两个限制谓词时,生成的SQL查询与我预期的有所不同。
javax.persistence.criteria.Path#in(Collection<?>)
在int属性之上构建的第一个谓词产生了SQL,其中的参数集合的所有元素都内联:in (10, 20, 30)。
int
in (10, 20, 30)
在String属性之上构建的第二个谓词产生了参数化的SQL :in (?, ?, ?)。
String
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)一些数据库需要知道类型信息才能正确处理诸如… +?…,等等。因此,选择是将所有此类参数包装在CAST函数调用中,并希望/祈祷数据库实现了正确的CAST函数或使用文字。最后,我选择了原义路线,因为那是用户预先要求的。包装函数调用将限制数据库利用很多数据库中索引的能力。
哪个对数据库更好?
它取决于数据库和查询,可能不会有很大的不同。例如,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。