10

Trying to use JodaTime's LocalDate with Spring Data MongoDB and Spring 4 but I am getting the following exception:

SEVERE: Servlet.service() for servlet [dispatcher] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.data.mapping.model.MappingException: No property node found on entity class com.th.model.Price to bind constructor parameter to!] with root cause
org.springframework.data.mapping.model.MappingException: No property node found on entity class com.th.model.Price to bind constructor parameter to!
    at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:74)
    at org.springframework.data.mapping.model.SpELExpressionParameterValueProvider.getParameterValue(SpELExpressionParameterValueProvider.java:63)
    at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:71)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:249)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:230)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:190)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:186)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:77)
    at org.springframework.data.mongodb.core.MongoTemplate$ReadDbObjectCallback.doWith(MongoTemplate.java:2121)
    at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:1805)
    at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1628)
    at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1611)
    at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:535)
    at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:526)
    at com.th.model.Company.getPrices(Company.java:255)
    at com.th.controller.Home.getPrices(Home.java:32)
    …

Here my configuration:

<mongo:db-factory id="mongoDbFactory" dbname="th" host="localhost"/>

<mongo:mapping-converter id="mappingConverter">
    <mongo:custom-converters>
    <mongo:converter>
        <bean class="com.th.tool.MongoDBConverters.DateToLocalDateConverter"/>
    </mongo:converter>
    <mongo:converter>
        <bean class="com.th.tool.MongoDBConverters.LocalDateToDateConverter"/>
    </mongo:converter>
    </mongo:custom-converters>
</mongo:mapping-converter>


<bean class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
    <constructor-arg name="mongoConverter" ref="mappingConverter"/>
</bean>

public class MongoDBConverters {

    public static class DateToLocalDateConverter implements Converter<Date, LocalDate> {

        @Override
        public LocalDate convert(Date source) {
            return source == null ? null : LocalDate.fromDateFields(source);
        }
    }

    public static class LocalDateToDateConverter implements Converter<LocalDate, Date> {

        @Override
        public Date convert(LocalDate source) {
            return source == null ? null : source.toDate();
        }
    }
}
Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
Marco C
  • 3,101
  • 3
  • 30
  • 49

2 Answers2

8

Apparently I can't have a constructor like this:

public Price(String companyId, JsonNode node) {
        this.companyId = companyId;

        if(node != null) {
            this.date = Tools.getLocalDate(node.get("Date").getTextValue());
            this.closePrice = Double.parseDouble(node.get("Close").getTextValue());
            this.volume = Double.parseDouble(node.get("Volume").getTextValue());
        }
}

A normal one with direct assignment work:

public Price(String companyId, LocalDate date, double closePrice, double volume) {
    this.companyId = companyId;
    this.date = date;
    this.closePrice = closePrice;
    this.volume = volume;
}
Marco C
  • 3,101
  • 3
  • 30
  • 49
  • 1
    That's correct. `JsonNode` is a Jackson type I assume. Spring Data MongoDB does have anything to do with Jackson. The mechanics of constructor parameter resolution is described in [this answer](http://stackoverflow.com/a/13834796/18122). – Oliver Drotbohm Jan 06 '15 at 10:32
4

You have to make your inner classes static at the moment they require an instance of the enclosing class MongoDBConverters to be available before the object can be constructed. Making this class static doesn't require that.

public class MongoDBConverters {
    public static class DateToLocalDateConverter implements Converter<Date, LocalDate> {
        @Override
        public LocalDate convert(Date source) {
            return source == null ? null : LocalDate.fromDateFields(source);
        }
    }

    public static class LocalDateToDateConverter implements Converter<LocalDate, Date> {
        @Override
        public Date convert(LocalDate source) {
            return source == null ? null : source.toDate();
        }
    }
}

For more info check Java inner class and static nested class.

Community
  • 1
  • 1
M. Deinum
  • 115,695
  • 22
  • 220
  • 224