@@ -605,7 +605,7 @@ public static String getProjection(String query) {
605
605
private static javax .persistence .criteria .Order toJpaOrder (Order order , From <?, ?> from , CriteriaBuilder cb ) {
606
606
607
607
PropertyPath property = PropertyPath .from (order .getProperty (), from .getJavaType ());
608
- Expression <?> expression = toExpressionRecursively (from , property );
608
+ Expression <?> expression = toExpressionRecursively (from , property , true );
609
609
610
610
if (order .isIgnoreCase () && String .class .equals (expression .getJavaType ())) {
611
611
Expression <String > lower = cb .lower ((Expression <String >) expression );
@@ -619,8 +619,12 @@ static <T> Expression<T> toExpressionRecursively(From<?, ?> from, PropertyPath p
619
619
return toExpressionRecursively (from , property , false );
620
620
}
621
621
622
- @ SuppressWarnings ("unchecked" )
623
622
static <T > Expression <T > toExpressionRecursively (From <?, ?> from , PropertyPath property , boolean isForSelection ) {
623
+ return toExpressionRecursively (from , property , isForSelection , false );
624
+ }
625
+
626
+ @ SuppressWarnings ("unchecked" )
627
+ static <T > Expression <T > toExpressionRecursively (From <?, ?> from , PropertyPath property , boolean isForSelection , boolean hasRequiredOuterJoin ) {
624
628
625
629
Bindable <?> propertyPathModel ;
626
630
Bindable <?> model = from .getModel ();
@@ -637,10 +641,14 @@ static <T> Expression<T> toExpressionRecursively(From<?, ?> from, PropertyPath p
637
641
propertyPathModel = from .get (segment ).getModel ();
638
642
}
639
643
640
- if (requiresOuterJoin (propertyPathModel , model instanceof PluralAttribute , !property .hasNext (), isForSelection )
644
+ if (requiresOuterJoin (propertyPathModel , model instanceof PluralAttribute , !property .hasNext (), isForSelection , hasRequiredOuterJoin )
641
645
&& !isAlreadyFetched (from , segment )) {
642
- Join <?, ?> join = getOrCreateJoin (from , segment );
643
- return (Expression <T >) (property .hasNext () ? toExpressionRecursively (join , property .next (), isForSelection )
646
+ Join <?, ?> join = getOrCreateJoin (from , segment , JoinType .LEFT );
647
+ return (Expression <T >) (property .hasNext () ? toExpressionRecursively (join , property .next (), isForSelection , true )
648
+ : join );
649
+ } else if (property .hasNext ()) {
650
+ Join <?, ?> join = getOrCreateJoin (from , segment , JoinType .INNER );
651
+ return (Expression <T >) (property .hasNext () ? toExpressionRecursively (join , property .next (), isForSelection , false )
644
652
: join );
645
653
} else {
646
654
Path <Object > path = from .get (segment );
@@ -655,11 +663,12 @@ static <T> Expression<T> toExpressionRecursively(From<?, ?> from, PropertyPath p
655
663
* @param propertyPathModel may be {@literal null}.
656
664
* @param isPluralAttribute is the attribute of Collection type?
657
665
* @param isLeafProperty is this the final property navigated by a {@link PropertyPath}?
658
- * @param isForSelection is the property navigated for the selection part of the query?
666
+ * @param isForSelection is the property navigated for the selection or ordering part of the query?
667
+ * @param hasRequiredOuterJoin has a parent already required an outer join?
659
668
* @return whether an outer join is to be used for integrating this attribute in a query.
660
669
*/
661
670
private static boolean requiresOuterJoin (@ Nullable Bindable <?> propertyPathModel , boolean isPluralAttribute ,
662
- boolean isLeafProperty , boolean isForSelection ) {
671
+ boolean isLeafProperty , boolean isForSelection , boolean hasRequiredOuterJoin ) {
663
672
664
673
if (propertyPathModel == null && isPluralAttribute ) {
665
674
return true ;
@@ -681,14 +690,14 @@ private static boolean requiresOuterJoin(@Nullable Bindable<?> propertyPathModel
681
690
boolean isInverseOptionalOneToOne = PersistentAttributeType .ONE_TO_ONE == attribute .getPersistentAttributeType ()
682
691
&& StringUtils .hasText (getAnnotationProperty (attribute , "mappedBy" , "" ));
683
692
684
- // if this path is part of the select list we need to generate an explicit outer join in order to prevent Hibernate
693
+ // if this path is part of the select or order list we need to generate an explicit outer join in order to prevent Hibernate
685
694
// to use an inner join instead.
686
695
// see https://hibernate.atlassian.net/browse/HHH-12999.
687
- if (isLeafProperty && !isForSelection && !attribute .isCollection () && !isInverseOptionalOneToOne ) {
696
+ if (isLeafProperty && !isForSelection && !attribute .isCollection () && !isInverseOptionalOneToOne && ! hasRequiredOuterJoin ) {
688
697
return false ;
689
698
}
690
699
691
- return getAnnotationProperty (attribute , "optional" , true );
700
+ return hasRequiredOuterJoin || getAnnotationProperty (attribute , "optional" , true );
692
701
}
693
702
694
703
private static <T > T getAnnotationProperty (Attribute <?, ?> attribute , String propertyName , T defaultValue ) {
@@ -716,24 +725,25 @@ static Expression<Object> toExpressionRecursively(Path<Object> path, PropertyPat
716
725
}
717
726
718
727
/**
719
- * Returns an existing join for the given attribute if one already exists or creates a new one if not.
728
+ * Returns an existing join for the given attribute and join type if one already exists or creates a new one if not.
720
729
*
721
730
* @param from the {@link From} to get the current joins from.
722
731
* @param attribute the {@link Attribute} to look for in the current joins.
732
+ * @param joinType the join type
723
733
* @return will never be {@literal null}.
724
734
*/
725
- private static Join <?, ?> getOrCreateJoin (From <?, ?> from , String attribute ) {
735
+ private static Join <?, ?> getOrCreateJoin (From <?, ?> from , String attribute , JoinType joinType ) {
726
736
727
737
for (Join <?, ?> join : from .getJoins ()) {
728
738
729
739
boolean sameName = join .getAttribute ().getName ().equals (attribute );
730
740
731
- if (sameName && join .getJoinType ().equals (JoinType . LEFT )) {
741
+ if (sameName && join .getJoinType ().equals (joinType )) {
732
742
return join ;
733
743
}
734
744
}
735
745
736
- return from .join (attribute , JoinType . LEFT );
746
+ return from .join (attribute , joinType );
737
747
}
738
748
739
749
/**
0 commit comments