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
This commit is contained in:
dwebb
2013-11-13 10:57:08 -05:00
parent 5316930683
commit 5242504c13
5 changed files with 152 additions and 112 deletions

View File

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

View File

@@ -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<Keyspace>, 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<Keyspace>, Init
+ entityClassName);
}
// validate columns
List<String> alter = CQLUtils.alterTable(useTableName, entity, table);
List<String> 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<Keyspace>, 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);
}

View File

@@ -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<T>() {
@@ -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<ResultSet>() {
@@ -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<String> queryList = CQLUtils.alterTable(tableName, entity, tableMetadata);
final List<String> queryList = CqlUtils.alterTable(tableName, entity, tableMetadata);
execute(new SessionCallback<Object>() {
@@ -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<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(q);
}
});
}

View File

@@ -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<String> ids = new ArrayList<String>();
final List<String> idColumns = new ArrayList<String>();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
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<CassandraPersistentProperty>() {
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<String> alterTable(final String tableName, final CassandraPersistentEntity<?> entity, final TableMetadata table) {
public static List<String> alterTable(final String tableName, final CassandraPersistentEntity<?> entity,
final TableMetadata table) {
final List<String> result = new ArrayList<String>();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
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<CassandraPersistentProperty>() {
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<CassandraPersistentProperty>() {
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<CassandraPersistentProperty>() {
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();
}
}

View File

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