编码对象时未使用MongoDB BSON编解码器

我正在尝试将对象存储在MongoDB数据库中(使用MongoDB 3.0.2)并在尝试使用错误消息对对象进行编码时收到CodecConfigurationException

 Can't find a codec for class java.time.LocalDate. 

我已经编写并包含了LocalDate对象的编解码器。 细节如下。

我试图存储的对象DutyBlock具有以下成员变量:

 public class DutyBlock { private LocalDate startDate; private LocalDate endDate; //Inclusive private int blockLength; private double pointValue; private ArrayList assigned; } 

我编写了以下编解码器来编码数据库中的DutyBlock对象:

 public class DutyBlockCodec implements Codec { @Override public void encode(BsonWriter writer, DutyBlock t, EncoderContext ec) { Document document = new Document(); document.append("startDate", t.getStartDate()); document.append("endDate", t.getEndDate()); document.append("blockLength", t.getBlockLength()); document.append("pointValue", t.getPointValue()); document.append("assigned", t.getRasOnDuty()); writer.writeString(document.toJson()); //Line 27 in the error message. } @Override public Class getEncoderClass() { return DutyBlock.class; } @Override public DutyBlock decode(BsonReader reader, DecoderContext dc) { String json = reader.readString(); return new DutyBlock(Document.parse(json)); } } 

由于MongoDB目前不支持java.time.LocalDate class ,因此我编写了以下编解码器来编码数据库中的LocalDate对象:

 public class LocalDateCodec implements Codec { @Override public void encode(BsonWriter writer, LocalDate t, EncoderContext ec) { writer.writeString(t.toString()); } @Override public Class getEncoderClass() { return LocalDate.class; } @Override public LocalDate decode(BsonReader reader, DecoderContext dc) { String date = reader.readString(); return LocalDate.parse(date); } } 

在实例化CodecRegistry时,我已经将两个Codec (以及一个用于Ra类型)添加到MongoClient级别的CodecRegistry。

 public class DutyScheduleDB { private MongoClient mongoClient; private MongoDatabase db; public DutyScheduleDB() { CodecRegistry codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new LocalDateCodec(), new DutyBlockCodec(), new RaCodec()), MongoClient.getDefaultCodecRegistry()); MongoClientOptions options = MongoClientOptions.builder() .codecRegistry(codecRegistry).build(); mongoClient = new MongoClient(new ServerAddress(), options); db = mongoClient.getDatabase("DutySchedulerDB"); } . (More code not shown) . . } 

我尝试将DutyBlock对象的ArrayList存储为MongoDB数据库中org.bson.Document一部分。

 public void storeScheduledCalendar(String id, String calendarName, ArrayList cal) { //Access collection of scheduled calendars. MongoCollection collection = db.getCollection("ScheduledCalendars"); //Query parameter is uuid + calendarName. Document doc = new Document("name", id + calendarName); doc.append("dutyBlocks", cal); //Insert doc to collection. collection.insertOne(doc); //Line 59 in the error message. } 

但是,我遇到此错误消息:

 Exception in thread "main" org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.time.LocalDate. at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46) at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63) at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37) at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:174) at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:189) at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:131) at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:45) at org.bson.Document.toJson(Document.java:294) at org.bson.Document.toJson(Document.java:268) at org.bson.Document.toJson(Document.java:255) at SchedulingHeuristic.DutyBlockCodec.encode(DutyBlockCodec.java:27) at SchedulingHeuristic.DutyBlockCodec.encode(DutyBlockCodec.java:16) at org.bson.codecs.EncoderContext.encodeWithChildContext(EncoderContext.java:91) at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:175) at org.bson.codecs.DocumentCodec.writeIterable(DocumentCodec.java:197) at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:170) at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:189) at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:131) at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:45) at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63) at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29) at com.mongodb.connection.InsertCommandMessage.writeTheWrites(InsertCommandMessage.java:99) at com.mongodb.connection.InsertCommandMessage.writeTheWrites(InsertCommandMessage.java:43) at com.mongodb.connection.BaseWriteCommandMessage.encodeMessageBody(BaseWriteCommandMessage.java:112) at com.mongodb.connection.BaseWriteCommandMessage.encodeMessageBody(BaseWriteCommandMessage.java:35) at com.mongodb.connection.RequestMessage.encode(RequestMessage.java:132) at com.mongodb.connection.BaseWriteCommandMessage.encode(BaseWriteCommandMessage.java:89) at com.mongodb.connection.WriteCommandProtocol.sendMessage(WriteCommandProtocol.java:170) at com.mongodb.connection.WriteCommandProtocol.execute(WriteCommandProtocol.java:73) at com.mongodb.connection.InsertCommandProtocol.execute(InsertCommandProtocol.java:66) at com.mongodb.connection.InsertCommandProtocol.execute(InsertCommandProtocol.java:37) at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:155) at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:219) at com.mongodb.connection.DefaultServerConnection.insertCommand(DefaultServerConnection.java:108) at com.mongodb.operation.MixedBulkWriteOperation$Run$2.executeWriteCommandProtocol(MixedBulkWriteOperation.java:416) at com.mongodb.operation.MixedBulkWriteOperation$Run$RunExecutor.execute(MixedBulkWriteOperation.java:604) at com.mongodb.operation.MixedBulkWriteOperation$Run.execute(MixedBulkWriteOperation.java:363) at com.mongodb.operation.MixedBulkWriteOperation$1.call(MixedBulkWriteOperation.java:148) at com.mongodb.operation.MixedBulkWriteOperation$1.call(MixedBulkWriteOperation.java:141) at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:186) at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:177) at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:141) at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:72) at com.mongodb.Mongo.execute(Mongo.java:747) at com.mongodb.Mongo$2.execute(Mongo.java:730) at com.mongodb.MongoCollectionImpl.executeSingleWriteRequest(MongoCollectionImpl.java:482) at com.mongodb.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:277) at DutyScheduleDB.storeScheduledCalendar(DutyScheduleDB.java:59) at DutyScheduleDB.main(DutyScheduleDB.java:106) 

