J'essaie de stocker un objet dans une base de données MongoDB (en utilisant MongoDB 3.0.2) et j'obtiens un CodecConfigurationException
lorsque j'essaie de coder l'objet avec un message d'erreur
Can't find a codec for class Java.time.LocalDate.
J'ai écrit et inclus un codec pour les objets LocalDate
. Les détails suivent.
L'objet, DutyBlock
, que j'essaie de stocker a ces variables membres:
public class DutyBlock {
private LocalDate startDate;
private LocalDate endDate; //Inclusive
private int blockLength;
private double pointValue;
private ArrayList<Ra> assigned;
}
J'ai écrit le codec suivant pour encoder les objets DutyBlock
dans la base de données:
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));
}
}
Puisque MongoDB ne prend actuellement pas en charge le Java.time.LocalDate class
, J'ai écrit le codec suivant pour coder les objets LocalDate
dans la base de données:
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);
}
}
J'ai ajouté les deux Codec
(avec un pour le type Ra
) au CodecRegistry
au niveau MongoClient lors de l'instanciation du 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)
.
.
}
J'essaie de stocker un ArrayList
d'objets DutyBlock
dans le cadre d'un org.bson.Document
dans la base de données 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.
}
Cependant, je rencontre ce message d'erreur:
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)
Il semble que mon codec pour LocalDate
ne soit pas utilisé lors de la tentative de codage des objets DutyBlock
, bien que j'ai vérifié que la collection que j'essaie de stocker le org.bson.Document
in contient en effet le LocalDateCodec
via un
System.out.println(collection.getCodecRegistry().get(LocalDate.class));
Quelqu'un peut-il expliquer pourquoi cela se produit?
Après plusieurs jours de recherche, j'ai trouvé une solution.
Le DutyBlockCodec
dépend du LocalDateCodec
(que j'ai créé) pour encoder/décoder. Cette dépendance n'est pas satisfaite simplement en ajoutant les deux codecs dans le même registre de codecs. La solution consiste à passer un objet CodecRegistry
contenant les codecs dont DutyBlockCodec
dépend (par exemple un CodecRegistry
contenant en son sein le LocalDateCodec
) au DutyBlockCodec
, qui est stocké en tant que variable membre. Afin d'utiliser la LocalDateCodec
pour encoder, j'utilise la méthode EncoderContext.encodeWithChildContext()
, en passant le codec, le scripteur et l'élément à encoder. De plus, j'écris des champs individuels plutôt que d'écrire un Document
en tant que String
(comme dans mon code d'origine). Ainsi, le codec DutyBlock
finit par ressembler à ceci:
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
dépend d'un autre codec, et nécessite donc un CodecRegistry
à transmettre à son constructeur. Bien que je pense qu'il est possible de créer un CodecRegistry
avec le LocalDateCodec
, passez-le comme argument au constructeur de DutyBlockCodec
, puis créez un autre CodecRegistry
contenant à la fois LocalDateCodec
et DutyBlockCodec
, c'est assez déroutant, et MongoDB fournit une fonctionnalité, CodecProvider
pour faciliter ce processus.
En utilisant l'interface CodecProvider
, j'ai écrit 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;
}
}
J'ai ajouté ces CodecProviders
au client MongoDB en utilisant la méthode 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");
Mon code source pour ce projet se trouve à https://github.com/desrepair/DutyScheduler Je suis prêt à répondre à toutes les questions que les gens peuvent avoir.