使用 Micronaut 为 MongoDB 注册编解码器
Registering codecs for MongoDB using Micronaut
我正在使用 Micronaut 和 MongoDB 创建一个新应用程序。关于我的一些数据库对象,我不得不陷入僵局。
我有一个包含枚举字段的对象,我需要将其转换为可以保存的值(我打算将其作为字符串保存)。
根据我的理解和收到的错误消息,我需要创建一个新的编解码器来处理这个问题(类似于 JPA 转换器)。虽然我找到了说明这一点的示例,但我对应该如何为 MongoClient 注册转换器感到有点困惑。
我使用的是框架和相应 MongoClient 的最新 Micronaut 版本。
下面是我得到的例外情况供参考:
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class fts.marketing.models.CampaignEmailStatus.
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 com.mongodb.client.model.BuildersHelper.encodeValue(BuildersHelper.java:37)
at com.mongodb.client.model.Filters$SimpleEncodingFilter.toBsonDocument(Filters.java:1109)
at com.mongodb.client.model.Filters$AndFilter.toBsonDocument(Filters.java:946)
at com.mongodb.internal.operation.Operations.createFindOperation(Operations.java:142)
at com.mongodb.internal.operation.Operations.find(Operations.java:130)
at com.mongodb.internal.operation.AsyncOperations.find(AsyncOperations.java:85)
at com.mongodb.async.client.FindIterableImpl.createFindOperation(FindIterableImpl.java:229)
at com.mongodb.async.client.FindIterableImpl.asAsyncReadOperation(FindIterableImpl.java:225)
at com.mongodb.async.client.MongoIterableImpl.batchCursor(MongoIterableImpl.java:161)
at com.mongodb.async.client.MongoIterableSubscription.requestInitialData(MongoIterableSubscription.java:46)
at com.mongodb.async.client.AbstractSubscription.tryRequestInitialData(AbstractSubscription.java:151)
at com.mongodb.async.client.AbstractSubscription.request(AbstractSubscription.java:84)
at com.mongodb.reactivestreams.client.internal.ObservableToPublisher.request(ObservableToPublisher.java:50)
at io.reactivex.internal.operators.flowable.FlowableToListSingle$ToListSubscriber.onSubscribe(FlowableToListSingle.java:84)
at com.mongodb.reactivestreams.client.internal.ObservableToPublisher.onSubscribe(ObservableToPublisher.java:39)
at com.mongodb.async.client.MongoIterableSubscription.<init>(MongoIterableSubscription.java:40)
at com.mongodb.async.client.Observables.subscribe(Observables.java:47)
at com.mongodb.reactivestreams.client.internal.ObservableToPublisher.subscribe(ObservableToPublisher.java:36)
at com.mongodb.reactivestreams.client.internal.FindPublisherImpl.subscribe(FindPublisherImpl.java:189)
我认为用编解码器创建一个 class 并使其可注入就足够了:
@Singleton
public class EnumCodec implements Codec<CampaignEmailStatus> {
@Override
public CampaignEmailStatus decode(BsonReader reader, DecoderContext decoderContext) {
String enumString = reader.readString();
return CampaignEmailStatus.valueOf(enumString);
}
@Override
public void encode(BsonWriter writer, CampaignEmailStatus value, EncoderContext encoderContext) {
String enumString = value.name();
writer.writeString(enumString);
}
@Override
public Class<CampaignEmailStatus> getEncoderClass() {
return CampaignEmailStatus.class;
}
}
解释编解码器的 Mongo 驱动程序文档在此处:https://mongodb.github.io/mongo-java-driver/3.11/bson/codecs/
编解码器的注入发生在这里
io.micronaut.configuration.mongo.reactive.DefaultReactiveMongoConfiguration#codecs
如果它不工作,你应该可以从那里调试它
PS: 我没有测试上面的代码,但它应该给你一个想法
MongoDB java 客户端在处理枚举方面不一致且仍未修复,请参阅 JAVA-2720。
对于作为对象属性出现的枚举,有自动 EnumPropertyCodecProvider
编解码器实现,开箱即用。但是,如果您将 enum 与 com.mongodb.client.model.Filters
一起使用,则它不起作用并以问题中的异常结束。
为 Micronaut 注册特定枚举编解码器的解决方案可行,但它需要为您要处理的每个枚举定义一个编解码器 bean。这是适用于所有枚举的更通用的解决方案:
@Singleton
public class EnumCodecRegistry implements CodecRegistry {
@SuppressWarnings("unchecked")
@Override
public <T> Codec<T> get(Class<T> clazz) {
if (Enum.class.isAssignableFrom(clazz)) {
return new EnumCodec(clazz);
}
return null;
}
@Override
public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) {
return get(clazz);
}
private static class EnumCodec<T extends Enum<T>> implements Codec<T> {
private final Class<T> clazz;
EnumCodec(final Class<T> clazz) {
this.clazz = clazz;
}
@Override
public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) {
writer.writeString(value.name());
}
@Override
public Class<T> getEncoderClass() {
return clazz;
}
@Override
public T decode(final BsonReader reader, final DecoderContext decoderContext) {
return Enum.valueOf(clazz, reader.readString());
}
}
}
我正在使用 Micronaut 和 MongoDB 创建一个新应用程序。关于我的一些数据库对象,我不得不陷入僵局。
我有一个包含枚举字段的对象,我需要将其转换为可以保存的值(我打算将其作为字符串保存)。
根据我的理解和收到的错误消息,我需要创建一个新的编解码器来处理这个问题(类似于 JPA 转换器)。虽然我找到了说明这一点的示例,但我对应该如何为 MongoClient 注册转换器感到有点困惑。
我使用的是框架和相应 MongoClient 的最新 Micronaut 版本。
下面是我得到的例外情况供参考:
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class fts.marketing.models.CampaignEmailStatus.
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 com.mongodb.client.model.BuildersHelper.encodeValue(BuildersHelper.java:37)
at com.mongodb.client.model.Filters$SimpleEncodingFilter.toBsonDocument(Filters.java:1109)
at com.mongodb.client.model.Filters$AndFilter.toBsonDocument(Filters.java:946)
at com.mongodb.internal.operation.Operations.createFindOperation(Operations.java:142)
at com.mongodb.internal.operation.Operations.find(Operations.java:130)
at com.mongodb.internal.operation.AsyncOperations.find(AsyncOperations.java:85)
at com.mongodb.async.client.FindIterableImpl.createFindOperation(FindIterableImpl.java:229)
at com.mongodb.async.client.FindIterableImpl.asAsyncReadOperation(FindIterableImpl.java:225)
at com.mongodb.async.client.MongoIterableImpl.batchCursor(MongoIterableImpl.java:161)
at com.mongodb.async.client.MongoIterableSubscription.requestInitialData(MongoIterableSubscription.java:46)
at com.mongodb.async.client.AbstractSubscription.tryRequestInitialData(AbstractSubscription.java:151)
at com.mongodb.async.client.AbstractSubscription.request(AbstractSubscription.java:84)
at com.mongodb.reactivestreams.client.internal.ObservableToPublisher.request(ObservableToPublisher.java:50)
at io.reactivex.internal.operators.flowable.FlowableToListSingle$ToListSubscriber.onSubscribe(FlowableToListSingle.java:84)
at com.mongodb.reactivestreams.client.internal.ObservableToPublisher.onSubscribe(ObservableToPublisher.java:39)
at com.mongodb.async.client.MongoIterableSubscription.<init>(MongoIterableSubscription.java:40)
at com.mongodb.async.client.Observables.subscribe(Observables.java:47)
at com.mongodb.reactivestreams.client.internal.ObservableToPublisher.subscribe(ObservableToPublisher.java:36)
at com.mongodb.reactivestreams.client.internal.FindPublisherImpl.subscribe(FindPublisherImpl.java:189)
我认为用编解码器创建一个 class 并使其可注入就足够了:
@Singleton
public class EnumCodec implements Codec<CampaignEmailStatus> {
@Override
public CampaignEmailStatus decode(BsonReader reader, DecoderContext decoderContext) {
String enumString = reader.readString();
return CampaignEmailStatus.valueOf(enumString);
}
@Override
public void encode(BsonWriter writer, CampaignEmailStatus value, EncoderContext encoderContext) {
String enumString = value.name();
writer.writeString(enumString);
}
@Override
public Class<CampaignEmailStatus> getEncoderClass() {
return CampaignEmailStatus.class;
}
}
解释编解码器的 Mongo 驱动程序文档在此处:https://mongodb.github.io/mongo-java-driver/3.11/bson/codecs/
编解码器的注入发生在这里
io.micronaut.configuration.mongo.reactive.DefaultReactiveMongoConfiguration#codecs
如果它不工作,你应该可以从那里调试它
PS: 我没有测试上面的代码,但它应该给你一个想法
MongoDB java 客户端在处理枚举方面不一致且仍未修复,请参阅 JAVA-2720。
对于作为对象属性出现的枚举,有自动 EnumPropertyCodecProvider
编解码器实现,开箱即用。但是,如果您将 enum 与 com.mongodb.client.model.Filters
一起使用,则它不起作用并以问题中的异常结束。
为 Micronaut 注册特定枚举编解码器的解决方案可行,但它需要为您要处理的每个枚举定义一个编解码器 bean。这是适用于所有枚举的更通用的解决方案:
@Singleton
public class EnumCodecRegistry implements CodecRegistry {
@SuppressWarnings("unchecked")
@Override
public <T> Codec<T> get(Class<T> clazz) {
if (Enum.class.isAssignableFrom(clazz)) {
return new EnumCodec(clazz);
}
return null;
}
@Override
public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) {
return get(clazz);
}
private static class EnumCodec<T extends Enum<T>> implements Codec<T> {
private final Class<T> clazz;
EnumCodec(final Class<T> clazz) {
this.clazz = clazz;
}
@Override
public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) {
writer.writeString(value.name());
}
@Override
public Class<T> getEncoderClass() {
return clazz;
}
@Override
public T decode(final BsonReader reader, final DecoderContext decoderContext) {
return Enum.valueOf(clazz, reader.readString());
}
}
}