Skip to content

Commit 2bd3251

Browse files
committed
Add Querydsl integration
Spring Data repositories that support Querydsl are now supported as DataFetchers returning single objects and iterables including projection support.
1 parent 57fb7c5 commit 2bd3251

File tree

13 files changed

+815
-5
lines changed

13 files changed

+815
-5
lines changed

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ configure(moduleProjects) {
3030
sourceCompatibility = JavaVersion.VERSION_1_8
3131
targetCompatibility = JavaVersion.VERSION_1_8
3232
}
33-
33+
3434
dependencyManagement {
3535
imports {
3636
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.3"
3737
mavenBom "io.projectreactor:reactor-bom:2020.0.7"
3838
mavenBom "org.springframework:spring-framework-bom:5.3.7"
39+
mavenBom "org.springframework.data:spring-data-bom:2021.0.1"
3940
mavenBom "org.junit:junit-bom:5.7.2"
4041
}
4142
dependencies {

samples/webmvc-http/build.gradle

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,20 @@ dependencies {
1818
testImplementation project(':spring-graphql-test')
1919
testImplementation 'org.springframework:spring-webflux'
2020
testImplementation 'org.springframework.boot:spring-boot-starter-test'
21+
22+
implementation(
23+
"com.querydsl:querydsl-core:4.4.0",
24+
"com.querydsl:querydsl-jpa:4.4.0"
25+
)
26+
annotationProcessor "com.querydsl:querydsl-apt:4.4.0:jpa",
27+
"org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final",
28+
"javax.annotation:javax.annotation-api:1.3.2"
29+
}
30+
31+
compileJava {
32+
options.annotationProcessorPath = configurations.annotationProcessor
2133
}
34+
2235
test {
2336
useJUnitPlatform()
24-
}
37+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package io.spring.sample.graphql.repository;
22

3+
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
34
import org.springframework.data.repository.CrudRepository;
45

5-
public interface ArtifactRepositories extends CrudRepository<ArtifactRepository, String> {
6+
public interface ArtifactRepositories extends CrudRepository<ArtifactRepository, String>, QuerydslPredicateExecutor<ArtifactRepository> {
67

78
}

samples/webmvc-http/src/main/java/io/spring/sample/graphql/repository/ArtifactRepositoryDataWiring.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.spring.sample.graphql.repository;
22

33
import graphql.schema.idl.RuntimeWiring;
4+
45
import org.springframework.graphql.boot.RuntimeWiringCustomizer;
6+
import org.springframework.graphql.data.QuerydslDataFetcherSupport;
57
import org.springframework.stereotype.Component;
68

79
@Component
@@ -16,8 +18,10 @@ public ArtifactRepositoryDataWiring(ArtifactRepositories repositories) {
1618
@Override
1719
public void customize(RuntimeWiring.Builder builder) {
1820
builder.type("Query",
19-
typeWiring -> typeWiring.dataFetcher("artifactRepositories", env -> this.repositories.findAll())
20-
.dataFetcher("artifactRepository", env -> this.repositories.findById(env.getArgument("id"))));
21+
typeWiring -> typeWiring.dataFetcher("artifactRepositories", QuerydslDataFetcherSupport
22+
.builder(repositories).many())
23+
.dataFetcher("artifactRepository", QuerydslDataFetcherSupport
24+
.builder(repositories).single()));
2125
}
2226

2327
}

spring-graphql/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,19 @@ dependencies {
1111
compileOnly 'org.springframework:spring-websocket'
1212
compileOnly 'javax.servlet:javax.servlet-api:4.0.1'
1313

14+
compileOnly 'com.querydsl:querydsl-core:4.4.0'
15+
compileOnly 'org.springframework.data:spring-data-commons'
16+
1417
testImplementation 'org.junit.jupiter:junit-jupiter'
1518
testImplementation 'org.assertj:assertj-core'
19+
testImplementation 'org.mockito:mockito-core:3.11.1'
1620
testImplementation 'io.projectreactor:reactor-test'
1721
testImplementation 'org.springframework:spring-webflux'
1822
testImplementation 'org.springframework:spring-webmvc'
1923
testImplementation 'org.springframework:spring-websocket'
2024
testImplementation 'org.springframework:spring-test'
25+
testImplementation 'org.springframework.data:spring-data-commons'
26+
testImplementation 'com.querydsl:querydsl-core:4.4.0'
2127
testImplementation 'javax.servlet:javax.servlet-api:4.0.1'
2228
testImplementation 'com.fasterxml.jackson.core:jackson-databind'
2329

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
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+
* https://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 org.springframework.graphql.data;
18+
19+
import org.springframework.core.convert.converter.Converter;
20+
import org.springframework.data.mapping.PersistentEntity;
21+
import org.springframework.data.mapping.PersistentProperty;
22+
import org.springframework.data.mapping.PersistentPropertyAccessor;
23+
import org.springframework.data.mapping.PreferredConstructor;
24+
import org.springframework.data.mapping.PreferredConstructor.Parameter;
25+
import org.springframework.data.mapping.SimplePropertyHandler;
26+
import org.springframework.data.mapping.context.MappingContext;
27+
import org.springframework.data.mapping.model.EntityInstantiator;
28+
import org.springframework.data.mapping.model.EntityInstantiators;
29+
import org.springframework.data.mapping.model.ParameterValueProvider;
30+
31+
/**
32+
* {@link Converter} to instantiate DTOs from fully equipped domain objects.
33+
*
34+
* @author Mark Paluch
35+
* @since 1.0.0
36+
*/
37+
class DtoInstantiatingConverter<T> implements Converter<Object, T> {
38+
39+
private final Class<T> targetType;
40+
41+
private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context;
42+
43+
private final EntityInstantiator instantiator;
44+
45+
/**
46+
* Create a new {@link Converter} to instantiate DTOs.
47+
* @param dtoType target type
48+
* @param context mapping context to be used
49+
* @param entityInstantiators the instantiators to use for object creation
50+
*/
51+
public DtoInstantiatingConverter(Class<T> dtoType,
52+
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context,
53+
EntityInstantiators entityInstantiators) {
54+
this.targetType = dtoType;
55+
this.context = context;
56+
this.instantiator = entityInstantiators
57+
.getInstantiatorFor(context.getRequiredPersistentEntity(dtoType));
58+
}
59+
60+
@SuppressWarnings("unchecked")
61+
@Override
62+
public T convert(Object source) {
63+
64+
if (targetType.isInterface()) {
65+
return (T) source;
66+
}
67+
68+
PersistentEntity<?, ?> sourceEntity = context
69+
.getRequiredPersistentEntity(source.getClass());
70+
71+
PersistentPropertyAccessor<?> sourceAccessor = sourceEntity
72+
.getPropertyAccessor(source);
73+
PersistentEntity<?, ?> targetEntity = context
74+
.getRequiredPersistentEntity(targetType);
75+
PreferredConstructor<?, ? extends PersistentProperty<?>> constructor = targetEntity
76+
.getPersistenceConstructor();
77+
78+
@SuppressWarnings({"rawtypes", "unchecked"})
79+
Object dto = instantiator
80+
.createInstance(targetEntity, new ParameterValueProvider() {
81+
82+
@Override
83+
public Object getParameterValue(Parameter parameter) {
84+
return sourceAccessor.getProperty(sourceEntity
85+
.getRequiredPersistentProperty(parameter.getName()));
86+
}
87+
});
88+
89+
PersistentPropertyAccessor<?> dtoAccessor = targetEntity
90+
.getPropertyAccessor(dto);
91+
92+
targetEntity.doWithProperties((SimplePropertyHandler) property -> {
93+
94+
if (constructor.isConstructorParameter(property)) {
95+
return;
96+
}
97+
98+
dtoAccessor.setProperty(property,
99+
sourceAccessor.getProperty(sourceEntity
100+
.getRequiredPersistentProperty(property.getName())));
101+
});
102+
103+
return (T) dto;
104+
}
105+
106+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
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+
* https://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 org.springframework.graphql.data;
18+
19+
import org.springframework.data.mapping.Association;
20+
import org.springframework.data.mapping.PersistentEntity;
21+
import org.springframework.data.mapping.context.AbstractMappingContext;
22+
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
23+
import org.springframework.data.mapping.model.BasicPersistentEntity;
24+
import org.springframework.data.mapping.model.Property;
25+
import org.springframework.data.mapping.model.SimpleTypeHolder;
26+
import org.springframework.data.util.TypeInformation;
27+
28+
/**
29+
* Lightweight {@link org.springframework.data.mapping.context.MappingContext} to provide class metadata
30+
* for entity to DTO mapping.
31+
*
32+
* @author Mark Paluch
33+
* @since 1.0.0
34+
*/
35+
class DtoMappingContext extends AbstractMappingContext<DtoMappingContext.DtoPersistentEntity<?>,
36+
DtoMappingContext.DtoPersistentProperty> {
37+
38+
@Override
39+
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
40+
// No Java std lib type introspection to not interfere with encapsulation. We do not want to get into
41+
// the business of materializing Java types.
42+
if (type.getType().getName().startsWith("java.") || type.getType().getName()
43+
.startsWith("javax.")) {
44+
return false;
45+
}
46+
return super.shouldCreatePersistentEntityFor(type);
47+
}
48+
49+
@Override
50+
protected <T> DtoPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
51+
return new DtoPersistentEntity<>(typeInformation);
52+
}
53+
54+
@Override
55+
protected DtoPersistentProperty createPersistentProperty(Property property, DtoPersistentEntity<?> owner,
56+
SimpleTypeHolder simpleTypeHolder) {
57+
return new DtoPersistentProperty(property, owner, simpleTypeHolder);
58+
}
59+
60+
static class DtoPersistentEntity<T> extends BasicPersistentEntity<T, DtoPersistentProperty> {
61+
62+
public DtoPersistentEntity(TypeInformation<T> information) {
63+
super(information);
64+
}
65+
66+
}
67+
68+
static class DtoPersistentProperty extends AnnotationBasedPersistentProperty<DtoPersistentProperty> {
69+
70+
public DtoPersistentProperty(Property property, PersistentEntity<?, DtoPersistentProperty> owner,
71+
SimpleTypeHolder simpleTypeHolder) {
72+
super(property, owner, simpleTypeHolder);
73+
}
74+
75+
@Override
76+
protected Association<DtoPersistentProperty> createAssociation() {
77+
return null;
78+
}
79+
80+
}
81+
82+
}

0 commit comments

Comments
 (0)