|
4 | 4 | */ |
5 | 5 | package org.hibernate.query.sqm.internal; |
6 | 6 |
|
| 7 | +import java.sql.Time; |
| 8 | +import java.sql.Timestamp; |
7 | 9 | import java.sql.Types; |
8 | 10 | import java.util.ArrayList; |
9 | 11 | import java.util.Collection; |
|
21 | 23 | import org.hibernate.engine.spi.SessionFactoryImplementor; |
22 | 24 | import org.hibernate.engine.spi.SharedSessionContractImplementor; |
23 | 25 | import org.hibernate.internal.util.collections.CollectionHelper; |
| 26 | +import org.hibernate.internal.util.collections.Stack; |
24 | 27 | import org.hibernate.jpa.spi.JpaCompliance; |
25 | 28 | import org.hibernate.metamodel.mapping.BasicValuedMapping; |
26 | 29 | import org.hibernate.metamodel.mapping.Bindable; |
@@ -168,48 +171,72 @@ public static ModelPartContainer getTargetMappingIfNeeded( |
168 | 171 | SqmPath<?> sqmPath, |
169 | 172 | ModelPartContainer modelPartContainer, |
170 | 173 | SqmToSqlAstConverter sqlAstCreationState) { |
171 | | - final SqmQueryPart<?> queryPart = sqlAstCreationState.getCurrentSqmQueryPart(); |
172 | | - if ( queryPart != null ) { |
173 | | - // We only need to do this for queries |
174 | | - final Clause clause = sqlAstCreationState.getCurrentClauseStack().getCurrent(); |
175 | | - if ( clause != Clause.FROM && modelPartContainer.getPartMappingType() != modelPartContainer && sqmPath.getLhs() instanceof SqmFrom<?, ?> ) { |
176 | | - final ModelPart modelPart; |
177 | | - if ( modelPartContainer instanceof PluralAttributeMapping ) { |
178 | | - modelPart = getCollectionPart( |
179 | | - (PluralAttributeMapping) modelPartContainer, |
180 | | - castNonNull( sqmPath.getNavigablePath().getParent() ) |
181 | | - ); |
182 | | - } |
183 | | - else { |
184 | | - modelPart = modelPartContainer; |
185 | | - } |
186 | | - if ( modelPart instanceof EntityAssociationMapping association ) { |
187 | | - // If the path is one of the association's target key properties, |
188 | | - // we need to render the target side if in group/order by |
189 | | - if ( association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() ) |
190 | | - && ( clause == Clause.GROUP || clause == Clause.ORDER |
191 | | - || !isFkOptimizationAllowed( sqmPath.getLhs(), association ) |
192 | | - || queryPart.getFirstQuerySpec().groupByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState ) |
193 | | - || queryPart.getFirstQuerySpec().orderByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState ) ) ) { |
194 | | - return association.getAssociatedEntityMappingType(); |
195 | | - } |
| 174 | + // We only need to do this for queries |
| 175 | + if ( sqlAstCreationState.getCurrentClauseStack().getCurrent() != Clause.FROM |
| 176 | + && modelPartContainer.getPartMappingType() != modelPartContainer && sqmPath.getLhs() instanceof SqmFrom<?, ?> ) { |
| 177 | + final ModelPart modelPart; |
| 178 | + if ( modelPartContainer instanceof PluralAttributeMapping pluralAttributeMapping ) { |
| 179 | + modelPart = getCollectionPart( |
| 180 | + pluralAttributeMapping, |
| 181 | + castNonNull( sqmPath.getNavigablePath().getParent() ) |
| 182 | + ); |
| 183 | + } |
| 184 | + else { |
| 185 | + modelPart = modelPartContainer; |
| 186 | + } |
| 187 | + if ( modelPart instanceof EntityAssociationMapping association ) { |
| 188 | + if ( shouldRenderTargetSide( sqmPath, association, sqlAstCreationState ) ) { |
| 189 | + return association.getAssociatedEntityMappingType(); |
196 | 190 | } |
197 | 191 | } |
198 | 192 | } |
199 | 193 | return modelPartContainer; |
200 | 194 | } |
201 | 195 |
|
| 196 | + private static boolean shouldRenderTargetSide( |
| 197 | + SqmPath<?> sqmPath, |
| 198 | + EntityAssociationMapping association, |
| 199 | + SqmToSqlAstConverter sqlAstCreationState) { |
| 200 | + if ( !association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() ) ) { |
| 201 | + return false; |
| 202 | + } |
| 203 | + // If the path is one of the association's target key properties, |
| 204 | + // we need to render the target side if in group/order by |
| 205 | + final Clause clause = sqlAstCreationState.getCurrentClauseStack().getCurrent(); |
| 206 | + return clause == Clause.GROUP || clause == Clause.ORDER |
| 207 | + || !isFkOptimizationAllowed( sqmPath.getLhs(), association ) |
| 208 | + || clauseContainsPath( Clause.GROUP, sqmPath, sqlAstCreationState ) |
| 209 | + || clauseContainsPath( Clause.ORDER, sqmPath, sqlAstCreationState ); |
| 210 | + } |
| 211 | + |
| 212 | + private static boolean clauseContainsPath( |
| 213 | + Clause clauseToCheck, |
| 214 | + SqmPath<?> sqmPath, |
| 215 | + SqmToSqlAstConverter sqlAstCreationState) { |
| 216 | + final Stack<SqmQueryPart> queryPartStack = sqlAstCreationState.getSqmQueryPartStack(); |
| 217 | + final NavigablePath navigablePath = sqmPath.getNavigablePath(); |
| 218 | + final Boolean found = queryPartStack.findCurrentFirst( queryPart -> { |
| 219 | + final SqmQuerySpec<?> spec = queryPart.getFirstQuerySpec(); |
| 220 | + if ( clauseToCheck == Clause.GROUP && spec.groupByClauseContains( navigablePath, sqlAstCreationState ) ) { |
| 221 | + return true; |
| 222 | + } |
| 223 | + else if ( clauseToCheck == Clause.ORDER && spec.orderByClauseContains( navigablePath, sqlAstCreationState ) ) { |
| 224 | + return true; |
| 225 | + } |
| 226 | + else { |
| 227 | + return null; |
| 228 | + } |
| 229 | + } ); |
| 230 | + return Boolean.TRUE.equals( found ); |
| 231 | + } |
| 232 | + |
202 | 233 | private static CollectionPart getCollectionPart(PluralAttributeMapping attribute, NavigablePath path) { |
203 | 234 | final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact( path.getLocalName() ); |
204 | | - if ( nature != null ) { |
205 | | - switch ( nature ) { |
206 | | - case ELEMENT: |
207 | | - return attribute.getElementDescriptor(); |
208 | | - case INDEX: |
209 | | - return attribute.getIndexDescriptor(); |
210 | | - } |
211 | | - } |
212 | | - return null; |
| 235 | + return nature != null ? switch ( nature ) { |
| 236 | + case ELEMENT -> attribute.getElementDescriptor(); |
| 237 | + case INDEX -> attribute.getIndexDescriptor(); |
| 238 | + case ID -> attribute.getIdentifierDescriptor(); |
| 239 | + } : null; |
213 | 240 | } |
214 | 241 |
|
215 | 242 | /** |
@@ -1168,8 +1195,8 @@ private static boolean isMatchingDateJdbcType(Class<?> resultClass, JdbcType jdb |
1168 | 1195 | if ( jdbcType != null ) { |
1169 | 1196 | return switch ( jdbcType.getDefaultSqlTypeCode() ) { |
1170 | 1197 | case Types.DATE -> resultClass.isAssignableFrom(java.sql.Date.class); |
1171 | | - case Types.TIME -> resultClass.isAssignableFrom(java.sql.Time.class); |
1172 | | - case Types.TIMESTAMP -> resultClass.isAssignableFrom(java.sql.Timestamp.class); |
| 1198 | + case Types.TIME -> resultClass.isAssignableFrom( Time.class); |
| 1199 | + case Types.TIMESTAMP -> resultClass.isAssignableFrom( Timestamp.class); |
1173 | 1200 | default -> false; |
1174 | 1201 | }; |
1175 | 1202 | } |
|
0 commit comments