2015-06-01 16 views
18

Sto tentando di memorizzare un oggetto in un database MongoDB (usando MongoDB 3.0.2) e sto ottenendo un CodecConfigurationException quando si tenta di codificare l'oggetto con messaggio di erroreMongoDB BSON codec non viene utilizzato durante la codifica oggetto

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

Ho scritto e incluso un codec per gli oggetti LocalDate. Seguono i dettagli

L'oggetto, DutyBlock, che sto cercando di negozio ha queste variabili membro:

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

ho scritto il seguente codec per codificare gli DutyBlock oggetti all'interno del database:

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

} 

Dal Attualmente MongoDB non supporta lo java.time.LocalDate class, ho scritto il seguente codec per codificare gli oggetti LocalDate all'interno del database:

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

Ho aggiunto i due Codec s'(insieme a uno per il tipo di Ra) al CodecRegistry a livello MongoClient mentre istanziare la MongoClient.

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

tento di memorizzare un ArrayList di DutyBlock oggetti come parte di un org.bson.Document all'interno del database MongoDB.

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

Comunque, sto correndo in questo messaggio di errore:

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) 

Sembra che il mio codec per LocalDate non viene utilizzato quando si tenta di codificare DutyBlock oggetti, anche se ho verificato che la collezione che sto cercando di memorizzare il org.bson.Document in contenga in effetti il ​​LocalDateCodec tramite un

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

Può chiunque c fornire alcune informazioni sul perché questo sta accadendo?

risposta

19

Dopo diversi giorni di ricerca, ho trovato una soluzione.

Il DutyBlockCodec dipende dallo LocalDateCodec (che ho creato) per codificare/decodificare. Questa dipendenza non è soddisfatta solo aggiungendo i due codec nello stesso registro di codec. La soluzione consiste nel passare un oggetto contenente i codec che il DutyBlockCodec dipende (ad esempio un CodecRegistry che contiene al suo interno lo LocalDateCodec) nel costruttore di DutyBlockCodec, che è memorizzato come variabile membro. Per utilizzare la codifica LocalDateCodec, utilizzo il metodo EncoderContext.encodeWithChildContext(), passando il codec, il writer e l'elemento da codificare. Inoltre, scrivo singoli campi piuttosto che scrivere uno Document come String (come nel mio codice originale). Così il codec DutyBlock finisce per guardare in questo modo:

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 dipende da un altro codec, e quindi richiede un CodecRegistry per essere passato in su suo costruttore.Mentre credo che sia possibile creare un LocalDateCodec, passare questo come argomento al costruttore DutyBlockCodec, quindi creare un altro CodecRegistry contenente sia LocalDateCodec e DutyBlockCodec, questo è piuttosto confuso, e MongoDB fornisce una funzionalità, lo CodecProvider a facilitare questo processo.

Utilizzando l'interfaccia CodecProvider, ho scritto un 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; 
    } 
} 

ho aggiunto questi CodecProviders al Cliente MongoDB utilizzando il metodo CodecRegistries.fromProviders().

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

mio codice sorgente per questo progetto sono disponibili all'indirizzo https://github.com/desrepair/DutyScheduler Sono aperto a rispondere a qualsiasi domanda le persone possono avere.

+2

+1 Grazie mille! Funziona come previsto. Penso che mongoDB non possa pensare a un modo più complicato per farlo. Hanno raggiunto la massima complessità possibile – Li3ro

Problemi correlati