Spring mongodb: No enum constant Error

9.5k views Asked by At

Beginner with Java here,

I am trying to define my Model to access data from my Mongodb database with Spring.

I would like to define Gender as an Enum, where in the database the values are stored as strings: male or female

But I get the following error: No enum constant com.nemeantalestudios.mythology.models.Gender.male

Here is my figure POJO:

@Getter
@Setter
@Document(collection = "figures")
public class Figure {

    @Id
    public String id;
    public String name;
    public String greekName;
    public String romanName;
    public String description;
    public String category;
    public String immortal;
    public Gender gender;
}

And here is my enum for gender:

public enum Gender {
    MALE("male"),
    FEMALE("female");

    private final String text;

    Gender(final String text) {
        this.text = text;
    }

    @Override
    public String toString() {
        return text;
    }
}

That is how I try to access it:

@Override public void run(String... args) throws Exception { Optional chronos = figureRepository.findById("5893a17a88dcfdf6dfa73429"); List figureSet = figureRepository.findAll(); System.out.println(figureSet.size());

chronos.ifPresent(figure -> {
    System.out.println(figure.name);
    System.out.println(figure.category);
    System.out.println(figure.description);
    System.out.println(figure.immortal);
    System.out.println(figure.romanName);
    System.out.println(figure.greekName);
    System.out.println(figure.gender.toString());
});

}

In case of here is the stacktrace:

Caused by: java.lang.IllegalArgumentException: No enum constant com.nemeantalestudios.mythology.models.Gender.male
    at java.lang.Enum.valueOf(Enum.java:238) ~[na:1.8.0_171]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getPotentiallyConvertedSimpleRead(MappingMongoConverter.java:884) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1392) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1335) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readProperties(MappingMongoConverter.java:335) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:297) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:253) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:202) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:198) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:86) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.MongoTemplate$ReadDocumentCallback.doWith(MongoTemplate.java:2785) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.MongoTemplate.executeFindOneInternal(MongoTemplate.java:2401) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:2193) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.core.MongoTemplate.findById(MongoTemplate.java:797) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findById(SimpleMongoRepository.java:121) ~[spring-data-mongodb-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_171]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_171]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_171]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_171]
    at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:377) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:629) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:593) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:578) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at com.sun.proxy.$Proxy50.findById(Unknown Source) ~[na:na]
    at com.nemeantalestudios.mythology.MythologyApplication.run(MythologyApplication.java:31) [classes/:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:797) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]

What am I doing wrong?

1

There are 1 answers

1
Andrei Sfat On BEST ANSWER

You will want to create your custom converter for spring data to know how to handle the strings from the enum.

public class FigureConverter {

    @ReadingConverter
    public static class GenderConverter implements Converter<String, Gender> {

        @Override
        public Gender convert(final String source) {
            return Gender.fromString(source);
        }
    }

}

And the configuration to enable the converter:

@Configuration
public class MongoDBConfig {

    @Autowired
    private MongoProperties mongoProperties;

    @Autowired
    private MongoClient mongoClient;


    @Bean
    public MongoTemplate mongoTemplate() {

        MongoTemplate mongoTemplate = new MongoTemplate(mongoClient, mongoProperties.getDatabase());
        MappingMongoConverter mongoMapping = (MappingMongoConverter) mongoTemplate.getConverter();
        mongoMapping.setCustomConversions(customConversions()); // tell mongodb to use the custom converters
        mongoMapping.afterPropertiesSet();
        return mongoTemplate;

    }


    public MongoCustomConversions customConversions() {
        return new MongoCustomConversions(Collections.singletonList(new GenderConverter()));
    }
}

You also might want to save the data using the same converter. You will just want to add a @WritingConverter to achieve that.

I would recommend you use the enum itself, and not the string, because in that case you can leverage spring data to deal with conversions. So if you would save it like a normal enum, you would get the enum saved in mongo as "MALE" or "FEMALE" and spring data would know how to convert that automatically.