|
24 | 24 | import org.hibernate.metamodel.MappingMetamodel;
|
25 | 25 | import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
26 | 26 | import org.hibernate.metamodel.mapping.Bindable;
|
| 27 | +import org.hibernate.metamodel.mapping.CollectionPart; |
27 | 28 | import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
28 | 29 | import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
29 | 30 | import org.hibernate.metamodel.mapping.EntityMappingType;
|
30 | 31 | import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
31 | 32 | import org.hibernate.metamodel.mapping.JdbcMapping;
|
32 |
| -import org.hibernate.metamodel.mapping.ManagedMappingType; |
33 | 33 | import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
| 34 | +import org.hibernate.metamodel.mapping.ModelPart; |
34 | 35 | import org.hibernate.metamodel.mapping.ModelPartContainer;
|
35 | 36 | import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
36 | 37 | import org.hibernate.query.IllegalQueryOperationException;
|
|
47 | 48 | import org.hibernate.query.sqm.tree.SqmStatement;
|
48 | 49 | import org.hibernate.query.sqm.tree.domain.SqmPath;
|
49 | 50 | import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
|
| 51 | +import org.hibernate.query.sqm.tree.expression.SqmExpression; |
50 | 52 | import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
|
51 | 53 | import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
52 | 54 | import org.hibernate.query.sqm.tree.from.SqmFrom;
|
53 | 55 | import org.hibernate.query.sqm.tree.from.SqmJoin;
|
| 56 | +import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin; |
| 57 | +import org.hibernate.query.sqm.tree.select.SqmOrderByClause; |
54 | 58 | import org.hibernate.query.sqm.tree.select.SqmQueryPart;
|
55 |
| -import org.hibernate.query.sqm.tree.jpa.ParameterCollector; |
| 59 | +import org.hibernate.query.sqm.tree.select.SqmQuerySpec; |
56 | 60 | import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
| 61 | +import org.hibernate.query.sqm.tree.select.SqmSortSpecification; |
57 | 62 | import org.hibernate.spi.NavigablePath;
|
58 | 63 | import org.hibernate.sql.ast.Clause;
|
59 | 64 | import org.hibernate.sql.ast.SqlTreeCreationException;
|
|
69 | 74 | import org.hibernate.type.internal.ConvertedBasicTypeImpl;
|
70 | 75 | import org.hibernate.type.spi.TypeConfiguration;
|
71 | 76 |
|
| 77 | +import static org.hibernate.internal.util.NullnessUtil.castNonNull; |
| 78 | +import static org.hibernate.query.sqm.tree.jpa.ParameterCollector.collectParameters; |
| 79 | + |
72 | 80 | /**
|
73 | 81 | * Helper utilities for dealing with SQM
|
74 | 82 | *
|
@@ -119,31 +127,110 @@ public static IllegalQueryOperationException expectingNonSelect(SqmStatement<?>
|
119 | 127 | );
|
120 | 128 | }
|
121 | 129 |
|
122 |
| - public static boolean needsTargetTableMapping( |
| 130 | + /** |
| 131 | + * Utility that returns the entity association target's mapping type if the specified {@code sqmPath} should |
| 132 | + * be dereferenced using the target table, i.e. when the path's lhs is an explicit join that is used in the |
| 133 | + * group by clause, or defaults to the provided {@code modelPartContainer} otherwise. |
| 134 | + */ |
| 135 | + public static ModelPartContainer getTargetMappingIfNeeded( |
123 | 136 | SqmPath<?> sqmPath,
|
124 | 137 | ModelPartContainer modelPartContainer,
|
125 | 138 | SqmToSqlAstConverter sqlAstCreationState) {
|
126 |
| - final Clause currentClause = sqlAstCreationState.getCurrentClauseStack().getCurrent(); |
127 |
| - return ( currentClause == Clause.GROUP || currentClause == Clause.SELECT || currentClause == Clause.ORDER || currentClause == Clause.HAVING ) |
128 |
| - && modelPartContainer.getPartMappingType() != modelPartContainer |
129 |
| - && sqmPath.getLhs() instanceof SqmFrom<?, ?> |
130 |
| - && modelPartContainer.getPartMappingType() instanceof ManagedMappingType |
131 |
| - && ( groupByClauseContains( sqlAstCreationState.getCurrentSqmQueryPart(), sqmPath.getNavigablePath() ) |
132 |
| - || isNonOptimizableJoin( sqmPath.getLhs() ) ); |
| 139 | + final SqmQueryPart<?> queryPart = sqlAstCreationState.getCurrentSqmQueryPart(); |
| 140 | + if ( queryPart != null ) { |
| 141 | + // We only need to do this for queries |
| 142 | + final Clause clause = sqlAstCreationState.getCurrentClauseStack().getCurrent(); |
| 143 | + if ( clause != Clause.FROM && modelPartContainer.getPartMappingType() != modelPartContainer && sqmPath.getLhs() instanceof SqmFrom<?, ?> ) { |
| 144 | + final ModelPart modelPart; |
| 145 | + if ( modelPartContainer instanceof PluralAttributeMapping ) { |
| 146 | + modelPart = getCollectionPart( |
| 147 | + (PluralAttributeMapping) modelPartContainer, |
| 148 | + castNonNull( sqmPath.getNavigablePath().getParent() ) |
| 149 | + ); |
| 150 | + } |
| 151 | + else { |
| 152 | + modelPart = modelPartContainer; |
| 153 | + } |
| 154 | + if ( modelPart instanceof EntityAssociationMapping ) { |
| 155 | + final EntityAssociationMapping association = (EntityAssociationMapping) modelPart; |
| 156 | + // If the path is one of the association's target key properties, |
| 157 | + // we need to render the target side if in group/order by |
| 158 | + if ( association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() ) |
| 159 | + && ( clause == Clause.GROUP || clause == Clause.ORDER |
| 160 | + || !isFkOptimizationAllowed( sqmPath.getLhs() ) |
| 161 | + || queryPart.getFirstQuerySpec().groupByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState ) |
| 162 | + || queryPart.getFirstQuerySpec().orderByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState ) ) ) { |
| 163 | + return association.getAssociatedEntityMappingType(); |
| 164 | + } |
| 165 | + } |
| 166 | + } |
| 167 | + } |
| 168 | + return modelPartContainer; |
133 | 169 | }
|
134 | 170 |
|
135 |
| - private static boolean groupByClauseContains(SqmQueryPart<?> sqmQueryPart, NavigablePath path) { |
136 |
| - return sqmQueryPart.isSimpleQueryPart() && sqmQueryPart.getFirstQuerySpec().groupByClauseContains( path ); |
| 171 | + private static CollectionPart getCollectionPart(PluralAttributeMapping attribute, NavigablePath path) { |
| 172 | + final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact( path.getLocalName() ); |
| 173 | + if ( nature != null ) { |
| 174 | + switch ( nature ) { |
| 175 | + case ELEMENT: |
| 176 | + return attribute.getElementDescriptor(); |
| 177 | + case INDEX: |
| 178 | + return attribute.getIndexDescriptor(); |
| 179 | + } |
| 180 | + } |
| 181 | + return null; |
137 | 182 | }
|
138 | 183 |
|
139 |
| - private static boolean isNonOptimizableJoin(SqmPath<?> sqmPath) { |
| 184 | + /** |
| 185 | + * Utility that returns {@code false} when the provided {@link SqmPath sqmPath} is |
| 186 | + * a join that cannot be dereferenced through the foreign key on the associated table, |
| 187 | + * i.e. a join that's neither {@linkplain SqmJoinType#INNER} nor {@linkplain SqmJoinType#LEFT} |
| 188 | + * or one that has an explicit on clause predicate. |
| 189 | + */ |
| 190 | + public static boolean isFkOptimizationAllowed(SqmPath<?> sqmPath) { |
140 | 191 | if ( sqmPath instanceof SqmJoin<?, ?> ) {
|
141 |
| - final SqmJoinType sqmJoinType = ( (SqmJoin<?, ?>) sqmPath ).getSqmJoinType(); |
142 |
| - return sqmJoinType != SqmJoinType.INNER && sqmJoinType != SqmJoinType.LEFT; |
| 192 | + final SqmJoin<?, ?> sqmJoin = (SqmJoin<?, ?>) sqmPath; |
| 193 | + switch ( sqmJoin.getSqmJoinType() ) { |
| 194 | + case INNER: |
| 195 | + case LEFT: |
| 196 | + return !( sqmJoin instanceof SqmQualifiedJoin<?, ?>) |
| 197 | + || ( (SqmQualifiedJoin<?, ?>) sqmJoin ).getJoinPredicate() == null; |
| 198 | + default: |
| 199 | + return false; |
| 200 | + } |
143 | 201 | }
|
144 | 202 | return false;
|
145 | 203 | }
|
146 | 204 |
|
| 205 | + public static List<NavigablePath> getGroupByNavigablePaths(SqmQuerySpec<?> querySpec) { |
| 206 | + final List<SqmExpression<?>> expressions = querySpec.getGroupByClauseExpressions(); |
| 207 | + if ( expressions.isEmpty() ) { |
| 208 | + return Collections.emptyList(); |
| 209 | + } |
| 210 | + |
| 211 | + final List<NavigablePath> navigablePaths = new ArrayList<>( expressions.size() ); |
| 212 | + final SqmPathVisitor pathVisitor = new SqmPathVisitor( path -> navigablePaths.add( path.getNavigablePath() ) ); |
| 213 | + for ( SqmExpression<?> expression : expressions ) { |
| 214 | + expression.accept( pathVisitor ); |
| 215 | + } |
| 216 | + return navigablePaths; |
| 217 | + } |
| 218 | + |
| 219 | + public static List<NavigablePath> getOrderByNavigablePaths(SqmQuerySpec<?> querySpec) { |
| 220 | + final SqmOrderByClause order = querySpec.getOrderByClause(); |
| 221 | + if ( order == null || order.getSortSpecifications().isEmpty() ) { |
| 222 | + return Collections.emptyList(); |
| 223 | + } |
| 224 | + |
| 225 | + final List<SqmSortSpecification> sortSpecifications = order.getSortSpecifications(); |
| 226 | + final List<NavigablePath> navigablePaths = new ArrayList<>( sortSpecifications.size() ); |
| 227 | + final SqmPathVisitor pathVisitor = new SqmPathVisitor( path -> navigablePaths.add( path.getNavigablePath() ) ); |
| 228 | + for ( SqmSortSpecification sortSpec : sortSpecifications ) { |
| 229 | + sortSpec.getSortExpression().accept( pathVisitor ); |
| 230 | + } |
| 231 | + return navigablePaths; |
| 232 | + } |
| 233 | + |
147 | 234 | public static Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> generateJdbcParamsXref(
|
148 | 235 | DomainParameterXref domainParameterXref,
|
149 | 236 | JdbcParameterBySqmParameterAccess jdbcParameterBySqmParameterAccess) {
|
@@ -523,7 +610,7 @@ public static SqmStatement.ParameterResolutions resolveParameters(SqmStatement<?
|
523 | 610 | if ( statement.getQuerySource() == SqmQuerySource.CRITERIA ) {
|
524 | 611 | final CriteriaParameterCollector parameterCollector = new CriteriaParameterCollector();
|
525 | 612 |
|
526 |
| - ParameterCollector.collectParameters( |
| 613 | + collectParameters( |
527 | 614 | statement,
|
528 | 615 | parameterCollector::process,
|
529 | 616 | statement.nodeBuilder().getServiceRegistry()
|
|
0 commit comments