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