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]
これは、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>
これで例外はなくなり、ZonedDateTimeフィールドを含むCustomerオブジェクト MongoDBに書き込まれます になります。
私はspring-data-mongodbプロジェクトにバグ( DATAMONGO-2106 )を提出しましたが、この動作を変更する必要がないか、優先度が高いかどうかは理解できます。
最良の回避策は何ですか?例外メッセージのためにduckduckgoingするとき、 カスタムコーデック 、 カスタムコンバーター を登録する、または Jackson JSR 31 を使用するなどのいくつかのアプローチを見つけます。 Java.timeパッケージのクラスを処理するために、プロジェクトにカスタムコードを追加したくない。
DATAMONGO-2106 でOliver Drotbohm自身が述べたように、タイムゾーンを持つ日付時刻タイプの永続化は、Spring Data MongoDBではサポートされていませんでした。
これらは既知の回避策です:
カスタムコンバーターを作成し、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()
));
}
}
カスタムコーデックを記述します。少なくとも理論的には。私の コーデックテストブランチ は、Spring Boot 2.0.1で正常に動作しているときにSpring Boot 2.0.5を使用すると、データを非整列化できません。
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;
}
}