当前位置: 首页 > 面试题库 >

为什么Hibernate内联传递给JPA Criteria Query的Integer参数列表?

景恩
2023-03-14
问题内容

我正在使用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]

我的问题:

  1. 为什么将Integer列表的元素直接内联到sql以及将String列表的元素作为已准备好的语句参数处理?
  2. 此功能是Hibernate特定的还是JPA保证的?
  3. 从数据库角度来看,应首选哪两个?
  4. 这是int-yes string-no内联,以某种方式与sql注入相关吗?
  5. 这是否与RDMBS可以处理的sql IN子句中的值数限制有关?
  6. 如何编写将与String参数列表相同的条件查询来处理Integer参数列表。

问题答案:

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

应该始终对字符串进行参数绑定(而不是将文字放入查询中),以避免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。



 类似资料:
  • 我正在使用JPA Criteria API构建查询。当我使用

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

  • 问题内容: 我创建了一个将列表作为参数的函数。它会随机排列列表,替换第一个元素,然后返回新列表。 改组对我的问题没有影响。但是,我很惊讶地看到返回的importedList覆盖了原始的firstList。 通过在函数中复制列表,我找到了一种解决方法,但是效率似乎很低。 我的问题是为什么该函数替换firstList?例如,如果是字符串,则不会发生这种情况。 问题答案: 字符串,整数,元组是不可变的p

  • 问题内容: 我当时在Swinject上工作,但问题困扰着我。我已经整整一天都被困在那里。我怀疑这是由于Swift是一种静态类型的语言,但我不确定。 我在这个操场上总结了我的问题 我尝试了不同的解决方案,例如test.self或type(of:test),但它们都不起作用。 所以我想我不能用提供为变量的通用参数来调用函数? 问题答案: 与 协议元类型有两种。对于某些协议和符合类型: A 描述协议本身

  • 有时对一个类的某些方面进行 参数化(parameterize)是很有用的。例如, 你可能需要管理不同版本的 gem 软件包,既可以为每一种版本创建分离的单独的类, 也可以使用继承和覆盖,为一个类传递一个版本号作为参数。 操作步骤 声明参数作为如下类定义的一部分: class eventmachine( $version ) { package { "eventmachine": pro

  • 我知道 当您同步一个代码块时,您可以指定要使用哪个对象的锁作为锁,这样,例如,您可以使用某个第三方对象作为这段代码的锁。这使您能够在单个对象中拥有多个用于代码同步的锁。 但是,我不明白是否需要将参数传递给块。因为我是否传递String的实例并不重要,一些随机类的实例传递给同步块,因为同步块可以完美地工作,而不管传递给块的参数如何。 所以我的问题是,如果无论如何同步块阻止两个线程同时进入关键部分。那