IN PROGRESS - issue DATACASS-32: Implement the TemplateAPI for CQL

https://jira.springsource.org/browse/DATACASS-32

Added CassandraAdminOperations.
This commit is contained in:
dwebb
2013-11-13 15:21:44 -05:00
parent 6d78de85c7
commit 20123c5348
11 changed files with 297 additions and 258 deletions

View File

@@ -18,9 +18,12 @@ package org.springframework.data.cassandra.config;
/**
* @author Alex Shvid
*/
public class BeanNames {
public final class BeanNames {
private BeanNames() {
}
public static final String CASSANDRA_CLUSTER = "cassandra-cluster";
public static final String CASSANDRA_KEYSPACE = "cassandra-keyspace";
static final String CASSANDRA_CLUSTER = "cassandra-cluster";
static final String CASSANDRA_KEYSPACE = "cassandra-keyspace";
}

View File

@@ -21,5 +21,5 @@ package org.springframework.data.cassandra.config;
* @author Alex Shvid
*/
public enum CompressionType {
none, snappy;
NONE, SNAPPY;
}

View File

@@ -0,0 +1,242 @@
package org.springframework.data.cassandra.core;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.convert.CassandraConverter;
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.mapping.context.MappingContext;
import org.springframework.util.Assert;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.TableMetadata;
/**
*
*/
public class CassandraAdmin implements CassandraAdminOperations {
private static Logger log = LoggerFactory.getLogger(CassandraAdmin.class);
private final Keyspace keyspace;
private final Session session;
private final CassandraConverter cassandraConverter;
private final MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
private ClassLoader beanClassLoader;
/**
* Constructor used for a basic template configuration
*
* @param keyspace must not be {@literal null}.
*/
public CassandraAdmin(Keyspace keyspace) {
this.keyspace = keyspace;
this.session = keyspace.getSession();
this.cassandraConverter = keyspace.getCassandraConverter();
this.mappingContext = this.cassandraConverter.getMappingContext();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraAdminOperations#createTable(boolean, java.lang.String, java.lang.Class, java.util.Map)
*/
@Override
public void createTable(boolean ifNotExists, final String tableName, Class<?> entityClass,
Map<String, Object> optionsByName) {
try {
final CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
execute(new SessionCallback<Object>() {
public Object doInSession(Session s) throws DataAccessException {
String cql = CqlUtils.createTable(tableName, entity);
log.info("CREATE TABLE CQL -> " + cql);
s.execute(cql);
return null;
}
});
} catch (LinkageError e) {
e.printStackTrace();
} finally {
}
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraAdminOperations#alterTable(java.lang.String, java.lang.Class, boolean)
*/
@Override
public void alterTable(String tableName, Class<?> entityClass, boolean dropRemovedAttributeColumns) {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraAdminOperations#replaceTable(java.lang.String, java.lang.Class)
*/
@Override
public void replaceTable(String tableName, Class<?> entityClass) {
// TODO Auto-generated method stub
}
/**
* Create a list of query operations to alter the table for the given entity
*
* @param entityClass
* @param tableName
*/
protected void doAlterTable(Class<?> entityClass, String tableName) {
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
Assert.notNull(entity);
final TableMetadata tableMetadata = getTableMetadata(entityClass, tableName);
final List<String> queryList = CqlUtils.alterTable(tableName, entity, tableMetadata);
execute(new SessionCallback<Object>() {
public Object doInSession(Session s) throws DataAccessException {
for (String q : queryList) {
log.info(q);
s.execute(q);
}
return null;
}
});
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#dropTable(java.lang.Class)
*/
@Override
public void dropTable(Class<?> entityClass) {
final String tableName = determineTableName(entityClass);
dropTable(tableName);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#dropTable(java.lang.String)
*/
@Override
public void dropTable(String tableName) {
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);
}
});
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#getTableMetadata(java.lang.Class)
*/
@Override
public TableMetadata getTableMetadata(Class<?> entityClass, String tableName) {
/*
* Determine the table name if not provided
*/
if (tableName == null) {
tableName = determineTableName(entityClass);
}
Assert.notNull(tableName);
final String metadataTableName = tableName;
return execute(new SessionCallback<TableMetadata>() {
public TableMetadata doInSession(Session s) throws DataAccessException {
log.info("Keyspace => " + keyspace.getKeyspace());
return s.getCluster().getMetadata().getKeyspace(keyspace.getKeyspace()).getTable(metadataTableName);
}
});
}
/**
* Execute a command at the Session Level
*
* @param callback
* @return
*/
protected <T> T execute(SessionCallback<T> callback) {
Assert.notNull(callback);
try {
return callback.doInSession(session);
} catch (DataAccessException e) {
throw potentiallyConvertRuntimeException(e);
}
}
private RuntimeException potentiallyConvertRuntimeException(RuntimeException ex) {
RuntimeException resolved = this.exceptionTranslator.translateExceptionIfPossible(ex);
return resolved == null ? ex : resolved;
}
/**
* @param entityClass
* @return
*/
public String determineTableName(Class<?> entityClass) {
if (entityClass == null) {
throw new InvalidDataAccessApiUsageException(
"No class parameter provided, entity table name can't be determined!");
}
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
if (entity == null) {
throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class "
+ entityClass.getName());
}
return entity.getTable();
}
}

View File

@@ -10,12 +10,23 @@ package org.springframework.data.cassandra.core;
import java.util.Map;
import com.datastax.driver.core.TableMetadata;
/**
* @author David Webb (dwebb@brightmove.com)
*
*/
public interface CassandraAdminOperations {
/**
* Get the Table Meta Data from Cassandra
*
* @param entityClass
* @param tableName
* @return
*/
TableMetadata getTableMetadata(Class<?> entityClass, String tableName);
/**
* Create a table with the name and fields indicated by the entity class
*

View File

@@ -204,9 +204,9 @@ public class CassandraClusterFactoryBean implements FactoryBean<Cluster>,
private static Compression convertCompressionType(CompressionType type) {
switch(type) {
case none:
case NONE:
return Compression.NONE;
case snappy:
case SNAPPY:
return Compression.SNAPPY;
}
throw new IllegalArgumentException("unknown compression type " + type);

View File

@@ -18,7 +18,6 @@ package org.springframework.data.cassandra.core;
import java.util.List;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.dto.RingMember;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
@@ -66,7 +65,7 @@ public interface CassandraOperations {
* @param query must not be {@literal null}.
* @return
*/
ResultSetFuture executeQueryAsync(final String query);
ResultSetFuture executeQueryAsynchronously(final String query);
/**
* Execute query and convert ResultSet to the list of entities
@@ -105,7 +104,7 @@ public interface CassandraOperations {
*
* @param object
*/
void remove(Object object);
void delete(Object object);
/**
* Removes the given object from the given table.
@@ -113,51 +112,7 @@ public interface CassandraOperations {
* @param object
* @param table must not be {@literal null} or empty.
*/
void remove(Object object, String tableName);
/**
* Create a table with the name and fields indicated by the entity class
*
* @param entityClass class that determines metadata of the table to create/drop.
*/
void createTable(Class<?> entityClass);
/**
* Create a table with the name and fields indicated by the entity class
*
* @param entityClass class that determines metadata of the table to create/drop.
* @param tableName explicit name of the table
*/
void createTable(Class<?> entityClass, String tableName);
/**
* Alter table with the name and fields indicated by the entity class
*
* @param entityClass class that determines metadata of the table to create/drop.
*/
void alterTable(Class<?> entityClass);
/**
* Alter table with the name and fields indicated by the entity class
*
* @param entityClass class that determines metadata of the table to create/drop.
* @param tableName explicit name of the table
*/
void alterTable(Class<?> entityClass, String tableName);
/**
* Alter table with the name and fields indicated by the entity class
*
* @param entityClass class that determines metadata of the table to create/drop.
*/
void dropTable(Class<?> entityClass);
/**
* Alter table with the name and fields indicated by the entity class
*
* @param tableName explicit name of the table.
*/
void dropTable(String tableName);
void delete(Object object, String tableName);
/**
* Returns the underlying {@link CassandraConverter}.

View File

@@ -30,7 +30,6 @@ import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.convert.CassandraConverter;
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;
@@ -56,7 +55,6 @@ import com.datastax.driver.core.exceptions.NoHostAvailableException;
public class CassandraTemplate implements CassandraOperations {
private static Logger log = LoggerFactory.getLogger(CassandraTemplate.class);
private static final Collection<String> ITERABLE_CLASSES;
static {
@@ -198,7 +196,7 @@ public class CassandraTemplate implements CassandraOperations {
* @see org.springframework.data.cassandra.core.CassandraOperations#executeQueryAsync(java.lang.String)
*/
@Override
public ResultSetFuture executeQueryAsync(final String query) {
public ResultSetFuture executeQueryAsynchronously(final String query) {
return execute(new SessionCallback<ResultSetFuture>() {
@@ -316,7 +314,7 @@ public class CassandraTemplate implements CassandraOperations {
* @param entityClass
* @return
*/
String determineTableName(Class<?> entityClass) {
public String determineTableName(Class<?> entityClass) {
if (entityClass == null) {
throw new InvalidDataAccessApiUsageException(
@@ -371,25 +369,6 @@ public class CassandraTemplate implements CassandraOperations {
}
/**
* Execute a command at the Session Level
*
* @param callback
* @return
*/
protected <T> T execute(SessionCallback<T> callback) {
Assert.notNull(callback);
try {
return callback.doInSession(session);
} catch (DataAccessException e) {
throw potentiallyConvertRuntimeException(e);
}
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#insert(java.lang.Object)
*/
@@ -423,9 +402,9 @@ public class CassandraTemplate implements CassandraOperations {
* @see org.springframework.data.cassandra.core.CassandraOperations#remove(java.lang.Object)
*/
@Override
public void remove(Object object) {
public void delete(Object object) {
remove(object, determineTableName(object.getClass()));
delete(object, determineTableName(object.getClass()));
}
@@ -433,7 +412,7 @@ public class CassandraTemplate implements CassandraOperations {
* @see org.springframework.data.cassandra.core.CassandraOperations#remove(java.lang.Object, java.lang.String)
*/
@Override
public void remove(Object object, String tableName) {
public void delete(Object object, String tableName) {
CassandraPersistentEntity<?> entityClass = getEntity(object);
@@ -475,172 +454,31 @@ public class CassandraTemplate implements CassandraOperations {
}
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#createTable(java.lang.Class)
*/
@Override
public void createTable(Class<?> entityClass) {
try {
final CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
final String useTableName = entity.getTable();
createTable(entityClass, useTableName);
} catch (LinkageError e) {
e.printStackTrace();
} finally {
}
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#createTable(java.lang.Class, java.lang.String)
*/
@Override
public void createTable(Class<?> entityClass, final String tableName) {
try {
final CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
execute(new SessionCallback<Object>() {
public Object doInSession(Session s) throws DataAccessException {
String cql = CqlUtils.createTable(tableName, entity);
log.info("CREATE TABLE CQL -> " + cql);
s.execute(cql);
return null;
}
});
} catch (LinkageError e) {
e.printStackTrace();
} finally {
}
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#alterTable(java.lang.Class)
*/
@Override
public void alterTable(Class<?> entityClass) {
alterTable(entityClass, getTableName(entityClass));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#alterTable(java.lang.Class, java.lang.String)
*/
@Override
public void alterTable(Class<?> entityClass, String tableName) {
doAlterTable(entityClass, tableName);
}
/**
* Create a list of query operations to alter the table for the given entity
* Execute a command at the Session Level
*
* @param entityClass
* @param tableName
* @param callback
* @return
*/
protected void doAlterTable(Class<?> entityClass, String tableName) {
protected <T> T execute(SessionCallback<T> callback) {
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
Assert.notNull(callback);
Assert.notNull(entity);
try {
final TableMetadata tableMetadata = getTableMetadata(entityClass, tableName);
final List<String> queryList = CqlUtils.alterTable(tableName, entity, tableMetadata);
execute(new SessionCallback<Object>() {
public Object doInSession(Session s) throws DataAccessException {
for (String q : queryList) {
log.info(q);
s.execute(q);
}
return null;
}
});
return callback.doInSession(session);
} catch (DataAccessException e) {
throw potentiallyConvertRuntimeException(e);
}
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#dropTable(java.lang.Class)
*/
@Override
public void dropTable(Class<?> entityClass) {
final String tableName = getTableName(entityClass);
dropTable(tableName);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#dropTable(java.lang.String)
*/
@Override
public void dropTable(String tableName) {
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);
}
});
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#getTableMetadata(java.lang.Class)
* @see org.springframework.data.cassandra.core.CassandraOperations#getTableMetadata(java.lang.Class, java.lang.String)
*/
@Override
public TableMetadata getTableMetadata(Class<?> entityClass, String tableName) {
/*
* Determine the table name if not provided
*/
if (tableName == null) {
tableName = getTableName(entityClass);
}
Assert.notNull(tableName);
final String metadataTableName = tableName;
return execute(new SessionCallback<TableMetadata>() {
public TableMetadata doInSession(Session s) throws DataAccessException {
log.info("Keyspace => " + keyspace.getKeyspace());
return s.getCluster().getMetadata().getKeyspace(keyspace.getKeyspace()).getTable(metadataTableName);
}
});
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.dto;
package org.springframework.data.cassandra.core;
import java.io.Serializable;

View File

@@ -15,21 +15,14 @@
*/
package org.springframework.data.cassandra.util;
/**
* Helper class featuring helper methods for working with Cassandra tables.
* Mainly intended for internal use within the framework.
* Helper class featuring helper methods for working with Cassandra tables. Mainly intended for internal use within the
* framework.
*
* @author Alex Shvid
*/
public abstract class CassandraNamingUtils {
/**
* Private constructor to prevent instantiation.
*/
private CassandraNamingUtils() {
}
/**
* Obtains the table name to use for the provided class
*
@@ -39,5 +32,5 @@ public abstract class CassandraNamingUtils {
public static String getPreferredTableName(Class<?> entityClass) {
return entityClass.getSimpleName().toLowerCase();
}
}

View File

@@ -28,9 +28,6 @@ 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;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
@@ -75,23 +72,23 @@ public class CassandraOperationsTableTest {
log.info("Creating Table...");
cassandraTemplate.createTable(User.class);
cassandraTemplate.createTable(Comment.class);
// cassandraTemplate.createTable(User.class);
// cassandraTemplate.createTable(Comment.class);
}
@Test
public void alterTableTest() {
cassandraTemplate.alterTable(UserAlter.class);
// cassandraTemplate.alterTable(UserAlter.class);
}
@Test
public void dropTableTest() {
cassandraTemplate.dropTable(User.class);
cassandraTemplate.dropTable("comments");
// cassandraTemplate.dropTable(User.class);
// cassandraTemplate.dropTable("comments");
}

View File

@@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.cassandra.config.TestConfig;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.dto.RingMember;
import org.springframework.data.cassandra.core.RingMember;
import org.springframework.data.cassandra.test.LogEntry;
import org.springframework.data.cassandra.test.User;
import org.springframework.test.context.ContextConfiguration;
@@ -80,9 +80,9 @@ public class CassandraOperationsTest {
log.info("Creating Table...");
cassandraTemplate.createTable(User.class);
// cassandraTemplate.createTable(User.class);
cassandraTemplate.createTable(LogEntry.class);
// cassandraTemplate.createTable(LogEntry.class);
}
@@ -136,7 +136,7 @@ public class CassandraOperationsTest {
log.debug(x.getLastName());
}
cassandraTemplate.remove(u);
cassandraTemplate.delete(u);
User delUser = cassandraTemplate.selectOne("select * from test.users where username='cassandra';", User.class);