Correctly apply TableOptions through CassandraAdminTemplate.createTable(…).

Closes: #359
Original pull request: #1385
This commit is contained in:
Mikhail2048
2023-05-26 22:40:17 +03:00
committed by Mark Paluch
parent d4bbdcbd09
commit cd444b2286
10 changed files with 76 additions and 17 deletions

View File

@@ -55,6 +55,7 @@ public interface CassandraAdminOperations extends CassandraOperations {
void createTable(boolean ifNotExists, CqlIdentifier tableName, Class<?> entityClass,
Map<String, Object> optionsByName);
/**
* Drops a table based on the given {@link Class entity type}. The name of the table is derived from either the simple
* name of the {@link Class entity class} or name of the table specified with the {@link Table} mapping annotation.

View File

@@ -29,8 +29,10 @@ import org.springframework.data.cassandra.core.cql.generator.DropUserTypeCqlGene
import org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.DropTableSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.DropUserTypeSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.TableOption;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
@@ -44,6 +46,7 @@ import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
* @author Fabio J. Mendes
* @author John Blum
* @author Vagif Zeynalov
* @author Mikhail Polivakha
*/
public class CassandraAdminTemplate extends CassandraTemplate implements CassandraAdminOperations {
@@ -105,13 +108,23 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
}
@Override
public void createTable(boolean ifNotExists, CqlIdentifier tableName, Class<?> entityClass,
Map<String, Object> optionsByName) {
public void createTable(boolean ifNotExists, CqlIdentifier tableName, Class<?> entityClass, Map<String, Object> optionsByName) {
CassandraPersistentEntity<?> entity = getConverter().getMappingContext().getRequiredPersistentEntity(entityClass);
CreateTableSpecification createTableSpecification = this.schemaFactory
.getCreateTableSpecificationFor(entity, tableName).ifNotExists(ifNotExists);
.getCreateTableSpecificationFor(entity, tableName)
.ifNotExists(ifNotExists);
if (!CollectionUtils.isEmpty(optionsByName)) {
optionsByName.forEach((key, value) -> {
TableOption tableOption = TableOption.valueOfIgnoreCase(key);
if (tableOption.requiresValue()) {
createTableSpecification.with(tableOption, value);
} else {
createTableSpecification.with(tableOption);
}
});
}
getCqlOperations().execute(CreateTableCqlGenerator.toCql(createTableSpecification));
}

View File

@@ -196,7 +196,7 @@ class EntityOperations {
StatementBuilder<Delete> appendVersionCondition(StatementBuilder<Delete> delete);
/**
* Initializes the version property of the of the current entity if available.
* Initializes the version property of the current entity if available.
*
* @return the entity with the version property updated if available.
*/

View File

@@ -295,6 +295,7 @@ public class StatementFactory {
Assert.notNull(persistentEntity, "CassandraPersistentEntity must not be null");
boolean insertNulls;
if (options instanceof InsertOptions) {
InsertOptions insertOptions = (InsertOptions) options;

View File

@@ -258,12 +258,12 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
@SuppressWarnings("unchecked")
private <S> ConvertingPropertyAccessor<S> newConvertingPropertyAccessor(S source,
CassandraPersistentEntity<?> entity) {
PersistentPropertyAccessor<S> propertyAccessor = source instanceof PersistentPropertyAccessor
? (PersistentPropertyAccessor<S>) source
: entity.getPropertyAccessor(source);
? (PersistentPropertyAccessor<S>) source
: entity.getPropertyAccessor(source);
return new ConvertingPropertyAccessor<>(propertyAccessor, getConversionService());
}
private <S> CassandraPersistentEntityParameterValueProvider newParameterValueProvider(ConversionContext context,

View File

@@ -147,7 +147,7 @@ public class SchemaFactory {
if (property.isCompositePrimaryKey()) {
CassandraPersistentEntity<?> primaryKeyEntity = mappingContext
.getRequiredPersistentEntity(property.getRawType());
.getRequiredPersistentEntity(property.getRawType());
for (CassandraPersistentProperty primaryKeyProperty : primaryKeyEntity) {

View File

@@ -43,9 +43,8 @@ class UserTypeUtil {
Assert.notNull(dataType, "DataType must not be null");
if (dataType instanceof ListType) {
if (dataType instanceof ListType collectionType) {
ListType collectionType = (ListType) dataType;
DataType elementType = collectionType.getElementType();
if (isCollectionType(elementType) || isNonFrozenUdt(elementType)) {
@@ -53,9 +52,8 @@ class UserTypeUtil {
}
}
if (dataType instanceof SetType) {
if (dataType instanceof SetType collectionType) {
SetType collectionType = (SetType) dataType;
DataType elementType = collectionType.getElementType();
if (isCollectionType(elementType) || isNonFrozenUdt(elementType)) {
@@ -63,9 +61,7 @@ class UserTypeUtil {
}
}
if (dataType instanceof MapType) {
MapType collectionType = (MapType) dataType;
if (dataType instanceof MapType collectionType) {
DataType keyType = collectionType.getKeyType();
DataType valueType = collectionType.getValueType();

View File

@@ -18,6 +18,7 @@ package org.springframework.data.cassandra.core.cql.keyspace;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Enumeration that represents all known table options. If a table option is not listed here, but is supported by
@@ -26,6 +27,7 @@ import org.springframework.lang.Nullable;
*
* @author Matthew T. Adams
* @author Mark Paluch
* @author Mikhail Polivakha
* @see CompactionOption
* @see CompressionOption
* @see CachingOption
@@ -84,6 +86,15 @@ public enum TableOption implements Option {
this.delegate = new DefaultOption(name, type, requiresValue, escapesValue, quotesValue);
}
public static TableOption valueOfIgnoreCase(String optionName) {
for (TableOption value : values()) {
if (value.getName().equalsIgnoreCase(optionName)) {
return value;
}
}
throw new IllegalArgumentException(String.format("Unable to recognize specified Table option '%s'", optionName));
}
@Override
public Class<?> getType() {
return this.delegate.getType();

View File

@@ -296,7 +296,7 @@ public class TableSpecification<T> extends TableOptionsSpecification<TableSpecif
this.columns.add(column);
if (!optionalKeyType.isPresent()) {
if (optionalKeyType.isEmpty()) {
this.nonKeyColumns.add(column);
}

View File

@@ -17,13 +17,19 @@ package org.springframework.data.cassandra.core;
import static org.assertj.core.api.Assertions.*;
import java.time.LocalDate;
import java.util.Collection;
import java.util.Map;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.cql.generator.DropTableCqlGenerator;
import org.springframework.data.cassandra.core.cql.keyspace.DropTableSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.TableOption;
import org.springframework.data.cassandra.core.mapping.Table;
import org.springframework.data.cassandra.domain.User;
import org.springframework.data.cassandra.test.util.AbstractKeyspaceCreatingIntegrationTests;
@@ -36,6 +42,7 @@ import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
* Integration tests for {@link CassandraAdminTemplate}.
*
* @author Mark Paluch
* @author Mikhail Polivakha
*/
class CassandraAdminTemplateIntegrationTests extends AbstractKeyspaceCreatingIntegrationTests {
@@ -59,6 +66,27 @@ class CassandraAdminTemplateIntegrationTests extends AbstractKeyspaceCreatingInt
return getSession().getKeyspace().flatMap(metadata::getKeyspace).get();
}
@Test
void givenAdminTemplate_whenCreateTableWithOptions_ThenCreatedTableContainsTheseOptions() {
cassandraAdminTemplate.createTable(
true,
CqlIdentifier.fromCql("someTable"),
SomeTable.class,
Map.of(
TableOption.COMMENT.getName(), "This is comment for table",
TableOption.BLOOM_FILTER_FP_CHANCE.getName(), "0.3"
)
);
TableMetadata someTable = getKeyspaceMetadata().getTables().values().stream().findFirst().orElse(null);
Assertions.assertThat(someTable).isNotNull();
Assertions.assertThat(someTable.getOptions().get(CqlIdentifier.fromCql(TableOption.COMMENT.getName())))
.isEqualTo("This is comment for table");
Assertions.assertThat(someTable.getOptions().get(CqlIdentifier.fromCql(TableOption.BLOOM_FILTER_FP_CHANCE.getName())))
.isEqualTo(0.3);
}
@Test // DATACASS-173
void testCreateTables() {
@@ -85,4 +113,13 @@ class CassandraAdminTemplateIntegrationTests extends AbstractKeyspaceCreatingInt
assertThat(getKeyspaceMetadata().getTables()).hasSize(0);
}
@Table("someTable")
private static class SomeTable {
@Id
private String name;
private Integer number;
private LocalDate createdAt;
}
}