ejemplos consultas avanzadas java mongodb encoding bson

java - consultas - mongodb ejemplos



El códec MongoDB BSON no se está utilizando al codificar un objeto (1)

Estoy intentando almacenar un objeto en una base de datos MongoDB (utilizando MongoDB 3.0.2) y CodecConfigurationException una CodecConfigurationException cuando intento codificar el objeto con un mensaje de error

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

He escrito e incluido un códec para los objetos LocalDate . Los detalles siguen.

El objeto, DutyBlock , que estoy intentando almacenar tiene estas variables miembro:

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

Escribí el siguiente códec para codificar los objetos DutyBlock dentro de la base de datos:

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

Como actualmente MongoDB no admite la java.time.LocalDate class , he escrito el siguiente códec para codificar los objetos LocalDate dentro de la base de datos:

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

He agregado los dos Codec ''s (junto con uno para el tipo Ra ) al CodecRegistry en el nivel MongoClient mientras se crea una instancia de 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) . . }

Intento almacenar un ArrayList de objetos DutyBlock como parte de un org.bson.Document dentro de la base de datos 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. }

Sin embargo, estoy corriendo en este mensaje de error:

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)

Parece que mi códec para LocalDate no se está utilizando al intentar codificar objetos DutyBlock , aunque he verificado que la colección en la que estoy intentando almacenar el org.bson.Document contiene el LocalDateCodec través de

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

¿Alguien puede darnos una idea de por qué sucede esto?


Después de varios días de investigación, he descubierto una solución.

El DutyBlockCodec depende de LocalDateCodec (que creé) para codificar / decodificar. Esta dependencia no se satisface simplemente agregando los dos códecs en el mismo registro de códecs. La solución es pasar un objeto CodecRegistry que contenga los códecs de los que depende DutyBlockCodec (por ejemplo, un CodecRegistry contenga el LocalDateCodec ) al constructor de DutyBlockCodec , que se almacena como una variable miembro. Para usar LocalDateCodec para codificar, utilizo el método EncoderContext.encodeWithChildContext() , pasando el códec, el escritor y el elemento para codificar. Además, escribo campos individuales en lugar de escribir un Document como una String (como en mi código original). Así, el códec DutyBlock termina pareciéndose a esto:

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 depende de otro códec y, por lo tanto, requiere que CodecRegistry se transmita a su constructor. Si bien creo que es posible crear un CodecRegistry con LocalDateCodec , luego pasar esto como un argumento al DutyBlockCodec de DutyBlockCodec , luego crear otro CodecRegistry contenga tanto LocalDateCodec como a DutyBlockCodec , esto es bastante confuso, y MongoDB proporciona una funcionalidad, Facilitar este proceso.

Usando la interfaz de CodecProvider , escribí 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; } }

CodecProviders estos CodecProviders al cliente MongoDB usando el método 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");

Puede encontrar mi código fuente para este proyecto en https://github.com/desrepair/DutyScheduler . Estoy abierto a responder cualquier pregunta que pueda tener la gente.