当MongoDB主键为String时,mongoTemplate无法根据id查询的问题

MongoDB推荐使用ObjectId作为主键,但国内的开发都知道,事情往往不如人所愿,当我们真的出现了"_id"主键的类型为String时,且还必须想用mongoTemplate.findOne或findList时,直接使用该方法会导致查询结果为空。

因为mongoTemplate会在查询的时候将主键转换为ObjectId。

实际上这一步转换是MappingMongoConverter中做的,源码如下:

java 复制代码
	/**
	 * Converts the given raw id value into either {@link ObjectId} or {@link String}.
	 *
	 * @param id can be {@literal null}.
	 * @param targetType must not be {@literal null}.
	 * @return {@literal null} if source {@literal id} is already {@literal null}.
	 * @since 2.2
	 */
@Nullable
	default Object convertId(@Nullable Object id, Class<?> targetType) {

		if (id == null || ClassUtils.isAssignableValue(targetType, id)) {
			return id;
		}

		if (ClassUtils.isAssignable(ObjectId.class, targetType)) {

			if (id instanceof String) {

				if (ObjectId.isValid(id.toString())) {
					return new ObjectId(id.toString());
				}

				// avoid ConversionException as convertToMongoType will return String anyways.
				return id;
			}
		}

		try {
			return getConversionService().canConvert(id.getClass(), targetType)
					? getConversionService().convert(id, targetType)
					: convertToMongoType(id, (TypeInformation<?>) null);
		} catch (ConversionException o_O) {
			return convertToMongoType(id,(TypeInformation<?>)  null);
		}
	}

注意示例版本为spring-data-mongodb:3.3.5,一些更古早的版本,如2.1.9,需要重写其他部分,如果有人需要请留言,我会抽时间补充,但鉴于之前的版本有比较重大的漏洞,所以推荐升级版本。

if (ObjectId.isValid(id.toString())) { return new ObjectId(id.toString()); }

这段就是mongoTemplate将String转换为ObjectId的步骤。

所以我们要做的就是重写这部分,并且替换掉原有的:

1、新建一个Converter

java 复制代码
public class MyMongoConverter extends MappingMongoConverter {
    public MyMongoConverter(DbRefResolver dbRefResolver, MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
        super(dbRefResolver, mappingContext);
    }

    @Override
    public Object convertId(@Nullable Object id, Class<?> targetType) {
        if (id == null || ClassUtils.isAssignableValue(targetType, id)) {
            return id;
        }

        if (ClassUtils.isAssignable(ObjectId.class, targetType)) {
            if (id instanceof String) {
                return id;
            }
        }

        try {
            return getConversionService().canConvert(id.getClass(), targetType)
                    ? getConversionService().convert(id, targetType)
                    : convertToMongoType(id, (TypeInformation<?>) null);
        } catch (ConversionException o_O) {
            return convertToMongoType(id,(TypeInformation<?>)  null);
        }
    }
}

2、在构建MongoTemplate时用我们自己写的替换

java 复制代码
MongoTemolate mongoTemplate = new MongoTemplate(mongoDbFactory(), getDefaultMongoConverter(mongoDbFactory()))

protected static MongoConverter getDefaultMongoConverter(MongoDatabaseFactory factory) {

        DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
        MongoCustomConversions conversions = new MongoCustomConversions(Collections.emptyList());

        MongoMappingContext mappingContext = new MongoMappingContext();
        mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
        mappingContext.afterPropertiesSet();

        MyMongoConverter converter = new MyMongoConverter(dbRefResolver, mappingContext);

        converter.setCustomConversions(conversions);
        converter.afterPropertiesSet();
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));

        return converter;
    }
    /**
     * @return {@link MongoDatabaseFactory}
     */
    protected MongoDatabaseFactory mongoDbFactory() {
        ServerAddress serverAddress = new ServerAddress(host, port);

        MongoClientSettings.Builder setting = MongoClientSettings.builder()
                .applyToClusterSettings(builder -> builder.hosts(Collections.singletonList(serverAddress)))
                .retryWrites(false);
        if (ObjectUtil.isNotEmpty(username)) {
            MongoCredential credential = MongoCredential.createCredential(username, authenticationDatabase, password.toCharArray());
            setting.credential(credential);
        }
        MongoClient mongoClient = MongoClients.create(setting.build());
        return new SimpleMongoClientDatabaseFactory(mongoClient, database);
    }

host,port,username,authenticationDatabase,password,database

分别为MongoDB的地址,端口号,用户名,认证数据库,密码,数据库名

相关推荐
字节渡客5 分钟前
Redis键明明过期了,业务还在读到旧数据
数据库·redis·spring
OnlineProxy2 小时前
亚马逊多账号运营:如何在规避“关联封号”风险的同时实现电商规模化扩张
服务器·数据库·redis
辻弋2014 小时前
五年前的旅行视频糊成马赛克?Video2X用Real-ESRGAN逐帧重建细节,但只支持Windows、集显用户建议直接放弃
服务器·数据库·windows·游戏引擎·电脑
可乐鸡翅yeah_4 小时前
业务中 M3U8 水印相关坑,硬水印和动态水印区别
前端·网络·数据库·ffmpeg·m3u8在线
石头麻辣鱼5 小时前
Bamboo 调度系统 OceanBase 适配实战:存储过程迁移踩过的三个大坑
数据库
龙亘川5 小时前
数智驱动民政升级 精准守护民生保障
大数据·数据库·人工智能
念何架构之路5 小时前
zap扩展生态与总结
java·前端·数据库
晚安日记wanna6 小时前
MySQL 回表为什么这么慢?5 种优化手段逐个拆解
数据库·mysql·性能优化
第七页独白6 小时前
汽车零件厂如何通过 QMS 真正落地 IATF 16949——QMS软件系统:品质检验-内审稽核-8d客诉管理:全星质量管理软件系统
java·前端·数据库
晚安日记wanna6 小时前
索引建了却不走?7 个失效原因逐层排查
数据库·mysql·性能优化