From e40c40b49c94509c271b1ea4a299d36d7763acd5 Mon Sep 17 00:00:00 2001 From: Albert Shift Date: Mon, 30 Mar 2015 22:06:40 -0700 Subject: [PATCH] refactor existing classes --- .../com/noorq/casser/core/SchemaUtil.java | 121 +++++++++---- .../noorq/casser/core/SessionInitializer.java | 8 +- .../noorq/casser/core/TableOperations.java | 23 +++ .../core/reflect/DslInvocationHandler.java | 10 +- .../casser/mapping/CasserMappingProperty.java | 160 +++++++++--------- .../mapping/CasserMappingRepository.java | 7 +- .../noorq/casser/mapping/CasserProperty.java | 10 +- .../com/noorq/casser/mapping/CqlUtil.java | 5 + .../com/noorq/casser/mapping/MappingUtil.java | 20 +++ 9 files changed, 242 insertions(+), 122 deletions(-) diff --git a/src/main/java/com/noorq/casser/core/SchemaUtil.java b/src/main/java/com/noorq/casser/core/SchemaUtil.java index ce6f93f..549e703 100644 --- a/src/main/java/com/noorq/casser/core/SchemaUtil.java +++ b/src/main/java/com/noorq/casser/core/SchemaUtil.java @@ -26,18 +26,21 @@ import com.datastax.driver.core.DataType; import com.datastax.driver.core.RegularStatement; import com.datastax.driver.core.SimpleStatement; import com.datastax.driver.core.TableMetadata; +import com.datastax.driver.core.UserType; import com.datastax.driver.core.schemabuilder.Alter; import com.datastax.driver.core.schemabuilder.Create; import com.datastax.driver.core.schemabuilder.Create.Options; import com.datastax.driver.core.schemabuilder.CreateType; import com.datastax.driver.core.schemabuilder.SchemaBuilder; import com.datastax.driver.core.schemabuilder.SchemaStatement; +import com.datastax.driver.core.schemabuilder.UDTType; import com.noorq.casser.mapping.CasserEntityType; import com.noorq.casser.mapping.CasserMappingEntity; import com.noorq.casser.mapping.CasserMappingProperty; import com.noorq.casser.mapping.CqlUtil; import com.noorq.casser.mapping.OrderingDirection; import com.noorq.casser.support.CasserMappingException; +import com.noorq.casser.support.Either; public final class SchemaUtil { @@ -69,7 +72,18 @@ public final class SchemaUtil { throw new CasserMappingException("primary key columns are not supported in UserDefinedType for column " + columnName + " in entity " + entity); } - create.addColumn(columnName, prop.getDataType()); + Either type = prop.getColumnType(); + + if (type.isLeft()) { + create.addColumn(columnName, type.getLeft()); + } + else if (type.isRight()) { + UDTType udtType = SchemaBuilder.frozen(type.getRight()); + create.addUDTColumn(prop.getColumnName(), udtType); + } + else { + throwNoMapping(prop); + } } @@ -110,16 +124,26 @@ public final class SchemaUtil { OrdinalBasedPropertyComparator.INSTANCE); for (CasserMappingProperty prop : partitionKeys) { - create.addPartitionKey(prop.getColumnName(), prop.getDataType()); + + Either type = prop.getColumnType(); + + if (type.isRight()) { + throw new CasserMappingException("user defined type can not be a partition key for " + prop.getPropertyName() + " in " + prop.getEntity()); + } + + create.addPartitionKey(prop.getColumnName(), type.getLeft()); } for (CasserMappingProperty prop : clusteringColumns) { - if (prop.getDataType() != null) { - create.addClusteringColumn(prop.getColumnName(), prop.getDataType()); + Either type = prop.getColumnType(); + + if (type.isLeft()) { + create.addClusteringColumn(prop.getColumnName(), type.getLeft()); } - else if (prop.getUDTType() != null) { - create.addUDTClusteringColumn(prop.getColumnName(), prop.getUDTType()); + else if (type.isRight()) { + UDTType udtType = SchemaBuilder.frozen(type.getRight()); + create.addUDTClusteringColumn(prop.getColumnName(), udtType); } else { throwNoMapping(prop); @@ -129,13 +153,16 @@ public final class SchemaUtil { for (CasserMappingProperty prop : columns) { + Either type = prop.getColumnType(); + if (prop.isStatic()) { - if (prop.getDataType() != null) { - create.addStaticColumn(prop.getColumnName(), prop.getDataType()); + if (type.isLeft()) { + create.addStaticColumn(prop.getColumnName(), type.getLeft()); } - else if (prop.getUDTType() != null) { - create.addUDTStaticColumn(prop.getColumnName(), prop.getUDTType()); + else if (type.isRight()) { + UDTType udtType = SchemaBuilder.frozen(type.getRight()); + create.addUDTStaticColumn(prop.getColumnName(), udtType); } else { throwNoMapping(prop); @@ -143,11 +170,12 @@ public final class SchemaUtil { } else { - if (prop.getDataType() != null) { - create.addColumn(prop.getColumnName(), prop.getDataType()); + if (type.isLeft()) { + create.addColumn(prop.getColumnName(), type.getLeft()); } - else if (prop.getUDTType() != null) { - create.addUDTColumn(prop.getColumnName(), prop.getUDTType()); + else if (type.isRight()) { + UDTType udtType = SchemaBuilder.frozen(type.getRight()); + create.addUDTColumn(prop.getColumnName(), udtType); } else { throwNoMapping(prop); @@ -187,37 +215,61 @@ public final class SchemaUtil { for (CasserMappingProperty prop : entity.getMappingProperties()) { String columnName = prop.getColumnName(); - DataType columnDataType = prop.getDataType(); - String loweredColumnName = columnName.toLowerCase(); if (dropRemovedColumns) { visitedColumns.add(loweredColumnName); } - ColumnMetadata columnMetadata = tmd.getColumn(loweredColumnName); - - if (columnMetadata != null - && columnDataType.equals(columnMetadata.getType())) { - continue; - } - if (prop.isPartitionKey() || prop.isClusteringColumn()) { throw new CasserMappingException( "unable to alter column that is a part of primary key '" + columnName + "' for entity " + entity); } + + Either type = prop.getColumnType(); + + ColumnMetadata columnMetadata = tmd.getColumn(loweredColumnName); - if (columnMetadata == null) { - - result.add(alter.addColumn(columnName).type(columnDataType)); + if (columnMetadata != null) { - } else { - - result.add(alter.alterColumn(columnName).type(columnDataType)); + if (type.isLeft()) { + + if (!type.getLeft().equals(columnMetadata.getType())) { + result.add(alter.alterColumn(columnName).type(type.getLeft())); + } + + } + else if (type.isRight()) { + + DataType metadataType = columnMetadata.getType(); + if (metadataType.getName() == DataType.Name.UDT && + metadataType instanceof UserType) { + + UserType metadataUserType = (UserType) metadataType; + + if (!type.getRight().equals(metadataUserType.getTypeName())) { + UDTType udtType = SchemaBuilder.frozen(type.getRight()); + result.add(alter.alterColumn(columnName).udtType(udtType)); + } + + } + else { + throw new CasserMappingException("expected UserType in metadata " + metadataType + " for " + prop.getPropertyName() + " in " + prop.getEntity()); + } + + } } + else if (type.isLeft()) { + result.add(alter.addColumn(columnName).type(type.getLeft())); + } + else if (type.isRight()) { + UDTType udtType = SchemaBuilder.frozen(type.getRight()); + result.add(alter.addColumn(columnName).udtType(udtType)); + } + } if (dropRemovedColumns) { @@ -239,7 +291,16 @@ public final class SchemaUtil { throw new CasserMappingException("expected table entity " + entity); } - return SchemaBuilder.dropTable(entity.getName()); + return SchemaBuilder.dropTable(entity.getName()).ifExists(); + + } + + public static SchemaStatement createIndex(CasserMappingProperty prop) { + + return SchemaBuilder.createIndex(prop.getIndexName().get()) + .ifNotExists() + .onTable(prop.getEntity().getName()) + .andColumn(prop.getColumnName()); } diff --git a/src/main/java/com/noorq/casser/core/SessionInitializer.java b/src/main/java/com/noorq/casser/core/SessionInitializer.java index 2e1d9f2..0ceff89 100644 --- a/src/main/java/com/noorq/casser/core/SessionInitializer.java +++ b/src/main/java/com/noorq/casser/core/SessionInitializer.java @@ -45,6 +45,7 @@ public class SessionInitializer extends AbstractSessionOperations { private CasserMappingRepository mappingRepository = new CasserMappingRepository(); private boolean dropRemovedColumns = false; + private boolean dropRemovedIndexes = false; private KeyspaceMetadata keyspaceMetadata; @@ -106,7 +107,12 @@ public class SessionInitializer extends AbstractSessionOperations { this.dropRemovedColumns = enabled; return this; } - + + public SessionInitializer dropRemovedIndexes(boolean enabled) { + this.dropRemovedIndexes = enabled; + return this; + } + @Override public boolean isShowCql() { return showCql; diff --git a/src/main/java/com/noorq/casser/core/TableOperations.java b/src/main/java/com/noorq/casser/core/TableOperations.java index 309ab8e..ab7c122 100644 --- a/src/main/java/com/noorq/casser/core/TableOperations.java +++ b/src/main/java/com/noorq/casser/core/TableOperations.java @@ -16,6 +16,7 @@ package com.noorq.casser.core; import java.util.List; +import java.util.stream.Collectors; import com.datastax.driver.core.RegularStatement; import com.datastax.driver.core.TableMetadata; @@ -36,7 +37,16 @@ public final class TableOperations { } public void createTable(CasserMappingEntity entity) { + sessionOps.execute(SchemaUtil.createTable(entity)); + + List list = entity.getMappingProperties().stream() + .filter(p -> p.getIndexName().isPresent()) + .map(p -> SchemaUtil.createIndex(p)) + .collect(Collectors.toList()); + + executeBatch(list); + } public void validateTable(TableMetadata tmd, CasserMappingEntity entity) { @@ -47,6 +57,8 @@ public final class TableOperations { List list = SchemaUtil.alterTable(tmd, entity, dropRemovedColumns); + addAlterIndexes(tmd, entity, list); + if (!list.isEmpty()) { throw new CasserException("schema changed for entity " + entity.getMappingInterface() + ", apply this command: " + list); } @@ -60,7 +72,18 @@ public final class TableOperations { } List list = SchemaUtil.alterTable(tmd, entity, dropRemovedColumns); + addAlterIndexes(tmd, entity, list); + + executeBatch(list); + } + + private void addAlterIndexes(TableMetadata tmd, CasserMappingEntity entity, List list) { + + + } + + private void executeBatch(List list) { if (!list.isEmpty()) { Batch b = QueryBuilder.batch(list.toArray(new RegularStatement[list.size()])); sessionOps.execute(b); diff --git a/src/main/java/com/noorq/casser/core/reflect/DslInvocationHandler.java b/src/main/java/com/noorq/casser/core/reflect/DslInvocationHandler.java index 0191bb8..7ace8f1 100644 --- a/src/main/java/com/noorq/casser/core/reflect/DslInvocationHandler.java +++ b/src/main/java/com/noorq/casser/core/reflect/DslInvocationHandler.java @@ -21,12 +21,14 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; +import com.datastax.driver.core.DataType; import com.noorq.casser.core.Casser; import com.noorq.casser.mapping.CasserMappingEntity; import com.noorq.casser.mapping.CasserMappingProperty; import com.noorq.casser.support.CasserException; import com.noorq.casser.support.CasserMappingException; import com.noorq.casser.support.DslPropertyException; +import com.noorq.casser.support.Either; public class DslInvocationHandler implements InvocationHandler { @@ -46,7 +48,9 @@ public class DslInvocationHandler implements InvocationHandler { map.put(prop.getGetterMethod(), prop); - if (prop.getUDTType() != null) { + Either type = prop.getColumnType(); + + if (type.isRight()) { Object childDsl = Casser.dsl(prop.getJavaType(), classLoader, Optional.of(new CasserPropertyNode(prop, parent))); @@ -69,7 +73,9 @@ public class DslInvocationHandler implements InvocationHandler { if (prop != null) { - if (prop.getUDTType() != null) { + Either type = prop.getColumnType(); + + if (type.isRight()) { Object childDsl = udtMap.get(method); diff --git a/src/main/java/com/noorq/casser/mapping/CasserMappingProperty.java b/src/main/java/com/noorq/casser/mapping/CasserMappingProperty.java index fcbe80a..a6bebcd 100644 --- a/src/main/java/com/noorq/casser/mapping/CasserMappingProperty.java +++ b/src/main/java/com/noorq/casser/mapping/CasserMappingProperty.java @@ -29,8 +29,6 @@ import java.util.function.Function; import com.datastax.driver.core.DataType; import com.datastax.driver.core.UDTValue; -import com.datastax.driver.core.schemabuilder.SchemaBuilder; -import com.datastax.driver.core.schemabuilder.UDTType; import com.noorq.casser.mapping.convert.DateToTimeUUIDConverter; import com.noorq.casser.mapping.convert.EntityToUDTValueConverter; import com.noorq.casser.mapping.convert.EnumToStringConverter; @@ -39,6 +37,7 @@ import com.noorq.casser.mapping.convert.TimeUUIDToDateConverter; import com.noorq.casser.mapping.convert.TypedConverter; import com.noorq.casser.mapping.convert.UDTValueToEntityConverter; import com.noorq.casser.support.CasserMappingException; +import com.noorq.casser.support.Either; public class CasserMappingProperty implements CasserProperty { @@ -48,6 +47,8 @@ public class CasserMappingProperty implements CasserProperty { private Optional propertyName = Optional.empty(); private Optional columnName = Optional.empty(); + private Optional indexName = null; + private boolean keyInfo = false; private boolean isPartitionKey = false; private boolean isClusteringColumn = false; @@ -55,11 +56,7 @@ public class CasserMappingProperty implements CasserProperty { private OrderingDirection ordering = OrderingDirection.ASC; private Optional> javaType = Optional.empty(); - - private boolean typeInfo = false; - private DataType dataType = null; - private UDTType udtType = null; - private String udtName = null; + private Optional> columnType = Optional.empty(); private Optional isStatic = Optional.empty(); @@ -85,23 +82,13 @@ public class CasserMappingProperty implements CasserProperty { } @Override - public DataType getDataType() { - ensureTypeInfo(); - return dataType; + public Either getColumnType() { + if (!columnType.isPresent()) { + columnType = Optional.of(resolveColumnType()); + } + return columnType.get(); } - @Override - public UDTType getUDTType() { - ensureTypeInfo(); - return udtType; - } - - @Override - public String getUDTName() { - ensureTypeInfo(); - return udtName; - } - @Override public boolean isPartitionKey() { ensureKeyInfo(); @@ -153,6 +140,16 @@ public class CasserMappingProperty implements CasserProperty { return columnName.get(); } + + @Override + public Optional getIndexName() { + + if (indexName == null) { + indexName = Optional.ofNullable(MappingUtil.getIndexName(getter)); + } + + return indexName; + } @Override public String getPropertyName() { @@ -180,7 +177,9 @@ public class CasserMappingProperty implements CasserProperty { private Function resolveReadConverter(CasserMappingRepository repository) { - if (getUDTType() != null) { + Either columnType = getColumnType(); + + if (columnType.isRight()) { Class javaType = (Class) getJavaType(); @@ -190,26 +189,28 @@ public class CasserMappingProperty implements CasserProperty { new UDTValueToEntityConverter(javaType, repository)); } + else { - Class propertyType = getJavaType(); - - if (Enum.class.isAssignableFrom(propertyType)) { - return TypedConverter.create( - String.class, - Enum.class, - new StringToEnumConverter(propertyType)); + Class propertyType = getJavaType(); + + if (Enum.class.isAssignableFrom(propertyType)) { + return TypedConverter.create( + String.class, + Enum.class, + new StringToEnumConverter(propertyType)); + } + + DataType dataType = columnType.getLeft(); + + if (dataType.getName() == DataType.Name.TIMEUUID && propertyType == Date.class) { + return TypedConverter.create( + UUID.class, + Date.class, + TimeUUIDToDateConverter.INSTANCE); + } + + return null; } - - DataType dataType = getDataType(); - - if (dataType.getName() == DataType.Name.TIMEUUID && propertyType == Date.class) { - return TypedConverter.create( - UUID.class, - Date.class, - TimeUUIDToDateConverter.INSTANCE); - } - - return null; } @Override @@ -224,58 +225,57 @@ public class CasserMappingProperty implements CasserProperty { private Function resolveWriteConverter(CasserMappingRepository repository) { - if (getUDTType() != null) { + Either columnType = getColumnType(); + + if (columnType.isRight()) { Class javaType = (Class) getJavaType(); return TypedConverter.create( javaType, UDTValue.class, - new EntityToUDTValueConverter(javaType, getUDTName(), repository)); + new EntityToUDTValueConverter(javaType, columnType.getRight(), repository)); } + else { - Class propertyType = getJavaType(); - - if (Enum.class.isAssignableFrom(propertyType)) { - - return TypedConverter.create( - Enum.class, - String.class, - EnumToStringConverter.INSTANCE); - - } - - DataType dataType = getDataType(); - - if (dataType.getName() == DataType.Name.TIMEUUID && propertyType == Date.class) { - - return TypedConverter.create( - Date.class, - UUID.class, - DateToTimeUUIDConverter.INSTANCE); - } - return null; - } + Class propertyType = getJavaType(); - private void ensureTypeInfo() { - if (!typeInfo) { - - dataType = resolveDataType(); - - if (dataType == null) { + if (Enum.class.isAssignableFrom(propertyType)) { - Class propertyType = getJavaType(); - - this.udtName = MappingUtil.getUserDefinedTypeName(propertyType, false); - - if (this.udtName != null) { - this.udtType = SchemaBuilder.frozen(udtName); - } + return TypedConverter.create( + Enum.class, + String.class, + EnumToStringConverter.INSTANCE); } - typeInfo = true; - } + + DataType dataType = columnType.getLeft(); + + if (dataType.getName() == DataType.Name.TIMEUUID && propertyType == Date.class) { + + return TypedConverter.create( + Date.class, + UUID.class, + DateToTimeUUIDConverter.INSTANCE); + } + + return null; + } + } + + private Either resolveColumnType() { + + DataType dataType = resolveDataType(); + if (dataType != null) { + return Either.left(dataType); + } + else { + Class propertyType = getJavaType(); + String udtName = MappingUtil.getUserDefinedTypeName(propertyType, false); + return Either.right(udtName); + } + } private DataType resolveDataType() { diff --git a/src/main/java/com/noorq/casser/mapping/CasserMappingRepository.java b/src/main/java/com/noorq/casser/mapping/CasserMappingRepository.java index 96438e3..8410228 100644 --- a/src/main/java/com/noorq/casser/mapping/CasserMappingRepository.java +++ b/src/main/java/com/noorq/casser/mapping/CasserMappingRepository.java @@ -21,10 +21,11 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; +import com.datastax.driver.core.DataType; import com.datastax.driver.core.UserType; -import com.datastax.driver.core.schemabuilder.UDTType; import com.noorq.casser.support.CasserException; import com.noorq.casser.support.CasserMappingException; +import com.noorq.casser.support.Either; public class CasserMappingRepository { @@ -87,9 +88,9 @@ public class CasserMappingRepository { for (CasserMappingProperty prop : props) { - UDTType type = prop.getUDTType(); + Either type = prop.getColumnType(); - if (type != null) { + if (type.isRight()) { add(prop.getJavaType(), OPTIONAL_UDT); diff --git a/src/main/java/com/noorq/casser/mapping/CasserProperty.java b/src/main/java/com/noorq/casser/mapping/CasserProperty.java index a1cf8f5..1914a3d 100644 --- a/src/main/java/com/noorq/casser/mapping/CasserProperty.java +++ b/src/main/java/com/noorq/casser/mapping/CasserProperty.java @@ -19,7 +19,7 @@ import java.util.Optional; import java.util.function.Function; import com.datastax.driver.core.DataType; -import com.datastax.driver.core.schemabuilder.UDTType; +import com.noorq.casser.support.Either; public interface CasserProperty { @@ -29,14 +29,12 @@ public interface CasserProperty { String getColumnName(); + Optional getIndexName(); + Class getJavaType(); - DataType getDataType(); + Either getColumnType(); - UDTType getUDTType(); - - String getUDTName(); - boolean isPartitionKey(); boolean isClusteringColumn(); diff --git a/src/main/java/com/noorq/casser/mapping/CqlUtil.java b/src/main/java/com/noorq/casser/mapping/CqlUtil.java index 0095728..8fe6e8f 100644 --- a/src/main/java/com/noorq/casser/mapping/CqlUtil.java +++ b/src/main/java/com/noorq/casser/mapping/CqlUtil.java @@ -21,6 +21,11 @@ public final class CqlUtil { } public static String forceQuote(String identity) { + + if (identity == null) { + return null; + } + if (identity.startsWith("\"")) { return identity; } diff --git a/src/main/java/com/noorq/casser/mapping/MappingUtil.java b/src/main/java/com/noorq/casser/mapping/MappingUtil.java index f8f3510..09f31b2 100644 --- a/src/main/java/com/noorq/casser/mapping/MappingUtil.java +++ b/src/main/java/com/noorq/casser/mapping/MappingUtil.java @@ -28,6 +28,26 @@ public final class MappingUtil { private MappingUtil() { } + public static String getIndexName(Method getterMethod) { + + String indexName = null; + + Index index = getterMethod.getDeclaredAnnotation(Index.class); + if (index != null) { + indexName = index.value(); + if (index.forceQuote()) { + indexName = CqlUtil.forceQuote(indexName); + } + + if (indexName == null || indexName.isEmpty()) { + indexName = getDefaultColumnName(getterMethod); + } + + } + + return indexName; + } + public static String getColumnName(Method getterMethod) { String columnName = null;