DATACASS-455 - Polishing.
Remove unused caches in CassandraMappingContext. Use CqlIdentifier instead of driver types (TableMetadata, UserType) to look up table/user type usage.
This commit is contained in:
@@ -23,6 +23,8 @@ import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity
|
||||
import org.springframework.data.cql.core.CqlIdentifier;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.datastax.driver.core.AbstractTableMetadata;
|
||||
|
||||
/**
|
||||
* Schema drop support for Cassandra based on {@link CassandraMappingContext} and {@link CassandraPersistentEntity}.
|
||||
* This class generates CQL to drop user types (UDT) and tables.
|
||||
@@ -63,9 +65,11 @@ public class CassandraPersistentEntitySchemaDropper {
|
||||
*/
|
||||
public void dropTables(boolean dropUnused) {
|
||||
|
||||
cassandraAdminOperations.getKeyspaceMetadata().getTables().stream()
|
||||
.filter(table -> dropUnused || mappingContext.usesTable(table))
|
||||
.forEach(table -> cassandraAdminOperations.dropTable(CqlIdentifier.cqlId(table.getName())));
|
||||
cassandraAdminOperations.getKeyspaceMetadata().getTables() //
|
||||
.stream() //
|
||||
.map(AbstractTableMetadata::getName) //
|
||||
.map(CqlIdentifier::cqlId) //
|
||||
.filter(table -> dropUnused || mappingContext.usesTable(table)).forEach(cassandraAdminOperations::dropTable);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,12 +85,13 @@ public class CassandraPersistentEntitySchemaDropper {
|
||||
.map(CassandraPersistentEntity::getTableName).collect(Collectors.toSet());
|
||||
|
||||
cassandraAdminOperations.getKeyspaceMetadata().getUserTypes().forEach(userType -> {
|
||||
CqlIdentifier identifier = CqlIdentifier.cqlId(userType.getTypeName());
|
||||
|
||||
if (canRecreate.contains(identifier)) {
|
||||
cassandraAdminOperations.dropUserType(identifier);
|
||||
} else if (dropUnused && !mappingContext.usesUserType(userType)) {
|
||||
cassandraAdminOperations.dropUserType(identifier);
|
||||
CqlIdentifier typeName = CqlIdentifier.cqlId(userType.getTypeName());
|
||||
|
||||
if (canRecreate.contains(typeName)) {
|
||||
cassandraAdminOperations.dropUserType(typeName);
|
||||
} else if (dropUnused && !mappingContext.usesUserType(typeName)) {
|
||||
cassandraAdminOperations.dropUserType(typeName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
@@ -52,8 +53,6 @@ import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.datastax.driver.core.DataType;
|
||||
import com.datastax.driver.core.TableMetadata;
|
||||
import com.datastax.driver.core.UserType;
|
||||
|
||||
/**
|
||||
* Default implementation of a {@link MappingContext} for Cassandra using {@link CassandraPersistentEntity} and
|
||||
@@ -67,7 +66,7 @@ import com.datastax.driver.core.UserType;
|
||||
*/
|
||||
public class CassandraMappingContext
|
||||
extends AbstractMappingContext<BasicCassandraPersistentEntity<?>, CassandraPersistentProperty>
|
||||
implements ApplicationContextAware {
|
||||
implements ApplicationContextAware, BeanClassLoaderAware {
|
||||
|
||||
private CassandraPersistentEntityMetadataVerifier verifier = new CompositeCassandraPersistentEntityMetadataVerifier();
|
||||
|
||||
@@ -82,10 +81,7 @@ public class CassandraMappingContext
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
// useful caches
|
||||
private final Map<Class<?>, CassandraPersistentEntity<?>> entitiesByType = new HashMap<>();
|
||||
private final Map<CqlIdentifier, Set<CassandraPersistentEntity<?>>> entitySetsByTableName = new HashMap<>();
|
||||
|
||||
private final Set<BasicCassandraPersistentEntity<?>> primaryKeyEntities = new HashSet<>();
|
||||
private final Set<BasicCassandraPersistentEntity<?>> userDefinedTypes = new HashSet<>();
|
||||
private final Set<BasicCassandraPersistentEntity<?>> tableEntities = new HashSet<>();
|
||||
|
||||
@@ -110,29 +106,32 @@ public class CassandraMappingContext
|
||||
@SuppressWarnings("all")
|
||||
protected void processMappingOverrides() {
|
||||
|
||||
mapping.getEntityMappings().stream()//
|
||||
.filter(entityMapping -> entityMapping != null).forEach(entityMapping -> {
|
||||
String entityClassName = entityMapping.getEntityClassName();
|
||||
mapping.getEntityMappings().stream() //
|
||||
.filter(entityMapping -> entityMapping != null) //
|
||||
.forEach(entityMapping -> {
|
||||
|
||||
try {
|
||||
Class<?> entityClass = ClassUtils.forName(entityClassName, beanClassLoader);
|
||||
Class<?> entityClass = getEntityClass(entityMapping.getEntityClassName());
|
||||
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
|
||||
|
||||
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
|
||||
String entityTableName = entityMapping.getTableName();
|
||||
|
||||
String entityTableName = entityMapping.getTableName();
|
||||
|
||||
if (StringUtils.hasText(entityTableName)) {
|
||||
entity.setTableName(cqlId(entityTableName, Boolean.valueOf(entityMapping.getForceQuote())));
|
||||
}
|
||||
|
||||
processMappingOverrides(entity, entityMapping);
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(String.format("Unknown persistent entity name [%s]", entityClassName), e);
|
||||
if (StringUtils.hasText(entityTableName)) {
|
||||
entity.setTableName(cqlId(entityTableName, Boolean.valueOf(entityMapping.getForceQuote())));
|
||||
}
|
||||
|
||||
processMappingOverrides(entity, entityMapping);
|
||||
});
|
||||
}
|
||||
|
||||
private Class<?> getEntityClass(String entityClassName) {
|
||||
|
||||
try {
|
||||
return ClassUtils.forName(entityClassName, beanClassLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(String.format("Unknown persistent entity name [%s]", entityClassName), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void processMappingOverrides(CassandraPersistentEntity<?> entity, EntityMapping entityMapping) {
|
||||
entityMapping.getPropertyMappings()
|
||||
.forEach((key, propertyMapping) -> processMappingOverride(entity, propertyMapping));
|
||||
@@ -159,6 +158,9 @@ public class CassandraMappingContext
|
||||
this.context = applicationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader)
|
||||
*/
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
@@ -223,29 +225,12 @@ public class CassandraMappingContext
|
||||
/**
|
||||
* Returns only those entities representing a user defined type.
|
||||
*
|
||||
* @see #getPersistentEntities(boolean)
|
||||
* @since 1.5
|
||||
*/
|
||||
public Collection<CassandraPersistentEntity<?>> getUserDefinedTypeEntities() {
|
||||
return Collections.unmodifiableSet(userDefinedTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all persistent entities or only non-primary-key entities.
|
||||
*
|
||||
* @param includePrimaryKeyTypesAndUdts If {@literal true}, returns all entities, including entities that represent
|
||||
* primary key types and user-defined types. If {@literal false}, returns only entities that don't represent
|
||||
* primary key types and no user-defined types.
|
||||
*/
|
||||
public Collection<BasicCassandraPersistentEntity<?>> getPersistentEntities(boolean includePrimaryKeyTypesAndUdts) {
|
||||
|
||||
if (includePrimaryKeyTypesAndUdts) {
|
||||
return super.getPersistentEntities();
|
||||
}
|
||||
|
||||
return getTableEntities();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.AbstractMappingContext#addPersistentEntity(org.springframework.data.util.TypeInformation)
|
||||
*/
|
||||
@@ -275,8 +260,6 @@ public class CassandraMappingContext
|
||||
|
||||
entity.findAnnotation(Table.class).ifPresent(table -> tableEntities.add(entity));
|
||||
}
|
||||
|
||||
entitiesByType.put(entity.getType(), entity);
|
||||
});
|
||||
|
||||
return optional;
|
||||
@@ -328,25 +311,28 @@ public class CassandraMappingContext
|
||||
/**
|
||||
* Returns whether this mapping context has any entities mapped to the given table.
|
||||
*
|
||||
* @param table must not be {@literal null}.
|
||||
* @param name must not be {@literal null}.
|
||||
* @return @return {@literal true} is this {@literal TableMetadata} is used by a mapping.
|
||||
*/
|
||||
public boolean usesTable(TableMetadata table) {
|
||||
return entitySetsByTableName.containsKey(cqlId(table.getName()));
|
||||
public boolean usesTable(CqlIdentifier name) {
|
||||
|
||||
Assert.notNull(name, "Table name must not be null!");
|
||||
|
||||
return entitySetsByTableName.containsKey(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this mapping context has any entities using the given user type.
|
||||
*
|
||||
* @param userType must not be {@literal null}.
|
||||
* @param name must not be {@literal null}.
|
||||
* @return {@literal true} is this {@literal UserType} is used.
|
||||
* @since 1.5
|
||||
*/
|
||||
public boolean usesUserType(final UserType userType) {
|
||||
public boolean usesUserType(CqlIdentifier name) {
|
||||
|
||||
CqlIdentifier identifier = CqlIdentifier.cqlId(userType.getTypeName());
|
||||
Assert.notNull(name, "User type name must not be null!");
|
||||
|
||||
return (hasMappedUserType(identifier) || hasReferencedUserType(identifier));
|
||||
return hasMappedUserType(name) || hasReferencedUserType(name);
|
||||
}
|
||||
|
||||
private boolean hasReferencedUserType(final CqlIdentifier identifier) {
|
||||
@@ -356,10 +342,10 @@ public class CassandraMappingContext
|
||||
.map(it -> it.findAnnotation(CassandraType.class)) //
|
||||
.filter(Optional::isPresent) //
|
||||
.flatMap(Optionals::toStream) //
|
||||
.anyMatch(it -> {
|
||||
return StringUtils.hasText(it.userTypeName()) //
|
||||
&& CqlIdentifier.cqlId(it.userTypeName()).equals(identifier);
|
||||
}); //
|
||||
.map(CassandraType::userTypeName) //
|
||||
.filter(StringUtils::hasText) //
|
||||
.map(CqlIdentifier::cqlId) //
|
||||
.anyMatch(identifier::equals); //
|
||||
}
|
||||
|
||||
private boolean hasMappedUserType(CqlIdentifier identifier) {
|
||||
@@ -537,14 +523,6 @@ public class CassandraMappingContext
|
||||
.orElseGet(() -> getDataTypeFor(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this {@link CassandraMappingContext} already contains a {@link CassandraPersistentEntity} for the
|
||||
* given type.
|
||||
*/
|
||||
public boolean contains(Class<?> type) {
|
||||
return entitiesByType.containsKey(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
* @since 1.5.1
|
||||
|
||||
@@ -68,7 +68,7 @@ public class CassandraMappingContextUnitTests {
|
||||
|
||||
private static class Transient {}
|
||||
|
||||
@Test // DATACASS-282
|
||||
@Test // DATACASS-282, DATACASS-455
|
||||
public void testGetExistingPersistentEntityHappyPath() {
|
||||
|
||||
TableMetadata tableMetadata = mock(TableMetadata.class);
|
||||
@@ -76,11 +76,10 @@ public class CassandraMappingContextUnitTests {
|
||||
|
||||
mappingContext.getRequiredPersistentEntity(X.class);
|
||||
|
||||
assertThat(mappingContext.contains(Y.class)).isFalse();
|
||||
assertThat(mappingContext.getUserDefinedTypeEntities()).isEmpty();
|
||||
assertThat(mappingContext.getTableEntities()).hasSize(1);
|
||||
assertThat(mappingContext.contains(X.class)).isTrue();
|
||||
assertThat(mappingContext.usesTable(tableMetadata)).isTrue();
|
||||
assertThat(mappingContext.getPersistentEntities()).hasSize(1);
|
||||
assertThat(mappingContext.usesTable(CqlIdentifier.cqlId(tableMetadata.getName()))).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATACASS-248
|
||||
@@ -353,7 +352,7 @@ public class CassandraMappingContextUnitTests {
|
||||
.isEqualTo(DataType.list(DataType.varchar()));
|
||||
}
|
||||
|
||||
@Test // DATACASS-172
|
||||
@Test // DATACASS-172, DATACASS-455
|
||||
public void shouldRegisterUdtTypes() {
|
||||
|
||||
CassandraPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(MappedUdt.class);
|
||||
@@ -361,8 +360,8 @@ public class CassandraMappingContextUnitTests {
|
||||
assertThat(persistentEntity.isUserDefinedType()).isTrue();
|
||||
|
||||
assertThat(mappingContext.getUserDefinedTypeEntities()).hasSize(1);
|
||||
assertThat(mappingContext.getPersistentEntities()).hasSize(1);
|
||||
assertThat(mappingContext.getTableEntities()).hasSize(0);
|
||||
assertThat(mappingContext.contains(MappedUdt.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATACASS-172
|
||||
@@ -380,45 +379,38 @@ public class CassandraMappingContextUnitTests {
|
||||
BasicCassandraPersistentEntity<?> existingPersistentEntity = mappingContext
|
||||
.getRequiredPersistentEntity(MappedUdt.class);
|
||||
|
||||
assertThat(mappingContext.getPersistentEntities(true)).contains(existingPersistentEntity);
|
||||
assertThat(mappingContext.getPersistentEntities()).contains(existingPersistentEntity);
|
||||
assertThat(mappingContext.getUserDefinedTypeEntities()).contains(existingPersistentEntity);
|
||||
assertThat(mappingContext.getPersistentEntities(false)).doesNotContain(existingPersistentEntity);
|
||||
assertThat(mappingContext.getTableEntities()).doesNotContain(existingPersistentEntity);
|
||||
}
|
||||
|
||||
@Test // DATACASS-172
|
||||
@Test // DATACASS-172, DATACASS-455
|
||||
public void usesTypeShouldNotReportTypeUsage() {
|
||||
|
||||
UserType myTypeMock = mock(UserType.class, "mappedudt");
|
||||
when(myTypeMock.getTypeName()).thenReturn("mappedudt");
|
||||
|
||||
assertThat(mappingContext.usesUserType(myTypeMock)).isFalse();
|
||||
assertThat(mappingContext.usesUserType(CqlIdentifier.cqlId("mappedudt"))).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATACASS-172
|
||||
@Test // DATACASS-172, DATACASS-455
|
||||
public void usesTypeShouldReportTypeUsageInMappedUdt() {
|
||||
|
||||
UserType myTypeMock = mock(UserType.class, "mappedudt");
|
||||
when(myTypeMock.getTypeName()).thenReturn("mappedudt");
|
||||
|
||||
mappingContext.setUserTypeResolver(typeName -> myTypeMock);
|
||||
|
||||
mappingContext.getRequiredPersistentEntity(WithUdt.class);
|
||||
|
||||
assertThat(mappingContext.usesUserType(myTypeMock)).isTrue();
|
||||
assertThat(mappingContext.usesUserType(CqlIdentifier.cqlId("mappedudt"))).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATACASS-172
|
||||
@Test // DATACASS-172, DATACASS-455
|
||||
public void usesTypeShouldReportTypeUsageInColumn() {
|
||||
|
||||
UserType myTypeMock = mock(UserType.class, "mappedudt");
|
||||
when(myTypeMock.getTypeName()).thenReturn("mappedudt");
|
||||
|
||||
mappingContext.setUserTypeResolver(typeName -> myTypeMock);
|
||||
|
||||
mappingContext.getRequiredPersistentEntity(MappedUdt.class);
|
||||
|
||||
assertThat(mappingContext.usesUserType(myTypeMock)).isTrue();
|
||||
assertThat(mappingContext.usesUserType(CqlIdentifier.cqlId("mappedudt"))).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATACASS-172
|
||||
@@ -449,7 +441,7 @@ public class CassandraMappingContextUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-282
|
||||
@Test // DATACASS-282, DATACASS-455
|
||||
public void shouldNotRetainInvalidEntitiesInCache() {
|
||||
|
||||
TableMetadata tableMetadata = mock(TableMetadata.class);
|
||||
@@ -465,8 +457,8 @@ public class CassandraMappingContextUnitTests {
|
||||
|
||||
assertThat(mappingContext.getUserDefinedTypeEntities()).isEmpty();
|
||||
assertThat(mappingContext.getTableEntities()).isEmpty();
|
||||
assertThat(mappingContext.contains(InvalidEntityWithIdAndPrimaryKeyColumn.class)).isFalse();
|
||||
assertThat(mappingContext.usesTable(tableMetadata)).isFalse();
|
||||
assertThat(mappingContext.getPersistentEntities()).isEmpty();
|
||||
assertThat(mappingContext.usesTable(CqlIdentifier.cqlId(tableMetadata.getName()))).isFalse();
|
||||
}
|
||||
|
||||
@Table
|
||||
|
||||
Reference in New Issue
Block a user