在尝试编码DutyBlock对象时似乎没有使用我的LocalDate编解码器,尽管我已经validation了我试图存储org.bson.Document的集合确实包含了LocalDateCodec

 System.out.println(collection.getCodecRegistry().get(LocalDate.class)); 

任何人都可以提供一些有关这种情况发生的见解吗?

经过几天的研究,我找到了解决方案。

DutyBlockCodec依赖于LocalDateCodec (我创建)来编码/解码。 仅通过将两个编解码器添加到相同的编解码器注册表中就不满足此依赖性。 解决方案是将包含DutyBlockCodec依赖的编解码器的CodecRegistry对象(例如, CodecRegistry包含LocalDateCodecDutyBlockCodec )传递给DutyBlockCodec的构造函数,该构造函数存储为成员变量。 为了使用LocalDateCodec进行编码,我使用EncoderContext.encodeWithChildContext()方法,传入编解码器, EncoderContext.encodeWithChildContext()器和元素进行编码。 另外,我编写单独的字段而不是将Document编写为String (如在我的原始代码中)。 因此, DutyBlock编解码器最终看起来像这样:

 public class DutyBlockCodec implements Codec { private final CodecRegistry codecRegistry; public DutyBlockCodec(final CodecRegistry codecRegistry) { this.codecRegistry = codecRegistry; } @Override public void encode(BsonWriter writer, DutyBlock t, EncoderContext ec) { writer.writeStartDocument(); Codec dateCodec = codecRegistry.get(LocalDate.class); writer.writeName("startDate"); ec.encodeWithChildContext(dateCodec, writer, t.getStartDate()); writer.writeName("endDate"); ec.encodeWithChildContext(dateCodec, writer, t.getEndDate()); writer.writeName("blockLength"); writer.writeInt32(t.getBlockLength()); writer.writeName("pointValue"); writer.writeDouble(t.getPointValue()); //Writing ArrayList of RAs writer.writeName("assigned"); writer.writeStartArray(); for (Ra ra : t.getRasOnDuty()) { Codec raCodec = codecRegistry.get(Ra.class); ec.encodeWithChildContext(raCodec, writer, ra); } writer.writeEndArray(); writer.writeEndDocument(); } @Override public Class getEncoderClass() { return DutyBlock.class; } @Override public DutyBlock decode(BsonReader reader, DecoderContext dc) { reader.readStartDocument(); Codec dateCodec = codecRegistry.get(LocalDate.class); reader.readName(); LocalDate startDate = dateCodec.decode(reader, dc); reader.readName(); LocalDate endDate = dateCodec.decode(reader, dc); reader.readName(); int blockLength = reader.readInt32(); reader.readName(); double pointValue = reader.readDouble(); //Reading ArrayList of RAs reader.readName(); Codec raCodec = codecRegistry.get(Ra.class); ArrayList rasOnDuty = new ArrayList<>(); reader.readStartArray(); while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { rasOnDuty.add(raCodec.decode(reader, dc)); } reader.readEndArray(); reader.readEndDocument(); return new DutyBlock(startDate, endDate, blockLength, pointValue, rasOnDuty); } } 

DutyBlockCodec依赖于另一个编解码器,因此需要在其构造函数中传入CodecRegistry 。 虽然我相信可以使用LocalDateCodec创建CodecRegistry ,然后将其作为参数传递给DutyBlockCodec的构造函数,然后创建另一个包含LocalDateCodecDutyBlockCodec ,这是相当混乱的,而且MongoDB提供了一个function, CodecProvider到促进这一进程。

使用CodecProvider接口,我写了一个DutyBlockCodecProvider

 public class DutyBlockCodecProvider implements CodecProvider { @Override public  Codec get(Class type, CodecRegistry cr) { if (type == DutyBlock.class) { return (Codec) new DutyBlockCodec(cr); } return null; } } 

我使用CodecRegistries.fromProviders()方法将这些CodecProviders添加到MongoDB客户端。

 CodecRegistry codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new LocalDateCodec()), CodecRegistries.fromProviders( new RaCodecProvider(), new DutyBlockCodecProvider(), new ScheduledDutyCodecProvider()), MongoClient.getDefaultCodecRegistry()); MongoClientOptions options = MongoClientOptions.builder() .codecRegistry(codecRegistry).build(); mongoClient = new MongoClient(new ServerAddress(), options); db = mongoClient.getDatabase("DutySchedulerDB"); 

我可以在https://github.com/desrepair/DutyScheduler找到这个项目的源代码。我愿意回答人们可能遇到的任何问题。