21
21
import java .lang .annotation .Annotation ;
22
22
import java .lang .reflect .AnnotatedElement ;
23
23
import java .lang .reflect .Member ;
24
- import java .util .ArrayList ;
25
- import java .util .Collections ;
26
- import java .util .HashMap ;
27
- import java .util .HashSet ;
28
- import java .util .Iterator ;
29
- import java .util .List ;
30
- import java .util .Locale ;
31
- import java .util .Map ;
32
- import java .util .Objects ;
33
- import java .util .Set ;
24
+ import java .util .*;
34
25
import java .util .regex .Matcher ;
35
26
import java .util .regex .Pattern ;
36
27
import java .util .stream .Collectors ;
42
33
import javax .persistence .Query ;
43
34
import javax .persistence .criteria .CriteriaBuilder ;
44
35
import javax .persistence .criteria .Expression ;
36
+ import javax .persistence .criteria .Fetch ;
45
37
import javax .persistence .criteria .From ;
46
38
import javax .persistence .criteria .Join ;
47
39
import javax .persistence .criteria .JoinType ;
48
40
import javax .persistence .metamodel .Attribute ;
49
- import javax .persistence .metamodel .Attribute .PersistentAttributeType ;
50
41
import javax .persistence .metamodel .Bindable ;
51
42
import javax .persistence .metamodel .ManagedType ;
52
43
import javax .persistence .metamodel .PluralAttribute ;
53
44
import javax .persistence .metamodel .SingularAttribute ;
45
+ import javax .persistence .metamodel .Attribute .PersistentAttributeType ;
54
46
55
47
import org .springframework .core .annotation .AnnotationUtils ;
56
48
import org .springframework .dao .InvalidDataAccessApiUsageException ;
@@ -104,7 +96,8 @@ public abstract class QueryUtils {
104
96
105
97
private static final Pattern ALIAS_MATCH ;
106
98
private static final Pattern COUNT_MATCH ;
107
- private static final Pattern PROJECTION_CLAUSE = Pattern .compile ("select\\ s+(?:distinct\\ s+)?(.+)\\ s+from" , Pattern .CASE_INSENSITIVE );
99
+ private static final Pattern PROJECTION_CLAUSE = Pattern .compile ("select\\ s+(?:distinct\\ s+)?(.+)\\ s+from" ,
100
+ Pattern .CASE_INSENSITIVE );
108
101
109
102
private static final Pattern NO_DIGITS = Pattern .compile ("\\ D+" );
110
103
@@ -114,8 +107,8 @@ public abstract class QueryUtils {
114
107
private static final String EQUALS_CONDITION_STRING = "%s.%s = :%s" ;
115
108
private static final Pattern ORDER_BY = Pattern .compile (".*order\\ s+by\\ s+.*" , CASE_INSENSITIVE );
116
109
117
- private static final Pattern NAMED_PARAMETER = Pattern
118
- . compile ( COLON_NO_DOUBLE_COLON + IDENTIFIER + "|#" + IDENTIFIER , CASE_INSENSITIVE );
110
+ private static final Pattern NAMED_PARAMETER = Pattern . compile ( COLON_NO_DOUBLE_COLON + IDENTIFIER + "|#" + IDENTIFIER ,
111
+ CASE_INSENSITIVE );
119
112
120
113
private static final Pattern CONSTRUCTOR_EXPRESSION ;
121
114
@@ -490,9 +483,9 @@ public static String createCountQueryFor(String originalQuery, @Nullable String
490
483
&& !variable .startsWith ("count(" ) //
491
484
&& !variable .contains ("," ); //
492
485
493
- String complexCountValue = matcher .matches () &&
494
- StringUtils . hasText ( matcher . group ( COMPLEX_COUNT_FIRST_INDEX )) ?
495
- COMPLEX_COUNT_VALUE : COMPLEX_COUNT_LAST_VALUE ;
486
+ String complexCountValue = matcher .matches () && StringUtils . hasText ( matcher . group ( COMPLEX_COUNT_FIRST_INDEX ))
487
+ ? COMPLEX_COUNT_VALUE
488
+ : COMPLEX_COUNT_LAST_VALUE ;
496
489
497
490
String replacement = useVariable ? SIMPLE_COUNT_VALUE : complexCountValue ;
498
491
countQuery = matcher .replaceFirst (String .format (COUNT_REPLACEMENT_TEMPLATE , replacement ));
@@ -626,15 +619,16 @@ static <T> Expression<T> toExpressionRecursively(From<?, ?> from, PropertyPath p
626
619
/**
627
620
* Creates an expression with proper inner and left joins by recursively navigating the path
628
621
*
629
- * @param from the {@link From}
630
- * @param property the property path
631
- * @param isForSelection is the property navigated for the selection or ordering part of the query?
622
+ * @param from the {@link From}
623
+ * @param property the property path
624
+ * @param isForSelection is the property navigated for the selection or ordering part of the query?
632
625
* @param hasRequiredOuterJoin has a parent already required an outer join?
633
- * @param <T> the type of the expression
626
+ * @param <T> the type of the expression
634
627
* @return the expression
635
628
*/
636
- @ SuppressWarnings ("unchecked" ) static <T > Expression <T > toExpressionRecursively (From <?, ?> from ,
637
- PropertyPath property , boolean isForSelection , boolean hasRequiredOuterJoin ) {
629
+ @ SuppressWarnings ("unchecked" )
630
+ static <T > Expression <T > toExpressionRecursively (From <?, ?> from , PropertyPath property , boolean isForSelection ,
631
+ boolean hasRequiredOuterJoin ) {
638
632
639
633
String segment = property .getSegment ();
640
634
@@ -663,16 +657,15 @@ static <T> Expression<T> toExpressionRecursively(From<?, ?> from, PropertyPath p
663
657
}
664
658
665
659
/**
666
- * Checks if this attribute requires an outer join.
667
- * This is the case eg. if it hadn't already been fetched with an inner join and if it's an a optional association,
668
- * and if previous paths has already required outer joins.
669
- * It also ensures outer joins are used even when Hibernate defaults to inner joins (HHH-12712 and HHH-12999).
660
+ * Checks if this attribute requires an outer join. This is the case eg. if it hadn't already been fetched with an
661
+ * inner join and if it's an a optional association, and if previous paths has already required outer joins. It also
662
+ * ensures outer joins are used even when Hibernate defaults to inner joins (HHH-12712 and HHH-12999).
670
663
*
671
- * @param from the {@link From} to check for fetches.
672
- * @param property the property path
673
- * @param isForSelection is the property navigated for the selection or ordering part of the query? if true,
674
- * we need to generate an explicit outer join in order to prevent Hibernate to use an
675
- * inner join instead. see https://hibernate.atlassian.net/browse/HHH-12999
664
+ * @param from the {@link From} to check for fetches.
665
+ * @param property the property path
666
+ * @param isForSelection is the property navigated for the selection or ordering part of the query? if true, we need
667
+ * to generate an explicit outer join in order to prevent Hibernate to use an inner join instead. see
668
+ * https://hibernate.atlassian.net/browse/HHH-12999
676
669
* @param hasRequiredOuterJoin has a parent already required an outer join?
677
670
* @return whether an outer join is to be used for integrating this attribute in a query.
678
671
*/
@@ -696,7 +689,7 @@ private static boolean requiresOuterJoin(From<?, ?> from, PropertyPath property,
696
689
if (model instanceof ManagedType ) {
697
690
managedType = (ManagedType <?>) model ;
698
691
} else if (model instanceof SingularAttribute
699
- && ((SingularAttribute <?, ?>) model ).getType () instanceof ManagedType ) {
692
+ && ((SingularAttribute <?, ?>) model ).getType () instanceof ManagedType ) {
700
693
managedType = (ManagedType <?>) ((SingularAttribute <?, ?>) model ).getType ();
701
694
}
702
695
if (managedType != null ) {
@@ -760,34 +753,48 @@ private static <T> T getAnnotationProperty(Attribute<?, ?> attribute, String pro
760
753
/**
761
754
* Returns an existing join for the given attribute if one already exists or creates a new one if not.
762
755
*
763
- * @param from the {@link From} to get the current joins from.
756
+ * @param from the {@link From} to get the current joins from.
764
757
* @param attribute the {@link Attribute} to look for in the current joins.
765
- * @param joinType the join type to create if none was found
758
+ * @param joinType the join type to create if none was found
766
759
* @return will never be {@literal null}.
767
760
*/
768
761
private static Join <?, ?> getOrCreateJoin (From <?, ?> from , String attribute , JoinType joinType ) {
769
- return from .getJoins ().stream ()
770
- .filter (join -> join .getAttribute ().getName ().equals (attribute ))
771
- .findFirst ()
772
- .orElseGet (() -> from .join (attribute , joinType ));
762
+
763
+ for (Join <?, ?> join : from .getJoins ()) {
764
+
765
+ if (join .getAttribute ().getName ().equals (attribute )) {
766
+ return join ;
767
+ }
768
+ }
769
+ return from .join (attribute , joinType );
773
770
}
774
771
775
772
/**
776
773
* Return whether the given {@link From} contains an inner join for the attribute with the given name.
777
774
*
778
- * @param from the {@link From} to check for joins.
775
+ * @param from the {@link From} to check for joins.
779
776
* @param attribute the attribute name to check.
780
777
* @return true if the attribute has already been inner joined
781
778
*/
782
779
private static boolean isAlreadyInnerJoined (From <?, ?> from , String attribute ) {
783
780
784
- boolean isInnerJoinFetched = from .getFetches ().stream ().anyMatch (
785
- fetch -> fetch .getAttribute ().getName ().equals (attribute ) && fetch .getJoinType ().equals (JoinType .INNER ));
781
+ for (Fetch <?, ?> fetch : from .getFetches ()) {
786
782
787
- boolean isSimplyInnerJoined = from .getJoins ().stream ()
788
- .anyMatch (join -> join .getAttribute ().getName ().equals (attribute ) && join .getJoinType ().equals (JoinType .INNER ));
783
+ if (fetch .getAttribute ().getName ().equals (attribute ) //
784
+ && fetch .getJoinType ().equals (JoinType .INNER )) {
785
+ return true ;
786
+ }
787
+ }
788
+
789
+ for (Join <?, ?> join : from .getJoins ()) {
790
+
791
+ if (join .getAttribute ().getName ().equals (attribute ) //
792
+ && join .getJoinType ().equals (JoinType .INNER )) {
793
+ return true ;
794
+ }
795
+ }
789
796
790
- return isInnerJoinFetched || isSimplyInnerJoined ;
797
+ return false ;
791
798
}
792
799
793
800
/**
0 commit comments