4

I am trying to use the Java 8 LocalDate and LocalDateTime in my Java Spring project, but I get problems saving and retrieving the dates from my MongoDB.

I am using Spring Data Mongo (1.6.1.Release). I've followed this answer and implemented my own converter (After it didn't work I tried to directly convert LocalDate to String).

The problem I guess, is that Spring Data is not using the converter, since my database entry looks like this:

{ "_id" : "frank@steiler.eu", "_class" : "de.steilerdev.myVerein.server.model.User", "firstName" : "Frank", "lastName" : "Steiler", "password" : "ef9aa46dd6f98af4878ed72eac69134ac236b5581e1207bd8e9d4f691ed20a48f4ed8d2b80229e4a649f1e7b5302c2c6166fc783b9cbfff6d8a18ad820652b1e", "salt" : "6b912cac277c6656", "memberSince" : { "year" : 2000, "month" : 1, "day" : 1 }, "birthday" : { "year" : 1994, "month" : 6, "day" : 28 } }

Here is my code (If I am missing anything out, my complete project can be found here):

database-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans  xmlns="http://www.springframework.org/schema/data/mongo"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:beans="http://www.springframework.org/schema/beans"
              xmlns:context="http://www.springframework.org/schema/context"
              xsi:schemaLocation=" http://www.springframework.org/schema/beans
                                   http://www.springframework.org/schema/beans/spring-beans.xsd
                                   http://www.springframework.org/schema/context
                                   http://www.springframework.org/schema/context/spring-context.xsd
                                   http://www.springframework.org/schema/data/mongo
                                   http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">

        <!-- Including database configuration files -->
        <context:property-placeholder location="classpath:mongo.properties"/>

        <mapping-converter id="javaTimeConverter">
            <custom-converters>
                <converter>
                    <beans:bean class="de.steilerdev.myVerein.server.time.StringToLocalDateConverter" />
                </converter>
                <converter>
                    <beans:bean class="de.steilerdev.myVerein.server.time.StringToLocalDateTimeConverter" />
                </converter>
                <converter>
                    <beans:bean class="de.steilerdev.myVerein.server.time.LocalDateTimeToStringConverter" />
                </converter>
                <converter>
                    <beans:bean class="de.steilerdev.myVerein.server.time.LocalDateToStringConverter" />
                </converter>
            </custom-converters>
        </mapping-converter>

       <!-- Database configuration -->
       <repositories base-package="de.steilerdev.myVerein.server.model" />
       <template db-factory-ref="mongoDbFactory" converter-ref="javaTimeConverter"/>

       <db-factory host="${dbHost}"
                   port="${dbPort}"
                   dbname="${dbName}"
                   username="${dbUsername}"
                   password="${dbPassword}"/>

       <!-- Data Validation using Hibernate Validator and JavaX validation interface -->
       <beans:bean id="dataValidator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

       <beans:bean id="mongoValidator" class="org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener">
              <beans:constructor-arg name="validator" ref="dataValidator"/>
       </beans:bean>

</beans:beans>

One of the convertes (The others are similiar)

package de.steilerdev.myVerein.server.time;

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * This class is a converter used by SpringData, to convert a Java 8 LocalDateTime to a Java 7 Date
 */
@Component
public class LocalDateTimeToStringConverter implements Converter<LocalDateTime, String>
{
    @Override
    public String convert(LocalDateTime source) {
        return source == null ? null : source.toString();
    }
}

My user object (without the getter and setter)

package de.steilerdev.myVerein.server.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import de.steilerdev.myVerein.server.security.PasswordEncoder;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.keygen.KeyGenerators;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

public class User implements UserDetails
{
    @NotBlank
    private String firstName;
    @NotBlank
    private String lastName;

    @Id
    @Indexed
    @NotBlank
    @Email
    private String email;

    @JsonIgnore
    @NotBlank
    private String password;

    @JsonIgnore
    @NotBlank
    private String salt;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private HashMap<String,String> privateInformation;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private HashMap<String,String> publicInformation;

    @DBRef
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private List<Division> divisions;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private LocalDate memberSince;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private LocalDate passiveSince;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private LocalDate birthday;

    @Transient
    @JsonIgnore
    Collection<? extends GrantedAuthority> authorities;

    @Transient
    @JsonInclude(JsonInclude.Include.NON_NULL)
    boolean administrationAllowed;
}

My error occurs during log in and is the following:

org.springframework.security.authentication.InternalAuthenticationServiceException: No property null found on entity class java.time.LocalDate to bind constructor parameter to!
    at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:110)
    at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:132)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
    at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:211)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1081)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1566)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1523)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.data.mapping.model.MappingException: No property null found on entity class java.time.LocalDate 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.readValue(MappingMongoConverter.java:1129)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$200(MappingMongoConverter.java:77)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1078)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getValueInternal(MappingMongoConverter.java:829)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:278)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:266)
    at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:294)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:266)
    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.executeFindOneInternal(MongoTemplate.java:1760)
    at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:1577)
    at org.springframework.data.mongodb.core.MongoTemplate.findOne(MongoTemplate.java:497)
    at org.springframework.data.mongodb.repository.query.AbstractMongoQuery$SingleEntityExecution.execute(AbstractMongoQuery.java:307)
    at org.springframework.data.mongodb.repository.query.AbstractMongoQuery.execute(AbstractMongoQuery.java:107)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:421)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:381)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$DefaultMethodInvokingMethodInterceptor.invoke(RepositoryFactorySupport.java:512)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy1100.findByEmail(Unknown Source)
    at de.steilerdev.myVerein.server.security.UserAuthenticationService.loadUserByUsername(UserAuthenticationService.java:48)
    at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:102)
    ... 36 more
Community
  • 1
  • 1
steilerDev
  • 961
  • 7
  • 11

1 Answers1

4

Ok so I do think I may have something for you :)

You have 4 converters:

  • LocalDate -> String
  • String -> LocalDate
  • LocalDateTime -> String
  • String -> LocalDateTime

Now, see your class declarations:

public class LocalDateToStringConverter implements Converter<LocalDateTime, String>

public class StringToLocalDateConverter implements Converter<String, LocalDate>

public class LocalDateTimeToStringConverter implements Converter<LocalDateTime, String>

public class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime>

What is wrong with the first one? You are mapping LocalDateTime to String, while your class name says you should be mapping LocalDate to String.

Copy/paste is a mean and evil thing sometimes. Let me know if it had helped!

Ps. you are creating your converters two times: one time in database-config.xml while the second one through @Component annotation.

Rafal G.
  • 4,252
  • 1
  • 25
  • 41
  • hey, thanks for your response, tried it, but didn't change any behaviour. – steilerDev Jan 18 '15 at 00:30
  • @Fat_FS I can see that you have added github repo - very cool :) I will try to look into it and let you know. – Rafal G. Jan 18 '15 at 10:44
  • Oh man good catch! That was indeed the problem. Would have never found it without you :) – steilerDev Jan 18 '15 at 12:11
  • Hi, what was the problem? I've having a similar issue – annesadleir Sep 10 '15 at 12:51
  • @annesadleir Read question carefully and then read an answer. OP has made a mistake when copy/pasteing code and classname was not matching implemented Converter interface types. – Rafal G. Sep 10 '15 at 18:44
  • Hi @R4J I read the question very carefully -- Fat_FS's first answer was that the copy-paste problem had been fixed and had made no difference. The subsequent pair of comments implied that Fat_FS had fixed the problem after you had commented on their github. But the github link is broken. I have not got an error in my Converter genericisation, but I am getting exactly the same error message when retrieving from the database. – annesadleir Sep 12 '15 at 11:58