Skip to content

Repository query into embedded simple type structure throws MappingExceptionΒ #3983

Open
@jpsrn

Description

@jpsrn

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);
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions