GithubHelp home page GithubHelp logo

introproventures / graphql-jpa-query Goto Github PK

View Code? Open in Web Editor NEW
185.0 13.0 53.0 2.58 MB

Generate GraphQL Query Api for your JPA Entity Models

Home Page: https://github.com/introproventures/graphql-jpa-query

License: Apache License 2.0

Java 100.00%
graphql-query graphql-schema jpa-query graphql-java schema-generation jpa-entity-model

graphql-jpa-query's Introduction

GraphQL Query Api for JPA Entity Models

CI codecov Maven Central

GraphQL JPA Query library uses JPA specification to derive and build GraphQL Apis using GraphQL Java for your JPA Entity Java Classes. It provides a powerfull JPA Query Schema Builder to generate GraphQL Schema using JPA EntityManager Api and instruments GraphQL Schema with JPA Query Data Fetchers that transform GraphQL queries into JPA queries on the fly.

GraphQL is a query language for Web APIs implemented by GraphQL Java graphql-java runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

Your applications can now use GraphQL queries that smoothly follow references between JPA entities with flexible type safe criteria expressions and user-friendly SQL query syntax semantics i.e. query by page, where criteria expressions, select, order by etc.

While typical REST APIs require loading from multiple URLs, GraphQL APIs get all the data your app needs in a single request. Apps using GraphQL can be quick even on slow mobile network connections.

JPA (Java Persistence Annotation) is Java's standard solution to bridge the gap between object-oriented domain models and relational database systems.

GraphQL JPA Query creates a uniform query API for your applications without being limited by a single data source. You can use it with multiple JPA compliant databases by instrumenting a separate EntityManager for each DataSource and expose a single GraphQL Query Apis for your Web application domain using Spring Boot Auto Configuration magic.

Features

  • Code first generation of GraphQL schema from JPA entities
  • Customize GraphQL schema using annotations on JPA entities
  • Execute GraphQL queries with dynamic SQL criteria expressions via JPA Criteria Apis
  • Paginate GraphQL query results
  • Support GraphQL Relay Connection specification
  • Optimized JPA Query performance with single fetch queries
  • Merging two or more GraphQL schemas from different JPA entity models
  • Support for GraphQL schema auto-configuration, GraphQL Web Rest Controller via Spring Boot Starters
  • GraphQL Subscriptions (Experimental)
  • Spring GraphQl 1.x.x Auto configurations for Schema Runtime Wiring, Execution, Protocols, Testing

Supported Apis

  • jpa-api 2.1 for up to 0.5.x versions
  • jpa-api 3.1 for 1.0.x versions
  • graphql-java 20.x for 1.1.x versions
  • graphql-java 19.x for 0.5.x versions
  • graphql-java 13 for 0.4.x versions
  • spring-boot 2.6.x for 0.4.x versions
  • spring-boot 2.7.x for 0.5.x versions
  • spring-boot 3.0.x for 1.0.x versions
  • spring-boot 3.1.x for 1.1.x versions
  • spring-graphql 1.0.x for 0.5.2+ versions
  • spring-graphql 1.1.x for 1.0.0+ versions
  • spring-graphql 1.2.x for 1.1.0+ versions

Tested using JDK Versions

  • Jdk 17
  • Jdk 19

Modules

The GraphQL-JPA-Query library consists of the following modules:

  1. graphql-jpa-query-annotations - Provides annotations for instrumenting your entity models with GraphQL Schema Descriptions
  2. graphql-jpa-query-dependencies - Provides dependency management for project and external modules versions
  3. graphql-jpa-query-autoconfigure - Provides autoconfiguration and merging of multiple GraphQL schemas in Spring Boot context
  4. graphql-jpa-query-web - Provides Graphql Web Controller with Spring Boot auto-configuration support
  5. graphql-jpa-query-schema - Provides interface specifications and implementation of the JPA Schema Builder and JPA Data Fetchers
  6. graphql-jpa-query-boot-starter- Provides Spring Boot starter support to enable GraphQL JPA Query in your project
  7. graphql-jpa-query-examples - Provides example application for Starwars sample entity models

Building with Maven Central Maven Central

You can use Maven Central repository to include and build individual modules in your project.

For GraphQL JPA Annotations use:

<dependency>
  <groupId>com.introproventures</groupId>
  <artifactId>graphql-jpa-query-annotations</artifactId>
  <version>tag</version>
</dependency>

To import GraphQL JPA Dependencies into your dependencyManagement use:

<dependency>
  <groupId>com.introproventures</groupId>
  <artifactId>graphql-jpa-query-dependencies</artifactId>
  <version>tag</version>
      <type>pom</type>
  <scope>import</scope>	
</dependency>

