使用Micronaut为MongoDB注册编解码器

4
我正在使用Micronaut和MongoDB创建一个新应用程序。关于我的一些数据库对象,我遇到了一些停滞不前的问题。
我有一个包含枚举字段的对象,我需要将其转换为可以保存的值(计划将其持久化为字符串)。
从我所了解的和收到的错误消息来看,我需要创建一个新编解码器来处理这个问题(类似于JPA转换器)。尽管我找到了一些示例,但我仍然有点困惑,不知道我应该如何为MongoClient注册转换器。
我使用最新版本的Micronaut框架和相应的MongoClient。
以下是我参考的异常:
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$1$1.request(ObservableToPublisher.java:50)
    at io.reactivex.internal.operators.flowable.FlowableToListSingle$ToListSubscriber.onSubscribe(FlowableToListSingle.java:84)
    at com.mongodb.reactivestreams.client.internal.ObservableToPublisher$1.onSubscribe(ObservableToPublisher.java:39)
    at com.mongodb.async.client.MongoIterableSubscription.<init>(MongoIterableSubscription.java:40)
    at com.mongodb.async.client.Observables$1.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)
2个回答

4

我认为创建一个有编解码器的类并使其可注入应该就可以了:

@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驱动程序文档,解释了Codecs: https://mongodb.github.io/mongo-java-driver/3.11/bson/codecs/

codecs的注入发生在这里。

io.micronaut.configuration.mongo.reactive.DefaultReactiveMongoConfiguration#codecs

如果代码无法正常工作,您应该能够从那里进行调试

附注:我没有测试上面的代码,但它应该能给您一个想法


非常感谢。你建议的方法非常有效。说实话,我没想到这么容易做到。可惜他们的文档里没有这样的例子。 - akortex
1
@Aris_Kortex 很高兴它对你有用。Micronaut是一个开源框架。如果您愿意,您可以提供更多的文档。其他人也会从您的学习中受益。 - saw303
@Marco,对我来说这个不起作用。你是如何注册编解码器的?https://stackoverflow.com/questions/68559101/org-bson-codecs-configuration-codecconfigurationexception-cant-find-a-codec-fo - San Jaisy

2
MongoDB的Java客户端在处理枚举类型时不一致,这个问题仍未解决,可以参考链接JAVA-2720
对于出现在对象属性中的枚举类型,有自动的EnumPropertyCodecProvider编解码器实现,可以直接使用。但是,如果你在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());
        }
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接