前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何解决读取BsonUndefined的问题

如何解决读取BsonUndefined的问题

作者头像
dhyuan
发布2022-11-08 12:49:42
9500
发布2022-11-08 12:49:42
举报
文章被收录于专栏:响应式编程响应式编程

因为MongoDB数据中有的字段值为'undefined',程序程序访问到这个数据时会抛出如下类似的exception:

代码语言:javascript
复制
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type org.bson.BsonUndefined to type XXXXX.

我们可以通过以下几步解决这个问题:

1)我们首先需要分析是什么情况导致数据中存在undefined值。

从BSON的规范 https://bsonspec.org/spec.html 看,undefined已经是depricated。数据库中出现undefined的多半说明程序有问题,所以需要找出是在哪里、什么情况下向MongoDB写入了undefined value并进行修改。

2)如果这个问题仅存在于开发/测试环境,而不是遗留数据导致。

那么修正代码出错的地方并清理数据库中的脏数据就可以了。

3)如果这种脏数据是遗留数据并且在生产环境也是存在的。

那么我们可以通过添加一个converter类

代码语言:javascript
复制
BsonUndefinedToNullObjectConverterFactory implements ConverterFactory<BsonUndefined, Object>

把undefined 转为任意类型对象的null,就可以避免如上的Exception。代码如stackoverflow 上的[这篇帖子](https://stackoverflow.com/questions/37066690/no-converter-found-capable-of-converting-from-type-org-bson-bsonundefined)。

为了把Converter 传给mongoTemplate,我们需要定义一个MongoCustomConversions Bean。为什么需要这样的Bean,可以参考 AbstractMongoClientConfiguration的源代码。另外,因为AbstractMongoClientConfiguration中已经定义了一个MongoCustomConversions bean,我们就需要给自己的Bean加上@Primary,以便让spring-data-mongo优先使用我们设置了自定义converter的 MongoCustomConversions bean。

如果你的spring application还是通过XML方式进行beans定义与组装的,那么你就不能通过定义一个ConverterFactory来方便地把undefined 转为__任意类型__对象的null了。这是因为XML不支持类型化参数。这时,只能把converter一个个地定义出来。XML的组装大致如下:

代码语言:javascript
复制
<mongo:mapping-converter id="mappingConverter" >
    <mongo:custom-converters>
        <mongo:converter>
            <bean class="your.package.UndefinedToLongNullReadConverter"/>
        </mongo:converter>
        <mongo:converter>
            <bean class="your.package.UndefinedToStringNullReadConverter"/>
        </mongo:converter>
    </mongo:custom-converters>
</mongo:mapping-converter>

4)到这里即使数据库里有脏数据,程序也能‘愉快’地运行了。问题似乎已经被彻底解决了,其实没有。

因为生产环境的脏数据还没有被清理,我们现在只是容忍了脏数据的存在。在当前微服务架构下,这样的数据可能会被多个不同的微服务访问到,这就意味着这些微服务都要使用如上所述的一个converter才能避免exception。我们有必要发现这些脏数据存在的位置,并进行清理。

通过在converter 返回 o -> null 之前,执行下面的代码就可以通过log看到是哪个DAO触发了这个转换,进而可以分析出哪个collection存在脏数据。如果我们清理了这个collection的所有脏数据之后这种undefined脏数据还是会产生出来,那么我们就应该好好review一下之前的代码是哪里有问题并进行修改了。

代码语言:javascript
复制
StackTraceElement[] causes = Thread.currentThread().getStackTrace();
for(StackTraceElement st : causes){
    if (st.toString().indexOf("YOUR_DAO_PACKAGE") >= 0) {
        log.warn(st.toString());
    } else {
        log.info(st.toString());
    }
}

如果是使用JDK9及以上,那么可以使用StackWalker避免getStackTrace()的性能损耗。可以参考 https://stackoverflow.com/questions/2347828/how-expensive-is-thread-getstacktrace。

写了个Demo来复现并解决这个问题,代码可参考[这里](https://github.com/dhyuan/demo_projects/tree/master/mongo_testcontainer)。


Reference: https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mapping-chapter

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 响应式编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档