For GraphQL JPA Schema Builder use:

<dependency>
    <groupId>com.introproventures</groupId>
    <artifactId>graphql-jpa-query-schema</artifactId>
    <version>tag</version>
</dependency>

For GraphQL JPA Query Boot Starter use:

<dependency>
  <groupId>com.introproventures</groupId>
  <artifactId>graphql-jpa-query-boot-starter</artifactId>
  <version>tag</version>
</dependency>

Other Dependencies

The core library module graphql-jpa-query-schema keeps dependencies to a minimum. The main dependecies for schema module are graphql-java, evo-inflector, javax.transaction-api, hibernate-jpa-2.1-api. The tests depend on Spring Boot 2.1 with Web and Hibernate for JPA starters as well as Project Lombok.

Schema Generation

The models are introspected using a JPA Entity Manager to auto-generate a GraphQL Schema. After that, you can use GraphQL schema to execute GraphQL query against your data.

Schema Merging to Work with Multiple Databases

You can use graphql-jpa-query with multiple databases by instrumenting EntityManager with its own DataSource and using GraphQLJpaSchemaBuilder to build GraphQLSchema for it. Then, you need to register each GraphQLSchema via GraphQLSchemaConfigurer.register(Registry registry) method implementation. The graphql-jpa-query-autoconfigure module provides Spring Boot Auto Configuration Factory Bean mechanism that merges schemas from many configurers defined in your Spring App context into a single GraphQLSchema bean. See https://github.com/introproventures/graphql-jpa-query/tree/master/graphql-jpa-query-example-merge example for details.

Schema Documentation

GraphQL provides a well documented schema for your domain entity model. The Schema Builder produces descriptions using @GraphQLDescription annotation on Java types and fields. These descriptions will show up in the GraphiQL schema browser to help you provide documented API to end-users. See the GraphiQL section below for more details. You can use @GraphQLIgnore annotation to exclude entity type or field from schema.

Queries

This library will wrap each entity into two query fields for each entity model (say Human or Droid - see tests) will have two representations in the generated schema:

  • One that models the Entity directly using singular form, i.e. Human or Droid to get single instance by id.
  • One that wraps the Entity in a pagable query request with where criteria expression using Entity pluralized form, i.e. Humans or Droids

Singular Query Wrapper

You can use simple query, if you need a single object as root of your query.

For Example:

query {
  Human(id: 1) { name }
}

Will return:

Human: {
  name: "Luke Skywalker"
}

Query Wrapper with Where Criteria Expressions

This library supports flexible type safe criteria expressions with user-friendly SQL query syntax semantics using where arguments and select field to specify the entity graph query with entiy attribute names as a combination of logical expressions like EQ, NE, GT, GE, LT, LR, IN, NIN, IS_NULL, NOT_NULL, BETWEEN, NOT_BETWEEN. You can use logical AND/OR combinations in SQL criteria expressions to specify complex criterias to fetch your data from SQL database. If you omit, where argument, all entities will be returned.

For Example:

query {
    Humans(where: { 
        name: { IN: ["Luke Skywalker", "Darth Vader"] }
    }) {
        select { name }
    }
}

Will return:

{
    Humans: {
        select: [
            { name: 'Luke Skywalker' },
            { name: 'Darth Vader' }
        ]
    }
}

Relation Attributes in Where Criteria Expressions:

It is also possible to specify complex filters using many-to-one and one-to-many entity attributes in where criteria expressions with variable parameter bindings, i.e.

Given the following query with many-to-one relation with variables {"authorId": 1 } :

query($authorId: Long) {  
  Books(where: {
    author: {id: {EQ: $authorId}}
  }) {
    select {
      id
      title
      genre
      author {
        id
        name
      }
    }
  }
}

will result in

{
  "data": {
    "Books": {
      "select": [
        {
          "id": 2,
          "title": "War and Peace",
          "genre": "NOVEL",
          "author": {
            "id": 1,
            "name": "Leo Tolstoy"
          }
        },
        {
          "id": 3,
          "title": "Anna Karenina",
          "genre": "NOVEL",
          "author": {
            "id": 1,
            "name": "Leo Tolstoy"
          }
        }
      ]
    }
  }
}

And given the following query with one-to-many relation:

query {
  Authors(where: {
    books: {genre: {IN: NOVEL}}
  }) {
    select {
      id
      name
      books {
        id
        title
        genre
      }
    }
  }
}

will result in

{
  "data": {
    "Authors": {
      "select": [
        {
          "id": 1,
          "name": "Leo Tolstoy",
          "books": [
            {
              "id": 2,
              "title": "War and Peace",
              "genre": "NOVEL"
            },
            {
              "id": 3,
              "title": "Anna Karenina",
              "genre": "NOVEL"
            }
          ]
        }
      ]
    }
  }
}

