【问题标题】:JPA2 critera query in-expression on FK issues n select statements, where n is #valuesJPA 2 标准查询语句中关于 FK 问题的表达式,其中 n 是 #values
【发布时间】:2026-01-17 05:35:01
【问题描述】:

我在对 DB2 和 MySQL 使用休眠时遇到了同样的问题。

这是一个测试:

        EntityManager em = emf.createEntityManager();
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Customers> query = cb.createQuery(Customers.class);
        Root<Customers> root = query.from(Customers.class);

        ArrayList<String> strList = new ArrayList<String>();
        strList.add("ADMIN");
        strList.add("SYSADMIN");
        strList.add("SALES");

        ArrayList<Predicate> predicateList = new ArrayList<Predicate>();

        Path<Groups> groups = root.get(Customers_.groups);
        Path<String> groupName = groups.get(Groups_.name);
        In<String> in = cb.in(groupName);
        for (String s : strList) { //has a value
            in = in.value(s);
        }
        predicateList.add(in);
        Predicate[] predicates = new Predicate[predicateList.size()];

        query.where(predicateList.toArray(predicates));
        TypedQuery<Customers> typedQuery = em.createQuery(query);
        this.outList = typedQuery.getResultList();

生成我需要的查询,后跟三个不需要的查询(一个额外的查询,因为 strList 中有许多值)。以下内容打印在日志中(我格式化了第一个查询以将其分开。)第一个查询完全符合我的要求,接下来的三个查询在生产中导致不需要的 IO,我对此不以为然。请注意,如果 in 表达式 不在 FK 上,则不会发生此问题。

INFO: Hibernate: 
select 
  customers0_.id as id0_, customers0_.fname as fname0_, customers0_.groups as groups0_, customers0_.lname as lname0_ 
from 
  test.customers customers0_ 
where 
  customers0_.groups in (? , ? , ?)

INFO: Hibernate: select groups0_.name as name1_0_ from test.groups groups0_ where groups0_.name=?
INFO: Hibernate: select groups0_.name as name1_0_ from test.groups groups0_ where groups0_.name=?
INFO: Hibernate: select groups0_.name as name1_0_ from test.groups groups0_ where groups0_.name=?

为什么要多出三个查询?我该如何防止它们?我需要作为标准查询的答案。


这里是实体对象:

@Entity
@Table(name = "customers", catalog = "test", schema = "")
public class Customers implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id", nullable = false)
    private Integer id;
    @Column(name = "fname", length = 45)
    private String fname;
    @Column(name = "lname", length = 45)
    private String lname;
    @JoinColumn(name = "groups", referencedColumnName = "name")
    @ManyToOne
    private Groups groups;

...getters and setters... 
}

下一个实体

@Entity
@Table(name = "groups", catalog = "test", schema = "")
public class Groups implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "name", nullable = false, length = 45, unique = true)
    private String name;
    @OneToMany(mappedBy = "groups")
    private Collection<Customers> customersCollection;

...getters and setters... 
}

编辑 ~~~~~~~~~~~~~~~~~~~~~~~~~ 解决方案 ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

在上面的代码中添加第二行可以解决问题(感谢 Clement):

Root<Customers> root = query.from(Customers.class);
root.fetch(Customers_.groups, JoinType.LEFT); //that's it that's all now it will not create the extra queries

【问题讨论】:

  • 您是否查看过 JPQL 是否会帮助您? *.com/questions/2687690/…
  • 使用 MySQL 或 DB2 不是问题的一部分。不知何故,JPA 认为您要求的是投影列表中的值,而不是“IN (v1, v2, v3)”。
  • @James :这只是一个更大的查询的一小部分,它有许多可能的标准,所以我宁愿坚持标准。
  • @Joshua:我对此了解不多,但我最近对 ​​MySQL 的简单测试失败了,无论我选择哪种 Hibernate 方言,我都会收到 org.hibernate.exception.SQLGrammarException!它以 *IN(v1, v2, v3) 的形式生成 SQL,但存在语法错误。
  • 啊,测试使用了保留字作为表列名“组”。

标签: hibernate jpa jpa-2.0


【解决方案1】:

附加查询的原因是因为您使用了一个字段:private Groups groups; 用于从客户到组的关联。由于 hibernate 无法拦截直接字段访问,因此在获取 Customer 时它必须获取 Groups 对象。它以 N+1 选择方式对条件查询执行此操作(而不是例如找出并自动执行连接或子选择)。

要解决此问题,您可以告诉 hibernate 也获取关联:

root.fetch(Customers_.groups, JoinType.LEFT); // LEFT join since your schema could have a customer with a null group.

【讨论】:

  • 请奖励赏金。谢谢。
  • 您是否知道在接受 5 小时后才能奖励赏金(或者看起来……我提供的第一个,所以我不知道还有 2 个小时!)。跨度>
  • ic,我的错,我也不知道。 :)
最近更新 更多