本文介绍了去除子查询在JPA标准中产生的冗余连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只需要使用JPA标准执行以下MySQL查询(根据给定的国家名称(在country中)从state_table获取一个州的列表)。

SELECT state_id, 
       state_name, 
       country_id
FROM   state_table 
WHERE  country_id IN(SELECT country_id 
                     FROM   country 
                     WHERE  country_name = ?)

我编写了以下JPA标准查询。

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<StateTable>criteriaQuery=criteriaBuilder.createQuery(StateTable.class);
Root<StateTable> root = criteriaQuery.from(entityManager.getMetamodel().entity(StateTable.class));

Subquery<Long> subquery = criteriaQuery.subquery(Long.class);
Root<Country> subRoot = subquery.from(Country.class);
subquery.select(subRoot.get(Country_.countryId));

ParameterExpression<String>parameterExpression=criteriaBuilder.parameter(String.class);
subquery.where(criteriaBuilder.equal(subRoot.get(Country_.countryName), parameterExpression));
criteriaQuery.where(criteriaBuilder.in(root.get(StateTable_.country).get(Country_.countryId)).value(subquery));
List<StateTable> list = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, "India").getResultList();

此条件查询不必要地在生成的SQL查询中生成冗余联接,如下所示。

SELECT t1.state_id, 
       t1.state_name, 
       t1.country_id 
FROM   projectdb.country t0, 
       projectdb.state_table t1 
WHERE  (t0.country_id IN (SELECT t2.country_id 
                           FROM   projectdb.country t2 
                           WHERE  (t2.country_name = ? )) 
         AND (t0.country_id = t1.country_id )) 

可以注意到,存在冗余联接AND (t0.country_id = t1.country_id ))


这是由root.get(StateTable_.countryId).get(Country_.countryId)引起的,root.get(StateTable_.countryId).get(Country_.countryId)意味着在上面给出的条件查询中的以下行中有一个内部联接。在这种情况下,不应出现不必要的情况。

criteriaQuery.where(criteriaBuilder.in(root.get(StateTable_.countryId).get(Country_.countryId)).value(subquery));

如何删除此冗余联接?

我使用的是NikpseLink 2.3.2提供的JPA 2.0。


这是一个非常基本的查询,很难相信这是NikpseLink中的疏忽。在我看来,我一定是做错了什么,一定是遗漏了一些显而易见的、非常基本的东西。请澄清。

我最近已将NikpseLink升级到其最新版本2.5.1,并安装了JPA 2.1。


长话短说:引用root.get("property1").get("propery2InAnotherRelatedEntity")这样的实体中的嵌套属性总是会导致生成多余的联接,这通常不应该发生。

请原谅我,如果我做了这么多修改,但我不得不…:)

JPA

示例中的冗余联接是由不正确的推荐答案标准查询构造产生的,而不是由其中的子查询产生的。所以这个问题的题目真的没有多大意义。

要消除冗余联接,您只需正确构建条件。事实上,编辑2中的条件查询是问题开头的原始SQL查询到JPA条件语言的正确转换,不应该产生任何多余的联接。您使用Hibernate进行的实验证实了这一点。而你得到的QueryException绝对是一个NikpseLink的错误。我建议您将其报告给NikpseLink:https://wiki.eclipse.org/EclipseLink/Bugs的开发人员。

但实际上,这里根本不需要子查询,如果使用正确,连接通常不是坏事,所以我建议用这个完全等价但更简洁的查询来替换SQL查询:

SELECT state_id,
       state_name,
       s.country_id
FROM   state_table s, country c
WHERE  s.country_id = c.country_id AND country_name = ?

它到条件查询的翻译也简单明了得多(肯定不会混淆NikpseLink:)):

CriteriaBuilder criteriaBuilder =
    entityManager.getCriteriaBuilder();

CriteriaQuery<StateTable> criteriaQuery =
    criteriaBuilder.createQuery(StateTable.class);

Root<StateTable> root = criteriaQuery.from(StateTable.class);
ParameterExpression<String> parameterExpression =
    criteriaBuilder.parameter(String.class);

criteriaQuery.where(criteriaBuilder.equal(
    root.get(StateTable_.country).get(Country_.countryName),
    parameterExpression));

List<StateTable> list = entityManager.createQuery(criteriaQuery)
    .setParameter(parameterExpression, "India").getResultList();

并且在MySQL中的性能不会比原始版本差。

这篇关于去除子查询在JPA标准中产生的冗余连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-28 03:14