Spring Boot + Spring Data MongoDB 中,MongoRepository方式,自定义 field-naming-strategy,域的名称映射方式
(可直接翻至第五条看解决方案!)
1. 背景
一个Spring Boot + MongoDB +PostgreSQL 的项目
MongoDB
:存了一些外部的现有数据,需要拿来做CRUD,但是这些数据命名不规范
Database
的名字:snake_lower_case
+snake_Upper_Case
+snake_CamelCase
混合Collection & Field
的名字:UPPER_CASE
看了下外部模块代码(C++)挺多的,都是按这种名字写死在代码里的,这又不能让人家改。。(一个尴尬又不失礼貌的微笑.jpg)
2.所用依赖
之前用过MongoDB Java Driver,太基础了,这次用一个对懒人更友好,和spring boot 集成更好的
具体用的是MongoRepository,更死板,约定性非常强,但…jpa真香!
pom.xml
部分信息如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
3.相关代码(已做简化)
@Document(collection = "AREAL_STRUCT_MANAGEMENT")//这一句补充说明一下,可以解决类名和数据库名不同的问题
public class ArealStructManagement {
@Id
private ObjectId id;
private String name;
private String namingTest;
}
mongoDB数据大概长这样
{
"_id":ObjectId("..."),
"NAME":"wine",
"NAMING_TEST":"OK?"
}
4.问题
在application.properties里面配置了:
spring.data.mongodb.field-naming-strategy=org.springframework.data.mapping.model.SnakeCaseFieldNamingStrategy
只解决了一部分问题,把nameTest
换成了name_Test
…也是个混合命名,所以这个配置项真的有用吗。。
Spring只提供了几个命名方式,都不满足需求
所以到底怎么把我写的类中的成员变量名字,在插入/查询数据库的时候自动改成另一种格式??
找了半天资料,找到了自定义的方法。
5.解决方案
在配置文件里声明一个Bean,进行自定义设置,这个地方还能解决插入的数据带_class
的问题,具体方案请参阅其他博客
//配置文件
@Configuration
@EnableMongoRepositories(basePackageClasses = ArealStructManagementDao.class)
public class MongoConfig extends AbstractMongoConfiguration {
@Bean
public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, BeanFactory beanFactory) {
//这句解决问题
context.setFieldNamingStrategy(new UpperCaseWithUnderscoreFieldNamingStrategy());
}
}
然后随便找个地方写一个自己的映射策略
//自定义策略,目的:myName -> MY_NAME
//写的比较糙,可以优化,把delimiter当成参数,参考FieldNamingStrategy的其他实现
public class UpperCaseWithUnderscoreFieldNamingStrategy implements FieldNamingStrategy{
@Override
public String getFieldName(PersistentProperty<?> property) {
//有现成的工具,何乐不为
List<String> parts= ParsingUtils.splitCamelCaseToLower(property.getName());
StringBuilder sb=new StringBuilder();
Iterator it=parts.iterator();
if(it.hasNext()){
sb.append(it.next().toString().toUpperCase());//按需要,转成大写。
while (it.hasNext()){
sb.append("_");
sb.append(it.next().toString().toUpperCase());//按需要,转成大写。
}
}
return sb.toString();
}
}
这样改完之后,测试插入和查询的名字都OK,问题解决!
后记
安利一个新接触的依赖lombok
,以前对这种不感兴趣的,现在发现还真是方便,可以省去大部分getter setter constructor的编写,支持链式set
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>