spring-data-mongodb
spring-data-mongodb copied to clipboard
CodecConfigurationException when saving ZonedDateTime to MongoDB with Spring Boot >= 2.0.1.RELEASE [DATAMONGO-2106]
thokrae opened DATAMONGO-2106 and commented
I was able to reproduce this bug with a minimal modification of the official Spring Boot guide for Accessing Data with MongoDB, see https://github.com/thokrae/spring-data-mongo-zoneddatetime.
After adding a java.time.ZonedDateTime
field to the Customer class, running the example code from the guide fails with a CodecConfigurationException:
Customer.java:
public String lastName;
public ZonedDateTime created;
public Customer() {
output:
...
Caused by: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.time.ZonedDateTime.
at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46) ~[bson-3.6.3.jar:na]
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63) ~[bson-3.6.3.jar:na]
at org.bson.codecs.configuration.ChildCodecRegistry.get(ChildCodecRegistry.java:51) ~[bson-3.6.3.jar:na]
at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:184) ~[bson-3.6.3.jar:na]
at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:199) ~[bson-3.6.3.jar:na]
at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:141) ~[bson-3.6.3.jar:na]
at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:45) ~[bson-3.6.3.jar:na]
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63) ~[bson-3.6.3.jar:na]
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29) ~[bson-3.6.3.jar:na]
at com.mongodb.operation.BulkWriteBatch$WriteRequestEncoder.encode(BulkWriteBatch.java:381) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.operation.BulkWriteBatch$WriteRequestEncoder.encode(BulkWriteBatch.java:371) ~[mongodb-driver-core-3.6.3.jar:na]
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63) ~[bson-3.6.3.jar:na]
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29) ~[bson-3.6.3.jar:na]
at com.mongodb.connection.BsonWriterHelper.writeDocument(BsonWriterHelper.java:74) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.connection.BsonWriterHelper.writePayload(BsonWriterHelper.java:58) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.connection.CommandMessage.encodeMessageBodyWithMetadata(CommandMessage.java:133) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.connection.RequestMessage.encode(RequestMessage.java:147) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:245) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:98) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:441) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.connection.CommandProtocolImpl.execute(CommandProtocolImpl.java:80) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:189) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:264) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:126) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.operation.MixedBulkWriteOperation.executeCommand(MixedBulkWriteOperation.java:372) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.operation.MixedBulkWriteOperation.executeBulkWriteBatch(MixedBulkWriteOperation.java:254) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.operation.MixedBulkWriteOperation.access$700(MixedBulkWriteOperation.java:65) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.operation.MixedBulkWriteOperation$1.call(MixedBulkWriteOperation.java:198) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.operation.MixedBulkWriteOperation$1.call(MixedBulkWriteOperation.java:189) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.operation.OperationHelper.withReleasableConnection(OperationHelper.java:433) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:189) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:65) ~[mongodb-driver-core-3.6.3.jar:na]
at com.mongodb.Mongo$3.execute(Mongo.java:837) ~[mongodb-driver-3.6.3.jar:na]
at com.mongodb.MongoCollectionImpl.executeSingleWriteRequest(MongoCollectionImpl.java:1025) ~[mongodb-driver-3.6.3.jar:na]
at com.mongodb.MongoCollectionImpl.executeInsertOne(MongoCollectionImpl.java:513) ~[mongodb-driver-3.6.3.jar:na]
at com.mongodb.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:493) ~[mongodb-driver-3.6.3.jar:na]
at com.mongodb.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:487) ~[mongodb-driver-3.6.3.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate$8.doInCollection(MongoTemplate.java:1276) ~[spring-data-mongodb-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:524) ~[spring-data-mongodb-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.mongodb.core.MongoTemplate.insertDocument(MongoTemplate.java:1270) ~[spring-data-mongodb-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.mongodb.core.MongoTemplate.doInsert(MongoTemplate.java:1051) ~[spring-data-mongodb-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:988) ~[spring-data-mongodb-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:80) ~[spring-data-mongodb-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:377) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:629) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:593) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:578) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at com.sun.proxy.$Proxy48.save(Unknown Source) ~[na:na]
at com.enbw.App.run(App.java:23) [classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:797) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
... 5 common frames omitted
This can be solved by changing the Spring Boot version from 2.0.5.RELEASE to 2.0.1.RELEASE in the pom.xml (the version of spring-mongo-db changes from 2.0.10.RELEASE to 2.0.6.RELEASE):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
Now the exception is gone and the Customer objects including the ZonedDateTime fields are written to MongoDB.
Would you agree this is a breaking change in a point release and therefore a bug?
Affects: 2.0.10 (Kay SR10)
1 votes, 3 watchers
Oliver Drotbohm commented
ZonedDateTime
is not a type MongoDB understands. Spring Data in turn only supports date-time-types without time zone information out of the box as only those types guarantee orderability of the values in the database. See the reference documentation for details.
If you want to persist types that carry time zone information you need to map them yourself, i.e. register custom converters for them
Oliver Drotbohm commented
I just checked your code sample and also can't get this to work on 2.0.1 as the app fails to bootstrap with a different error coming from Spring Data
thokrae commented
Thank you for the reponse.
The example code runs with 2.0.1.RELEASE if I provide the connection details of a MongoDB cluster in the application.properties.
spring.data.mongodb.host=***
spring.data.mongodb.port=***
spring.data.mongodb.database=***
spring.data.mongodb.user=
spring.data.mongodb.pw=***
Screenshot of the result in MongoBooster
I also had another exception when trying to connect to a local MongoDB instance on a Ubuntu 18.04 system, but assumed this was a configuration issue
Oliver Drotbohm commented
I am running into an exception that a parameter name cannot be determined on 2.0.1. In general, as documented, date time types with time zones are not supported
MongoDB Java driver has support for ZonedDateTime for version >= 3.7, Implementation details
@dineshbhagat thanks for the heads up. We'll look into this.
MongoDB Java driver has support for ZonedDateTime for version >= 3.7, Implementation details
My bad 😞, there is only the following codec as of now in version 4.2
InstantCodec
LocalDateCodec
LocalDateTimeCodec
LocalTimeCodec
MongoDB Java driver has support for ZonedDateTime for version >= 3.7, Implementation details
No, for ZonedDateTime
we still need to add bson-codecs-jsr310 dependency and then register the codec.
<dependency>
<groupId>io.github.cbartosiak</groupId>
<artifactId>bson-codecs-jsr310</artifactId>
<version>3.5.4</version>
</dependency>
I am really facing issue with LocalDate and LocalDateTime. When i saved or retrive or delete by date everything seems to work with default setting. I did not do anythig special for jsr310. Except changing date before saving like below -
.date( LocalDate.of( v.getDate().getYear(), v.getDate().getMonth(), v.getDate().getDayOfMonth()) .atStartOfDay(ZoneOffset.UTC) .toInstant() .atZone(ZoneOffset.UTC) .toLocalDate())
but the only problem i see when i look at db .. in database date is one day less and time is 1 hr less to actual. i was expecting it should save exact same value but its not. Even for Insant its same.