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:
Mark Paluch
2017-06-06 12:28:24 +02:00
parent 820fef2ecd
commit c0892b4aef
3 changed files with 66 additions and 91 deletions

View File

@@ -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);
}
});
}

View File

@@ -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

View File

@@ -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