Closed reda-alaoui closed 2 years ago
@dohorn
Thinking more about it, I doubt Spring Data takes into account the EntityGraph
passed by the annotation.
To my knowledge, Spring Data performs a multiselect to perform the query in the case of a projection.
I think that Spring data just ignores the EntityGraph
that you provide via the annotation in your example:
public interface ProductDto {
String getName();
List<RatingDto> getRatings();
}
public interface RatingDto {
int getRating();
}
public interface BaseProductRepository<T extends AbstractProduct> extends Repository<T, String>, EntityGraphQuerydslPredicateExecutor<T> {
@EntityGraph(attributePaths = {"ratings"})
Optional<ProductDto> findById(String id);
}
Can you check that please?
@dohorn I am closing this for now. If you think I am missing something, tell me.
Hello @reda-alaoui,
I've been exploring your project recently, and I must say I am interested on this feature.
I steped a little bit through the code an found this lines of code, in the method com.cosium.spring.data.jpa.entity.graph.repository.support.EntityGraphQueryHintCandidates#canApplyEntityGraph.
private boolean canApplyEntityGraph(ResolvableType repositoryMethodReturnType) {
Class<?> resolvedReturnType = repositoryMethodReturnType.resolve();
if (resolvedReturnType != null
&& (Void.TYPE.equals(resolvedReturnType)
|| domainClass.isAssignableFrom(resolvedReturnType))) {
return true;
}
for (Class<?> genericType : repositoryMethodReturnType.resolveGenerics()) {
if (domainClass.isAssignableFrom(genericType)) {
return true;
}
}
return false;
}
I am not sure what the purpose is, but here if a DTO Class is the repositoryMethodReturnType
, the method always returns false. If the method would return true on Dto Projections, Spring would parse it correctly in org.springframework.data.repository.query.ResultProcessor#processResult(java.lang.Object, org.springframework.core.convert.converter.Converter<java.lang.Object,java.lang.Object>)
public <T> T processResult(@Nullable Object source, Converter<Object, Object> preparingConverter) {
if (source == null || type.isInstance(source) || !type.isProjecting()) {
return (T) source;
}
Assert.notNull(preparingConverter, "Preparing converter must not be null");
ChainingConverter converter = ChainingConverter.of(type.getReturnedType(), preparingConverter).and(this.converter);
if (source instanceof Window<?> && method.isScrollQuery()) {
return (T) ((Window<?>) source).map(converter::convert);
}
if (source instanceof Slice && (method.isPageQuery() || method.isSliceQuery())) {
return (T) ((Slice<?>) source).map(converter::convert);
}
if (source instanceof Collection<?> collection && method.isCollectionQuery()) {
Collection<Object> target = createCollectionFor(collection);
for (Object columns : collection) {
target.add(type.isInstance(columns) ? columns : converter.convert(columns));
}
return (T) target;
}
if (source instanceof Stream && method.isStreamQuery()) {
return (T) ((Stream<Object>) source).map(t -> type.isInstance(t) ? t : converter.convert(t));
}
if (ReactiveWrapperConverters.supports(source.getClass())) {
return (T) ReactiveWrapperConverters.map(source, it -> processResult(it, preparingConverter));
}
return (T) converter.convert(source);
}
And Spring does incount EntityGraphs annotations to projections, it than buidls a join query.
I look forward to hearing back from you
Discussed in https://github.com/Cosium/spring-data-jpa-entity-graph/discussions/69