温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

spring-data-elasticsearch和Jackson配合使用有什么bug

发布时间:2021-06-29 09:52:06 来源:亿速云 阅读:257 作者:chen 栏目:大数据

本篇内容介绍了“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”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI