From f855b582acd939db7ddb35e7ed3cc9ff44498d70 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 0ef904745..0e4d3c475 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 @@ -1063,7 +1063,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 d83a5d5be..86cb13288 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 @@ -114,7 +114,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); + } }