2121import jakarta .persistence .criteria .CriteriaQuery ;
2222import jakarta .persistence .criteria .Expression ;
2323import jakarta .persistence .criteria .From ;
24- import jakarta .persistence .criteria .Join ;
25- import jakarta .persistence .criteria .JoinType ;
2624import jakarta .persistence .criteria .Predicate ;
2725import jakarta .persistence .metamodel .Attribute ;
2826import jakarta .persistence .metamodel .EntityType ;
3129import java .util .Collection ;
3230import java .util .Iterator ;
3331import java .util .List ;
34- import java .util .Objects ;
3532
3633import org .springframework .data .domain .Sort ;
3734import org .springframework .data .jpa .domain .JpaSort ;
@@ -94,6 +91,14 @@ public JpaQueryCreator(PartTree tree, ReturnedType type, ParameterMetadataProvid
9491 this .entity = JpqlQueryBuilder .entity (returnedType .getDomainType ());
9592 }
9693
94+ From <?, ?> getFrom () {
95+ return from ;
96+ }
97+
98+ JpqlQueryBuilder .Entity getEntity () {
99+ return entity ;
100+ }
101+
97102 /**
98103 * Returns all {@link jakarta.persistence.criteria.ParameterExpression} created when creating the query.
99104 *
@@ -125,12 +130,13 @@ protected JpqlQueryBuilder.Predicate or(JpqlQueryBuilder.Predicate base, JpqlQue
125130 @ Override
126131 protected final String complete (@ Nullable JpqlQueryBuilder .Predicate predicate , Sort sort ) {
127132
128- JpqlQueryBuilder .AbstractJpqlQuery query = applyPredicate (predicate , buildQuery ( sort ) );
133+ JpqlQueryBuilder .AbstractJpqlQuery query = createQuery (predicate , sort );
129134 return query .render ();
130135 }
131136
132- protected JpqlQueryBuilder .AbstractJpqlQuery applyPredicate (@ Nullable JpqlQueryBuilder .Predicate predicate ,
133- JpqlQueryBuilder .AbstractJpqlQuery query ) {
137+ protected JpqlQueryBuilder .AbstractJpqlQuery createQuery (@ Nullable JpqlQueryBuilder .Predicate predicate , Sort sort ) {
138+
139+ JpqlQueryBuilder .Select query = buildQuery (sort );
134140
135141 if (predicate != null ) {
136142 return query .where (predicate );
@@ -145,7 +151,7 @@ protected JpqlQueryBuilder.AbstractJpqlQuery applyPredicate(@Nullable JpqlQueryB
145151 * @param sort
146152 * @return
147153 */
148- protected JpqlQueryBuilder .AbstractJpqlQuery buildQuery (Sort sort ) {
154+ protected JpqlQueryBuilder .Select buildQuery (Sort sort ) {
149155
150156 JpqlQueryBuilder .Select select = doSelect (sort );
151157
@@ -159,8 +165,8 @@ protected JpqlQueryBuilder.AbstractJpqlQuery buildQuery(Sort sort) {
159165 QueryUtils .checkSortExpression (order );
160166
161167 try {
162- expression = JpqlQueryBuilder .expression (
163- toExpressionRecursively ( entity , from , PropertyPath .from (order .getProperty (), entityType .getJavaType ())));
168+ expression = JpqlQueryBuilder .expression (JpqlUtils . toExpressionRecursively ( entity , from ,
169+ PropertyPath .from (order .getProperty (), entityType .getJavaType ())));
164170 } catch (PropertyReferenceException e ) {
165171
166172 if (order instanceof JpaSort .JpaOrder jpaOrder && jpaOrder .isUnsafe ()) {
@@ -227,6 +233,10 @@ Collection<String> getRequiredSelection(Sort sort, ReturnedType returnedType) {
227233 return returnedType .getInputProperties ();
228234 }
229235
236+ String render (ParameterMetadata <?> metadata ) {
237+ return "?" + (metadata .getPosition () + 1 );
238+ }
239+
230240 /**
231241 * Creates a {@link Predicate} from the given {@link Part}.
232242 *
@@ -270,7 +280,7 @@ public JpqlQueryBuilder.Predicate build() {
270280 PropertyPath property = part .getProperty ();
271281 Type type = part .getType ();
272282
273- PathAndOrigin pas = toExpressionRecursively (entity , from , property );
283+ PathAndOrigin pas = JpqlUtils . toExpressionRecursively (entity , from , property );
274284 JpqlQueryBuilder .WhereStep where = JpqlQueryBuilder .where (pas );
275285 JpqlQueryBuilder .WhereStep whereIgnoreCase = JpqlQueryBuilder .where (potentiallyIgnoreCase (pas ));
276286
@@ -352,19 +362,14 @@ public JpqlQueryBuilder.Predicate build() {
352362 }
353363 }
354364
355- private String render (ParameterMetadata <?> metadata ) {
356- return "?" + (metadata .getPosition () + 1 );
357- }
358-
359365 /**
360366 * Applies an {@code UPPERCASE} conversion to the given {@link Expression} in case the underlying {@link Part}
361367 * requires ignoring case.
362368 *
363369 * @param path must not be {@literal null}.
364370 * @return
365371 */
366- private <T > JpqlQueryBuilder .Expression potentiallyIgnoreCase (JpqlQueryBuilder .Origin source ,
367- PropertyPath path ) {
372+ private <T > JpqlQueryBuilder .Expression potentiallyIgnoreCase (JpqlQueryBuilder .Origin source , PropertyPath path ) {
368373 return potentiallyIgnoreCase (path , JpqlQueryBuilder .expression (source , path ));
369374 }
370375
@@ -414,56 +419,4 @@ private boolean canUpperCase(PropertyPath path) {
414419 }
415420 }
416421
417- static PathAndOrigin toExpressionRecursively (JpqlQueryBuilder .Origin source , From <?, ?> from ,
418- PropertyPath property ) {
419- return toExpressionRecursively (source , from , property , false );
420- }
421-
422- static PathAndOrigin toExpressionRecursively (JpqlQueryBuilder .Origin source , From <?, ?> from ,
423- PropertyPath property , boolean isForSelection ) {
424- return toExpressionRecursively (source , from , property , isForSelection , false );
425- }
426-
427- /**
428- * Creates an expression with proper inner and left joins by recursively navigating the path
429- *
430- * @param from the {@link From}
431- * @param property the property path
432- * @param isForSelection is the property navigated for the selection or ordering part of the query?
433- * @param hasRequiredOuterJoin has a parent already required an outer join?
434- * @param <T> the type of the expression
435- * @return the expression
436- */
437- @ SuppressWarnings ("unchecked" )
438- static PathAndOrigin toExpressionRecursively (JpqlQueryBuilder .Origin source , From <?, ?> from ,
439- PropertyPath property , boolean isForSelection , boolean hasRequiredOuterJoin ) {
440-
441- String segment = property .getSegment ();
442-
443- boolean isLeafProperty = !property .hasNext ();
444-
445- boolean requiresOuterJoin = QueryUtils .requiresOuterJoin (from , property , isForSelection , hasRequiredOuterJoin );
446-
447- // if it does not require an outer join and is a leaf, simply get the segment
448- if (!requiresOuterJoin && isLeafProperty ) {
449- return new PathAndOrigin (property , source , false );
450- }
451-
452- // get or create the join
453- JpqlQueryBuilder .Join joinSource = requiresOuterJoin ? JpqlQueryBuilder .leftJoin (source , segment )
454- : JpqlQueryBuilder .innerJoin (source , segment );
455- JoinType joinType = requiresOuterJoin ? JoinType .LEFT : JoinType .INNER ;
456- Join <?, ?> join = QueryUtils .getOrCreateJoin (from , segment , joinType );
457-
458- // if it's a leaf, return the join
459- if (isLeafProperty ) {
460- return new PathAndOrigin (property , joinSource , true );
461- }
462-
463- PropertyPath nextProperty = Objects .requireNonNull (property .next (), "An element of the property path is null" );
464-
465- // recurse with the next property
466- return toExpressionRecursively (joinSource , join , nextProperty , isForSelection , requiresOuterJoin );
467- }
468-
469422}
0 commit comments