DATACASS-33 - refactored a bunch & ready to begin testing compound primary key support

This commit is contained in:
Matthew Adams
2014-02-05 20:03:46 -06:00
parent df990d2c14
commit a6cd162c52
44 changed files with 1483 additions and 853 deletions

View File

@@ -21,6 +21,14 @@ import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import org.springframework.cassandra.core.keyspace.AlterKeyspaceSpecification;
import org.springframework.cassandra.core.keyspace.AlterTableSpecification;
import org.springframework.cassandra.core.keyspace.CreateIndexSpecification;
import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.cassandra.core.keyspace.DropIndexSpecification;
import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification;
import org.springframework.cassandra.core.keyspace.DropTableSpecification;
import org.springframework.dao.DataAccessException;
import com.datastax.driver.core.ResultSet;
@@ -754,4 +762,59 @@ public interface CqlOperations {
*/
void truncate(String tableName);
/**
* Convenience method to convert the given specification to CQL and execute it.
*
* @param specification The specification to execute; must not be null.
*/
ResultSet execute(DropTableSpecification specification);
/**
* Convenience method to convert the given specification to CQL and execute it.
*
* @param specification The specification to execute; must not be null.
*/
ResultSet execute(CreateTableSpecification specification);
/**
* Convenience method to convert the given specification to CQL and execute it.
*
* @param specification The specification to execute; must not be null.
*/
ResultSet execute(AlterTableSpecification specification);
/**
* Convenience method to convert the given specification to CQL and execute it.
*
* @param specification The specification to execute; must not be null.
*/
ResultSet execute(DropKeyspaceSpecification specification);
/**
* Convenience method to convert the given specification to CQL and execute it.
*
* @param specification The specification to execute; must not be null.
*/
ResultSet execute(CreateKeyspaceSpecification specification);
/**
* Convenience method to convert the given specification to CQL and execute it.
*
* @param specification The specification to execute; must not be null.
*/
ResultSet execute(AlterKeyspaceSpecification specification);
/**
* Convenience method to convert the given specification to CQL and execute it.
*
* @param specification The specification to execute; must not be null.
*/
ResultSet execute(DropIndexSpecification specification);
/**
* Convenience method to convert the given specification to CQL and execute it.
*
* @param specification The specification to execute; must not be null.
*/
ResultSet execute(CreateIndexSpecification specification);
}

View File

@@ -27,6 +27,22 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.cassandra.core.cql.generator.AlterKeyspaceCqlGenerator;
import org.springframework.cassandra.core.cql.generator.AlterTableCqlGenerator;
import org.springframework.cassandra.core.cql.generator.CreateIndexCqlGenerator;
import org.springframework.cassandra.core.cql.generator.CreateKeyspaceCqlGenerator;
import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.cassandra.core.cql.generator.DropIndexCqlGenerator;
import org.springframework.cassandra.core.cql.generator.DropKeyspaceCqlGenerator;
import org.springframework.cassandra.core.cql.generator.DropTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.AlterKeyspaceSpecification;
import org.springframework.cassandra.core.keyspace.AlterTableSpecification;
import org.springframework.cassandra.core.keyspace.CreateIndexSpecification;
import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.cassandra.core.keyspace.DropIndexSpecification;
import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification;
import org.springframework.cassandra.core.keyspace.DropTableSpecification;
import org.springframework.cassandra.support.CassandraAccessor;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.QueryTimeoutException;
@@ -278,6 +294,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
process(doExecute(cql, options), rch);
}
@Override
public void query(String cql, RowCallbackHandler rch) throws DataAccessException {
query(cql, rch, null);
}
@@ -287,26 +304,32 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
return process(doExecute(cql, options), rowMapper);
}
@Override
public <T> List<T> query(String cql, RowMapper<T> rowMapper) throws DataAccessException {
return query(cql, rowMapper, null);
}
@Override
public List<Map<String, Object>> queryForListOfMap(String cql) throws DataAccessException {
return processListOfMap(doExecute(cql, null));
}
@Override
public <T> List<T> queryForList(String cql, Class<T> elementType) throws DataAccessException {
return processList(doExecute(cql, null), elementType);
}
@Override
public Map<String, Object> queryForMap(String cql) throws DataAccessException {
return processMap(doExecute(cql, null));
}
@Override
public <T> T queryForObject(String cql, Class<T> requiredType) throws DataAccessException {
return processOne(doExecute(cql, null), requiredType);
}
@Override
public <T> T queryForObject(String cql, RowMapper<T> rowMapper) throws DataAccessException {
return processOne(doExecute(cql, null), rowMapper);
}
@@ -766,6 +789,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
logger.debug("Executing prepared CQL query");
return execute(psc, new PreparedStatementCallback<T>() {
@Override
public T doInPreparedStatement(PreparedStatement ps) throws DriverException {
ResultSet rs = null;
BoundStatement bs = null;
@@ -794,6 +818,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
logger.debug("Executing prepared CQL query");
execute(psc, new PreparedStatementCallback<Object>() {
@Override
public Object doInPreparedStatement(PreparedStatement ps) throws DriverException {
ResultSet rs = null;
BoundStatement bs = null;
@@ -822,6 +847,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
logger.debug("Executing prepared CQL query");
return execute(psc, new PreparedStatementCallback<List<T>>() {
@Override
public List<T> doInPreparedStatement(PreparedStatement ps) throws DriverException {
ResultSet rs = null;
BoundStatement bs = null;
@@ -843,4 +869,100 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
return query(psc, psb, rowMapper, null);
}
@Override
public ResultSet execute(final DropTableSpecification specification) {
return execute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(DropTableCqlGenerator.toCql(specification));
}
});
}
@Override
public ResultSet execute(final CreateTableSpecification specification) {
return execute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(CreateTableCqlGenerator.toCql(specification));
}
});
}
@Override
public ResultSet execute(final AlterTableSpecification specification) {
return execute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(AlterTableCqlGenerator.toCql(specification));
}
});
}
@Override
public ResultSet execute(final DropKeyspaceSpecification specification) {
return execute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(DropKeyspaceCqlGenerator.toCql(specification));
}
});
}
@Override
public ResultSet execute(final CreateKeyspaceSpecification specification) {
return execute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(CreateKeyspaceCqlGenerator.toCql(specification));
}
});
}
@Override
public ResultSet execute(final AlterKeyspaceSpecification specification) {
return execute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(AlterKeyspaceCqlGenerator.toCql(specification));
}
});
}
@Override
public ResultSet execute(final DropIndexSpecification specification) {
return execute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(DropIndexCqlGenerator.toCql(specification));
}
});
}
@Override
public ResultSet execute(final CreateIndexSpecification specification) {
return execute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(CreateIndexCqlGenerator.toCql(specification));
}
});
}
}

View File

@@ -17,6 +17,8 @@ package org.springframework.cassandra.core.cql;
import java.util.regex.Pattern;
import com.datastax.driver.core.DataType;
public class CqlStringUtils {
protected static final String SINGLE_QUOTE = "\'";
@@ -24,6 +26,8 @@ public class CqlStringUtils {
protected static final String DOUBLE_QUOTE = "\"";
protected static final String DOUBLE_DOUBLE_QUOTE = "\"\"";
protected static final String EMPTY_STRING = "";
protected static final String TYPE_PARAMETER_PREFIX = "<";
protected static final String TYPE_PARAMETER_SUFFIX = ">";
public static StringBuilder noNull(StringBuilder sb) {
return sb == null ? new StringBuilder() : sb;
@@ -137,4 +141,34 @@ public class CqlStringUtils {
public static String removeSingleQuotes(Object thing) {
return thing == null ? (String) null : ((String) thing).replaceAll(SINGLE_QUOTE, EMPTY_STRING);
}
/**
* Renders the given {@link DataType} as a CQL string.
*
* @param dataType The {@link DataType} to render; must not be null.
*/
public static String toCql(DataType dataType) {
if (dataType.getTypeArguments().isEmpty()) {
return dataType.getName().name();
}
StringBuilder s = new StringBuilder();
s.append(dataType.getName().name()).append(TYPE_PARAMETER_PREFIX);
boolean first = true;
for (DataType argDataType : dataType.getTypeArguments()) {
if (first) {
first = false;
} else {
s.append(',');
}
s.append(argDataType.getName().name());
}
return s.append(TYPE_PARAMETER_SUFFIX).toString();
}
}

View File

@@ -29,10 +29,15 @@ import org.springframework.cassandra.core.keyspace.Option;
*/
public class AlterKeyspaceCqlGenerator extends KeyspaceOptionsCqlGenerator<AlterKeyspaceSpecification> {
public static String toCql(AlterKeyspaceSpecification specification) {
return new AlterKeyspaceCqlGenerator(specification).toCql();
}
public AlterKeyspaceCqlGenerator(AlterKeyspaceSpecification specification) {
super(specification);
}
@Override
public StringBuilder toCql(StringBuilder cql) {
cql = noNull(cql);

View File

@@ -34,10 +34,15 @@ import org.springframework.cassandra.core.keyspace.TableOption;
*/
public class AlterTableCqlGenerator extends TableOptionsCqlGenerator<AlterTableSpecification> {
public static String toCql(AlterTableSpecification specification) {
return new AlterTableCqlGenerator(specification).toCql();
}
public AlterTableCqlGenerator(AlterTableSpecification specification) {
super(specification);
}
@Override
public StringBuilder toCql(StringBuilder cql) {
cql = noNull(cql);

View File

@@ -28,10 +28,15 @@ import org.springframework.util.StringUtils;
*/
public class CreateIndexCqlGenerator extends IndexNameCqlGenerator<CreateIndexSpecification> {
public static String toCql(CreateIndexSpecification specification) {
return new CreateIndexCqlGenerator(specification).toCql();
}
public CreateIndexCqlGenerator(CreateIndexSpecification specification) {
super(specification);
}
@Override
public StringBuilder toCql(StringBuilder cql) {
cql = noNull(cql);

View File

@@ -30,10 +30,15 @@ import org.springframework.cassandra.core.keyspace.Option;
*/
public class CreateKeyspaceCqlGenerator extends KeyspaceCqlGenerator<CreateKeyspaceSpecification> {
public static String toCql(CreateKeyspaceSpecification specification) {
return new CreateKeyspaceCqlGenerator(specification).toCql();
}
public CreateKeyspaceCqlGenerator(CreateKeyspaceSpecification specification) {
super(specification);
}
@Override
public StringBuilder toCql(StringBuilder cql) {
cql = noNull(cql);

View File

@@ -35,10 +35,15 @@ import org.springframework.cassandra.core.keyspace.Option;
*/
public class CreateTableCqlGenerator extends TableCqlGenerator<CreateTableSpecification> {
public static String toCql(CreateTableSpecification specification) {
return new CreateTableCqlGenerator(specification).toCql();
}
public CreateTableCqlGenerator(CreateTableSpecification specification) {
super(specification);
}
@Override
public StringBuilder toCql(StringBuilder cql) {
cql = noNull(cql);

View File

@@ -27,10 +27,15 @@ import org.springframework.cassandra.core.keyspace.DropIndexSpecification;
*/
public class DropIndexCqlGenerator extends IndexNameCqlGenerator<DropIndexSpecification> {
public static String toCql(DropIndexSpecification specification) {
return new DropIndexCqlGenerator(specification).toCql();
}
public DropIndexCqlGenerator(DropIndexSpecification specification) {
super(specification);
}
@Override
public StringBuilder toCql(StringBuilder cql) {
return noNull(cql).append("DROP INDEX ")
// .append(spec().getIfExists() ? "IF EXISTS " : "")

View File

@@ -26,10 +26,15 @@ import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification;
*/
public class DropKeyspaceCqlGenerator extends KeyspaceNameCqlGenerator<DropKeyspaceSpecification> {
public static String toCql(DropKeyspaceSpecification specification) {
return new DropKeyspaceCqlGenerator(specification).toCql();
}
public DropKeyspaceCqlGenerator(DropKeyspaceSpecification specification) {
super(specification);
}
@Override
public StringBuilder toCql(StringBuilder cql) {
return noNull(cql).append("DROP KEYSPACE ").append(spec().getIfExists() ? "IF EXISTS " : "")
.append(spec().getNameAsIdentifier()).append(";");

View File

@@ -26,10 +26,15 @@ import org.springframework.cassandra.core.keyspace.DropTableSpecification;
*/
public class DropTableCqlGenerator extends TableNameCqlGenerator<DropTableSpecification> {
public static String toCql(DropTableSpecification specification) {
return new DropTableCqlGenerator(specification).toCql();
}
public DropTableCqlGenerator(DropTableSpecification specification) {
super(specification);
}
@Override
public StringBuilder toCql(StringBuilder cql) {
return noNull(cql).append("DROP TABLE ")
// .append(spec().getIfExists() ? "IF EXISTS " : "")

View File

@@ -47,6 +47,11 @@ public class CreateTableSpecification extends TableSpecification<CreateTableSpec
return ifNotExists;
}
@Override
public CreateTableSpecification name(String name) {
return (CreateTableSpecification) super.name(name);
}
/**
* Entry point into the {@link CreateTableSpecification}'s fluent API to create a table. Convenient if imported
* statically.

View File

@@ -46,4 +46,15 @@ public class DropTableSpecification extends TableNameSpecification<DropTableSpec
public static DropTableSpecification dropTable() {
return new DropTableSpecification();
}
/**
* Entry point into the {@link DropTableSpecification}'s fluent API to drop a table. Convenient if imported
* statically. This static method is shorter than the no-arg form, which would be
* <code>dropTable().name(tableName)</code>.
*
* @param tableName The name of the table to drop.
*/
public static DropTableSpecification dropTable(String tableName) {
return new DropTableSpecification().name(tableName);
}
}

View File

@@ -24,7 +24,6 @@ import com.datastax.driver.core.Session;
/**
* @author David Webb
*
*/
public class CassandraAccessor implements InitializingBean {
@@ -56,9 +55,7 @@ public class CassandraAccessor implements InitializingBean {
*/
@Override
public void afterPropertiesSet() {
if (getSession() == null) {
throw new IllegalArgumentException("Property 'session' is required");
}
Assert.notNull(session);
}
/**

View File

@@ -7,6 +7,8 @@ import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.EntityMapping;
import org.springframework.data.cassandra.mapping.Mapping;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.cassandra.config.java;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;
@@ -27,7 +28,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.cassandra.config.CassandraDataSessionFactoryBean;
import org.springframework.data.cassandra.config.Mapping;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
@@ -35,6 +35,8 @@ import org.springframework.data.cassandra.core.CassandraAdminOperations;
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext;
import org.springframework.data.cassandra.mapping.Mapping;
import org.springframework.data.cassandra.mapping.PrimaryKeyClass;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.ClassUtils;
@@ -139,6 +141,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC
false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Table.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(PrimaryKeyClass.class));
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {

View File

@@ -10,9 +10,9 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cassandra.config.xml.CassandraSessionParser;
import org.springframework.data.cassandra.config.DefaultDataBeanNames;
import org.springframework.data.cassandra.config.CassandraDataSessionFactoryBean;
import org.springframework.data.cassandra.config.EntityMapping;
import org.springframework.data.cassandra.config.Mapping;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.mapping.EntityMapping;
import org.springframework.data.cassandra.mapping.Mapping;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Attr;

View File

@@ -0,0 +1,15 @@
package org.springframework.data.cassandra.convert;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
public class CassandraPersistentEntityParameterValueProvider extends
PersistentEntityParameterValueProvider<CassandraPersistentProperty> {
public CassandraPersistentEntityParameterValueProvider(PersistentEntity<?, CassandraPersistentProperty> entity,
PropertyValueProvider<CassandraPersistentProperty> provider, Object parent) {
super(entity, provider, parent);
}
}

View File

@@ -31,8 +31,6 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
@@ -54,14 +52,14 @@ import com.datastax.driver.core.querybuilder.Update;
public class MappingCassandraConverter extends AbstractCassandraConverter implements CassandraConverter,
ApplicationContextAware, BeanClassLoaderAware {
protected static final Logger log = LoggerFactory.getLogger(MappingCassandraConverter.class);
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final CassandraMappingContext mappingContext;
protected ApplicationContext applicationContext;
private SpELContext spELContext;
private boolean useFieldAccessOnly = true;
protected SpELContext spELContext;
protected boolean useFieldAccessOnly = true;
private ClassLoader beanClassLoader;
protected ClassLoader beanClassLoader;
/**
* Creates a new {@link MappingCassandraConverter} with the given {@link CassandraMappingContext}.
@@ -111,45 +109,48 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(row, spELContext);
final PropertyValueProvider<CassandraPersistentProperty> propertyProvider = new CassandraPropertyValueProvider(row,
evaluator);
final CassandraPropertyValueProvider propertyProvider = new CassandraPropertyValueProvider(row, evaluator);
PersistentEntityParameterValueProvider<CassandraPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<CassandraPersistentProperty>(
CassandraPersistentEntityParameterValueProvider parameterProvider = new CassandraPersistentEntityParameterValueProvider(
entity, propertyProvider, null);
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
S instance = instantiator.createInstance(entity, parameterProvider);
final BeanWrapper<CassandraPersistentEntity<S>, S> wrapper = BeanWrapper.create(instance, conversionService);
S result = wrapper.getBean();
readPropertiesFromRow(entity, row, propertyProvider, wrapper);
return wrapper.getBean();
}
protected void readPropertiesFromRow(final CassandraPersistentEntity<?> entity, final Row row,
final CassandraPropertyValueProvider propertyProvider, final BeanWrapper<?, ?> wrapper) {
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
MappingCassandraConverter.this.handlePersistentPropertyRead(row, entity, prop, propertyProvider, wrapper);
MappingCassandraConverter.this.readPropertyFromRow(row, entity, prop, propertyProvider, wrapper);
}
});
return result;
}
protected void handlePersistentPropertyRead(final Row row, final CassandraPersistentEntity<?> entity,
final CassandraPersistentProperty prop,
final PropertyValueProvider<CassandraPersistentProperty> propertyProvider, final BeanWrapper<?, ?> wrapper) {
protected void readPropertyFromRow(final Row row, final CassandraPersistentEntity<?> entity,
final CassandraPersistentProperty prop, final CassandraPropertyValueProvider propertyProvider,
final BeanWrapper<?, ?> wrapper) {
if (entity.isConstructorArgument(prop)) { // skip 'cause prop was set in ctor
return;
}
if (prop.isCompositePrimaryKey()) {
// handle composite primary key properties via recursion into this method
throw new UnsupportedOperationException("composite primary keys are TODO");
readPropertiesFromRow(prop.getCompositePrimaryKeyEntity(), row, propertyProvider, wrapper);
return;
}
boolean hasValueForProperty = row.getColumnDefinitions().contains(prop.getColumnName());
if (!hasValueForProperty) {
if (!row.getColumnDefinitions().contains(prop.getColumnName())) {
return;
}
@@ -174,103 +175,121 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
@Override
public void write(Object obj, Object builtStatement) {
public void write(Object source, Object sink) {
if (obj == null) {
if (source == null) {
return;
}
Class<?> beanClassLoaderClass = transformClassToBeanClassLoaderClass(obj.getClass());
Class<?> beanClassLoaderClass = transformClassToBeanClassLoaderClass(source.getClass());
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(beanClassLoaderClass);
if (entity == null) {
throw new MappingException("No mapping metadata found for " + obj.getClass());
throw new MappingException("No mapping metadata found for " + source.getClass());
}
if (builtStatement instanceof Insert) {
writeInsertInternal(obj, (Insert) builtStatement, entity);
} else if (builtStatement instanceof Update) {
writeUpdateInternal(obj, (Update) builtStatement, entity);
} else if (builtStatement instanceof Where) {
writeDeleteWhereInternal(obj, (Where) builtStatement, entity);
if (sink instanceof Insert) {
writeInsertFromObject(source, (Insert) sink, entity);
} else if (sink instanceof Update) {
writeUpdateFromObject(source, (Update) sink, entity);
} else if (sink instanceof Where) {
writeDeleteWhereFromObject(source, (Where) sink, entity);
} else {
throw new MappingException("Unknown buildStatement " + builtStatement.getClass().getName());
throw new MappingException("Unknown buildStatement " + sink.getClass().getName());
}
}
private void writeInsertInternal(final Object objectToSave, final Insert insert, CassandraPersistentEntity<?> entity) {
final BeanWrapper<CassandraPersistentEntity<Object>, Object> wrapper = BeanWrapper.create(objectToSave,
conversionService);
// Write the properties
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
Object propertyObj = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly);
if (propertyObj != null) {
insert.value(prop.getColumnName(), propertyObj);
}
}
});
protected void writeInsertFromObject(final Object object, final Insert insert, CassandraPersistentEntity<?> entity) {
writeInsertFromWrapper(BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(object, conversionService),
insert, entity);
}
private void writeUpdateInternal(final Object objectToSave, final Update update, CassandraPersistentEntity<?> entity) {
protected void writeInsertFromWrapper(final BeanWrapper<CassandraPersistentEntity<Object>, Object> wrapper,
final Insert insert, CassandraPersistentEntity<?> entity) {
final BeanWrapper<CassandraPersistentEntity<Object>, Object> wrapper = BeanWrapper.create(objectToSave,
conversionService);
// Write the properties
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
Object propertyObj = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly);
Object value = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly);
if (propertyObj != null) {
if (prop.isIdProperty()) {
update.where(QueryBuilder.eq(prop.getColumnName(), propertyObj));
if (prop.isCompositePrimaryKey()) {
writeInsertFromWrapper(
BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(value, conversionService), insert,
prop.getCompositePrimaryKeyEntity());
return;
}
if (value != null) {
insert.value(prop.getColumnName(), value);
}
}
});
}
protected void writeUpdateFromObject(final Object object, final Update update, CassandraPersistentEntity<?> entity) {
writeUpdateFromWrapper(BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(object, conversionService),
update, entity);
}
protected void writeUpdateFromWrapper(final BeanWrapper<CassandraPersistentEntity<Object>, Object> wrapper,
final Update update, final CassandraPersistentEntity<?> entity) {
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
Object value = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly);
if (prop.isCompositePrimaryKey()) {
writeUpdateFromWrapper(
BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(value, conversionService), update,
prop.getCompositePrimaryKeyEntity());
return;
}
if (value != null) {
if (prop.isIdProperty() || entity.isCompositePrimaryKey()) {
update.where(QueryBuilder.eq(prop.getColumnName(), value));
} else {
update.with(QueryBuilder.set(prop.getColumnName(), propertyObj));
update.with(QueryBuilder.set(prop.getColumnName(), value));
}
}
}
});
}
private void writeDeleteWhereInternal(final Object objectToSave, final Where whereId,
CassandraPersistentEntity<?> entity) {
protected void writeDeleteWhereFromObject(final Object object, final Where where, CassandraPersistentEntity<?> entity) {
writeDeleteWhereFromWrapper(
BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(object, conversionService), where, entity);
}
final BeanWrapper<CassandraPersistentEntity<Object>, Object> wrapper = BeanWrapper.create(objectToSave,
conversionService);
protected void writeDeleteWhereFromWrapper(final BeanWrapper<CassandraPersistentEntity<Object>, Object> wrapper,
final Where where, CassandraPersistentEntity<?> entity) {
// Write the properties
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
CassandraPersistentProperty idProperty = entity.getIdProperty();
Object idValue = wrapper.getProperty(idProperty, idProperty.getType(), useFieldAccessOnly);
if (prop.isIdProperty()) {
if (idValue == null) {
String msg = String.format("no id value found in object {}", wrapper.getBean());
log.error(msg);
throw new IllegalArgumentException(msg);
}
Object propertyObj = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly);
if (propertyObj != null) {
whereId.and(QueryBuilder.eq(prop.getColumnName(), propertyObj));
}
}
}
});
if (idProperty.isCompositePrimaryKey()) {
writeDeleteWhereFromWrapper(
BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(idValue, conversionService), where,
idProperty.getCompositePrimaryKeyEntity());
return;
}
where.and(QueryBuilder.eq(idProperty.getColumnName(), idValue));
}
@SuppressWarnings("unchecked")
private <T> Class<T> transformClassToBeanClassLoaderClass(Class<T> entity) {
protected <T> Class<T> transformClassToBeanClassLoaderClass(Class<T> entity) {
try {
return (Class<T>) ClassUtils.forName(entity.getName(), beanClassLoader);
} catch (ClassNotFoundException e) {

View File

@@ -6,7 +6,12 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.core.SessionCallback;
import org.springframework.cassandra.core.cql.generator.AlterTableCqlGenerator;
import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.cassandra.core.cql.generator.DropTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.AlterTableSpecification;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.cassandra.core.keyspace.DropTableSpecification;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.convert.CassandraConverter;
@@ -60,7 +65,9 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
@Override
public void replaceTable(String tableName, Class<?> entityClass, Map<String, Object> optionsByName) {
throw new UnsupportedOperationException("not yet implemented");
dropTable(tableName);
createTable(false, tableName, entityClass, optionsByName);
}
/**
@@ -102,15 +109,7 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
log.info("Dropping table => " + tableName);
final String q = CqlUtils.dropTable(tableName);
log.info(q);
execute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) {
return s.execute(q);
}
});
execute(DropTableSpecification.dropTable(tableName));
}
@Override

View File

@@ -30,7 +30,6 @@ import com.datastax.driver.core.querybuilder.Select;
* @author Alex Shvid
* @author David Webb
* @author Matthew Adams
*
*/
public interface CassandraOperations extends CqlOperations {
@@ -58,9 +57,10 @@ public interface CassandraOperations extends CqlOperations {
* @param selectClass must not be {@literal null}, mapped entity type.
* @return
*/
<T> List<T> select(Select selectQuery, Class<T> selectClass);
<T> T selectOneById(Class<T> selectClass, Object id);
/**
* Execute query and convert ResultSet to the entity
*
@@ -72,13 +72,14 @@ public interface CassandraOperations extends CqlOperations {
<T> T selectOne(Select selectQuery, Class<T> selectClass);
Long countById(Class<?> clazz, Object id);
/**
* Counts rows for given query
*
* @param selectQuery
* @return
*/
Long count(Select selectQuery);
/**
@@ -87,7 +88,6 @@ public interface CassandraOperations extends CqlOperations {
* @param tableName
* @return
*/
Long count(String tableName);
/**
@@ -457,4 +457,8 @@ public interface CassandraOperations extends CqlOperations {
* @return
*/
CassandraConverter getConverter();
void deleteById(Class<?> clazz, Object id);
<T> List<T> selectByIds(Class<T> clazz, Iterable<?> ids);
}

View File

@@ -16,12 +16,9 @@
package org.springframework.data.cassandra.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.springframework.cassandra.core.CqlTemplate;
import org.springframework.cassandra.core.QueryOptions;
@@ -30,53 +27,48 @@ import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.exception.EntityWriterException;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext;
import org.springframework.data.cassandra.util.CqlUtils;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.convert.EntityWriter;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import com.datastax.driver.core.Query;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Batch;
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;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Update;
/**
* The Cassandra Data Template is a convenience API for all Cassandra Operations using POJOs. For low level Cassandra
* Operations use the {@link CqlTemplate}
* The CassandraTemplate is a convenient API for all Cassandra operations using POJOs with their Spring Data Cassandra
* mapping information. For low-level Cassandra operation, see {@link CqlTemplate}.
*
* @author Alex Shvid
* @author David Webb
* @author Matthew T. Adams
*
* @see CqlTemplate
*/
public class CassandraTemplate extends CqlTemplate implements CassandraOperations {
/*
* List of iterable classes when testing POJOs for specific operations.
*/
public static final Collection<String> ITERABLE_CLASSES;
static {
Set<String> iterableClasses = new HashSet<String>();
iterableClasses.add(List.class.getName());
iterableClasses.add(Collection.class.getName());
iterableClasses.add(Iterator.class.getName());
ITERABLE_CLASSES = Collections.unmodifiableCollection(iterableClasses);
}
/*
* Required elements for successful Template Operations. These can be set with the Constructor, or wired in
* later.
*/
private CassandraConverter cassandraConverter;
private CassandraMappingContext mappingContext;
private boolean useFieldAccessOnly = false;
/**
* Default Constructor for wiring in the required components later
@@ -84,15 +76,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
public CassandraTemplate() {
}
/**
* Constructor if only session is known at time of Template Creation
*
* @param session must not be {@literal null}
*/
public CassandraTemplate(Session session) {
this(session, new MappingCassandraConverter(new DefaultCassandraMappingContext()));
}
/**
* Constructor if only session and converter are known at time of Template Creation
*
@@ -101,23 +84,60 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
*/
public CassandraTemplate(Session session, CassandraConverter converter) {
setSession(session);
this.cassandraConverter = converter;
this.mappingContext = cassandraConverter.getCassandraMappingContext();
setConverter(converter);
}
public void setConverter(CassandraConverter cassandraConverter) {
Assert.notNull(cassandraConverter);
this.cassandraConverter = cassandraConverter;
mappingContext = cassandraConverter.getCassandraMappingContext();
Assert.notNull(mappingContext);
}
@Override
public CassandraConverter getConverter() {
return cassandraConverter;
}
public CassandraMappingContext getCassandraMappingContext() {
return mappingContext;
}
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
Assert.notNull(cassandraConverter);
Assert.notNull(mappingContext);
}
@Override
public Long countById(Class<?> clazz, Object id) {
Assert.notNull(clazz);
Assert.notNull(id);
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(clazz);
if (entity == null) {
throw new IllegalArgumentException(String.format("unknown persistent class [%s]", clazz.getName()));
}
Select select = QueryBuilder.select().countAll().from(entity.getTableName());
appendIdCriteria(select.where(), entity, id);
return count(select);
}
@Override
public Long count(Select selectQuery) {
return doSelectCount(selectQuery);
return selectCount(selectQuery);
}
@Override
public Long count(String tableName) {
Select select = QueryBuilder.select().countAll().from(tableName);
return doSelectCount(select);
return selectCount(select);
}
@Override
@@ -136,7 +156,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
@Override
public <T> void delete(List<T> entities, String tableName) {
delete(entities, tableName, null);
}
@@ -145,7 +164,21 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entities);
Assert.notEmpty(entities);
Assert.notNull(tableName);
doBatchDelete(tableName, entities, options, false);
batchDelete(tableName, entities, options, false);
}
@Override
public void deleteById(Class<?> clazz, Object id) {
Assert.notNull(clazz);
Assert.notNull(id);
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(clazz);
Delete delete = QueryBuilder.delete().all().from(entity.getTableName());
appendIdCriteria(delete.where(), entity, id);
execute(delete.getQueryString());
}
@Override
@@ -171,7 +204,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
public <T> void delete(T entity, String tableName, QueryOptions options) {
Assert.notNull(entity);
Assert.notNull(tableName);
doDelete(tableName, entity, options, false);
delete(tableName, entity, options, false);
}
@Override
@@ -198,7 +231,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entities);
Assert.notEmpty(entities);
Assert.notNull(tableName);
doBatchDelete(tableName, entities, options, true);
batchDelete(tableName, entities, options, true);
}
@Override
@@ -224,7 +257,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
public <T> void deleteAsynchronously(T entity, String tableName, QueryOptions options) {
Assert.notNull(entity);
Assert.notNull(tableName);
doDelete(tableName, entity, options, true);
delete(tableName, entity, options, true);
}
/**
@@ -246,11 +279,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
return entity.getTableName();
}
@Override
public CassandraConverter getConverter() {
return cassandraConverter;
}
@Override
public String getTableName(Class<?> entityClass) {
return determineTableName(entityClass);
@@ -280,7 +308,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entities);
Assert.notEmpty(entities);
Assert.notNull(tableName);
return doBatchInsert(tableName, entities, options, false);
return batchInsert(tableName, entities, options, false);
}
@Override
@@ -307,7 +335,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entity);
Assert.notNull(tableName);
ensureNotIterable(entity);
return doInsert(tableName, entity, options, false);
return insert(tableName, entity, options, false);
}
@Override
@@ -334,7 +362,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entities);
Assert.notEmpty(entities);
Assert.notNull(tableName);
return doBatchInsert(tableName, entities, options, true);
return batchInsert(tableName, entities, options, true);
}
@Override
@@ -363,7 +391,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
ensureNotIterable(entity);
return doInsert(tableName, entity, options, true);
return insert(tableName, entity, options, true);
}
@Override
@@ -373,7 +401,108 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
@Override
public <T> List<T> select(String cql, Class<T> selectClass) {
return doSelect(cql, new ReadRowCallback<T>(cassandraConverter, selectClass));
return select(cql, new ReadRowCallback<T>(cassandraConverter, selectClass));
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public <T> List<T> selectByIds(Class<T> clazz, Iterable<?> ids) {
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(clazz);
if (entity == null) {
throw new IllegalArgumentException(String.format("unknown persistent entity class [%s]", clazz.getName()));
}
if (entity.getIdProperty().isCompositePrimaryKey()) {
throw new IllegalArgumentException(String.format(
"entity class [%s] uses a composite primary key class [%s] which this method can't support", clazz.getName(),
entity.getIdProperty().getCompositePrimaryKeyEntity().getType().getName()));
}
List idList = null;
if (ids instanceof List) {
idList = (List) ids;
} else {
idList = new ArrayList();
for (Object id : ids) {
idList.add(id);
}
}
Select select = QueryBuilder.select().all().from(entity.getTableName());
select.where(QueryBuilder.in(entity.getIdProperty().getColumnName(), idList.toArray()));
return select(select, clazz);
}
@Override
public <T> T selectOneById(Class<T> selectClass, Object id) {
Assert.notNull(selectClass);
Assert.notNull(id);
CassandraPersistentEntity<?> entityClass = mappingContext.getPersistentEntity(selectClass);
if (entityClass == null) {
throw new IllegalArgumentException(String.format("unknown entity class [%s]", selectClass.getName()));
}
Select select = QueryBuilder.select().all().from(entityClass.getTableName());
appendIdCriteria(select.where(), entityClass, id);
return selectOne(select, selectClass);
}
protected interface ClauseCallback {
void onClause(Clause clause);
}
protected void appendIdCriteria(final ClauseCallback clauseCallback, CassandraPersistentEntity<?> entity, Object id) {
CassandraPersistentProperty idProperty = entity.getIdProperty();
if (idProperty.isCompositePrimaryKey()) {
CassandraPersistentEntity<?> idEntity = idProperty.getCompositePrimaryKeyEntity();
final BeanWrapper<CassandraPersistentEntity<Object>, Object> idWrapper = BeanWrapper
.<CassandraPersistentEntity<Object>, Object> create(id, cassandraConverter.getConversionService());
idEntity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty p) {
clauseCallback.onClause(QueryBuilder.eq(p.getColumnName(),
idWrapper.getProperty(p, p.getActualType(), useFieldAccessOnly)));
}
});
return;
}
clauseCallback.onClause(QueryBuilder.eq(idProperty.getColumnName(), id));
}
protected void appendIdCriteria(final com.datastax.driver.core.querybuilder.Select.Where where,
CassandraPersistentEntity<?> entity, Object id) {
appendIdCriteria(new ClauseCallback() {
@Override
public void onClause(Clause clause) {
where.and(clause);
}
}, entity, id);
}
protected void appendIdCriteria(final Where where, CassandraPersistentEntity<?> entity, Object id) {
appendIdCriteria(new ClauseCallback() {
@Override
public void onClause(Clause clause) {
where.and(clause);
}
}, entity, id);
}
@Override
@@ -383,7 +512,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
@Override
public <T> T selectOne(String cql, Class<T> selectClass) {
return doSelectOne(cql, new ReadRowCallback<T>(cassandraConverter, selectClass));
return selectOne(cql, new ReadRowCallback<T>(cassandraConverter, selectClass));
}
@Override
@@ -410,7 +539,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entities);
Assert.notEmpty(entities);
Assert.notNull(tableName);
return doBatchUpdate(tableName, entities, options, false);
return batchUpdate(tableName, entities, options, false);
}
@Override
@@ -436,7 +565,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
public <T> T update(T entity, String tableName, QueryOptions options) {
Assert.notNull(entity);
Assert.notNull(tableName);
return doUpdate(tableName, entity, options, false);
return update(tableName, entity, options, false);
}
@Override
@@ -463,7 +592,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entities);
Assert.notEmpty(entities);
Assert.notNull(tableName);
return doBatchUpdate(tableName, entities, options, true);
return batchUpdate(tableName, entities, options, true);
}
@Override
@@ -490,7 +619,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
public <T> T updateAsynchronously(T entity, String tableName, QueryOptions options) {
Assert.notNull(entity);
Assert.notNull(tableName);
return doUpdate(tableName, entity, options, true);
return update(tableName, entity, options, true);
}
/**
@@ -505,12 +634,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
return null;
}
/**
* @param query
* @param readRowCallback
* @return
*/
private <T> List<T> doSelect(final String query, ReadRowCallback<T> readRowCallback) {
private <T> List<T> select(final String query, ReadRowCallback<T> readRowCallback) {
ResultSet resultSet = doExecute(new SessionCallback<ResultSet>() {
@@ -534,11 +658,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
return result;
}
/**
* @param selectQuery
* @return
*/
private Long doSelectCount(final Select query) {
private Long selectCount(final Select query) {
Long count = null;
@@ -569,7 +689,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param readRowCallback
* @return
*/
private <T> T doSelectOne(final String query, ReadRowCallback<T> readRowCallback) {
private <T> T selectOne(final String query, ReadRowCallback<T> readRowCallback) {
logger.info(query);
@@ -607,36 +727,29 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param tableName
* @param objectToRemove
*/
protected <T> void doBatchDelete(final String tableName, final List<T> entities, final QueryOptions options,
protected <T> void batchDelete(final String tableName, final List<T> entities, final QueryOptions options,
final boolean deleteAsynchronously) {
Assert.notEmpty(entities);
try {
final Batch b = createDeleteBatchQuery(tableName, entities, options, cassandraConverter);
logger.info(b.toString());
final Batch b = CqlUtils.toDeleteBatchQuery(tableName, entities, options, cassandraConverter);
logger.info(b.toString());
doExecute(new SessionCallback<Object>() {
doExecute(new SessionCallback<Object>() {
@Override
public Object doInSession(Session s) throws DataAccessException {
if (deleteAsynchronously) {
s.executeAsync(b);
} else {
s.execute(b);
}
return null;
@Override
public Object doInSession(Session s) throws DataAccessException {
if (deleteAsynchronously) {
s.executeAsync(b);
} else {
s.execute(b);
}
});
} catch (EntityWriterException e) {
throw getExceptionTranslator().translateExceptionIfPossible(
new RuntimeException("Failed to translate Object to Query", e));
}
return null;
}
});
}
/**
@@ -648,36 +761,29 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param insertAsychronously
* @return
*/
protected <T> List<T> doBatchInsert(final String tableName, final List<T> entities, final QueryOptions options,
protected <T> List<T> batchInsert(final String tableName, final List<T> entities, final QueryOptions options,
final boolean insertAsychronously) {
Assert.notEmpty(entities);
try {
final Batch b = createInsertBatchQuery(tableName, entities, options, cassandraConverter);
logger.info(b.getQueryString());
final Batch b = CqlUtils.toInsertBatchQuery(tableName, entities, options, cassandraConverter);
logger.info(b.getQueryString());
return doExecute(new SessionCallback<List<T>>() {
return doExecute(new SessionCallback<List<T>>() {
@Override
public List<T> doInSession(Session s) throws DataAccessException {
if (insertAsychronously) {
s.executeAsync(b);
} else {
s.execute(b);
}
return entities;
@Override
public List<T> doInSession(Session s) throws DataAccessException {
if (insertAsychronously) {
s.executeAsync(b);
} else {
s.execute(b);
}
});
} catch (EntityWriterException e) {
throw getExceptionTranslator().translateExceptionIfPossible(
new RuntimeException("Failed to translate Object to Query", e));
}
return entities;
}
});
}
/**
@@ -689,36 +795,29 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param updateAsychronously
* @return
*/
protected <T> List<T> doBatchUpdate(final String tableName, final List<T> entities, final QueryOptions options,
protected <T> List<T> batchUpdate(final String tableName, final List<T> entities, final QueryOptions options,
final boolean updateAsychronously) {
Assert.notEmpty(entities);
try {
final Batch b = toUpdateBatchQuery(tableName, entities, options, cassandraConverter);
logger.info(b.toString());
final Batch b = CqlUtils.toUpdateBatchQuery(tableName, entities, options, cassandraConverter);
logger.info(b.toString());
return doExecute(new SessionCallback<List<T>>() {
return doExecute(new SessionCallback<List<T>>() {
@Override
public List<T> doInSession(Session s) throws DataAccessException {
if (updateAsychronously) {
s.executeAsync(b);
} else {
s.execute(b);
}
return entities;
@Override
public List<T> doInSession(Session s) throws DataAccessException {
if (updateAsychronously) {
s.executeAsync(b);
} else {
s.execute(b);
}
});
} catch (EntityWriterException e) {
throw getExceptionTranslator().translateExceptionIfPossible(
new RuntimeException("Failed to translate Object to Query", e));
}
return entities;
}
});
}
/**
@@ -727,54 +826,26 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param tableName
* @param objectToRemove
*/
protected <T> void doDelete(final String tableName, final T objectToRemove, final QueryOptions options,
protected <T> void delete(final String tableName, final T objectToRemove, final QueryOptions options,
final boolean deleteAsynchronously) {
try {
final Query q = createDeleteQuery(tableName, objectToRemove, options, cassandraConverter);
logger.info(q.toString());
final Query q = CqlUtils.toDeleteQuery(tableName, objectToRemove, options, cassandraConverter);
logger.info(q.toString());
doExecute(new SessionCallback<Object>() {
doExecute(new SessionCallback<Object>() {
@Override
public Object doInSession(Session s) throws DataAccessException {
if (deleteAsynchronously) {
s.executeAsync(q);
} else {
s.execute(q);
}
return null;
@Override
public Object doInSession(Session s) throws DataAccessException {
if (deleteAsynchronously) {
s.executeAsync(q);
} else {
s.execute(q);
}
});
} catch (EntityWriterException e) {
throw getExceptionTranslator().translateExceptionIfPossible(
new RuntimeException("Failed to translate Object to Query", e));
}
}
/**
* Execute a command at the Session Level
*
* @param callback
* @return
*/
@Override
protected <T> T doExecute(SessionCallback<T> callback) {
Assert.notNull(callback);
try {
return callback.doInSession(getSession());
} catch (DataAccessException e) {
throw translateExceptionIfPossible(e);
}
return null;
}
});
}
/**
@@ -783,42 +854,34 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param tableName
* @param entity
*/
protected <T> T doInsert(final String tableName, final T entity, final QueryOptions options,
protected <T> T insert(final String tableName, final T entity, final QueryOptions options,
final boolean insertAsychronously) {
try {
final Query q = createInsertQuery(tableName, entity, options, cassandraConverter);
final Query q = CqlUtils.toInsertQuery(tableName, entity, options, cassandraConverter);
logger.info(q.toString());
if (q.getConsistencyLevel() != null) {
logger.info(q.getConsistencyLevel().name());
}
if (q.getRetryPolicy() != null) {
logger.info(q.getRetryPolicy().toString());
}
return doExecute(new SessionCallback<T>() {
@Override
public T doInSession(Session s) throws DataAccessException {
if (insertAsychronously) {
s.executeAsync(q);
} else {
s.execute(q);
}
return entity;
}
});
} catch (EntityWriterException e) {
throw getExceptionTranslator().translateExceptionIfPossible(
new RuntimeException("Failed to translate Object to Query", e));
logger.info(q.toString());
if (q.getConsistencyLevel() != null) {
logger.info(q.getConsistencyLevel().name());
}
if (q.getRetryPolicy() != null) {
logger.info(q.getRetryPolicy().toString());
}
return doExecute(new SessionCallback<T>() {
@Override
public T doInSession(Session s) throws DataAccessException {
if (insertAsychronously) {
s.executeAsync(q);
} else {
s.execute(q);
}
return entity;
}
});
}
/**
@@ -830,35 +893,27 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param updateAsychronously
* @return
*/
protected <T> T doUpdate(final String tableName, final T entity, final QueryOptions options,
protected <T> T update(final String tableName, final T entity, final QueryOptions options,
final boolean updateAsychronously) {
try {
final Query q = toUpdateQuery(tableName, entity, options, cassandraConverter);
logger.info(q.toString());
final Query q = CqlUtils.toUpdateQuery(tableName, entity, options, cassandraConverter);
logger.info(q.toString());
return doExecute(new SessionCallback<T>() {
return doExecute(new SessionCallback<T>() {
@Override
public T doInSession(Session s) throws DataAccessException {
if (updateAsychronously) {
s.executeAsync(q);
} else {
s.execute(q);
}
return entity;
@Override
public T doInSession(Session s) throws DataAccessException {
if (updateAsychronously) {
s.executeAsync(q);
} else {
s.execute(q);
}
});
} catch (EntityWriterException e) {
throw getExceptionTranslator().translateExceptionIfPossible(
new RuntimeException("Failed to translate Object to Query", e));
}
return entity;
}
});
}
/**
@@ -867,10 +922,197 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param o
*/
protected void ensureNotIterable(Object o) {
if (null != o) {
if (o.getClass().isArray() || ITERABLE_CLASSES.contains(o.getClass().getName())) {
throw new IllegalArgumentException("Cannot use a collection here.");
}
if (null == o) {
return;
}
if (o.getClass().isArray() || (o instanceof Iterable) || (o instanceof Iterator)) {
throw new IllegalArgumentException("cannot use a multivalued object here.");
}
}
/**
* Generates a Query Object for an insert
*
* @param tableName
* @param objectToSave
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
*/
public static Query createInsertQuery(String tableName, final Object objectToSave, QueryOptions options,
EntityWriter<Object, Object> entityWriter) {
final Insert q = QueryBuilder.insertInto(tableName);
/*
* Write properties
*/
entityWriter.write(objectToSave, q);
/*
* Add Query Options
*/
CqlTemplate.addQueryOptions(q, options);
/*
* Add TTL to Insert object
*/
if (options != null && options.getTtl() != null) {
q.using(QueryBuilder.ttl(options.getTtl()));
}
return q;
}
/**
* Generates a Query Object for an Update
*
* @param tableName
* @param objectToSave
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
*/
public static Query toUpdateQuery(String tableName, final Object objectToSave, QueryOptions options,
EntityWriter<Object, Object> entityWriter) {
final Update q = QueryBuilder.update(tableName);
/*
* Write properties
*/
entityWriter.write(objectToSave, q);
/*
* Add Query Options
*/
CqlTemplate.addQueryOptions(q, options);
/*
* Add TTL to Insert object
*/
if (options != null && options.getTtl() != null) {
q.using(QueryBuilder.ttl(options.getTtl()));
}
return q;
}
/**
* Generates a Batch Object for multiple Updates
*
* @param tableName
* @param objectsToSave
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
*/
public static <T> Batch toUpdateBatchQuery(final String tableName, final List<T> objectsToSave, QueryOptions options,
EntityWriter<Object, Object> entityWriter) {
/*
* Return variable is a Batch statement
*/
final Batch b = QueryBuilder.batch();
for (final T objectToSave : objectsToSave) {
b.add((Statement) toUpdateQuery(tableName, objectToSave, options, entityWriter));
}
/*
* Add Query Options
*/
CqlTemplate.addQueryOptions(b, options);
return b;
}
/**
* Generates a Batch Object for multiple inserts
*
* @param tableName
* @param entities
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
*/
public static <T> Batch createInsertBatchQuery(final String tableName, final List<T> entities, QueryOptions options,
EntityWriter<Object, Object> entityWriter) {
Batch batch = QueryBuilder.batch();
for (T entity : entities) {
batch.add((Statement) createInsertQuery(tableName, entity, options, entityWriter));
}
CqlTemplate.addQueryOptions(batch, options);
return batch;
}
/**
* Create a Delete Query Object from an annotated POJO
*
* @param tableName
* @param object
* @param entity
* @param optionsByName
* @return
*/
public static Query createDeleteQuery(String tableName, final Object object, QueryOptions options,
EntityWriter<Object, Object> entityWriter) {
Delete.Selection ds = QueryBuilder.delete();
Delete q = ds.from(tableName);
Where w = q.where();
entityWriter.write(object, w);
CqlTemplate.addQueryOptions(q, options);
return q;
}
/**
* Create a Batch Query object for multiple deletes.
*
* @param tableName
* @param entities
* @param entity
* @param optionsByName
*
* @return
*/
public static <T> Batch createDeleteBatchQuery(String tableName, List<T> entities, QueryOptions options,
EntityWriter<Object, Object> entityWriter) {
Batch batch = QueryBuilder.batch();
for (T entity : entities) {
batch.add((Statement) createDeleteQuery(tableName, entity, options, entityWriter));
}
CqlTemplate.addQueryOptions(batch, options);
return batch;
}
public boolean getUseFieldAccessOnly() {
return useFieldAccessOnly;
}
public void setUseFieldAccessOnly(boolean useFieldAccessOnly) {
this.useFieldAccessOnly = useFieldAccessOnly;
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2011-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.exception;
/**
* Exception to handle failing to write a PersistedEntity to a CQL String or Query object
*
* @author David Webb
*
*/
public class EntityWriterException extends Exception {
private static final long serialVersionUID = -3068204776019978031L;
/**
* @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

@@ -15,6 +15,9 @@
*/
package org.springframework.data.cassandra.mapping;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.cassandra.support.exception.UnsupportedCassandraOperationException;
import org.springframework.context.ApplicationContext;
@@ -24,6 +27,7 @@ import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.data.cassandra.util.CassandraNamingUtils;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.Expression;
@@ -34,8 +38,7 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Cassandra specific {@link BasicPersistentEntity} implementation that adds Cassandra specific metadata such as the
* table name.
* Cassandra specific {@link BasicPersistentEntity} implementation that adds Cassandra specific metadata.
*
* @author Alex Shvid
* @author Matthew T. Adams
@@ -43,10 +46,14 @@ import org.springframework.util.StringUtils;
public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T, CassandraPersistentProperty> implements
CassandraPersistentEntity<T>, ApplicationContextAware {
private String tableName;
private final SpelExpressionParser spelParser;
private final StandardEvaluationContext spelContext;
private final Class<T> type;
protected String tableName;
protected CassandraMappingContext mappingContext;
protected final SpelExpressionParser spelParser;
protected final StandardEvaluationContext spelContext;
public BasicCassandraPersistentEntity(TypeInformation<T> typeInformation) {
this(typeInformation, null);
}
/**
* Creates a new {@link BasicCassandraPersistentEntity} with the given {@link TypeInformation}. Will default the table
@@ -54,23 +61,22 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
*
* @param typeInformation
*/
public BasicCassandraPersistentEntity(TypeInformation<T> typeInformation) {
public BasicCassandraPersistentEntity(TypeInformation<T> typeInformation, CassandraMappingContext mappingContext) {
super(typeInformation, DefaultCassandraPersistentPropertyColumnComparator.IT);
super(typeInformation, CassandraPersistentPropertyComparator.IT);
this.spelParser = new SpelExpressionParser();
this.spelContext = new StandardEvaluationContext();
this.type = typeInformation.getType();
this.mappingContext = mappingContext;
determineTableName();
}
protected void determineTableName() {
Table anno = type.getAnnotation(Table.class);
Table anno = getType().getAnnotation(Table.class);
this.tableName = anno != null && StringUtils.hasText(anno.value()) ? anno.value() : CassandraNamingUtils
.getPreferredTableName(type);
.getPreferredTableName(getType());
}
@Override
@@ -102,4 +108,46 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
Assert.hasText(tableName);
this.tableName = tableName;
}
@Override
public CassandraMappingContext getMappingContext() {
return mappingContext;
}
@Override
public boolean isCompositePrimaryKey() {
return getType().isAnnotationPresent(PrimaryKeyClass.class);
}
@Override
public List<CassandraPersistentProperty> getCompositePrimaryKeyProperties() {
final List<CassandraPersistentProperty> properties = new ArrayList<CassandraPersistentProperty>();
if (!isCompositePrimaryKey()) {
throw new IllegalStateException(String.format("[%s] does not represent a composite primary key class", this
.getType().getName()));
}
addCompositePrimaryKeyProperties(this, properties);
return properties;
}
protected void addCompositePrimaryKeyProperties(CassandraPersistentEntity<?> compositePrimaryKeyEntity,
final List<CassandraPersistentProperty> properties) {
compositePrimaryKeyEntity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty p) {
if (p.isCompositePrimaryKey()) {
addCompositePrimaryKeyProperties(p.getCompositePrimaryKeyEntity(), properties);
} else {
properties.add(p);
}
}
});
}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.cassandra.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -24,6 +25,7 @@ import org.springframework.cassandra.core.Ordering;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
@@ -49,15 +51,20 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
*/
public BasicCassandraPersistentProperty(Field field, PropertyDescriptor propertyDescriptor,
CassandraPersistentEntity<?> owner, CassandraSimpleTypeHolder simpleTypeHolder) {
super(field, propertyDescriptor, owner, simpleTypeHolder);
}
@Override
public CassandraPersistentEntity<?> getOwner() {
return (CassandraPersistentEntity<?>) super.getOwner();
}
@Override
public boolean isCompositePrimaryKey() {
return getField().getType().isAnnotationPresent(PrimaryKeyClass.class);
}
@Override
public Class<?> getCompositePrimaryKeyType() {
if (!isCompositePrimaryKey()) {
return null;
@@ -67,31 +74,23 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
}
@Override
public CassandraPersistentEntity<?> getCompositePrimaryKeyEntity() {
public TypeInformation<?> getCompositePrimaryKeyTypeInformation() {
if (!isCompositePrimaryKey()) {
return null;
}
return (CassandraPersistentEntity<?>) ClassTypeInformation.from(getCompositePrimaryKeyType());
return ClassTypeInformation.from(getCompositePrimaryKeyType());
}
@Override
public String getColumnName() {
// first check @Column annotation
Column column = findAnnotation(Column.class);
if (column != null && StringUtils.hasText(column.value())) {
return column.value();
List<String> columnNames = getColumnNames();
if (columnNames.size() != 1) {
throw new IllegalStateException("property does not have a single column mapping");
}
// else check @PrimaryKeyColumn annotation
PrimaryKeyColumn pk = findAnnotation(PrimaryKeyColumn.class);
if (pk != null && StringUtils.hasText(pk.name())) {
return pk.name();
}
// else default
return field.getName().toLowerCase(); // TODO: replace with naming strategy class
return columnNames.get(0);
}
@Override
@@ -199,11 +198,6 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return isAnnotationPresent(PrimaryKeyColumn.class);
}
@Override
protected Association<CassandraPersistentProperty> createAssociation() {
return new Association<CassandraPersistentProperty>(this, null);
}
protected DataType getDataTypeFor(DataType.Name typeName) {
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(typeName);
if (dataType == null) {
@@ -230,4 +224,79 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
+ this.getName() + "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());
}
}
@Override
public List<String> getColumnNames() {
final List<String> columnNames = new ArrayList<String>();
if (isCompositePrimaryKey()) {
addCompositePrimaryKeyColumnNames(getCompositePrimaryKeyEntity(), columnNames);
return columnNames;
}
// else not a composite primary key property -- first check @Column annotation
Column column = findAnnotation(Column.class);
if (column != null && StringUtils.hasText(column.value())) {
columnNames.add(column.value());
return columnNames;
}
// else check @PrimaryKeyColumn annotation
PrimaryKeyColumn pk = findAnnotation(PrimaryKeyColumn.class);
if (pk != null && StringUtils.hasText(pk.name())) {
columnNames.add(pk.name());
return columnNames;
}
// else default
columnNames.add(field.getName().toLowerCase()); // TODO: replace with naming strategy class
return columnNames;
}
protected void addCompositePrimaryKeyColumnNames(CassandraPersistentEntity<?> compositePrimaryKeyEntity,
final List<String> columnNames) {
compositePrimaryKeyEntity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty p) {
if (p.isCompositePrimaryKey()) {
addCompositePrimaryKeyColumnNames(p.getCompositePrimaryKeyEntity(), columnNames);
} else {
columnNames.add(p.getColumnName());
}
}
});
}
@Override
public List<CassandraPersistentProperty> getCompositePrimaryKeyProperties() {
if (!isCompositePrimaryKey()) {
throw new IllegalStateException(String.format("[%s] does not represent a composite primary key property",
getField()));
}
return getCompositePrimaryKeyEntity().getCompositePrimaryKeyProperties();
}
@Override
public CassandraPersistentEntity<?> getCompositePrimaryKeyEntity() {
CassandraMappingContext mappingContext = getOwner().getMappingContext();
if (mappingContext == null) {
throw new IllegalStateException("need CassandraMappingContext");
}
return mappingContext.getPersistentEntity(getCompositePrimaryKeyTypeInformation());
}
@Override
public Association<CassandraPersistentProperty> getAssociation() {
throw new UnsupportedOperationException("Cassandra does not support associations");
}
@Override
protected Association<CassandraPersistentProperty> createAssociation() {
return new Association<CassandraPersistentProperty>(this, null);
}
}

View File

@@ -0,0 +1,64 @@
package org.springframework.data.cassandra.mapping;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.data.util.TypeInformation;
public class CachingCassandraPersistentEntity<T> extends BasicCassandraPersistentEntity<T> {
protected String tableName;
protected String name;
protected Boolean isCompositePrimaryKey;
protected List<CassandraPersistentProperty> compositePrimaryKeyProperties;
protected Map<String, CassandraPersistentProperty> properties = new HashMap<String, CassandraPersistentProperty>();
public CachingCassandraPersistentEntity(TypeInformation<T> typeInformation) {
super(typeInformation);
}
public CachingCassandraPersistentEntity(TypeInformation<T> typeInformation, CassandraMappingContext mappingContext) {
super(typeInformation, mappingContext);
}
@Override
public String getTableName() {
if (tableName == null) {
tableName = super.getTableName();
}
return tableName;
}
@Override
public String getName() {
if (name == null) {
name = super.getName();
}
return name;
}
@Override
public boolean isCompositePrimaryKey() {
if (isCompositePrimaryKey == null) {
isCompositePrimaryKey = super.isCompositePrimaryKey();
}
return isCompositePrimaryKey;
}
@Override
public List<CassandraPersistentProperty> getCompositePrimaryKeyProperties() {
if (compositePrimaryKeyProperties == null) {
compositePrimaryKeyProperties = super.getCompositePrimaryKeyProperties();
}
return compositePrimaryKeyProperties;
}
@Override
public CassandraPersistentProperty getPersistentProperty(String name) {
if (properties.get(name) == null) {
properties.put(name, super.getPersistentProperty(name));
}
return properties.get(name);
}
}

View File

@@ -17,8 +17,10 @@ package org.springframework.data.cassandra.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.List;
import org.springframework.cassandra.core.Ordering;
import org.springframework.data.util.TypeInformation;
import com.datastax.driver.core.DataType;
@@ -37,11 +39,12 @@ public class CachingCassandraPersistentProperty extends BasicCassandraPersistent
private Boolean isClusterKeyColumn;
private Boolean isPrimaryKeyColumn;
private String columnName;
private List<String> columnNames;
private Ordering ordering;
private boolean orderingCached = false;
private DataType dataType;
private Class<?> compositePrimaryKeyType;
private CassandraPersistentEntity<?> compositePrimaryKeyEntity;
private TypeInformation<?> compositePrimaryKeyTypeInformation;
/**
* Creates a new {@link CachingCassandraPersistentProperty}.
@@ -52,12 +55,12 @@ public class CachingCassandraPersistentProperty extends BasicCassandraPersistent
}
@Override
public CassandraPersistentEntity<?> getCompositePrimaryKeyEntity() {
public TypeInformation<?> getCompositePrimaryKeyTypeInformation() {
if (compositePrimaryKeyEntity == null) {
compositePrimaryKeyEntity = super.getCompositePrimaryKeyEntity();
if (compositePrimaryKeyTypeInformation == null) {
compositePrimaryKeyTypeInformation = super.getCompositePrimaryKeyTypeInformation();
}
return compositePrimaryKeyEntity;
return compositePrimaryKeyTypeInformation;
}
@Override
@@ -150,4 +153,12 @@ public class CachingCassandraPersistentProperty extends BasicCassandraPersistent
}
return isPartitionKeyColumn;
}
@Override
public List<String> getColumnNames() {
if (columnNames == null) {
columnNames = super.getColumnNames();
}
return columnNames;
}
}

View File

@@ -0,0 +1,21 @@
package org.springframework.data.cassandra.mapping;
import java.util.Comparator;
/**
* {@link Comparator} implementation that uses {@link Column#value()}.
*
* @author Matthew T. Adams
*/
public enum CassandraColumnAnnotationComparator implements Comparator<Column> {
/**
* The sole instance of this class.
*/
IT;
@Override
public int compare(Column left, Column right) {
return left.value().compareTo(right.value());
}
}

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.cassandra.mapping;
import java.util.List;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.MutablePersistentEntity;
@@ -24,7 +27,15 @@ import org.springframework.data.mapping.model.MutablePersistentEntity;
* @author Alex Shvid
* @author Matthew T. Adams
*/
public interface CassandraPersistentEntity<T> extends MutablePersistentEntity<T, CassandraPersistentProperty> {
public interface CassandraPersistentEntity<T> extends MutablePersistentEntity<T, CassandraPersistentProperty>,
ApplicationContextAware {
/**
* Returns whether this entity represents a composite primary key.
*/
boolean isCompositePrimaryKey();
List<CassandraPersistentProperty> getCompositePrimaryKeyProperties();
/**
* Returns the table name to which the entity shall be persisted.
@@ -37,4 +48,6 @@ public interface CassandraPersistentEntity<T> extends MutablePersistentEntity<T,
* @param tableName The table name; must contain a valid Cassandra table name.
*/
void setTableName(String tableName);
CassandraMappingContext getMappingContext();
}

View File

@@ -15,8 +15,11 @@
*/
package org.springframework.data.cassandra.mapping;
import java.util.List;
import org.springframework.cassandra.core.Ordering;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.TypeInformation;
import com.datastax.driver.core.DataType;
@@ -33,12 +36,6 @@ public interface CassandraPersistentProperty extends PersistentProperty<Cassandr
*/
boolean isCompositePrimaryKey();
/**
* Returns the type of the composite primary key class of this entity, or null if this class does not use a composite
* primary key.
*/
Class<?> getCompositePrimaryKeyType();
/**
* Returns a {@link CassandraPersistentEntity} representing the composite primary key class of this entity, or null if
* this class does not use a composite primary key.
@@ -46,22 +43,42 @@ public interface CassandraPersistentProperty extends PersistentProperty<Cassandr
CassandraPersistentEntity<?> getCompositePrimaryKeyEntity();
/**
* The name of the column to which a property is persisted.
* Returns a {@link TypeInformation} representing the type of the composite primary key class of this entity, or null
* if this class does not use a composite primary key.
*/
TypeInformation<?> getCompositePrimaryKeyTypeInformation();
/**
* Gets the list of composite primary key properties that this composite primary key field is a placeholder for.
*/
List<CassandraPersistentProperty> getCompositePrimaryKeyProperties();
/**
* The name of the single column to which the property is persisted. This is a convenience method when the caller
* knows that the property is mapped to a single column. Throws {@link IllegalStateException} if this property is
* mapped to multiple columns.
*/
String getColumnName();
/**
* The ordering for the column. Valid only for clustered columns.
* The names of the columns to which the property is persisted if this is a composite primary key property. Never
* returns null.
*/
List<String> getColumnNames();
/**
* The ordering (ascending or descending) for the column. Valid only for primary key columns; returns null for
* non-primary key columns.
*/
Ordering getPrimaryKeyOrdering();
/**
* The column's data type.
* The column's data type. Not valid for a composite primary key, in which case this method returns null.
*/
DataType getDataType();
/**
* Whether the property has secondary index on this column.
* Whether the property has a secondary index on this column.
*/
boolean isIndexed();

View File

@@ -0,0 +1,87 @@
package org.springframework.data.cassandra.mapping;
import java.lang.reflect.Field;
import java.util.Comparator;
/**
* {@link Comparator} implementation that orders {@link CassandraPersistentProperty} instances.
* <p/>
* Composite primary key properties and primary key properties sort before non-primary key properties.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public enum CassandraPersistentPropertyComparator implements Comparator<CassandraPersistentProperty> {
/**
* The sole instance of this class.
*/
IT;
@Override
public int compare(CassandraPersistentProperty left, CassandraPersistentProperty right) {
if (left != null && right == null) {
return -1;
}
if (left == null && right != null) {
return 1;
}
if (left == null && right == null) {
return 0;
}
if (left.equals(right)) {
return 0;
}
boolean leftIsCompositePrimaryKey = left.isCompositePrimaryKey();
boolean rightIsCompositePrimaryKey = right.isCompositePrimaryKey();
if (leftIsCompositePrimaryKey && rightIsCompositePrimaryKey) {
return 0;
}
boolean leftIsPrimaryKey = left.isPrimaryKeyColumn();
boolean rightIsPrimaryKey = right.isPrimaryKeyColumn();
Field leftField = left.getField();
Field rightField = right.getField();
if (leftIsPrimaryKey && rightIsPrimaryKey) {
return CassandraPrimaryKeyColumnAnnotationComparator.IT.compare(leftField.getAnnotation(PrimaryKeyColumn.class),
rightField.getAnnotation(PrimaryKeyColumn.class));
}
boolean leftIsKey = leftIsCompositePrimaryKey || leftIsPrimaryKey;
boolean rightIsKey = rightIsCompositePrimaryKey || rightIsPrimaryKey;
if (leftIsKey && !rightIsKey) {
return 1;
}
if (!leftIsKey && rightIsKey) {
return -1;
}
// else, neither property is a composite primary key nor a primary key; compare @Column annotations
Column leftColumn = leftField.getAnnotation(Column.class);
Column rightColumn = rightField.getAnnotation(Column.class);
if (leftColumn == null && rightColumn == null) {
return leftField.getName().compareTo(rightField.getName());
}
if (leftColumn != null && rightColumn != null) {
return CassandraColumnAnnotationComparator.IT.compare(leftColumn, rightColumn);
}
if (leftColumn != null && rightColumn == null) {
return leftColumn.value().compareTo(rightField.getName());
}
// else leftColumn == null && rightColumn != null)
return leftField.getName().compareTo(rightColumn.value());
}
}

View File

@@ -19,7 +19,7 @@ import org.springframework.cassandra.core.PrimaryKeyType;
*
* @author Matthew T. Adams
*/
public enum DefaultCassandraPrimaryKeyColumnComparator implements Comparator<PrimaryKeyColumn> {
public enum CassandraPrimaryKeyColumnAnnotationComparator implements Comparator<PrimaryKeyColumn> {
/**
* The sole instance of this class.
@@ -27,23 +27,23 @@ public enum DefaultCassandraPrimaryKeyColumnComparator implements Comparator<Pri
IT;
@Override
public int compare(PrimaryKeyColumn o1, PrimaryKeyColumn o2) {
public int compare(PrimaryKeyColumn left, PrimaryKeyColumn right) {
int comparison = o1.type().compareTo(o2.type());
int comparison = left.type().compareTo(right.type());
if (comparison != 0) {
return comparison;
}
comparison = new Integer(o1.ordinal()).compareTo(o2.ordinal());
comparison = new Integer(left.ordinal()).compareTo(right.ordinal());
if (comparison != 0) {
return comparison;
}
comparison = o1.name().compareTo(o2.name());
comparison = left.name().compareTo(right.name());
if (comparison != 0) {
return comparison;
}
return o1.ordering().compareTo(o2.ordering());
return left.ordering().compareTo(right.ordering());
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.cassandra.mapping;
import static org.springframework.cassandra.core.keyspace.CreateTableSpecification.createTable;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.HashMap;
@@ -73,7 +75,7 @@ public class DefaultCassandraMappingContext extends
@Override
protected <T> CassandraPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
BasicCassandraPersistentEntity<T> entity = new BasicCassandraPersistentEntity<T>(typeInformation);
CassandraPersistentEntity<T> entity = new CachingCassandraPersistentEntity<T>(typeInformation, this);
if (context != null) {
entity.setApplicationContext(context);
@@ -104,9 +106,7 @@ public class DefaultCassandraMappingContext extends
Assert.notNull(entity);
final CreateTableSpecification spec = new CreateTableSpecification();
spec.name(entity.getTableName());
final CreateTableSpecification spec = createTable().name(entity.getTableName());
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@@ -124,7 +124,7 @@ public class DefaultCassandraMappingContext extends
if (pkProp.isPartitionKeyColumn()) {
spec.partitionKeyColumn(pkProp.getColumnName(), pkProp.getDataType());
} else {
} else { // it's a cluster column
spec.clusteredKeyColumn(pkProp.getColumnName(), pkProp.getDataType(), pkProp.getPrimaryKeyOrdering());
}
}

View File

@@ -1,22 +0,0 @@
package org.springframework.data.cassandra.mapping;
import java.util.Comparator;
/**
* {@link Comparator} implementation that uses the {@link CassandraPersistentProperty}'s column name for ordering.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public enum DefaultCassandraPersistentPropertyColumnComparator implements Comparator<CassandraPersistentProperty> {
/**
* The sole instance of this class.
*/
IT;
@Override
public int compare(CassandraPersistentProperty o1, CassandraPersistentProperty o2) {
return o1.getColumnName().compareTo(o2.getColumnName());
}
}

View File

@@ -1,4 +1,7 @@
package org.springframework.data.cassandra.config;
package org.springframework.data.cassandra.mapping;
import java.util.HashMap;
import java.util.Map;
/**
* Mapping information for an individual entity class.
@@ -7,9 +10,21 @@ package org.springframework.data.cassandra.config;
*/
public class EntityMapping {
/**
* The name of the entity's class.
*/
protected String entityClassName;
/**
* The name of the table to which the entity is mapped.
*/
protected String tableName;
/**
* The {@link PropertyMapping}s for each persistent property, keyed on property name.
*/
protected Map<String, PropertyMapping> propertyMappings = new HashMap<String, PropertyMapping>();
public EntityMapping(String entityClassName, String tableName) {
setEntityClassName(entityClassName);
setTableName(tableName);

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.config;
package org.springframework.data.cassandra.mapping;
import java.util.Collections;
import java.util.HashSet;

View File

@@ -0,0 +1,38 @@
package org.springframework.data.cassandra.mapping;
import org.springframework.util.Assert;
/**
* Mapping between a persistent entity's property and its column.
*
* @author Matthew T. Adams
*/
public class PropertyMapping {
protected String propertyName;
protected String columnName;
public PropertyMapping(String propertyName, String columnName) {
setPropertyName(propertyName);
setColumnName(columnName);
}
public String getPropertyName() {
return propertyName;
}
protected void setPropertyName(String propertyName) {
Assert.notNull(propertyName);
this.propertyName = propertyName;
}
public String getColumnName() {
return columnName;
}
protected void setColumnName(String columnName) {
Assert.notNull(columnName);
this.columnName = columnName;
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.data.cassandra.repository;
import java.io.Serializable;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
@@ -24,9 +23,7 @@ import org.springframework.data.repository.CrudRepository;
* Cassandra-specific extension of the {@link CrudRepository} interface.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public interface CassandraRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
List<T> findByPartitionKey(ID id);
}

View File

@@ -33,12 +33,4 @@ public interface CassandraEntityInformation<T, ID extends Serializable> extends
* @return
*/
String getTableName();
/**
* Returns the column that the id will be persisted to.
*
* @return
*/
String getIdColumn();
}

View File

@@ -59,10 +59,6 @@ public class MappingCassandraEntityInformation<T, ID extends Serializable> exten
this.customTableName = customTableName;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.support.EntityInformation#getId(java.lang.Object)
*/
@SuppressWarnings("unchecked")
@Override
public ID getId(T entity) {
@@ -80,28 +76,14 @@ public class MappingCassandraEntityInformation<T, ID extends Serializable> exten
}
}
/* (non-Javadoc)
* @see org.springframework.data.repository.support.EntityInformation#getIdType()
*/
@SuppressWarnings("unchecked")
@Override
public Class<ID> getIdType() {
return (Class<ID>) entityMetadata.getIdProperty().getType();
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.CassandraEntityInformation#getTableName()
*/
@Override
public String getTableName() {
return customTableName == null ? entityMetadata.getTableName() : customTableName;
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.CassandraEntityInformation#getIdColumn()
*/
public String getIdColumn() {
return entityMetadata.getIdProperty().getName();
}
}

View File

@@ -31,6 +31,7 @@ import com.datastax.driver.core.querybuilder.Clause;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Select.Where;
/**
* Repository base implementation for Cassandra.
@@ -41,8 +42,8 @@ import com.datastax.driver.core.querybuilder.Select;
public class SimpleCassandraRepository<T, ID extends Serializable> implements CassandraRepository<T, ID> {
private final CassandraTemplate cassandraTemplate;
private final CassandraEntityInformation<T, ID> entityInformation;
protected final CassandraTemplate cassandraTemplate;
protected final CassandraEntityInformation<T, ID> entityInformation;
/**
* Creates a new {@link SimpleCassandraRepository} for the given {@link CassandraEntityInformation} and
@@ -51,8 +52,7 @@ public class SimpleCassandraRepository<T, ID extends Serializable> implements Ca
* @param metadata must not be {@literal null}.
* @param template must not be {@literal null}.
*/
public SimpleCassandraRepository(CassandraEntityInformation<T, ID> metadata,
CassandraTemplate cassandraTemplate) {
public SimpleCassandraRepository(CassandraEntityInformation<T, ID> metadata, CassandraTemplate cassandraTemplate) {
Assert.notNull(cassandraTemplate);
Assert.notNull(metadata);
@@ -65,6 +65,7 @@ public class SimpleCassandraRepository<T, ID extends Serializable> implements Ca
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
*/
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null!");
@@ -76,6 +77,7 @@ public class SimpleCassandraRepository<T, ID extends Serializable> implements Ca
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
*/
@Override
public <S extends T> List<S> save(Iterable<S> entities) {
Assert.notNull(entities, "The given Iterable of entities not be null!");
@@ -90,78 +92,31 @@ public class SimpleCassandraRepository<T, ID extends Serializable> implements Ca
return result;
}
private Clause getIdClause(ID id) {
Clause clause = QueryBuilder.eq(entityInformation.getIdColumn(), id);
return clause;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable)
*/
public T findOne(ID id) {
Assert.notNull(id, "The given id must not be null!");
Select select = QueryBuilder.select().all().from(entityInformation.getTableName());
select.where(getIdClause(id));
return cassandraTemplate.selectOne(select, entityInformation.getJavaType());
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.repository.CassandraRepository#findByPartitionKey(java.io.Serializable)
*/
@Override
public List<T> findByPartitionKey(ID id) {
Assert.notNull(id, "The given id must not be null!");
Select select = QueryBuilder.select().all().from(entityInformation.getTableName());
select.where(getIdClause(id));
return cassandraTemplate.select(select, entityInformation.getJavaType());
public T findOne(ID id) {
return cassandraTemplate.selectOneById(entityInformation.getJavaType(), id);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable)
*/
@Override
public boolean exists(ID id) {
Assert.notNull(id, "The given id must not be null!");
Select select = QueryBuilder.select().countAll().from(entityInformation.getTableName());
select.where(getIdClause(id));
Long num = cassandraTemplate.count(select);
return num != null && num.longValue() > 0;
return cassandraTemplate.countById(entityInformation.getJavaType(), id) >= 1; // TODO: == instead of >= ?
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#count()
*/
@Override
public long count() {
return cassandraTemplate.count(entityInformation.getTableName());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable)
*/
@Override
public void delete(ID id) {
Assert.notNull(id, "The given id must not be null!");
Delete delete = QueryBuilder.delete().all().from(entityInformation.getTableName());
delete.where(getIdClause(id));
cassandraTemplate.execute(delete.getQueryString());
cassandraTemplate.deleteById(entityInformation.getJavaType(), id);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
*/
@Override
public void delete(T entity) {
Assert.notNull(entity, "The given entity must not be null!");
delete(entityInformation.getId(entity));
@@ -171,6 +126,7 @@ public class SimpleCassandraRepository<T, ID extends Serializable> implements Ca
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable)
*/
@Override
public void delete(Iterable<? extends T> entities) {
Assert.notNull(entities, "The given Iterable of entities not be null!");
@@ -184,6 +140,7 @@ public class SimpleCassandraRepository<T, ID extends Serializable> implements Ca
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#deleteAll()
*/
@Override
public void deleteAll() {
cassandraTemplate.truncate(entityInformation.getTableName());
}
@@ -192,29 +149,19 @@ public class SimpleCassandraRepository<T, ID extends Serializable> implements Ca
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#findAll()
*/
@Override
public List<T> findAll() {
Select select = QueryBuilder.select().all().from(entityInformation.getTableName());
return findAll(select);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
*/
@Override
public Iterable<T> findAll(Iterable<ID> ids) {
List<ID> parameters = new ArrayList<ID>();
for (ID id : ids) {
parameters.add(id);
}
Clause clause = QueryBuilder.in(entityInformation.getIdColumn(), parameters.toArray());
Select select = QueryBuilder.select().all().from(entityInformation.getTableName());
select.where(clause);
return findAll(select);
return cassandraTemplate.selectByIds(entityInformation.getJavaType(), ids);
}
private List<T> findAll(Select query) {
protected List<T> findAll(Select query) {
if (query == null) {
return Collections.emptyList();

View File

@@ -3,25 +3,14 @@ package org.springframework.data.cassandra.util;
import java.util.ArrayList;
import java.util.List;
import org.springframework.cassandra.core.CqlTemplate;
import org.springframework.cassandra.core.QueryOptions;
import org.springframework.data.cassandra.exception.EntityWriterException;
import org.springframework.cassandra.core.cql.CqlStringUtils;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.convert.EntityWriter;
import org.springframework.data.mapping.PropertyHandler;
import com.datastax.driver.core.ColumnMetadata;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Query;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.TableMetadata;
import com.datastax.driver.core.querybuilder.Batch;
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;
import com.datastax.driver.core.querybuilder.Update;
/**
* Utilities to convert Cassandra Annotated objects to Queries and CQL.
@@ -33,39 +22,7 @@ import com.datastax.driver.core.querybuilder.Update;
public abstract class CqlUtils {
/**
* 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>();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
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(");");
result.add(str.toString());
}
}
});
return result;
}
/**
* Alter the table to refelct the entity annotations
* Alter the table to reflect the entity annotations
*
* @param tableName
* @param entity
@@ -104,7 +61,7 @@ public abstract class CqlUtils {
str.append("TYPE ");
}
str.append(toCQL(columnDataType));
str.append(CqlStringUtils.toCql(columnDataType));
str.append(';');
result.add(str.toString());
@@ -114,243 +71,4 @@ public abstract class CqlUtils {
return result;
}
/**
* Generates a Query Object for an insert
*
* @param tableName
* @param objectToSave
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
* @throws EntityWriterException
*/
public static Query toInsertQuery(String tableName, final Object objectToSave, QueryOptions options,
EntityWriter<Object, Object> entityWriter) throws EntityWriterException {
final Insert q = QueryBuilder.insertInto(tableName);
/*
* Write properties
*/
entityWriter.write(objectToSave, q);
/*
* Add Query Options
*/
CqlTemplate.addQueryOptions(q, options);
/*
* Add TTL to Insert object
*/
if (options != null && options.getTtl() != null) {
q.using(QueryBuilder.ttl(options.getTtl()));
}
return q;
}
/**
* Generates a Query Object for an Update
*
* @param tableName
* @param objectToSave
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
* @throws EntityWriterException
*/
public static Query toUpdateQuery(String tableName, final Object objectToSave, QueryOptions options,
EntityWriter<Object, Object> entityWriter) throws EntityWriterException {
final Update q = QueryBuilder.update(tableName);
/*
* Write properties
*/
entityWriter.write(objectToSave, q);
/*
* Add Query Options
*/
CqlTemplate.addQueryOptions(q, options);
/*
* Add TTL to Insert object
*/
if (options != null && options.getTtl() != null) {
q.using(QueryBuilder.ttl(options.getTtl()));
}
return q;
}
/**
* Generates a Batch Object for multiple Updates
*
* @param tableName
* @param objectsToSave
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
* @throws EntityWriterException
*/
public static <T> Batch toUpdateBatchQuery(final String tableName, final List<T> objectsToSave, QueryOptions options,
EntityWriter<Object, Object> entityWriter) throws EntityWriterException {
/*
* Return variable is a Batch statement
*/
final Batch b = QueryBuilder.batch();
for (final T objectToSave : objectsToSave) {
b.add((Statement) toUpdateQuery(tableName, objectToSave, options, entityWriter));
}
/*
* Add Query Options
*/
CqlTemplate.addQueryOptions(b, options);
return b;
}
/**
* Generates a Batch Object for multiple inserts
*
* @param tableName
* @param objectsToSave
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
* @throws EntityWriterException
*/
public static <T> Batch toInsertBatchQuery(final String tableName, final List<T> objectsToSave, QueryOptions options,
EntityWriter<Object, Object> entityWriter) throws EntityWriterException {
/*
* Return variable is a Batch statement
*/
final Batch b = QueryBuilder.batch();
for (final T objectToSave : objectsToSave) {
b.add((Statement) toInsertQuery(tableName, objectToSave, options, entityWriter));
}
/*
* Add Query Options
*/
CqlTemplate.addQueryOptions(b, options);
return b;
}
/**
* Create a Delete Query Object from an annotated POJO
*
* @param tableName
* @param objectToRemove
* @param entity
* @param optionsByName
* @return
* @throws EntityWriterException
*/
public static Query toDeleteQuery(String tableName, final Object objectToRemove, QueryOptions options,
EntityWriter<Object, Object> entityWriter) throws EntityWriterException {
final Delete.Selection ds = QueryBuilder.delete();
final Delete q = ds.from(tableName);
final Where w = q.where();
/*
* Write where condition to find by Id
*/
entityWriter.write(objectToRemove, w);
CqlTemplate.addQueryOptions(q, options);
return q;
}
/**
* @param dataType
* @return
*/
public static String toCQL(DataType dataType) {
if (dataType.getTypeArguments().isEmpty()) {
return dataType.getName().name();
} else {
StringBuilder str = new StringBuilder();
str.append(dataType.getName().name());
str.append('<');
for (DataType argDataType : dataType.getTypeArguments()) {
if (str.charAt(str.length() - 1) != '<') {
str.append(',');
}
str.append(argDataType.getName().name());
}
str.append('>');
return str.toString();
}
}
/**
* @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();
}
/**
* Create a Batch Query object for multiple deletes.
*
* @param tableName
* @param entities
* @param entity
* @param optionsByName
*
* @return
* @throws EntityWriterException
*/
public static <T> Batch toDeleteBatchQuery(String tableName, List<T> entities, QueryOptions options,
EntityWriter<Object, Object> entityWriter) throws EntityWriterException {
/*
* Return variable is a Batch statement
*/
final Batch b = QueryBuilder.batch();
for (final T objectToSave : entities) {
b.add((Statement) toDeleteQuery(tableName, objectToSave, options, entityWriter));
}
CqlTemplate.addQueryOptions(b, options);
return b;
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.test.integration.mapping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.cassandra.core.keyspace.ColumnSpecification;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.data.cassandra.mapping.BasicCassandraPersistentProperty;
import org.springframework.data.cassandra.mapping.CachingCassandraPersistentProperty;
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.mapping.CassandraSimpleTypeHolder;
import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.PrimaryKeyClass;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.util.ReflectionUtils;
import com.datastax.driver.core.DataType;
/**
* Integration test for {@link BasicCassandraPersistentProperty} with a composite primary key class.
*
* @author Matthew T. Adams
*/
public class CassandraCompositePrimaryKeyIntegrationTests {
private static final CassandraSimpleTypeHolder SIMPLE_TYPE_HOLDER = new CassandraSimpleTypeHolder();
@PrimaryKeyClass
static class Key {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
String z;
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.CLUSTERED)
String a;
}
@Table
static class Thing {
@PrimaryKey
Key id;
Date time;
@Column("message")
String text;
}
CassandraMappingContext context;
CassandraPersistentEntity<?> thing;
CassandraPersistentEntity<?> key;
@Before
public void setup() {
context = new DefaultCassandraMappingContext();
thing = context.getPersistentEntity(ClassTypeInformation.from(Thing.class));
key = context.getPersistentEntity(ClassTypeInformation.from(Key.class));
}
@Test
public void validateMappingInfo() {
Field field = ReflectionUtils.findField(Thing.class, "id");
CassandraPersistentProperty property = new CachingCassandraPersistentProperty(field, null, thing,
SIMPLE_TYPE_HOLDER);
assertTrue(property.isIdProperty());
assertTrue(property.isCompositePrimaryKey());
List<String> expectedColumnNames = Arrays.asList(new String[] { "z", "a" });
assertTrue(expectedColumnNames.equals(property.getColumnNames()));
List<String> actualColumnNames = new ArrayList<String>();
List<CassandraPersistentProperty> properties = property.getCompositePrimaryKeyProperties();
for (CassandraPersistentProperty p : properties) {
actualColumnNames.addAll(p.getColumnNames());
}
assertTrue(expectedColumnNames.equals(actualColumnNames));
CreateTableSpecification spec = context.getCreateTableSpecificationFor(thing);
List<ColumnSpecification> partitionKeyColumns = spec.getPartitionKeyColumns();
assertEquals(1, partitionKeyColumns.size());
ColumnSpecification partitionKeyColumn = partitionKeyColumns.get(0);
assertEquals("z", partitionKeyColumn.getName());
assertEquals(PrimaryKeyType.PARTITIONED, partitionKeyColumn.getKeyType());
assertEquals(DataType.text(), partitionKeyColumn.getType());
List<ColumnSpecification> clusteredKeyColumns = spec.getClusteredKeyColumns();
assertEquals(1, clusteredKeyColumns.size());
ColumnSpecification clusteredKeyColumn = clusteredKeyColumns.get(0);
assertEquals("a", clusteredKeyColumn.getName());
assertEquals(PrimaryKeyType.CLUSTERED, clusteredKeyColumn.getKeyType());
assertEquals(DataType.text(), partitionKeyColumn.getType());
}
}

View File

@@ -24,6 +24,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.data.cassandra.mapping.BasicCassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.BasicCassandraPersistentProperty;
import org.springframework.data.cassandra.mapping.CachingCassandraPersistentProperty;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder;
@@ -76,6 +77,6 @@ public class CompoundPrimaryKeyIntegrationTests {
}
private CassandraPersistentProperty getPropertyFor(Field field) {
return new BasicCassandraPersistentProperty(field, null, entity, new CassandraSimpleTypeHolder());
return new CachingCassandraPersistentProperty(field, null, entity, new CassandraSimpleTypeHolder());
}
}