Skip to content

Commit 2f6fae6

Browse files
authored
Fix handling of @id property in Java records.
Original Pull Request #2757 Closes #2756
1 parent 9abcacb commit 2f6fae6

File tree

6 files changed

+252
-113
lines changed

6 files changed

+252
-113
lines changed

src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchTemplate.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,16 @@ public String doIndex(IndexQuery query, IndexCoordinates indexCoordinates) {
224224
Object queryObject = query.getObject();
225225

226226
if (queryObject != null) {
227-
query.setObject(updateIndexedObject(queryObject, new IndexedObjectInformation(indexResponse.id(),
228-
indexResponse.index(), indexResponse.seqNo(), indexResponse.primaryTerm(), indexResponse.version())));
227+
query.setObject(entityOperations.updateIndexedObject(
228+
queryObject,
229+
new IndexedObjectInformation(
230+
indexResponse.id(),
231+
indexResponse.index(),
232+
indexResponse.seqNo(),
233+
indexResponse.primaryTerm(),
234+
indexResponse.version()),
235+
elasticsearchConverter,
236+
routingResolver));
229237
}
230238

231239
return indexResponse.id();

src/main/java/org/springframework/data/elasticsearch/client/elc/ReactiveElasticsearchTemplate.java

+10-7
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,16 @@ public <T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entitiesPubli
141141
.flatMap(indexAndResponse -> {
142142
T savedEntity = entities.entityAt(indexAndResponse.getT1());
143143
BulkResponseItem response = indexAndResponse.getT2();
144-
updateIndexedObject(savedEntity, new IndexedObjectInformation( //
145-
response.id(), //
146-
response.index(), //
147-
response.seqNo(), //
148-
response.primaryTerm(), //
149-
response.version()));
150-
return maybeCallbackAfterSave(savedEntity, index);
144+
var updatedEntity = entityOperations.updateIndexedObject(
145+
savedEntity, new IndexedObjectInformation( //
146+
response.id(), //
147+
response.index(), //
148+
response.seqNo(), //
149+
response.primaryTerm(), //
150+
response.version()),
151+
converter,
152+
routingResolver);
153+
return maybeCallbackAfterSave(updatedEntity, index);
151154
});
152155
});
153156
}

src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java

+15-45
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import org.springframework.data.elasticsearch.core.routing.RoutingResolver;
5555
import org.springframework.data.elasticsearch.core.script.Script;
5656
import org.springframework.data.elasticsearch.support.VersionInfo;
57-
import org.springframework.data.mapping.PersistentPropertyAccessor;
5857
import org.springframework.data.mapping.callback.EntityCallbacks;
5958
import org.springframework.data.mapping.context.MappingContext;
6059
import org.springframework.data.util.Streamable;
@@ -240,7 +239,11 @@ public <T> Iterable<T> save(Iterable<T> entities, IndexCoordinates index) {
240239
// noinspection unchecked
241240
return indexQueries.stream() //
242241
.map(IndexQuery::getObject) //
243-
.map(entity -> (T) updateIndexedObject(entity, iterator.next())) //
242+
.map(entity -> (T) entityOperations.updateIndexedObject(
243+
entity,
244+
iterator.next(),
245+
elasticsearchConverter,
246+
routingResolver)) //
244247
.collect(Collectors.toList()); //
245248
}
246249

