Browse Source

Revisit internal caching arrangements.

Introduce caching and reduce allocations on hot code paths.

Closes: #4818
Original Pull Request: #4819
pull/4824/head
Mark Paluch 1 year ago committed by Christoph Strobl
parent
commit
1075a25df4
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 1
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java
  2. 61
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
  3. 12
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java
  4. 8
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldName.java

1
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java

@ -171,6 +171,7 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<Bson> implements M
this.typeKey = typeKey; this.typeKey = typeKey;
} }
@Override
public Alias readAliasFrom(Bson source) { public Alias readAliasFrom(Bson source) {
if (source instanceof List) { if (source instanceof List) {

61
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -130,6 +131,23 @@ public class MappingMongoConverter extends AbstractMongoConverter
private static final String INCOMPATIBLE_TYPES = "Cannot convert %1$s of type %2$s into an instance of %3$s; Implement a custom Converter<%2$s, %3$s> and register it with the CustomConversions; Parent object was: %4$s"; private static final String INCOMPATIBLE_TYPES = "Cannot convert %1$s of type %2$s into an instance of %3$s; Implement a custom Converter<%2$s, %3$s> and register it with the CustomConversions; Parent object was: %4$s";
private static final String INVALID_TYPE_TO_READ = "Expected to read Document %s into type %s but didn't find a PersistentEntity for the latter"; private static final String INVALID_TYPE_TO_READ = "Expected to read Document %s into type %s but didn't find a PersistentEntity for the latter";
private static final BiPredicate<MongoPersistentEntity<?>, MongoPersistentProperty> PROPERTY_FILTER = (e,
property) -> {
if (property.isIdProperty()) {
return false;
}
if (e.isCreatorArgument(property)) {
return false;
}
if (!property.isReadable()) {
return false;
}
return true;
};
public static final TypeInformation<Bson> BSON = TypeInformation.of(Bson.class); public static final TypeInformation<Bson> BSON = TypeInformation.of(Bson.class);
protected static final Log LOGGER = LogFactory.getLog(MappingMongoConverter.class); protected static final Log LOGGER = LogFactory.getLog(MappingMongoConverter.class);
@ -368,7 +386,7 @@ public class MappingMongoConverter extends AbstractMongoConverter
evaluator, spELContext); evaluator, spELContext);
readProperties(context, entity, convertingAccessor, documentAccessor, valueProvider, evaluator, readProperties(context, entity, convertingAccessor, documentAccessor, valueProvider, evaluator,
Predicates.isTrue()); (mongoPersistentProperties, mongoPersistentProperty) -> true);
return (R) projectionFactory.createProjection(mappedType.getType(), accessor.getBean()); return (R) projectionFactory.createProjection(mappedType.getType(), accessor.getBean());
} }
@ -518,6 +536,23 @@ public class MappingMongoConverter extends AbstractMongoConverter
parameterProvider); parameterProvider);
} }
class EvaluatingDocumentAccessor extends DocumentAccessor implements ValueExpressionEvaluator {
/**
* Creates a new {@link DocumentAccessor} for the given {@link Document}.
*
* @param document must be a {@link Document} effectively, must not be {@literal null}.
*/
public EvaluatingDocumentAccessor(Bson document) {
super(document);
}
@Override
public <T> T evaluate(String expression) {
return expressionEvaluatorFactory.create(getDocument()).evaluate(expression);
}
}
private <S> S read(ConversionContext context, MongoPersistentEntity<S> entity, Document bson) { private <S> S read(ConversionContext context, MongoPersistentEntity<S> entity, Document bson) {
S existing = context.findContextualEntity(entity, bson); S existing = context.findContextualEntity(entity, bson);
@ -525,19 +560,18 @@ public class MappingMongoConverter extends AbstractMongoConverter
return existing; return existing;
} }
ValueExpressionEvaluator evaluator = expressionEvaluatorFactory.create(bson); EvaluatingDocumentAccessor documentAccessor = new EvaluatingDocumentAccessor(bson);
DocumentAccessor documentAccessor = new DocumentAccessor(bson);
InstanceCreatorMetadata<MongoPersistentProperty> instanceCreatorMetadata = entity.getInstanceCreatorMetadata(); InstanceCreatorMetadata<MongoPersistentProperty> instanceCreatorMetadata = entity.getInstanceCreatorMetadata();
ParameterValueProvider<MongoPersistentProperty> provider = instanceCreatorMetadata != null ParameterValueProvider<MongoPersistentProperty> provider = instanceCreatorMetadata != null
&& instanceCreatorMetadata.hasParameters() ? getParameterProvider(context, entity, documentAccessor, evaluator) && instanceCreatorMetadata.hasParameters()
? getParameterProvider(context, entity, documentAccessor, documentAccessor)
: NoOpParameterValueProvider.INSTANCE; : NoOpParameterValueProvider.INSTANCE;
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
S instance = instantiator.createInstance(entity, provider); S instance = instantiator.createInstance(entity, provider);
return populateProperties(context, entity, documentAccessor, evaluator, instance); return populateProperties(context, entity, documentAccessor, documentAccessor, instance);
} }
private <S> S populateProperties(ConversionContext context, MongoPersistentEntity<S> entity, private <S> S populateProperties(ConversionContext context, MongoPersistentEntity<S> entity,
@ -559,9 +593,7 @@ public class MappingMongoConverter extends AbstractMongoConverter
MongoDbPropertyValueProvider valueProvider = new MongoDbPropertyValueProvider(contextToUse, documentAccessor, MongoDbPropertyValueProvider valueProvider = new MongoDbPropertyValueProvider(contextToUse, documentAccessor,
evaluator, spELContext); evaluator, spELContext);
Predicate<MongoPersistentProperty> propertyFilter = isIdentifier(entity).or(isConstructorArgument(entity)) readProperties(contextToUse, entity, accessor, documentAccessor, valueProvider, evaluator, PROPERTY_FILTER);
.or(Predicates.negate(PersistentProperty::isReadable)).negate();
readProperties(contextToUse, entity, accessor, documentAccessor, valueProvider, evaluator, propertyFilter);
return accessor.getBean(); return accessor.getBean();
} }
@ -606,13 +638,13 @@ public class MappingMongoConverter extends AbstractMongoConverter
private void readProperties(ConversionContext context, MongoPersistentEntity<?> entity, private void readProperties(ConversionContext context, MongoPersistentEntity<?> entity,
PersistentPropertyAccessor<?> accessor, DocumentAccessor documentAccessor, PersistentPropertyAccessor<?> accessor, DocumentAccessor documentAccessor,
MongoDbPropertyValueProvider valueProvider, ValueExpressionEvaluator evaluator, MongoDbPropertyValueProvider valueProvider, ValueExpressionEvaluator evaluator,
Predicate<MongoPersistentProperty> propertyFilter) { BiPredicate<MongoPersistentEntity<?>, MongoPersistentProperty> propertyFilter) {
DbRefResolverCallback callback = null; DbRefResolverCallback callback = null;
for (MongoPersistentProperty prop : entity) { for (MongoPersistentProperty prop : entity) {
if (!propertyFilter.test(prop)) { if (!propertyFilter.test(entity, prop)) {
continue; continue;
} }
@ -1943,10 +1975,6 @@ public class MappingMongoConverter extends AbstractMongoConverter
MongoDbPropertyValueProvider(ConversionContext context, DocumentAccessor accessor, MongoDbPropertyValueProvider(ConversionContext context, DocumentAccessor accessor,
ValueExpressionEvaluator evaluator, SpELContext spELContext) { ValueExpressionEvaluator evaluator, SpELContext spELContext) {
Assert.notNull(context, "ConversionContext must no be null");
Assert.notNull(accessor, "DocumentAccessor must no be null");
Assert.notNull(evaluator, "ValueExpressionEvaluator must not be null");
this.context = context; this.context = context;
this.accessor = accessor; this.accessor = accessor;
this.evaluator = evaluator; this.evaluator = evaluator;
@ -2359,9 +2387,6 @@ public class MappingMongoConverter extends AbstractMongoConverter
public <S extends Object> S convert(Object source, TypeInformation<? extends S> typeHint, public <S extends Object> S convert(Object source, TypeInformation<? extends S> typeHint,
ConversionContext context) { ConversionContext context) {
Assert.notNull(source, "Source must not be null");
Assert.notNull(typeHint, "TypeInformation must not be null");
if (conversions.hasCustomReadTarget(source.getClass(), typeHint.getType())) { if (conversions.hasCustomReadTarget(source.getClass(), typeHint.getType())) {
return (S) elementConverter.convert(source, typeHint); return (S) elementConverter.convert(source, typeHint);
} }

12
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java

@ -30,6 +30,8 @@ import org.springframework.lang.Nullable;
*/ */
public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty { public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty {
private final Lazy<Boolean> isEntity = Lazy.of(super::isEntity);
private final Lazy<Boolean> isUnwrapped = Lazy.of(super::isUnwrapped);
private final Lazy<Boolean> isIdProperty = Lazy.of(super::isIdProperty); private final Lazy<Boolean> isIdProperty = Lazy.of(super::isIdProperty);
private final Lazy<Boolean> isAssociation = Lazy.of(super::isAssociation); private final Lazy<Boolean> isAssociation = Lazy.of(super::isAssociation);
private final Lazy<DBRef> dbref = Lazy.of(super::getDBRef); private final Lazy<DBRef> dbref = Lazy.of(super::getDBRef);
@ -58,6 +60,16 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty
super(property, owner, simpleTypeHolder, fieldNamingStrategy); super(property, owner, simpleTypeHolder, fieldNamingStrategy);
} }
@Override
public boolean isEntity() {
return isEntity.get();
}
@Override
public boolean isUnwrapped() {
return isUnwrapped.get();
}
@Override @Override
public boolean isIdProperty() { public boolean isIdProperty() {
return isIdProperty.get(); return isIdProperty.get();

8
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldName.java

@ -25,7 +25,11 @@ import org.springframework.util.ObjectUtils;
* @author Christoph Strobl * @author Christoph Strobl
* @since 4.2 * @since 4.2
*/ */
public record FieldName(String name, Type type) { public record FieldName(String name, Type type, String[] parts) {
public FieldName(String name, Type type) {
this(name, type, name.split("\\."));
}
private static final String ID_KEY = "_id"; private static final String ID_KEY = "_id";
@ -65,7 +69,7 @@ public record FieldName(String name, Type type) {
return new String[] { name }; return new String[] { name };
} }
return name.split("\\."); return parts;
} }
/** /**

Loading…
Cancel
Save