Skip to content

Commit

Permalink
Add Querydsl integration
Browse files Browse the repository at this point in the history
Spring Data repositories that support Querydsl are now supported as DataFetchers returning single objects and iterables including projection support.
  • Loading branch information
mp911de committed Jun 18, 2021
1 parent 57fb7c5 commit c49810f
Show file tree
Hide file tree
Showing 13 changed files with 811 additions and 5 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ configure(moduleProjects) {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.3"
mavenBom "io.projectreactor:reactor-bom:2020.0.7"
mavenBom "org.springframework:spring-framework-bom:5.3.7"
mavenBom "org.springframework.data:spring-data-bom:2021.0.1"
mavenBom "org.junit:junit-bom:5.7.2"
}
dependencies {
Expand Down
15 changes: 14 additions & 1 deletion samples/webmvc-http/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,20 @@ dependencies {
testImplementation project(':spring-graphql-test')
testImplementation 'org.springframework:spring-webflux'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

implementation(
"com.querydsl:querydsl-core:4.4.0",
"com.querydsl:querydsl-jpa:4.4.0"
)
annotationProcessor "com.querydsl:querydsl-apt:4.4.0:jpa",
"org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final",
"javax.annotation:javax.annotation-api:1.3.2"
}

compileJava {
options.annotationProcessorPath = configurations.annotationProcessor
}

test {
useJUnitPlatform()
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package io.spring.sample.graphql.repository;

import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.CrudRepository;

public interface ArtifactRepositories extends CrudRepository<ArtifactRepository, String> {
public interface ArtifactRepositories extends CrudRepository<ArtifactRepository, String>, QuerydslPredicateExecutor<ArtifactRepository> {

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.spring.sample.graphql.repository;

import graphql.schema.idl.RuntimeWiring;

import org.springframework.graphql.boot.RuntimeWiringCustomizer;
import org.springframework.graphql.data.QuerydslDataFetcherSupport;
import org.springframework.stereotype.Component;

@Component
Expand All @@ -16,8 +18,10 @@ public ArtifactRepositoryDataWiring(ArtifactRepositories repositories) {
@Override
public void customize(RuntimeWiring.Builder builder) {
builder.type("Query",
typeWiring -> typeWiring.dataFetcher("artifactRepositories", env -> this.repositories.findAll())
.dataFetcher("artifactRepository", env -> this.repositories.findById(env.getArgument("id"))));
typeWiring -> typeWiring.dataFetcher("artifactRepositories", QuerydslDataFetcherSupport
.builder(repositories).many())
.dataFetcher("artifactRepository", QuerydslDataFetcherSupport
.builder(repositories).single()));
}

}
6 changes: 6 additions & 0 deletions spring-graphql/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ dependencies {
compileOnly 'org.springframework:spring-websocket'
compileOnly 'javax.servlet:javax.servlet-api:4.0.1'

compileOnly 'com.querydsl:querydsl-core:4.4.0'
compileOnly 'org.springframework.data:spring-data-commons'

testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.mockito:mockito-core:3.11.1'
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'org.springframework:spring-webflux'
testImplementation 'org.springframework:spring-webmvc'
testImplementation 'org.springframework:spring-websocket'
testImplementation 'org.springframework:spring-test'
testImplementation 'org.springframework.data:spring-data-commons'
testImplementation 'com.querydsl:querydsl-core:4.4.0'
testImplementation 'javax.servlet:javax.servlet-api:4.0.1'
testImplementation 'com.fasterxml.jackson.core:jackson-databind'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.graphql.data;

import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.SimplePropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.mapping.model.ParameterValueProvider;

/**
* {@link Converter} to instantiate DTOs from fully equipped domain objects.
*
* @author Mark Paluch
* @since 1.0.0
*/
class DtoInstantiatingConverter<T> implements Converter<Object, T> {

private final Class<T> targetType;

private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context;

private final EntityInstantiator instantiator;

/**
* Creates a new {@link Converter} to instantiate DTOs.
*
* @param dtoType target type
* @param context mapping context to be used
* @param entityInstantiators the instantiators to use for object creation
*/
public DtoInstantiatingConverter(Class<T> dtoType,
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context,
EntityInstantiators entityInstantiators) {
this.targetType = dtoType;
this.context = context;
this.instantiator = entityInstantiators
.getInstantiatorFor(context.getRequiredPersistentEntity(dtoType));
}

@SuppressWarnings("unchecked")
@Override
public T convert(Object source) {

if (targetType.isInterface()) {
return (T) source;
}

PersistentEntity<?, ?> sourceEntity = context
.getRequiredPersistentEntity(source.getClass());

PersistentPropertyAccessor<?> sourceAccessor = sourceEntity
.getPropertyAccessor(source);
PersistentEntity<?, ?> targetEntity = context
.getRequiredPersistentEntity(targetType);
PreferredConstructor<?, ? extends PersistentProperty<?>> constructor = targetEntity
.getPersistenceConstructor();

@SuppressWarnings({"rawtypes", "unchecked"})
Object dto = instantiator
.createInstance(targetEntity, new ParameterValueProvider() {

@Override
public Object getParameterValue(Parameter parameter) {
return sourceAccessor.getProperty(sourceEntity
.getRequiredPersistentProperty(parameter.getName()));
}
});

PersistentPropertyAccessor<?> dtoAccessor = targetEntity
.getPropertyAccessor(dto);

targetEntity.doWithProperties((SimplePropertyHandler) property -> {

if (constructor.isConstructorParameter(property)) {
return;
}

dtoAccessor.setProperty(property,
sourceAccessor.getProperty(sourceEntity
.getRequiredPersistentProperty(property.getName())));
});

return (T) dto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.graphql.data;

import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;

/**
* Lightweight {@link org.springframework.data.mapping.context.MappingContext} to provide class metadata for entity to DTO mapping.
*
* @author Mark Paluch
* @since 1.0.0
*/
class DtoMappingContext extends AbstractMappingContext<DtoMappingContext.DtoPersistentEntity<?>, DtoMappingContext.DtoPersistentProperty> {

@Override
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {

// No Java std lib type introspection to not interfere with encapsulation. We do not want to get into the business of materializing Java types.
if (type.getType().getName().startsWith("java.") || type.getType().getName()
.startsWith("javax.")) {
return false;
}
return super.shouldCreatePersistentEntityFor(type);
}

@Override
protected <T> DtoPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
return new DtoPersistentEntity<>(typeInformation);
}

@Override
protected DtoPersistentProperty createPersistentProperty(Property property, DtoPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new DtoPersistentProperty(property, owner, simpleTypeHolder);
}

static class DtoPersistentEntity<T> extends BasicPersistentEntity<T, DtoPersistentProperty> {

public DtoPersistentEntity(TypeInformation<T> information) {
super(information);
}
}

static class DtoPersistentProperty extends AnnotationBasedPersistentProperty<DtoPersistentProperty> {

public DtoPersistentProperty(Property property, PersistentEntity<?, DtoPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
super(property, owner, simpleTypeHolder);
}

@Override
protected Association<DtoPersistentProperty> createAssociation() {
return null;
}
}
}
Loading

0 comments on commit c49810f

Please sign in to comment.