@@ -397,47 +400,6 @@ protected <T> UpdateQuery buildUpdateQueryByEntity(T entity) {
397400

398401
return updateQueryBuilder.build();
399402
}
400-
401-
protected <T> T updateIndexedObject(T entity, IndexedObjectInformation indexedObjectInformation) {
402-
403-
ElasticsearchPersistentEntity<?> persistentEntity = elasticsearchConverter.getMappingContext()
404-
.getPersistentEntity(entity.getClass());
405-
406-
if (persistentEntity != null) {
407-
PersistentPropertyAccessor<Object> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
408-
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
409-
410-
// Only deal with text because ES generated Ids are strings!
411-
if (indexedObjectInformation.id() != null && idProperty != null && idProperty.isReadable()
412-
&& idProperty.getType().isAssignableFrom(String.class)) {
413-
propertyAccessor.setProperty(idProperty, indexedObjectInformation.id());
414-
}
415-
416-
if (indexedObjectInformation.seqNo() != null && indexedObjectInformation.primaryTerm() != null
417-
&& persistentEntity.hasSeqNoPrimaryTermProperty()) {
418-
ElasticsearchPersistentProperty seqNoPrimaryTermProperty = persistentEntity.getSeqNoPrimaryTermProperty();
419-
// noinspection ConstantConditions
420-
propertyAccessor.setProperty(seqNoPrimaryTermProperty,
421-
new SeqNoPrimaryTerm(indexedObjectInformation.seqNo(), indexedObjectInformation.primaryTerm()));
422-
}
423-
424-
if (indexedObjectInformation.version() != null && persistentEntity.hasVersionProperty()) {
425-
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
426-
// noinspection ConstantConditions
427-
propertyAccessor.setProperty(versionProperty, indexedObjectInformation.version());
428-
}
429-
430-
var indexedIndexNameProperty = persistentEntity.getIndexedIndexNameProperty();
431-
if (indexedIndexNameProperty != null) {
432-
propertyAccessor.setProperty(indexedIndexNameProperty, indexedObjectInformation.index());
433-
}
434-
435-
// noinspection unchecked
436-
return (T) propertyAccessor.getBean();
437-
}
438-
return entity;
439-
}
440-
441403
// endregion
442404

443405
// region SearchOperations
@@ -736,7 +698,11 @@ protected void updateIndexedObjectsWithQueries(List<?> queries,
736698
Object queryObject = indexQuery.getObject();
737699

738700
if (queryObject != null) {
739-
indexQuery.setObject(updateIndexedObject(queryObject, indexedObjectInformationList.get(i)));
701+
indexQuery.setObject(entityOperations.updateIndexedObject(
702+
queryObject,
703+
indexedObjectInformationList.get(i),
704+
elasticsearchConverter,
705+
routingResolver));
740706
}
741707
}
742708
}
@@ -802,7 +768,11 @@ public T doWith(@Nullable Document document) {
802768
documentAfterLoad.hasSeqNo() ? documentAfterLoad.getSeqNo() : null, //
803769
documentAfterLoad.hasPrimaryTerm() ? documentAfterLoad.getPrimaryTerm() : null, //
804770
documentAfterLoad.hasVersion() ? documentAfterLoad.getVersion() : null); //
805-
entity = updateIndexedObject(entity, indexedObjectInformation);
771+
entity = entityOperations.updateIndexedObject(
772+
entity,
773+
indexedObjectInformation,
774+
elasticsearchConverter,
775+
routingResolver);
806776

807777
return maybeCallbackAfterConvert(entity, documentAfterLoad, index);
808778
}

src/main/java/org/springframework/data/elasticsearch/core/AbstractReactiveElasticsearchTemplate.java

+15-54
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import org.springframework.data.elasticsearch.core.event.ReactiveAfterSaveCallback;
4444
import org.springframework.data.elasticsearch.core.event.ReactiveBeforeConvertCallback;
4545
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
46-
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
4746
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
4847
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
4948
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
@@ -55,7 +54,6 @@
5554
import org.springframework.data.elasticsearch.core.script.Script;
5655
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
5756
import org.springframework.data.elasticsearch.support.VersionInfo;
58-
import org.springframework.data.mapping.PersistentPropertyAccessor;
5957
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
6058
import org.springframework.lang.NonNull;
6159
import org.springframework.lang.Nullable;
@@ -320,51 +318,6 @@ protected IndexQuery getIndexQuery(Object value) {
320318
return query;
321319
}
322320

