mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-05-30 08:42:10 +00:00
Improve handling of immutable classes.
Original Pull Request #1801 Closes #1800
This commit is contained in:
parent
502ce0b6aa
commit
159687e241
@ -188,7 +188,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
IndexQuery query = getIndexQuery(entityAfterBeforeConvert);
|
||||
doIndex(query, index);
|
||||
|
||||
T entityAfterAfterSave = maybeCallbackAfterSave(entityAfterBeforeConvert, index);
|
||||
T entityAfterAfterSave = (T) maybeCallbackAfterSave(query.getObject(), index);
|
||||
|
||||
return entityAfterAfterSave;
|
||||
}
|
||||
@ -215,13 +215,18 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
List<IndexQuery> indexQueries = Streamable.of(entities).stream().map(this::getIndexQuery)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!indexQueries.isEmpty()) {
|
||||
List<IndexedObjectInformation> indexedObjectInformations = bulkIndex(indexQueries, index);
|
||||
Iterator<IndexedObjectInformation> iterator = indexedObjectInformations.iterator();
|
||||
entities.forEach(entity -> updateIndexedObject(entity, iterator.next()));
|
||||
if (indexQueries.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return indexQueries.stream().map(IndexQuery::getObject).map(entity -> (T) entity).collect(Collectors.toList());
|
||||
List<IndexedObjectInformation> indexedObjectInformations = bulkIndex(indexQueries, index);
|
||||
Iterator<IndexedObjectInformation> iterator = indexedObjectInformations.iterator();
|
||||
|
||||
// noinspection unchecked
|
||||
return indexQueries.stream() //
|
||||
.map(IndexQuery::getObject) //
|
||||
.map(entity -> (T) updateIndexedObject(entity, iterator.next())) //
|
||||
.collect(Collectors.toList()); //
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -419,7 +424,9 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
Assert.notNull(query.getId(), "No document id defined for MoreLikeThisQuery");
|
||||
|
||||
MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = requestFactory.moreLikeThisQueryBuilder(query, index);
|
||||
return search(new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).withPageable(query.getPageable()).build(), clazz, index);
|
||||
return search(
|
||||
new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).withPageable(query.getPageable()).build(),
|
||||
clazz, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -611,7 +618,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected void updateIndexedObject(Object entity, IndexedObjectInformation indexedObjectInformation) {
|
||||
protected <T> T updateIndexedObject(T entity, IndexedObjectInformation indexedObjectInformation) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = elasticsearchConverter.getMappingContext()
|
||||
.getPersistentEntity(entity.getClass());
|
||||
@ -621,22 +628,30 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
// Only deal with text because ES generated Ids are strings!
|
||||
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null
|
||||
&& idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, indexedObjectInformation.getId());
|
||||
}
|
||||
|
||||
if (indexedObjectInformation.getSeqNo() != null && indexedObjectInformation.getPrimaryTerm() != null
|
||||
&& persistentEntity.hasSeqNoPrimaryTermProperty()) {
|
||||
ElasticsearchPersistentProperty seqNoPrimaryTermProperty = persistentEntity.getSeqNoPrimaryTermProperty();
|
||||
// noinspection ConstantConditions
|
||||
propertyAccessor.setProperty(seqNoPrimaryTermProperty,
|
||||
new SeqNoPrimaryTerm(indexedObjectInformation.getSeqNo(), indexedObjectInformation.getPrimaryTerm()));
|
||||
}
|
||||
|
||||
if (indexedObjectInformation.getVersion() != null && persistentEntity.hasVersionProperty()) {
|
||||
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
|
||||
// noinspection ConstantConditions
|
||||
propertyAccessor.setProperty(versionProperty, indexedObjectInformation.getVersion());
|
||||
}
|
||||
|
||||
// noinspection unchecked
|
||||
T updatedEntity = (T) propertyAccessor.getBean();
|
||||
return updatedEntity;
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
ElasticsearchPersistentEntity<?> getRequiredPersistentEntity(Class<?> clazz) {
|
||||
@ -807,13 +822,16 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
|
||||
protected void updateIndexedObjectsWithQueries(List<?> queries,
|
||||
List<IndexedObjectInformation> indexedObjectInformations) {
|
||||
|
||||
for (int i = 0; i < queries.size(); i++) {
|
||||
Object query = queries.get(i);
|
||||
|
||||
if (query instanceof IndexQuery) {
|
||||
IndexQuery indexQuery = (IndexQuery) query;
|
||||
Object queryObject = indexQuery.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
updateIndexedObject(queryObject, indexedObjectInformations.get(i));
|
||||
indexQuery.setObject(updateIndexedObject(queryObject, indexedObjectInformations.get(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -848,6 +866,10 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
}
|
||||
|
||||
T entity = reader.read(type, document);
|
||||
IndexedObjectInformation indexedObjectInformation = IndexedObjectInformation.of(
|
||||
document.hasId() ? document.getId() : null, document.getSeqNo(), document.getPrimaryTerm(),
|
||||
document.getVersion());
|
||||
entity = updateIndexedObject(entity, indexedObjectInformation);
|
||||
return maybeCallbackAfterConvert(entity, document, index);
|
||||
}
|
||||
}
|
||||
|
@ -157,9 +157,10 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
IndexResponse indexResponse = execute(client -> client.index(request, RequestOptions.DEFAULT));
|
||||
|
||||
Object queryObject = query.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
updateIndexedObject(queryObject, IndexedObjectInformation.of(indexResponse.getId(), indexResponse.getSeqNo(),
|
||||
indexResponse.getPrimaryTerm(), indexResponse.getVersion()));
|
||||
query.setObject(updateIndexedObject(queryObject, IndexedObjectInformation.of(indexResponse.getId(),
|
||||
indexResponse.getSeqNo(), indexResponse.getPrimaryTerm(), indexResponse.getVersion())));
|
||||
}
|
||||
|
||||
return indexResponse.getId();
|
||||
@ -168,6 +169,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(String id, Class<T> clazz, IndexCoordinates index) {
|
||||
|
||||
GetRequest request = requestFactory.getRequest(id, routingResolver.getRouting(), index);
|
||||
GetResponse response = execute(client -> client.get(request, RequestOptions.DEFAULT));
|
||||
|
||||
|
@ -177,8 +177,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
Object queryObject = query.getObject();
|
||||
if (queryObject != null) {
|
||||
updateIndexedObject(queryObject, IndexedObjectInformation.of(documentId, response.getSeqNo(),
|
||||
response.getPrimaryTerm(), response.getVersion()));
|
||||
query.setObject(updateIndexedObject(queryObject, IndexedObjectInformation.of(documentId, response.getSeqNo(),
|
||||
response.getPrimaryTerm(), response.getVersion())));
|
||||
}
|
||||
|
||||
return documentId;
|
||||
|
@ -477,7 +477,6 @@ class EntityOperations {
|
||||
*/
|
||||
private static class AdaptibleMappedEntity<T> extends MappedEntity<T> implements AdaptibleEntity<T> {
|
||||
|
||||
private final T bean;
|
||||
private final ElasticsearchPersistentEntity<?> entity;
|
||||
private final ConvertingPropertyAccessor<T> propertyAccessor;
|
||||
private final IdentifierAccessor identifierAccessor;
|
||||
@ -490,7 +489,6 @@ class EntityOperations {
|
||||
|
||||
super(entity, identifierAccessor, propertyAccessor);
|
||||
|
||||
this.bean = bean;
|
||||
this.entity = entity;
|
||||
this.propertyAccessor = propertyAccessor;
|
||||
this.identifierAccessor = identifierAccessor;
|
||||
@ -510,6 +508,11 @@ class EntityOperations {
|
||||
new ConvertingPropertyAccessor<>(propertyAccessor, conversionService), conversionService, routingResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getBean() {
|
||||
return propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T populateIdIfNecessary(@Nullable Object id) {
|
||||
@ -584,7 +587,7 @@ class EntityOperations {
|
||||
@Override
|
||||
public String getRouting() {
|
||||
|
||||
String routing = routingResolver.getRouting(bean);
|
||||
String routing = routingResolver.getRouting(propertyAccessor.getBean());
|
||||
|
||||
if (routing != null) {
|
||||
return routing;
|
||||
|
@ -25,12 +25,12 @@ import org.springframework.lang.Nullable;
|
||||
* @since 4.1
|
||||
*/
|
||||
public class IndexedObjectInformation {
|
||||
private final String id;
|
||||
@Nullable private final String id;
|
||||
@Nullable private final Long seqNo;
|
||||
@Nullable private final Long primaryTerm;
|
||||
@Nullable private final Long version;
|
||||
|
||||
private IndexedObjectInformation(String id, @Nullable Long seqNo, @Nullable Long primaryTerm,
|
||||
private IndexedObjectInformation(@Nullable String id, @Nullable Long seqNo, @Nullable Long primaryTerm,
|
||||
@Nullable Long version) {
|
||||
this.id = id;
|
||||
this.seqNo = seqNo;
|
||||
@ -38,11 +38,12 @@ public class IndexedObjectInformation {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public static IndexedObjectInformation of(String id, @Nullable Long seqNo, @Nullable Long primaryTerm,
|
||||
public static IndexedObjectInformation of(@Nullable String id, @Nullable Long seqNo, @Nullable Long primaryTerm,
|
||||
@Nullable Long version) {
|
||||
return new IndexedObjectInformation(id, seqNo, primaryTerm, version);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
@ -275,27 +275,42 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
}
|
||||
|
||||
private <T> T updateIndexedObject(T entity, IndexedObjectInformation indexedObjectInformation) {
|
||||
AdaptibleEntity<T> adaptibleEntity = operations.forEntity(entity, converter.getConversionService(),
|
||||
routingResolver);
|
||||
adaptibleEntity.populateIdIfNecessary(indexedObjectInformation.getId());
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntityFor(entity.getClass());
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = converter.getMappingContext()
|
||||
.getPersistentEntity(entity.getClass());
|
||||
|
||||
if (persistentEntity != null) {
|
||||
PersistentPropertyAccessor<Object> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
// Only deal with text because ES generated Ids are strings!
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null
|
||||
&& idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, indexedObjectInformation.getId());
|
||||
}
|
||||
|
||||
if (indexedObjectInformation.getSeqNo() != null && indexedObjectInformation.getPrimaryTerm() != null
|
||||
&& persistentEntity.hasSeqNoPrimaryTermProperty()) {
|
||||
ElasticsearchPersistentProperty seqNoPrimaryTermProperty = persistentEntity.getSeqNoPrimaryTermProperty();
|
||||
// noinspection ConstantConditions
|
||||
propertyAccessor.setProperty(seqNoPrimaryTermProperty,
|
||||
new SeqNoPrimaryTerm(indexedObjectInformation.getSeqNo(), indexedObjectInformation.getPrimaryTerm()));
|
||||
}
|
||||
|
||||
if (indexedObjectInformation.getVersion() != null && persistentEntity.hasVersionProperty()) {
|
||||
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
|
||||
// noinspection ConstantConditions
|
||||
propertyAccessor.setProperty(versionProperty, indexedObjectInformation.getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
// noinspection unchecked
|
||||
T updatedEntity = (T) propertyAccessor.getBean();
|
||||
return updatedEntity;
|
||||
} else {
|
||||
AdaptibleEntity<T> adaptibleEntity = operations.forEntity(entity, converter.getConversionService(),
|
||||
routingResolver);
|
||||
adaptibleEntity.populateIdIfNecessary(indexedObjectInformation.getId());
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@ -457,7 +472,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
|
||||
DocumentCallback<T> callback = new ReadDocumentCallback<>(converter, entityType, index);
|
||||
|
||||
return doGet(id, index).flatMap(it -> callback.toEntity(DocumentAdapters.from(it)));
|
||||
return doGet(id, index).flatMap(response -> callback.toEntity(DocumentAdapters.from(response)));
|
||||
}
|
||||
|
||||
private Mono<GetResult> doGet(String id, IndexCoordinates index) {
|
||||
@ -1097,6 +1112,10 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
}
|
||||
|
||||
T entity = reader.read(type, document);
|
||||
IndexedObjectInformation indexedObjectInformation = IndexedObjectInformation.of(
|
||||
document.hasId() ? document.getId() : null, document.getSeqNo(), document.getPrimaryTerm(),
|
||||
document.getVersion());
|
||||
entity = updateIndexedObject(entity, indexedObjectInformation);
|
||||
return maybeCallAfterConvert(entity, document, index);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.join;
|
||||
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@ -35,6 +36,7 @@ public class JoinField<ID> {
|
||||
this(name, null);
|
||||
}
|
||||
|
||||
@PersistenceConstructor
|
||||
public JoinField(String name, @Nullable ID parent) {
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
|
@ -35,7 +35,6 @@ import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
import org.springframework.data.mapping.model.FieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
@ -238,19 +237,6 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
||||
getType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.BasicPersistentEntity#setPersistentPropertyAccessorFactory(org.springframework.data.mapping.model.PersistentPropertyAccessorFactory)
|
||||
*/
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
@Override
|
||||
public void setPersistentPropertyAccessorFactory(PersistentPropertyAccessorFactory factory) {
|
||||
|
||||
// Do nothing to avoid the usage of ClassGeneratingPropertyAccessorFactory for now
|
||||
// DATACMNS-1322 switches to proper immutability behavior which Spring Data Elasticsearch
|
||||
// cannot yet implement
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ElasticsearchPersistentProperty getPersistentPropertyWithFieldName(String fieldName) {
|
||||
|
@ -279,11 +279,6 @@ public class SimpleElasticsearchPersistentProperty extends
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImmutable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSeqNoPrimaryTermProperty() {
|
||||
return isSeqNoPrimaryTerm;
|
||||
|
@ -60,7 +60,6 @@ import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||
import org.elasticsearch.search.sort.FieldSortBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilders;
|
||||
import org.elasticsearch.search.sort.SortOrder;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -137,7 +136,8 @@ public abstract class ElasticsearchTemplateTests {
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
indexOperations = operations.indexOps(SampleEntity.class);
|
||||
deleteIndices();
|
||||
|
||||
operations.indexOps(IndexCoordinates.of("*")).delete();
|
||||
|
||||
indexOperations.create();
|
||||
indexOperations.putMapping(SampleEntity.class);
|
||||
@ -155,29 +155,6 @@ public abstract class ElasticsearchTemplateTests {
|
||||
indexOpsJoinEntity.putMapping(SampleJoinEntity.class);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
|
||||
deleteIndices();
|
||||
}
|
||||
|
||||
private void deleteIndices() {
|
||||
|
||||
indexOperations.delete();
|
||||
operations.indexOps(SampleEntityUUIDKeyed.class).delete();
|
||||
operations.indexOps(UseServerConfigurationEntity.class).delete();
|
||||
operations.indexOps(SampleMappingEntity.class).delete();
|
||||
operations.indexOps(Book.class).delete();
|
||||
operations.indexOps(IndexCoordinates.of(INDEX_1_NAME)).delete();
|
||||
operations.indexOps(IndexCoordinates.of(INDEX_2_NAME)).delete();
|
||||
operations.indexOps(IndexCoordinates.of(INDEX_3_NAME)).delete();
|
||||
operations.indexOps(SearchHitsEntity.class).delete();
|
||||
operations.indexOps(HighlightEntity.class).delete();
|
||||
operations.indexOps(OptimisticEntity.class).delete();
|
||||
operations.indexOps(OptimisticAndVersionedEntity.class).delete();
|
||||
operations.indexOps(IndexCoordinates.of(INDEX_NAME_JOIN_SAMPLE_ENTITY)).delete();
|
||||
}
|
||||
|
||||
@Test // DATAES-106
|
||||
public void shouldReturnCountForGivenCriteriaQuery() {
|
||||
|
||||
@ -1126,7 +1103,8 @@ public abstract class ElasticsearchTemplateTests {
|
||||
Collection<String> ids = IntStream.rangeClosed(1, 10).mapToObj(i -> nextIdAsString()).collect(Collectors.toList());
|
||||
ids.add(referenceId);
|
||||
ids.stream()
|
||||
.map(id -> getIndexQuery(SampleEntity.builder().id(id).message(sampleMessage).version(System.currentTimeMillis()).build()))
|
||||
.map(id -> getIndexQuery(
|
||||
SampleEntity.builder().id(id).message(sampleMessage).version(System.currentTimeMillis()).build()))
|
||||
.forEach(indexQuery -> operations.index(indexQuery, index));
|
||||
indexOperations.refresh();
|
||||
|
||||
@ -1141,7 +1119,8 @@ public abstract class ElasticsearchTemplateTests {
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(10);
|
||||
assertThat(searchHits.getSearchHits()).hasSize(5);
|
||||
|
||||
Collection<String> returnedIds = searchHits.getSearchHits().stream().map(SearchHit::getId).collect(Collectors.toList());
|
||||
Collection<String> returnedIds = searchHits.getSearchHits().stream().map(SearchHit::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
moreLikeThisQuery.setPageable(PageRequest.of(1, 5));
|
||||
|
||||
@ -3588,6 +3567,24 @@ public abstract class ElasticsearchTemplateTests {
|
||||
assertThat(explanation).isNotNull();
|
||||
}
|
||||
|
||||
@Test // #1800
|
||||
@DisplayName("should work with immutable classes")
|
||||
void shouldWorkWithImmutableClasses() {
|
||||
|
||||
ImmutableEntity entity = new ImmutableEntity(null, "some text", null);
|
||||
|
||||
ImmutableEntity saved = operations.save(entity);
|
||||
|
||||
assertThat(saved).isNotNull();
|
||||
assertThat(saved.getId()).isNotEmpty();
|
||||
SeqNoPrimaryTerm seqNoPrimaryTerm = saved.getSeqNoPrimaryTerm();
|
||||
assertThat(seqNoPrimaryTerm).isNotNull();
|
||||
|
||||
ImmutableEntity retrieved = operations.get(saved.getId(), ImmutableEntity.class);
|
||||
|
||||
assertThat(retrieved).isEqualTo(saved);
|
||||
}
|
||||
|
||||
// region entities
|
||||
@Document(indexName = INDEX_NAME_SAMPLE_ENTITY)
|
||||
@Setting(shards = 1, replicas = 0, refreshInterval = "-1")
|
||||
@ -4366,5 +4363,61 @@ public abstract class ElasticsearchTemplateTests {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
@Document(indexName = "immutable-class")
|
||||
private static final class ImmutableEntity {
|
||||
@Id private final String id;
|
||||
@Field(type = FieldType.Text) private final String text;
|
||||
@Nullable private final SeqNoPrimaryTerm seqNoPrimaryTerm;
|
||||
|
||||
public ImmutableEntity(@Nullable String id, String text, @Nullable SeqNoPrimaryTerm seqNoPrimaryTerm) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
this.seqNoPrimaryTerm = seqNoPrimaryTerm;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SeqNoPrimaryTerm getSeqNoPrimaryTerm() {
|
||||
return seqNoPrimaryTerm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
ImmutableEntity that = (ImmutableEntity) o;
|
||||
|
||||
if (!id.equals(that.id))
|
||||
return false;
|
||||
if (!text.equals(that.text))
|
||||
return false;
|
||||
return seqNoPrimaryTerm != null ? seqNoPrimaryTerm.equals(that.seqNoPrimaryTerm) : that.seqNoPrimaryTerm == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = id.hashCode();
|
||||
result = 31 * result + text.hashCode();
|
||||
result = 31 * result + (seqNoPrimaryTerm != null ? seqNoPrimaryTerm.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImmutableEntity{" + "id='" + id + '\'' + ", text='" + text + '\'' + ", seqNoPrimaryTerm="
|
||||
+ seqNoPrimaryTerm + '}';
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@ -64,6 +65,7 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.annotations.Mapping;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
|
||||
@ -1149,6 +1151,26 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // #1800
|
||||
@DisplayName("should work with immutable classes")
|
||||
void shouldWorkWithImmutableClasses() {
|
||||
|
||||
ImmutableEntity entity = new ImmutableEntity(null, "some text", null);
|
||||
AtomicReference<ImmutableEntity> savedEntity = new AtomicReference<>();
|
||||
|
||||
template.save(entity).as(StepVerifier::create).consumeNextWith(saved -> {
|
||||
assertThat(saved).isNotNull();
|
||||
savedEntity.set(saved);
|
||||
assertThat(saved.getId()).isNotEmpty();
|
||||
SeqNoPrimaryTerm seqNoPrimaryTerm = saved.getSeqNoPrimaryTerm();
|
||||
assertThat(seqNoPrimaryTerm).isNotNull();
|
||||
}).verifyComplete();
|
||||
|
||||
template.get(savedEntity.get().getId(), ImmutableEntity.class).as(StepVerifier::create)
|
||||
.consumeNextWith(retrieved -> {
|
||||
assertThat(retrieved).isEqualTo(savedEntity.get());
|
||||
}).verifyComplete();
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Helper functions
|
||||
@ -1243,8 +1265,10 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
Message message1 = (Message) o;
|
||||
|
||||
@ -1301,14 +1325,19 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
SampleEntity that = (SampleEntity) o;
|
||||
|
||||
if (rate != that.rate) return false;
|
||||
if (id != null ? !id.equals(that.id) : that.id != null) return false;
|
||||
if (message != null ? !message.equals(that.message) : that.message != null) return false;
|
||||
if (rate != that.rate)
|
||||
return false;
|
||||
if (id != null ? !id.equals(that.id) : that.id != null)
|
||||
return false;
|
||||
if (message != null ? !message.equals(that.message) : that.message != null)
|
||||
return false;
|
||||
return version != null ? version.equals(that.version) : that.version == null;
|
||||
}
|
||||
|
||||
@ -1440,5 +1469,60 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "immutable-class")
|
||||
private static final class ImmutableEntity {
|
||||
@Id private final String id;
|
||||
@Field(type = FieldType.Text) private final String text;
|
||||
@Nullable private final SeqNoPrimaryTerm seqNoPrimaryTerm;
|
||||
|
||||
public ImmutableEntity(@Nullable String id, String text, @Nullable SeqNoPrimaryTerm seqNoPrimaryTerm) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
this.seqNoPrimaryTerm = seqNoPrimaryTerm;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SeqNoPrimaryTerm getSeqNoPrimaryTerm() {
|
||||
return seqNoPrimaryTerm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
ImmutableEntity that = (ImmutableEntity) o;
|
||||
|
||||
if (!id.equals(that.id))
|
||||
return false;
|
||||
if (!text.equals(that.text))
|
||||
return false;
|
||||
return seqNoPrimaryTerm != null ? seqNoPrimaryTerm.equals(that.seqNoPrimaryTerm) : that.seqNoPrimaryTerm == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = id.hashCode();
|
||||
result = 31 * result + text.hashCode();
|
||||
result = 31 * result + (seqNoPrimaryTerm != null ? seqNoPrimaryTerm.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImmutableEntity{" + "id='" + id + '\'' + ", text='" + text + '\'' + ", seqNoPrimaryTerm="
|
||||
+ seqNoPrimaryTerm + '}';
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.IndexOperations;
|
||||
@ -95,11 +96,16 @@ public class ImmutableElasticsearchRepositoryTests {
|
||||
static class ImmutableEntity {
|
||||
private final String id, name;
|
||||
|
||||
public ImmutableEntity(String name) {
|
||||
this.id = null;
|
||||
@PersistenceConstructor
|
||||
public ImmutableEntity(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public ImmutableEntity(String name) {
|
||||
this(null, name);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user