Merge branch 'master' of local repository into DATACASS-89

This commit is contained in:
David T Webb
2014-03-19 09:43:27 -04:00
58 changed files with 2286 additions and 253 deletions

View File

@@ -234,10 +234,6 @@ public class CassandraCqlClusterFactoryBean implements FactoryBean<Cluster>, Ini
(CreateKeyspaceSpecification) spec).toCql() : new DropKeyspaceCqlGenerator(
(DropKeyspaceSpecification) spec).toCql();
if (log.isDebugEnabled()) {
log.debug("executing CQL [{}]", cql);
}
template.execute(cql);
}
}

View File

@@ -27,6 +27,8 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.cql.generator.AlterKeyspaceCqlGenerator;
import org.springframework.cassandra.core.cql.generator.AlterTableCqlGenerator;
@@ -89,6 +91,8 @@ import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
*/
public class CqlTemplate extends CassandraAccessor implements CqlOperations {
protected static final Logger log = LoggerFactory.getLogger(CqlTemplate.class);
/**
* Add common {@link Query} options for all types of queries.
*
@@ -455,6 +459,11 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
if (log.isDebugEnabled()) {
log.debug("executing [{}]", q.toString());
}
return s.execute(q);
}
});
@@ -466,6 +475,10 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
@Override
public ResultSetFuture doInSession(Session s) throws DataAccessException {
if (log.isDebugEnabled()) {
log.debug("asynchronously executing [{}]", q.toString());
}
return s.executeAsync(q);
}
});

View File

@@ -0,0 +1,93 @@
package org.springframework.cassandra.core.converter;
import java.util.List;
import java.util.Map;
import org.springframework.core.convert.converter.Converter;
import com.datastax.driver.core.ResultSet;
/**
* Convenient converter that can be used to convert a single-row-single-column, single-row-multi-column, or multi-row
* {@link ResultSet} into the a value of a given type. The majority of the expected usage is to convert a
* single-row-single-column result set into the target type.
* <p/>
* The algorithm is:
* <ul>
* <li>if there is one row with one column, convert that value to this converter's type if possible or throw,</li>
* <li>else if there is one row with multiple columns, convert the columns into this converter's type if possible or
* throw,</li>
* <li>else convert the rows into this converter's type (since there are multiple rows) or throw.</li>
* </ul>
* <p/>
* If the converter throws due to the inability to convert a given {@link ResultSet}, it will throw an
* {@link IllegalArgumentException}.
*
* @author Matthew T. Adams
*
* @param <T>
*/
public abstract class AbstractResultSetConverter<T> implements Converter<ResultSet, T> {
/**
* Converts the given value to this converter's type or throws {@link IllegalArgumentException}.
*/
protected abstract T doConvertSingleValue(Object object);
protected abstract Class<?> getType();
protected ResultSetToListConverter converter = new ResultSetToListConverter();
protected T getNullResultSetValue() {
return null;
}
protected T getExhaustedResultSetValue() {
return null;
}
@Override
public T convert(ResultSet source) {
if (source == null) {
return getNullResultSetValue();
}
if (source.isExhausted()) {
return getExhaustedResultSetValue();
}
List<Map<String, Object>> list = converter.convert(source);
if (list.size() == 1) {
Map<String, Object> map = list.get(0);
return map.size() == 1 ? doConvertSingleValue(map.get(map.keySet().iterator().next())) : doConvertSingleRow(map);
}
return doConvertResultSet(list);
}
/**
* Converts the given result set (as a {@link List}&lt;{@link Map}&lt;String,Object&gt;&gt;) to this converter's type
* or throws {@link IllegalArgumentException}. This default implementation simply throws.
*/
protected T doConvertResultSet(List<Map<String, Object>> resultSet) {
doThrow("result set");
return null;
}
/**
* Converts the given row (as a {@link Map}&lt;String,Object&gt;) to this converter's type or throws
* {@link IllegalArgumentException}. This default implementation simply throws.
*/
protected T doConvertSingleRow(Map<String, Object> row) {
doThrow("row");
return null;
}
protected void doThrow(String string) {
throw new IllegalArgumentException(String.format("can't convert %s to desired type [%s]", string, getType()
.getName()));
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.cassandra.core.converter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
/**
* Thin wrapper that allows subclasses to delegate conversion of the given value to a {@link DefaultConversionService}.
*
* @author Matthew T. Adams
*
* @param <T>
*/
public abstract class AbstractResultSetToBasicFixedTypeConverter<T> extends AbstractResultSetConverter<T> {
protected final static ConversionService CONVERTER = new DefaultConversionService();
}

View File

@@ -0,0 +1,46 @@
package org.springframework.cassandra.core.converter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.Assert;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
public class ResultSetToArrayConverter implements Converter<ResultSet, Object[]> {
protected Converter<Row, Object[]> rowConverter;
public ResultSetToArrayConverter(Converter<Row, Object[]> rowConverter) {
setRowConverter(rowConverter);
}
public Converter<Row, Object[]> getRowConverter() {
return rowConverter;
}
public void setRowConverter(Converter<Row, Object[]> rowConverter) {
Assert.notNull(rowConverter);
this.rowConverter = rowConverter;
}
@Override
public Object[] convert(ResultSet resultSet) {
if (resultSet == null) {
return null;
}
List<Object[]> list = new ArrayList<Object[]>();
Iterator<Row> i = resultSet.iterator();
while (i.hasNext()) {
list.add(rowConverter.convert(i.next()));
}
return list.toArray();
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.cassandra.core.converter;
import java.math.BigDecimal;
public class ResultSetToBigDecimalConverter extends AbstractResultSetToBasicFixedTypeConverter<BigDecimal> {
@Override
protected BigDecimal doConvertSingleValue(Object object) {
return CONVERTER.convert(object, BigDecimal.class);
}
@Override
protected Class<?> getType() {
return BigDecimal.class;
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.cassandra.core.converter;
import java.math.BigInteger;
public class ResultSetToBigIntegerConverter extends AbstractResultSetToBasicFixedTypeConverter<BigInteger> {
@Override
protected BigInteger doConvertSingleValue(Object object) {
return CONVERTER.convert(object, BigInteger.class);
}
@Override
protected Class<?> getType() {
return BigInteger.class;
}
}

View File

@@ -0,0 +1,14 @@
package org.springframework.cassandra.core.converter;
public class ResultSetToBooleanConverter extends AbstractResultSetToBasicFixedTypeConverter<Boolean> {
@Override
protected Boolean doConvertSingleValue(Object object) {
return CONVERTER.convert(object, Boolean.class);
}
@Override
protected Class<?> getType() {
return Boolean.class;
}
}

View File

@@ -0,0 +1,21 @@
package org.springframework.cassandra.core.converter;
import java.nio.ByteBuffer;
public class ResultSetToByteBufferConverter extends AbstractResultSetConverter<ByteBuffer> {
@Override
protected ByteBuffer doConvertSingleValue(Object object) {
if (!(object instanceof ByteBuffer)) {
doThrow("value");
}
return (ByteBuffer) object;
}
@Override
protected Class<?> getType() {
return ByteBuffer.class;
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.cassandra.core.converter;
import java.util.Date;
public class ResultSetToDateConverter extends AbstractResultSetToBasicFixedTypeConverter<Date> {
@Override
protected Date doConvertSingleValue(Object object) {
return CONVERTER.convert(object, Date.class);
}
@Override
protected Class<?> getType() {
return Date.class;
}
}

View File

@@ -0,0 +1,14 @@
package org.springframework.cassandra.core.converter;
public class ResultSetToDoubleConverter extends AbstractResultSetToBasicFixedTypeConverter<Double> {
@Override
protected Double doConvertSingleValue(Object object) {
return CONVERTER.convert(object, Double.class);
}
@Override
protected Class<?> getType() {
return Double.class;
}
}

View File

@@ -0,0 +1,14 @@
package org.springframework.cassandra.core.converter;
public class ResultSetToFloatConverter extends AbstractResultSetToBasicFixedTypeConverter<Float> {
@Override
protected Float doConvertSingleValue(Object object) {
return CONVERTER.convert(object, Float.class);
}
@Override
protected Class<?> getType() {
return Float.class;
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.cassandra.core.converter;
import java.net.InetAddress;
public class ResultSetToInetAddressConverter extends AbstractResultSetToBasicFixedTypeConverter<InetAddress> {
@Override
protected InetAddress doConvertSingleValue(Object object) {
return CONVERTER.convert(object, InetAddress.class);
}
@Override
protected Class<?> getType() {
return InetAddress.class;
}
}

View File

@@ -0,0 +1,14 @@
package org.springframework.cassandra.core.converter;
public class ResultSetToIntegerConverter extends AbstractResultSetToBasicFixedTypeConverter<Integer> {
@Override
protected Integer doConvertSingleValue(Object object) {
return CONVERTER.convert(object, Integer.class);
}
@Override
protected Class<?> getType() {
return Integer.class;
}
}

View File

@@ -0,0 +1,50 @@
package org.springframework.cassandra.core.converter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.Assert;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
public class ResultSetToListConverter implements Converter<ResultSet, List<Map<String, Object>>> {
protected Converter<Row, Map<String, Object>> rowConverter = new RowToMapConverter();
public ResultSetToListConverter() {
}
public ResultSetToListConverter(Converter<Row, Map<String, Object>> rowConverter) {
setRowConverter(rowConverter);
}
public Converter<Row, Map<String, Object>> getRowConverter() {
return rowConverter;
}
public void setRowConverter(Converter<Row, Map<String, Object>> rowConverter) {
Assert.notNull(rowConverter);
this.rowConverter = rowConverter;
}
@Override
public List<Map<String, Object>> convert(ResultSet resultSet) {
if (resultSet == null) {
return null;
}
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Iterator<Row> i = resultSet.iterator();
while (i.hasNext()) {
list.add(rowConverter.convert(i.next()));
}
return list;
}
}

View File

@@ -0,0 +1,48 @@
package org.springframework.cassandra.core.converter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.util.StringUtils;
public class ResultSetToListOfStringConverter extends AbstractResultSetConverter<List<String>> {
@Override
protected List<String> doConvertSingleValue(Object object) {
List<String> list = new ArrayList<String>();
list.add(object == null ? null : object.toString());
return list;
}
@Override
protected List<String> doConvertSingleRow(Map<String, Object> row) {
List<String> list = new ArrayList<String>(row.size());
for (Object value : row.values()) {
list.add(value == null ? null : value.toString());
}
return list;
}
@Override
protected List<String> doConvertResultSet(List<Map<String, Object>> resultSet) {
List<String> list = new ArrayList<String>(resultSet.size());
for (Map<String, Object> row : resultSet) {
list.add(StringUtils.arrayToCommaDelimitedString(doConvertSingleRow(row).toArray()));
}
return list;
}
@Override
protected Class<?> getType() {
return List.class;
}
}

View File

@@ -0,0 +1,14 @@
package org.springframework.cassandra.core.converter;
public class ResultSetToLongConverter extends AbstractResultSetToBasicFixedTypeConverter<Long> {
@Override
protected Long doConvertSingleValue(Object object) {
return CONVERTER.convert(object, Long.class);
}
@Override
protected Class<?> getType() {
return Long.class;
}
}

View File

@@ -0,0 +1,61 @@
package org.springframework.cassandra.core.converter;
import java.util.List;
import java.util.Map;
public class ResultSetToStringConverter extends AbstractResultSetConverter<String> {
@Override
protected String doConvertSingleValue(Object object) {
return object == null ? null : object.toString();
}
@Override
protected String doConvertSingleRow(Map<String, Object> row) {
StringBuilder s = new StringBuilder();
boolean firstEntry = true;
for (Map.Entry<String, Object> entry : row.entrySet()) {
if (firstEntry) {
firstEntry = false;
} else {
s.append(", ");
}
s.append("\"").append(entry.getKey().replaceAll("\"", "\\\"")).append("\"");
s.append(" : ");
s.append("\"").append(entry.getValue().toString().replaceAll("\"", "\\\"")).append("\"");
}
return s.toString();
}
@Override
protected String doConvertResultSet(List<Map<String, Object>> resultSet) {
boolean firstElement = true;
StringBuilder s = new StringBuilder("{ ");
for (Map<String, Object> map : resultSet) {
if (firstElement) {
firstElement = false;
} else {
s.append(", ");
}
s.append(doConvertSingleRow(map));
}
s.append(" }");
return s.toString();
}
@Override
protected Class<?> getType() {
return String.class;
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.cassandra.core.converter;
import java.util.UUID;
public class ResultSetToUuidConverter extends AbstractResultSetToBasicFixedTypeConverter<UUID> {
@Override
protected UUID doConvertSingleValue(Object object) {
return CONVERTER.convert(object, UUID.class);
}
@Override
protected Class<?> getType() {
return UUID.class;
}
}

View File

@@ -0,0 +1,19 @@
package org.springframework.cassandra.core.converter;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
import com.datastax.driver.core.Row;
public class RowToArrayConverter implements Converter<Row, Object[]> {
protected RowToListConverter delegate = new RowToListConverter();
@Override
public Object[] convert(Row row) {
List<Object> list = delegate.convert(row);
return list == null ? null : list.toArray();
}
}

View File

@@ -0,0 +1,31 @@
package org.springframework.cassandra.core.converter;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.ColumnDefinitions.Definition;
public class RowToListConverter implements Converter<Row, List<Object>> {
@Override
public List<Object> convert(Row row) {
if (row == null) {
return null;
}
ColumnDefinitions cols = row.getColumnDefinitions();
List<Object> list = new ArrayList<Object>(cols.size());
for (Definition def : cols.asList()) {
String name = def.getName();
list.add(row.isNull(name) ? null : def.getType().deserialize(row.getBytesUnsafe(name)));
}
return list;
}
}

View File

@@ -0,0 +1,32 @@
package org.springframework.cassandra.core.converter;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.convert.converter.Converter;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.ColumnDefinitions.Definition;
import com.datastax.driver.core.Row;
public class RowToMapConverter implements Converter<Row, Map<String, Object>> {
@Override
public Map<String, Object> convert(Row row) {
if (row == null) {
return null;
}
ColumnDefinitions cols = row.getColumnDefinitions();
Map<String, Object> map = new HashMap<String, Object>(cols.size());
for (Definition def : cols.asList()) {
String name = def.getName();
map.put(name, row.isNull(name) ? null : def.getType().deserialize(row.getBytesUnsafe(name)));
}
return map;
}
}

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.data.cassandra.convert;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
@@ -25,7 +23,6 @@ import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.util.Assert;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Row;
/**
@@ -37,9 +34,7 @@ import com.datastax.driver.core.Row;
*/
public class BasicCassandraRowValueProvider implements CassandraRowValueProvider {
private static Logger log = LoggerFactory.getLogger(BasicCassandraRowValueProvider.class);
private final Row source;
private final ColumnReader reader;
private final SpELExpressionEvaluator evaluator;
/**
@@ -53,88 +48,24 @@ public class BasicCassandraRowValueProvider implements CassandraRowValueProvider
Assert.notNull(source);
Assert.notNull(evaluator);
this.source = source;
this.reader = new ColumnReader(source);
this.evaluator = evaluator;
}
@Override
@SuppressWarnings("unchecked")
public <T> T getPropertyValue(CassandraPersistentProperty property) {
public Object getPropertyValue(CassandraPersistentProperty property) {
String expression = property.getSpelExpression();
if (expression != null) {
return evaluator.evaluate(expression);
}
String columnName = property.getColumnName().toCql();
if (source.isNull(columnName)) {
return null;
}
DataType columnType = source.getColumnDefinitions().getType(columnName);
/*
* Handle the types of collections that are available
*/
if (columnType.isCollection()) {
List<DataType> collectionTypes = columnType.getTypeArguments();
if (collectionTypes.size() == 2) {
return (T) source
.getMap(columnName, collectionTypes.get(0).asJavaClass(), collectionTypes.get(1).asJavaClass());
}
if (columnType.equals(DataType.list(collectionTypes.get(0)))) {
return (T) source.getList(columnName, collectionTypes.get(0).asJavaClass());
}
if (columnType.equals(DataType.set(collectionTypes.get(0)))) {
return (T) source.getSet(columnName, collectionTypes.get(0).asJavaClass());
}
throw new IllegalStateException("Unknown Collection type encountered. Valid collections are Set, List and Map.");
}
if (columnType.equals(DataType.text()) || columnType.equals(DataType.ascii())
|| columnType.equals(DataType.varchar())) {
return (T) source.getString(columnName);
}
if (columnType.equals(DataType.cint()) || columnType.equals(DataType.varint())) {
return (T) new Integer(source.getInt(columnName));
}
if (columnType.equals(DataType.cdouble())) {
return (T) new Double(source.getDouble(columnName));
}
if (columnType.equals(DataType.bigint()) || columnType.equals(DataType.counter())) {
return (T) new Long(source.getLong(columnName));
}
if (columnType.equals(DataType.cfloat())) {
return (T) new Float(source.getFloat(columnName));
}
if (columnType.equals(DataType.decimal())) {
return (T) source.getDecimal(columnName);
}
if (columnType.equals(DataType.cboolean())) {
return (T) new Boolean(source.getBool(columnName));
}
if (columnType.equals(DataType.timestamp())) {
return (T) source.getDate(columnName);
}
if (columnType.equals(DataType.blob())) {
return (T) source.getBytes(columnName);
}
if (columnType.equals(DataType.inet())) {
return (T) source.getInet(columnName);
}
if (columnType.equals(DataType.uuid()) || columnType.equals(DataType.timeuuid())) {
return (T) source.getUUID(columnName);
}
return (T) source.getBytes(columnName);
return reader.get(property.getColumnName());
}
@Override
public Row getRow() {
return source;
return reader.getRow();
}
}

View File

@@ -0,0 +1,142 @@
package org.springframework.data.cassandra.convert;
import java.util.List;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Row;
/**
* Helpful class to read a column's value from a row, with possible type conversion.
*
* @author Matthew T. Adams
*/
public class ColumnReader {
protected Row row;
protected ColumnDefinitions columns;
public ColumnReader(Row row) {
this.row = row;
this.columns = row.getColumnDefinitions();
}
/**
* Returns the row's column value.
*/
public Object get(CqlIdentifier name) {
return get(name.toCql());
}
/**
* Returns the row's column value.
*/
public Object get(String name) {
return get(columns.getIndexOf(name));
}
public Object get(int i) {
if (row.isNull(i)) {
return null;
}
DataType type = columns.getType(i);
if (type.isCollection()) {
List<DataType> collectionTypes = type.getTypeArguments();
if (collectionTypes.size() == 2) {
return row.getMap(i, collectionTypes.get(0).asJavaClass(), collectionTypes.get(1).asJavaClass());
}
if (type.equals(DataType.list(collectionTypes.get(0)))) {
return row.getList(i, collectionTypes.get(0).asJavaClass());
}
if (type.equals(DataType.set(collectionTypes.get(0)))) {
return row.getSet(i, collectionTypes.get(0).asJavaClass());
}
throw new IllegalStateException("Unknown Collection type encountered. Valid collections are Set, List and Map.");
}
if (type.equals(DataType.text()) || type.equals(DataType.ascii()) || type.equals(DataType.varchar())) {
return row.getString(i);
}
if (type.equals(DataType.cint()) || type.equals(DataType.varint())) {
return new Integer(row.getInt(i));
}
if (type.equals(DataType.cdouble())) {
return new Double(row.getDouble(i));
}
if (type.equals(DataType.bigint()) || type.equals(DataType.counter())) {
return new Long(row.getLong(i));
}
if (type.equals(DataType.cfloat())) {
return new Float(row.getFloat(i));
}
if (type.equals(DataType.decimal())) {
return row.getDecimal(i);
}
if (type.equals(DataType.cboolean())) {
return new Boolean(row.getBool(i));
}
if (type.equals(DataType.timestamp())) {
return row.getDate(i);
}
if (type.equals(DataType.blob())) {
return row.getBytes(i);
}
if (type.equals(DataType.inet())) {
return row.getInet(i);
}
if (type.equals(DataType.uuid()) || type.equals(DataType.timeuuid())) {
return row.getUUID(i);
}
return row.getBytesUnsafe(i);
}
public Row getRow() {
return row;
}
/**
* Returns the row's column value as an instance of the given type.
*
* @throws ClassCastException if the value cannot be converted to the requested type.
*/
public <T> T get(CqlIdentifier name, Class<T> requestedType) {
return get(columns.getIndexOf(name.toCql()), requestedType);
}
/**
* Returns the row's column value as an instance of the given type.
*
* @throws ClassCastException if the value cannot be converted to the requested type.
*/
public <T> T get(String name, Class<T> requestedType) {
return get(columns.getIndexOf(name), requestedType);
}
/**
* Returns the row's column value as an instance of the given type.
*
* @throws ClassCastException if the value cannot be converted to the requested type.
*/
@SuppressWarnings("unchecked")
public <T> T get(int i, Class<T> requestedType) {
Object o = get(i);
if (o == null) {
return null;
}
return (T) o;
}
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.cassandra.convert;
import static org.springframework.data.cassandra.repository.support.BasicMapId.id;
import static org.springframework.data.cassandra.repository.support.BasicMapId.*;
import java.io.Serializable;
import java.util.Map;
@@ -56,6 +56,7 @@ import com.datastax.driver.core.querybuilder.Update;
*
* @author Alex Shvid
* @author Matthew T. Adams
* @author Oliver Gierke
*/
public class MappingCassandraConverter extends AbstractCassandraConverter implements CassandraConverter,
ApplicationContextAware, BeanClassLoaderAware {
@@ -65,7 +66,6 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
protected final CassandraMappingContext mappingContext;
protected ApplicationContext applicationContext;
protected SpELContext spELContext;
protected boolean useFieldAccessOnly = true;
protected ClassLoader beanClassLoader;
@@ -124,7 +124,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
S instance = instantiator.createInstance(entity, parameterProvider);
BeanWrapper<CassandraPersistentEntity<S>, S> wrapper = BeanWrapper.create(instance, conversionService);
BeanWrapper<S> wrapper = BeanWrapper.create(instance, conversionService);
readPropertiesFromRow(entity, rowValueProvider, wrapper);
@@ -132,7 +132,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
protected void readPropertiesFromRow(final CassandraPersistentEntity<?> entity,
final BasicCassandraRowValueProvider row, final BeanWrapper<?, ?> wrapper) {
final BasicCassandraRowValueProvider row, final BeanWrapper<?> wrapper) {
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@@ -145,7 +145,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
protected void readPropertyFromRow(final CassandraPersistentEntity<?> entity, final CassandraPersistentProperty prop,
final BasicCassandraRowValueProvider row, final BeanWrapper<?, ?> wrapper) {
final BasicCassandraRowValueProvider row, final BeanWrapper<?> wrapper) {
if (entity.isConstructorArgument(prop)) { // skip 'cause prop was set in ctor
return;
@@ -161,14 +161,13 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
// wrap the key
@SuppressWarnings("rawtypes")
BeanWrapper keyWrapper = BeanWrapper.create(key, conversionService);
BeanWrapper<Object> keyWrapper = BeanWrapper.create(key, conversionService);
// now recurse on using the key this time
readPropertiesFromRow(prop.getCompositePrimaryKeyEntity(), row, keyWrapper);
// now that the key's properties have been populated, set the key property on the entity
wrapper.setProperty(keyProperty, keyWrapper.getBean(), useFieldAccessOnly);
wrapper.setProperty(keyProperty, keyWrapper.getBean());
return;
}
@@ -177,7 +176,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
Object obj = row.getPropertyValue(prop);
wrapper.setProperty(prop, obj, useFieldAccessOnly);
wrapper.setProperty(prop, obj);
}
protected Object instantiatePrimaryKey(CassandraPersistentEntity<?> entity, CassandraPersistentProperty keyProperty,
@@ -189,14 +188,6 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
propertyProvider, null));
}
public boolean getUseFieldAccessOnly() {
return useFieldAccessOnly;
}
public void setUseFieldAccessOnly(boolean useFieldAccessOnly) {
this.useFieldAccessOnly = useFieldAccessOnly;
}
@Override
public <R> R read(Class<R> type, Object row) {
if (row instanceof Row) {
@@ -231,23 +222,21 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
protected void writeInsertFromObject(final Object object, final Insert insert, CassandraPersistentEntity<?> entity) {
writeInsertFromWrapper(BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(object, conversionService),
insert, entity);
writeInsertFromWrapper(BeanWrapper.create(object, conversionService), insert, entity);
}
protected void writeInsertFromWrapper(final BeanWrapper<CassandraPersistentEntity<Object>, Object> wrapper,
final Insert insert, CassandraPersistentEntity<?> entity) {
protected void writeInsertFromWrapper(final BeanWrapper<Object> wrapper, final Insert insert,
CassandraPersistentEntity<?> entity) {
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
Object value = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly);
Object value = wrapper.getProperty(prop, prop.getType());
if (prop.isCompositePrimaryKey()) {
writeInsertFromWrapper(
BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(value, conversionService), insert,
writeInsertFromWrapper(BeanWrapper.create(value, conversionService), insert,
prop.getCompositePrimaryKeyEntity());
return;
}
@@ -260,23 +249,21 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
protected void writeUpdateFromObject(final Object object, final Update update, CassandraPersistentEntity<?> entity) {
writeUpdateFromWrapper(BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(object, conversionService),
update, entity);
writeUpdateFromWrapper(BeanWrapper.create(object, conversionService), update, entity);
}
protected void writeUpdateFromWrapper(final BeanWrapper<CassandraPersistentEntity<Object>, Object> wrapper,
final Update update, final CassandraPersistentEntity<?> entity) {
protected void writeUpdateFromWrapper(final BeanWrapper<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);
Object value = wrapper.getProperty(prop, prop.getType());
if (prop.isCompositePrimaryKey()) {
writeUpdateFromWrapper(
BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(value, conversionService), update,
writeUpdateFromWrapper(BeanWrapper.create(value, conversionService), update,
prop.getCompositePrimaryKeyEntity());
return;
}
@@ -293,12 +280,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
protected void writeDeleteWhereFromObject(final Object object, final Where where, CassandraPersistentEntity<?> entity) {
writeDeleteWhereFromWrapper(
BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(object, conversionService), where, entity);
writeDeleteWhereFromWrapper(BeanWrapper.create(object, conversionService), where, entity);
}
protected void writeDeleteWhereFromWrapper(final BeanWrapper<CassandraPersistentEntity<Object>, Object> wrapper,
final Where where, CassandraPersistentEntity<?> entity) {
protected void writeDeleteWhereFromWrapper(final BeanWrapper<Object> wrapper, final Where where,
CassandraPersistentEntity<?> entity) {
Object id = getId(wrapper, entity);
if (id == null) {
@@ -319,8 +305,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
if (idProperty != null) {
if (idProperty.isCompositePrimaryKey()) {
writeDeleteWhereFromWrapper(
BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(id, conversionService), where,
writeDeleteWhereFromWrapper(BeanWrapper.create(id, conversionService), where,
idProperty.getCompositePrimaryKeyEntity());
return;
}
@@ -335,8 +320,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
Assert.notNull(object);
final BeanWrapper<?, ?> wrapper = (object instanceof BeanWrapper) ? (BeanWrapper<?, ?>) object : BeanWrapper
.create(object, conversionService);
final BeanWrapper<?> wrapper = object instanceof BeanWrapper ? (BeanWrapper<?>) object : BeanWrapper.create(object,
conversionService);
object = wrapper == null ? object : wrapper.getBean();
if (!entity.getType().isAssignableFrom(object.getClass())) {
@@ -351,7 +336,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
CassandraPersistentProperty idProperty = entity.getIdProperty();
if (idProperty != null) {
return wrapper.getProperty(entity.getIdProperty(), idProperty.getType(), useFieldAccessOnly);
return wrapper.getProperty(entity.getIdProperty(), idProperty.getType());
}
// if the class doesn't have an id property, then it's using MapId
@@ -361,7 +346,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
@Override
public void doWithPersistentProperty(CassandraPersistentProperty p) {
if (p.isPrimaryKeyColumn()) {
id.with(p.getName(), (Serializable) wrapper.getProperty(p, p.getType(), useFieldAccessOnly));
id.with(p.getName(), (Serializable) wrapper.getProperty(p, p.getType()));
}
}
});

View File

@@ -62,6 +62,8 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
String cql = new CreateTableCqlGenerator(getCassandraMappingContext().getCreateTableSpecificationFor(entity))
.toCql();
log.debug(cql);
s.execute(cql);
return null;
}

View File

@@ -55,20 +55,18 @@ import com.datastax.driver.core.querybuilder.Update;
* @author Alex Shvid
* @author David Webb
* @author Matthew T. Adams
*
* @author Oliver Gierke
* @see CqlTemplate
*/
public class CassandraTemplate extends CqlTemplate implements CassandraOperations {
protected CassandraConverter cassandraConverter;
protected CassandraMappingContext mappingContext;
protected boolean useFieldAccessOnly = false;
/**
* Default Constructor for wiring in the required components later
*/
public CassandraTemplate() {
}
public CassandraTemplate() {}
/**
* Constructor if only session and converter are known at time of Template Creation
@@ -98,19 +96,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
return mappingContext;
}
public boolean getUseFieldAccessOnly() {
return useFieldAccessOnly;
}
/**
* Whether only fields should be used when accessing a persistent entity's data.
*
* @param useFieldAccessOnly
*/
public void setUseFieldAccessOnly(boolean useFieldAccessOnly) {
this.useFieldAccessOnly = useFieldAccessOnly;
}
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
@@ -313,8 +298,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
CassandraPersistentEntity<?> idEntity = idProperty.getCompositePrimaryKeyEntity();
final BeanWrapper<CassandraPersistentEntity<Object>, Object> idWrapper = BeanWrapper
.<CassandraPersistentEntity<Object>, Object> create(id, cassandraConverter.getConversionService());
final BeanWrapper<Object> idWrapper = BeanWrapper.create(id, cassandraConverter.getConversionService());
idEntity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@@ -322,7 +306,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
public void doWithPersistentProperty(CassandraPersistentProperty p) {
clauseCallback.doWithClause(QueryBuilder.eq(p.getColumnName().toCql(),
idWrapper.getProperty(p, p.getActualType(), useFieldAccessOnly)));
idWrapper.getProperty(p, p.getActualType())));
}
});
@@ -595,7 +579,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param objectToSave
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
*/
public static Insert createInsertQuery(String tableName, Object objectToSave, QueryOptions options,
@@ -623,7 +606,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param objectToSave
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
*/
public static Update toUpdateQuery(String tableName, Object objectToSave, QueryOptions options,
@@ -651,7 +633,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param objectsToSave
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
*/
public static <T> Batch toUpdateBatchQuery(String tableName, List<T> objectsToSave, QueryOptions options,
@@ -684,7 +665,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param entities
* @param entity
* @param optionsByName
*
* @return The Query object to run with session.execute();
*/
public static <T> Batch createInsertBatchQuery(String tableName, List<T> entities, QueryOptions options,
@@ -731,7 +711,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
* @param entities
* @param entity
* @param optionsByName
*
* @return
*/
public static <T> Batch createDeleteBatchQuery(String tableName, List<T> entities, QueryOptions options,

View File

@@ -42,7 +42,7 @@ public class BasicCassandraPersistentEntityMetadataVerifier implements Cassandra
@Override
public void verify(CassandraPersistentEntity<?> entity) throws MappingException {
VerifierMappingExceptions exceptions = new VerifierMappingExceptions(
VerifierMappingExceptions exceptions = new VerifierMappingExceptions(entity,
"Mapping Exceptions from BasicCassandraPersistentEntityMetadataVerifier");
final List<CassandraPersistentProperty> idProperties = new ArrayList<CassandraPersistentProperty>();

View File

@@ -20,6 +20,7 @@ import java.util.List;
import org.springframework.cassandra.core.Ordering;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.TypeInformation;
@@ -131,4 +132,14 @@ public interface CassandraPersistentProperty extends PersistentProperty<Cassandr
* @param columnName
*/
void setColumnNames(List<CqlIdentifier> columnNames);
public enum PropertyToFieldNameConverter implements Converter<CassandraPersistentProperty, String> {
INSTANCE;
@Override
public String convert(CassandraPersistentProperty source) {
return source.getColumnName().toCql();
}
}
}

View File

@@ -31,12 +31,14 @@ import org.springframework.data.mapping.model.MappingException;
public class VerifierMappingExceptions extends MappingException {
Collection<MappingException> exceptions = new LinkedList<MappingException>();
private String className;
/**
* @param s
*/
public VerifierMappingExceptions(String s) {
public VerifierMappingExceptions(CassandraPersistentEntity<?> entity, String s) {
super(s);
this.className = entity.getType().getName();
}
/**
@@ -79,7 +81,7 @@ public class VerifierMappingExceptions extends MappingException {
@Override
public String getMessage() {
StringBuilder builder = new StringBuilder();
StringBuilder builder = new StringBuilder(className).append(":\n");
for (MappingException e : exceptions) {
builder.append(e.getMessage()).append("\n");
}

View File

@@ -21,22 +21,25 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.QueryAnnotation;
/**
* Annotation to declare finder queries directly on repository methods.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@QueryAnnotation
public @interface Query {
/**
* Takes a Cassandra CQL3 string to define the actual query to be executed.
* A Cassandra CQL3 string to define the actual query to be executed. Placeholders {@code ?0}, {@code ?1}, etc are
* supported.
*
* @return
*/
String value() default "";
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright 2010-2014 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.repository.query;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.core.converter.ResultSetToBigDecimalConverter;
import org.springframework.cassandra.core.converter.ResultSetToBigIntegerConverter;
import org.springframework.cassandra.core.converter.ResultSetToBooleanConverter;
import org.springframework.cassandra.core.converter.ResultSetToByteBufferConverter;
import org.springframework.cassandra.core.converter.ResultSetToDateConverter;
import org.springframework.cassandra.core.converter.ResultSetToDoubleConverter;
import org.springframework.cassandra.core.converter.ResultSetToFloatConverter;
import org.springframework.cassandra.core.converter.ResultSetToInetAddressConverter;
import org.springframework.cassandra.core.converter.ResultSetToIntegerConverter;
import org.springframework.cassandra.core.converter.ResultSetToListConverter;
import org.springframework.cassandra.core.converter.ResultSetToLongConverter;
import org.springframework.cassandra.core.converter.ResultSetToStringConverter;
import org.springframework.cassandra.core.converter.ResultSetToUuidConverter;
import org.springframework.cassandra.core.converter.RowToMapConverter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
/**
* Base class for {@link RepositoryQuery} implementations for Cassandra.
*/
public abstract class AbstractCassandraQuery implements RepositoryQuery {
protected static final Converter<?, ?>[] DEFAULT_CONVERTERS = new Converter<?, ?>[] { new ResultSetToListConverter(),
new ResultSetToStringConverter(), new RowToMapConverter(), new ResultSetToBigDecimalConverter(),
new ResultSetToBigIntegerConverter(), new ResultSetToBooleanConverter(), new ResultSetToByteBufferConverter(),
new ResultSetToDateConverter(), new ResultSetToDoubleConverter(), new ResultSetToFloatConverter(),
new ResultSetToInetAddressConverter(), new ResultSetToIntegerConverter(), new ResultSetToLongConverter(),
new ResultSetToUuidConverter() };
protected static Logger log = LoggerFactory.getLogger(AbstractCassandraQuery.class);
private ConversionService conversionService;
Converter<ResultSet, List<Map<String, Object>>> resultSetToListConverter = new ResultSetToListConverter();
private final CassandraQueryMethod method;
private final CassandraOperations template;
protected RowToMapConverter rowToMapConverter = new RowToMapConverter();
/**
* Creates a new {@link AbstractCassandraQuery} from the given {@link CassandraQueryMethod} and
* {@link CassandraOperations}.
*
* @param method must not be {@literal null}.
* @param template must not be {@literal null}.
*/
public AbstractCassandraQuery(CassandraQueryMethod method, CassandraOperations operations) {
Assert.notNull(operations);
Assert.notNull(method);
this.method = method;
this.template = operations;
this.conversionService = createDefaultConversionService();
}
protected ConfigurableConversionService createDefaultConversionService() {
ConfigurableConversionService conversionService = new DefaultConversionService();
for (Converter<?, ?> converter : DEFAULT_CONVERTERS) {
conversionService.addConverter(converter);
}
return conversionService;
}
@Override
public CassandraQueryMethod getQueryMethod() {
return method;
}
@Override
public Object execute(Object[] parameters) {
CassandraParameterAccessor accessor = new CassandraParametersParameterAccessor(method, parameters);
String query = createQuery(accessor);
ResultSet resultSet = template.query(query);
// return raw result set if requested
if (method.isResultSetQuery()) {
return resultSet;
}
Class<?> declaredReturnType = method.getReturnType().getType();
Class<?> returnedUnwrappedObjectType = method.getReturnedObjectType();
if (method.isSingleEntityQuery()) {
return getSingleEntity(resultSet, returnedUnwrappedObjectType);
}
Object retval = resultSet;
if (method.isCollectionOfEntityQuery()) {
retval = getCollectionOfEntity(resultSet, declaredReturnType, returnedUnwrappedObjectType);
}
// TODO: support Page & Slice queries
// if we get this far, let the configured conversion service try to convert the result set
return conversionService.convert(retval, TypeDescriptor.forObject(retval),
TypeDescriptor.valueOf(declaredReturnType));
}
public Object getCollectionOfEntity(ResultSet resultSet, Class<?> declaredReturnType,
Class<?> returnedUnwrappedObjectType) {
Collection<Object> results = null;
if (ClassUtils.isAssignable(SortedSet.class, declaredReturnType)) {
results = new TreeSet<Object>();
} else if (ClassUtils.isAssignable(Set.class, declaredReturnType)) {
results = new HashSet<Object>();
} else { // List.class, Collection.class, or array
results = new ArrayList<Object>();
}
CassandraConverter converter = template.getConverter();
for (Row row : resultSet) {
results.add(converter.read(returnedUnwrappedObjectType, row));
}
return results;
}
public Object getSingleEntity(ResultSet resultSet, Class<?> type) {
if (resultSet.isExhausted()) {
return null;
}
Iterator<Row> iterator = resultSet.iterator();
Object object = template.getConverter().read(type, iterator.next());
warnIfMoreResults(iterator);
return object;
}
protected void warnIfMoreResults(Iterator<Row> iterator) {
if (log.isWarnEnabled() && iterator.hasNext()) {
int i = 0;
while (iterator.hasNext()) {
iterator.next();
i++;
}
log.warn("ignoring extra {} row{}", i, i == 1 ? "" : "s");
}
}
public ConversionService getConversionService() {
return conversionService;
}
public void setConversionService(ConversionService conversionService) {
Assert.notNull(conversionService);
this.conversionService = conversionService;
}
/**
* Creates a string query using the given {@link ParameterAccessor}
*
* @param accessor must not be {@literal null}.
*/
protected abstract String createQuery(CassandraParameterAccessor accessor);
}

View File

@@ -0,0 +1,6 @@
package org.springframework.data.cassandra.repository.query;
import org.springframework.data.repository.query.ParameterAccessor;
public interface CassandraParameterAccessor extends ParameterAccessor {
}

View File

@@ -0,0 +1,37 @@
package org.springframework.data.cassandra.repository.query;
import java.lang.reflect.Method;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
public class CassandraParameters extends Parameters<CassandraParameters, Parameter> {
public CassandraParameters(List<Parameter> originals) {
super(originals);
}
public CassandraParameters(Method method) {
super(method);
}
@Override
protected CassandraParameter createParameter(MethodParameter parameter) {
return new CassandraParameter(parameter);
}
@Override
protected CassandraParameters createFrom(List<Parameter> parameters) {
return new CassandraParameters(parameters);
}
class CassandraParameter extends Parameter {
protected CassandraParameter(MethodParameter parameter) {
super(parameter);
}
}
}

View File

@@ -0,0 +1,17 @@
package org.springframework.data.cassandra.repository.query;
import org.springframework.data.repository.query.ParametersParameterAccessor;
public class CassandraParametersParameterAccessor extends ParametersParameterAccessor implements
CassandraParameterAccessor {
/**
* Creates a new {@link CassandraParametersParameterAccessor}.
*
* @param method must not be {@literal null}.
* @param values must not be {@@iteral null}.
*/
public CassandraParametersParameterAccessor(CassandraQueryMethod method, Object... values) {
super(method.getParameters(), values);
}
}

View File

@@ -0,0 +1,220 @@
/*
* Copyright 2010-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.repository.query;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.Assert;
import com.datastax.driver.core.querybuilder.Clause;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
/**
* Custom query creator to create Cassandra criteria.
*/
class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
private static final Logger LOG = LoggerFactory.getLogger(CassandraQueryCreator.class);
private final CassandraParameterAccessor accessor;
private final CassandraMappingContext context;
/**
* Creates a new {@link CassandraQueryCreator} from the given {@link PartTree}, {@link ConvertingParameterAccessor}
* and {@link MappingContext}.
*
* @param tree
* @param accessor
* @param context
*/
public CassandraQueryCreator(PartTree tree, CassandraParameterAccessor accessor, CassandraMappingContext context) {
super(tree, accessor);
Assert.notNull(context);
this.accessor = accessor;
this.context = context;
}
@Override
protected Clause create(Part part, Iterator<Object> iterator) {
PersistentPropertyPath<CassandraPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
CassandraPersistentProperty property = path.getLeafProperty();
Clause criteria = from(part, property,
null /* TODO where(path.toDotPath(CassandraPersistentProperty.PropertyToFieldNameConverter.INSTANCE))*/,
iterator);
return criteria;
}
@Override
protected Clause and(Part part, Clause base, Iterator<Object> iterator) {
if (base == null) {
return create(part, iterator);
}
PersistentPropertyPath<CassandraPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
CassandraPersistentProperty property = path.getLeafProperty();
return from(part, property,
null /* TODO base.and(path.toDotPath(CassandraPersistentProperty.PropertyToFieldNameConverter.INSTANCE))*/,
iterator);
}
@Override
protected Clause or(Clause base, Clause criteria) {
throw new InvalidDataAccessApiUsageException(String.format("Cassandra does not support an OR operator!"));
}
@Override
protected Select complete(Clause criteria, Sort sort) {
if (criteria == null) {
return null;
}
Select select = QueryBuilder.select().all().from("TODO");
select.where(criteria);
if (LOG.isDebugEnabled()) {
LOG.debug("Created query " + select.getQueryString());
}
return select;
}
private Clause from(Part part, CassandraPersistentProperty property, Clause criteria, Iterator<Object> parameters) {
Type type = part.getType();
switch (type) {
// TODO
// case AFTER:
// case GREATER_THAN:
// return criteria.gt(parameters.nextConverted(property));
// case GREATER_THAN_EQUAL:
// return criteria.gte(parameters.nextConverted(property));
// case BEFORE:
// case LESS_THAN:
// return criteria.lt(parameters.nextConverted(property));
// case LESS_THAN_EQUAL:
// return criteria.lte(parameters.nextConverted(property));
// case BETWEEN:
// return criteria.gt(parameters.nextConverted(property)).lt(parameters.nextConverted(property));
// case IS_NOT_NULL:
// return criteria.ne(null);
// case IS_NULL:
// return criteria.is(null);
// case NOT_IN:
// return criteria.nin(nextAsArray(parameters, property));
// case IN:
// return criteria.in(nextAsArray(parameters, property));
// case LIKE:
// case STARTING_WITH:
// case ENDING_WITH:
// case CONTAINING:
// return addAppropriateLikeRegexTo(criteria, part, parameters.next().toString());
// case REGEX:
// return criteria.regex(parameters.next().toString());
// case EXISTS:
// return criteria.exists((Boolean) parameters.next());
// case TRUE:
// return criteria.is(true);
// case FALSE:
// return criteria.is(false);
// case WITHIN:
//
// Object parameter = parameters.next();
// return criteria.within((Shape) parameter);
// case SIMPLE_PROPERTY:
//
// return isSimpleComparisionPossible(part) ? criteria.is(parameters.nextConverted(property))
// : createLikeRegexCriteriaOrThrow(part, property, criteria, parameters, false);
//
// case NEGATING_SIMPLE_PROPERTY:
//
// return isSimpleComparisionPossible(part) ? criteria.ne(parameters.nextConverted(property))
// : createLikeRegexCriteriaOrThrow(part, property, criteria, parameters, true);
default:
throw new UnsupportedCassandraQueryOperatorException(String.format(""));
}
}
private boolean isSimpleComparisionPossible(Part part) {
switch (part.shouldIgnoreCase()) {
case NEVER:
return true;
case WHEN_POSSIBLE:
return part.getProperty().getType() != String.class;
case ALWAYS:
return false;
default:
return true;
}
}
/**
* Returns the next element from the given {@link Iterator} expecting it to be of a certain type.
*
* @param <T>
* @param iterator
* @param type
* @throws IllegalArgumentException in case the next element in the iterator is not of the given type.
* @return
*/
@SuppressWarnings("unchecked")
private <T> T nextAs(Iterator<Object> iterator, Class<T> type) {
Object parameter = iterator.next();
if (parameter.getClass().isAssignableFrom(type)) {
return (T) parameter;
}
throw new IllegalArgumentException(String.format("Expected parameter type of %s but got %s!", type,
parameter.getClass()));
}
private Object[] nextAsArray(Iterator<Object> iterator, CassandraPersistentProperty property) {
Object next = iterator.next(); // TODO nextConverted(property);
if (next instanceof Collection) {
return ((Collection<?>) next).toArray();
} else if (next.getClass().isArray()) {
return (Object[]) next;
}
return new Object[] { next };
}
}

View File

@@ -0,0 +1,156 @@
package org.springframework.data.cassandra.repository.query;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.repository.Query;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.ResultSet;
public class CassandraQueryMethod extends QueryMethod {
// TODO: double-check this list
public static final List<Class<?>> ALLOWED_PARAMETER_TYPES = Collections.unmodifiableList(Arrays
.asList(new Class<?>[] { String.class, CharSequence.class, char.class, Character.class, char[].class, long.class,
Long.class, boolean.class, Boolean.class, BigDecimal.class, BigInteger.class, double.class, Double.class,
float.class, Float.class, InetAddress.class, Date.class, UUID.class, }));
public static boolean isMapOfCharSequenceToObject(TypeInformation<?> type) {
if (!type.isMap()) {
return false;
}
TypeInformation<?> keyType = type.getComponentType();
TypeInformation<?> valueType = type.getMapValueType();
return ClassUtils.isAssignable(CharSequence.class, keyType.getType()) && Object.class.equals(valueType.getType());
}
protected Method method;
protected CassandraMappingContext mappingContext;
protected Query query;
protected String queryString;
protected boolean queryCached = false;
public CassandraQueryMethod(Method method, RepositoryMetadata metadata, CassandraMappingContext mappingContext) {
super(method, metadata);
verify(method, metadata);
this.method = method;
Assert.notNull(mappingContext, "MappingContext must not be null!");
this.mappingContext = mappingContext;
}
public void verify(Method method, RepositoryMetadata metadata) {
// TODO: support Page & Slice queries
if (isSliceQuery() || isPageQuery()) {
throw new InvalidDataAccessApiUsageException("neither slice nor page queries are supported yet");
}
Set<Class<?>> offendingTypes = new HashSet<Class<?>>();
for (Class<?> type : method.getParameterTypes()) {
if (!ALLOWED_PARAMETER_TYPES.contains(type)) {
offendingTypes.add(type);
}
}
if (offendingTypes.size() > 0) {
throw new IllegalArgumentException(String.format(
"encountered unsupported query parameter type%s [%s] in method %s", offendingTypes.size() == 1 ? "" : "s",
StringUtils.arrayToCommaDelimitedString(new ArrayList<Class<?>>(offendingTypes).toArray()), method));
}
}
@Override
protected CassandraParameters createParameters(Method method) {
return new CassandraParameters(method);
}
/**
* Returns the {@link Query} annotation that is applied to the method or {@code null} if none available.
*/
Query getQueryAnnotation() {
if (query == null) {
query = method.getAnnotation(Query.class);
queryCached = true;
}
return query;
}
/**
* Returns whether the method has an annotated query.
*/
public boolean hasAnnotatedQuery() {
return getAnnotatedQuery() != null;
}
/**
* Returns the query string declared in a {@link Query} annotation or {@literal null} if neither the annotation found
* nor the attribute was specified.
*/
public String getAnnotatedQuery() {
if (!queryCached) {
queryString = (String) AnnotationUtils.getValue(getQueryAnnotation());
queryString = StringUtils.hasText(queryString) ? queryString : null;
}
return queryString;
}
public TypeInformation<?> getReturnType() {
return ClassTypeInformation.fromReturnTypeOf(method);
}
public boolean isResultSetQuery() {
return ResultSet.class.isAssignableFrom(method.getReturnType());
}
public boolean isSingleEntityQuery() {
return ClassUtils.isAssignable(getDomainClass(), method.getReturnType());
}
public boolean isCollectionOfEntityQuery() {
return isQueryForEntity() && isCollectionQuery();
}
public boolean isMapOfCharSequenceToObjectQuery() {
return isMapOfCharSequenceToObject(getReturnType());
}
public boolean isListOfMapOfCharSequenceToObject() {
TypeInformation<?> type = getReturnType();
if (!ClassUtils.isAssignable(List.class, type.getType())) {
return false;
}
return isMapOfCharSequenceToObject(type.getComponentType());
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2002-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.repository.query;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.parser.PartTree;
/**
* {@link RepositoryQuery} implementation for Cassandra.
*
*/
public class PartTreeCassandraQuery extends AbstractCassandraQuery {
private final PartTree tree;
private final CassandraMappingContext context;
/**
* Creates a new {@link PartTreeCassandraQuery} from the given {@link QueryMethod} and {@link CassandraTemplate}.
*
* @param method must not be {@literal null}.
* @param template must not be {@literal null}.
*/
public PartTreeCassandraQuery(CassandraQueryMethod method, CassandraOperations cassandraOperations) {
super(method, cassandraOperations);
this.tree = new PartTree(method.getName(), method.getEntityInformation().getJavaType());
this.context = cassandraOperations.getConverter().getMappingContext();
}
/**
* Return the {@link PartTree} backing the query.
*
* @return the tree
*/
public PartTree getTree() {
return tree;
}
@Override
protected String createQuery(CassandraParameterAccessor accessor) {
CassandraQueryCreator creator = new CassandraQueryCreator(tree, accessor, context);
return creator.createQuery().getQueryString();
}
}

View File

@@ -0,0 +1,50 @@
package org.springframework.data.cassandra.repository.query;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.cassandra.core.CassandraOperations;
public class StringBasedCassandraQuery extends AbstractCassandraQuery {
private static final Pattern PLACEHOLDER = Pattern.compile("\\?(\\d+)");
private static final Logger LOG = LoggerFactory.getLogger(StringBasedCassandraQuery.class);
protected String query;
public StringBasedCassandraQuery(String query, CassandraQueryMethod queryMethod, CassandraOperations operations) {
super(queryMethod, operations);
this.query = query;
}
public StringBasedCassandraQuery(CassandraQueryMethod queryMethod, CassandraOperations operations) {
this(queryMethod.getAnnotatedQuery(), queryMethod, operations);
}
@Override
public String createQuery(CassandraParameterAccessor accessor) {
return replacePlaceholders(query, accessor);
}
private String replacePlaceholders(String input, CassandraParameterAccessor accessor) {
Matcher matcher = PLACEHOLDER.matcher(input);
String result = input;
while (matcher.find()) {
String group = matcher.group();
int index = Integer.parseInt(matcher.group(1));
result = result.replace(group, getParameterWithIndex(accessor, index).toString());
}
return result;
}
private Object getParameterWithIndex(CassandraParameterAccessor accessor, int index) {
return accessor.getBindableValue(index);
}
}

View File

@@ -0,0 +1,17 @@
package org.springframework.data.cassandra.repository.query;
import org.springframework.dao.InvalidDataAccessApiUsageException;
public class UnsupportedCassandraQueryOperatorException extends InvalidDataAccessApiUsageException {
public UnsupportedCassandraQueryOperatorException(String msg) {
super(msg);
// TODO Auto-generated constructor stub
}
public UnsupportedCassandraQueryOperatorException(String msg, Throwable cause) {
super(msg, cause);
// TODO Auto-generated constructor stub
}
}

View File

@@ -16,16 +16,23 @@
package org.springframework.data.cassandra.repository.support;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.core.CassandraTemplate;
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.repository.TypedIdCassandraRepository;
import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.cassandra.repository.query.CassandraQueryMethod;
import org.springframework.data.cassandra.repository.query.StringBasedCassandraQuery;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.util.Assert;
/**
@@ -38,7 +45,7 @@ import org.springframework.util.Assert;
public class CassandraRepositoryFactory extends RepositoryFactorySupport {
private final CassandraTemplate cassandraTemplate;
private final MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
private final CassandraMappingContext mappingContext;
/**
* Creates a new {@link MongoRepositoryFactory} with the given {@link MongoOperations}.
@@ -51,6 +58,9 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
this.cassandraTemplate = cassandraTemplate;
this.mappingContext = cassandraTemplate.getConverter().getMappingContext();
// TODO: remove when supporting declarative query methods
setQueryLookupStrategyKey(QueryLookupStrategy.Key.USE_DECLARED_QUERY);
}
@Override
@@ -82,4 +92,28 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
return new MappingCassandraEntityInformation<T, ID>((CassandraPersistentEntity<T>) entity,
cassandraTemplate.getConverter());
}
@Override
protected QueryLookupStrategy getQueryLookupStrategy(Key key) {
return new CassandraQueryLookupStrategy();
}
private class CassandraQueryLookupStrategy implements QueryLookupStrategy {
@Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
CassandraQueryMethod queryMethod = new CassandraQueryMethod(method, metadata, mappingContext);
String namedQueryName = queryMethod.getNamedQueryName();
if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName);
return new StringBasedCassandraQuery(namedQuery, queryMethod, cassandraTemplate);
} else if (queryMethod.hasAnnotatedQuery()) {
return new StringBasedCassandraQuery(queryMethod, cassandraTemplate);
} else {
throw new InvalidDataAccessApiUsageException("declarative query methods are a todo");
}
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2010-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.test.integration.querymethods.declared;
import java.util.Date;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;
/**
* Sample domain class.
*/
@Table
public class Person {
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0)
private String lastname;
@PrimaryKeyColumn(type = PrimaryKeyType.CLUSTERED, ordinal = 1)
private String firstname;
private String nickname;
private Date birthDate;
private int numberOfChildren;
private boolean cool;
// TODO: private UUID uuid = UUID.randomUUID();
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Date getBirthDate() {
return new Date(birthDate.getTime());
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate == null ? null : new Date(birthDate.getTime());
}
public int getNumberOfChildren() {
return numberOfChildren;
}
public void setNumberOfChildren(int numberOfChildren) {
this.numberOfChildren = numberOfChildren;
}
public boolean isCool() {
return cool;
}
public void setCool(boolean cool) {
this.cool = cool;
}
// public UUID getUuid() {
// return uuid;
// }
//
// public void setUuid(UUID uuid) {
// this.uuid = uuid;
// }
}

View File

@@ -0,0 +1,251 @@
package org.springframework.data.cassandra.test.integration.querymethods.declared;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.test.integration.querymethods.declared.base.PersonRepository;
import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest;
import org.springframework.data.cassandra.test.integration.support.IntegrationTestConfig;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
public abstract class QueryIntegrationTests extends AbstractSpringDataEmbeddedCassandraIntegrationTest {
public static class Config extends IntegrationTestConfig {
@Override
public String[] getEntityBasePackages() {
return new String[] { Person.class.getPackage().getName() };
}
@Override
public SchemaAction getSchemaAction() {
return SchemaAction.RECREATE_DROP_UNUSED;
}
}
@Autowired
PersonRepository r;
@Before
public void before() {
deleteAllEntities();
}
@Test
public void testListMethodSingleResult() {
Person saved = new Person();
saved.setFirstname(uuid());
saved.setLastname(uuid());
saved = r.save(saved);
List<Person> results = r.findFolksWithLastnameAsList(saved.getLastname());
assertNotNull(results);
assertTrue(results.size() == 1);
Person found = results.iterator().next();
assertNotNull(found);
assertEquals(found.getLastname(), saved.getLastname());
assertEquals(found.getFirstname(), saved.getFirstname());
}
@Test
public void testListMethodMultipleResults() {
Person saved = new Person();
saved.setFirstname("a");
saved.setLastname(uuid());
saved = r.save(saved);
Person saved2 = new Person();
saved2.setFirstname("b");
saved2.setLastname(saved.getLastname());
saved2 = r.save(saved2);
List<Person> results = r.findFolksWithLastnameAsList(saved.getLastname());
assertNotNull(results);
assertTrue(results.size() == 2);
boolean first = true;
for (Person person : results) {
assertNotNull(person);
assertEquals(saved.getLastname(), person.getLastname());
assertEquals(first ? saved.getFirstname() : saved2.getFirstname(), person.getFirstname());
first = false;
}
}
@Test
public void testListOfMapOfStringToObjectMethodSingleResult() {
Person saved = new Person();
saved.setFirstname(uuid());
saved.setLastname(uuid());
saved = r.save(saved);
List<Map<String, Object>> results = r.findFolksWithLastnameAsListOfMapOfStringToObject(saved.getLastname());
assertNotNull(results);
assertTrue(results.size() == 1);
Map<String, Object> found = results.iterator().next();
assertNotNull(found);
assertEquals(found.get("lastname"), saved.getLastname());
assertEquals(found.get("firstname"), saved.getFirstname());
}
@Test
public void testEntityMethodResult() {
Person saved = new Person();
saved.setFirstname(uuid());
saved.setLastname(uuid());
saved = r.save(saved);
Person found = r.findSingle(saved.getLastname(), saved.getFirstname());
assertNotNull(found);
assertEquals(found.getLastname(), saved.getLastname());
assertEquals(found.getFirstname(), saved.getFirstname());
}
@Test
public void testListOfMapOfStringToObjectMethodMultipleResults() {
Person saved = new Person();
saved.setFirstname("a");
saved.setLastname(uuid());
saved = r.save(saved);
Person saved2 = new Person();
saved2.setFirstname("b");
saved2.setLastname(saved.getLastname());
saved2 = r.save(saved2);
Collection<Person> results = r.findFolksWithLastnameAsList(saved.getLastname());
assertNotNull(results);
assertTrue(results.size() == 2);
boolean first = true;
for (Person person : results) {
assertNotNull(person);
assertEquals(saved.getLastname(), person.getLastname());
assertEquals(first ? saved.getFirstname() : saved2.getFirstname(), person.getFirstname());
first = false;
}
}
@Test
public void testStringMethodResult() {
Person saved = new Person();
saved.setFirstname(uuid());
saved.setLastname(uuid());
saved.setNickname(uuid());
saved = r.save(saved);
String nickname = r.findSingleNickname(saved.getLastname(), saved.getFirstname());
assertNotNull(nickname);
assertEquals(saved.getNickname(), nickname);
}
@Test
public void testBooleanMethodResult() {
Person saved = new Person();
saved.setFirstname(uuid());
saved.setLastname(uuid());
saved.setCool(true);
saved = r.save(saved);
boolean value = r.findSingleCool(saved.getLastname(), saved.getFirstname());
assertEquals(saved.isCool(), value);
}
@Test
public void testDateMethodResult() {
Person saved = new Person();
saved.setFirstname(uuid());
saved.setLastname(uuid());
saved.setBirthDate(new Date());
saved = r.save(saved);
Date value = r.findSingleBirthdate(saved.getLastname(), saved.getFirstname());
assertEquals(saved.getBirthDate(), value);
}
@Test
public void testIntMethodResult() {
Person saved = new Person();
saved.setFirstname(uuid());
saved.setLastname(uuid());
saved.setNumberOfChildren(1);
saved = r.save(saved);
int value = r.findSingleNumberOfChildren(saved.getLastname(), saved.getFirstname());
assertEquals(saved.getNumberOfChildren(), value);
}
// TODO: @Test
// public void testUuidMethodResult() {
//
// Person saved = new Person();
// saved.setFirstname(uuid());
// saved.setLastname(uuid());
// saved.setUuid(UUID.randomUUID());
//
// saved = r.save(saved);
//
// UUID value = r.findSingleUuid(saved.getLastname(), saved.getFirstname());
//
// assertEquals(saved.getUuid(), value);
// }
@Test
public void testArrayMethodSingleResult() {
Person saved = new Person();
saved.setFirstname(uuid());
saved.setLastname(uuid());
saved = r.save(saved);
Person[] results = r.findFolksWithLastnameAsArray(saved.getLastname());
assertNotNull(results);
assertTrue(results.length == 1);
Person found = results[0];
assertNotNull(found);
assertEquals(found.getLastname(), saved.getLastname());
assertEquals(found.getFirstname(), saved.getFirstname());
}
}

View File

@@ -0,0 +1,109 @@
/*
* 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.test.integration.querymethods.declared;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
import java.lang.reflect.Method;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.repository.Query;
import org.springframework.data.cassandra.repository.query.CassandraParametersParameterAccessor;
import org.springframework.data.cassandra.repository.query.CassandraQueryMethod;
import org.springframework.data.cassandra.repository.query.StringBasedCassandraQuery;
import org.springframework.data.repository.core.RepositoryMetadata;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
/**
* Unit tests for {@link StringBasedCassandraQuery}.
*/
@RunWith(MockitoJUnitRunner.class)
public class StringBasedCassandraQueryIntegrationTests {
@Mock
CassandraOperations operations;
@Mock
RepositoryMetadata metadata;
CassandraConverter converter;
@Before
public void setUp() {
when(operations.getConverter()).thenReturn(converter);
this.converter = new MappingCassandraConverter(new BasicCassandraMappingContext());
}
@Test
public void bindsSimplePropertyCorrectly() throws Exception {
Method method = SampleRepository.class.getMethod("findByLastname", String.class);
CassandraQueryMethod queryMethod = new CassandraQueryMethod(method, metadata, converter.getMappingContext());
StringBasedCassandraQuery cassandraQuery = new StringBasedCassandraQuery(queryMethod, operations);
CassandraParametersParameterAccessor accesor = new CassandraParametersParameterAccessor(queryMethod, "Matthews");
String stringQuery = cassandraQuery.createQuery(accesor);
SimpleStatement actual = new SimpleStatement(stringQuery);
String table = Person.class.getSimpleName().toLowerCase();
Select expected = QueryBuilder.select().all().from(table);
expected.where(QueryBuilder.eq("lastname", "Matthews"));
assertThat(actual.getQueryString(), is(expected.getQueryString()));
}
@Test
public void bindsMultipleParametersCorrectly() throws Exception {
Method method = SampleRepository.class.getMethod("findByLastnameAndFirstname", String.class, String.class);
CassandraQueryMethod queryMethod = new CassandraQueryMethod(method, metadata, converter.getMappingContext());
StringBasedCassandraQuery cassandraQuery = new StringBasedCassandraQuery(queryMethod, operations);
CassandraParametersParameterAccessor accesor = new CassandraParametersParameterAccessor(queryMethod, "Matthews",
"John");
String stringQuery = cassandraQuery.createQuery(accesor);
SimpleStatement actual = new SimpleStatement(stringQuery);
String table = Person.class.getSimpleName().toLowerCase();
Select expected = QueryBuilder.select().all().from(table);
expected.where(QueryBuilder.eq("lastname", "Matthews")).and(QueryBuilder.eq("firstname", "John"));
assertThat(actual.getQueryString(), is(expected.getQueryString()));
}
private interface SampleRepository {
@Query("SELECT * FROM person WHERE lastname='?0';")
Person findByLastname(String lastname);
@Query("SELECT * FROM person WHERE lastname='?0' AND firstname='?1';")
Person findByLastnameAndFirstname(String lastname, String firstname);
}
}

View File

@@ -0,0 +1,54 @@
package org.springframework.data.cassandra.test.integration.querymethods.declared.anno;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.springframework.data.cassandra.repository.Query;
import org.springframework.data.cassandra.test.integration.querymethods.declared.Person;
import org.springframework.data.cassandra.test.integration.querymethods.declared.base.PersonRepository;
import org.springframework.data.cassandra.test.integration.querymethods.declared.named.PersonRepositoryWithNamedQueries;
import com.datastax.driver.core.ResultSet;
/**
* we extend {@link PersonRepositoryWithNamedQueries} here just to keep the test codebase in sync.
*/
public interface PersonRepositoryWithQueryAnnotations extends PersonRepository {
@Override
@Query("select * from person where lastname = '?0'")
List<Person> findFolksWithLastnameAsList(String lastname);
@Override
@Query("select * from person where lastname = '?0'")
ResultSet findFolksWithLastnameAsResultSet(String last);
@Override
@Query("select * from person where lastname = '?0'")
Person[] findFolksWithLastnameAsArray(String lastname);
@Override
@Query("select * from person where lastname = '?0' and firstname = '?1'")
Person findSingle(String last, String first);
@Override
@Query("select * from person where lastname = '?0'")
List<Map<String, Object>> findFolksWithLastnameAsListOfMapOfStringToObject(String last);
@Override
@Query("select nickname from person where lastname = '?0' and firstname = '?1'")
String findSingleNickname(String last, String first);
@Override
@Query("select birthdate from person where lastname = '?0' and firstname = '?1'")
Date findSingleBirthdate(String last, String first);
@Override
@Query("select cool from person where lastname = '?0' and firstname = '?1'")
boolean findSingleCool(String last, String first);
@Override
@Query("select numberofchildren from person where lastname = '?0' and firstname = '?1'")
int findSingleNumberOfChildren(String last, String first);
}

View File

@@ -0,0 +1,15 @@
package org.springframework.data.cassandra.test.integration.querymethods.declared.anno;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
import org.springframework.data.cassandra.test.integration.querymethods.declared.QueryIntegrationTests;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration
public class QueryAnnotationIntegrationTests extends QueryIntegrationTests {
@Configuration
@EnableCassandraRepositories(basePackageClasses = PersonRepositoryWithQueryAnnotations.class)
public static class Config extends QueryIntegrationTests.Config {
}
}

View File

@@ -0,0 +1,33 @@
package org.springframework.data.cassandra.test.integration.querymethods.declared.base;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.test.integration.querymethods.declared.Person;
import org.springframework.data.repository.NoRepositoryBean;
import com.datastax.driver.core.ResultSet;
@NoRepositoryBean
public interface PersonRepository extends CassandraRepository<Person> {
List<Person> findFolksWithLastnameAsList(String lastname);
ResultSet findFolksWithLastnameAsResultSet(String last);
Person[] findFolksWithLastnameAsArray(String lastname);
Person findSingle(String last, String first);
List<Map<String, Object>> findFolksWithLastnameAsListOfMapOfStringToObject(String last);
String findSingleNickname(String last, String first);
Date findSingleBirthdate(String last, String first);
boolean findSingleCool(String last, String first);
int findSingleNumberOfChildren(String last, String first);
}

View File

@@ -0,0 +1,15 @@
package org.springframework.data.cassandra.test.integration.querymethods.declared.named;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
import org.springframework.data.cassandra.test.integration.querymethods.declared.QueryIntegrationTests;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration
public class NamedQueryIntegrationTests extends QueryIntegrationTests {
@Configuration
@EnableCassandraRepositories(basePackageClasses = PersonRepositoryWithNamedQueries.class, namedQueriesLocation = "classpath:META-INF/PersonRepositoryWithNamedQueries.properties")
public static class Config extends QueryIntegrationTests.Config {
}
}

View File

@@ -0,0 +1,6 @@
package org.springframework.data.cassandra.test.integration.querymethods.declared.named;
import org.springframework.data.cassandra.test.integration.querymethods.declared.base.PersonRepository;
public interface PersonRepositoryWithNamedQueries extends PersonRepository {
}

View File

@@ -0,0 +1,5 @@
package org.springframework.data.cassandra.test.integration.querymethods.derived;
// TODO
public class TODO {
}

View File

@@ -21,7 +21,9 @@ import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
* Sample repository managing {@link User} entities.
*
* @author Alex Shvid
*
* @author Matthew T. Adams
*/
public interface UserRepository extends TypedIdCassandraRepository<User, String> {
String findByNamedQuery(String username);
}

View File

@@ -93,6 +93,12 @@ public class UserRepositoryIntegrationTests {
setUp();
}
public void findByNamedQuery() {
String name = repository.findByNamedQuery("bob");
Assert.assertNotNull(name);
Assert.assertEquals("Bob", name);
}
public void findsUserById() throws Exception {
User user = repository.findOne(bob.getUsername());

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2013-2014 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.test.integration.repository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Base class for Java config tests for {@link UserRepository}.
*
* @author Matthew T. Adams
*/
@RunWith(SpringJUnit4ClassRunner.class)
public abstract class UserRepositoryIntegrationTestsDelegator extends
AbstractSpringDataEmbeddedCassandraIntegrationTest {
@Autowired
protected UserRepository repository;
@Autowired
protected CassandraOperations template;
UserRepositoryIntegrationTests tests;
@Before
public void before() {
tests = new UserRepositoryIntegrationTests(repository, template);
tests.before();
}
@Test
public void findByNamedQuery() {
tests.findByNamedQuery();
}
@Test
public void findsUserById() throws Exception {
tests.findsUserById();
}
@Test
public void findsAll() throws Exception {
tests.findsAll();
}
@Test
public void findsAllWithGivenIds() {
tests.findsAllWithGivenIds();
}
@Test
public void deletesUserCorrectly() throws Exception {
tests.deletesUserCorrectly();
}
@Test
public void deletesUserByIdCorrectly() {
tests.deletesUserByIdCorrectly();
}
}

View File

@@ -15,26 +15,18 @@
*/
package org.springframework.data.cassandra.test.integration.repository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest;
import org.springframework.data.cassandra.test.integration.support.IntegrationTestConfig;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Base class for Java config tests for {@link UserRepository}.
* Java config tests for {@link UserRepository}.
*
* @author Matthew T. Adams
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class UserRepositoryJavaConfigIntegrationTests extends AbstractSpringDataEmbeddedCassandraIntegrationTest {
public class UserRepositoryJavaConfigIntegrationTests extends UserRepositoryIntegrationTestsDelegator {
@Configuration
@EnableCassandraRepositories(basePackageClasses = UserRepository.class)
@@ -51,42 +43,4 @@ public class UserRepositoryJavaConfigIntegrationTests extends AbstractSpringData
}
}
@Autowired
protected UserRepository repository;
@Autowired
protected CassandraOperations template;
UserRepositoryIntegrationTests tests;
@Before
public void before() {
tests = new UserRepositoryIntegrationTests(repository, template);
tests.before();
}
@Test
public void findsUserById() throws Exception {
tests.findsUserById();
}
@Test
public void findsAll() throws Exception {
tests.findsAll();
}
@Test
public void findsAllWithGivenIds() {
tests.findsAllWithGivenIds();
}
@Test
public void deletesUserCorrectly() throws Exception {
tests.deletesUserCorrectly();
}
@Test
public void deletesUserByIdCorrectly() {
tests.deletesUserByIdCorrectly();
}
}

View File

@@ -15,60 +15,13 @@
*/
package org.springframework.data.cassandra.test.integration.repository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Base class for xml config tests for {@link UserRepository}.
* xml config tests for {@link UserRepository}.
*
* @author Matthew T. Adams
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class UserRepositoryXmlConfigIntegrationTests extends AbstractSpringDataEmbeddedCassandraIntegrationTest {
@Autowired
protected UserRepository repository;
@Autowired
protected CassandraOperations template;
UserRepositoryIntegrationTests tests;
@Before
public void before() {
tests = new UserRepositoryIntegrationTests(repository, template);
tests.before();
}
@Test
public void findsUserById() throws Exception {
tests.findsUserById();
}
@Test
public void findsAll() throws Exception {
tests.findsAll();
}
@Test
public void findsAllWithGivenIds() {
tests.findsAllWithGivenIds();
}
@Test
public void deletesUserCorrectly() throws Exception {
tests.deletesUserCorrectly();
}
@Test
public void deletesUserByIdCorrectly() {
tests.deletesUserByIdCorrectly();
}
public class UserRepositoryXmlConfigIntegrationTests extends UserRepositoryIntegrationTestsDelegator {
}

View File

@@ -0,0 +1,9 @@
Person.findFolksWithLastnameAsList=select * from person where lastname = '?0'
Person.findFolksWithLastnameAsResultSet=select * from person where lastname = '?0'
Person.findFolksWithLastnameAsArray=select * from person where lastname = '?0'
Person.findSingle=select * from person where lastname = '?0' and firstname = '?1'
Person.findFolksWithLastnameAsListOfMapOfStringToObject=select * from person where lastname = '?0'
Person.findSingleNickname=select nickname from person where lastname = '?0' and firstname = '?1'
Person.findSingleBirthdate=select birthdate from person where lastname = '?0' and firstname = '?1'
Person.findSingleCool=select cool from person where lastname = '?0' and firstname = '?1'
Person.findSingleNumberOfChildren=select numberofchildren from person where lastname = '?0' and firstname = '?1'

View File

@@ -1 +1 @@
User.findByNamedQuery=SELECT firstName FROM table WHERE firstName=?0
User.findByNamedQuery=SELECT firstname FROM users WHERE username='?0'

View File

@@ -16,7 +16,7 @@
entity-base-packages="org.springframework.data.cassandra.test.integration.repository">
<cass:entity
class="org.springframework.data.cassandra.test.integration.repository.User">
<cass:table name="users_x" />
<cass:table name="users" />
</cass:entity>
</cass:mapping>