22
22
import static com .introproventures .graphql .jpa .query .support .GraphQLSupport .isLogicalArgument ;
23
23
import static com .introproventures .graphql .jpa .query .support .GraphQLSupport .isPageArgument ;
24
24
import static com .introproventures .graphql .jpa .query .support .GraphQLSupport .isWhereArgument ;
25
+ import static com .introproventures .graphql .jpa .query .support .GraphQLSupport .selections ;
25
26
import static graphql .introspection .Introspection .SchemaMetaFieldDef ;
26
27
import static graphql .introspection .Introspection .TypeMetaFieldDef ;
27
28
import static graphql .introspection .Introspection .TypeNameMetaFieldDef ;
60
61
import graphql .schema .GraphQLScalarType ;
61
62
import graphql .schema .GraphQLSchema ;
62
63
import graphql .schema .GraphQLType ;
64
+ import jakarta .persistence .EntityGraph ;
63
65
import jakarta .persistence .EntityManager ;
66
+ import jakarta .persistence .Subgraph ;
64
67
import jakarta .persistence .TypedQuery ;
65
68
import jakarta .persistence .criteria .AbstractQuery ;
66
69
import jakarta .persistence .criteria .CriteriaBuilder ;
@@ -121,6 +124,7 @@ public final class GraphQLJpaQueryFactory {
121
124
private static final String DESC = "DESC" ;
122
125
123
126
private static final Logger logger = LoggerFactory .getLogger (GraphQLJpaQueryFactory .class );
127
+ public static final String JAKARTA_PERSISTENCE_FETCHGRAPH = "jakarta.persistence.fetchgraph" ;
124
128
private static Function <Object , Object > unproxy ;
125
129
126
130
static {
@@ -260,15 +264,26 @@ protected Stream<Object> queryResultStream(DataFetchingEnvironment environment,
260
264
keys .toArray ()
261
265
);
262
266
267
+ // Let's create entity graph from selection
268
+ var entityGraph = createEntityGraph (queryEnvironment );
269
+
263
270
// Let's execute query and get wrap result into stream
264
- return getResultStream (query , fetchSize , isDistinct );
271
+ return getResultStream (query , fetchSize , isDistinct , entityGraph );
265
272
}
266
273
267
- protected <T > Stream <T > getResultStream (TypedQuery <T > query , int fetchSize , boolean isDistinct ) {
274
+ protected <T > Stream <T > getResultStream (
275
+ TypedQuery <T > query ,
276
+ int fetchSize ,
277
+ boolean isDistinct ,
278
+ EntityGraph <?> entityGraph
279
+ ) {
268
280
// Let' try reduce overhead and disable all caching
269
281
query .setHint (ORG_HIBERNATE_READ_ONLY , true );
270
282
query .setHint (ORG_HIBERNATE_FETCH_SIZE , fetchSize );
271
283
query .setHint (ORG_HIBERNATE_CACHEABLE , false );
284
+ if (entityGraph != null ) {
285
+ query .setHint (JAKARTA_PERSISTENCE_FETCHGRAPH , entityGraph );
286
+ }
272
287
273
288
if (logger .isDebugEnabled ()) {
274
289
logger .info ("\n GraphQL JPQL Fetch Query String:\n {}" , getJPQLQueryString (query ));
@@ -441,7 +456,9 @@ protected Map<Object, List<Object>> loadOneToMany(DataFetchingEnvironment enviro
441
456
442
457
TypedQuery <Object []> query = getBatchQuery (environment , field , isDefaultDistinct (), keys );
443
458
444
- List <Object []> resultList = getResultList (query );
459
+ var entityGraph = createEntityGraph (environment );
460
+
461
+ List <Object []> resultList = getResultList (query , entityGraph );
445
462
446
463
if (logger .isTraceEnabled ()) {
447
464
logger .trace (
@@ -477,7 +494,9 @@ protected Map<Object, Object> loadManyToOne(DataFetchingEnvironment environment,
477
494
478
495
TypedQuery <Object []> query = getBatchQuery (environment , field , isDefaultDistinct (), keys );
479
496
480
- List <Object []> resultList = getResultList (query );
497
+ var entityGraph = createEntityGraph (environment );
498
+
499
+ List <Object []> resultList = getResultList (query , entityGraph );
481
500
482
501
Map <Object , Object > resultMap = new LinkedHashMap <>(resultList .size ());
483
502
@@ -486,7 +505,7 @@ protected Map<Object, Object> loadManyToOne(DataFetchingEnvironment environment,
486
505
return resultMap ;
487
506
}
488
507
489
- protected <T > List <T > getResultList (TypedQuery <T > query ) {
508
+ protected <T > List <T > getResultList (TypedQuery <T > query , EntityGraph <?> entityGraph ) {
490
509
if (logger .isDebugEnabled ()) {
491
510
logger .info ("\n GraphQL JPQL Batch Query String:\n {}" , getJPQLQueryString (query ));
492
511
}
@@ -496,6 +515,10 @@ protected <T> List<T> getResultList(TypedQuery<T> query) {
496
515
query .setHint (ORG_HIBERNATE_FETCH_SIZE , defaultFetchSize );
497
516
query .setHint (ORG_HIBERNATE_CACHEABLE , false );
498
517
518
+ if (entityGraph != null ) {
519
+ query .setHint (JAKARTA_PERSISTENCE_FETCHGRAPH , entityGraph );
520
+ }
521
+
499
522
return query .getResultList ();
500
523
}
501
524
@@ -1693,8 +1716,10 @@ private EmbeddableType<?> computeEmbeddableType(GraphQLObjectType objectType) {
1693
1716
* @return resolved GraphQL object type or null if no output type is provided
1694
1717
*/
1695
1718
private GraphQLObjectType getObjectType (DataFetchingEnvironment environment ) {
1696
- GraphQLType outputType = environment .getFieldType ();
1719
+ return getObjectType (environment .getFieldType ());
1720
+ }
1697
1721
1722
+ private GraphQLObjectType getObjectType (GraphQLType outputType ) {
1698
1723
if (outputType instanceof GraphQLList ) outputType = ((GraphQLList ) outputType ).getWrappedType ();
1699
1724
1700
1725
if (outputType instanceof GraphQLObjectType ) return (GraphQLObjectType ) outputType ;
@@ -1976,6 +2001,88 @@ private <T> T detach(T entity) {
1976
2001
return entity ;
1977
2002
}
1978
2003
2004
+ EntityGraph <?> createEntityGraph (DataFetchingEnvironment environment ) {
2005
+ Field root = environment .getMergedField ().getSingleField ();
2006
+ GraphQLObjectType fieldType = getObjectType (environment );
2007
+ EntityType <?> entityType = getEntityType (fieldType );
2008
+
2009
+ EntityGraph <?> entityGraph = entityManager .createEntityGraph (entityType .getJavaType ());
2010
+
2011
+ var entityDescriptor = EntityIntrospector .introspect (entityType );
2012
+
2013
+ selections (root )
2014
+ .forEach (selectedField -> {
2015
+ var propertyDescriptor = entityDescriptor .getPropertyDescriptor (selectedField .getName ());
2016
+
2017
+ propertyDescriptor
2018
+ .flatMap (AttributePropertyDescriptor ::getAttribute )
2019
+ .ifPresent (attribute -> {
2020
+ if (
2021
+ isManagedType (attribute ) && hasSelectionSet (selectedField ) && hasNoArguments (selectedField )
2022
+ ) {
2023
+ var attributeFieldDefinition = fieldType .getFieldDefinition (attribute .getName ());
2024
+ entityGraph .addAttributeNodes (attribute .getName ());
2025
+ addSubgraph (
2026
+ selectedField ,
2027
+ attributeFieldDefinition ,
2028
+ entityGraph .addSubgraph (attribute .getName ())
2029
+ );
2030
+ } else if (isBasic (attribute )) {
2031
+ entityGraph .addAttributeNodes (attribute .getName ());
2032
+ }
2033
+ });
2034
+ });
2035
+
2036
+ return entityGraph ;
2037
+ }
2038
+
2039
+ void addSubgraph (Field field , GraphQLFieldDefinition fieldDefinition , Subgraph <?> subgraph ) {
2040
+ var fieldObjectType = getObjectType (fieldDefinition .getType ());
2041
+ var fieldEntityType = getEntityType (fieldObjectType );
2042
+ var fieldEntityDescriptor = EntityIntrospector .introspect (fieldEntityType );
2043
+
2044
+ selections (field )
2045
+ .forEach (selectedField -> {
2046
+ var propertyDescriptor = fieldEntityDescriptor .getPropertyDescriptor (selectedField .getName ());
2047
+
2048
+ propertyDescriptor
2049
+ .flatMap (AttributePropertyDescriptor ::getAttribute )
2050
+ .ifPresent (attribute -> {
2051
+ var selectedName = selectedField .getName ();
2052
+
2053
+ if (
2054
+ hasSelectionSet (selectedField ) && isManagedType (attribute ) && hasNoArguments (selectedField )
2055
+ ) {
2056
+ var selectedFieldDefinition = fieldObjectType .getFieldDefinition (selectedName );
2057
+ subgraph .addAttributeNodes (selectedName );
2058
+ addSubgraph (selectedField , selectedFieldDefinition , subgraph .addSubgraph (selectedName ));
2059
+ } else if (isBasic (attribute )) {
2060
+ subgraph .addAttributeNodes (selectedName );
2061
+ }
2062
+ });
2063
+ });
2064
+ }
2065
+
2066
+ static boolean isManagedType (Attribute <?, ?> attribute ) {
2067
+ return (
2068
+ attribute .getPersistentAttributeType () != Attribute .PersistentAttributeType .EMBEDDED &&
2069
+ attribute .getPersistentAttributeType () != Attribute .PersistentAttributeType .BASIC &&
2070
+ attribute .getPersistentAttributeType () != Attribute .PersistentAttributeType .ELEMENT_COLLECTION
2071
+ );
2072
+ }
2073
+
2074
+ static boolean isBasic (Attribute <?, ?> attribute ) {
2075
+ return !isManagedType (attribute );
2076
+ }
2077
+
2078
+ static boolean hasNoArguments (Field field ) {
2079
+ return !hasArguments (field );
2080
+ }
2081
+
2082
+ static boolean hasArguments (Field field ) {
2083
+ return field .getArguments () != null && !field .getArguments ().isEmpty ();
2084
+ }
2085
+
1979
2086
/**
1980
2087
* Creates builder to build {@link GraphQLJpaQueryFactory}.
1981
2088
* @return created builder
0 commit comments