diff --git a/.project b/.project
deleted file mode 100644
index 11c63df2c..000000000
--- a/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- graphql-jpa-query
-
-
-
-
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
-
-
- org.eclipse.m2e.core.maven2Nature
-
-
diff --git a/README.md b/README.md
index 06a219a7b..24d18fcdc 100644
--- a/README.md
+++ b/README.md
@@ -150,7 +150,7 @@ Will return:
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 OR, AND, EQ, NE, GT, GE, LT, LR, IN, NIN, IS_NULL, NOT_NULL, BETWEEN, NOT_BETWEEN.
+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:
@@ -173,7 +173,200 @@ Will return:
}
}
-You can use familiar SQL criteria expressions to specify complex criterias to fetch your data from SQL database. If you omit, where argument, all entities will be returned.
+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
--------------------
diff --git a/graphql-jpa-query-boot-starter/.project b/graphql-jpa-query-boot-starter/.project
deleted file mode 100644
index 703ec2a93..000000000
--- a/graphql-jpa-query-boot-starter/.project
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
- graphql-jpa-query-boot-starter
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.eclipse.wst.common.project.facet.core.builder
-
-
-
-
- org.hibernate.eclipse.console.hibernateBuilder
-
-
-
-
- org.springframework.ide.eclipse.core.springbuilder
-
-
-
-
- org.springframework.ide.eclipse.boot.validation.springbootbuilder
-
-
-
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
-
- org.springframework.ide.eclipse.boot.validation.springbootbuilder
-
-
-
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
-
-
- org.springframework.ide.eclipse.core.springnature
- org.eclipse.m2e.core.maven2Nature
- org.eclipse.wst.common.project.facet.core.nature
- org.eclipse.jdt.core.javanature
- org.hibernate.eclipse.console.hibernateNature
-
-
diff --git a/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/books/Author.java b/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/books/Author.java
index 43f297e02..6bd10b220 100644
--- a/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/books/Author.java
+++ b/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/books/Author.java
@@ -19,6 +19,7 @@
import java.util.Collection;
import javax.persistence.Entity;
+import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
@@ -32,6 +33,6 @@ public class Author {
String name;
- @OneToMany(mappedBy="author")
+ @OneToMany(mappedBy="author", fetch=FetchType.LAZY)
Collection books;
}
diff --git a/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/books/Book.java b/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/books/Book.java
index d5636c9ed..a81493125 100644
--- a/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/books/Book.java
+++ b/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/books/Book.java
@@ -19,6 +19,7 @@
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
+import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@@ -32,7 +33,7 @@ public class Book {
String title;
- @ManyToOne
+ @ManyToOne(fetch=FetchType.LAZY)
Author author;
@Enumerated(EnumType.STRING)
diff --git a/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Character.java b/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Character.java
index 8536a88fa..cc9bc37f3 100644
--- a/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Character.java
+++ b/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Character.java
@@ -22,6 +22,7 @@
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
+import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
@@ -29,6 +30,7 @@
import javax.persistence.OrderBy;
import com.introproventures.graphql.jpa.query.annotation.GraphQLDescription;
+
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@@ -50,14 +52,14 @@ public abstract class Character {
String name;
@GraphQLDescription("Who are the known friends to this character")
- @ManyToMany
- @JoinTable(name="character_friends",
+ @ManyToMany(fetch = FetchType.LAZY)
+ @JoinTable(name="character_friends",
joinColumns=@JoinColumn(name="source_id", referencedColumnName="id"),
inverseJoinColumns=@JoinColumn(name="friend_id", referencedColumnName="id"))
Set friends;
@GraphQLDescription("What Star Wars episodes does this character appear in")
- @ElementCollection(targetClass = Episode.class)
+ @ElementCollection(targetClass = Episode.class, fetch = FetchType.LAZY)
@Enumerated(EnumType.ORDINAL)
@OrderBy
Set appearsIn;
diff --git a/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/CodeList.java b/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/CodeList.java
index 809c0cc9d..fc7574efe 100644
--- a/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/CodeList.java
+++ b/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/CodeList.java
@@ -17,11 +17,13 @@
package com.introproventures.graphql.jpa.query.example.starwars;
import javax.persistence.Entity;
+import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import com.introproventures.graphql.jpa.query.annotation.GraphQLDescription;
+
import lombok.Data;
@Entity
@@ -39,7 +41,7 @@ public class CodeList {
boolean active;
String description;
- @ManyToOne
+ @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
CodeList parent;
diff --git a/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Human.java b/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Human.java
index aa0af3640..b4d7bbc9d 100644
--- a/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Human.java
+++ b/graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Human.java
@@ -35,7 +35,7 @@ public class Human extends Character {
@JoinColumn(name = "favorite_droid_id")
Droid favoriteDroid;
- @ManyToOne
+ @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "gender_code_id")
CodeList gender;
diff --git a/graphql-jpa-query-example-merge/src/main/resources/application.yml b/graphql-jpa-query-example-merge/src/main/resources/application.yml
index be0aa8936..b23f02b12 100644
--- a/graphql-jpa-query-example-merge/src/main/resources/application.yml
+++ b/graphql-jpa-query-example-merge/src/main/resources/application.yml
@@ -2,11 +2,14 @@ spring:
h2:
console.enabled: true
+ jpa:
+ open-in-view: false
+
graphql:
jpa:
query:
- name: GraphQLJpaQueryStarwars
- description: GraphQL Jpa Query Starwars Schema Example
+ name: Query
+ description: Combined GraphQL Jpa Query for Starwars and Books Example
enabled: true
starwars:
diff --git a/graphql-jpa-query-example-merge/src/main/resources/hibernate.properties b/graphql-jpa-query-example-merge/src/main/resources/hibernate.properties
new file mode 100644
index 000000000..34f5b4c38
--- /dev/null
+++ b/graphql-jpa-query-example-merge/src/main/resources/hibernate.properties
@@ -0,0 +1,8 @@
+hibernate.generate_statistics=true
+org.hibernate.stat=DEBUG
+hibernate.show_sql=true
+hibernate.format_sql=true
+
+logging.level.org.hibernate=debug
+#logging.level.org.hibernate.type.descriptor.sql=trace
+#logging.level.org.hibernate.SQL=debug
diff --git a/graphql-jpa-query-example/.project b/graphql-jpa-query-example/.project
deleted file mode 100644
index dc1942712..000000000
--- a/graphql-jpa-query-example/.project
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
- graphql-jpa-query-boot-demo
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.eclipse.wst.common.project.facet.core.builder
-
-
-
-
- org.hibernate.eclipse.console.hibernateBuilder
-
-
-
-
- org.springframework.ide.eclipse.core.springbuilder
-
-
-
-
- org.springframework.ide.eclipse.boot.validation.springbootbuilder
-
-
-
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
-
- org.springframework.ide.eclipse.boot.validation.springbootbuilder
-
-
-
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
-
-
- org.springframework.ide.eclipse.core.springnature
- org.eclipse.m2e.core.maven2Nature
- org.eclipse.wst.common.project.facet.core.nature
- org.eclipse.jdt.core.javanature
- org.hibernate.eclipse.console.hibernateNature
-
-
diff --git a/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Character.java b/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Character.java
index 8536a88fa..8535d8e55 100644
--- a/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Character.java
+++ b/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Character.java
@@ -22,6 +22,7 @@
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
+import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
@@ -29,6 +30,7 @@
import javax.persistence.OrderBy;
import com.introproventures.graphql.jpa.query.annotation.GraphQLDescription;
+
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@@ -50,14 +52,14 @@ public abstract class Character {
String name;
@GraphQLDescription("Who are the known friends to this character")
- @ManyToMany
+ @ManyToMany(fetch=FetchType.LAZY )
@JoinTable(name="character_friends",
joinColumns=@JoinColumn(name="source_id", referencedColumnName="id"),
inverseJoinColumns=@JoinColumn(name="friend_id", referencedColumnName="id"))
Set friends;
@GraphQLDescription("What Star Wars episodes does this character appear in")
- @ElementCollection(targetClass = Episode.class)
+ @ElementCollection(targetClass = Episode.class, fetch=FetchType.LAZY)
@Enumerated(EnumType.ORDINAL)
@OrderBy
Set appearsIn;
diff --git a/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/CodeList.java b/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/CodeList.java
index 809c0cc9d..fc7574efe 100644
--- a/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/CodeList.java
+++ b/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/CodeList.java
@@ -17,11 +17,13 @@
package com.introproventures.graphql.jpa.query.example.starwars;
import javax.persistence.Entity;
+import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import com.introproventures.graphql.jpa.query.annotation.GraphQLDescription;
+
import lombok.Data;
@Entity
@@ -39,7 +41,7 @@ public class CodeList {
boolean active;
String description;
- @ManyToOne
+ @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
CodeList parent;
diff --git a/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Human.java b/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Human.java
index aa0af3640..b4d7bbc9d 100644
--- a/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Human.java
+++ b/graphql-jpa-query-example/src/main/java/com/introproventures/graphql/jpa/query/example/starwars/Human.java
@@ -35,7 +35,7 @@ public class Human extends Character {
@JoinColumn(name = "favorite_droid_id")
Droid favoriteDroid;
- @ManyToOne
+ @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "gender_code_id")
CodeList gender;
diff --git a/graphql-jpa-query-example/src/main/resources/application.yml b/graphql-jpa-query-example/src/main/resources/application.yml
index 2ce70ac9a..50b4c99c7 100644
--- a/graphql-jpa-query-example/src/main/resources/application.yml
+++ b/graphql-jpa-query-example/src/main/resources/application.yml
@@ -2,6 +2,7 @@ spring:
jpa:
hibernate.ddl-auto: create-drop
show-sql: true
+ open-in-view: false
h2:
console.enabled: true
diff --git a/graphql-jpa-query-example/src/main/resources/data.sql b/graphql-jpa-query-example/src/main/resources/data.sql
new file mode 100644
index 000000000..39032979c
--- /dev/null
+++ b/graphql-jpa-query-example/src/main/resources/data.sql
@@ -0,0 +1,104 @@
+-- Insert Code Lists
+insert into code_list (id, type, code, description, sequence, active, parent_id) values
+ (0, 'org.crygier.graphql.model.starwars.Gender', 'Male', 'Male', 1, true, null),
+ (1, 'org.crygier.graphql.model.starwars.Gender', 'Female', 'Female', 2, true, null);
+
+-- Insert Droids
+insert into character (id, name, primary_function, dtype) values
+ ('2000', 'C-3PO', 'Protocol', 'Droid'),
+ ('2001', 'R2-D2', 'Astromech', 'Droid');
+
+-- Insert Humans
+insert into character (id, name, home_planet, favorite_droid_id, dtype, gender_code_id) values
+ ('1000', 'Luke Skywalker', 'Tatooine', '2000', 'Human', 0),
+ ('1001', 'Darth Vader', 'Tatooine', '2001', 'Human', 0),
+ ('1002', 'Han Solo', NULL, NULL, 'Human', 0),
+ ('1003', 'Leia Organa', 'Alderaan', NULL, 'Human', 1),
+ ('1004', 'Wilhuff Tarkin', NULL, NULL, 'Human', 0);
+
+-- Luke's friends
+insert into character_friends (source_id, friend_id) values
+ ('1000', '1002'),
+ ('1000', '1003'),
+ ('1000', '2000'),
+ ('1000', '2001');
+
+-- Luke Appears in
+insert into character_appears_in (character_id, appears_in) values
+ ('1000', 3),
+ ('1000', 4),
+ ('1000', 5),
+ ('1000', 6);
+
+-- Vader's friends
+insert into character_friends (source_id, friend_id) values
+ ('1001', '1004');
+
+-- Vader Appears in
+insert into character_appears_in (character_id, appears_in) values
+ ('1001', 3),
+ ('1001', 4),
+ ('1001', 5);
+
+-- Solo's friends
+insert into character_friends (source_id, friend_id) values
+ ('1002', '1000'),
+ ('1002', '1003'),
+ ('1002', '2001');
+
+-- Solo Appears in
+insert into character_appears_in (character_id, appears_in) values
+ ('1002', 3),
+ ('1002', 4),
+ ('1002', 5),
+ ('1002', 6);
+
+-- Leia's friends
+insert into character_friends (source_id, friend_id) values
+ ('1003', '1000'),
+ ('1003', '1002'),
+ ('1003', '2000'),
+ ('1003', '2001');
+
+-- Leia Appears in
+insert into character_appears_in (character_id, appears_in) values
+ ('1003', 3),
+ ('1003', 4),
+ ('1003', 5),
+ ('1003', 6);
+
+-- Wilhuff's friends
+insert into character_friends (source_id, friend_id) values
+ ('1004', '1001');
+
+-- Wilhuff Appears in
+insert into character_appears_in (character_id, appears_in) values
+ ('1004', 3);
+
+-- C3PO's friends
+insert into character_friends (source_id, friend_id) values
+ ('2000', '1000'),
+ ('2000', '1002'),
+ ('2000', '1003'),
+ ('2000', '2001');
+
+-- C3PO Appears in
+insert into character_appears_in (character_id, appears_in) values
+ ('2000', 3),
+ ('2000', 4),
+ ('2000', 5),
+ ('2000', 6);
+
+-- R2's friends
+insert into character_friends (source_id, friend_id) values
+ ('2001', '1000'),
+ ('2001', '1002'),
+ ('2001', '1003');
+
+-- R2 Appears in
+insert into character_appears_in (character_id, appears_in) values
+ ('2001', 3),
+ ('2001', 4),
+ ('2001', 5),
+ ('2001', 6);
+
diff --git a/graphql-jpa-query-example/src/main/resources/hibernate.properties b/graphql-jpa-query-example/src/main/resources/hibernate.properties
new file mode 100644
index 000000000..34f5b4c38
--- /dev/null
+++ b/graphql-jpa-query-example/src/main/resources/hibernate.properties
@@ -0,0 +1,8 @@
+hibernate.generate_statistics=true
+org.hibernate.stat=DEBUG
+hibernate.show_sql=true
+hibernate.format_sql=true
+
+logging.level.org.hibernate=debug
+#logging.level.org.hibernate.type.descriptor.sql=trace
+#logging.level.org.hibernate.SQL=debug
diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java
index 693f1b611..acaddfaae 100644
--- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java
@@ -66,6 +66,10 @@ public Object get(DataFetchingEnvironment environment) {
//EntityGraph> entityGraph = buildEntityGraph(new Field("select", new SelectionSet(Arrays.asList(field))));
+ // Let's clear session persistent context to avoid getting stale objects cached in the same session
+ // between requests with different search criteria. This looks like a Hibernate bug...
+ entityManager.clear();
+
return getQuery(environment, field, true)
//.setHint("javax.persistence.fetchgraph", entityGraph) // TODO: fix runtime exception
.getResultList();
@@ -114,23 +118,21 @@ protected TypedQuery> getQuery(DataFetchingEnvironment environment, Field fiel
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery