使用Spring Boot> = 2.0.1将ZonedDateTime保存到MongoDB时发生CodecConfigurationException [英] CodecConfigurationException when saving ZonedDateTime to MongoDB with Spring Boot >= 2.0.1.RELEASE

查看:123
本文介绍了使用Spring Boot> = 2.0.1将ZonedDateTime保存到MongoDB时发生CodecConfigurationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我仅需对使用MongoDB访问数据,请参见 https://github.com/thokrae/spring-data-mongo-zoneddatetime .

java.time.ZonedDateTime字段添加到Customer类之后,从指南中运行示例代码失败,并出现CodecConfigurationException:

After adding a java.time.ZonedDateTime field to the Customer class, running the example code from the guide fails with a CodecConfigurationException:

Customer.java:

Customer.java:

    public String lastName;
    public ZonedDateTime created;

    public Customer() {

输出:

...
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.4.jar:na]
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63) ~[bson-3.6.4.jar:na]
at org.bson.codecs.configuration.ChildCodecRegistry.get(ChildCodecRegistry.java:51) ~[bson-3.6.4.jar:na]

可以通过将pom.xml中的Spring Boot版本从2.0.5.RELEASE更改为2.0.1.RELEASE来解决:

This can be solved by changing the Spring Boot version from 2.0.5.RELEASE to 2.0.1.RELEASE in the pom.xml:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
    </parent>

现在,异常消失了,包括ZonedDateTime字段在内的Customer对象

Now the exception is gone and the Customer objects including the ZonedDateTime fields are written to MongoDB.

我通过spring-data-mongodb提交了一个错误( DATAMONGO-2106 )项目,但会了解是否不希望更改此行为,也不具有较高的优先级.

I filed a bug (DATAMONGO-2106) with the spring-data-mongodb project but would understand if changing this behaviour is not wanted nor has a high priority.

最好的解决方法是什么?在处理异常消息时,我会找到几种方法,例如注册自定义编解码器,一个自定义转换器或使用杰克逊JSR 310 .我宁愿不要在我的项目中添加自定义代码来处理java.time包中的类.

What is the best workaround? When duckduckgoing for the exception message I find several approaches like registering a custom codec, a custom converter or using Jackson JSR 310. I would prefer to not add custom code to my project to handle a class from the java.time package.

推荐答案

带有时区的持久日期时间类型不受Spring Data MongoDB的支持. spring.io/browse/DATAMONGO-2106?focusedCommentId=162512&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-162512"rel =" noreferrer> DATAMONGO-2106 .

Persisting date time types with time zones was never supported by Spring Data MongoDB, as stated by Oliver Drotbohm himself in DATAMONGO-2106.

这些是已知的解决方法:

These are the known workarounds:

  1. 使用不带时区的日期时间类型,例如java.time.Instant. (通常建议仅在后端使用UTC,但我不得不扩展采用不同方法的现有代码库.)
  2. 编写一个自定义转换器,并通过扩展AbstractMongoConfiguration对其进行注册.参见分支我的测试存储库中的转换器以获取正在运行的示例.

  1. Use a date time type without a time zone, e.g. java.time.Instant. (It is generally advisable to only use UTC in the backend, but I had to extend an existing code base which was following a different approach.)
  2. Write a custom converter and register it by extending AbstractMongoConfiguration. See the branch converter in my test repository for a running example.

@Component
@WritingConverter
public class ZonedDateTimeToDocumentConverter implements Converter<ZonedDateTime, Document> {
    static final String DATE_TIME = "dateTime";
    static final String ZONE = "zone";

    @Override
    public Document convert(@Nullable ZonedDateTime zonedDateTime) {
        if (zonedDateTime == null) return null;

        Document document = new Document();
        document.put(DATE_TIME, Date.from(zonedDateTime.toInstant()));
        document.put(ZONE, zonedDateTime.getZone().getId());
        document.put("offset", zonedDateTime.getOffset().toString());
        return document;
    }
}

