From 66b91b4a7608d2cdbe469f30828dbbdaabf3f29c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 11 Feb 2022 15:38:09 +0100 Subject: [PATCH] Introduce caching for commonly used model computations. We now cache the outcome for column types, AnnotatedType lookuop by annotation and bypass the conversion service by considering primitive type wrappers in the assignability check. Closes #1218 --- .../convert/DefaultColumnTypeResolver.java | 27 ++++++++++++++++++- .../convert/MappingCassandraConverter.java | 3 ++- .../BasicCassandraPersistentProperty.java | 4 ++- .../CachingCassandraPersistentProperty.java | 16 +++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/DefaultColumnTypeResolver.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/DefaultColumnTypeResolver.java index 0d5b78e2f..adc487b96 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/DefaultColumnTypeResolver.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/DefaultColumnTypeResolver.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import java.util.stream.StreamSupport; @@ -76,6 +77,8 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver { private final UserTypeResolver userTypeResolver; private final Supplier codecRegistry; private final Supplier customConversions; + private final Map columnTypeCache = new ConcurrentHashMap<>(); + private final Map, CassandraColumnType> typeInformationColumnTypeCache = new ConcurrentHashMap<>(); /** * Creates a new {@link DefaultColumnTypeResolver}. @@ -100,6 +103,18 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver { Assert.notNull(property, "Property must not be null"); + CassandraColumnType cassandraColumnType = columnTypeCache.get(property); + if (cassandraColumnType == null) { + // avoid recursive update + cassandraColumnType = doResolve(property); + columnTypeCache.put(property, cassandraColumnType); + } + + return cassandraColumnType; + } + + private CassandraColumnType doResolve(CassandraPersistentProperty property) { + if (property.isAnnotationPresent(CassandraType.class)) { CassandraType annotation = property.getRequiredAnnotation(CassandraType.class); @@ -169,7 +184,17 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver { */ @Override public CassandraColumnType resolve(TypeInformation typeInformation) { - return resolve(typeInformation, FrozenIndicator.NOT_FROZEN); + + Assert.notNull(typeInformation, "TypeInformation must not be null"); + + CassandraColumnType cassandraColumnType = typeInformationColumnTypeCache.get(typeInformation); + if (cassandraColumnType == null) { + // avoid recursive update + cassandraColumnType = resolve(typeInformation, FrozenIndicator.NOT_FROZEN); + typeInformationColumnTypeCache.put(typeInformation, cassandraColumnType); + } + + return cassandraColumnType; } private CassandraColumnType resolve(TypeInformation typeInformation, FrozenIndicator frozen) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java index b6f6234ab..fb081b388 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java @@ -926,7 +926,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter @SuppressWarnings({ "rawtypes", "unchecked" }) private Object getPotentiallyConvertedSimpleRead(Object value, @Nullable Class target) { - if (value == null || target == null || target.isAssignableFrom(value.getClass())) { + if (value == null || target == null + || ClassUtils.resolvePrimitiveIfNecessary(target).isAssignableFrom(value.getClass())) { return value; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentProperty.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentProperty.java index 22b4f618d..fddc05fe0 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentProperty.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentProperty.java @@ -113,7 +113,9 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP this.columnName = determineColumnName(); } - Assert.state(this.columnName != null, () -> String.format("Cannot determine column name for %s", this)); + if (columnName == null) { + throw new IllegalStateException(String.format("Cannot determine column name for %s", this)); + } return this.columnName; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CachingCassandraPersistentProperty.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CachingCassandraPersistentProperty.java index f3f481fc2..ef1f0c482 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CachingCassandraPersistentProperty.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CachingCassandraPersistentProperty.java @@ -15,6 +15,12 @@ */ package org.springframework.data.cassandra.core.mapping; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedType; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + import org.springframework.data.cassandra.core.cql.Ordering; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; @@ -36,6 +42,7 @@ public class CachingCassandraPersistentProperty extends BasicCassandraPersistent private final boolean isPrimaryKeyColumn; private final boolean isEmbedded; private final boolean isStaticColumn; + private final Map, Optional> findAnnotatedTypeCache = new ConcurrentHashMap<>(); public CachingCassandraPersistentProperty(Property property, CassandraPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { @@ -106,4 +113,13 @@ public class CachingCassandraPersistentProperty extends BasicCassandraPersistent public boolean isEmbedded() { return isEmbedded; } + + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentProperty#findAnnotatedType(Class) + */ + @Override + public AnnotatedType findAnnotatedType(Class annotationType) { + return findAnnotatedTypeCache + .computeIfAbsent(annotationType, key -> Optional.ofNullable(super.findAnnotatedType(key))).orElse(null); + } }