当前位置: 首页 > 知识库问答 >
问题:

在JPA条件中消除子查询产生的冗余联接

阴永逸
2023-03-14
SELECT state_id, 
       state_name, 
       country_id
FROM   state_table 
WHERE  country_id IN(SELECT country_id 
                     FROM   country 
                     WHERE  country_name = ?)
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();
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 )) 
criteriaQuery.where(criteriaBuilder.in(root.get(StateTable_.countryId).get(Country_.countryId)).value(subquery));

我使用的是Eclipselink2.3.2提供的JPA2.0。

在这种情况下,可以通过在这两个表之间使用联接来更好地处理它,而不是使用子查询,但编写这样的子查询而不在其中使用冗余联接是可能的,因为这是一个相当基本的事情。

编辑1:

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

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(root.get(StateTable_.country), subRoot), criteriaBuilder.equal(subRoot.get(Country_.countryName), parameterExpression));

criteriaQuery.where(criteriaBuilder.exists(subquery));
List<StateTable> list = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, "India").getResultList();
SELECT t0.state_id, 
       t0.state_name, 
       t0.country_id 
FROM   projectdb.state_table t0 
WHERE  EXISTS (SELECT t1.country_id 
               FROM   projectdb.country t1 
               WHERE  ((t0.country_id = t1.country_id) 
                        AND (t1.country_name = ?))) 
SELECT s 
FROM   StateTable s 
WHERE  s.country IN(SELECT c 
                      FROM   Country c 
                      WHERE  c.countryname = :countryName) 
SELECT t0.state_id, 
       t0.state_name, 
       t0.country_id 
FROM   projectdb.state_table t0 
WHERE  t0.country_id IN (SELECT t1.country_id 
                         FROM   projectdb.country t1 
                         WHERE  (t1.country_name = ?)) 
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<StateTable>criteriaQuery=criteriaBuilder.createQuery(StateTable.class);
Root<StateTable> root = criteriaQuery.from(entityManager.getMetamodel().entity(StateTable.class));
criteriaQuery.select(root);

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

ParameterExpression<String>parameterExpression=criteriaBuilder.parameter(String.class);
subquery.where(criteriaBuilder.equal(subRoot.get(Country_.countryName), parameterExpression));

criteriaQuery.where(criteriaBuilder.in(root.get(StateTable_.country)).value(subquery));
List<StateTable> list = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, "India").getResultList();
Exception [EclipseLink-6048] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.QueryException
Exception Description: Illegal use of getField() [projectdb.country.country_id] in expression.
Query: ReadAllQuery(referenceClass=StateTable )
    at org.eclipse.persistence.exceptions.QueryException.illegalUseOfGetField(QueryException.java:563)
    at org.eclipse.persistence.expressions.Expression.getField(Expression.java:1739)
    at org.eclipse.persistence.internal.expressions.FunctionExpression.normalize(FunctionExpression.java:476)
    at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalize(SQLSelectStatement.java:1402)
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildNormalSelectStatement(ExpressionQueryMechanism.java:549)
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.prepareSelectAllRows(ExpressionQueryMechanism.java:1708)
    at org.eclipse.persistence.queries.ReadAllQuery.prepareSelectAllRows(ReadAllQuery.java:785)
    at org.eclipse.persistence.queries.ReadAllQuery.prepare(ReadAllQuery.java:716)
    at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:661)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:888)
    at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:613)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:867)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1114)
    at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:402)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1202)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2894)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1797)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1779)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1744)
    at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:258)
    at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:468)
    at client.beans.ProductDetailsBean.test(ProductDetailsBean.java:139)
    at client.beans.ProductDetailsBean.getProductList(ProductDetailsBean.java:119)
    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)
    at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88)
    at $Proxy482.getProductList(Unknown Source)
    at client.bean.ProductDetailsManagedBean.load(ProductDetailsManagedBean.java:59)
    at org.primefaces.component.datagrid.DataGrid.loadLazyData(DataGrid.java:144)
    at org.primefaces.component.datagrid.DataGridRenderer.encodeMarkup(DataGridRenderer.java:54)
    at org.primefaces.component.datagrid.DataGridRenderer.encodeEnd(DataGridRenderer.java:47)
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:919)
    at org.primefaces.renderkit.CoreRenderer.renderChild(CoreRenderer.java:74)
    at org.primefaces.renderkit.CoreRenderer.renderChildren(CoreRenderer.java:57)
    at org.primefaces.component.panel.PanelRenderer.encodeContent(PanelRenderer.java:204)
    at org.primefaces.component.panel.PanelRenderer.encodeMarkup(PanelRenderer.java:121)
    at org.primefaces.component.panel.PanelRenderer.encodeEnd(PanelRenderer.java:58)
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:919)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1864)
    at javax.faces.render.Renderer.encodeChildren(Renderer.java:176)
    at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:889)
    at org.primefaces.renderkit.CoreRenderer.renderChild(CoreRenderer.java:70)
    at org.primefaces.renderkit.CoreRenderer.renderChildren(CoreRenderer.java:57)
    at org.primefaces.component.layout.LayoutUnitRenderer.encodeEnd(LayoutUnitRenderer.java:51)
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:919)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1864)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1860)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1860)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1860)
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:461)
    at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:133)
    at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
    at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
    at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:120)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:647)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:344)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
    at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:70)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
    at java.lang.Thread.run(Thread.java:722)