It is possible to use compound criterias in where search expressions given:

query {
  Authors(where: {
    books: {
      genre: {IN: NOVEL}
      title: {LIKE: "War"}
    }
  }) {
    select {
      id
      name
      books {
        id
        title
        genre
      }
    }
  }
}

Will return filtered inner collection result:

{
  "data": {
    "Authors": {
      "select": [
        {
          "id": 1,
          "name": "Leo Tolstoy",
          "books": [
            {
              "id": 2,
              "title": "War and Peace",
              "genre": "NOVEL"
            }
          ]
        }
      ]
    }
  }
}

It is also possible to filter inner collections as follows:

query {
  Authors(where: {
    books: {genre: {IN: NOVEL}}
  }) {
    select {
      id
      name
      books(where: {title: {LIKE: "War"}}) {
        id
        title
        genre
      }
    }
  }
}

will result in

{
  "data": {
    "Authors": {
      "select": [
        {
          "id": 1,
          "name": "Leo Tolstoy",
          "books": [
            {
              "id": 2,
              "title": "War and Peace",
              "genre": "NOVEL"
            }
          ]
        }
      ]
    }
  }
}

Collection Filtering

You can specify criteria expressions for one-to-many associations in order to further filter entity by collection attributes:

For Example:

query {
  Humans {
    select { 
      name
      friends(where: {
        appearsIn: {
          IN: [A_NEW_HOPE]
        }
        name: {
          LIKE: "Han"
        }

      }) {
        id
        name
      }
    }
  }
}

Will Return:

"Humans": {
    "select": [
      {
        "name": "Luke Skywalker",
        "friends": [
          {
            "id": "1002",
            "name": "Han Solo"
          }
        ]
      },
      {
        "name": "Darth Vader",
        "friends": []
      },
      {
        "name": "Han Solo",
        "friends": []
      },
      {
        "name": "Leia Organa",
        "friends": [
          {
            "id": "1002",
            "name": "Han Solo"
          }
        ]
      },
      {
        "name": "Wilhuff Tarkin",
        "friends": []
      }
    ]
  }

Reverse Query

You can execute an inverse query to fitler results with a join in many-to-one association in one query with parameter bindings support added in 0.3.1

For Example:

query {
    Humans {
        select {
            name
            favoriteDroid(where: {appearsIn: {IN:[A_NEW_HOPE]}}) {
            	name
          	}
        }
    }
}

Will Return:

{
  "Humans": {
    "select": [
      {
        "name": "Luke Skywalker",
        "favoriteDroid": {
          "name": "C-3PO"
        }
      },
      {
        "name": "Darth Vader",
        "favoriteDroid": {
          "name": "R2-D2"
        }
      }
    ]
  }

Type Safe Arguments

The JPA Schema builder will derive QraphQL scalar types from JPA model attributes. At runtime, it will validate provided values against the schema. Enum Java types are also translated to QraphQL Enum scalar type.

Variable Parameter Bindings

Just like a REST API, it is possible to pass variable arguments to an endpoint in a GraphQL API. By declaring the arguments in the query defintion, typechecking happens automatically. Each variable argument must be named with $ prefix and have a type. To use variable inside query, simply reference it in any criteria expressions. Each variable reference will be resolved to its value during query execution, for example:

{
"query": "query HumanById($id: Long!) {
      Human(id: $id) { name }
}",
   "variables": {"id": 1}
}

Pagination

GraphQL does not specify any language or idioms for performing Pagination. This library provides support for pageable queries with page argument on pluralized query wrapper. Tha page start is 1-based, i.e. provide 1 as value for start parameter to request the first page with the number of records in the limit argument value.

This allows you to query for the "Page" version of any Entity, and return page metadata i.e. pages and total records count with the select data.

For example:

query {
    Humans(page:{start:1, limit: 3}) {
        pages
        total
        select {
            name
        }
    }
}

Will return:

