MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

MongoDB BSON-Codec wird beim Codieren des Objekts nicht verwendet

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

Der DutyBlockCodec hängt vom LocalDateCodec ab (die ich erstellt habe), um zu codieren/decodieren. Diese Abhängigkeit wird nicht einfach dadurch erfüllt, dass die beiden Codecs derselben Codec-Registrierung hinzugefügt werden. Die Lösung besteht darin, eine CodecRegistry zu übergeben Objekt, das die Codecs enthält, die DutyBlockCodec abhängig von (z.B. einer CodecRegistry enthält darin den LocalDateCodec ) zum DutyBlockCodec Der Konstruktor von , der als Membervariable gespeichert wird. Um den LocalDateCodec zu verwenden Zum Codieren verwende ich den EncoderContext.encodeWithChildContext() -Methode, wobei der Codec, der Writer und das zu codierende Element übergeben werden. Außerdem schreibe ich einzelne Felder, anstatt ein Document zu schreiben als String (wie in meinem ursprünglichen Code). Also der DutyBlock Codec sieht am Ende so aus:

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 hängt von einem anderen Codec ab und erfordert daher eine CodecRegistry an seinen Konstruktor übergeben werden. Obwohl ich glaube, dass es möglich ist, eine CodecRegistry zu erstellen mit dem LocalDateCodec , dann übergeben Sie dies als Argument an DutyBlockCodec den Konstruktor von und erstellen Sie dann eine weitere CodecRegistry die sowohl LocalDateCodec enthalten und DutyBlockCodec , das ist ziemlich verwirrend, und MongoDB bietet eine Funktionalität, den CodecProviders um diesen Vorgang zu erleichtern.

Verwendung des CodecProviders Schnittstelle habe ich einen DutyBlockCodecProvider geschrieben

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 habe diese CodecProviders hinzugefügt an den MongoDB-Client mithilfe von CodecRegistries.fromProviders() Methode.

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

Meinen Quellcode für dieses Projekt finden Sie unter https://github.com/desrepair/DutyScheduler. Ich bin offen für die Beantwortung aller Fragen, die möglicherweise auftreten.