2

我想通过 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) 

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

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.*/

我只想获取 type 的标量值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

和表都有一个列country,其中列的外键(此处未提及)在两个表中。zone_chargezone_idzone_idzone_table

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


更新 :

一些不同观点和看法的问题:

所有这些都暗示着同样的事情。Hibernate 在生成整洁的 SQL 语句方面往往非常聪明。我不认为那些使用 EcliplseLink(又是广泛使用的 ORM 框架之一)的人喜欢在数据库上执行这种不整洁的 SQL 语句。

你真的只是继续和他们在一起吗?

4

0 回答 0