2015-06-01 9 views
18

Ich bin versucht, ein Objekt in einer MongoDB-Datenbank zu speichern (mit MongoDB 3.0.2) und bin ein CodecConfigurationException bekommen, wenn versucht wird, das Objekt mit der Fehlermeldung zu kodierenMongoDB BSON Codec nicht verwendet werden, während Objekt kodiert

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

Ich habe geschrieben und einen Codec für die LocalDate Objekte enthalten. Details folgen.

Das Objekt, DutyBlock, dass ich zu speichern bin versucht hat, diese Membervariablen:

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

Ich schrieb den folgenden Codec die DutyBlock Objekte innerhalb der Datenbank zu kodieren:

public class DutyBlockCodec implements Codec<DutyBlock> { 

    @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<DutyBlock> getEncoderClass() { 
     return DutyBlock.class; 
    } 

    @Override 
    public DutyBlock decode(BsonReader reader, DecoderContext dc) { 
     String json = reader.readString(); 
     return new DutyBlock(Document.parse(json)); 
    } 

} 

Seit MongoDB unterstützt derzeit nicht die java.time.LocalDate class, ich habe den folgenden Codec geschrieben, um die LocalDate Objekte in der Datenbank zu kodieren:

public class LocalDateCodec implements Codec<LocalDate> { 

    @Override 
    public void encode(BsonWriter writer, LocalDate t, EncoderContext ec) { 
     writer.writeString(t.toString()); 
    } 

    @Override 
    public Class<LocalDate> getEncoderClass() { 
     return LocalDate.class; 
    } 

    @Override 
    public LocalDate decode(BsonReader reader, DecoderContext dc) { 
     String date = reader.readString(); 
     return LocalDate.parse(date); 
    } 
} 

Ich habe die beiden Codec ‚s (zusammen mit einer für den Ra Typ) an den CodecRegistry am MongoClient Ebene hinzugefügt, während die MongoClient instanziieren.

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) 
    . 
    . 
} 

ich versuchen, eine ArrayList von DutyBlock Objekte als Teil eines org.bson.Document innerhalb der MongoDB Datenbank zu speichern.

public void storeScheduledCalendar(String id, 
     String calendarName, 
     ArrayList<DutyBlock> 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. 
} 

aber ich laufe in dieser Fehlermeldung:

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) 

Es scheint, dass mein Codec für LocalDate nicht verwendet wird, wenn DutyBlock Objekte zu kodieren versuchen, obwohl ich überprüft haben, dass die Sammlung, die ich enthalten die LocalDateCodec über eine

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

Kann jemand c ist in der Tat die org.bson.Document in speichern bin versucht, einen Einblick geben, warum dies geschieht?

Antwort

19

Nach mehreren Tagen der Forschung habe ich eine Lösung gefunden.

Die DutyBlockCodec hängt von der LocalDateCodec (die ich erstellt habe) ab, um zu encodieren/zu decodieren. Diese Abhängigkeit wird nicht nur durch Hinzufügen der zwei Codecs in die gleiche Codec-Registrierung erfüllt. Die Lösung besteht darin, ein CodecRegistry-Objekt zu übergeben, das die Codecs enthält, die DutyBlockCodec von (z. B. CodecRegistry, das darin den LocalDateCodec enthält) an den DutyBlockCodec-Konstruktor, der als eine Elementvariable gespeichert ist. Um die LocalDateCodec zu verwenden, um zu kodieren, verwende ich die EncoderContext.encodeWithChildContext() Methode und übergebe den Codec, den Verfasser und das Element, um zu kodieren. Zusätzlich schreibe ich einzelne Felder anstatt eine Document als String (wie in meinem ursprünglichen Code) zu schreiben. So ist die DutyBlock Codec endet wie folgt aussehen:

public class DutyBlockCodec implements Codec<DutyBlock> { 
    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<DutyBlock> getEncoderClass() { 
     return DutyBlock.class; 
    } 

    @Override 
    public DutyBlock decode(BsonReader reader, DecoderContext dc) { 
     reader.readStartDocument(); 
      Codec<LocalDate> 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<Ra> raCodec = codecRegistry.get(Ra.class); 
      ArrayList<Ra> 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 auf einem anderen Codec abhängig und erfordert so eine CodecRegistry an den Konstruktor übergeben werden.Während ich glaube, dass es möglich ist, eine CodecRegistry mit der LocalDateCodec zu erstellen, dann übergeben Sie dies als ein Argument an DutyBlockCodec 's Konstruktor, dann erstellen Sie eine weitere CodecRegistry enthält LocalDateCodec und DutyBlockCodec, das ist eher verwirrend, und MongoDB bietet eine Funktionalität, die CodecProvider zu diesen Prozess erleichtern.

die CodecProvider Schnittstelle, schrieb ich eine DutyBlockCodecProvider

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

ich diese CodecProviders zum MongoDB-Client mit der CodecRegistries.fromProviders() Methode hinzugefügt.

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"); 

Mein Quellcode für dieses Projekt finden Sie unter https://github.com/desrepair/DutyScheduler finden Ich bin offen für alle Fragen zu beantworten Menschen haben kann.

+2

+1 Vielen Dank! Es funktioniert wie erwartet. Ich denke, mongoDB konnte sich keinen komplizierteren Weg vorstellen. Sie erreichten die höchste Komplexität, so gut sie konnten – Li3ro