93
93
*/
94
94
class QraphQLJpaBaseDataFetcher implements DataFetcher <Object > {
95
95
96
- private static final String OPTIONAL = "optional" ;
96
+ private static final String WHERE = "where" ;
97
+
98
+ protected static final String OPTIONAL = "optional" ;
99
+
100
+ protected static final List <String > ARGUMENTS = Arrays .asList (OPTIONAL );
97
101
98
102
// "__typename" is part of the graphql introspection spec and has to be ignored
99
103
private static final String TYPENAME = "__typename" ;
@@ -130,11 +134,7 @@ protected TypedQuery<?> getQuery(DataFetchingEnvironment environment, Field fiel
130
134
from .alias (from .getModel ().getName ());
131
135
132
136
// Build predicates from query arguments
133
- List <Predicate > predicates = getFieldArguments (field , query , cb , from , environment )
134
- .stream ()
135
- .map (it -> getPredicate (cb , from , from , environment , it ))
136
- .filter (it -> it != null )
137
- .collect (Collectors .toList ());
137
+ List <Predicate > predicates = getFieldPredicates (field , query , cb ,from , from , environment );
138
138
139
139
// Use AND clause to filter results
140
140
if (!predicates .isEmpty ())
@@ -146,9 +146,10 @@ protected TypedQuery<?> getQuery(DataFetchingEnvironment environment, Field fiel
146
146
return entityManager .createQuery (query .distinct (isDistinct ));
147
147
}
148
148
149
- protected final List <Argument > getFieldArguments (Field field , CriteriaQuery <?> query , CriteriaBuilder cb , From <?,?> from , DataFetchingEnvironment environment ) {
149
+ protected final List <Predicate > getFieldPredicates (Field field , CriteriaQuery <?> query , CriteriaBuilder cb , Root <?> root , From <?,?> from , DataFetchingEnvironment environment ) {
150
150
151
151
List <Argument > arguments = new ArrayList <>();
152
+ List <Predicate > predicates = new ArrayList <>();
152
153
153
154
// Loop through all of the fields being requested
154
155
field .getSelectionSet ().getSelections ().forEach (selection -> {
@@ -161,6 +162,8 @@ protected final List<Argument> getFieldArguments(Field field, CriteriaQuery<?> q
161
162
Path <?> fieldPath = from .get (selectedField .getName ());
162
163
From <?,?> fetch = null ;
163
164
Optional <Argument > optionalArgument = getArgument (selectedField , OPTIONAL );
165
+ Optional <Argument > whereArgument = getArgument (selectedField , WHERE );
166
+ Boolean isOptional = null ;
164
167
165
168
// Build predicate arguments for singular attributes only
166
169
if (fieldPath .getModel () instanceof SingularAttribute ) {
@@ -176,46 +179,50 @@ protected final List<Argument> getFieldArguments(Field field, CriteriaQuery<?> q
176
179
query .orderBy (cb .asc (fieldPath ));
177
180
}
178
181
179
- // Process where arguments clauses.
180
- arguments .addAll (selectedField .getArguments ()
181
- .stream ()
182
- .filter (it -> !isOrderByArgument (it ) && !isOptionalArgument (it ))
183
- .map (it -> new Argument (selectedField .getName () + "." + it .getName (), it .getValue ()))
184
- .collect (Collectors .toList ()));
185
-
186
182
// Check if it's an object and the foreign side is One. Then we can eagerly join causing an inner join instead of 2 queries
187
- if (fieldPath .getModel () instanceof SingularAttribute ) {
188
- SingularAttribute <?,?> attribute = (SingularAttribute <?,?>) fieldPath .getModel ();
189
- if (attribute .getPersistentAttributeType () == Attribute .PersistentAttributeType .MANY_TO_ONE
190
- || attribute .getPersistentAttributeType () == Attribute .PersistentAttributeType .ONE_TO_ONE
191
- ) {
192
- // Let's do fugly conversion
193
- Boolean isOptional = optionalArgument .map (it -> getArgumentValue (environment , it , Boolean .class ))
194
- .orElse (attribute .isOptional ());
195
-
196
- // Let's apply left outer join to retrieve optional associations
197
- fetch = reuseFetch (from , selectedField .getName (), isOptional );
198
- }
183
+ SingularAttribute <?,?> attribute = (SingularAttribute <?,?>) fieldPath .getModel ();
184
+ if (attribute .getPersistentAttributeType () == Attribute .PersistentAttributeType .MANY_TO_ONE
185
+ || attribute .getPersistentAttributeType () == Attribute .PersistentAttributeType .ONE_TO_ONE
186
+ ) {
187
+ // Let's do fugly conversion
188
+ isOptional = optionalArgument .map (it -> getArgumentValue (environment , it , Boolean .class ))
189
+ .orElse (attribute .isOptional ());
190
+
191
+ // Let's apply left outer join to retrieve optional associations
192
+ fetch = reuseFetch (from , selectedField .getName (), isOptional );
193
+ } else if (attribute .getPersistentAttributeType () == PersistentAttributeType .EMBEDDED ) {
194
+ // Process where arguments clauses.
195
+ arguments .addAll (selectedField .getArguments ()
196
+ .stream ()
197
+ .filter (it -> !isOrderByArgument (it ) && !isOptionalArgument (it ))
198
+ .map (it -> new Argument (selectedField .getName () + "." + it .getName (),
199
+ it .getValue ()))
200
+ .collect (Collectors .toList ()));
201
+
199
202
}
200
203
} else {
201
- // We must add plural attributes with explicit join
204
+ // We must add plural attributes with explicit join fetch
202
205
// Let's do fugly conversion
203
206
// the many end is a collection, and it is always optional by default (empty collection)
204
- Boolean isOptional = optionalArgument .map (it -> getArgumentValue (environment , it , Boolean .class ))
207
+ isOptional = optionalArgument .map (it -> getArgumentValue (environment , it , Boolean .class ))
205
208
.orElse (toManyDefaultOptional );
206
-
209
+
207
210
// Let's apply join to retrieve associated collection
208
211
fetch = reuseFetch (from , selectedField .getName (), isOptional );
209
212
210
- // TODO add fetch argument parameter
211
213
// Let's fetch element collections to avoid filtering their values used where search criteria
212
- from .fetch (selectedField .getName (),
213
- isOptional ? JoinType .LEFT : JoinType .INNER );
214
+ GraphQLObjectType objectType = getObjectType (environment );
215
+ EntityType <?> entityType = getEntityType (objectType );
216
+
217
+ PluralAttribute <?, ?, ?> attribute = (PluralAttribute <?, ?, ?>) entityType .getAttribute (selectedField .getName ());
218
+
219
+ if (PersistentAttributeType .ELEMENT_COLLECTION == attribute .getPersistentAttributeType ()) {
220
+ from .fetch (selectedField .getName ());
221
+ }
214
222
}
215
-
216
223
// Let's build join fetch graph to avoid Hibernate error:
217
224
// "query specified join fetching, but the owner of the fetched association was not present in the select list"
218
- if (fetch != null && selectedField . getSelectionSet () != null ) {
225
+ if (selectedField . getSelectionSet () != null && fetch != null ) {
219
226
GraphQLFieldDefinition fieldDefinition = getFieldDef (environment .getGraphQLSchema (),
220
227
this .getObjectType (environment ),
221
228
selectedField );
@@ -224,16 +231,21 @@ protected final List<Argument> getFieldArguments(Field field, CriteriaQuery<?> q
224
231
DataFetchingEnvironment fieldEnvironment = wherePredicateEnvironment (environment ,
225
232
fieldDefinition ,
226
233
args );
227
- // TODO nested where criteria expressions
228
- getFieldArguments (selectedField , query , cb , fetch , fieldEnvironment );
234
+ predicates .addAll (getFieldPredicates (selectedField , query , cb , root , fetch , fieldEnvironment ));
229
235
}
230
236
}
231
237
}
232
238
});
233
-
239
+
234
240
arguments .addAll (field .getArguments ());
235
241
236
- return arguments ;
242
+ arguments .stream ()
243
+ .filter (it -> !isOrderByArgument (it ) && !isOptionalArgument (it ))
244
+ .map (it -> getPredicate (cb , root , from , environment , it ))
245
+ .filter (it -> it != null )
246
+ .forEach (predicates ::add );
247
+
248
+ return predicates ;
237
249
}
238
250
239
251
/**
@@ -331,7 +343,7 @@ protected Predicate getPredicate(CriteriaBuilder cb, Root<?> from, From<?,?> pat
331
343
String fieldName = argument .getName ().split ("\\ ." )[0 ];
332
344
333
345
From <?,?> join = getCompoundJoin (path , argument .getName (), true );
334
- Argument where = new Argument ("where" , argument .getValue ());
346
+ Argument where = new Argument (WHERE , argument .getValue ());
335
347
Map <String , Object > variables = environment .getExecutionContext ().getVariables ();
336
348
337
349
GraphQLFieldDefinition fieldDef = getFieldDef (
@@ -342,7 +354,7 @@ protected Predicate getPredicate(CriteriaBuilder cb, Root<?> from, From<?,?> pat
342
354
343
355
Map <String , Object > arguments = (Map <String , Object >) new ValuesResolver ()
344
356
.getArgumentValues (fieldDef .getArguments (), Collections .singletonList (where ), variables )
345
- .get ("where" );
357
+ .get (WHERE );
346
358
347
359
return getWherePredicate (cb , from , join , wherePredicateEnvironment (environment , fieldDef , arguments ), where );
348
360
}
@@ -417,7 +429,7 @@ protected Predicate getArgumentPredicate(CriteriaBuilder cb, From<?,?> from,
417
429
new Field (it .getName ()));
418
430
boolean isOptional = false ;
419
431
420
- return getArgumentPredicate (cb , reuseFetch (from , it .getName (), isOptional ),
432
+ return getArgumentPredicate (cb , reuseJoin (from , it .getName (), isOptional ),
421
433
wherePredicateEnvironment (environment , fieldDefinition , args ),
422
434
arg );
423
435
}
@@ -502,7 +514,7 @@ protected Predicate getArgumentsPredicate(CriteriaBuilder cb,
502
514
new Field (it .getName ()));
503
515
boolean isOptional = false ;
504
516
505
- return getArgumentPredicate (cb , reuseFetch (path , it .getName (), isOptional ),
517
+ return getArgumentPredicate (cb , reuseJoin (path , it .getName (), isOptional ),
506
518
wherePredicateEnvironment (environment , fieldDefinition , args ),
507
519
arg );
508
520
}
@@ -631,7 +643,7 @@ private Predicate getLogicalPredicate(String fieldName, CriteriaBuilder cb, From
631
643
isOptional = isOptionalAttribute (getAttribute (environment , argument ));
632
644
}
633
645
634
- return getArgumentPredicate (cb , reuseFetch (path , fieldName , isOptional ),
646
+ return getArgumentPredicate (cb , reuseJoin (path , fieldName , isOptional ),
635
647
wherePredicateEnvironment (environment , fieldDefinition , args ),
636
648
arg );
637
649
}
@@ -714,7 +726,7 @@ protected final DataFetchingEnvironment wherePredicateEnvironment(DataFetchingEn
714
726
private From <?,?> getCompoundJoin (From <?,?> rootPath , String fieldName , boolean outer ) {
715
727
String [] compoundField = fieldName .split ("\\ ." );
716
728
717
- Join <?,?> join ;
729
+ From <?,?> join ;
718
730
719
731
if (compoundField .length == 1 ) {
720
732
return rootPath ;
@@ -740,7 +752,7 @@ private From<?,?> getCompoundJoin(From<?,?> rootPath, String fieldName, boolean
740
752
private Path <?> getCompoundJoinedPath (From <?,?> rootPath , String fieldName , boolean outer ) {
741
753
String [] compoundField = fieldName .split ("\\ ." );
742
754
743
- Join <?,?> join ;
755
+ From <?,?> join ;
744
756
745
757
if (compoundField .length == 1 ) {
746
758
return rootPath .get (compoundField [0 ]);
@@ -760,7 +772,7 @@ private Path<?> getCompoundJoinedPath(From<?,?> rootPath, String fieldName, bool
760
772
}
761
773
762
774
// trying to find already existing joins to reuse
763
- private Join <?,?> reuseJoin (From <?, ?> from , String fieldName , boolean outer ) {
775
+ private From <?,?> reuseJoin (From <?, ?> from , String fieldName , boolean outer ) {
764
776
765
777
for (Join <?,?> join : from .getJoins ()) {
766
778
if (join .getAttribute ().getName ().equals (fieldName )) {
0 commit comments