使用Spring Boot将ZonedDateTime保存到MongoDB时的CodecConfigurationException> = 2.0.1.RELEASE

问题描述 投票:6回答:1

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

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

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]

这可以通过将Spring Boot版本从2.0.5.RELEASE更改为pom.xml中的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项目提交了一个bug(DATAMONGO-2106),但是如果不想要改变这种行为也不会有高优先级。

什么是最好的解决方法?当我发现异常消息时,我发现了一些注册custom codeccustom converter或使用Jackson JSR 310的方法。我宁愿不在我的项目中添加自定义代码来处理java.time包中的类。

spring mongodb spring-boot spring-data-mongodb java.time
1个回答
3
投票

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

这些是已知的解决方法:

  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. 编写自定义编解码器。至少在理论上。使用Spring Boot 2.0.5时,我的codec test branch无法解组数据,同时使用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; } }
© www.soinside.com 2019 - 2024. All rights reserved.