Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public class GraphQLJpaQueryProperties {
*/
private boolean isDefaultDistinct = true;

/**
* Set default value for optional argument for join fetch collections.
*/
private boolean toManyDefaultOptional = true;

/**
* Enable or disable QraphQL module services.
*/
Expand Down Expand Up @@ -143,5 +148,15 @@ public String getPath() {
public void setPath(String path) {
this.path = path;
}


public boolean isToManyDefaultOptional() {
return toManyDefaultOptional;
}


public void setToManyDefaultOptional(boolean toManyDefaultOptional) {
this.toManyDefaultOptional = toManyDefaultOptional;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ public void contextLoads() {


@Test
public void configurationProperties() {
public void defaultConfigurationProperties() {
// given
assertThat(graphQLJpaQueryProperties.isDefaultDistinct()).isTrue();
assertThat(graphQLJpaQueryProperties.isUseDistinctParameter()).isFalse();
assertThat(graphQLJpaQueryProperties.isToManyDefaultOptional()).isTrue();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ insert into author (id, name) values (4, 'Anton Chekhov');
insert into book (id, title, author_id, genre) values (5, 'The Cherry Orchard', 4, 'PLAY');
insert into book (id, title, author_id, genre) values (6, 'The Seagull', 4, 'PLAY');
insert into book (id, title, author_id, genre) values (7, 'Three Sisters', 4, 'PLAY');
insert into author (id, name, genre) values (8, 'Igor Dianov', 'JAVA');
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
package com.introproventures.graphql.jpa.query.schema.model.book;

public enum Genre {
NOVEL, PLAY
NOVEL, PLAY, JAVA
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,15 @@
*/
class GraphQLJpaOneToManyDataFetcher extends GraphQLJpaQueryDataFetcher {

protected static final String OPTIONAL = "optional";
private final PluralAttribute<Object,Object,Object> attribute;

public GraphQLJpaOneToManyDataFetcher(EntityManager entityManager, EntityType<?> entityType, PluralAttribute<Object,Object,Object> attribute) {
super(entityManager, entityType);
public GraphQLJpaOneToManyDataFetcher(EntityManager entityManager,
EntityType<?> entityType,
boolean toManyDefaultOptional,
boolean defaultDistinct,
PluralAttribute<Object,Object,Object> attribute) {
super(entityManager, entityType, defaultDistinct, toManyDefaultOptional);

this.attribute = attribute;
}
Expand All @@ -63,12 +68,23 @@ public Object get(DataFetchingEnvironment environment) {

// Resolve collection query if where argument is present or any field in selection has orderBy argument
if(whereArg.isPresent() || hasSelectionAnyOrderBy(field)) {
TypedQuery<?> query = getQuery(environment, field, isDefaultDistinct());

//EntityGraph<?> entityGraph = buildEntityGraph(new Field("select", new SelectionSet(Arrays.asList(field))));
// Let's not pass distinct if enabled to have better performance
if(isDefaultDistinct()) {
query.setHint(HIBERNATE_QUERY_PASS_DISTINCT_THROUGH, false);
}

List<?> resultList = query.getResultList();

// Let's remove any duplicate references for root entities
if(isDefaultDistinct()) {
resultList = resultList.stream()
.distinct()
.collect(Collectors.toList());
}

return getQuery(environment, field, true)
//.setHint("javax.persistence.fetchgraph", entityGraph) // TODO: fix runtime exception
.getResultList();
return resultList;
}

// Let hibernate resolve collection query
Expand Down Expand Up @@ -126,6 +142,7 @@ protected TypedQuery<?> getQuery(DataFetchingEnvironment environment, Field fiel
query.select(join.alias(attribute.getName()));

List<Predicate> predicates = getFieldArguments(field, query, cb, join, environment).stream()
.filter(it -> !OPTIONAL.equals(it.getName()))
.map(it -> getPredicate(cb, from, join, environment, it))
.filter(it -> it != null)
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,21 @@ class GraphQLJpaQueryDataFetcher extends QraphQLJpaBaseDataFetcher {

private boolean defaultDistinct = true;

private static final String HIBERNATE_QUERY_PASS_DISTINCT_THROUGH = "hibernate.query.passDistinctThrough";
private static final String ORG_HIBERNATE_CACHEABLE = "org.hibernate.cacheable";
private static final String ORG_HIBERNATE_FETCH_SIZE = "org.hibernate.fetchSize";
private static final String ORG_HIBERNATE_READ_ONLY = "org.hibernate.readOnly";
private static final String JAVAX_PERSISTENCE_FETCHGRAPH = "javax.persistence.fetchgraph";

public GraphQLJpaQueryDataFetcher(EntityManager entityManager, EntityType<?> entityType) {
super(entityManager, entityType);
protected static final String HIBERNATE_QUERY_PASS_DISTINCT_THROUGH = "hibernate.query.passDistinctThrough";
protected static final String ORG_HIBERNATE_CACHEABLE = "org.hibernate.cacheable";
protected static final String ORG_HIBERNATE_FETCH_SIZE = "org.hibernate.fetchSize";
protected static final String ORG_HIBERNATE_READ_ONLY = "org.hibernate.readOnly";
protected static final String JAVAX_PERSISTENCE_FETCHGRAPH = "javax.persistence.fetchgraph";

private GraphQLJpaQueryDataFetcher(EntityManager entityManager, EntityType<?> entityType, boolean toManyDefaultOptional) {
super(entityManager, entityType, toManyDefaultOptional);
}

public GraphQLJpaQueryDataFetcher(EntityManager entityManager, EntityType<?> entityType, boolean defaultDistinct) {
super(entityManager, entityType);
public GraphQLJpaQueryDataFetcher(EntityManager entityManager,
EntityType<?> entityType,
boolean defaultDistinct,
boolean toManyDefaultOptional) {
super(entityManager, entityType, toManyDefaultOptional);
this.defaultDistinct = defaultDistinct;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ public class GraphQLJpaSchemaBuilder implements GraphQLSchemaBuilder {

private boolean isUseDistinctParameter = false;
private boolean isDefaultDistinct = true;
// the many end is a collection, and it is always optional by default (empty collection)
private boolean toManyDefaultOptional = true;

public GraphQLJpaSchemaBuilder(EntityManager entityManager) {
this.entityManager = entityManager;
Expand Down Expand Up @@ -162,7 +164,7 @@ private GraphQLFieldDefinition getQueryFieldByIdDefinition(EntityType<?> entityT
.name(entityType.getName())
.description(getSchemaDescription( entityType.getJavaType()))
.type(getObjectType(entityType))
.dataFetcher(new GraphQLJpaSimpleDataFetcher(entityManager, entityType))
.dataFetcher(new GraphQLJpaSimpleDataFetcher(entityManager, entityType, toManyDefaultOptional))
.argument(entityType.getAttributes().stream()
.filter(this::isValidInput)
.filter(this::isNotIgnored)
Expand Down Expand Up @@ -205,7 +207,10 @@ private GraphQLFieldDefinition getQueryFieldSelectDefinition(EntityType<?> entit
+ "Use the '"+QUERY_SELECT_PARAM_NAME+"' field to request actual fields. "
+ "Use the '"+ORDER_BY_PARAM_NAME+"' on a field to specify sort order for each field. ")
.type(pageType)
.dataFetcher(new GraphQLJpaQueryDataFetcher(entityManager, entityType, isDefaultDistinct))
.dataFetcher(new GraphQLJpaQueryDataFetcher(entityManager,
entityType,
isDefaultDistinct,
toManyDefaultOptional))
.argument(paginationArgument)
.argument(getWhereArgument(entityType));
if (isUseDistinctParameter) {
Expand Down Expand Up @@ -637,11 +642,13 @@ && isNotIgnoredOrder(attribute) ) {
if (attribute instanceof SingularAttribute
&& attribute.getPersistentAttributeType() != Attribute.PersistentAttributeType.BASIC) {
ManagedType foreignType = getForeignType(attribute);
SingularAttribute<?,?> singularAttribute = SingularAttribute.class.cast(attribute);

// TODO fix page count query
arguments.add(getWhereArgument(foreignType));

arguments.add(optionalArgument(SingularAttribute.class.cast(attribute)));
// to-one end could be optional
arguments.add(optionalArgument(singularAttribute.isOptional()));

} // Get Sub-Objects fields queries via DataFetcher
else if (attribute instanceof PluralAttribute
Expand All @@ -651,7 +658,15 @@ else if (attribute instanceof PluralAttribute
EntityType elementType = (EntityType) ((PluralAttribute) attribute).getElementType();

arguments.add(getWhereArgument(elementType));
dataFetcher = new GraphQLJpaOneToManyDataFetcher(entityManager, baseEntity, (PluralAttribute) attribute);

// make it configurable via builder api
arguments.add(optionalArgument(toManyDefaultOptional));

dataFetcher = new GraphQLJpaOneToManyDataFetcher(entityManager,
baseEntity,
toManyDefaultOptional,
isDefaultDistinct,
(PluralAttribute) attribute);
}

return GraphQLFieldDefinition.newFieldDefinition()
Expand All @@ -663,15 +678,15 @@ else if (attribute instanceof PluralAttribute
.build();
}

private GraphQLArgument optionalArgument(SingularAttribute<?,?> attribute) {
private GraphQLArgument optionalArgument(Boolean defaultValue) {
return GraphQLArgument.newArgument()
.name("optional")
.description("Optional association specification")
.type(Scalars.GraphQLBoolean)
.defaultValue(attribute.isOptional())
.defaultValue(defaultValue)
.build();
}

}
protected ManagedType<?> getForeignType(Attribute<?,?> attribute) {
if(SingularAttribute.class.isInstance(attribute))
return (ManagedType<?>) ((SingularAttribute<?,?>) attribute).getType();
Expand Down Expand Up @@ -1102,5 +1117,15 @@ public GraphQLSchemaBuilder namingStrategy(NamingStrategy instance) {

return this;
}


public boolean isToManyDefaultOptional() {
return toManyDefaultOptional;
}


public void setToManyDefaultOptional(boolean toManyDefaultOptional) {
this.toManyDefaultOptional = toManyDefaultOptional;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

package com.introproventures.graphql.jpa.query.schema.impl;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
Expand All @@ -26,14 +30,12 @@
import graphql.language.ObjectValue;
import graphql.schema.DataFetchingEnvironment;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class GraphQLJpaSimpleDataFetcher extends QraphQLJpaBaseDataFetcher {

public GraphQLJpaSimpleDataFetcher(EntityManager entityManager, EntityType<?> entityType) {
super(entityManager, entityType);
public GraphQLJpaSimpleDataFetcher(EntityManager entityManager,
EntityType<?> entityType,
boolean toManyDefaultOptional) {
super(entityManager, entityType, toManyDefaultOptional);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,21 @@ class QraphQLJpaBaseDataFetcher implements DataFetcher<Object> {

protected final EntityManager entityManager;
protected final EntityType<?> entityType;

private boolean toManyDefaultOptional = true;

/**
* Creates JPA entity DataFetcher instance
*
* @param entityManager
* @param entityType
*/
public QraphQLJpaBaseDataFetcher(EntityManager entityManager, EntityType<?> entityType) {
public QraphQLJpaBaseDataFetcher(EntityManager entityManager,
EntityType<?> entityType,
boolean toManyDefaultOptional) {
this.entityManager = entityManager;
this.entityType = entityType;
this.toManyDefaultOptional = toManyDefaultOptional;
}

@Override
Expand Down Expand Up @@ -155,6 +160,7 @@ protected final List<Argument> getFieldArguments(Field field, CriteriaQuery<?> q

Path<?> fieldPath = from.get(selectedField.getName());
Join<?,?> join = null;
Optional<Argument> optionalArgument = getArgument(selectedField, OPTIONAL);

// Build predicate arguments for singular attributes only
if(fieldPath.getModel() instanceof SingularAttribute) {
Expand Down Expand Up @@ -183,31 +189,28 @@ protected final List<Argument> getFieldArguments(Field field, CriteriaQuery<?> q
if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_ONE
|| attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_ONE
) {
// Let's apply left outer join to retrieve optional associations
Optional<Argument> optionalArgument = getArgument(selectedField, OPTIONAL);

// Let's do fugly conversion
Boolean isOptional = optionalArgument.map(it -> getArgumentValue(environment, it, Boolean.class))
.orElse(attribute.isOptional());

// Let's apply left outer join to retrieve optional associations
join = reuseJoin(from, selectedField.getName(), isOptional);
}
}
} else {
// We must add plural attributes with explicit join
GraphQLObjectType objectType = getObjectType(environment);
EntityType<?> entityType = getEntityType(objectType);

PluralAttribute<?, ?, ?> attribute = (PluralAttribute<?, ?, ?>) entityType.getAttribute(selectedField.getName());

// Let's apply left outer join to retrieve optional many-to-many associations
boolean isOptional = (PersistentAttributeType.MANY_TO_MANY == attribute.getPersistentAttributeType());
// Let's do fugly conversion
// the many end is a collection, and it is always optional by default (empty collection)
Boolean isOptional = optionalArgument.map(it -> getArgumentValue(environment, it, Boolean.class))
.orElse(toManyDefaultOptional);

// Let's apply join to retrieve associated collection
join = reuseJoin(from, selectedField.getName(), isOptional);

// TODO add fetch argument parameter
// Let's fetch element collections to avoid filtering their values used where search criteria
from.fetch(selectedField.getName(), isOptional ? JoinType.LEFT : JoinType.INNER);

from.fetch(selectedField.getName(),
isOptional ? JoinType.LEFT : JoinType.INNER);
}

// Let's build join fetch graph to avoid Hibernate error:
Expand All @@ -218,9 +221,10 @@ protected final List<Argument> getFieldArguments(Field field, CriteriaQuery<?> q
selectedField);
Map<String, Object> args = environment.getArguments();

DataFetchingEnvironment fieldEnvironment = wherePredicateEnvironment(environment, fieldDefinition, args);


DataFetchingEnvironment fieldEnvironment = wherePredicateEnvironment(environment,
fieldDefinition,
args);
// TODO nested where criteria expressions
getFieldArguments(selectedField, query, cb, join, fieldEnvironment);
}
}
Expand Down
Loading