DATAJDBC-399 - Performance improvements in metadata lookups.

Avoid Stream usage in favor of a simple for loop in event triggering in JdbcAggregateTemplate. Tweaked JdbcMappingContext to verify the presence of parameter names on metadata creation. Avoid the re-resolution of the column name for a property by caching the resolved column name. This significantly improves performance as it avoids repeated parsing and concatenation of strings. Added caching to PersistentPropertyPathExtension.
This commit is contained in:
Oliver Drotbohm
2019-07-23 08:36:38 +02:00
parent 1fb404a84e
commit 5a8abe73e6
9 changed files with 196 additions and 32 deletions

View File

@@ -15,10 +15,10 @@
*/
package org.springframework.data.jdbc.core;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
@@ -364,13 +364,17 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
private <T> Iterable<T> triggerAfterLoad(Iterable<T> all) {
return StreamSupport.stream(all.spliterator(), false).map(e -> {
List<T> result = new ArrayList<>();
for (T e : all) {
RelationalPersistentEntity<?> entity = context.getRequiredPersistentEntity(e.getClass());
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(e);
return triggerAfterLoad(identifierAccessor.getRequiredIdentifier(), e);
}).collect(Collectors.toList());
result.add(triggerAfterLoad(identifierAccessor.getRequiredIdentifier(), e));
}
return result;
}
private <T> T triggerAfterLoad(Object id, T entity) {

View File

@@ -18,9 +18,9 @@ package org.springframework.data.jdbc.core.convert;
import lombok.RequiredArgsConstructor;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
* Provides {@link SqlGenerator}s per domain type. Instances get cached, so when asked multiple times for the same
@@ -31,12 +31,10 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
@RequiredArgsConstructor
public class SqlGeneratorSource {
private final Map<Class, SqlGenerator> sqlGeneratorCache = new ConcurrentHashMap<>();
private final Map<Class<?>, SqlGenerator> CACHE = new ConcurrentReferenceHashMap<>();
private final RelationalMappingContext context;
SqlGenerator getSqlGenerator(Class<?> domainType) {
return sqlGeneratorCache.computeIfAbsent(domainType,
t -> new SqlGenerator(context, context.getRequiredPersistentEntity(t)));
return CACHE.computeIfAbsent(domainType, t -> new SqlGenerator(context, context.getRequiredPersistentEntity(t)));
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.jdbc.core.mapping;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
@@ -23,6 +25,8 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link MappingContext} implementation for JDBC.
@@ -35,6 +39,8 @@ import org.springframework.data.util.TypeInformation;
*/
public class JdbcMappingContext extends RelationalMappingContext {
private static final String MISSING_PARAMETER_NAME = "A constructor parameter name must not be null to be used with Spring Data JDBC! Offending parameter: %s";
/**
* Creates a new {@link JdbcMappingContext}.
*/
@@ -51,6 +57,27 @@ public class JdbcMappingContext extends RelationalMappingContext {
super(namingStrategy);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.mapping.RelationalMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
*/
@Override
protected <T> RelationalPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
RelationalPersistentEntity<T> entity = super.createPersistentEntity(typeInformation);
PreferredConstructor<T, RelationalPersistentProperty> constructor = entity.getPersistenceConstructor();
if (constructor == null) {
return entity;
}
for (Parameter<Object, RelationalPersistentProperty> parameter : constructor.getParameters()) {
Assert.state(StringUtils.hasText(parameter.getName()), () -> String.format(MISSING_PARAMETER_NAME, parameter));
}
return entity;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(org.springframework.data.mapping.model.Property, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder)

View File

@@ -20,7 +20,6 @@ import static org.assertj.core.api.Assertions.*;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.convert.SqlGenerator;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils;
import org.springframework.data.mapping.PersistentPropertyPath;