Overview
在本指南中,您可以了解 Java Reactive Streams 驱动程序中的编解码器和支持类,这些类处理 Java 对象与 BSON 数据之间的编码和解码。Codec 抽象允许您将任何 Java 类型映射到相应的 BSON 类型。您可以使用此抽象将域对象直接映射到 BSON,或从 BSON 直接映射到域对象,而无需使用 Document 或 BsonDocument 等基于中间映射的对象。
在以下部分中了解如何使用 Codec 抽象类来指定自定义编码和解码逻辑:
要了解如何自定义普通旧式 Java 对象 (POJO) 的编码和解码逻辑,请参阅 使用 POJO 对数据进行模型 指南。
实现编解码器
Codec 接口包含用于将 Java 对象序列化和反序列化为 BSON 数据的抽象方法。在此接口的实现中定义 BSON 与 Java 对象之间的转换逻辑。
要实现 Codec 接口,请覆盖 encode()、 decode() 和 getEncoderClass() 抽象方法。
encode() 方法需要使用以下参数:
Parameter Type | 说明 |
|---|---|
| 实现 |
| 实施所编码的数据。该类型必须与分配给您的实施的类型变量相匹配。 |
| 包含编码为 BSON 的 Java 对象数据的元数据,包括是否将当前值存储在 MongoDB 集合中。 |
此方法使用 BsonWriter 实例将编码值发送到 MongoDB,并且不返回值。
decode() 方法返回使用 BSON 数据中的值填充的 Java 对象实例。此方法需要以下参数:
Parameter Type | 说明 |
|---|---|
| 实现 |
| 包含有关解码为 Java 对象的 BSON 数据的信息。 |
getEncoderClass() 方法返回该 Java 类的一个类实例,因为 Java 由于类型擦除而无法推断类型。
例子
以下代码示例展示了如何实现自定义 Codec。
ReadStatus枚举包含值READ和UNREAD,用以表示书籍是否已阅读。
public enum ReadStatus { READ, UNREAD }
ReadStatusCodec 类实现 Codec,以便将 Java enum 值转换为相应的 BSON 布尔值。encode() 方法将 ReadStatus 转换为 BSON 布尔值,decode() 方法则执行相反方向的转换。
public class ReadStatusCodec implements Codec<ReadStatus> { public void encode(BsonWriter writer, ReadStatus value, EncoderContext encoderContext) { writer.writeBoolean(value.equals(ReadStatus.READ) ? Boolean.TRUE : Boolean.FALSE); } public ReadStatus decode(BsonReader reader, DecoderContext decoderContext) { return reader.readBoolean() ? ReadStatus.READ : ReadStatus.UNREAD; } public Class<ReadStatus> getEncoderClass() { return ReadStatus.class; } }
您可以将 ReadStatusCodec 实例添加到 CodecRegistry,其中包含 Codec 与其所应用的 Java Realm 对象类型之间的映射。继续访问本页的 CodecRegistry 部分,看看如何包含您的 Codec。
有关此部分中的类和接口的更多信息,请参阅以下 API 文档:
使用 CodecRegistry
CodecRegistry 是 Codec 实例的不可变集合,这些实例对其指定的 Java 类进行编码和解码。您可以使用以下任一 CodecRegistries 类静态工厂方法,从关联类型中包含的 Codec 实例构造 CodecRegistry:
fromCodecs()fromProviders()fromRegistries()
以下代码片段演示如何使用 fromCodecs() 方法构建 CodecRegistry:
CodecRegistry codecRegistry = CodecRegistries.fromCodecs(new IntegerCodec(), new ReadStatusCodec());
在前面的示例中,CodecRegistry包含以下 Codec 实现:
IntegerCodec,一个用于转换Integers的Codec,并且是 BSON 软件包的一部分。ReadStatusCodec,我们的示例
Codec,可将 Java 枚举值转换为 BSON 布尔值。
您可以使用以下代码从上一示例中的 CodecRegistry 实例检索 Codec 实例:
Codec<ReadStatus> readStatusCodec = codecRegistry.get(ReadStatus.class); Codec<Integer> integerCodec = codecRegistry.get(Integer.class);
如果您尝试检索未注册的类的 Codec 实例,则 get() 方法会抛出 CodecConfigurationException。
有关此部分中的类和接口的更多信息,请参阅以下 API 文档:
使用 CodecProvider
CodecProvider 是接口,其中包含创建 Codec 实例并将其分配给 CodecRegistry 实例的抽象方法。与 CodecRegistry 类似,BSON 库使用 get() 方法检索的 Codec 实例在 Java 和 BSON 数据类型之间进行转换。
但是,如果您添加的类包含需要对应 Codec 对象的字段,则必须在实例化该类的 Codec 之前为类字段实例化 Codec 对象。您可以使用 get() 方法中的 CodecRegistry 参数来传递 Codec 依赖的任何 Codec 实例。
以下代码示例展示了如何实现 CodecProvider 以将其在 CodecRegistry 实例(例如前面示例中的 ReadStatusCodec)所需的任何 Codec 实例传递给 BookCodec:
public class BookCodecProvider implements CodecProvider { public BookCodecProvider() {} public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) { if (clazz == Book.class) { return (Codec<T>) new BookCodec(registry); } // return null when not a provider for the requested class return null; } }
要查看演示使用这些 Codec 类进行读写操作的可运行示例,请参阅本指南的自定义编解码示例一节。
使用 POJO 时,请考虑使用 PojoCodecProvider 最大限度地减少重复代码,以转换常用数据类型并自定义其行为。有关更多信息,请参阅我们的使用 POJO 建模数据指南。
使用默认编解码器注册表
默认编解码器注册表是一组 CodecProvider 类,用于指定常用 Java 和 MongoDB 类型之间的转换。除非您指定不同的注册表,否则驱动程序会自动使用默认的编解码器注册表。
如果必须覆盖一个或多个 Codec 类的行为,但保留其他类的默认编解码器注册表中的行为,则可以按优先顺序指定所有注册表。例如,要使用自定义 MyEnumCodec 覆盖枚举类型的 Codec 的默认提供商行为,请在默认编解码器注册表之前将其添加到注册表中,如以下示例所示:
CodecRegistry newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new MyEnumCodec()), MongoClientSettings.getDefaultCodecRegistry());
有关此部分中的类和接口的更多信息,请参阅以下 API 文档部分:
自定义类型映射
BsonTypeClassMap 类包含 BSON 和 Java 类型之间的推荐映射。可以在自定义 Codec 或 CodecProvider 中使用此类来帮助管理哪些 Java 类型可将 BSON types 解码为能够实现 Iterable 或 Map 的容器类,例如 Document 类。
您可以通过传递包含新条目或替换条目的 Map 来添加或修改 BsonTypeClassMap 默认映射。
以下代码片段显示了如何检索与默认 BsonTypeClassMap 实例中的 BSON 类型相对应的 Java 类型:
BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(); Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY); System.out.println("Java type: " + clazz.getName());
此代码输出以下内容:
Java type: java.util.List
可以通过在 BsonTypeClassMap 构造函数中指定替换项来修改实例中的这些映射。以下示例展示了如何将 BsonTypeClassMap 实例中 ARRAY 的映射替换为 Set 类:
Map<BsonType, Class<?>> replacements = new HashMap<BsonType, Class<?>>(); replacements.put(BsonType.ARRAY, Set.class); BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(replacements); Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY); System.out.println("Java type: " + clazz.getName());
此代码输出以下内容:
Java type: java.util.Set
请参阅 BsonTypeClassMap API 文档,获取默认映射的完整列表。
提示
有关 Document 类如何使用 BsonTypeClassMap 的示例,请参阅 DocumentCodecProvider 和 DocumentCodec 类的驱动程序源代码。
自定义编解码器示例
本节介绍如何实现 Codec 和 CodecProvider 来定义自定义 Java 类的编码和解码逻辑。它还展示如何指定和使用自定义实现来执行插入和检索操作。
以下示例定义了一个名为 Book 的自定义类及其字段,用于在 MongoDB 集合中存储和检索:
public class Book { private String title; private ReadStatus readStatus = ReadStatus.UNREAD; private Integer pageCount; public Book() {} // ...
此类包含以下字段,每个字段都需要 Codec:
title包含一个String值,对于该值,示例使用 BSON 库中包含的StringCodec。readStatus描述一本书是否已读,其中示例使用自定义的ReadStatusCodec将枚举值转换为 BSON 布尔值。pageCount包含一个Integer值,对于该值,示例使用 BSON 库中包含的IntegerCodec。
以下代码示例展示如何为 Book 类实现 Codec。请注意,构造函数需要 CodecRegistry 实例,从中检索对其字段进行编码和解码所需的 Codec 实例:
public class BookCodec implements Codec<Book> { private Codec<String> stringCodec; private Codec<ReadStatus> readStatusCodec; private Codec<Integer> integerCodec; public BookCodec(CodecRegistry registry) { this.stringCodec = registry.get(String.class); this.readStatusCodec = registry.get(ReadStatus.class); this.integerCodec = registry.get(Integer.class); } // Defines an encode() method to convert Book field values to BSON values public void encode(BsonWriter writer, Book value, EncoderContext encoderContext) { writer.writeStartDocument(); writer.writeName("title"); stringCodec.encode(writer, value.getTitle(), encoderContext); writer.writeName("readStatus"); readStatusCodec.encode(writer, value.getReadStatus(), encoderContext); writer.writeName("pageCount"); integerCodec.encode(writer, value.getPageCount(), encoderContext); writer.writeEndDocument(); } // Defines a decode() method to convert BSON values to Book field values public Book decode(BsonReader reader, DecoderContext decoderContext) { Book book = new Book(); reader.readStartDocument(); while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { String fieldName = reader.readName(); if (fieldName.equals("title")) { book.setTitle(stringCodec.decode(reader, decoderContext)); } else if (fieldName.equals("readStatus")) { book.setReadStatus(readStatusCodec.decode(reader, decoderContext)); } else if (fieldName.equals("pageCount")) { book.setPageCount(integerCodec.decode(reader, decoderContext)); } else if (fieldName.equals("_id")) { reader.readObjectId(); } else { reader.skipValue(); } } reader.readEndDocument(); return book; } // Returns an instance of the Book class, since Java cannot infer the class type public Class<Book> getEncoderClass() { return Book.class; } }
为了确保字段的 Codec 实例可用于 Book,我们实现了以下代码示例中所示的自定义 CodecProvider:
public class BookCodecProvider implements CodecProvider { public BookCodecProvider() {} public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) { if (clazz == Book.class) { return (Codec<T>) new BookCodec(registry); } // return null when not a provider for the requested class return null; } }
定义转换逻辑后,您可以执行以下操作:
将来自
Book实例的数据存储到 MongoDB 中将 MongoDB 中的数据提到实例
Book
以下示例类中包含的代码通过将 BookCodecProvider 传递给 withCodecRegistry() 方法,从而将其赋值给 MongoCollection 实例。该示例类还使用 Book 类和关联的编解码器插入和检索数据:
public class BookCodecExample { public static void main(String[] args) { String uri = "<MongoDB connection URI>"; try (MongoClient mongoClient = MongoClients.create(uri)) { CodecRegistry codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new ReadStatusCodec()), CodecRegistries.fromProviders(new BookCodecProvider()), MongoClientSettings.getDefaultCodecRegistry()); MongoDatabase database = mongoClient.getDatabase("codecs_example_db"); MongoCollection<Book> collection = database.getCollection("books", Book.class) .withCodecRegistry(codecRegistry); // construct and insert an instance of Book Book myBook = new Book(); myBook.setTitle("The Hobbit"); myBook.setReadStatus(ReadStatus.READ); myBook.setPageCount(310); Mono.from(collection.insertOne(myBook)).block(); // retrieve one or more instances of Book Flux.from(collection.find()) .doOnNext(System.out::println) .blockLast(); } } }
上述示例的输出如下所示:
Book [title=The Hobbit, readStatus=READ, pageCount=310]
API 文档
有关本指南中提到的方法和类的详情,请参阅以下 API 文档: