Spring 使用 mongo 系列
第二章 Spring Data mongo 使用之去掉’_class’字段
🌈 前言
上一篇文章解释到使用 Spring Data Mongo 存储时,’_class‘字段的作用。这篇我们看看是否能删除掉这个字段,若能删除,那是否还能正确转换成目标类。
1️⃣ ‘_class’ 字段是在哪添加的
在 Spring Data Mongo的配置类中,有一个类叫做 MappingMongoConverter,该类的描述为:
“Mongo转换器,它使用映射上下文来完成对象到文档的复杂映射。”
其类中有一个字段叫做 typeMapper.
👍 MappingMongoConverter
public class MappingMongoConverter {
private TypeMapper typeMapper;
/**
* 这个方法配置了 Mongo 的类型映射器。
*/
public void setTypeMapper(MongoTypeMapper typeMapper){
this.typeMapper = typeMapper == null
? new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY,mappingContext)
: typeMapper;
}
}
从上面方法我们可以看到该方法配置了 Mongo 的类型映射器,而默认的 DefaultMongoTypeMapper.DEFAULT_TYPE_KEY = “_class”
👉 查看默认的类型键名称
public class DefaultMongoTypeMapper extends DefaultTypeMapper<Bson> implements MongoTypeMapper {
public static final String DEFAULT_TYPE_KEY = "_class";
}
2️⃣ 尝试自定义类型键名称为: clazz
既然在 Mongo 的配置类里,那么我们尝试自定义类型键名称
1. 编辑配置类
/**
* 更改默认的 MongoDB 映射转换器,更改'_class'字段
* @param factory MongoDB 工厂对象
* @param context MongoDB 映射上下文
* @param beanFactory springBean 工厂
* @return MongoDB 映射转换器
*/
@Bean
public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, BeanFactory beanFactory) {
DbRefResolver defaultDbRefResolver = new DefaultDbRefResolver(factory);
MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(defaultDbRefResolver, context);
try {
mappingMongoConverter.setCustomConversions(beanFactory.getBean(CustomConversions.class));
} catch (NoSuchBeanDefinitionException ignore) {
}
mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper("clazz"));
return mappingMongoConverter;
}
2. 保存一个 Person 对象
/**
* 用于测试: insert
*/
@Test
public void test_insert() {
Person entity = new Person("张三", 20, "123321200201011111");
Person person = personRepository.save(entity);
logger.info("测试结果: {}", JSON.toJSONString(person));
}
2022-11-11 17:47:37.353 INFO 32688 --- [main] com.tu.mongodb.test.TestApi: 测试结果: {"age":20,"idNumber":"123321200201011111","name":"张三"}
我们发现是可以自定义类型映射 key 值的
3️⃣ 尝试取消类型键
1. 将刚刚的 “clazz” 替换为 null
@Bean
public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, BeanFactory beanFactory) {
DbRefResolver defaultDbRefResolver = new DefaultDbRefResolver(factory);
MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(defaultDbRefResolver, context);
try {
mappingMongoConverter.setCustomConversions(beanFactory.getBean(CustomConversions.class));
} catch (NoSuchBeanDefinitionException ignore) {
}
mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
return mappingMongoConverter;
}
2. 保存一个 Person 对象
2022-11-11 18:24:07.677 INFO 35051 --- [main] com.tu.mongodb.test.TestApi: 测试结果: {"age":20,"idNumber":"123321200201011113","name":"王五"}
3. 保存一个 Student 对象![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/eba78cb6309ca5124f7c4bcf743dbe74.png)
4. 通过上一章的方法获取 Student 对象
2022-11-11 18:27:24.414 INFO 35241 [main] com.tu.mongodb.test.TestApi: 查询结果 absent 是否为 Student 类: false
2022-11-11 18:27:24.556 INFO 35241 [main] com.tu.mongodb.test.TestApi: 测试结果: {"age":20,"idNumber":"123321200201021114","name":"赵六"}
我们发现,当前已经不能正确转换成 Student 类了
5. 使用另一种方式尝试获取 Student 对象
/**
* 根据 idNumber 获取,显式执行返回值类型,但必须继承自指定的 Repository<T,ID> 中的 T
* @param idNumber 身份证号
* @param t 返回值类型
* @return 返回值
* @param <T> {@link Person} 的子类
*/
<T extends Person> T findByIdNumber(String idNumber,Class<T> t);
2022-11-11 21:16:08.142 INFO 35758 [main] com.tu.mongodb.test.TestApi: 查询结果 absent 是否为 Student 类: true
2022-11-11 21:16:08.228 INFO 35758 [main] com.tu.mongodb.test.TestApi: 测试结果: {"age":20,"idNumber":"123321200201021114","name":"赵六","studentNo":"01010102"}
🌈 结语
我们可以通过配置,来自定义 typeKey 或者删除他。
注意:
1. 删除 typeKey 后,MongoRepository 内置方法将只能获取到指定的 T 类型,若需要得到 T 类型的子类,需要显式注明 Class 类型。
2. 若更换 typeKey,则其他访问该文档的应用程序也需要指定为当前的 typeKey,否则不能正常获取。
文末附上 demo 地址:github 项目地址