Skip to content

Commit 0df8a98

Browse files
igdianovIntroPro Ventures
authored andcommitted
Support @ElementCollection attribute mappings (#34)
Fixes #33
1 parent 488dd49 commit 0df8a98

File tree

6 files changed

+187
-30
lines changed

6 files changed

+187
-30
lines changed

graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -471,18 +471,8 @@ private GraphQLFieldDefinition getObjectField(Attribute attribute) {
471471
Stream<Attribute> attributes = findBasicAttributes(foreignType.getAttributes());
472472

473473
// TODO fix page count query
474-
// TODO fix argument bindings. Only static parameter bindings are supported due to GraphQL limitation
475474
arguments.add(getWhereArgument(foreignType));
476475

477-
// Disabled Simple attribute filtering
478-
// attributes.forEach(it -> {
479-
// arguments.add(GraphQLArgument.newArgument()
480-
// .name(it.getName())
481-
// .type((GraphQLInputType) getAttributeType(it))
482-
// .build()
483-
// );
484-
// });
485-
486476
} // Get Sub-Objects fields queries via DataFetcher
487477
else if (attribute instanceof PluralAttribute
488478
&& (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_MANY
@@ -513,22 +503,24 @@ private Stream<Attribute<?,?>> findBasicAttributes(Collection<Attribute<?,?>> at
513503

514504
@SuppressWarnings( "rawtypes" )
515505
private GraphQLType getAttributeType(Attribute<?,?> attribute) {
516-
if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC) {
517-
if (attribute.getJavaType().isEnum()) {
518-
return getTypeFromJavaType(attribute.getJavaType());
519-
} else {
520-
return JavaScalars.of(attribute.getJavaType());
521-
}
522-
523-
} else if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_MANY || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_MANY) {
506+
507+
if (isBasic(attribute)) {
508+
return getGraphQLTypeFromJavaType(attribute.getJavaType());
509+
}
510+
else if (isToMany(attribute)) {
524511
EntityType foreignType = (EntityType) ((PluralAttribute) attribute).getElementType();
525512
return new GraphQLList(new GraphQLTypeReference(foreignType.getName()));
526-
} else if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_ONE || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_ONE) {
513+
}
514+
else if (isToOne(attribute)) {
527515
EntityType foreignType = (EntityType) ((SingularAttribute) attribute).getType();
528516
return new GraphQLTypeReference(foreignType.getName());
529-
} else if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION) {
517+
}
518+
else if (isElementCollection(attribute)) {
530519
Type foreignType = ((PluralAttribute) attribute).getElementType();
531-
return new GraphQLList(getTypeFromJavaType(foreignType.getJavaType()));
520+
521+
if(foreignType.getPersistenceType() == Type.PersistenceType.BASIC) {
522+
return new GraphQLList(getGraphQLTypeFromJavaType(foreignType.getJavaType()));
523+
}
532524
}
533525

534526
final String declaringType = attribute.getDeclaringType().getJavaType().getName(); // fully qualified name of the entity class
@@ -538,7 +530,26 @@ private GraphQLType getAttributeType(Attribute<?,?> attribute) {
538530
"Attribute could not be mapped to GraphQL: field '" + declaringMember + "' of entity class '"+ declaringType +"'");
539531
}
540532

541-
private boolean isValidInput(Attribute<?,?> attribute) {
533+
protected final boolean isBasic(Attribute<?,?> attribute) {
534+
return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC;
535+
}
536+
537+
protected final boolean isElementCollection(Attribute<?,?> attribute) {
538+
return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION;
539+
}
540+
541+
protected final boolean isToMany(Attribute<?,?> attribute) {
542+
return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_MANY
543+
|| attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_MANY;
544+
}
545+
546+
protected final boolean isToOne(Attribute<?,?> attribute) {
547+
return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_ONE
548+
|| attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_ONE;
549+
}
550+
551+
552+
protected final boolean isValidInput(Attribute<?,?> attribute) {
542553
return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC ||
543554
attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION;
544555
}
@@ -586,7 +597,7 @@ private boolean isNotIgnored(AnnotatedElement annotatedElement) {
586597
}
587598

588599
@SuppressWarnings( "unchecked" )
589-
private GraphQLType getTypeFromJavaType(Class<?> clazz) {
600+
private GraphQLType getGraphQLTypeFromJavaType(Class<?> clazz) {
590601
if (clazz.isEnum()) {
591602

592603
if (classCache.containsKey(clazz))
@@ -597,15 +608,15 @@ private GraphQLType getTypeFromJavaType(Class<?> clazz) {
597608
for (Enum<?> enumValue : ((Class<Enum<?>>)clazz).getEnumConstants())
598609
enumBuilder.value(enumValue.name(), ordinal++);
599610

600-
GraphQLType answer = enumBuilder.build();
601-
setNoOpCoercing(answer);
611+
GraphQLType enumType = enumBuilder.build();
612+
setNoOpCoercing(enumType);
602613

603-
classCache.putIfAbsent(clazz, answer);
614+
classCache.putIfAbsent(clazz, enumType);
604615

605-
return answer;
616+
return enumType;
606617
}
607618

608-
return null;
619+
return JavaScalars.of(clazz);
609620
}
610621

611622
protected GraphQLInputType getFieldsEnumType(EntityType<?> entityType) {

graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@
3232
import org.springframework.test.context.junit4.SpringRunner;
3333
import org.springframework.util.Assert;
3434

35-
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor;
36-
import com.introproventures.graphql.jpa.query.schema.GraphQLSchemaBuilder;
3735
import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor;
3836
import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder;
3937

@@ -152,5 +150,23 @@ public void queryWithAlias() {
152150
//then:
153151
assertThat(result.toString()).isEqualTo(expected);
154152
}
153+
154+
155+
// https://github.com/introproventures/graphql-jpa-query/issues/33
156+
@Test
157+
public void queryForElementCollection() {
158+
//given
159+
String query = "{ Author(id: 1) { id name, phoneNumbers } }";
160+
//String query = "{ Author(id: 1) { id name } }";
161+
162+
String expected = "{Author={id=1, name=Leo Tolstoy, phoneNumbers=[1-123-1234, 1-123-5678]}}";
163+
164+
//when
165+
Object result = executor.execute(query).getData();
166+
167+
// then
168+
assertThat(result.toString()).isEqualTo(expected);
169+
}
170+
155171

156172
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2017 IntroPro Ventures Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.introproventures.graphql.jpa.query.schema.model.book;
18+
19+
import java.util.Collection;
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
23+
import javax.persistence.CollectionTable;
24+
import javax.persistence.Column;
25+
import javax.persistence.ElementCollection;
26+
import javax.persistence.Entity;
27+
import javax.persistence.Id;
28+
import javax.persistence.JoinColumn;
29+
import javax.persistence.OneToMany;
30+
31+
import lombok.EqualsAndHashCode;
32+
import lombok.Getter;
33+
import lombok.Setter;
34+
import lombok.ToString;
35+
36+
@Entity
37+
@Getter
38+
@Setter
39+
@ToString
40+
@EqualsAndHashCode(exclude={"books","phoneNumbers"}) // Fixes NPE in Hibernate when initializing loaded collections #1
41+
public class Author {
42+
@Id
43+
Long id;
44+
45+
String name;
46+
47+
@OneToMany(mappedBy="author")
48+
Collection<Book> books;
49+
50+
@ElementCollection
51+
@CollectionTable(name = "author_phone_numbers", joinColumns = @JoinColumn(name = "author_id"))
52+
@Column(name = "phone_number")
53+
private Set<String> phoneNumbers = new HashSet<>();
54+
55+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2017 IntroPro Ventures Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.introproventures.graphql.jpa.query.schema.model.book;
18+
19+
import javax.persistence.Entity;
20+
import javax.persistence.EnumType;
21+
import javax.persistence.Enumerated;
22+
import javax.persistence.Id;
23+
import javax.persistence.ManyToOne;
24+
25+
import lombok.Data;
26+
27+
@Data
28+
@Entity
29+
public class Book {
30+
@Id
31+
Long id;
32+
33+
String title;
34+
35+
@ManyToOne
36+
Author author;
37+
38+
@Enumerated(EnumType.STRING)
39+
Genre genre;
40+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2017 IntroPro Ventures Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.introproventures.graphql.jpa.query.schema.model.book;
18+
19+
public enum Genre {
20+
NOVEL, PLAY
21+
}

graphql-jpa-query-schema/src/test/resources/data.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,17 @@ insert into character_appears_in (character_id, appears_in) values
105105
-- Things
106106
insert into thing (id, type) values
107107
('2D1EBC5B7D2741979CF0E84451C5BBB1', 'Thing1');
108+
109+
-- Books
110+
insert into author (id, name) values (1, 'Leo Tolstoy');
111+
insert into book (id, title, author_id, genre) values (2, 'War and Peace', 1, 'NOVEL');
112+
insert into book (id, title, author_id, genre) values (3, 'Anna Karenina', 1, 'NOVEL');
113+
insert into author (id, name) values (4, 'Anton Chekhov');
114+
insert into book (id, title, author_id, genre) values (5, 'The Cherry Orchard', 4, 'PLAY');
115+
insert into book (id, title, author_id, genre) values (6, 'The Seagull', 4, 'PLAY');
116+
insert into book (id, title, author_id, genre) values (7, 'Three Sisters', 4, 'PLAY');
117+
insert into author_phone_numbers(phone_number, author_id) values
118+
('1-123-1234', 1),
119+
('1-123-5678', 1),
120+
('4-123-1234', 4),
121+
('4-123-5678', 4);

0 commit comments

Comments
 (0)