我看不出这个例外背后有什么理由。

编辑3:

在最新版本中的相同条件查询--在例外情况下失败的查询--在Hibernate(4.2.7 final)提供的JPA上没有任何修改就可以正常工作,在不同的项目中使用完全相同的表关系。

SELECT
    statetable0_.state_id as state_id1_24_,
    statetable0_.country_id as country_3_24_,
    statetable0_.state_name as state_na2_24_ 
FROM
    social_networking.state_table statetable0_ 
WHERE
    statetable0_.country_id IN (
        SELECT
            country1_.country_id 
        FROM
            social_networking.country country1_ 
        WHERE
            country1_.country_name=?
    )
SELECT * 
FROM   country c 
WHERE  EXISTS (SELECT s.country_id 
               FROM   state_table s 
               WHERE  s.country_id = c.country_id 
                      AND s.state_name = 'desired_state_name') 

Hibernate提供的JPA(我目前使用的是4.2.7final)生成查询语句,这与我们通常期望的一样,没有这样的冗余联接。

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

我最近将EclipseLink升级到了当前最新的2.5.1版本,该版本具有JPA2.1。

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

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

共有1个答案

居乐池
2023-03-14

示例中的冗余联接是由不正确的JPA条件查询构造而不是由子查询的存在产生的。所以题目真的没有太大的意义。

要消除冗余连接,只需正确构造条件。实际上,编辑2中的条件查询是问题开头的原始SQL查询到JPA条件语言的正确翻译,不应该产生任何冗余连接。您使用Hibernate进行的实验证实了这一点。而您得到的queryexception肯定是EclipseLink的一个bug。我建议您将其报告给Eclipselink的开发人员: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 = ?
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();
 类似资料:
  • 如何创建高效的 JPA 条件查询,以便仅当实体存在于联接表中时才选择实体列表?例如,采用以下三个表: 有问题的查询(我希望 JPA 生成什么)是: 以下条件查询将产生类似的结果,但有两个连接: 根本问题似乎是我对USER_WORKGROUP连接表使用@JoinTable注释,而不是对连接表使用单独的@Entity,所以我似乎不能在条件查询中将USER_WORGGroup用作根。 以下是实体类:

  • 如何在Hibernate中为下面给定的查询编写criteria builder api查询?batchId不是主键 从用户中选择批号('1','3') 我尝试过: 除了数据库中存在的数据外,我什么也得不到。

  • 显然,扩展了,是抽象的,继承是, 和 具有 其他原因 。 但生成的SQL是这样的: 在我期待的时候: 请注意 使用另一根- 使用子查询- 将lastName字段上移到主题 使用本机查询 使用实体图 是不能接受的。 我感兴趣的是可以在WHERE子句中直接声明和使用的东西(仅从CriteriaBuilder和/或单个Root生成,就像子句一样)(如果确实存在的话)。

  • subcompany.hbm.xml 子单位表 branch.java 指定表 我需要帮助编写条件查询使用提供的SQL。

  • 我试图转换这个JPQL查询;

  • 我试图使用CritierBuilder/CrtieriaQuery执行select语句,从表a中选择某些字段,然后如果该记录存在于另一个表中,则使用布尔标志。 基本上,我有一个“官员”列表和一个用户列表。用户是使用该系统的人,可以为官员添加书签/保存信息。当用户询问官员时,我希望能够显示他们已经为哪些官员添加了书签。 所以这个查询,我在我的h2数据库控制台中运行,它(相当)有效。如果官员被用户12