为什么EclipseLink中具有嵌套属性的子查询总是产生不必要/冗余/重复/多余的连接?

时间:2014-07-19 06:09:44

标签: jpa eclipselink criteria jpql jpa-2.1

我想通过EclipseLink(2.6.1)JPA标准生成以下SQL。

SELECT
    zonecharge0_.charge AS col_0_0_ 
FROM
    projectdb.zone_charge zonecharge0_ 
WHERE
    (
        EXISTS (
            SELECT
                country1_.country_id 
            FROM
                projectdb.country country1_ 
            WHERE
                country1_.country_id=? 
                and zonecharge0_.zone_id=country1_.zone_id
        )
    ) 
    AND (
        zonecharge0_.weight_id IN (
            SELECT
                MAX(weight2_.weight_id) 
            FROM
                projectdb.weight weight2_ 
            WHERE
                weight2_.weight BETWEEN 0 AND 60
        )
    )

在Hibernate(最终的4.3.7)上运行以下两个条件和JPQL查询会产生上述干净的查询,人们可以直观地预期。

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<BigDecimal>criteriaQuery=criteriaBuilder.createQuery(BigDecimal.class);
Metamodel metamodel = entityManager.getMetamodel();
Root<ZoneCharge> root = criteriaQuery.from(metamodel.entity(ZoneCharge.class));
criteriaQuery.select(root.get(ZoneCharge_.charge));

Subquery<Long> countrySubquery = criteriaQuery.subquery(Long.class);
Root<Country> countryRoot = countrySubquery.from(metamodel.entity(Country.class));
countrySubquery.select(countryRoot.get(Country_.countryId));

Predicate[] predicates=new Predicate[2];
predicates[0]=criteriaBuilder.equal(countryRoot, country);
predicates[1]=criteriaBuilder.equal(root.get(ZoneCharge_.zoneId), countryRoot.get(Country_.zoneId));
countrySubquery.where(predicates);

Subquery<Long> weightSubquery = criteriaQuery.subquery(Long.class);
Root<Weight> weightRoot = weightSubquery.from(metamodel.entity(Weight.class));
weightSubquery.select(criteriaBuilder.max(weightRoot.get(Weight_.weightId)));
weightSubquery.where(criteriaBuilder.between(weightRoot.get(Weight_.weight), BigDecimal.ZERO, weight));

predicates=new Predicate[2];
predicates[0]=criteriaBuilder.exists(countrySubquery);
predicates[1]=criteriaBuilder.in(root.get(ZoneCharge_.weightId).get(Weight_.weightId)).value(weightSubquery);

criteriaQuery.where(predicates);
BigDecimal charge = entityManager.createQuery(criteriaQuery).getSingleResult();

相应的JPQL:

SELECT zc.charge 
FROM   ZoneCharge AS zc 
WHERE  EXISTS(SELECT c.countryId 
              FROM   Country AS c 
              WHERE  zc.zoneId = c.zoneId 
                     AND c.countryId = 1) 
       AND zc.weightId.weightId IN(SELECT MAX(w.weightId) 
                                   FROM   Weight AS w 
                                   WHERE  w.weight BETWEEN 0 AND 60) 

运行这些查询(条件和JPQL)而不对EclipseLink(2.6.1)进行任何修改会产生以下非常丑陋/混乱的查询。

SELECT t0.charge 
FROM   projectdb.zone_charge t0, 
       projectdb.weight t1   /*Unnecessary table listing.*/
WHERE  ((EXISTS (SELECT t2.country_id 
                   FROM   projectdb.zone_table t3, /*Unnecessary table listing.*/
                          projectdb.country t2 
                   WHERE  ((( t2.country_id = ?) 
                              AND( t0.zone_id = t3.zone_id)) 
                            AND(t3.zone_id = t2.zone_id)))  /*Duplicate join.*/
           AND t1.weight_id IN (SELECT MAX(t4.weight_id) 
                                FROM   projectdb.weight t4 
                                WHERE  (t4.weight BETWEEN ? AND ?))) 
         AND (t1.weight_id = t0.weight_id))  /*Duplicate join.*/

我只想获取类型为BigDecimal的标量值。它不需要在这里加入,也没有提到过,虽然它产生了两个完全不必要的多余连接。

这些冗余连接的原因是给定谓词中的两个嵌套属性。

root.get(ZoneCharge_.weightId).get(Weight_.weightId)

root.get(ZoneCharge_.zoneId), countryRoot.get(Country_.zoneId)

查看Predicate数组。

我一直试图知道很长一段时间的原因,但我没有在EclipseLink中找到任何关于这种现象的提及。

我认为这不是一种疏忽。它看起来很刻意。

看到这种陈述,我感到非常失望。有没有理由让EclipseLink生成这种冗余连接?是否有一些设置或其他东西可以摆脱它们,我可能会在EclipseLink中丢失它们?他们还能避免吗?


其他信息:

表结构很简单。这里涉及三个表格。

  • zone_charge
  • 国家
  • 重量

zone_charge表有一列weight_id,它是引用weight表中相应主键的外键。

countryzone_charge表都有一个zone_id列,zone_id列中的zone_table列的外键(此处未提及)在两个表格中。

查询根据权重值返回特定区域中zone_charge的费用值。假设用户不知道区域。因此,此查询基于用户通过选择下拉框中的国家/地区提供的国家/地区在后台选择区域。


更新:

有些不同意见和观点的问题:

所有这些都意味着同样的事情。 Hibernate在生成整洁的SQL语句时往往非常聪明。我不认为那些使用EcliplseLink(反过来,这是一个广泛使用的ORM框架)就像在数据库上执行这种不整洁的SQL语句一样。

你真的只是跟着他们继续吗?

0 个答案:

没有答案