From 5242504c13520b45035cddaddb0a89187e1fe3b0 Mon Sep 17 00:00:00 2001 From: dwebb Date: Wed, 13 Nov 2013 10:57:08 -0500 Subject: [PATCH] IN PROGRESS - issue DATACASS-32: Implement the TemplateAPI for CQL https://jira.springsource.org/browse/DATACASS-32 Implemented Drop Table in Template Renamed CQLUtils to CqlUtils --- .../CassandraPropertyValueProvider.java | 2 +- .../core/CassandraKeyspaceFactoryBean.java | 10 +- .../cassandra/core/CassandraTemplate.java | 32 ++- .../util/{CQLUtils.java => CqlUtils.java} | 199 +++++++++--------- ...java => CassandraOperationsTableTest.java} | 21 +- 5 files changed, 152 insertions(+), 112 deletions(-) rename src/main/java/org/springframework/data/cassandra/util/{CQLUtils.java => CqlUtils.java} (83%) rename src/test/java/org/springframework/data/cassandra/template/{CassandraOperationsAlterTableTest.java => CassandraOperationsTableTest.java} (83%) diff --git a/src/main/java/org/springframework/data/cassandra/convert/CassandraPropertyValueProvider.java b/src/main/java/org/springframework/data/cassandra/convert/CassandraPropertyValueProvider.java index 25f6fbd9d..96d6c25b9 100644 --- a/src/main/java/org/springframework/data/cassandra/convert/CassandraPropertyValueProvider.java +++ b/src/main/java/org/springframework/data/cassandra/convert/CassandraPropertyValueProvider.java @@ -20,7 +20,7 @@ import java.nio.ByteBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; -import org.springframework.data.cassandra.util.CQLUtils; +import org.springframework.data.cassandra.util.CqlUtils; import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator; import org.springframework.data.mapping.model.PropertyValueProvider; import org.springframework.data.mapping.model.SpELExpressionEvaluator; diff --git a/src/main/java/org/springframework/data/cassandra/core/CassandraKeyspaceFactoryBean.java b/src/main/java/org/springframework/data/cassandra/core/CassandraKeyspaceFactoryBean.java index 56d9f6475..eba2c00f6 100644 --- a/src/main/java/org/springframework/data/cassandra/core/CassandraKeyspaceFactoryBean.java +++ b/src/main/java/org/springframework/data/cassandra/core/CassandraKeyspaceFactoryBean.java @@ -34,7 +34,7 @@ import org.springframework.data.cassandra.convert.MappingCassandraConverter; import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; -import org.springframework.data.cassandra.util.CQLUtils; +import org.springframework.data.cassandra.util.CqlUtils; import org.springframework.data.mapping.context.MappingContext; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -214,7 +214,7 @@ public class CassandraKeyspaceFactoryBean implements FactoryBean, Init createNewTable(session, useTableName, entity); } else { // alter table columns - for (String cql : CQLUtils.alterTable(useTableName, entity, table)) { + for (String cql : CqlUtils.alterTable(useTableName, entity, table)) { log.info("Execute on keyspace " + keyspace + " CQL " + cql); session.execute(cql); } @@ -226,7 +226,7 @@ public class CassandraKeyspaceFactoryBean implements FactoryBean, Init + entityClassName); } // validate columns - List alter = CQLUtils.alterTable(useTableName, entity, table); + List alter = CqlUtils.alterTable(useTableName, entity, table); if (!alter.isEmpty()) { throw new InvalidDataAccessApiUsageException("invalid table " + useTableName + " for entity " + entityClassName + ". modify it by " + alter); @@ -248,10 +248,10 @@ public class CassandraKeyspaceFactoryBean implements FactoryBean, Init private void createNewTable(Session session, String useTableName, CassandraPersistentEntity entity) throws NoHostAvailableException { - String cql = CQLUtils.createTable(useTableName, entity); + String cql = CqlUtils.createTable(useTableName, entity); log.info("Execute on keyspace " + keyspace + " CQL " + cql); session.execute(cql); - for (String indexCQL : CQLUtils.createIndexes(useTableName, entity)) { + for (String indexCQL : CqlUtils.createIndexes(useTableName, entity)) { log.info("Execute on keyspace " + keyspace + " CQL " + indexCQL); session.execute(indexCQL); } diff --git a/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index d7e99e737..a1f689953 100644 --- a/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -34,7 +34,7 @@ import org.springframework.data.cassandra.dto.RingMember; import org.springframework.data.cassandra.exception.EntityWriterException; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; -import org.springframework.data.cassandra.util.CQLUtils; +import org.springframework.data.cassandra.util.CqlUtils; import org.springframework.data.convert.EntityReader; import org.springframework.data.mapping.context.MappingContext; import org.springframework.util.Assert; @@ -350,7 +350,7 @@ public class CassandraTemplate implements CassandraOperations { try { - final Query q = CQLUtils.toInsertQuery(keyspace.getKeyspace(), tableName, objectToSave, entity); + final Query q = CqlUtils.toInsertQuery(keyspace.getKeyspace(), tableName, objectToSave, entity); log.info(q.toString()); return execute(new SessionCallback() { @@ -457,7 +457,7 @@ public class CassandraTemplate implements CassandraOperations { try { - final Query q = CQLUtils.toDeleteQuery(keyspace.getKeyspace(), tableName, objectToRemove, entity); + final Query q = CqlUtils.toDeleteQuery(keyspace.getKeyspace(), tableName, objectToRemove, entity); log.info(q.toString()); execute(new SessionCallback() { @@ -509,7 +509,7 @@ public class CassandraTemplate implements CassandraOperations { public Object doInSession(Session s) throws DataAccessException { - String cql = CQLUtils.createTable(tableName, entity); + String cql = CqlUtils.createTable(tableName, entity); log.info("CREATE TABLE CQL -> " + cql); @@ -559,7 +559,7 @@ public class CassandraTemplate implements CassandraOperations { final TableMetadata tableMetadata = getTableMetadata(entityClass, tableName); - final List queryList = CQLUtils.alterTable(tableName, entity, tableMetadata); + final List queryList = CqlUtils.alterTable(tableName, entity, tableMetadata); execute(new SessionCallback() { @@ -582,7 +582,10 @@ public class CassandraTemplate implements CassandraOperations { */ @Override public void dropTable(Class entityClass) { - // TODO Auto-generated method stub + + final String tableName = getTableName(entityClass); + + dropTable(tableName); } @@ -591,7 +594,22 @@ public class CassandraTemplate implements CassandraOperations { */ @Override public void dropTable(String tableName) { - // TODO Auto-generated method stub + + log.info("Dropping table => " + tableName); + + final String q = CqlUtils.dropTable(tableName); + log.info(q); + + execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + + return s.execute(q); + + } + + }); } diff --git a/src/main/java/org/springframework/data/cassandra/util/CQLUtils.java b/src/main/java/org/springframework/data/cassandra/util/CqlUtils.java similarity index 83% rename from src/main/java/org/springframework/data/cassandra/util/CQLUtils.java rename to src/main/java/org/springframework/data/cassandra/util/CqlUtils.java index c41797aef..28c89eae3 100644 --- a/src/main/java/org/springframework/data/cassandra/util/CQLUtils.java +++ b/src/main/java/org/springframework/data/cassandra/util/CqlUtils.java @@ -16,31 +16,29 @@ import com.datastax.driver.core.ColumnMetadata; import com.datastax.driver.core.DataType; import com.datastax.driver.core.Query; import com.datastax.driver.core.TableMetadata; -import com.datastax.driver.core.querybuilder.Clause; import com.datastax.driver.core.querybuilder.Delete; import com.datastax.driver.core.querybuilder.Delete.Where; import com.datastax.driver.core.querybuilder.Insert; import com.datastax.driver.core.querybuilder.QueryBuilder; - /** * * Utilties to convert Cassandra Annotated objects to Queries and CQL. * * @author Alex Shvid * @author David Webb (dwebb@brightmove.com) - * + * */ -public abstract class CQLUtils { - - private static Logger log = LoggerFactory.getLogger(CQLUtils.class); +public abstract class CqlUtils { + + private static Logger log = LoggerFactory.getLogger(CqlUtils.class); /** * Generates the CQL String to create a table in Cassandra * * @param tableName * @param entity - * @return The CQL that can be passed to session.execute() + * @return The CQL that can be passed to session.execute() */ public static String createTable(String tableName, final CassandraPersistentEntity entity) { @@ -51,67 +49,66 @@ public abstract class CQLUtils { final List ids = new ArrayList(); final List idColumns = new ArrayList(); - + entity.doWithProperties(new PropertyHandler() { public void doWithPersistentProperty(CassandraPersistentProperty prop) { - - if (str.charAt(str.length()-1) != '(') { + + if (str.charAt(str.length() - 1) != '(') { str.append(','); } - + String columnName = prop.getColumnName(); - + str.append(columnName); str.append(' '); - + DataType dataType = prop.getDataType(); - + str.append(toCQL(dataType)); - + if (prop.isIdProperty()) { ids.add(prop.getColumnName()); } - + if (prop.isColumnId()) { idColumns.add(prop.getColumnName()); } - + } }); - + if (ids.isEmpty()) { throw new InvalidDataAccessApiUsageException("not found primary ID in the entity " + entity.getType()); } str.append(",PRIMARY KEY("); - -// if (ids.size() > 1) { -// str.append('('); -// } - - for (String id: ids) { - if (str.charAt(str.length()-1) != '(') { + + // if (ids.size() > 1) { + // str.append('('); + // } + + for (String id : ids) { + if (str.charAt(str.length() - 1) != '(') { str.append(','); } str.append(id); } - -// if (ids.size() > 1) { -// str.append(')'); -// } - for (String id: idColumns) { + // if (ids.size() > 1) { + // str.append(')'); + // } + + for (String id : idColumns) { str.append(','); str.append(id); } str.append("));"); - - + return str.toString(); } - + /** * Create the List of CQL for the indexes required for Cassandra mapped Table. * @@ -124,23 +121,22 @@ public abstract class CQLUtils { entity.doWithProperties(new PropertyHandler() { public void doWithPersistentProperty(CassandraPersistentProperty prop) { - + if (prop.isIndexed()) { - + final StringBuilder str = new StringBuilder(); str.append("CREATE INDEX ON "); str.append(tableName); str.append(" ("); str.append(prop.getColumnName()); - str.append(");"); - + str.append(");"); + result.add(str.toString()); } - + } }); - - + return result; } @@ -152,30 +148,30 @@ public abstract class CQLUtils { * @param table * @return */ - public static List alterTable(final String tableName, final CassandraPersistentEntity entity, final TableMetadata table) { + public static List alterTable(final String tableName, final CassandraPersistentEntity entity, + final TableMetadata table) { final List result = new ArrayList(); - + entity.doWithProperties(new PropertyHandler() { public void doWithPersistentProperty(CassandraPersistentProperty prop) { String columnName = prop.getColumnName(); DataType columnDataType = prop.getDataType(); ColumnMetadata columnMetadata = table.getColumn(columnName.toLowerCase()); - + if (columnMetadata != null && columnDataType.equals(columnMetadata.getType())) { return; } - + final StringBuilder str = new StringBuilder(); str.append("ALTER TABLE "); str.append(tableName); if (columnMetadata == null) { str.append(" ADD "); - } - else { + } else { str.append(" ALTER "); } - + str.append(columnName); str.append(' '); @@ -187,13 +183,12 @@ public abstract class CQLUtils { str.append(';'); result.add(str.toString()); - + } }); - - - //System.out.println("CQL=" + table.asCQLQuery()); - + + // System.out.println("CQL=" + table.asCQLQuery()); + return result; } @@ -204,40 +199,40 @@ public abstract class CQLUtils { * @param tableName * @param entity * @param objectToSave - * @param mappingContext - * @param beanClassLoader + * @param mappingContext + * @param beanClassLoader * * @return The Query object to run with session.execute(); - * @throws EntityWriterException + * @throws EntityWriterException */ - public static Query toInsertQuery(String keyspaceName, String tableName, - final Object objectToSave, CassandraPersistentEntity entity) throws EntityWriterException { - + public static Query toInsertQuery(String keyspaceName, String tableName, final Object objectToSave, + CassandraPersistentEntity entity) throws EntityWriterException { + final Insert q = QueryBuilder.insertInto(keyspaceName, tableName); final Exception innerException = new Exception(); - + entity.doWithProperties(new PropertyHandler() { public void doWithPersistentProperty(CassandraPersistentProperty prop) { - + /* * See if the object has a value for that column, and if so, add it to the Query */ try { - + Object o = prop.getGetter().invoke(objectToSave, new Object[0]); - + log.info("Getter Invoke [" + prop.getColumnName() + " => " + o); - + if (o != null) { q.value(prop.getColumnName(), o); } - + } catch (IllegalAccessException e) { innerException.initCause(e); } catch (IllegalArgumentException e) { - innerException.initCause(e); + innerException.initCause(e); } catch (InvocationTargetException e) { - innerException.initCause(e); + innerException.initCause(e); } } }); @@ -245,52 +240,52 @@ public abstract class CQLUtils { if (innerException.getCause() != null) { throw new EntityWriterException("Failed to convert Persistent Entity to CQL/Query", innerException.getCause()); } - + return q; - + } - + /** * @param keyspace * @param tableName * @param objectToRemove * @param entity * @return - * @throws EntityWriterException + * @throws EntityWriterException */ - public static Query toDeleteQuery(String keyspace, String tableName, - final Object objectToRemove, CassandraPersistentEntity entity) throws EntityWriterException { + public static Query toDeleteQuery(String keyspace, String tableName, final Object objectToRemove, + CassandraPersistentEntity entity) throws EntityWriterException { final Delete.Selection ds = QueryBuilder.delete(); final Delete q = ds.from(keyspace, tableName); final Where w = q.where(); - + final Exception innerException = new Exception(); - + entity.doWithProperties(new PropertyHandler() { public void doWithPersistentProperty(CassandraPersistentProperty prop) { - + /* * See if the object has a value for that column, and if so, add it to the Query */ try { - + if (prop.isIdProperty()) { - Object o = (String)prop.getGetter().invoke(objectToRemove, new Object[0]); - + Object o = (String) prop.getGetter().invoke(objectToRemove, new Object[0]); + log.info("Getter Invoke [" + prop.getColumnName() + " => " + o); - + if (o != null) { w.and(QueryBuilder.eq(prop.getColumnName(), o)); } } - + } catch (IllegalAccessException e) { innerException.initCause(e); } catch (IllegalArgumentException e) { - innerException.initCause(e); + innerException.initCause(e); } catch (InvocationTargetException e) { - innerException.initCause(e); + innerException.initCause(e); } } }); @@ -298,12 +293,11 @@ public abstract class CQLUtils { if (innerException.getCause() != null) { throw new EntityWriterException("Failed to convert Persistent Entity to CQL/Query", innerException.getCause()); } - + return q; } - /** * Generate the CQL for insert * @@ -312,7 +306,7 @@ public abstract class CQLUtils { * @return */ public static String toInsertCQL(String tableName, final CassandraPersistentEntity entity) { - + final StringBuilder str = new StringBuilder(); str.append("INSERT INTO "); str.append(tableName); @@ -322,44 +316,42 @@ public abstract class CQLUtils { entity.doWithProperties(new PropertyHandler() { public void doWithPersistentProperty(CassandraPersistentProperty prop) { - - if (str.charAt(str.length()-1) != '(') { + + if (str.charAt(str.length() - 1) != '(') { str.append(", "); } - + String columnName = prop.getColumnName(); cols.add(columnName); - + str.append(columnName); - + } }); str.append(") VALUES ("); - + for (int i = 0; i < cols.size(); i++) { if (i > 0) { str.append(", "); } str.append("?"); } - + str.append(")"); return str.toString(); } - public static String toCQL(DataType dataType) { if (dataType.getTypeArguments().isEmpty()) { return dataType.getName().name(); - } - else { + } else { StringBuilder str = new StringBuilder(); str.append(dataType.getName().name()); str.append('<'); for (DataType argDataType : dataType.getTypeArguments()) { - if (str.charAt(str.length()-1) != '<') { + if (str.charAt(str.length() - 1) != '<') { str.append(','); } str.append(argDataType.getName().name()); @@ -369,4 +361,19 @@ public abstract class CQLUtils { } } + /** + * @param tableName + * @return + */ + public static String dropTable(String tableName) { + + if (tableName == null) { + return null; + } + + StringBuilder str = new StringBuilder(); + str.append("DROP TABLE " + tableName + ";"); + return str.toString(); + } + } diff --git a/src/test/java/org/springframework/data/cassandra/template/CassandraOperationsAlterTableTest.java b/src/test/java/org/springframework/data/cassandra/template/CassandraOperationsTableTest.java similarity index 83% rename from src/test/java/org/springframework/data/cassandra/template/CassandraOperationsAlterTableTest.java rename to src/test/java/org/springframework/data/cassandra/template/CassandraOperationsTableTest.java index 6dd3d4916..ee0dceb3a 100644 --- a/src/test/java/org/springframework/data/cassandra/template/CassandraOperationsAlterTableTest.java +++ b/src/test/java/org/springframework/data/cassandra/template/CassandraOperationsTableTest.java @@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.data.cassandra.config.TestConfig; import org.springframework.data.cassandra.core.CassandraTemplate; +import org.springframework.data.cassandra.test.Comment; import org.springframework.data.cassandra.test.User; import org.springframework.data.cassandra.test.UserAlter; import org.springframework.test.context.ContextConfiguration; @@ -40,7 +41,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigContextLoader.class) -public class CassandraOperationsAlterTableTest { +public class CassandraOperationsTableTest { @Autowired private CassandraTemplate cassandraTemplate; @@ -48,7 +49,7 @@ public class CassandraOperationsAlterTableTest { @Mock ApplicationContext context; - private static Logger log = LoggerFactory.getLogger(CassandraOperationsAlterTableTest.class); + private static Logger log = LoggerFactory.getLogger(CassandraOperationsTableTest.class); @BeforeClass public static void startCassandra() throws IOException, TTransportException, ConfigurationException, @@ -65,11 +66,17 @@ public class CassandraOperationsAlterTableTest { @Before public void setupKeyspace() { - cassandraTemplate.executeQuery("use test;"); + + /* + * Load data file to creat the test keyspace before we init the template + */ + DataLoader dataLoader = new DataLoader("Test Cluster", "localhost:9160"); + dataLoader.load(new ClassPathYamlDataSet("cassandra-data.yaml")); log.info("Creating Table..."); cassandraTemplate.createTable(User.class); + cassandraTemplate.createTable(Comment.class); } @@ -80,6 +87,14 @@ public class CassandraOperationsAlterTableTest { } + @Test + public void dropTableTest() { + + cassandraTemplate.dropTable(User.class); + cassandraTemplate.dropTable("comments"); + + } + @After public void clearCassandra() { EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();