当前位置: 首页 > 知识库问答 >
问题:

为什么Hibernate内联传递给JPA标准查询的整数参数列表?

戴鸿羲
2023-03-14

我正在使用JPA Criteria API构建查询。当我使用创建两个限制谓词时javax.persistence.criteria.Path#in(Collection

通过int属性生成的第一个谓词SQL内联了参数集合的所有元素:in(10, 20, 30)

第二个谓词是在String属性上构建的,它在 (?, ?, ?)

让我展示一下:

实体:

@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] 

我的问题:

  1. 为什么整数列表的元素直接内联到sql,而字符串列表的元素则作为准备好的语句参数处理

共有3个答案

沈俊美
2023-03-14
  1. 因为字符串可以包含SQL而整数不能,所以不需要从安全方面(SQL注入)。
  2. JPA规范没有像您希望的那样明确地指定它。它似乎是一个实现细节。
  3. 为String参数准备了语句参数。对于int参数没关系,因为它们不会被黑客滥用。
  4. 您应该在您正在使用的特定数据库的留档中查找它。JPA不关心这些事情。
  5. 为什么?有什么好处?当你不知道你在改进什么的时候,不要试图改进事情。
楚骞尧
2023-03-14

在版本HHH-9576中,添加了一个新参数来修复此问题,自版本5.2.12(?)起适用

<property name="hibernate.criteria.literal_handling_mode" value="bind"/>

如果您使用此参数,您不再需要Pace提出的详细解决方案。

从literal\u handling\u模式的hibernate文档中:

此枚举定义了JPA Criteria如何处理文字。默认情况下(AUTO),Criteria查询对任何不是数值的文字使用绑定参数。但是,为了增加JDBC语句缓存的可能性,您可能也希望对数值使用绑定参数。BIND模式将对任何文字值使用绑定变量。INLINE模式将按原样内联文字值。为了防止SQL注入,永远不要将INLINE与字符串变量一起使用。始终在INLINE模式下使用常量。

涂飞航
2023-03-14

为什么字符串绑定而数字文字不绑定?

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

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

所以iirc导致我在这里使用文字的问题与规模和操作有关。这意味着(同样,iirc)一些数据库需要知道类型信息才能正确处理诸如。。。,因此,我们的选择是要么将所有此类参数包装在CAST函数调用中,并希望/祈祷db实现正确的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。提供

 类似资料:
  • 问题内容: 我正在使用JPA Criteria API构建查询。当我使用方法创建两个限制谓词时,生成的SQL查询与我预期的有所不同。 在属性之上构建的第一个谓词产生了SQL,其中的参数集合的所有元素都内联:。 在属性之上构建的第二个谓词产生了参数化的SQL :。 让我展示: 实体: 查询: 日志: 我的问题: 为什么将Integer列表的元素直接内联到sql以及将String列表的元素作为已准备好

  • 我有表emp列emp_name,emp_desc,emp_age,emp_country,emp_pincode。 我正在使用Spring数据Jpa进行数据库操作。 和EMP存储库。findAll();从emp表激发select*。 但我有如下要求 客户端应用程序将传递列名以在方法中选择作为参数,我只想获取Jpa而不是Findall()中的列名。 如何在Jpa中实现这一点?

  • 问题内容: 如果将空列表传递到JPA查询中,则会收到错误消息。例如: 由于列表为空,因此Hibernate在SQL中将其生成为“ IN()”,这给我Hypersonic数据库错误。 在Hibernate问题跟踪中有一张票证,但是那里没有太多评论/活动。我也不知道其他ORM产品或JPA规范中的支持。 我不喜欢每次都要手动检查空对象和空列表的想法。是否有一些众所周知的方法/扩展?您如何处理这些情况?

  • 问题内容: 我有一个整数或字符串列表,需要将其作为Delphi DataSet的参数传递。怎么做? 这是一个例子。MyQuery类似于: 我将参数设置为列表或数组或其他内容: 它将导致此查询发送到sql服务器: 如果该解决方案也可以与字符串一起使用,则执行以下查询会更好: 变得: 我相信这是一个简单的问题,但是“ IN”并不是搜索网络的好关键字。 请回答如何在IDE中配置参数,查询以及如何传递参数

  • 例子: 从卷号在(1,2,3)中的学生中选择*; 在学生存储库(Spring Boot)中: @查询(value=“从卷号所在的学生中选择*”,native=true) 列出selectStudents(我在这里给出什么?) 或者有没有其他方法来实现这一点?

  • 问题内容: 在猫鼬文档中,它经常列出某些查询运算符(如)的可选回调,但是,它没有提及回调采用的参数(参数)。他们是什么,我怎么知道? 另外,如果,等都是可选的,我想在结束时指定一个回调,我必须在传递值,或空物体或我可以只指定回调- 和软管做猫鼬知道吗? 问题答案: 对于几乎所有的猫鼬查询,所提供的函数将在文档中所述的节点回调模式 中用两个参数调用: 在Mongoose中将回调传递给查询的任何地方,