{
  "Humans": {
    "pages": 2,
    "total": 5,
    "select": [
      {
        "name": "Luke Skywalker"
      },
      {
        "name": "Darth Vader"
      },
      {
        "name": "Han Solo"
      }
    ]
  }

The JPA DataFetcher implementation will execute an extra query to get the total elements only if you have requested 'pages' or 'total' fields.

Paging arguments support variable bindings.

Sorting

Sorting is supported on any field. Simply pass in an 'orderBy' argument with the value of ASC or DESC. Here's an example of sorting by name for Human objects. The default sort order can be specified using GraphQLDefaultSort annotation on entity field. If sort order is not specified and there is no field with default sort order provided, we will use field annotated with @Id to avoid paging confusions.

query {
    Human {
        name(orderBy: DESC)
        homePlanet
    }
}

Performance

Lazy loading of associations between entities is a well established best practice in JPA. Its main goal is to retrieve only the requested entities from the database and load the related entities only if needed. The use of FetchType.EAGER for associations mapping is one of the most common reasons for performance problems, because Hibernate loads eagerly fetched associations when it loads an entity. This is very inefficient and it gets even worse when you consider that Hibernate does that whether or not you will use the associated data.

The JPA DataFetcher implementation will attempt to build dynamic fetch graph in order to optimize query performance and avoid N+1 lazy loading. However, if there are composite foreign keys being used on @ManyToOne association declared in GraphQL query, Hibernate persistence provider will issue a separate SQL query to resolve the parent entity.

To disable default @ManyToOne associations behavior with eager fetch, make explicit use of FetchType.LAZY for all associations in your entity model. It will delay the initialization of the relationship unless it is specified in the GraphQL query entity graph to improve performance when fetching many entities with their associations.

GraphiQL Browser

GraphiQL (https://github.com/graphql/graphiql) can be used for simple testing. You can build and launch provided example as a Spring Boot Application, then navigate to http://localhost:8080/ to load GraphiQL browser. The collapsed Docs panel can opened by clicking on the button in the upper right corner to expose current test schema models.

You can run GraphQL queries in the left pannel. Type the query and hit the run button. The results should come up in the middle panel. If your query has variables, there is a minimized panel at the bottom left. Simply click on this to expand, and type in your variables as a JSON string with quoted keys.

License

Apache License v2.0

graphql-jpa-query's People

Contributors

almerico avatar anotender avatar bahurski-ivan avatar chanhengseang3 avatar czyba avatar dependabot[bot] avatar fehnomenal avatar iformas avatar igdianov avatar miklemv avatar molexx avatar negesti avatar proth1 avatar rtmkv avatar sinainin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-jpa-query's Issues

Provide additional page metadata in select response

Currently, only two page metadata fields are supported in select field wrapper:

  • pages: total number of pages
  • total: total number of records

Provide complete page metadata in select response field wrapper as follows:

{
  "page": {
    "size": 20,
    "totalElements": 2,
    "totalPages": 1,
    "number": 0
  }
}

Help: neat way to add other things to schema

JPA is working well but we need to add other (non-JPA generated) functionality to the service too.

A simple way looks to be to use jpa-annotations to annotate a method:

https://github.com/Enigmatis/graphql-java-annotations#fields

jpa-annotations that requires that the GraphQLObjectType it builds be passed to schemaBuilder.query() but that is already called by GraphQLJpaSchemaBuilder and cannot be called twice.

Any suggestions on how we might go about merging these in a clean way please?

Reactive and webflux ?

I am trying to setup a reactive environment but the starter is only for webmvc :

The bean 'requestMappingHandlerAdapter', defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.class] and overriding is disabled.

Work with multiple databases

How can we use graphql-jpa-query with multiple databases?

It looks like GraphqlSchemaBuilder fails when we have more than one EntityManager available.

Having 2 entities embedding common shared entity fails

I have 2 entities embedding a 3rd entity which leads to the following error :

No provided type may have a name which conflicts with any built in types (including Scalar and Introspection types).
You have redefined the type 'AddressEmbeddableType' from being a 'GraphQLObjectType' to a 'GraphQLObjectType'

In spring, EmbeddableTypeImpl doesn't implement hashcode nor equals for which GraphQLJpaSchemaBuilder fails to retrieve the proper type from the input or output cache in getEmbeddableType(EmbeddableType<?> embeddableType, boolean input) and thus a new type is created for the same shared embedded type.

Missing LocalTime

It looks like java.time.LocalTime is missing in the class JavaScalars.

error: Unsupported field type class for enum types in where criteria expressions

given:

        String query = "{ Books(where: {genre: {EQ: NOVEL}}) { select { id title, genre } }}";

when:

        Object result = executor.execute(query).getData();

then:

        String expected = "{Books={select=["
        		+ "{id=2, title=War and Peace, genre=NOVEL}, "
        		+ "{id=3, title=Anna Karenina, genre=NOVEL}"
        		+ "]}}";

        assertThat(result.toString()).isEqualTo(expected);

results in error:

2018-10-18 13:36:11.440  WARN 31844 --- [           main] g.e.SimpleDataFetcherExceptionHandler    : Exception while fetching data (/Books) : Unsupported field type class com.introproventures.graphql.jpa.query.schema.model.book.Genre for field genre

java.lang.IllegalArgumentException: Unsupported field type class com.introproventures.graphql.jpa.query.schema.model.book.Genre for field genre
	at com.introproventures.graphql.jpa.query.schema.impl.JpaPredicateBuilder.getTypedPredicate(JpaPredicateBuilder.java:368) ~[classes/:na]
	at com.introproventures.graphql.jpa.query.schema.impl.JpaPredicateBuilder.getPredicate(JpaPredicateBuilder.java:382) ~[classes/:na]
	at com.introproventures.graphql.jpa.query.schema.impl.QraphQLJpaBaseDataFetcher.lambda$25(QraphQLJpaBaseDataFetcher.java:393) ~[classes/:na]
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_161]
	at java.util.ArrayList.forEach(ArrayList.java:1257) ~[na:1.8.0_161]
	at java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:390) ~[na:1.8.0_161]
	at java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:1.8.0_161]
	at java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:1.8.0_161]
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) ~[na:1.8.0_161]
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_161]
	at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:1.8.0_161]
	at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:1.8.0_161]
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_161]
	at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418) ~[na:1.8.0_161]
	at com.introproventures.graphql.jpa.query.schema.impl.QraphQLJpaBaseDataFetcher.getFieldPredicate(QraphQLJpaBaseDataFetcher.java:395) ~[classes/:na]
	at com.introproventures.graphql.jpa.query.schema.impl.QraphQLJpaBaseDataFetcher.lambda$15(QraphQLJpaBaseDataFetcher.java:349) ~[classes/:na]
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_161]
	at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175) ~[na:1.8.0_161]
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382) ~[na:1.8.0_161]
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_161]
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_161]
	at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:1.8.0_161]
	at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:1.8.0_161]
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_161]
	at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418) ~[na:1.8.0_161]
	at com.introproventures.graphql.jpa.query.schema.impl.QraphQLJpaBaseDataFetcher.getArgumentPredicate(QraphQLJpaBaseDataFetcher.java:353) ~[classes/:na]
	at com.introproventures.graphql.jpa.query.schema.impl.QraphQLJpaBaseDataFetcher.getWherePredicate(QraphQLJpaBaseDataFetcher.java:305) ~[classes/:na]
	at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaQueryDataFetcher.getPredicate(GraphQLJpaQueryDataFetcher.java:135) ~[classes/:na]
	at com.introproventures.graphql.jpa.query.schema.impl.QraphQLJpaBaseDataFetcher.lambda$0(QraphQLJpaBaseDataFetcher.java:122) ~[classes/:na]
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_161]
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382) ~[na:1.8.0_161]
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_161]
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_161]
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[na:1.8.0_161]
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_161]
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) ~[na:1.8.0_161]
	at com.introproventures.graphql.jpa.query.schema.impl.QraphQLJpaBaseDataFetcher.getQuery(QraphQLJpaBaseDataFetcher.java:124) ~[classes/:na]
	at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaQueryDataFetcher.get(GraphQLJpaQueryDataFetcher.java:101) ~[classes/:na]
	at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:219) [graphql-java-6.0.jar:na]
	at graphql.execution.ExecutionStrategy.resolveField(ExecutionStrategy.java:165) [graphql-java-6.0.jar:na]
	at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:55) ~[graphql-java-6.0.jar:na]
	at graphql.execution.Execution.executeOperation(Execution.java:154) ~[graphql-java-6.0.jar:na]
	at graphql.execution.Execution.execute(Execution.java:98) ~[graphql-java-6.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:546) ~[graphql-java-6.0.jar:na]
	at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:488) ~[graphql-java-6.0.jar:na]
	at graphql.GraphQL.executeAsync(GraphQL.java:463) ~[graphql-java-6.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:394) ~[graphql-java-6.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:265) ~[graphql-java-6.0.jar:na]
	at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor.execute(GraphQLJpaExecutor.java:58) ~[classes/:na]
	at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor$$FastClassBySpringCGLIB$$3c8ebb24.invoke(<generated>) ~[classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor$$EnhancerBySpringCGLIB$$556acc4b.execute(<generated>) ~[classes/:na]
	at com.introproventures.graphql.jpa.query.schema.GraphQLExecutorTests.queryForEnumEq(GraphQLExecutorTests.java:289) ~[test-classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_161]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_161]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) ~[junit-4.12.jar:4.12]
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) ~[junit-4.12.jar:4.12]
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) ~[junit-4.12.jar:4.12]
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) ~[junit-4.12.jar:4.12]
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) ~[spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) ~[spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) ~[spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) ~[junit-4.12.jar:4.12]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) ~[spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) ~[spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) ~[junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) ~[junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) ~[junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) ~[junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) ~[junit-4.12.jar:4.12]
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) ~[spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) ~[spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363) ~[junit-4.12.jar:4.12]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) ~[spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89) ~[.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41) ~[.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541) ~[.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763) ~[.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463) ~[.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209) ~[.cp/:na]

