繁体   English   中英

MongoDB:注册编解码器(Java)

[英]MongoDB: Register codecs (Java)

我已经尝试了大约一个小时来注册我为我正在开发的游戏中的一个课程制作的编解码器。 class 称为Item 我尝试了这三个地方的代码和建议:

这是我想出的代码:

CodecRegistry defaultCodecRegistry = MongoClient.getDefaultCodecRegistry();
MyCodecProvider myCodecProvider = new MyCodecProvider();
ItemCodec itemCodec = new ItemCodec(defaultCodecRegistry);

CodecRegistry codecRegistry = CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(itemCodec), CodecRegistries.fromProviders(myCodecProvider), defaultCodecRegistry);;
MongoClientOptions options = MongoClientOptions.builder().codecRegistry(codecRegistry).build();

client = new MongoClient("localhost:27017", options);

所以我建立了一个名为MyCodecProvider的编解码器和编解码器提供程序,那我做错了什么,怎么会这么复杂? 我错过了什么吗? 它似乎比它需要的更复杂。 如果您需要更多代码,请询问。 谢谢。

编辑:我得到的确切错误是org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class [Lnet.hollowbit.archipeloserver.items.Item;. 另外,我正在尝试解析一个 Item 数组,我是否也需要专门为该数组制作一个编解码器?

您可以使用 ArrayList 为数组执行 Mongo 编解码器,如下所示:

com.example.model.Order

这是代表订单的类。 它包括一个项目的 ArrayList。

package com.example.model;

import java.util.ArrayList;
import org.bson.types.ObjectId;

/**
 * Class representing an Order.
 */
public class Order
{
    private ObjectId id;
    private ArrayList<Item> items;

    /**
     * Default constructor. Needed for testing.
     */
    public Order() {
        this.items = new ArrayList<>();
    }

    /**
     * Sets the id of the Order.
     * @param id The new id of the Order.
     */
    public void setId(ObjectId id) {
        this.id = id;
    }

    /**
     * Gets the id of the Order.
     * @return The id of the Order.
     */
    public ObjectId getId() {
        return this.id;
    }

    /**
     * Sets the items for the Order.
     * @param items The items for the Order.
     */
    public void setItems(ArrayList<Item> items) {
        this.items = items;
    }

    /**
     * Gets the items for the Order.
     * @return The items for the Order.
     */
    public ArrayList<Item> getItems() {
        return items;
    }

    /**
     * Adds an item to the Order.
     * @param item The new Item to add to the Order.
     */
    public void addItem(Item item) {
        this.items.add(item);
    }
}

com.example.model.Item

这是表示订单项的类。 可以有任意数量的商品作为订单的一部分。 项目嵌入在 Mongo 的订单文档中。

package com.example.model;

import org.bson.types.ObjectId;

/**
 * Class representing an order item.
 */
public class Item
{
    private ObjectId id;
    private String name;

    /**
     * Constructor.
     */
    public Item() {
        //
    }

    /**
     * Sets the id of the Item.
     * @param id The new id of the Item.
     */
    public void setId(ObjectId id) {
        this.id = id;
    }

    /**
     * Gets the id of the Item.
     * @return The id of the Item.
     */
    public ObjectId getId() {
        return this.id;
    }

    /**
     * Sets the name of the Item.
     * @param name The new name of the Item.
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Gets the name of the Item.
     * @return The name of the Item.
     */
    public String getName() {
        return this.name;
    }
}

com.example.mongo.ItemConverter

将项目转换为/从文档转换的简单转换器类。

package com.example.mongo;

import com.example.model.Item;
import org.bson.Document;

/**
 * Converts Mongo Documents to/from Items.
 */
public class ItemConverter {
    /**
     * Convert the passed Item into a Mongo Document.
     * @param item The Item that you want to convert into a Mongo Document.
     * @return Returns the Document that was created from the passed Item.
     */
    public Document convert(Item item) {
        Document document = new Document();
        document.put("_id", item.getId());
        document.put("name", item.getName());

        return document;
    }

    /**
     * Convert the passed Mongo Document into an Item.
     * @param document The Document that you want to convert into an Item.
     * @return Returns the Item that was created from the passed Mongo Document.
     */
    public Item convert(Document document) {
        Item item = new Item();
        item.setId(document.getObjectId("_id"));
        item.setName(document.getString("name"));

        return item;
    }
}

com.example.mongo.ItemCodec

用于编码和解码项目的编解码器。

package com.example.mongo;

import com.example.model.Item;
import com.mongodb.MongoClient;
import org.bson.BsonReader;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.BsonWriter;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.CollectibleCodec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.types.ObjectId;

/**
 * Mongo Decoder for Items.
 */
public class ItemCodec implements CollectibleCodec<Item> {

    private final CodecRegistry registry;
    private final Codec<Document> documentCodec;
    private final ItemConverter converter;

    /**
     * Default constructor.
     */
    public ItemCodec() {
        this.registry = MongoClient.getDefaultCodecRegistry();
        this.documentCodec = this.registry.get(Document.class);
        this.converter = new ItemConverter();
    }

    /**
     * Codec constructor.
     * @param codec The existing codec to use.
     */
    public ItemCodec(Codec<Document> codec) {
        this.documentCodec = codec;
        this.registry = MongoClient.getDefaultCodecRegistry();
        this.converter = new ItemConverter();
    }

    /**
     * Registry constructor.
     * @param registry The CodecRegistry to use.
     */
    public ItemCodec(CodecRegistry registry) {
        this.registry = registry;
        this.documentCodec = this.registry.get(Document.class);
        this.converter = new ItemConverter();
    }

    /**
     * Encode the passed Item into a Mongo/BSON document.
     * @param writer The writer to use for encoding.
     * @param item The Item to encode.
     * @param encoderContext The EncoderContext to use for encoding.
     */
    @Override
    public void encode(
                    BsonWriter writer,
                    Item item,
                    EncoderContext encoderContext
                ) {
        Document document = this.converter.convert(item);

        documentCodec.encode(writer, document, encoderContext);
    }

    /**
     * Get the class that this Codec works with.
     * @return Returns the class that this Codec works with.
     */
    @Override
    public Class<Item> getEncoderClass() {
        return Item.class;
    }

    /**
     * Decodes a Mongo/BSON document into an Item.
     * @param reader The reader containing the Document.
     * @param decoderContext The DecoderContext to use for decoding.
     * @return Returns the decoded Item.
     */
    @Override
    public Item decode(BsonReader reader, DecoderContext decoderContext) {
        Document document = documentCodec.decode(reader, decoderContext);
        Item item = this.converter.convert(document);

        return item;
    }

    /**
     * Generates a new ObjectId for the passed Item (if absent).
     * @param item The Item to work with.
     * @return Returns the passed Item with a new id added if there
     * was none.
     */
    @Override
    public Item generateIdIfAbsentFromDocument(Item item) {
        if (!documentHasId(item)) {
            item.setId(new ObjectId());
        }

        return item;
    }

    /**
     * Returns whether or not the passed Item has an id.
     * @param Item The Item that you want to check for
     * the presence of an id.
     * @return Returns whether or not the passed Item has an id.
     */
    @Override
    public boolean documentHasId(Item Item) {
        return (Item.getName() != null);
    }

    /**
     * Gets the id of the passed Item. If there is no id, it will
     * throw an IllegalStateException (RuntimeException).
     * @param Item The Item whose id you want to get.
     * @return Returns the id of the passed Item as a BsonValue.
     */
    @Override
    public BsonValue getDocumentId(Item Item)
    {
        if (!documentHasId(Item)) {
            throw new IllegalStateException("The document does not contain an _id");
        }

        return new BsonString(Item.getName());
    }

}

com.example.mongo.OrderCodec

用于编码/解码订单的编解码器。

package com.example.mongo;

import com.example.model.Item;
import com.example.model.Order;
import com.mongodb.MongoClient;
import java.util.ArrayList;
import org.bson.BsonReader;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.BsonWriter;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.CollectibleCodec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.types.ObjectId;

/**
 * Mongo decoder for Orders.
 */
public class OrderCodec implements CollectibleCodec<Order> {

    private final CodecRegistry registry;
    private final Codec<Document> documentCodec;
    private final ItemConverter itemConverter;

    /**
     * Default constructor.
     */
    public OrderCodec() {
        this.registry = MongoClient.getDefaultCodecRegistry();
        this.documentCodec = this.registry.get(Document.class);
        this.itemConverter = new ItemConverter();
    }

    /**
     * Codec constructor.
     * @param codec The existing codec to use.
     */
    public OrderCodec(Codec<Document> codec) {
        this.registry = MongoClient.getDefaultCodecRegistry();
        this.documentCodec = codec;
        this.itemConverter = new ItemConverter();
    }

    /**
     * Registry constructor.
     * @param registry The CodecRegistry to use.
     */
    public OrderCodec(CodecRegistry registry) {
        this.registry = registry;
        this.documentCodec = this.registry.get(Document.class);
        this.itemConverter = new ItemConverter();
    }

    /**
     * Encode the passed Order into a Mongo/BSON document.
     * @param writer The writer to use for encoding.
     * @param order The Order to encode.
     * @param encoderContext The EncoderContext to use for encoding.
     */
    @Override
    public void encode(
                    BsonWriter writer,
                    Order order,
                    EncoderContext encoderContext
                ) {
        Document document = new Document();
        document.put("_id", order.getId());
        document.put("items", order.getItems());

        documentCodec.encode(writer, document, encoderContext);
    }

    /**
     * Get the class that this Codec works with.
     * @return Returns the class that this Codec works with.
     */
    @Override
    public Class<Order> getEncoderClass() {
        return Order.class;
    }

    /**
     * Decodes a Mongo/BSON document into an Order.
     * @param reader The reader containing the Document.
     * @param decoderContext The DecoderContext to use for decoding.
     * @return Returns the decoded Order.
     */
    @Override
    public Order decode(BsonReader reader, DecoderContext decoderContext) {
        Document document = documentCodec.decode(reader, decoderContext);

        Order order = new Order();

        order.setId(document.getObjectId("_id"));

        ArrayList<Document> docArr = (ArrayList) document.get("items");
        for (Document doc : docArr) {
            Item item = this.itemConverter.convert(doc);
            order.addItem(item);
        }

        return order;
    }

    /**
     * Generates a new ObjectId for the passed Order (if absent).
     * @param order The Order to work with.
     * @return Returns the passed Order with a new id added if there
     * was none.
     */
    @Override
    public Order generateIdIfAbsentFromDocument(Order order) {
        if (!documentHasId(order)) {
            order.setId(new ObjectId());
        }

        return order;
    }

    /**
     * Returns whether or not the passed Order has an id.
     * @param order The Order that you want to check for
     * the presence of an id.
     * @return Returns whether or not the passed Order has an id.
     */
    @Override
    public boolean documentHasId(Order order) {
        return (order.getId() != null);
    }

    /**
     * Gets the id of the passed Order. If there is no id, it will
     * throw an IllegalStateException (RuntimeException).
     * @param order The Order whose id you want to get.
     * @return Returns the id of the passed Order as a BsonValue.
     */
    @Override
    public BsonValue getDocumentId(Order order) {
        if (!documentHasId(order)) {
            throw new IllegalStateException("The document does not contain an _id");
        }

        return new BsonString(order.getId().toHexString());
    }
}

com.example.main.Main

应用程序的主类。 在这里我们注册编解码器并创建我们的 MongoClient。

package com.example.main;

import com.example.model.Item;
import com.example.model.Order;
import com.example.mongo.ItemCodec;
import com.example.mongo.OrderCodec;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.ServerAddress;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;

/**
 * Main class.
 */
public class Main {

    /**
     * Main function for the app.
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        CodecRegistry codecRegistry = MongoClient.getDefaultCodecRegistry();
        Codec<Document> documentCodec = codecRegistry.get(Document.class);
        Codec<Item> itemCodec = new ItemCodec(codecRegistry);
        Codec<Order> orderCodec = new OrderCodec(codecRegistry);
        codecRegistry = CodecRegistries.fromRegistries(
                                            MongoClient.getDefaultCodecRegistry(),
                                            CodecRegistries.fromCodecs(
                                                documentCodec,
                                                itemCodec,
                                                orderCodec
                                            )
                                        );

        MongoClientOptions options = MongoClientOptions.builder().codecRegistry(codecRegistry).build();
        MongoClient mongo = new MongoClient(new ServerAddress("localhost", 27018), options);

        // Your code here.
    }
}

从那里您可以从 Mongo 读取/写入订单和项目。

更好的方法是实现您自己的 CodecProvider。 这样您就可以管理所有使用的编解码器实现。 使用您的案例:

创建 MyCodecProvider

public class MyCodecProvider implements CodecProvider {

private final BsonTypeClassMap bsonTypeClassMap;

public MyCodecProvider(final BsonTypeClassMap bsonTypeClassMap) {
    this.bsonTypeClassMap = bsonTypeClassMap;
}

@Override
public <T> Codec<T> get(final Class<T> clazz, final CodecRegistry registry) {
    if (clazz == Document.class) {
        // construct DocumentCodec with a CodecRegistry and a BsonTypeClassMap
        return (Codec<T>) new org.bson.codecs.DocumentCodec(registry, bsonTypeClassMap);
    }
    else if (clazz == Order.class) {
        return (Codec<T>) new OrderCodec(registry);
    }
    else if (clazz == Item.class) {
        return (Codec<T>) new ItemCodec(registry);
    }

    return null;
  }
}

现在您可以在主类中简单地执行此操作。

Map<BsonType, Class<?>> replacements = new HashMap<BsonType, Class<?>>();
    replacements.put(BsonType.DATE_TIME, Timestamp.class);

// replacements.put(BsonType.DATE_TIME, Timestamp.class); 如果你有复杂的 java 类型

    BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(replacements);


    MyCodecProvider provider = new MyCodecProvider(bsonTypeClassMap);
    CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(),
            fromProviders(PojoCodecProvider.builder().automatic(true).build()));
    CodecRegistry codecRegistry = MongoClient.getDefaultCodecRegistry();
    Codec<Document> documentCodec = codecRegistry.get(Document.class);

    codecRegistry = CodecRegistries.fromRegistries(
            CodecRegistries.fromProviders(provider),
            CodecRegistries.fromRegistries(pojoCodecRegistry),
            CodecRegistries.fromCodecs(documentCodec));

    MongoClientOptions options = MongoClientOptions.builder().codecRegistry(codecRegistry).build();
    MongoClient mongo = new MongoClient(new ServerAddress("localhost", 27018), options);

其他答案过于复杂。 对于您将使用的绝大多数 POJO 类,您根本不需要编写编解码器。 只需注册您的对象:

// Don't you hate it when sample code does not include references?
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import com.mongodb.MongoClientSettings;

...
{
    CodecRegistry myRegistry = fromRegistries(
                MongoClientSettings.getDefaultCodecRegistry(),
                fromProviders(
                    PojoCodecProvider.builder()
                        .register(Item.class)
                        .build()
                    )
                ));

上面的代码通过将其与默认注册表组合来创建一个新注册表。 这很重要,因为默认注册表涵盖了大量 Java 对象(列表、映射等)。

如果你有很多模型对象怎么办? 没问题:注册整个对象包...

{
    CodecRegistry myRegistry = fromRegistries(
                MongoClientSettings.getDefaultCodecRegistry(),
                fromProviders(
                    PojoCodecProvider.builder()
                        .register("com.funkytown.model.package")
                        .build()
                    )
                ));

那么接下来,你如何使用这个注册表? 你有 3 个选项。

  1. 将其附加到 MongoClient 实例:
MongoClient mongoClient = new MongoClient("localhost", MongoClientOptions.builder().codecRegistry(myRegistry).build());

  1. 将其与特定数据库一起使用:
myDatabase = mongoClient.getDatabase("FunkyTown").withCodecRegistry(myRegistry);
  1. 将其与特定数据库一起使用:
myCollection = mongoClient.getDatabase("FunkyTown").getCollection("Items").withCodecRegistry(myRegistry);

MongoDB 文档不是最好的,但根据我的经验,POJO 功能快速且灵活。

@Bean
public MongoClientSettings mongoClientSettings() {
    MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
            .codecRegistry(createCustomCodecRegistry()).build();
    return mongoClientSettings;
}

@Bean
public CodecRegistry createCustomCodecRegistry() {
    return CodecRegistries.fromRegistries(
            CodecRegistries.fromCodecs(
                    new UuidCodec(UuidRepresentation.STANDARD),
                    new ZonedDateTimeCodec()),
            MongoClientSettings.getDefaultCodecRegistry()
    );
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM