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

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

Completed the insert and fixed issue with select() and selectOne().
This commit is contained in:
dwebb
2013-11-12 14:18:36 -05:00
parent 5276c80778
commit 2d26154f13
7 changed files with 220 additions and 44 deletions

View File

@@ -67,6 +67,16 @@ public class CassandraPropertyValueProvider implements PropertyValueProvider<Cas
return null;
}
DataType columnType = source.getColumnDefinitions().getType(columnName);
/*
* Dave Webb - Added handler for text since getBytes was throwing
* InvalidTypeException when using getBytes on a text column.
*/
//TODO Might need to qualify all DataTypes as we encounter them.
if (columnType.equals(DataType.text())) {
return (T) source.getString(columnName);
}
ByteBuffer bytes = source.getBytes(columnName);
return (T) columnType.deserialize(bytes);
}

View File

@@ -133,13 +133,25 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
return result;
}
public void write(Object source, Row sink) {
// TODO Auto-generated method stub
}
public void setUseFieldAccessOnly(boolean useFieldAccessOnly) {
this.useFieldAccessOnly = useFieldAccessOnly;
}
/* (non-Javadoc)
* @see org.springframework.data.convert.EntityWriter#write(java.lang.Object, java.lang.Object)
*/
@Override
public void write(Object source, Row sink) {
/*
* There is no concept of passing a Row into Cassandra for Writing.
* This must be done with Query
*
* See the CQLUtils.
*/
}
}

View File

@@ -30,6 +30,7 @@ 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.exception.EntityWriterException;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.util.CQLUtils;
@@ -142,11 +143,39 @@ public class CassandraTemplate implements CassandraOperations {
}
/**
* Determines the PersistentEntityType for a given Object
*
* @param o
* @return
*/
protected CassandraPersistentEntity<?> getEntity(Object o) {
CassandraPersistentEntity<?> entity = null;
try {
String entityClassName = o.getClass().getName();
Class<?> entityClass = ClassUtils.forName(entityClassName, this.beanClassLoader);
entity = mappingContext.getPersistentEntity(entityClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (LinkageError e) {
e.printStackTrace();
} finally {}
return entity;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#getTableName(java.lang.Class)
*/
public String getTableName(Class<?> entityClass) {
return determineTableName(entityClass);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#executeQuery(java.lang.String)
*/
public ResultSet executeQuery(String query) {
try {
return session.execute(query);
@@ -157,31 +186,27 @@ public class CassandraTemplate implements CassandraOperations {
}
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#select(java.lang.String, java.lang.Class)
*/
public <T> List<T> select(String query, Class<T> selectClass) {
return selectInternal(query, new ReadRowCallback<T>(cassandraConverter, selectClass));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#selectOne(java.lang.String, java.lang.Class)
*/
public <T> T selectOne(String query, Class<T> selectClass) {
return selectOneInternal(query, new ReadRowCallback<T>(cassandraConverter, selectClass));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#getConverter()
*/
public CassandraConverter getConverter() {
return cassandraConverter;
}
/**
* Simple internal callback to allow operations on a {@link Row}.
*
* @author Alex Shvid
*/
private interface RowCallback<T> {
T doWith(Row object);
}
/**
* Simple {@link RowCallback} that will transform {@link Row} into the given target type using the given
* {@link EntityReader}.
@@ -206,6 +231,11 @@ public class CassandraTemplate implements CassandraOperations {
}
}
/**
* @param query
* @param readRowCallback
* @return
*/
<T> List<T> selectInternal(String query, ReadRowCallback<T> readRowCallback) {
try {
ResultSet resultSet = session.execute(query);
@@ -281,43 +311,37 @@ public class CassandraTemplate implements CassandraOperations {
}
/**
* Insert a row into a Cassandra ColumnFamily
* Insert a row into a Cassandra CQL Table
*
* @param tableName
* @param objectToSave
* @throws LinkageError
* @throws ClassNotFoundException
*/
protected <T> T doInsert(final String tableName, final T objectToSave) {
CassandraPersistentEntity<?> entity = getEntity(objectToSave);
Assert.notNull(entity);
try {
final String entityClassName = objectToSave.getClass().getName();
final Class<?> entityClass = ClassUtils.forName(entityClassName, this.beanClassLoader);
final CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
final String useTableName = tableName != null ? tableName : entity.getTable();
final Query q = CQLUtils.toInsertQuery(keyspace.getKeyspace(), tableName, objectToSave, entity);
log.info(q.toString());
return execute(new SessionCallback<T>() {
public T doInSession(Session s) throws DataAccessException {
s.execute(q);
Query q = CQLUtils.toInsertQuery(keyspace.getKeyspace(), useTableName, entity, objectToSave);
log.info(q.toString());
ResultSet rs = s.execute(q);
return null;
return objectToSave;
}
});
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (LinkageError e) {
e.printStackTrace();
} finally {}
return objectToSave;
} catch (EntityWriterException e) {
throw exceptionTranslator.translateExceptionIfPossible(new RuntimeException("Failed to translate Object to Query", e));
}
}
/**

View File

@@ -0,0 +1,22 @@
/**
* All BrightMove Code is Copyright 2004-2013 BrightMove Inc.
* Modification of code without the express written consent of
* BrightMove, Inc. is strictly forbidden.
*
* Author: David Webb (dwebb@brightmove.com)
* Created On: Nov 12, 2013
*/
package org.springframework.data.cassandra.core;
import com.datastax.driver.core.Row;
/**
* Simple internal callback to allow operations on a {@link Row}.
*
* @author Alex Shvid
*/
public interface RowCallback<T> {
T doWith(Row object);
}

View File

@@ -0,0 +1,41 @@
/**
* All BrightMove Code is Copyright 2004-2013 BrightMove Inc.
* Modification of code without the express written consent of
* BrightMove, Inc. is strictly forbidden.
*
* Author: David Webb (dwebb@brightmove.com)
* Created On: Nov 12, 2013
*/
package org.springframework.data.cassandra.exception;
/**
* Exception to handle failing to write a PersistedEntity to a CQL String or Query object
*
* @author David Webb (dwebb@brightmove.com)
*
*/
public class EntityWriterException extends Exception {
/**
* @param message
*/
public EntityWriterException(String message) {
super(message);
}
/**
* @param cause
*/
public EntityWriterException(Throwable cause) {
super(cause);
}
/**
* @param message
* @param cause
*/
public EntityWriterException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -7,9 +7,12 @@ import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
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.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.ClassUtils;
import com.datastax.driver.core.ColumnMetadata;
import com.datastax.driver.core.DataType;
@@ -31,6 +34,13 @@ 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()
*/
public static String createTable(String tableName, final CassandraPersistentEntity<?> entity) {
final StringBuilder str = new StringBuilder();
@@ -101,6 +111,13 @@ public abstract class CQLUtils {
return str.toString();
}
/**
* Create the List of CQL for the indexes required for Cassandra mapped Table.
*
* @param tableName
* @param entity
* @return The list of CQL statements to run with session.execute()
*/
public static List<String> createIndexes(final String tableName, final CassandraPersistentEntity<?> entity) {
final List<String> result = new ArrayList<String>();
@@ -126,6 +143,14 @@ public abstract class CQLUtils {
return result;
}
/**
* Alter the table to refelct the entity annotations
*
* @param tableName
* @param entity
* @param table
* @return
*/
public static List<String> alterTable(final String tableName, final CassandraPersistentEntity<?> entity, final TableMetadata table) {
final List<String> result = new ArrayList<String>();
@@ -171,9 +196,24 @@ public abstract class CQLUtils {
return result;
}
public static Query toInsertQuery(String keyspaceName, String tableName, final CassandraPersistentEntity<?> entity, final Object objectToSave) {
/**
* Generates a Query Object for an insert
*
* @param keyspaceName
* @param tableName
* @param entity
* @param objectToSave
* @param mappingContext
* @param beanClassLoader
*
* @return The Query object to run with session.execute();
* @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) {
@@ -191,15 +231,19 @@ public abstract class CQLUtils {
}
} catch (IllegalAccessException e) {
e.printStackTrace();
innerException.initCause(e);
} catch (IllegalArgumentException e) {
e.printStackTrace();
innerException.initCause(e);
} catch (InvocationTargetException e) {
e.printStackTrace();
innerException.initCause(e);
}
}
});
if (innerException.getCause() != null) {
throw new EntityWriterException("Failed to convert Persistent Entity to CQL/Query", innerException.getCause());
}
return q;
}

View File

@@ -89,8 +89,17 @@ public class CassandraOperationsTest {
}
}
/**
* This test inserts and selects users from the test.users table
* This is testing the CassandraTemplate:
* <ul>
* <li>insert()</li>
* <li>selectOne()</li>
* <li>select()</li>
* </ul>
*/
@Test
public void insertTest() {
public void UsersTest() {
User u = new User();
u.setUsername("cassandra");
@@ -99,6 +108,20 @@ public class CassandraOperationsTest {
cassandraTemplate.insert(u, "users");
User us = cassandraTemplate.selectOne("select * from test.users where username='cassandra';" , User.class);
log.debug("Output from select One");
log.debug(us.getFirstName());
log.debug(us.getLastName());
List<User> users = cassandraTemplate.select("Select * from test.users", User.class);
log.debug("Output from select All");
for (User x: users) {
log.debug(x.getFirstName());
log.debug(x.getLastName());
}
}
@After