Add to the executor's context

Now we have schema merging we can add non-JPA functionality to our projects. Sometimes that functionality needs some things in the context - set in the executor and available from DataFetchingEnvironment. But graphql-jpa-query defines the Executor which runs all calls and I don't see how to modify that context without breaking it.

In my particular case I need the HttpServletRequest used to make the query call. Maybe other use cases might want to add their own things in there?

Support for BETWEEN / NOT BETWEEN predicates

It would be nice to have to specify predicates using BETWEEN and NOT_BETWEEN for Integer, Dates, and String data types. For example:

Given: query { Books ( where: { id: {BETWEEN: [2, 5]}}) { select { id title} } }
Expected: {Humans={select=[ {id=1001}, {id=1002}, {id=1003}]}}

Given: query { Humans ( where: { id: {NOT_BETWEEN: [\"1001\", \"1003\"]}}) { select { id } } }
Expected: {Humans={select=[{id=1000}, {id=1004}]}}

Nested relationship queries in where clauses

In a top level where clause you can only use a relationship of the first entity, after that you can only use simple attributes not relationships.

For example:

query {  
  Books(where: {
    author: {id: {EQ: 1}}
  }) {
    select {
      id
      title
      genre
      author {
        id
        name
      }
    }
  }
}

you can say 'author: {id: ' but you can't say 'author: {books: '.
Would it be possible to allow this please?

There are comments on this in #96.

Page start 0 throws exception

Hi,
Thanks for this library, I have one issue if I send start value 0 I get :
request:

query {
  Applications(page: {start: 0, limit: 3}){
    select {
      longName
    }
    pages 
  }
}
{
  "data": {
    "Applications": null
  },
  "errors": [
    {
      "message": "Exception while fetching data (/Applications) : Negative value (-3) passed to setFirstResult",
      "path": [
        "Applications"
      ],
      "exception": {
        "cause": null,
        "stackTrace": [
          {
            "methodName": "setFirstResult",
            "fileName": "BaseQueryImpl.java",
            "lineNumber": 105,
            "className": "org.hibernate.jpa.spi.BaseQueryImpl",
            "nativeMethod": false
          },
          {
            "methodName": "setFirstResult",
            "fileName": "AbstractQueryImpl.java",
            "lineNumber": 84,
            "className": "org.hibernate.jpa.spi.AbstractQueryImpl",
            "nativeMethod": false
          },
          {
            "methodName": "setFirstResult",
            "fileName": "CriteriaQueryTypeQueryAdapter.java",
            "lineNumber": 71,
            "className": "org.hibernate.jpa.criteria.compile.CriteriaQueryTypeQueryAdapter",
            "nativeMethod": false
          },
          {
            "methodName": "get",
            "fileName": "GraphQLJpaQueryDataFetcher.java",
            "lineNumber": 103,
            "className": "com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaQueryDataFetcher",
            "nativeMethod": false
          },
          {
            "methodName": "fetchField",
            "fileName": "ExecutionStrategy.java",
            "lineNumber": 219,
            "className": "graphql.execution.ExecutionStrategy",
            "nativeMethod": false
          },
        

I can add full response too.
We are calling with start value 1, works fine just not follwing your readme.
Cheers

0.3.14 regression: spring.graphql.jpa.query.path must be specified now

Not a high priority as there's a simple solution... but 0.3.14 refuses to start up because spring.graphql.jpa.query.path is not defined. Which is odd because it is defined in default.properties which is referenced by GraphQLJpaQueryProperties. I have both graphql-jpa-query-autoconfigure and graphql-jpa-query-boot-starter as dependencies.

Simple workaround foe now is to add:
spring.graphql.jpa.query.path: /graphql
to the enclosing application's properties.

Help: mutations

What's the suggested way to add mutations please? I'd like to add support for creating and updating JPA entities.

JSON support for Postgres and MySQL - Set or List not mapped

With the changes in DB design and the introduction of JSON types, I think we have a problem with the current implementation here. At least this is what I've found here...

Currently Set or List types will be mapped to PersistentAttributeType@BASIC which is fine as it is just a field and not mapped by JPA, it is a Singular type from that perspective and it should not be @Embedded as it is resolved to objects by Java implementation, not generic JPA implementation (at least as for now). Therefore it must use a custom converter.

Now the problem I found is that on com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder#getObjectField the referenced type is null for both List and Set. That is because com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder#getAttributeType can't find a JPA type for it other than BASIC and there is no mapping for it out of the box. And I believe this is the main issue here. For all collection types there is GraphQLList built, but for BASIC there is none.

OK, my knowledge of GraphQL is very limited and I am just exploring this technology as it is hyped. A word of an advice would be nice or, better yet, a generic implementation in this project as I believe I won't be the only one with such a problem...

Pagination start/limit from variables

If you try to pass the start and limit as variables you get the following error:

"message": "Exception while fetching data (/ContentDAos) : graphql.language.VariableReference cannot be cast to graphql.language.IntValue",

Example query:

query PagedContent($start: Int, $limit: Int) {
  ContentDAos(page: {start:$start, limit:$limit}) {
    pages
    total
    select {
      title
    }
  }
}

variables:

{
  "start": 1,
  "limit": 2
}

Cannot simultaneously fetch multiple bags

Executing query:

query { Human(id:"1001") { favoriteDroid { name } appearsIn, friends { name friends { name } } } }

produces the following exception with the stack trace:

javax.persistence.PersistenceException: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [com.introproventures.graphql.jpa.query.example.model.Character.appearsIn, com.introproventures.graphql.jpa.query.example.model.Character.friends, com.introproventures.graphql.jpa.query.example.model.Character.friends]
	at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.jpa.internal.QueryImpl.getSingleResult(QueryImpl.java:560) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.jpa.criteria.compile.CriteriaQueryTypeQueryAdapter.getSingleResult(CriteriaQueryTypeQueryAdapter.java:54) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
	at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSimpleDataFetcher.get(GraphQLJpaSimpleDataFetcher.java:46) ~[classes/:na]
	at graphql.execution.ExecutionStrategy.resolveField(ExecutionStrategy.java:99) ~[graphql-java-3.0.0.jar:na]
	at graphql.execution.SimpleExecutionStrategy.execute(SimpleExecutionStrategy.java:19) [graphql-java-3.0.0.jar:na]
	at graphql.execution.Execution.executeOperation(Execution.java:106) [graphql-java-3.0.0.jar:na]
	at graphql.execution.Execution.execute(Execution.java:49) [graphql-java-3.0.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:222) [graphql-java-3.0.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:187) [graphql-java-3.0.0.jar:na]
	at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor.execute(GraphQLJpaExecutor.java:68) [classes/:na]
	at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor$$FastClassBySpringCGLIB$$3c8ebb24.invoke(<generated>) [classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) [spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721) [spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) [spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) [spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) [spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) [spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656) [spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor$$EnhancerBySpringCGLIB$$17062d1.execute(<generated>) [classes/:na]
	at com.introproventures.graphql.jpa.query.web.GraphQLController.postJson(GraphQLController.java:71) [classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_40]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_40]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_40]
	at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_40]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) [spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) [spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116) [spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) [spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) [spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) [spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) [spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) [spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872) [spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) [spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105) [spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) [spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) [spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:474) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:783) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:798) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1434) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_40]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_40]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.11.jar:8.5.11]
	at java.lang.Thread.run(Thread.java:745) [na:1.8.0_40]
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [com.introproventures.graphql.jpa.query.example.model.Character.appearsIn, com.introproventures.graphql.jpa.query.example.model.Character.friends, com.introproventures.graphql.jpa.query.example.model.Character.friends]
	at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:75) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.hql.QueryLoader.<init>(QueryLoader.java:106) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:211) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:142) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:81) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.internal.AbstractQueryImpl.applyEntityGraphQueryHint(AbstractQueryImpl.java:1056) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:604) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.jpa.internal.QueryImpl.getSingleResult(QueryImpl.java:529) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
	... 74 common frames omitted

