解决 Spring Boot 对 Elasticsearch 字段没有小驼峰映射的问题
场景重现
在使用 MyBatis/Mybatis-Plus 框架对 MySQL 操作时习惯了字段名小驼峰映射,然而在操作 Elasticsearch 时发现字段名没有小驼峰映射。
解决方法
1. 使用 `ObjectMapper` 手动转换:
这是最直接也最常用的方法。 在 Spring Boot 应用中使用 `ObjectMapper` 将从 Elasticsearch 获取的数据进行转换,将下划线命名转换为小驼峰命名。
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;@Service
public class ElasticsearchService {@Autowiredprivate ObjectMapper objectMapper; // 通过 @Bean 配置 ObjectMapperpublic Map<String, Object> getDocument(String index, String id) {// 假设你已经有了从 Elasticsearch 获取文档的方法,这里是模拟Map<String, Object> document = new HashMap<>();document.put("first_name", "John");document.put("last_name", "Doe");// 将下划线命名转换为小驼峰命名ObjectMapper snakeCaseMapper = new ObjectMapper();snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);ObjectMapper camelCaseMapper = new ObjectMapper();camelCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE);try {String jsonString = snakeCaseMapper.writeValueAsString(document);return camelCaseMapper.readValue(jsonString, Map.class);} catch (Exception e) {// 处理异常e.printStackTrace();return null;}}
}
解释:
• `ObjectMapper` 配置: 在 Spring Boot 应用中,你需要配置一个 `ObjectMapper` Bean。
• `PropertyNamingStrategy`: 通过设置 `PropertyNamingStrategy`,`ObjectMapper` 可以自动将下划线命名转换为小驼峰命名。 这里先使用`snakeCaseMapper`读取,再使用`camelCaseMapper`写出。
• 转换过程: 从 Elasticsearch 获取的数据(`Map`)使用 `ObjectMapper`序列化成 JSON 字符串,然后再反序列化成 `Map`。 在这个过程中,`PropertyNamingStrategy` 会自动进行命名转换。
配置 `ObjectMapper` Bean:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ObjectMapperConfig {@Beanpublic ObjectMapper objectMapper() {ObjectMapper objectMapper = new ObjectMapper();// 可以根据需要配置 ObjectMapper 的其他属性return objectMapper;}
}
2. 使用 Elasticsearch 的 `field_alias` (推荐但ES版本要支持):
从 Elasticsearch 7.0 开始,引入了 `field_alias`。 你可以利用这个功能,在 Elasticsearch 索引的 Mapping 中定义字段别名,将下划线命名的字段映射到小驼峰命名的字段。
PUT my_index
{"mappings": {"properties": {"first_name": {"type": "alias","path": "first_name"},"last_name": {"type": "alias","path": "last_name"}}}
}
注意: `field_alias` 只是别名,它仍然需要你存储实际的下划线命名的字段。 这样做的好处是,你可以在查询时使用小驼峰命名,但底层存储仍然是下划线命名。
3. 自定义 Elasticsearch 客户端 (不推荐):
你可以自定义 Elasticsearch 客户端,拦截响应结果,并手动进行字段命名转换。 这通常比较复杂,需要你深入了解 Elasticsearch 客户端的内部机制。不建议使用这种方法,因为它维护成本高。
4. 定义 DTO 类并使用 `@JsonProperty` 注解:
创建一个 DTO 类,对应 Elasticsearch 中的文档结构,并在 DTO 类的字段上使用 `@JsonProperty` 注解,将 Elasticsearch 的字段名映射到 DTO 类的字段上。
import com.fasterxml.jackson.annotation.JsonProperty;public class UserDTO {@JsonProperty("first_name")private String firstName;@JsonProperty("last_name")private String lastName;// Getters and setters
}
然后,从 Elasticsearch 获取数据后,将数据映射到 `UserDTO` 对象。
5. 使用 Elasticsearch 插件 (不推荐):
有一些第三方 Elasticsearch 插件可以自动进行字段命名转换。 但使用插件会增加系统的复杂性,并且可能存在兼容性问题,因此不建议使用。
选择哪种方法?
- 优先考虑
field_alias
(如果你的 Elasticsearch 版本支持): 它是最优雅的解决方案,对现有代码的改动最小。 ObjectMapper
手动转换: 如果你无法修改 Elasticsearch 的 Mapping,那么使用ObjectMapper
是一个不错的选择。 它简单易用,并且可以灵活地控制转换过程。@JsonProperty
注解: 如果你的数据结构相对固定,并且需要将其映射到 DTO 对象,那么使用@JsonProperty
注解是一个好主意。- 避免自定义客户端和插件: 除非你有非常特殊的需求,否则不建议使用自定义客户端和插件,因为它们维护成本很高。