spring-projects / spring-projects/spring-data-mongodb
Repository query into embedded simple type structure throws MappingException
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 1.7k
- Forks
- 1.1k
- PR merge metrics
- No merged PRs in 30d
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,
includeFilters=@ComponentScan.Filter(
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);
}
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with ReproTest.repositoryQueryIntoSimpleTypeStructureShouldNotThrowAnException and run the supplied reproduction. Trace the failure from MongoQueryCreator.java through PersistentPropertyPathFactory and AbstractMappingContext; done means the repository method completes without a MappingException for the embedded converted type.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, mongodb, spring
- Domain
- backend, database
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100