Polishing.
Reorder API methods, remove unused MongoPersistentProperty.isNullable method, reduce visibility where possible. Add Javadoc and tweak documentation wording. Introduce DotPath utility to abstract dot path concatenation. See #2803. Original pull request: #896.
This commit is contained in:
@@ -67,8 +67,17 @@ class DocumentAccessor {
|
||||
return this.document;
|
||||
}
|
||||
|
||||
public void putAll(MongoPersistentProperty prop, Document value) {
|
||||
value.entrySet().forEach(entry -> BsonUtils.asMap(document).put(entry.getKey(), entry.getValue()));
|
||||
/**
|
||||
* Copies all of the mappings from the given {@link Document} to the underlying target {@link Document}. These
|
||||
* mappings will replace any mappings that the target document had for any of the keys currently in the specified map.
|
||||
*
|
||||
* @param source
|
||||
*/
|
||||
public void putAll(Document source) {
|
||||
|
||||
Map<String, Object> target = BsonUtils.asMap(document);
|
||||
|
||||
target.putAll(source);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -672,7 +672,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
Document target = new Document();
|
||||
writeInternal(obj, target, mappingContext.getPersistentEntity(prop));
|
||||
|
||||
accessor.putAll(prop, target);
|
||||
accessor.putAll(target);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.data.mongodb.core.query.MongoRegexCreator;
|
||||
import org.springframework.data.mongodb.core.query.MongoRegexCreator.MatchMode;
|
||||
import org.springframework.data.mongodb.core.query.SerializationUtils;
|
||||
import org.springframework.data.mongodb.core.query.UntypedExampleMatcher;
|
||||
import org.springframework.data.mongodb.util.DotPath;
|
||||
import org.springframework.data.support.ExampleMatcherAccessor;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -134,7 +135,7 @@ public class MongoExampleMapper {
|
||||
while (iter.hasNext()) {
|
||||
|
||||
Map.Entry<String, Object> entry = iter.next();
|
||||
String propertyPath = StringUtils.hasText(path) ? path + "." + entry.getKey() : entry.getKey();
|
||||
String propertyPath = DotPath.from(path).append(entry.getKey()).toString();
|
||||
String mappedPropertyPath = getMappedPropertyPath(propertyPath, probeType);
|
||||
|
||||
if (isEmptyIdProperty(entry)) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.bson.BsonValue;
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.domain.Example;
|
||||
@@ -32,7 +33,6 @@ import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.PropertyReferenceException;
|
||||
import org.springframework.data.mapping.context.InvalidPersistentPropertyPath;
|
||||
@@ -43,6 +43,7 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.util.BsonUtils;
|
||||
import org.springframework.data.mongodb.util.DotPath;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -255,17 +256,18 @@ public class QueryMapper {
|
||||
PropertyPath path = PropertyPath.from(field.getKey(), entity.getTypeInformation());
|
||||
PersistentPropertyPath<MongoPersistentProperty> persistentPropertyPath = mappingContext
|
||||
.getPersistentPropertyPath(path);
|
||||
MongoPersistentProperty property = mappingContext.getPersistentPropertyPath(path).getLeafProperty();
|
||||
MongoPersistentProperty property = mappingContext.getPersistentPropertyPath(path).getRequiredLeafProperty();
|
||||
|
||||
if (property.isEmbedded() && property.isEntity()) {
|
||||
|
||||
mappingContext.getPersistentEntity(property)
|
||||
.doWithProperties((PropertyHandler<MongoPersistentProperty>) embedded -> {
|
||||
MongoPersistentEntity<?> embeddedEntity = mappingContext.getRequiredPersistentEntity(property);
|
||||
|
||||
for (MongoPersistentProperty embedded : embeddedEntity) {
|
||||
|
||||
DotPath dotPath = DotPath.from(persistentPropertyPath.toDotPath()).append(embedded.getName());
|
||||
target.put(dotPath.toString(), field.getValue());
|
||||
}
|
||||
|
||||
String dotPath = persistentPropertyPath.toDotPath();
|
||||
dotPath = dotPath + (StringUtils.hasText(dotPath) ? "." : "") + embedded.getName();
|
||||
target.put(dotPath, field.getValue());
|
||||
});
|
||||
} else {
|
||||
target.put(field.getKey(), field.getValue());
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.EmbeddedMongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
@@ -164,10 +163,6 @@ public class UpdateMapper extends QueryMapper {
|
||||
return getMappedUpdateModifier(field, rawValue);
|
||||
}
|
||||
|
||||
if(field.getProperty() != null && field.getProperty().isEmbedded()) {
|
||||
System.out.println("here we are: ");
|
||||
}
|
||||
|
||||
return super.getMappedObjectForField(field, rawValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,17 +28,7 @@ import org.springframework.lang.Nullable;
|
||||
public interface IndexOperationsProvider {
|
||||
|
||||
/**
|
||||
* Returns the operations that can be performed on indexes
|
||||
*
|
||||
* @param collectionName name of the MongoDB collection, must not be {@literal null}.
|
||||
* @param type the type used for field mapping. Can be {@literal null}.
|
||||
* @return index operations on the named collection
|
||||
* @since 2.5
|
||||
*/
|
||||
IndexOperations indexOps(String collectionName, @Nullable Class<?> type);
|
||||
|
||||
/**
|
||||
* Returns the operations that can be performed on indexes
|
||||
* Returns the operations that can be performed on indexes.
|
||||
*
|
||||
* @param collectionName name of the MongoDB collection, must not be {@literal null}.
|
||||
* @return index operations on the named collection
|
||||
@@ -46,4 +36,14 @@ public interface IndexOperationsProvider {
|
||||
default IndexOperations indexOps(String collectionName) {
|
||||
return indexOps(collectionName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operations that can be performed on indexes.
|
||||
*
|
||||
* @param collectionName name of the MongoDB collection, must not be {@literal null}.
|
||||
* @param type the type used for field mapping. Can be {@literal null}.
|
||||
* @return index operations on the named collection
|
||||
* @since 3.2
|
||||
*/
|
||||
IndexOperations indexOps(String collectionName, @Nullable Class<?> type);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.util.BsonUtils;
|
||||
import org.springframework.data.mongodb.util.DotPath;
|
||||
import org.springframework.data.spel.EvaluationContextProvider;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
@@ -161,16 +162,16 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
* @return List of {@link IndexDefinitionHolder} representing indexes for given type and its referenced property
|
||||
* types. Will never be {@code null}.
|
||||
*/
|
||||
private List<IndexDefinitionHolder> resolveIndexForClass(final TypeInformation<?> type, final String dotPath,
|
||||
final Path path, final String collection, final CycleGuard guard) {
|
||||
private List<IndexDefinitionHolder> resolveIndexForClass( TypeInformation<?> type, String dotPath,
|
||||
Path path, String collection, CycleGuard guard) {
|
||||
|
||||
return resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(type), dotPath, path, collection, guard);
|
||||
}
|
||||
|
||||
private List<IndexDefinitionHolder> resolveIndexForEntity(MongoPersistentEntity<?> entity, final String dotPath,
|
||||
final Path path, final String collection, final CycleGuard guard) {
|
||||
private List<IndexDefinitionHolder> resolveIndexForEntity(MongoPersistentEntity<?> entity, String dotPath,
|
||||
Path path, String collection, CycleGuard guard) {
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<>();
|
||||
List<IndexDefinitionHolder> indexInformation = new ArrayList<>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(dotPath, collection, entity));
|
||||
|
||||
entity.doWithProperties((PropertyHandler<MongoPersistentProperty>) property -> this
|
||||
@@ -184,10 +185,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
private void guardAndPotentiallyAddIndexForProperty(MongoPersistentProperty persistentProperty, String dotPath,
|
||||
Path path, String collection, List<IndexDefinitionHolder> indexes, CycleGuard guard) {
|
||||
|
||||
String propertyDotPath = dotPath;
|
||||
DotPath propertyDotPath = DotPath.from(dotPath);
|
||||
|
||||
if (!persistentProperty.isEmbedded()) {
|
||||
propertyDotPath = (StringUtils.hasText(dotPath) ? dotPath + "." : "") + persistentProperty.getFieldName();
|
||||
propertyDotPath = propertyDotPath.append(persistentProperty.getFieldName());
|
||||
}
|
||||
|
||||
Path propertyPath = path.append(persistentProperty);
|
||||
@@ -195,14 +196,14 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
if (persistentProperty.isEntity()) {
|
||||
try {
|
||||
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty), propertyDotPath,
|
||||
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty), propertyDotPath.toString(),
|
||||
propertyPath, collection, guard));
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = createIndexDefinitionHolderForProperty(propertyDotPath, collection,
|
||||
List<IndexDefinitionHolder> indexDefinitions = createIndexDefinitionHolderForProperty(propertyDotPath.toString(), collection,
|
||||
persistentProperty);
|
||||
|
||||
if (!indexDefinitions.isEmpty()) {
|
||||
@@ -270,7 +271,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
}
|
||||
|
||||
try {
|
||||
appendTextIndexInformation("", Path.empty(), indexDefinitionBuilder, root,
|
||||
appendTextIndexInformation(DotPath.empty(), Path.empty(), indexDefinitionBuilder, root,
|
||||
new TextIndexIncludeOptions(IncludeStrategy.DEFAULT), new CycleGuard());
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.info(e.getMessage());
|
||||
@@ -291,9 +292,9 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
}
|
||||
|
||||
private void appendTextIndexInformation(final String dotPath, final Path path,
|
||||
final TextIndexDefinitionBuilder indexDefinitionBuilder, final MongoPersistentEntity<?> entity,
|
||||
final TextIndexIncludeOptions includeOptions, final CycleGuard guard) {
|
||||
private void appendTextIndexInformation(DotPath dotPath, Path path,
|
||||
TextIndexDefinitionBuilder indexDefinitionBuilder, MongoPersistentEntity<?> entity,
|
||||
TextIndexIncludeOptions includeOptions, CycleGuard guard) {
|
||||
|
||||
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
|
||||
|
||||
@@ -302,7 +303,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
guard.protect(persistentProperty, path);
|
||||
|
||||
if (persistentProperty.isExplicitLanguageProperty() && !StringUtils.hasText(dotPath)) {
|
||||
if (persistentProperty.isExplicitLanguageProperty() && dotPath.isEmpty()) {
|
||||
indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName());
|
||||
}
|
||||
|
||||
@@ -310,8 +311,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
if (includeOptions.isForce() || indexed != null || persistentProperty.isEntity()) {
|
||||
|
||||
String propertyDotPath = (StringUtils.hasText(dotPath) ? dotPath + "." : "")
|
||||
+ persistentProperty.getFieldName();
|
||||
DotPath propertyDotPath = dotPath
|
||||
.append(persistentProperty.getFieldName());
|
||||
|
||||
Path propertyPath = path.append(persistentProperty);
|
||||
|
||||
@@ -324,7 +325,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
TextIndexIncludeOptions optionsForNestedType = includeOptions;
|
||||
if (!IncludeStrategy.FORCE.equals(includeOptions.getStrategy()) && indexed != null) {
|
||||
optionsForNestedType = new TextIndexIncludeOptions(IncludeStrategy.FORCE,
|
||||
new TextIndexedFieldSpec(propertyDotPath, weight));
|
||||
new TextIndexedFieldSpec(propertyDotPath.toString(), weight));
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -337,7 +338,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
entity.getName()), e);
|
||||
}
|
||||
} else if (includeOptions.isForce() || indexed != null) {
|
||||
indexDefinitionBuilder.onField(propertyDotPath, weight);
|
||||
indexDefinitionBuilder.onField(propertyDotPath.toString(), weight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,7 +649,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
MongoPersistentProperty property = association.getInverse();
|
||||
|
||||
String propertyDotPath = (StringUtils.hasText(path) ? path + "." : "") + property.getFieldName();
|
||||
DotPath propertyDotPath = DotPath.from(path).append(property.getFieldName());
|
||||
|
||||
if (property.isAnnotationPresent(GeoSpatialIndexed.class) || property.isAnnotationPresent(TextIndexed.class)) {
|
||||
throw new MappingException(
|
||||
@@ -656,7 +657,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
propertyDotPath));
|
||||
}
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = createIndexDefinitionHolderForProperty(propertyDotPath, collection,
|
||||
List<IndexDefinitionHolder> indexDefinitions = createIndexDefinitionHolderForProperty(propertyDotPath.toString(), collection,
|
||||
property);
|
||||
|
||||
if (!indexDefinitions.isEmpty()) {
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.springframework.data.mongodb.core.mapping;
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
public class EmbeddedEntityContext {
|
||||
class EmbeddedEntityContext {
|
||||
|
||||
private final MongoPersistentProperty property;
|
||||
|
||||
|
||||
@@ -32,13 +32,16 @@ import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Embedded variant of {@link MongoPersistentEntity}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2020/12
|
||||
* @since 3.2
|
||||
* @see Embedded
|
||||
*/
|
||||
public class EmbeddedMongoPersistentEntity<T> implements MongoPersistentEntity<T> {
|
||||
class EmbeddedMongoPersistentEntity<T> implements MongoPersistentEntity<T> {
|
||||
|
||||
private EmbeddedEntityContext context;
|
||||
private MongoPersistentEntity<T> delegate;
|
||||
private final EmbeddedEntityContext context;
|
||||
private final MongoPersistentEntity<T> delegate;
|
||||
|
||||
public EmbeddedMongoPersistentEntity(MongoPersistentEntity<T> delegate, EmbeddedEntityContext context) {
|
||||
|
||||
@@ -46,84 +49,103 @@ public class EmbeddedMongoPersistentEntity<T> implements MongoPersistentEntity<T
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollection() {
|
||||
return delegate.getCollection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguage() {
|
||||
return delegate.getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MongoPersistentProperty getTextScoreProperty() {
|
||||
return delegate.getTextScoreProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTextScoreProperty() {
|
||||
return delegate.hasTextScoreProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Collation getCollation() {
|
||||
return delegate.getCollation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCollation() {
|
||||
return delegate.hasCollation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShardKey getShardKey() {
|
||||
return delegate.getShardKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSharded() {
|
||||
return delegate.isSharded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return delegate.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PreferredConstructor<T, MongoPersistentProperty> getPersistenceConstructor() {
|
||||
return delegate.getPersistenceConstructor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConstructorArgument(PersistentProperty<?> property) {
|
||||
return delegate.isConstructorArgument(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdProperty(PersistentProperty<?> property) {
|
||||
return delegate.isIdProperty(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVersionProperty(PersistentProperty<?> property) {
|
||||
return delegate.isVersionProperty(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MongoPersistentProperty getIdProperty() {
|
||||
return delegate.getIdProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getRequiredIdProperty() {
|
||||
return delegate.getRequiredIdProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MongoPersistentProperty getVersionProperty() {
|
||||
return delegate.getVersionProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getRequiredVersionProperty() {
|
||||
return delegate.getRequiredVersionProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MongoPersistentProperty getPersistentProperty(String name) {
|
||||
return wrap(delegate.getPersistentProperty(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getRequiredPersistentProperty(String name) {
|
||||
|
||||
MongoPersistentProperty persistentProperty = getPersistentProperty(name);
|
||||
@@ -134,36 +156,44 @@ public class EmbeddedMongoPersistentEntity<T> implements MongoPersistentEntity<T
|
||||
throw new RuntimeException(":kladjnf");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MongoPersistentProperty getPersistentProperty(Class<? extends Annotation> annotationType) {
|
||||
return wrap(delegate.getPersistentProperty(annotationType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<MongoPersistentProperty> getPersistentProperties(Class<? extends Annotation> annotationType) {
|
||||
return Streamable.of(delegate.getPersistentProperties(annotationType)).stream().map(this::wrap)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdProperty() {
|
||||
return delegate.hasIdProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVersionProperty() {
|
||||
return delegate.hasVersionProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getType() {
|
||||
return delegate.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getTypeAlias() {
|
||||
return delegate.getTypeAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeInformation<T> getTypeInformation() {
|
||||
return delegate.getTypeInformation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithProperties(PropertyHandler<MongoPersistentProperty> handler) {
|
||||
|
||||
delegate.doWithProperties((PropertyHandler<MongoPersistentProperty>) property -> {
|
||||
@@ -171,6 +201,7 @@ public class EmbeddedMongoPersistentEntity<T> implements MongoPersistentEntity<T
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithProperties(SimplePropertyHandler handler) {
|
||||
|
||||
delegate.doWithProperties((SimplePropertyHandler) property -> {
|
||||
@@ -182,51 +213,63 @@ public class EmbeddedMongoPersistentEntity<T> implements MongoPersistentEntity<T
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithAssociations(AssociationHandler<MongoPersistentProperty> handler) {
|
||||
delegate.doWithAssociations(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithAssociations(SimpleAssociationHandler handler) {
|
||||
delegate.doWithAssociations(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
|
||||
return delegate.findAnnotation(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> A getRequiredAnnotation(Class<A> annotationType) throws IllegalStateException {
|
||||
return delegate.getRequiredAnnotation(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> boolean isAnnotationPresent(Class<A> annotationType) {
|
||||
return delegate.isAnnotationPresent(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B> PersistentPropertyAccessor<B> getPropertyAccessor(B bean) {
|
||||
return delegate.getPropertyAccessor(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B> PersistentPropertyPathAccessor<B> getPropertyPathAccessor(B bean) {
|
||||
return delegate.getPropertyPathAccessor(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifierAccessor getIdentifierAccessor(Object bean) {
|
||||
return delegate.getIdentifierAccessor(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNew(Object bean) {
|
||||
return delegate.isNew(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImmutable() {
|
||||
return delegate.isImmutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresPropertyPopulation() {
|
||||
return delegate.requiresPropertyPopulation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<MongoPersistentProperty> iterator() {
|
||||
|
||||
List<MongoPersistentProperty> target = new ArrayList<>();
|
||||
@@ -234,10 +277,12 @@ public class EmbeddedMongoPersistentEntity<T> implements MongoPersistentEntity<T
|
||||
return target.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super MongoPersistentProperty> action) {
|
||||
delegate.forEach(it -> action.accept(wrap(it)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<MongoPersistentProperty> spliterator() {
|
||||
return delegate.spliterator();
|
||||
}
|
||||
|
||||
@@ -26,10 +26,13 @@ import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Embedded variant of {@link MongoPersistentProperty}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2020/12
|
||||
* @since 3.2
|
||||
* @see Embedded
|
||||
*/
|
||||
public class EmbeddedMongoPersistentProperty implements MongoPersistentProperty {
|
||||
class EmbeddedMongoPersistentProperty implements MongoPersistentProperty {
|
||||
|
||||
private final MongoPersistentProperty delegate;
|
||||
private final EmbeddedEntityContext context;
|
||||
@@ -40,6 +43,7 @@ public class EmbeddedMongoPersistentProperty implements MongoPersistentProperty
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFieldName() {
|
||||
|
||||
if (!context.getProperty().isEmbedded()) {
|
||||
@@ -49,210 +53,254 @@ public class EmbeddedMongoPersistentProperty implements MongoPersistentProperty
|
||||
return context.getProperty().findAnnotation(Embedded.class).prefix() + delegate.getFieldName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getFieldType() {
|
||||
return delegate.getFieldType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFieldOrder() {
|
||||
return delegate.getFieldOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDbReference() {
|
||||
return delegate.isDbReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExplicitIdProperty() {
|
||||
return delegate.isExplicitIdProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLanguageProperty() {
|
||||
return delegate.isLanguageProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExplicitLanguageProperty() {
|
||||
return delegate.isExplicitLanguageProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTextScoreProperty() {
|
||||
return delegate.isTextScoreProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public DBRef getDBRef() {
|
||||
return delegate.getDBRef();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usePropertyAccess() {
|
||||
return delegate.usePropertyAccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasExplicitWriteTarget() {
|
||||
return delegate.hasExplicitWriteTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistentEntity<?, MongoPersistentProperty> getOwner() {
|
||||
return delegate.getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return delegate.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return delegate.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeInformation<?> getTypeInformation() {
|
||||
return delegate.getTypeInformation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends TypeInformation<?>> getPersistentEntityTypes() {
|
||||
return delegate.getPersistentEntityTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Method getGetter() {
|
||||
return delegate.getGetter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getRequiredGetter() {
|
||||
return delegate.getRequiredGetter();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Method getSetter() {
|
||||
return delegate.getSetter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getRequiredSetter() {
|
||||
return delegate.getRequiredSetter();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Method getWither() {
|
||||
return delegate.getWither();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getRequiredWither() {
|
||||
return delegate.getRequiredWither();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Field getField() {
|
||||
return delegate.getField();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field getRequiredField() {
|
||||
return delegate.getRequiredField();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getSpelExpression() {
|
||||
return delegate.getSpelExpression();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Association<MongoPersistentProperty> getAssociation() {
|
||||
return delegate.getAssociation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Association<MongoPersistentProperty> getRequiredAssociation() {
|
||||
return delegate.getRequiredAssociation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEntity() {
|
||||
return delegate.isEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdProperty() {
|
||||
return delegate.isIdProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVersionProperty() {
|
||||
return delegate.isVersionProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectionLike() {
|
||||
return delegate.isCollectionLike();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMap() {
|
||||
return delegate.isMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isArray() {
|
||||
return delegate.isArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransient() {
|
||||
return delegate.isTransient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
return delegate.isWritable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImmutable() {
|
||||
return delegate.isImmutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssociation() {
|
||||
return delegate.isAssociation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmbedded() {
|
||||
return delegate.isEmbedded();
|
||||
}
|
||||
|
||||
public boolean isNullable() {
|
||||
return delegate.isNullable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getComponentType() {
|
||||
return delegate.getComponentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getRawType() {
|
||||
return delegate.getRawType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getMapValueType() {
|
||||
return delegate.getMapValueType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getActualType() {
|
||||
return delegate.getActualType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
|
||||
return delegate.findAnnotation(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> A getRequiredAnnotation(Class<A> annotationType) throws IllegalStateException {
|
||||
return delegate.getRequiredAnnotation(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <A extends Annotation> A findPropertyOrOwnerAnnotation(Class<A> annotationType) {
|
||||
return delegate.findPropertyOrOwnerAnnotation(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
|
||||
return delegate.isAnnotationPresent(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasActualTypeAnnotation(Class<? extends Annotation> annotationType) {
|
||||
return delegate.hasActualTypeAnnotation(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getAssociationTargetType() {
|
||||
return delegate.getAssociationTargetType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PersistentPropertyAccessor<T> getAccessorForOwner(T owner) {
|
||||
return delegate.getAccessorForOwner(owner);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MongoMappingContext extends AbstractMappingContext<MongoPersistentEntity<?>, MongoPersistentProperty>
|
||||
implements ApplicationContextAware {
|
||||
@@ -81,18 +82,13 @@ public class MongoMappingContext extends AbstractMappingContext<MongoPersistentE
|
||||
return new CachingMongoPersistentProperty(property, owner, simpleTypeHolder, fieldNamingStrategy);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected MongoPersistentProperty createPersistentProperty(Property property, MongoPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.BasicMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation, org.springframework.data.mapping.model.MappingContext)
|
||||
*/
|
||||
@Override
|
||||
protected <T> BasicMongoPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
return new BasicMongoPersistentEntity<T>(typeInformation);
|
||||
return new BasicMongoPersistentEntity<>(typeInformation);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -101,7 +97,6 @@ public class MongoMappingContext extends AbstractMappingContext<MongoPersistentE
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
|
||||
super.setApplicationContext(applicationContext);
|
||||
}
|
||||
|
||||
@@ -132,16 +127,16 @@ public class MongoMappingContext extends AbstractMappingContext<MongoPersistentE
|
||||
this.autoIndexCreation = autoCreateIndexes;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MongoPersistentEntity<?> getPersistentEntity(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
MongoPersistentEntity entity = super.getPersistentEntity(persistentProperty);
|
||||
MongoPersistentEntity<?> entity = super.getPersistentEntity(persistentProperty);
|
||||
|
||||
if(entity == null || !persistentProperty.isEmbedded()) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
return new EmbeddedMongoPersistentEntity(entity, new EmbeddedEntityContext(persistentProperty));
|
||||
return new EmbeddedMongoPersistentEntity<>(entity, new EmbeddedEntityContext(persistentProperty));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,10 @@ public interface MongoPersistentEntity<T> extends MutablePersistentEntity<T, Mon
|
||||
return getShardKey().isSharded();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if the entity should be embedded.
|
||||
* @since 3.2
|
||||
*/
|
||||
default boolean isEmbedded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.mapping;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.Embedded.OnEmpty;
|
||||
import org.springframework.data.util.NullableUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -132,17 +128,7 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
|
||||
* @since 3.2
|
||||
*/
|
||||
default boolean isEmbedded() {
|
||||
return isEntity() && findAnnotation(Embedded.class) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if the property generally allows {@literal null} values;
|
||||
* @since 3.2
|
||||
*/
|
||||
default boolean isNullable() {
|
||||
|
||||
return (isEmbedded() && findAnnotation(Embedded.class).onEmpty().equals(OnEmpty.USE_NULL))
|
||||
&& !NullableUtils.isNonNull(getField(), ElementType.FIELD);
|
||||
return isEntity() && isAnnotationPresent(Embedded.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.util;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Value object representing a dot path.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class DotPath {
|
||||
|
||||
private static final DotPath EMPTY = new DotPath("");
|
||||
|
||||
private final String path;
|
||||
|
||||
private DotPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DotPath} from {@code dotPath}.
|
||||
*
|
||||
* @param dotPath the dot path, can be empty or {@literal null}.
|
||||
* @return the {@link DotPath} representing {@code dotPath}.
|
||||
*/
|
||||
public static DotPath from(@Nullable String dotPath) {
|
||||
|
||||
if (StringUtils.hasLength(dotPath)) {
|
||||
return new DotPath(dotPath);
|
||||
}
|
||||
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an empty dotpath.
|
||||
*
|
||||
* @return an empty dotpath.
|
||||
*/
|
||||
public static DotPath empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a segment to the dotpath. If the dotpath is not empty, then segments are separated with a dot.
|
||||
*
|
||||
* @param segment the segment to append.
|
||||
* @return
|
||||
*/
|
||||
public DotPath append(String segment) {
|
||||
|
||||
if (isEmpty()) {
|
||||
return new DotPath(segment);
|
||||
}
|
||||
|
||||
return new DotPath(path + "." + segment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this dotpath is empty.
|
||||
*
|
||||
* @return whether this dotpath is empty.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return !StringUtils.hasLength(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import lombok.EqualsAndHashCode;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.ToString;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.data.mongodb.core.mapping.Embedded;
|
||||
@@ -33,6 +34,8 @@ import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
|
||||
import org.springframework.data.mongodb.test.util.Template;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link Embedded}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@ExtendWith(MongoTemplateExtension.class)
|
||||
@@ -105,6 +108,7 @@ class MongoTemplateEmbeddedTests {
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
static class WithEmbedded {
|
||||
|
||||
String id;
|
||||
@@ -113,6 +117,7 @@ class MongoTemplateEmbeddedTests {
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
static class WithPrefixedEmbedded {
|
||||
|
||||
String id;
|
||||
@@ -121,6 +126,7 @@ class MongoTemplateEmbeddedTests {
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
static class EmbeddableType {
|
||||
|
||||
String stringValue;
|
||||
|
||||
@@ -83,7 +83,6 @@ class UnionWithOperationUnitTests {
|
||||
UnionWithOperation.unionWith("coll-1").pipeline(Aggregation.project().and("name").as("name")));
|
||||
|
||||
List<Document> pipeline = agg.toPipeline(contextFor(Supplier.class));
|
||||
System.out.println("pipeline: " + pipeline);
|
||||
assertThat(pipeline).containsExactly(new Document("$project", new Document("supplier", 1)), //
|
||||
new Document("$unionWith", new Document("coll", "coll-1").append("pipeline",
|
||||
Arrays.asList(new Document("$project", new Document("name", 1))))));
|
||||
|
||||
@@ -1155,7 +1155,6 @@ class UpdateMapperUnitTests {
|
||||
Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(WrapperAroundWithEmbedded.class));
|
||||
|
||||
System.out.println("mappedUpdate.toJson(): " + mappedUpdate.toJson());
|
||||
assertThat(mappedUpdate).isEqualTo(new Document("$set", new Document("withPrefixedEmbedded",
|
||||
new Document("prefix-stringValue", "updated").append("prefix-listValue", Arrays.asList("val-1", "val-2")))));
|
||||
}
|
||||
|
||||
@@ -79,19 +79,6 @@ public class MapReduceTests {
|
||||
template.getMongoDbFactory().getMongoDatabase("jmr1-out-db").drop();
|
||||
}
|
||||
|
||||
@Test // DATADOC-7
|
||||
@Ignore
|
||||
public void testForDocs() {
|
||||
|
||||
createMapReduceData();
|
||||
MapReduceResults<ValueObject> results = mongoTemplate.mapReduce("jmr1", MAP_FUNCTION, REDUCE_FUNCTION,
|
||||
ValueObject.class);
|
||||
|
||||
for (ValueObject valueObject : results) {
|
||||
System.out.println(valueObject);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-260
|
||||
public void testIssue260() {
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
[[new-features]]
|
||||
= New & Noteworthy
|
||||
|
||||
[[new-features.3.2]]
|
||||
== What's New in Spring Data MongoDB 3.2
|
||||
|
||||
* Support for <<embedded-entities,Embedded Types>> to unwrap nested objects into the parent `Document`.
|
||||
|
||||
[[new-features.3.1]]
|
||||
== What's New in Spring Data MongoDB 3.1
|
||||
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
[[embedded-entities]]
|
||||
== Embedded Types
|
||||
|
||||
Embedded entities are used to design value objects in your Java domain model whose properties are flattened out into the MongoDB Document.
|
||||
Embedded entities are used to design value objects in your Java domain model whose properties are flattened out into the parent's MongoDB Document.
|
||||
|
||||
[[embedded-entities.mapping]]
|
||||
=== Embedded Types Mapping
|
||||
|
||||
In the example below you see, that `User.name` is annotated with `@Embedded`.
|
||||
The consequence of this is that all properties of `UserName` are folded into the `user` document.
|
||||
Consider the following domain model where `User.name` is annotated with `@Embedded`.
|
||||
The `@Embedded` annotation signals that all properties of `UserName` should be unwrapped into the `user` document that owns the `name` property.
|
||||
|
||||
.Sample Code of embedding objects
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class User {
|
||||
class User {
|
||||
|
||||
@Id
|
||||
private String userId;
|
||||
@Id
|
||||
String userId;
|
||||
|
||||
@Embedded(onEmpty = USE_NULL) <1>
|
||||
UserName name;
|
||||
}
|
||||
|
||||
public class UserName {
|
||||
private String firstname;
|
||||
private String lastname;
|
||||
class UserName {
|
||||
|
||||
String firstname;
|
||||
|
||||
String lastname;
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
@@ -41,37 +44,42 @@ By using `onEmpty=USE_EMPTY` an empty `UserName`, with potential `null` value fo
|
||||
====
|
||||
|
||||
For less verbose embeddable type declarations use `@Embedded.Nullable` and `@Embedded.Empty` instead `@Embedded(onEmpty = USE_NULL)` and `@Embedded(onEmpty = USE_EMPTY)`.
|
||||
Using those annotations simultaneously set JSR-305 `@javax.annotation.Nonnull` accordingly.
|
||||
Both annotations are meta-annotated with JSR-305 `@javax.annotation.Nonnull` to aid with nullability inspections.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
It is possible to use complex types within an embedded object.
|
||||
However those must not be, nor contain embedded fields themselves.
|
||||
However, those must not be, nor contain embedded fields themselves.
|
||||
====
|
||||
|
||||
[[embedded-entities.mapping.field-names]]
|
||||
=== Embedded Types field names
|
||||
|
||||
A value object can be embedded multiple times by using the optional `prefix` attribute of the `@Embedded` annotation.
|
||||
By dosing so the chosen prefix is prepended to each property or `@Field("...")` name in the embedded object.
|
||||
By dosing so the chosen prefix is prepended to each property or `@Field("…")` name in the embedded object.
|
||||
Please note that values will overwrite each other if multiple properties render to the same field name.
|
||||
|
||||
.Sample Code of embedded object with name prefix
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class User {
|
||||
class User {
|
||||
|
||||
@Id
|
||||
private String userId;
|
||||
@Id
|
||||
String userId;
|
||||
|
||||
@Embedded.Nullable(prefix = "u") <1>
|
||||
@Embedded.Nullable(prefix = "u_") <1>
|
||||
UserName name;
|
||||
|
||||
@Embedded.Nullable(prefix = "a_") <2>
|
||||
UserName name;
|
||||
}
|
||||
|
||||
public class UserName {
|
||||
private String firstname;
|
||||
private String lastname;
|
||||
class UserName {
|
||||
|
||||
String firstname;
|
||||
|
||||
String lastname;
|
||||
}
|
||||
----
|
||||
|
||||
@@ -79,11 +87,14 @@ public class UserName {
|
||||
----
|
||||
{
|
||||
"_id" : "a6a805bd-f95f",
|
||||
"ufirstname" : "Jean",
|
||||
"ulastname" : "Grey"
|
||||
"u_firstname" : "Jean", <1>
|
||||
"u_lastname" : "Grey",
|
||||
"a_firstname" : "Something", <2>
|
||||
"a_lastname" : "Else"
|
||||
}
|
||||
----
|
||||
<1> The prefix `u` is prepended to all properties of `UserName`.
|
||||
<1> All properties of `UserName` are prefixed with `u_`.
|
||||
<2> All properties of `UserName` are prefixed with `a_`.
|
||||
====
|
||||
|
||||
While combining the `@Field` annotation with `@Embedded` on the very same property does not make sense and therefore leads to an error.
|
||||
@@ -104,7 +115,7 @@ public class User {
|
||||
|
||||
public class UserName {
|
||||
|
||||
@Field("first-name") <2>
|
||||
@Field("first-name") <2>
|
||||
private String firstname;
|
||||
|
||||
@Field("last-name")
|
||||
@@ -116,18 +127,18 @@ public class UserName {
|
||||
----
|
||||
{
|
||||
"_id" : "2647f7b9-89da",
|
||||
"u-first-name" : "Barbara", <2>
|
||||
"u-first-name" : "Barbara", <2>
|
||||
"u-last-name" : "Gordon"
|
||||
}
|
||||
----
|
||||
<1> The prefix `u-` is prepended to all properties of `UserName`.
|
||||
<2> The field name is the result of the combination of the annotated field name an the chosen prefix.
|
||||
<1> All properties of `UserName` are prefixed with `u-`.
|
||||
<2> Final field names are a result of concatenating `@Embedded(prefix)` and `@Field(name)`.
|
||||
====
|
||||
|
||||
[[embedded-entities.queries]]
|
||||
=== Query on Embedded Objects
|
||||
|
||||
Defining queries on embedded properties is possible on type as well as field level as the provided `Critieria` is matched against the domain type.
|
||||
Defining queries on embedded properties is possible on type- as well as field-level as the provided `Criteria` is matched against the domain type.
|
||||
Prefixes and potential custom field names will be considered when rendering the actual query.
|
||||
Use the property name of the embedded object to match against all contained fields as shown in the sample below.
|
||||
|
||||
@@ -149,7 +160,7 @@ db.collection.find({
|
||||
----
|
||||
====
|
||||
|
||||
It is also possible to address any field of the embedded object directly via its property name as shown in the snippet below.
|
||||
It is also possible to address any field of the embedded object directly using its property name as shown in the snippet below.
|
||||
|
||||
.Query on field of embedded object
|
||||
====
|
||||
@@ -194,7 +205,7 @@ Though possible, using the embedded object itself as sort criteria includes all
|
||||
====
|
||||
|
||||
[[embedded-entities.queries.project]]
|
||||
==== Project on embedded object
|
||||
==== Field projection on embedded objects
|
||||
|
||||
Fields of embedded objects can be subject for projection either as a whole or via single fields as shown in the samples below.
|
||||
|
||||
@@ -203,7 +214,7 @@ Fields of embedded objects can be subject for projection either as a whole or vi
|
||||
[source,java]
|
||||
----
|
||||
Query findByUserLastName = query(where("name.firstname").is("Gamora"));
|
||||
findByUserLastName.fields().include("name"); <1>
|
||||
findByUserLastName.fields().include("name"); <1>
|
||||
List<User> user = template.findAll(findByUserName, User.class);
|
||||
----
|
||||
|
||||
@@ -225,7 +236,7 @@ db.collection.find({
|
||||
[source,java]
|
||||
----
|
||||
Query findByUserLastName = query(where("name.lastname").is("Smoak"));
|
||||
findByUserLastName.fields().include("name.firstname"); <1>
|
||||
findByUserLastName.fields().include("name.firstname"); <1>
|
||||
List<User> user = template.findAll(findByUserName, User.class);
|
||||
----
|
||||
|
||||
@@ -258,9 +269,9 @@ The `Repository` abstraction allows deriving queries on fields of embedded objec
|
||||
----
|
||||
interface UserRepository extends CrudRepository<User, String> {
|
||||
|
||||
List<User> findByName(UserName username); <1>
|
||||
List<User> findByName(UserName username); <1>
|
||||
|
||||
List<User> findByNameFirstname(String firstname); <1>
|
||||
List<User> findByNameFirstname(String firstname); <2>
|
||||
}
|
||||
----
|
||||
<1> Matches against all fields of the embedded object.
|
||||
@@ -330,14 +341,14 @@ db.collection.update({
|
||||
=== Aggregations on Embedded Objects
|
||||
|
||||
The <<mongo.aggregation,Aggregation Framework>> will attempt to map embedded values of typed aggregations.
|
||||
Please make sure to work with the properties path including the embedded wrapper object when referencing one of it's values.
|
||||
Please make sure to work with the property path including the embedded wrapper object when referencing one of its values.
|
||||
Other than that no special action is required.
|
||||
|
||||
[[embedded-entities.indexes]]
|
||||
=== Index on Embedded Objects
|
||||
|
||||
It is possible to attach the `@Indexed` annotation to properties of an embedded type just as it is done with regular objects.
|
||||
However it is not possible to use `@Indexed` along with the `@Embedded` annotation on the very same property of an object.
|
||||
It is not possible to use `@Indexed` along with the `@Embedded` annotation on the owning property.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
@@ -348,9 +359,10 @@ public class User {
|
||||
private String userId;
|
||||
|
||||
@Embedded(onEmpty = USE_NULL)
|
||||
UserName name; <1>
|
||||
UserName name; <1>
|
||||
|
||||
@Indexed <2> // Invalid -> InvalidDataAccessApiUsageException
|
||||
// Invalid -> InvalidDataAccessApiUsageException
|
||||
@Indexed <2>
|
||||
@Embedded(onEmpty = USE_Empty)
|
||||
Address address;
|
||||
}
|
||||
@@ -360,7 +372,7 @@ public class UserName {
|
||||
private String firstname;
|
||||
|
||||
@Indexed
|
||||
private String lastname; <1>
|
||||
private String lastname; <1>
|
||||
}
|
||||
----
|
||||
<1> Index created for `lastname` in `users` collection.
|
||||
|
||||
Reference in New Issue
Block a user