I am trying to set up a Spring Boot application with a MongoDB database. Here is an excerpt from the dependencies I have (in Gradle representation).
compile("org.springframework.boot:spring-boot-starter-web:1.5.1.RELEASE")
compile("org.springframework.boot:spring-boot-starter-data-jpa:1.5.1.RELEASE")
compile("org.springframework.boot:spring-boot-starter-data-mongodb:1.5.1.RELEASE")
compile("org.springframework.boot:spring-boot-starter-hateoas:1.5.1.RELEASE")
compile("org.springframework.boot:spring-boot-starter-security:1.5.1.RELEASE")
compile("org.springframework.security:spring-security-test:1.5.1.RELEASE)
testCompile("org.springframework.boot:spring-boot-starter-test:1.5.1.RELEASE")
compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.8")
My @Document annotated Java class contains a OffsetDateTime attribute.
@Document(collection = "reports")
public class ReportDocument implements Serializable {
@Id private String id;
@Version private Long version;
//...
private OffsetDateTime start;
private OffsetDateTime end;
//...
}
When I call a REST-Controller that retrieves these Documents, it fails with an exception
org.springframework.data.mapping.model.MappingException: No property null found on entity class java.time.OffsetDateTime to bind constructor parameter to!
at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:74) ~[spring-data-commons-1.13.0.RELEASE.jar:na]
at org.springframework.data.mapping.model.SpELExpressionParameterValueProvider.getParameterValue(SpELExpressionParameterValueProvider.java:63) ~[spring-data-commons-1.13.0.RELEASE.jar:na]
at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:71) ~[spring-data-commons-1.13.0.RELEASE.jar:na]
at org.springframework.data.convert.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:83) ~[spring-data-commons-1.13.0.RELEASE.jar:na]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:257) ~[spring-data-mongodb-1.10.0.RELEASE.jar:na]
I read a lot of forums. Some people replaced the OffsetDateTime with the Joda-libraries DateTime. That's not the way to go for me, since Joda states to use the Java 8 DateTime-Types.
What am I doing wrong (I know the problem is always in front of the computer) and how can I solve it? Anyone any idea about this?
UPDATE (from Apr'22/2017) I did like @Veeram said and updated my application with the Converters (Date -> OffsetDateTime and vice versa).
package com.my.personal.app.converter;
import org.springframework.core.convert.converter.Converter;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.util.Date;
public class DateToOffsetDateTimeConverter implements Converter<Date, OffsetDateTime> {
@Override
public OffsetDateTime convert(Date source) {
return source == null ? null : OffsetDateTime.ofInstant(source.toInstant(), ZoneId.systemDefault());
}
}
and
package com.my.personal.app.converter;
import org.springframework.core.convert.converter.Converter;
import java.time.OffsetDateTime;
import java.util.Date;
public class OffsetDateTimeToDateConverter implements Converter<OffsetDateTime, Date> {
@Override
public Date convert(OffsetDateTime source) {
return source == null ? null : Date.from(source.toInstant());
}
}
registering the converters
package com.my.personal.app;
import com.my.personal.app.converter.DateToOffsetDateTimeConverter;
import com.my.personal.app.converter.OffsetDateTimeToDateConverter;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class MongoConfig extends AbstractMongoConfiguration {
@Override
protected String getDatabaseName() {
return "my-personal-database";
}
@Override
public Mongo mongo() throws Exception {
return new MongoClient("localhost");
}
@Bean
@Override
public CustomConversions customConversions() {
List<Converter<?, ?>> converterList = new ArrayList<Converter<?, ?>>();
converterList.add(new DateToOffsetDateTimeConverter());
converterList.add(new OffsetDateTimeToDateConverter());
return new CustomConversions(converterList);
}
@Bean
@Override
public MongoTemplate mongoTemplate() throws Exception {
MappingMongoConverter converter = new MappingMongoConverter(
new DefaultDbRefResolver(mongoDbFactory()), new MongoMappingContext());
converter.setCustomConversions(customConversions());
converter.afterPropertiesSet();
return new MongoTemplate(mongoDbFactory(), converter);
}
}
but again resulting in the exception
org.springframework.data.mapping.model.MappingException: No property null found on entity class java.time.OffsetDateTime to bind constructor parameter to!
at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:74) ~[spring-data-commons-1.13.0.RELEASE.jar:na]
at org.springframework.data.mapping.model.SpELExpressionParameterValueProvider.getParameterValue(SpELExpressionParameterValueProvider.java:63) ~[spring-data-commons-1.13.0.RELEASE.jar:na]
at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:71) ~[spring-data-commons-1.13.0.RELEASE.jar:na]
Am I missing or misunderstanding sth. or doing sth. wrong?
UPDATE of my documents in the collection
Here is an excerpt with the essential parts of my collection's documents
[
{
"_id": {
"$oid": "58f8b107affb5f08e0a78a96"
},
"_class": "com.my.personal.app.document.ReportDocument",
"version": 0,
"checklistId": 2,
"vehicleGuid": "some-vehicle-guid",
"userGuid": "some-user-guid",
"name": "Report 123",
"start": {
"dateTime": {
"$date": "2017-04-20T12:00:55.930Z"
},
"offset": "+02:00"
},
"stations": [
{
"_id": 1,
"name": "Front"
}
]
},
{
"_id": {
"$oid": "58f8bf78affb5f2dec896acf"
},
"_class": "com.my.personal.app.document.ReportDocument",
"version": 0,
"checklistId": 2,
"vehicleGuid": "some-vehicle-guid",
"userGuid": "some-user-guid",
"name": "Report 123",
"start": {
"dateTime": {
"$date": "2017-04-20T10:02:32.930Z"
},
"offset": "+02:00"
},
"stations": [
{
"_id": 1,
"name": "Front"
}
]
}
]
This is the REST controller which tries to call the documents
@RequestMapping(value = "/mongoreports")
public class MongoReportController {
@Autowired
private MongoReportRepository repository;
@RequestMapping(
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
public ResponseEntity<List<ReportDocument>> show(
@RequestParam(name = "vehicleGuid") Optional<String> vehicleGuid,
@RequestParam(name = "userGuid") Optional<String> userGuid) {
if (vehicleGuid.isPresent() && !userGuid.isPresent()) {
List<ReportDocument> reportDocuments = repository.findByVehicleGuidOrderByStartAsc(vehicleGuid.get());
return ResponseEntity.ok(reportDocuments);
}
if (!vehicleGuid.isPresent() && userGuid.isPresent()) {
List<ReportDocument> reportDocuments = repository.findByUserGuidOrderByStartAsc(userGuid.get());
return ResponseEntity.ok(reportDocuments);
}
if (vehicleGuid.isPresent() && userGuid.isPresent()) {
List<ReportDocument> reportDocuments = repository.findByUserGuidAndVehicleGuidOrderByStartAsc(vehicleGuid.get(), userGuid.get());
return ResponseEntity.ok(reportDocuments);
}
return ResponseEntity.badRequest().build();
}
and the according MongoRepository
package com.my.personal.app.repository;
import com.my.personal.app.document.ReportDocument;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface MongoReportRepository extends MongoRepository<ReportDocument, String> {
List<ReportDocument> findByVehicleGuidOrderByStartAsc(String vehicleGuid);
List<ReportDocument> findByUserGuidOrderByStartAsc(String userGuid);
List<ReportDocument> findByUserGuidAndVehicleGuidOrderByStartAsc(String userGuid, String vehicleGuid);
}
I stuggled with this for a fair few hours. The error I was getting was:
No converter found capable of converting from type [java.util.Date] to type [java.time.OffsetDateTime]
I eventually came up with the following config class that converts to a java.util.Date
rather than a String:
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Date;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
@Configuration
public class MongoConfig {
@Bean
public MongoCustomConversions mongoCustomConversions() {
return new MongoCustomConversions(Arrays.asList(
new OffsetDateTimeReadConverter(),
new OffsetDateTimeWriteConverter()
));
}
static class OffsetDateTimeWriteConverter implements Converter<OffsetDateTime, Date> {
@Override
public Date convert(OffsetDateTime source) {
return source == null ? null : Date.from(source.toInstant().atZone(ZoneOffset.UTC).toInstant());
}
}
static class OffsetDateTimeReadConverter implements Converter<Date, OffsetDateTime> {
@Override
public OffsetDateTime convert(Date source) {
return source == null ? null : source.toInstant().atOffset(ZoneOffset.UTC);
}
}
}
This worked for me when building against:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/>
</parent>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With