@Component
@ReadingConverter
public class DocumentToZonedDateTimeConverter implements Converter<Document, ZonedDateTime> {

    @Override
    public ZonedDateTime convert(@Nullable Document document) {
        if (document == null) return null;

        Date dateTime = document.getDate(DATE_TIME);
        String zoneId = document.getString(ZONE);
        ZoneId zone = ZoneId.of(zoneId);

        return ZonedDateTime.ofInstant(dateTime.toInstant(), zone);
    }
}

@Configuration
public class MongoConfiguration extends AbstractMongoConfiguration {

    @Value("${spring.data.mongodb.database}")
    private String database;

    @Value("${spring.data.mongodb.host}")
    private String host;

    @Value("${spring.data.mongodb.port}")
    private int port;

    @Override
    public MongoClient mongoClient() {
        return new MongoClient(host, port);
    }

    @Override
    protected String getDatabaseName() {
        return database;
    }

    @Bean
    public CustomConversions customConversions() {
        return new MongoCustomConversions(asList(
                new ZonedDateTimeToDocumentConverter(),
                new DocumentToZonedDateTimeConverter()
        ));
    }
}

  • 编写自定义编解码器.至少在理论上.我的编解码器测试分支在使用Spring Boot 2.0.1正常工作时,使用Spring Boot 2.0.5时无法解组数据.

  • Write a custom codec. At least in theory. My codec test branch is unable to unmarshal the data when using Spring Boot 2.0.5 while working fine with Spring Boot 2.0.1.

    public class ZonedDateTimeCodec implements Codec<ZonedDateTime> {
    
        public static final String DATE_TIME = "dateTime";
        public static final String ZONE = "zone";
    
        @Override
        public void encode(final BsonWriter writer, final ZonedDateTime value, final EncoderContext encoderContext) {
            writer.writeStartDocument();
            writer.writeDateTime(DATE_TIME, value.toInstant().getEpochSecond() * 1_000);
            writer.writeString(ZONE, value.getZone().getId());
            writer.writeEndDocument();
        }
    
        @Override
        public ZonedDateTime decode(final BsonReader reader, final DecoderContext decoderContext) {
            reader.readStartDocument();
            long epochSecond = reader.readDateTime(DATE_TIME);
            String zoneId = reader.readString(ZONE);
            reader.readEndDocument();
    
            return ZonedDateTime.ofInstant(Instant.ofEpochSecond(epochSecond / 1_000), ZoneId.of(zoneId));
        }
    
        @Override
        public Class<ZonedDateTime> getEncoderClass() {
            return ZonedDateTime.class;
        }
    }
    
    @Configuration
    public class MongoConfiguration extends AbstractMongoConfiguration {
    
        @Value("${spring.data.mongodb.database}")
        private String database;
    
        @Value("${spring.data.mongodb.host}")
        private String host;
    
        @Value("${spring.data.mongodb.port}")
        private int port;
    
        @Override
        public MongoClient mongoClient() {
            return new MongoClient(host + ":" + port, createOptions());
        }
    
        private MongoClientOptions createOptions() {
            CodecProvider pojoCodecProvider = PojoCodecProvider.builder()
                    .automatic(true)
                    .build();
    
            CodecRegistry registry = CodecRegistries.fromRegistries(
                    createCustomCodecRegistry(),
                    MongoClient.getDefaultCodecRegistry(),
                    CodecRegistries.fromProviders(pojoCodecProvider)
            );
    
            return MongoClientOptions.builder()
                    .codecRegistry(registry)
                    .build();
        }
    
        private CodecRegistry createCustomCodecRegistry() {
            return CodecRegistries.fromCodecs(
                    new ZonedDateTimeCodec()
            );
        }
    
        @Override
        protected String getDatabaseName() {
            return database;
        }
    }
    

  • 这篇关于使用Spring Boot&gt; = 2.0.1将ZonedDateTime保存到MongoDB时发生CodecConfigurationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