diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractCommonQueryBuilder.java b/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractCommonQueryBuilder.java index afffe0f193..9475e8dd98 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractCommonQueryBuilder.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractCommonQueryBuilder.java @@ -56,6 +56,7 @@ import com.blazebit.persistence.impl.query.EntityFunctionNode; import com.blazebit.persistence.impl.query.ObjectBuilderTypedQuery; import com.blazebit.persistence.impl.query.QuerySpecification; +import com.blazebit.persistence.impl.transform.ExpressionModifierVisitor; import com.blazebit.persistence.impl.transform.ExpressionTransformerGroup; import com.blazebit.persistence.impl.transform.OuterFunctionVisitor; import com.blazebit.persistence.impl.transform.SimpleTransformerGroup; @@ -63,6 +64,7 @@ import com.blazebit.persistence.impl.transform.SizeTransformerGroup; import com.blazebit.persistence.impl.transform.SubqueryRecursiveExpressionVisitor; import com.blazebit.persistence.impl.util.SqlUtils; +import com.blazebit.persistence.parser.AliasReplacementVisitor; import com.blazebit.persistence.parser.EntityMetamodel; import com.blazebit.persistence.parser.expression.Expression; import com.blazebit.persistence.parser.expression.ExpressionFactory; @@ -247,7 +249,7 @@ protected AbstractCommonQueryBuilder(AbstractCommonQueryBuilder(queryGenerator, parameterManager, subqueryInitFactory, expressionFactory, groupByExpressionGatheringVisitor); - this.selectManager = new SelectManager<>(queryGenerator, parameterManager, this.joinManager, this.aliasManager, subqueryInitFactory, expressionFactory, mainQuery.jpaProvider, mainQuery, groupByExpressionGatheringVisitor, builder.resultType); + this.selectManager = new SelectManager<>(queryGenerator, parameterManager, this, this.joinManager, this.aliasManager, subqueryInitFactory, expressionFactory, mainQuery.jpaProvider, mainQuery, groupByExpressionGatheringVisitor, builder.resultType); this.orderByManager = new OrderByManager(queryGenerator, parameterManager, subqueryInitFactory, this.joinManager, this.aliasManager, embeddableSplittingVisitor, functionalDependencyAnalyzerVisitor, mainQuery.metamodel, mainQuery.jpaProvider, groupByExpressionGatheringVisitor); this.keysetManager = new KeysetManager(this, queryGenerator, parameterManager, mainQuery.jpaProvider, mainQuery.dbmsDialect); @@ -317,7 +319,7 @@ protected AbstractCommonQueryBuilder(MainQuery mainQuery, QueryContext queryCont this.groupByManager = new GroupByManager(queryGenerator, parameterManager, subqueryInitFactory, mainQuery.jpaProvider, this.aliasManager, embeddableSplittingVisitor, groupByExpressionGatheringVisitor); this.havingManager = new HavingManager<>(queryGenerator, parameterManager, subqueryInitFactory, expressionFactory, groupByExpressionGatheringVisitor); - this.selectManager = new SelectManager<>(queryGenerator, parameterManager, this.joinManager, this.aliasManager, subqueryInitFactory, expressionFactory, mainQuery.jpaProvider, mainQuery, groupByExpressionGatheringVisitor, resultClazz); + this.selectManager = new SelectManager<>(queryGenerator, parameterManager, this, this.joinManager, this.aliasManager, subqueryInitFactory, expressionFactory, mainQuery.jpaProvider, mainQuery, groupByExpressionGatheringVisitor, resultClazz); this.orderByManager = new OrderByManager(queryGenerator, parameterManager, subqueryInitFactory, this.joinManager, this.aliasManager, embeddableSplittingVisitor, functionalDependencyAnalyzerVisitor, mainQuery.metamodel, mainQuery.jpaProvider, groupByExpressionGatheringVisitor); this.keysetManager = new KeysetManager(this, queryGenerator, parameterManager, mainQuery.jpaProvider, mainQuery.dbmsDialect); @@ -364,7 +366,7 @@ void applyFrom(AbstractCommonQueryBuilder builder, boolean fixedS selectManager.setDefaultSelect(nodeMapping, builder.selectManager.getSelectInfos()); if (fixedSelect) { - selectManager.unserDefaultSelect(); + selectManager.unsetDefaultSelect(); } // No need to copy the finalSetOperationBuilder as that is only necessary for further builders which isn't possible after copying collectParameters(); @@ -1727,6 +1729,26 @@ protected JoinVisitor applyImplicitJoins(JoinVisitor parentVisitor) { return joinVisitor; } + void inlineSelectAlias(String selectAlias, Expression expression) { + final AliasReplacementVisitor aliasReplacementVisitor = new AliasReplacementVisitor(expression, selectAlias); + ExpressionModifierVisitor expressionModifierVisitor = new ExpressionModifierVisitor() { + @Override + public void visit(ExpressionModifier expressionModifier, ClauseType clauseType) { + Expression expr = expressionModifier.get(); + Expression newExpr = expr.accept(aliasReplacementVisitor); + if (expr != newExpr) { + expressionModifier.set(newExpr); + } + } + }; + joinManager.apply(expressionModifierVisitor); + selectManager.apply(expressionModifierVisitor); + whereManager.apply(expressionModifierVisitor); + havingManager.apply(expressionModifierVisitor); + groupByManager.apply(expressionModifierVisitor); + orderByManager.apply(expressionModifierVisitor); + } + protected void implicitJoinWhereClause() { final JoinVisitor joinVisitor = new JoinVisitor(mainQuery, null, joinManager, parameterManager, !mainQuery.jpaProvider.supportsSingleValuedAssociationIdExpressions()); joinVisitor.setJoinRequired(true); diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/SelectManager.java b/core/impl/src/main/java/com/blazebit/persistence/impl/SelectManager.java index 2590ff5311..3d925a5077 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/SelectManager.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/SelectManager.java @@ -80,6 +80,7 @@ public class SelectManager extends AbstractManager { private final Map selectAliasToPositionMap = new HashMap(); private final SelectObjectBuilderEndedListenerImpl selectObjectBuilderEndedListener = new SelectObjectBuilderEndedListenerImpl(); private CaseExpressionBuilderListener caseExpressionBuilderListener; + private final AbstractCommonQueryBuilder queryBuilder; private final GroupByExpressionGatheringVisitor groupByExpressionGatheringVisitor; private final JoinManager joinManager; private final AliasManager aliasManager; @@ -89,9 +90,10 @@ public class SelectManager extends AbstractManager { private final Class resultClazz; @SuppressWarnings("unchecked") - public SelectManager(ResolvingQueryGenerator queryGenerator, ParameterManager parameterManager, JoinManager joinManager, AliasManager aliasManager, SubqueryInitiatorFactory subqueryInitFactory, ExpressionFactory expressionFactory, JpaProvider jpaProvider, + public SelectManager(ResolvingQueryGenerator queryGenerator, ParameterManager parameterManager, AbstractCommonQueryBuilder queryBuilder, JoinManager joinManager, AliasManager aliasManager, SubqueryInitiatorFactory subqueryInitFactory, ExpressionFactory expressionFactory, JpaProvider jpaProvider, MainQuery mainQuery, GroupByExpressionGatheringVisitor groupByExpressionGatheringVisitor, Class resultClazz) { super(queryGenerator, parameterManager, subqueryInitFactory); + this.queryBuilder = queryBuilder; this.groupByExpressionGatheringVisitor = groupByExpressionGatheringVisitor; this.joinManager = joinManager; this.aliasManager = aliasManager; @@ -539,7 +541,7 @@ boolean isDistinct() { return this.distinct; } - void unserDefaultSelect() { + void unsetDefaultSelect() { hasDefaultSelect = false; } @@ -550,6 +552,9 @@ private void clearDefaultSelects() { for (int i = 0; i < selectInfos.size(); i++) { SelectInfo selectInfo = selectInfos.get(i); + if (selectInfo.getAlias() != null) { + queryBuilder.inlineSelectAlias(selectInfo.getAlias(), selectInfo.getExpression()); + } aliasManager.unregisterAliasInfoForBottomLevel(selectInfo); unregisterParameterExpressions(selectInfo.getExpression()); } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/transformer/correlation/AbstractCorrelatedSubselectTupleListTransformer.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/transformer/correlation/AbstractCorrelatedSubselectTupleListTransformer.java index e7a8d1c763..34963c8d1d 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/transformer/correlation/AbstractCorrelatedSubselectTupleListTransformer.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/transformer/correlation/AbstractCorrelatedSubselectTupleListTransformer.java @@ -69,10 +69,17 @@ public AbstractCorrelatedSubselectTupleListTransformer(ExpressionFactory ef, Cor super(ef, correlator, viewRootType, embeddingViewType, correlationResult, correlationProviderFactory, attributePath, fetches, viewRootIndex, embeddingViewIndex, tupleIndex, correlationBasisType, correlationBasisEntity, entityViewConfiguration); this.evm = evm; this.viewRootAlias = viewRootAlias; - this.viewRootIdExpression = viewRootAlias + "." + getEntityIdName(viewRootType.getEntityClass()); + String viewRootAliasPrefix = viewRootAlias + "."; + this.viewRootIdExpression = viewRootAliasPrefix + getEntityIdName(viewRootType.getEntityClass()); this.viewRootIdMapperCount = viewIdMapperCount(viewRootType); this.embeddingViewPath = embeddingViewPath; - this.embeddingViewIdExpression = viewRootAlias.equals(embeddingViewPath) ? viewRootAlias + "." + getEntityIdName(embeddingViewType.getEntityClass()) : viewRootAlias + "." + embeddingViewPath + "." + getEntityIdName(embeddingViewType.getEntityClass()); + if (viewRootAlias.equals(embeddingViewPath)) { + this.embeddingViewIdExpression = viewRootAliasPrefix + getEntityIdName(embeddingViewType.getEntityClass()); + } else if (embeddingViewPath.startsWith(viewRootAliasPrefix)) { + this.embeddingViewIdExpression = embeddingViewPath + "." + getEntityIdName(embeddingViewType.getEntityClass()); + } else { + this.embeddingViewIdExpression = viewRootAlias + "." + embeddingViewPath + "." + getEntityIdName(embeddingViewType.getEntityClass()); + } this.embeddingViewIdMapperCount = viewIdMapperCount(embeddingViewType); this.maximumViewMapperCount = Math.max(1, Math.max(viewRootIdMapperCount, embeddingViewIdMapperCount)); this.correlationBasisExpression = correlationBasisExpression; diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/fetch/subview/SubviewFetchTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/fetch/subview/SubviewFetchTest.java index 257c210d99..728722213c 100644 --- a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/fetch/subview/SubviewFetchTest.java +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/fetch/subview/SubviewFetchTest.java @@ -26,9 +26,11 @@ import com.blazebit.persistence.view.EntityViewManager; import com.blazebit.persistence.view.EntityViewSetting; import com.blazebit.persistence.view.EntityViews; +import com.blazebit.persistence.view.Sorters; import com.blazebit.persistence.view.spi.EntityViewConfiguration; import com.blazebit.persistence.view.testsuite.AbstractEntityViewTest; import com.blazebit.persistence.view.testsuite.fetch.subview.model.DocumentSelectSubviewTestView; +import com.blazebit.persistence.view.testsuite.fetch.subview.model.DocumentSubselectSubviewTestView; import com.blazebit.persistence.view.testsuite.fetch.subview.model.PersonSelectSubview; import org.junit.Before; import org.junit.Test; @@ -80,7 +82,7 @@ public void setUp() { @Test // NOTE: Eclipselink and Datanucleus don't support the single valued id access optimization which causes a cyclic join dependency @Category({ NoDatanucleus.class, NoOpenJPA.class, NoEclipselink.class }) - public void testSubqueryFetch() { + public void testSubqueryFetchOptional() { EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); cfg.addEntityView(DocumentSelectSubviewTestView.class); cfg.addEntityView(PersonSelectSubview.class); @@ -93,4 +95,21 @@ public void testSubqueryFetch() { assertEquals(1, results.size()); } + + @Test + // NOTE: Eclipselink and Datanucleus don't support the single valued id access optimization which causes a cyclic join dependency + @Category({ NoDatanucleus.class, NoOpenJPA.class, NoEclipselink.class }) + public void testSubselectFetchWithSorter() { + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(DocumentSubselectSubviewTestView.class); + EntityViewManager evm = cfg.createEntityViewManager(cbf); + + CriteriaBuilder criteria = cbf.create(em, Document.class, "d").orderByAsc("id"); + EntityViewSetting> setting = EntityViewSetting.create(DocumentSubselectSubviewTestView.class); + setting.addAttributeSorter("name", Sorters.ascending()); + CriteriaBuilder cb = evm.applySetting(setting, criteria); + List results = cb.getResultList(); + + assertEquals(1, results.size()); + } } diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/fetch/subview/model/DocumentSubselectSubviewTestView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/fetch/subview/model/DocumentSubselectSubviewTestView.java new file mode 100644 index 0000000000..97757cce0f --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/fetch/subview/model/DocumentSubselectSubviewTestView.java @@ -0,0 +1,42 @@ +/* + * Copyright 2014 - 2019 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.fetch.subview.model; + +import com.blazebit.persistence.testsuite.entity.Document; +import com.blazebit.persistence.testsuite.entity.Person; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.FetchStrategy; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.Mapping; + +/** + * + * @author Christian Beikov + * @since 1.4.0 + */ +@EntityView(Document.class) +public interface DocumentSubselectSubviewTestView { + + @IdMapping + public Long getId(); + + public String getName(); + + @Mapping(fetch = FetchStrategy.SUBSELECT) + public Person getOwner(); + +}