Skip to content

Repository query into embedded simple type structure throws MappingException #3983

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jpsrn opened this issue Mar 3, 2022 · 1 comment
Labels
for: team-attention An issue we need to discuss as a team to make progress type: bug A general bug type: enhancement A general enhancement

Comments

@jpsrn
Copy link

jpsrn commented Mar 3, 2022

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

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 3, 2022
@christophstrobl
Copy link
Member

Thanks for bringing this to our attention and providing the reproducer 👍 - I'll take this to the team.

@christophstrobl christophstrobl added the for: team-attention An issue we need to discuss as a team to make progress label Mar 10, 2022
@christophstrobl christophstrobl added type: bug A general bug type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: team-attention An issue we need to discuss as a team to make progress type: bug A general bug type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants