Skip to content

Generate and convert id on insert if explicitly defined. #4057

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-4026-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data MongoDB</name>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-benchmarks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-4026-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-4026-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-4026-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ public Document getDocument() {
return this.document;
}

/**
* Updates the documents {@link #ID_FIELD}.
*
* @param value the {@literal _id} value to set.
* @since 3.4.1
*/
public void updateId(Object value) {
document.put(ID_FIELD, value);
}

/**
* An {@link UpdateDefinition} that indicates that the {@link #getUpdateObject() update object} has already been
* mapped to the specific domain type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.commons.logging.LogFactory;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
Expand Down Expand Up @@ -83,6 +84,7 @@
import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
import org.springframework.data.mongodb.core.mapping.MongoId;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
Expand Down Expand Up @@ -1392,18 +1394,20 @@ protected Object insertDocument(String collectionName, Document document, Class<
collectionName));
}

MappedDocument mappedDocument = queryOperations.createInsertContext(MappedDocument.of(document)).prepareId(entityClass);

return execute(collectionName, collection -> {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName, entityClass,
document, null);
mappedDocument.getDocument(), null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);

if (writeConcernToUse == null) {
collection.insertOne(document);
collection.insertOne(mappedDocument.getDocument());
} else {
collection.withWriteConcern(writeConcernToUse).insertOne(document);
collection.withWriteConcern(writeConcernToUse).insertOne(mappedDocument.getDocument());
}

return operations.forEntity(document).getId();
return operations.forEntity(mappedDocument.getDocument()).getId();
});
}

Expand Down Expand Up @@ -1454,7 +1458,9 @@ protected Object saveDocument(String collectionName, Document dbDoc, Class<?> en
: collection.withWriteConcern(writeConcernToUse);

if (!mapped.hasId()) {
collectionToUse.insertOne(dbDoc);

mapped = queryOperations.createInsertContext(mapped).prepareId(mappingContext.getPersistentEntity(entityClass));
collectionToUse.insertOne(mapped.getDocument());
} else {

MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.codecs.Codec;

import org.bson.types.ObjectId;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.context.MappingContext;
Expand All @@ -46,6 +46,7 @@
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoId;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.ShardKey;
Expand Down Expand Up @@ -107,6 +108,14 @@ class QueryOperations {
this.aggregationUtil = new AggregationUtil(queryMapper, mappingContext);
}

InsertContext createInsertContext(Document source) {
return createInsertContext(MappedDocument.of(source));
}

InsertContext createInsertContext(MappedDocument mappedDocument) {
return new InsertContext(mappedDocument);
}

/**
* Create a new {@link QueryContext} instance.
*
Expand Down Expand Up @@ -227,6 +236,57 @@ AggregationDefinition createAggregation(Aggregation aggregation,
return new AggregationDefinition(aggregation, aggregationOperationContext);
}

/**
* {@link InsertContext} encapsulates common tasks required to interact with {@link Document} to be inserted.
*
* @since 3.4.1
*/
class InsertContext {

private final MappedDocument source;

private InsertContext(MappedDocument source) {
this.source = source;
}

/**
* Prepare the {@literal _id} field. May generate a new {@literal id} value and convert it to the id properties
* {@link MongoPersistentProperty#getFieldType() target type}.
*
* @param type must not be {@literal null}.
* @param <T>
* @return the {@link MappedDocument} containing the changes.
* @see #prepareId(MongoPersistentEntity)
*/
<T> MappedDocument prepareId(Class<T> type) {
return prepareId(mappingContext.getPersistentEntity(type));
}

/**
* Prepare the {@literal _id} field. May generate a new {@literal id} value and convert it to the id properties
* {@link MongoPersistentProperty#getFieldType() target type}.
*
* @param entity can be {@literal null}.
* @param <T>
* @return the {@link MappedDocument} containing the changes.
*/
<T> MappedDocument prepareId(@Nullable MongoPersistentEntity<T> entity) {

if (entity == null) {
return source;
}

MongoPersistentProperty idProperty = entity.getIdProperty();
if (idProperty != null
&& (idProperty.hasExplicitWriteTarget() || idProperty.isAnnotationPresent(MongoId.class))) {
if (!ClassUtils.isAssignable(ObjectId.class, idProperty.getFieldType())) {
source.updateId(queryMapper.convertId(new ObjectId(), idProperty.getFieldType()));
}
}
return source;
}
}

/**
* {@link QueryContext} encapsulates common tasks required to convert a {@link Query} into its MongoDB document
* representation, mapping field names, as well as determining and applying {@link Collation collations}.
Expand Down Expand Up @@ -288,8 +348,7 @@ <T> Document getMappedQuery(@Nullable MongoPersistentEntity<T> entity) {
return queryMapper.getMappedObject(getQueryObject(), entity);
}

Document getMappedFields(@Nullable MongoPersistentEntity<?> entity,
EntityProjection<?, ?> projection) {
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, EntityProjection<?, ?> projection) {

Document fields = evaluateFields(entity);

Expand Down Expand Up @@ -402,8 +461,7 @@ private DistinctQueryContext(@Nullable Object query, String fieldName) {
}

@Override
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity,
EntityProjection<?, ?> projection) {
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, EntityProjection<?, ?> projection) {
return getMappedFields(entity);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1403,7 +1403,8 @@ protected Mono<Object> insertDocument(String collectionName, Document dbDoc, Cla
.format("Inserting Document containing fields: " + dbDoc.keySet() + " in collection: " + collectionName));
}

Document document = new Document(dbDoc);
MappedDocument document = MappedDocument.of(dbDoc);
queryOperations.createInsertContext(document).prepareId(entityClass);

Flux<InsertOneResult> execute = execute(collectionName, collection -> {

Expand All @@ -1413,10 +1414,10 @@ protected Mono<Object> insertDocument(String collectionName, Document dbDoc, Cla

MongoCollection<Document> collectionToUse = prepareCollection(collection, writeConcernToUse);

return collectionToUse.insertOne(document);
return collectionToUse.insertOne(document.getDocument());
});

return Flux.from(execute).last().map(success -> MappedDocument.of(document).getId());
return Flux.from(execute).last().map(success -> document.getId());
}

protected Flux<ObjectId> insertDocumentList(String collectionName, List<Document> dbDocList) {
Expand Down Expand Up @@ -1480,7 +1481,7 @@ protected Mono<Object> saveDocument(String collectionName, Document document, Cl

Publisher<?> publisher;
if (!mapped.hasId()) {
publisher = collectionToUse.insertOne(document);
publisher = collectionToUse.insertOne(queryOperations.createInsertContext(mapped).prepareId(entityClass).getDocument());
} else {

MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.bson.Document;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -3635,6 +3636,38 @@ public void saveAndLoadStringThatIsAnObjectIdAsString() {
assertThat(target).isEqualTo(source);
}

@Test // GH-4026
void saveShouldGenerateNewIdOfTypeIfExplicitlyDefined() {

RawStringId source = new RawStringId();
source.value = "new value";

template.save(source);

template.execute(RawStringId.class, collection -> {

org.bson.Document first = collection.find(new org.bson.Document()).first();
assertThat(first.get("_id")).isInstanceOf(String.class);
return null;
});
}

@Test // GH-4026
void insertShouldGenerateNewIdOfTypeIfExplicitlyDefined() {

RawStringId source = new RawStringId();
source.value = "new value";

template.insert(source);

template.execute(RawStringId.class, collection -> {

org.bson.Document first = collection.find(new org.bson.Document()).first();
assertThat(first.get("_id")).isInstanceOf(String.class);
return null;
});
}

@Test // DATAMONGO-2193
public void shouldNotConvertStringToObjectIdForNonIdField() {

Expand Down
Loading