本篇内容介绍了“spring-data-elasticsearch和Jackson配合使用有什么bug”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
下面先简单描述项目。
项目依赖:
dependencies { implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-elasticsearch', version: '2.1.0.RELEASE' testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '2.1.0.RELEASE' testCompile group: 'junit', name: 'junit', version: '4.12' }
ES索引结构:
{ "test_log": { "mappings": { "_doc": { "properties": { "id": { "type": "keyword" }, "log_type": { "type": "keyword" } } } } } }
注意如果不加 @JsonProperty 注解,保存时会向 ES 添加新的 'logType' 字段(视 mapping 的 dynamic 配置而定,默认是true)。POJO如下:
@Document(indexName="test_log", type="_doc") public class TestLog { @Id private long id; @JsonProperty("log_type") private String logType; // setters & getters }
TestLogRepository:
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import org.springframework.stereotype.Repository; @Repository public interface TestLogRepository extends ElasticsearchRepository<TestLog, Long> {}
测试用例很简单:
@Test public void testSave() { testLogRepository.save(new TestLog(System.currentTimeMillis(), "test name 1")); }
测试结果是虽然向ES添加了一条新的记录,但 log_type 字段为空。
郁闷了一会后开始跟踪源码。先将整个过程分为初始化、序列化、写入三个阶段。写入肯定没有问题,因为是 ES client 直接向ES写生成好的JSON,那么问题就出在前边。
先来看初始化过程,其关键调用链描述如下:
1. spring 扫描自定义的 Repository;
2. 使用 ElasticsearchRepositoryFactoryBean 来初始化 TestLogReposity 的实例;
3. 在 AbstractMappingContext 的 addPersistentEntity 方法中将类型信息(TestLog)添加到 MappingContext( AbstractMappingContext )的 persistentEntities 的私有属性中(HashMap结构,里边的变量是 SimpleElasticsearchPersistentEntity 实例)。
4. 第3步中会在写 persistentEntities 时遍历 TestLog 所有 properties,将字段名保存到 PersitentEntity 的 propertyCache(ArrayList)中。我们需要关注的结构就是 context-> persistentEntities -> propertyCache, 这个 propertyCache 先简单表示为 ['id', 'logType']。
JSON序列化过程中关键调用链如下:
1. 调用 DefaultEntityMapper 的 mapToString;
2. 调用 ObjectMapper 的 writeValueAsString;
3. 调用 DefaultSerializerProvider 的 serializeValue;
4. 序列化 TestLog 时, Jackson发现从各种缓存中都找不到序列化器,只好构造一个,即调用 BeanSerializerFactory 的 createSerializer;
5. createSerializer 时会扫描 TestLog 中各式注解,如 @JsonProperty、@JsonSetter、@JsonGetter 以及 @JsonNaming,使用注解的值来构造要序列化的属性列表。如果没有注解,就直接使用字段名。本例中的 props 列表形为 ['id', 'log_type'];
6. 到第5步还一切OK,但是最后 Jackson 会回调 SpringDataSerializerModifier 对 props 进行修改,SpringDataSerializerModifier 直接从上面我们初始化好的 context-> persistentEntities -> propertyCache 一路查下来,发现 log_type 匹配不到 propertyCache 列表中的任何值,直接删掉。。。返回待序列化的字段列表只包含 [ 'id' ] 一个值!关键代码就在 BeanSerializerFactory 的 constructBeanSerializer,和 SpringDataSerializerModifier 的 changeProperties 方法中。源码就不贴了,有兴趣的同学自己去查。
到这里已经真相大白了,笔者特意去查了 spring-data-elasticsearch 的 issue 列表。发现还真有此类问题:DATAES-550、DATAES-562。
要补充的是试过 springboot 2.1.0.RELEASE、2.1.5.RELEASE、2.1.9.RELEASE都不行,官方的解决方案是使用 spring-data-elasticsearch 3.2.x以上版本。但一来此版本要求的ES是6.8以上,二来是和项目中其它地方有奇怪的冲突,尝试了一段时间遂放弃。最后还是使用了自定义的 ObjectMapper 和 ElasticsearchTemplate 来手动写入:
public void save(TestLog log) { IndexQuery indexQuery = new IndexQueryBuilder().withIndexName("test_log").withType("_doc").withId(log.getId()).withSource(objectMapper.writeValueAsString(log)).build(); elasticsearchTemplate.index(indexQuery); }
“spring-data-elasticsearch和Jackson配合使用有什么bug”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。