gpt4 book ai didi

spring - 使用 Spring Boot >= 2.0.1.RELEASE 将 ZonedDateTime 保存到 MongoDB 时出现 CodecConfigurationException

转载 作者:行者123 更新时间:2023-12-01 18:52:17 33 4
gpt4 key购买 nike

我能够通过对 Accessing Data with MongoDB 官方 Spring Boot 指南进行最小修改来重现我的问题,参见https://github.com/thokrae/spring-data-mongo-zoneddatetime .

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

客户.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 来解决:

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

现在异常消失了,Customer 对象包括 ZonedDateTime 字段 are written to MongoDB .

我向 spring-data-mongodb 项目提交了一个错误 ( DATAMONGO-2106 ),但我会理解是否不需要更改此行为,也没有高优先级。

最好的解决方法是什么?当 duckduckgoing 获取异常消息时,我发现了几种方法,例如注册 custom codec ,一个custom converter或使用Jackson JSR 310 。我不想向我的项目添加自定义代码来处理 java.time 包中的类。

最佳答案

Spring Data MongoDB 从来不支持持久化带有时区的日期时间类型,正如 Oliver Drotbohm 本人在 DATAMONGO-2106 中所述。 .

这些是已知的解决方法:

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

    @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()
    ));
    }
    }
  3. 编写自定义编解码器。至少在理论上是这样。我的codec test branch使用 Spring Boot 2.0.5 时无法解码数据,而使用 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 - 使用 Spring Boot >= 2.0.1.RELEASE 将 ZonedDateTime 保存到 MongoDB 时出现 CodecConfigurationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59713262/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com