323-
protected <T> T updateIndexedObject(T entity, IndexedObjectInformation indexedObjectInformation) {
324-
325-
ElasticsearchPersistentEntity<?> persistentEntity = converter.getMappingContext()
326-
.getPersistentEntity(entity.getClass());
327-
328-
if (persistentEntity != null) {
329-
// noinspection DuplicatedCode
330-
PersistentPropertyAccessor<Object> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
331-
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
332-
333-
// Only deal with text because ES generated Ids are strings!
334-
if (indexedObjectInformation.id() != null && idProperty != null && idProperty.isReadable()
335-
&& idProperty.getType().isAssignableFrom(String.class)) {
336-
propertyAccessor.setProperty(idProperty, indexedObjectInformation.id());
337-
}
338-
339-
if (indexedObjectInformation.seqNo() != null && indexedObjectInformation.primaryTerm() != null
340-
&& persistentEntity.hasSeqNoPrimaryTermProperty()) {
341-
ElasticsearchPersistentProperty seqNoPrimaryTermProperty = persistentEntity.getSeqNoPrimaryTermProperty();
342-
// noinspection ConstantConditions
343-
propertyAccessor.setProperty(seqNoPrimaryTermProperty,
344-
new SeqNoPrimaryTerm(indexedObjectInformation.seqNo(), indexedObjectInformation.primaryTerm()));
345-
}
346-
347-
if (indexedObjectInformation.version() != null && persistentEntity.hasVersionProperty()) {
348-
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
349-
// noinspection ConstantConditions
350-
propertyAccessor.setProperty(versionProperty, indexedObjectInformation.version());
351-
}
352-
353-
var indexedIndexNameProperty = persistentEntity.getIndexedIndexNameProperty();
354-
if (indexedIndexNameProperty != null) {
355-
propertyAccessor.setProperty(indexedIndexNameProperty, indexedObjectInformation.index());
356-
}
357-
358-
// noinspection unchecked
359-
return (T) propertyAccessor.getBean();
360-
} else {
361-
EntityOperations.AdaptableEntity<T> adaptableEntity = entityOperations.forEntity(entity,
362-
converter.getConversionService(), routingResolver);
363-
adaptableEntity.populateIdIfNecessary(indexedObjectInformation.id());
364-
}
365-
return entity;
366-
}
367-
368321
@Override
369322
public <T> Flux<MultiGetItem<T>> multiGet(Query query, Class<T> clazz) {
370323
return multiGet(query, clazz, getIndexCoordinatesFor(clazz));
@@ -391,12 +344,16 @@ public <T> Mono<T> save(T entity, IndexCoordinates index) {
391344
.map(it -> {
392345
T savedEntity = it.getT1();
393346
IndexResponseMetaData indexResponseMetaData = it.getT2();
394-
return updateIndexedObject(savedEntity, new IndexedObjectInformation(
395-
indexResponseMetaData.id(),
396-
indexResponseMetaData.index(),
397-
indexResponseMetaData.seqNo(),
398-
indexResponseMetaData.primaryTerm(),
399-
indexResponseMetaData.version()));
347+
return entityOperations.updateIndexedObject(
348+
savedEntity,
349+
new IndexedObjectInformation(
350+
indexResponseMetaData.id(),
351+
indexResponseMetaData.index(),
352+
indexResponseMetaData.seqNo(),
353+
indexResponseMetaData.primaryTerm(),
354+
indexResponseMetaData.version()),
355+
converter,
356+
routingResolver);
400357
}).flatMap(saved -> maybeCallbackAfterSave(saved, index));
401358
}
402359

@@ -652,7 +609,11 @@ public Mono<T> toEntity(@Nullable Document document) {
652609
documentAfterLoad.hasSeqNo() ? documentAfterLoad.getSeqNo() : null,
653610
documentAfterLoad.hasPrimaryTerm() ? documentAfterLoad.getPrimaryTerm() : null,
654611
documentAfterLoad.hasVersion() ? documentAfterLoad.getVersion() : null);
655-
entity = updateIndexedObject(entity, indexedObjectInformation);
612+
entity = entityOperations.updateIndexedObject(
613+
entity,
614+
indexedObjectInformation,
615+
converter,
616+
routingResolver);
656617

657618
return maybeCallbackAfterConvert(entity, documentAfterLoad, index);
658619
});

