问题描述
异常是在mongo执行存储函数向数据库写入数据的时候发生的,业务伪代码如下:
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的方式事项映射转换,版本升级之后即不可使用,出现如下异常:
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)
问题分析
跟进源码查看异常的抛出点可知,是因为在执行过程中拿不到编解码转换器的原因导致。异常抛出点源码如下:
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的映射支持。
解决方案
首先需要注册bson的POJO编解码器;
CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(),
fromProviders(PojoCodecProvider.builder().automatic(true).build()))
在使用的时候可以选择如下任意一种方式:
- 在创建MongoClient实例的时候设置pojoCodecRegistry
MongoClientSettings settings = MongoClientSettings.builder()
.codecRegistry(pojoCodecRegistry)
.build();
MongoClient mongoClient = MongoClients.create(settings);
- 在使用MongoDatabase实例的时候设置pojoCodecRegistry
database = database.withCodecRegistry(pojoCodecRegistry);
- 在使用MongoCollection实例的时候设置pojoCodecRegistry
collection = collection.withCodecRegistry(pojoCodecRegistry);
这里选择是使用通过在使用MongoDatabase实例的时候设置pojoCodecRegistry的方式,业务伪代码改变如下:
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);