0%

mongo找不到pojo编解码转换器问题

问题描述

异常是在mongo执行存储函数向数据库写入数据的时候发生的,业务伪代码如下:

1
2
3
4
5
Document dbObject = new Document();
dbObject.append("$eval", "function(x1,x2,x3){return statisticsFunction(x1, x2, x3);}")
.append("args", Arrays.asList(arg1, arg2, arg3))
.append("nolock", true);
Document result = mongoTemplate.executeCommand(dbObject);

在没有升级mongo版本之前,POJO通过继承ReflectionDBObject的方式事项映射转换,版本升级之后即不可使用,出现如下异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class ...

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.ChildCodecRegistry.get(ChildCodecRegistry.java:51)
at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:184)
at org.bson.codecs.DocumentCodec.writeIterable(DocumentCodec.java:207)
at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:180)
at org.bson.codecs.DocumentCodec.writeIterable(DocumentCodec.java:207)
at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:180)
at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:199)
at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:141)
at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:45)

问题分析

跟进源码查看异常的抛出点可知,是因为在执行过程中拿不到编解码转换器的原因导致。异常抛出点源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
final class CodecCache {
private final ConcurrentMap<Class<?>, Optional<? extends Codec<?>>> codecCache =
new ConcurrentHashMap<Class<?>, Optional<? extends Codec<?>>>();

public boolean containsKey(final Class<?> clazz) {
return codecCache.containsKey(clazz);
}

public void put(final Class<?> clazz, final Codec<?> codec){
codecCache.put(clazz, Optional.of(codec));
}

@SuppressWarnings("unchecked")
public <T> Codec<T> getOrThrow(final Class<T> clazz) {
if (codecCache.containsKey(clazz)) {
Optional<? extends Codec<?>> optionalCodec = codecCache.get(clazz);
if (!optionalCodec.isEmpty()) {
return (Codec<T>) optionalCodec.get();
}
}
throw new CodecConfigurationException(format("Can't find a codec for %s.", clazz));
}
}

ReflectionDBObject 是mongo一种老的POJO映射方式,所使用的POJO需要继承它,在新版本已不适用,目前bson提供了POJO的映射支持。

解决方案

参考mongo官方文档

首先需要注册bson的POJO编解码器;

1
2
CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(),
fromProviders(PojoCodecProvider.builder().automatic(true).build()))

在使用的时候可以选择如下任意一种方式:

  • 在创建MongoClient实例的时候设置pojoCodecRegistry

    1
    2
    3
    4
    MongoClientSettings settings = MongoClientSettings.builder()
    .codecRegistry(pojoCodecRegistry)
    .build();
    MongoClient mongoClient = MongoClients.create(settings);
  • 在使用MongoDatabase实例的时候设置pojoCodecRegistry

    1
    database = database.withCodecRegistry(pojoCodecRegistry);
  • 在使用MongoCollection实例的时候设置pojoCodecRegistry

    1
    collection = collection.withCodecRegistry(pojoCodecRegistry);

这里选择是使用通过在使用MongoDatabase实例的时候设置pojoCodecRegistry的方式,业务伪代码改变如下:

1
2
3
4
5
Document dbObject = new Document();
dbObject.append("$eval", "function(x1,x2,x3){return statisticsFunction(x1, x2, x3);}")
.append("args", Arrays.asList(arg1, arg2, arg3))
.append("nolock", true);
Document result = mongoTemplate.getDb().withCodecRegistry(pojoCodecRegistry).runCommand(dbObject);