Query help: filter inside relationships without returning data

Say I want to find all Humans who have a relationship with a Droid which appeared in A New Hope.
The README has this:

query {
    Humans {
        select {
            name
            favoriteDroid(where: {appearsIn: {IN:[A_NEW_HOPE]}}) {
            	name
          	}
        }
    }
}

Which returns:

{
  "Humans": {
    "select": [
      {
        "name": "Luke Skywalker",
        "favoriteDroid": {
          "name": "C-3PO"
        }
      },
      {
        "name": "Darth Vader",
        "favoriteDroid": {
          "name": "R2-D2"
        }
      }
    ]
  }

but, I just want the Humans. I don't want any info on the Droids. Sure, it works, I can just look at the top-level Humans but there's a load of nested JSON I don't want which will get created, sent over the network and parsed. (In our real db this is much bigger!)
In the query above I have to provide at least one Droid field - either empty braces or removing the braces fails graphiql's schema validation.

Am I missing a way to do this?

Thanks.

Various possible enhancements regarding performance

Hi,
And thank you for this very good graphql implementation.

I am thinking about some possible enhancements, all related to performance :

  1. Be able to ignore a field into the "where" argument
    My need is to keep a field for display but to not be able to write a where clause with it.
    Then I could prevent where database clause on fields that does not have database index.

  2. Have a global timeout parameter
    If a request reach the timeout, an error is sent to the client and it should be very nice if we could stop the database request

Feel free to ask me more details if needed.

Enable parameter bindings in reverse (many-to-one) queries

Executing an inverse query to filter results with a join in many-to-one association works only with static parameter binding using where criteria expressions, i.e. the following query will fail:

query Q($episode: Episode!) {
    Humans {
        select {
            name
            favoriteDroid(where: {appearsIn: {IN:[$episode]}}) {
            	name
          	}
        }
    }
}

This has to do with how graphql-java library performs argument binding in DataFetchingEnvironment context when invoking DataFetcher

Bug: 'where' filter on nested many-to-one not working

It seems where clauses on a nested many-to-one relationship are ignored.

I've updated the example app to demonstrate the problem. There aren't enough '-to-one' relationships available in the example model to recreate so I've added a new one - a Droid's PrimaryFunction replacing String primaryFunction:

https://github.com/molexx/graphql-jpa-query/tree/filterNestedManyToOne/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/model

This simpler example works - only the Astromech is returned:

query {
    Droids {
        select {
            name
            primaryFunction(where:{function:{EQ:"Astromech"}}) {
                function
            }
        }
    }
}

But this one doesn't - the Protocol droid is also returned:

query {
    Humans {
        select {
            id
            name
            homePlanet
            favoriteDroid {
                name
                primaryFunction(where:{function:{EQ:"Astromech"}}) {
                      function
                }
            }
        }
    }
}

I've also applied the model changes to graphql-jpa-query-schema's tests and added the above two examples as tests - the second one currently fails as the Protocol droid and its parent Luke Skywalker should not be returned.

https://github.com/molexx/graphql-jpa-query/blob/796b6488da4ece90b5ec705b097c0462740180fd/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/StarwarsQueryExecutorTests.java#L615

@MappedSuperclass and @Embedded jpa annotations should be taken into account

Hi,

My jpa model uses @MappedSuperclass and @Embedded jpa annotations.
It leads to a class cast exception:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.introproventures.graphql.jpa.query.web.GraphQLController': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'graphQLExecutor' defined in class path resource 

Could you fix that issue in a future version ?

NPE in Hibernate when executing nested GraphQL query with collection attributes

For example:
query { Human(id:"1001") { favoriteDroid { name } appearsIn, friends { name friends { name } } } }

Generates the following NPE with the stack trace:

java.lang.NullPointerException: null
	at org.hibernate.engine.internal.StatefulPersistenceContext.getLoadedCollectionOwnerOrNull(StatefulPersistenceContext.java:786) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.event.spi.AbstractCollectionEvent.getLoadedOwnerOrNull(AbstractCollectionEvent.java:58) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.event.spi.InitializeCollectionEvent.<init>(InitializeCollectionEvent.java:22) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1989) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:570) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:252) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:566) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:135) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:430) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at com.introproventures.graphql.jpa.query.example.model.Character.hashCode(Character.java:43) ~[classes/:na]
	at com.introproventures.graphql.jpa.query.example.model.Human.hashCode(Human.java:29) ~[classes/:na]
	at java.util.HashMap.hash(HashMap.java:338) ~[na:1.8.0_40]
	at java.util.HashMap.put(HashMap.java:611) ~[na:1.8.0_40]
	at java.util.HashSet.add(HashSet.java:219) ~[na:1.8.0_40]
	at java.util.AbstractCollection.addAll(AbstractCollection.java:344) ~[na:1.8.0_40]
	at org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:327) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:234) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:221) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:194) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.Loader.endCollectionLoad(Loader.java:1180) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:1144) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.Loader.processResultSet(Loader.java:992) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.Loader.doQuery(Loader.java:930) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:336) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.Loader.doList(Loader.java:2617) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.Loader.doList(Loader.java:2600) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2429) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.Loader.list(Loader.java:2424) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:501) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:371) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:216) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1326) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.internal.QueryImpl.list(QueryImpl.java:87) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:606) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.jpa.internal.QueryImpl.getSingleResult(QueryImpl.java:529) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.jpa.criteria.compile.CriteriaQueryTypeQueryAdapter.getSingleResult(CriteriaQueryTypeQueryAdapter.java:54) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
	at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSimpleDataFetcher.get(GraphQLJpaSimpleDataFetcher.java:46) ~[classes/:na]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.