Open
Description
Attempting to use a Spring Data repository to query the structure of a simple type (i.e., a type having a write converter) embedded in another type fails to a MappingException
. This problem is present in the latest Spring Data MongoDB version 3.3.2 and seems to have been introduced in this Spring Data Commons commit originally included in Spring Data Commons 2.5.0-M3 (see the issue #2294 and the PR #2293). A similar problem seems to have been reported in #3659, but the associated PR #3661 appears to have fixed the issue only for executions originating from QueryMapper
.
Example MappingException
stack trace.
org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for property private org.springframework.data.mongodb.core.ReproTest$SomeSimpleType org.springframework.data.mongodb.core.ReproTest$Container.simpleTypedValue!
at org.springframework.data.mapping.context.MappingContext.getRequiredPersistentEntity(MappingContext.java:152)
at org.springframework.data.mapping.context.PersistentPropertyPathFactory.getPair(PersistentPropertyPathFactory.java:225)
at org.springframework.data.mapping.context.PersistentPropertyPathFactory.createPersistentPropertyPath(PersistentPropertyPathFactory.java:199)
at org.springframework.data.mapping.context.PersistentPropertyPathFactory.lambda$getPersistentPropertyPath$1(PersistentPropertyPathFactory.java:172)
at java.base/java.util.concurrent.ConcurrentMap.computeIfAbsent(ConcurrentMap.java:330)
at org.springframework.data.mapping.context.PersistentPropertyPathFactory.getPersistentPropertyPath(PersistentPropertyPathFactory.java:171)
at org.springframework.data.mapping.context.PersistentPropertyPathFactory.from(PersistentPropertyPathFactory.java:86)
at org.springframework.data.mapping.context.PersistentPropertyPathFactory.from(PersistentPropertyPathFactory.java:99)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentPropertyPath(AbstractMappingContext.java:293)
at org.springframework.data.mongodb.repository.query.MongoQueryCreator.create(MongoQueryCreator.java:120)
at org.springframework.data.mongodb.repository.query.MongoQueryCreator.create(MongoQueryCreator.java:67)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:119)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:95)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:81)
at org.springframework.data.mongodb.repository.query.PartTreeMongoQuery.createQuery(PartTreeMongoQuery.java:89)
at org.springframework.data.mongodb.repository.query.AbstractMongoQuery.doExecute(AbstractMongoQuery.java:142)
at org.springframework.data.mongodb.repository.query.AbstractMongoQuery.execute(AbstractMongoQuery.java:127)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:159)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
at jdk.proxy2/jdk.proxy2.$Proxy51.findFirstBySimpleTypedValue_Something(Unknown Source)
Here's a test that reproduces the problem.
package org.springframework.data.mongodb.core;
import com.mongodb.client.MongoClient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.mongodb.test.util.Client;
import org.springframework.data.mongodb.test.util.MongoClientExtension;
import org.springframework.data.repository.CrudRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThatNoException;
@ExtendWith({ MongoClientExtension.class, SpringExtension.class })
@ContextConfiguration
public class ReproTest {
static @Client MongoClient mongoClient;
@Configuration
@EnableMongoRepositories(
considerNestedRepositories = true,
[email protected](
type = FilterType.ASSIGNABLE_TYPE,
classes = ReproTest.ContainerRepository.class))
static class Config extends AbstractMongoClientConfiguration {
@Override
protected String getDatabaseName() {
return "test";
}
@Override
public MongoClient mongoClient() {
return mongoClient;
}
@Override
protected boolean autoIndexCreation() {
return false;
}
@Override
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
return Collections.emptySet();
}
@Override
protected void configureConverters(MongoCustomConversions.MongoConverterConfigurationAdapter converterConfigurationAdapter) {
converterConfigurationAdapter.registerConverter(new SimpleTypeConverter());
}
}
@Autowired
MongoOperations mongoOps;
@Autowired
ContainerRepository repository;
@Test
public void repositoryQueryIntoSimpleTypeStructureShouldNotThrowAnException() {
assertThatNoException()
.isThrownBy(() -> repository.findFirstBySimpleTypedValue_Something("example"));
}
@Document
public static class Container {
@Id
private String id;
private SomeSimpleType simpleTypedValue;
}
public static class SomeSimpleType {
private String something;
public String getSomething() {
return something;
}
}
@WritingConverter
public static class SimpleTypeConverter implements Converter<SomeSimpleType, org.bson.Document> {
@Override
public org.bson.Document convert(SomeSimpleType actual) {
return new org.bson.Document("something", actual.getSomething());
}
}
public interface ContainerRepository extends CrudRepository<Container, String> {
Optional<Container> findFirstBySimpleTypedValue_Something(String something);
}
}