Внешние соединения с условиями ON в JPA

Мне нужен запрос критериев для следующего 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 

person Tiny    schedule 24.07.2013    source источник


Ответы (2)


Оператор with — это специфичное для спящего режима расширение JPQL. Вы не найдете поддержки для этого в API критериев.

person JB Nizet    schedule 24.07.2013
comment
Итак, я должен использовать только JPQL? - person Tiny; 25.07.2013
comment
Да, на самом деле HQL, поскольку with не является ключевым словом JPQL. - person JB Nizet; 25.07.2013
comment
Поддерживается ли он критериями Hibernate? Этот запрос, в свою очередь, будет динамическим, а построение запроса с помощью конкатенации строк несколько утомительно. - person Tiny; 25.07.2013
comment
comment
@Tiny Я хотел бы знать, используется ли JPQL вместо createQuery, как будут обрабатываться условия фильтра? Например. Если мой метод DAOImpl получает какое-либо условие фильтра и если я хочу передать JPQL в качестве параметра условия фильтра, мой запрос должен быть like % может иметь проблемы с производительностью, не так ли? - person Jacob; 01.08.2013
comment
Он должен быть доступен в JPA, предоставляемом EclipseLink 2.4, разработка которого все еще продолжается. Это доступно там? - person Tiny; 05.11.2013

Это можно сделать вместе с API критериев, предоставляемым JPA 2.1, используя новая функция присоединиться к пункту ON. Соответственно, запрос критериев, приведенный в вопросе, можно переписать следующим образом.

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.

person Tiny    schedule 21.05.2014