【问题标题】:Outer joins with ON conditions in JPAJPA中具有ON条件的外部联接
【发布时间】:2013-07-24 12:32:50
【问题描述】:

我需要以下 SQL 查询的条件查询。

SELECT w.weight_id, w.weight, zc.charge
FROM weight w
LEFT OUTER JOIN zone_charge zc ON w.weight_id=zc.weight_id
AND zc.zone_id=?             <-------
ORDER BY w.weight ASC

相应的 JPQL 查询是这样的,

SELECT w.weightId, w.weight, zc.charge 
FROM Weight w 
LEFT JOIN w.zoneChargeSet zc 
WITH zc.zone.zoneId=:id      <-------
ORDER BY w.weight

我无法使用标准复制相同的内容,尤其是 WITH zc.zone.zoneId=:id


以下条件查询使用where 子句。

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple>criteriaQuery=criteriaBuilder.createTupleQuery();
Root<Weight> root = criteriaQuery.from(entityManager.getMetamodel().entity(Weight.class));
SetJoin<Weight, ZoneCharge> join = root.join(Weight_.zoneChargeSet, JoinType.LEFT);

ParameterExpression<Long>parameterExpression=criteriaBuilder.parameter(Long.class);
criteriaQuery.where(criteriaBuilder.equal(join.get(ZoneCharge_.zoneTable).get(ZoneTable_.zoneId), parameterExpression));
criteriaQuery.multiselect(root.get(Weight_.weightId), root.get(Weight_.weight), join.get(ZoneCharge_.charge));
criteriaQuery.orderBy(criteriaBuilder.asc(root.get(Weight_.weight)));
TypedQuery<Tuple> typedQuery = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, 1L);
List<Tuple> list = typedQuery.getResultList();

如何修改使其对应...LEFT OUTER JOIN zone_charge zc ON w.weight_id=zc.weight_id AND zc.zone_id=?

这会生成以下 SQL 查询。

SELECT weight0_.weight_id  AS col_0_0_, 
       weight0_.weight     AS col_1_0_, 
       zonecharge1_.charge AS col_2_0_ 
FROM   social_networking.weight weight0_ 
       LEFT OUTER JOIN social_networking.zone_charge zonecharge1_ 
                    ON weight0_.weight_id = zonecharge1_.weight_id 
WHERE  zonecharge1_.zone_id =? 
ORDER  BY weight0_.weight ASC 

【问题讨论】:

    标签: hibernate jpa eclipselink criteria jpa-2.1


    【解决方案1】:

    with 运算符是 JPQL 的特定于休眠的扩展。您不会在条件 API 中找到对它的支持。

    【讨论】:

    • 所以,我只能选择 JPQL 吗?
    • 是的,实际上是 HQL,因为 with 不是 JPQL 关键字。
    • Hibernate 标准是否支持它?反过来,此查询将是动态的,并且通过字符串连接构建查询有点乏味。
    • @Tiny我想知道如果使用JPQL代替createQuery,过滤条件将如何处理?例如。如果我的 DAOImpl 方法接收到任何过滤条件,并且如果我想作为过滤条件的参数传递给 JPQL,我的查询应该是 like % 可能有性能问题不是吗?
    【解决方案2】:

    这可以与JPA 2.1 提供的标准API 一起使用新功能join ON clause 来完成。因此,问题中给出的条件查询可以重写如下。

    CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
    CriteriaQuery<Tuple>criteriaQuery=criteriaBuilder.createQuery(Tuple.class);
    Root<Weight> root = criteriaQuery.from(entityManager.getMetamodel().entity(Weight.class));
    
    ListJoin<Weight, ZoneCharge> join = root.join(Weight_.zoneChargeList, JoinType.LEFT);
    criteriaQuery.multiselect(root.get(Weight_.weightId), root.get(Weight_.weight), join.get(ZoneCharge_.charge));
    
    ParameterExpression<Long>parameterExpression=criteriaBuilder.parameter(Long.class);
    join.on(criteriaBuilder.equal(join.get(ZoneCharge_.zoneTable).get(ZoneTable_.zoneId), parameterExpression));
    
    criteriaQuery.orderBy(criteriaBuilder.asc(root.get(Weight_.weight)));
    List<Tuple> list = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, 1L).getResultList();
    

    它产生以下所需的 SQL 查询。

    SELECT weight0_.weight_id  AS col_0_0_, 
           weight0_.weight     AS col_1_0_, 
           zonecharge1_.charge AS col_2_0_ 
    FROM   social_networking.weight weight0_ 
           LEFT OUTER JOIN social_networking.zone_charge zonecharge1_ 
                        ON weight0_.weight_id = zonecharge1_.weight_id 
                           AND ( zonecharge1_.zone_id =? ) 
    ORDER  BY weight0_.weight ASC 
    

    值得一提的是,此查询在 Hibernate (4.3.5 final) 上成功,但相同的查询在 EclipseLink (2.5.1) 上意外失败,并出现以下异常。

    java.lang.IllegalArgumentException: No parameter with name : Parameter[name=null] was found within the query: ReportQuery(referenceClass=Weight ).
        at org.eclipse.persistence.internal.jpa.EJBQueryImpl.setParameter(EJBQueryImpl.java:548)
        at admin.beans.ZoneChargeBean.getZoneChargeList(ZoneChargeBean.java:83)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1081)
        at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1153)
        at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4695)
        at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:630)
        at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
        at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:582)
        at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:46)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
        at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
        at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:582)
        at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.doCall(SystemInterceptorProxy.java:163)
        at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.aroundInvoke(SystemInterceptorProxy.java:140)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
        at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
        at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:369)
        at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4667)
        at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4655)
        at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:212)
        ... 72 more
    

    ParamterExpression 必须被删除才能按如下方式工作。

    CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
    CriteriaQuery<Tuple>criteriaQuery=criteriaBuilder.createQuery(Tuple.class);
    Root<Weight> root = criteriaQuery.from(entityManager.getMetamodel().entity(Weight.class));
    
    ListJoin<Weight, ZoneCharge> join = root.join(Weight_.zoneChargeList, JoinType.LEFT);
    
    criteriaQuery.multiselect(root.get(Weight_.weightId), root.get(Weight_.weight), join.get(ZoneCharge_.charge));
    join.on(criteriaBuilder.equal(join.get(ZoneCharge_.zoneTable).get(ZoneTable_.zoneId), 1L));
    
    criteriaQuery.orderBy(criteriaBuilder.asc(root.get(Weight_.weight)));
    List<Tuple> list = entityManager.createQuery(criteriaQuery).getResultList();
    

    这应该是 EclipseLink 2.5.1 中的一个疏忽。

    【讨论】:

      猜你喜欢
      • 2020-10-25
      • 2019-02-23
      • 2020-06-26
      • 2021-01-30
      • 2020-09-24
      • 2020-09-08
      • 1970-01-01
      • 2016-04-16
      • 2015-03-12
      相关资源
      最近更新 更多