src/main/java/org/springframework/data/elasticsearch/core/EntityOperations.java

+64
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Map;
1919

2020
import org.springframework.core.convert.ConversionService;
21+
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
2122
import org.springframework.data.elasticsearch.core.join.JoinField;
2223
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
2324
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
@@ -94,6 +95,69 @@ public <T> AdaptableEntity<T> forEntity(T entity, ConversionService conversionSe
9495
return AdaptableMappedEntity.of(entity, context, conversionService, routingResolver);
9596
}
9697

98+
/**
99+
* Updates an entity after it is stored in Elasticsearch with additional data like id, version, seqno...
100+
*
101+
* @param <T> the entity class
102+
* @param entity the entity to update
103+
* @param indexedObjectInformation the update information
104+
* @param elasticsearchConverter the converter providing necessary mapping information
105+
* @param routingResolver routing resolver to use
106+
* @return
107+
*/
108+
public <T> T updateIndexedObject(T entity,
109+
IndexedObjectInformation indexedObjectInformation,
110+
ElasticsearchConverter elasticsearchConverter,
111+
RoutingResolver routingResolver) {
112+
113+
Assert.notNull(entity, "entity must not be null");
114+
Assert.notNull(indexedObjectInformation, "indexedObjectInformation must not be null");
115+
Assert.notNull(elasticsearchConverter, "elasticsearchConverter must not be null");
116+
117+
ElasticsearchPersistentEntity<?> persistentEntity = elasticsearchConverter.getMappingContext()
118+
.getPersistentEntity(entity.getClass());
119+
120+
if (persistentEntity != null) {
121+
PersistentPropertyAccessor<Object> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
122+
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
123+
124+
// Only deal with text because ES generated Ids are strings!
125+
if (indexedObjectInformation.id() != null && idProperty != null
126+
// isReadable from the base class is false in case of records
127+
&& (idProperty.isReadable() || idProperty.getOwner().getType().isRecord())
128+
&& idProperty.getType().isAssignableFrom(String.class)) {
129+
propertyAccessor.setProperty(idProperty, indexedObjectInformation.id());
130+
}
131+
132+
if (indexedObjectInformation.seqNo() != null && indexedObjectInformation.primaryTerm() != null
133+
&& persistentEntity.hasSeqNoPrimaryTermProperty()) {
134+
ElasticsearchPersistentProperty seqNoPrimaryTermProperty = persistentEntity.getSeqNoPrimaryTermProperty();
135+
// noinspection ConstantConditions
136+
propertyAccessor.setProperty(seqNoPrimaryTermProperty,
137+
new SeqNoPrimaryTerm(indexedObjectInformation.seqNo(), indexedObjectInformation.primaryTerm()));
138+
}
139+
140+
if (indexedObjectInformation.version() != null && persistentEntity.hasVersionProperty()) {
141+
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
142+
// noinspection ConstantConditions
143+
propertyAccessor.setProperty(versionProperty, indexedObjectInformation.version());
144+
}
145+
146+
var indexedIndexNameProperty = persistentEntity.getIndexedIndexNameProperty();
147+
if (indexedIndexNameProperty != null) {
148+
propertyAccessor.setProperty(indexedIndexNameProperty, indexedObjectInformation.index());
149+
}
150+
151+
// noinspection unchecked
152+
return (T) propertyAccessor.getBean();
153+
} else {
154+
EntityOperations.AdaptableEntity<T> adaptableEntity = forEntity(entity,
155+
elasticsearchConverter.getConversionService(), routingResolver);
156+
adaptableEntity.populateIdIfNecessary(indexedObjectInformation.id());
157+
}
158+
return entity;
159+
}
160+
97161
/**
98162
* Determine index name and type name from {@link Entity} with {@code index} and {@code type} overrides. Allows using
99163
* preferred values for index and type if provided, otherwise fall back to index and type defined on entity level.

0 commit comments

Comments
 (0)