DATACASS-104 - completed

This commit is contained in:
Matthew Adams
2014-02-26 14:01:38 -06:00
parent 376f0b4ffa
commit bd8c423256
39 changed files with 1011 additions and 148 deletions

View File

@@ -234,8 +234,8 @@ public class CassandraClusterFactoryBean implements FactoryBean<Cluster>, Initia
(CreateKeyspaceSpecification) spec).toCql() : new DropKeyspaceCqlGenerator(
(DropKeyspaceSpecification) spec).toCql();
if (log.isInfoEnabled()) {
log.info("executing CQL [{}]", cql);
if (log.isDebugEnabled()) {
log.debug("executing CQL [{}]", cql);
}
template.execute(cql);
@@ -254,8 +254,8 @@ public class CassandraClusterFactoryBean implements FactoryBean<Cluster>, Initia
for (String script : scripts) {
if (log.isInfoEnabled()) {
log.info("executing raw CQL [{}]", script);
if (log.isDebugEnabled()) {
log.debug("executing raw CQL [{}]", script);
}
template.execute(script);
@@ -366,7 +366,6 @@ public class CassandraClusterFactoryBean implements FactoryBean<Cluster>, Initia
* @param keyspaceSpecifications The keyspaceSpecifications to set.
*/
public void setKeyspaceSpecifications(Set<KeyspaceActionSpecification<?>> keyspaceSpecifications) {
log.info("Setter Called");
this.keyspaceSpecifications = keyspaceSpecifications;
}

View File

@@ -41,8 +41,8 @@ public class MultiLevelSetFlattenerFactoryBean<T> implements FactoryBean<Set<T>>
for (Set<T> topSet : multiLevelSet) {
for (T t : topSet) {
log.info(t.toString());
log.info("Set contains -> " + set.contains(t));
log.debug(t.toString());
log.debug("Set contains -> " + set.contains(t));
set.add(t);
}
}

View File

@@ -1,5 +1,6 @@
package org.springframework.cassandra.core.cql;
import java.io.Serializable;
import java.util.regex.Pattern;
import org.springframework.cassandra.core.ReservedKeyword;
@@ -24,7 +25,9 @@ import com.datastax.driver.core.TableMetadata;
* @author John McPeek
* @author Matthew T. Adams
*/
public final class CqlIdentifier implements Comparable<CqlIdentifier> {
public final class CqlIdentifier implements Comparable<CqlIdentifier>, Serializable {
private static final long serialVersionUID = -974441606330912437L;
public static final String UNQUOTED_REGEX = "(?i)[a-z][\\w]*";
public static final Pattern UNQUOTED = Pattern.compile(UNQUOTED_REGEX);

View File

@@ -16,6 +16,7 @@
package org.springframework.cassandra.test.integration;
import java.io.IOException;
import java.util.UUID;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.thrift.transport.TTransportException;
@@ -36,6 +37,10 @@ import com.datastax.driver.core.Session;
*/
public class AbstractEmbeddedCassandraIntegrationTest {
public static String uuid() {
return UUID.randomUUID().toString();
}
static Logger log = LoggerFactory.getLogger(AbstractEmbeddedCassandraIntegrationTest.class);
protected static String CASSANDRA_CONFIG = "spring-cassandra.yaml";

View File

@@ -10,7 +10,7 @@
</appender>
<logger name="org.springframework" level="info" />
<logger name="org.springframework.cassandra" level="debug" />
<logger name="org.springframework.cassandra" level="info" />
<logger name="com.datastax" level="info" />
<root level="warn">

View File

@@ -31,4 +31,6 @@ public interface CassandraConverter extends
@Override
CassandraMappingContext getMappingContext();
Object getId(Object object, CassandraPersistentEntity<?> entity);
}

View File

@@ -15,6 +15,11 @@
*/
package org.springframework.data.cassandra.convert;
import static org.springframework.data.cassandra.repository.support.BasicMapId.id;
import java.io.Serializable;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
@@ -25,6 +30,8 @@ import org.springframework.core.convert.support.DefaultConversionService;
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.MapId;
import org.springframework.data.cassandra.repository.MapIdentifiable;
import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
@@ -275,7 +282,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
if (value != null) {
if (prop.isIdProperty() || entity.isCompositePrimaryKey()) {
if (prop.isIdProperty() || entity.isCompositePrimaryKey() || prop.isPrimaryKeyColumn()) {
update.where(QueryBuilder.eq(prop.getColumnName().toCql(), value));
} else {
update.with(QueryBuilder.set(prop.getColumnName().toCql(), value));
@@ -293,23 +300,73 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
protected void writeDeleteWhereFromWrapper(final BeanWrapper<CassandraPersistentEntity<Object>, Object> wrapper,
final Where where, CassandraPersistentEntity<?> entity) {
CassandraPersistentProperty idProperty = entity.getIdProperty();
Object idValue = wrapper.getProperty(idProperty, idProperty.getType(), useFieldAccessOnly);
if (idValue == null) {
Object id = getId(wrapper, entity);
if (id == null) {
String msg = String.format("no id value found in object {}", wrapper.getBean());
log.error(msg);
throw new IllegalArgumentException(msg);
}
if (idProperty.isCompositePrimaryKey()) {
writeDeleteWhereFromWrapper(
BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(idValue, conversionService), where,
idProperty.getCompositePrimaryKeyEntity());
if (id instanceof MapId) {
for (Map.Entry<String, Serializable> entry : ((MapId) id).entrySet()) {
where.and(QueryBuilder.eq(entry.getKey(), entry.getValue()));
}
return;
}
where.and(QueryBuilder.eq(idProperty.getColumnName().toCql(), idValue));
CassandraPersistentProperty idProperty = entity.getIdProperty();
if (idProperty != null) {
if (idProperty.isCompositePrimaryKey()) {
writeDeleteWhereFromWrapper(
BeanWrapper.<CassandraPersistentEntity<Object>, Object> create(id, conversionService), where,
idProperty.getCompositePrimaryKeyEntity());
return;
}
where.and(QueryBuilder.eq(idProperty.getColumnName().toCql(), id));
return;
}
}
@Override
public Object getId(Object object, CassandraPersistentEntity<?> entity) {
Assert.notNull(object);
final BeanWrapper<?, ?> wrapper = (object instanceof BeanWrapper) ? (BeanWrapper<?, ?>) object : BeanWrapper
.create(object, conversionService);
object = wrapper == null ? object : wrapper.getBean();
if (!entity.getType().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException(String.format(
"given instance of type [%s] is not of compatible expected type [%s]", object.getClass().getName(), entity
.getType().getName()));
}
if (object instanceof MapIdentifiable) {
return ((MapIdentifiable) object).getMapId();
}
CassandraPersistentProperty idProperty = entity.getIdProperty();
if (idProperty != null) {
return wrapper.getProperty(entity.getIdProperty(), idProperty.getType(), useFieldAccessOnly);
}
// if the class doesn't have an id property, then it's using MapId
final MapId id = id();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty p) {
if (p.isPrimaryKeyColumn()) {
id.with(p.getName(), (Serializable) wrapper.getProperty(p, p.getType(), useFieldAccessOnly));
}
}
});
return id;
}
@SuppressWarnings("unchecked")

View File

@@ -18,6 +18,7 @@ package org.springframework.data.cassandra.core;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.cassandra.core.CqlTemplate;
import org.springframework.cassandra.core.QueryOptions;
@@ -288,8 +289,24 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
void doWithClause(Clause clause);
}
protected void appendIdCriteria(final ClauseCallback clauseCallback, CassandraPersistentEntity<?> entity,
final Map<?, ?> id) {
for (Map.Entry<?, ?> entry : id.entrySet()) {
CassandraPersistentProperty property = entity.getPersistentProperty(entry.getKey().toString());
clauseCallback.doWithClause(QueryBuilder.eq(property.getColumnName().toCql(), entry.getValue()));
}
}
protected void appendIdCriteria(final ClauseCallback clauseCallback, CassandraPersistentEntity<?> entity, Object id) {
if (id instanceof Map<?, ?>) {
appendIdCriteria(clauseCallback, entity, (Map<?, ?>) id);
return;
}
CassandraPersistentProperty idProperty = entity.getIdProperty();
if (idProperty.isCompositePrimaryKey()) {
@@ -418,7 +435,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
*/
protected <T> T selectOne(String query, CassandraConverterRowCallback<T> readRowCallback) {
logger.info(query);
logger.debug(query);
ResultSet resultSet = query(query);
@@ -448,9 +465,8 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Batch b = createDeleteBatchQuery(getTableName(entities.get(0).getClass()).toCql(), entities, options,
cassandraConverter);
logger.info(b.toString());
String query = b.getQueryString();
logger.debug(query);
if (asynchronously) {
executeAsynchronously(query);
@@ -484,7 +500,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
cassandraConverter);
String query = b.getQueryString();
logger.info(query);
logger.debug(query);
if (asychronously) {
executeAsynchronously(query);
@@ -512,7 +528,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
cassandraConverter);
String query = b.getQueryString();
logger.info(query);
logger.debug(query);
if (asychronously) {
executeAsynchronously(query);
@@ -534,9 +550,9 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entity);
Delete delete = createDeleteQuery(getTableName(entity.getClass()).toCql(), entity, options, cassandraConverter);
logger.info(delete.toString());
String query = delete.getQueryString();
logger.debug(query);
if (asynchronously) {
executeAsynchronously(query);
@@ -561,7 +577,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Update q = toUpdateQuery(getTableName(entity.getClass()).toCql(), entity, options, cassandraConverter);
String query = q.getQueryString();
logger.info(query);
logger.debug(query);
if (asychronously) {
executeAsynchronously(query);

View File

@@ -187,8 +187,10 @@ public class DefaultCassandraMappingContext extends
} else {
if (prop.isIdProperty()) {
if (prop.isIdProperty() || prop.isPartitionKeyColumn()) {
spec.partitionKeyColumn(prop.getColumnName(), prop.getDataType());
} else if (prop.isClusterKeyColumn()) {
spec.clusteredKeyColumn(prop.getColumnName(), prop.getDataType());
} else {
spec.column(prop.getColumnName(), prop.getDataType());
}

View File

@@ -22,7 +22,6 @@ import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.MappingException;
@@ -48,6 +47,8 @@ public class DefaultCassandraPersistentEntityMetadataVerifier implements Cassand
final List<CassandraPersistentProperty> idProperties = new ArrayList<CassandraPersistentProperty>();
final List<CassandraPersistentProperty> compositePrimaryKeys = new ArrayList<CassandraPersistentProperty>();
final List<CassandraPersistentProperty> partitionKeyColumns = new ArrayList<CassandraPersistentProperty>();
final List<CassandraPersistentProperty> clusterKeyColumns = new ArrayList<CassandraPersistentProperty>();
final List<CassandraPersistentProperty> primaryKeyColumns = new ArrayList<CassandraPersistentProperty>();
/*
@@ -87,13 +88,20 @@ public class DefaultCassandraPersistentEntityMetadataVerifier implements Cassand
idProperties.add(p);
} else if (p.isCompositePrimaryKey()) {
compositePrimaryKeys.add(p);
} else if (p.isPrimaryKeyColumn()) {
} else if (p.isPartitionKeyColumn()) {
partitionKeyColumns.add(p);
primaryKeyColumns.add(p);
} else if (p.isClusterKeyColumn()) {
clusterKeyColumns.add(p);
primaryKeyColumns.add(p);
}
}
});
final int idPropertyCount = idProperties.size();
final int partitionKeyColumnCount = partitionKeyColumns.size();
final int primaryKeyColumnCount = primaryKeyColumns.size();
/*
* Perform rules verification on PrimaryKeyClass
*/
@@ -102,7 +110,7 @@ public class DefaultCassandraPersistentEntityMetadataVerifier implements Cassand
/*
* Must have at least 1 attribute annotated with @PrimaryKeyColumn
*/
if (primaryKeyColumns.size() == 0) {
if (primaryKeyColumnCount == 0) {
exceptions.add(new MappingException(String.format(
"composite primary key type [%s] has no fields annotated with @%s", entity.getType().getName(),
PrimaryKeyColumn.class.getSimpleName())));
@@ -111,22 +119,15 @@ public class DefaultCassandraPersistentEntityMetadataVerifier implements Cassand
/*
* At least one of the PrimaryKeyColumns must have a type PARTIONED
*/
boolean partitionKeyExists = false;
for (CassandraPersistentProperty p : primaryKeyColumns) {
if (p.getField().getAnnotation(PrimaryKeyColumn.class).type() == PrimaryKeyType.PARTITIONED) {
partitionKeyExists = true;
break;
}
}
if (!partitionKeyExists) {
if (partitionKeyColumnCount == 0) {
exceptions.add(new MappingException(
"At least on of the PrimaryKeyColumn annotation must have a type of PARTITIONED"));
"At least one of the @PrimaryKeyColumn annotation must have a type of PARTITIONED"));
}
/*
* Cannot have any Id or PrimaryKey Annotations
*/
if (idProperties.size() > 0) {
if (idPropertyCount > 0) {
exceptions.add(new MappingException(
"Annotations @Id and @PrimaryKey are invalid for type annotated with @PrimaryKeyClass"));
}
@@ -155,7 +156,7 @@ public class DefaultCassandraPersistentEntityMetadataVerifier implements Cassand
}
/*
* Ensure PrimaryKeyClass overrides "boolean equals(Object)"
* Check that PrimaryKeyClass overrides "boolean equals(Object)"
*/
try {
Method equalsMethod = thisType.getDeclaredMethod("equals", Object.class);
@@ -163,7 +164,7 @@ public class DefaultCassandraPersistentEntityMetadataVerifier implements Cassand
throw new NoSuchMethodException();
}
} catch (NoSuchMethodException e) {
String message = "@PrimaryKeyClass must override 'boolean equals(Object)' method and use all @PrimaryKeyColumn fields";
String message = "@PrimaryKeyClass should override 'boolean equals(Object)' method and use all @PrimaryKeyColumn fields";
if (strict) {
exceptions.add(new MappingException(message, e));
} else {
@@ -180,14 +181,13 @@ public class DefaultCassandraPersistentEntityMetadataVerifier implements Cassand
throw new NoSuchMethodException();
}
} catch (NoSuchMethodException e) {
String message = "@PrimaryKeyClass must override 'int hashCode()' method and use all @PrimaryKeyColumn fields";
String message = "@PrimaryKeyClass should override 'int hashCode()' method and use all @PrimaryKeyColumn fields";
if (strict) {
exceptions.add(new MappingException(message, e));
} else {
log.warn(message);
}
}
}
/*
@@ -200,23 +200,48 @@ public class DefaultCassandraPersistentEntityMetadataVerifier implements Cassand
*/
/*
* Ensure only one PK
* Ensure only one PK or at least one partitioned PKC and not both PK(s) & PKC(s)
*/
int idPropertyCount = idProperties.size();
if (idPropertyCount != 1) {
exceptions.add(new MappingException(String.format(
"@Table/@Persistent types must have only one @PrimaryKey attribute. Found %s.", idPropertyCount)));
if (primaryKeyColumnCount == 0) {
/*
* Can only have one PK.
*/
if (idPropertyCount != 1) {
exceptions
.add(new MappingException(String.format(
"@Table/@Persistent types must have only one @PrimaryKey attribute, if any. Found %s.",
idPropertyCount)));
throw exceptions;
}
/*
* Ensure that Id is a supported Type. At the point there is only 1.
*/
Class<?> typeClass = idProperties.get(0).getType();
if (!typeClass.isAnnotationPresent(PrimaryKeyClass.class)
&& CassandraSimpleTypeHolder.getDataTypeFor(typeClass) == null) {
exceptions.add(new MappingException(
"Fields annotated with @PrimaryKey must be simple CassandraTypes or @PrimaryKeyClass type"));
}
} else if (idPropertyCount > 0) {
/*
* Then we have both PK(s) & PKC(s)
*/
exceptions
.add(new MappingException(
String
.format(
"@Table/@Persistent types must not define both @PrimaryKeyColumn field%s (found %s) and @PrimaryKey field%s (found %s)",
primaryKeyColumnCount == 1 ? "" : "s", primaryKeyColumnCount, idPropertyCount == 1 ? "" : "s",
idPropertyCount)));
throw exceptions;
}
/*
* Ensure that Id is a supported Type. At the point there is only 1.
*/
Class<?> typeClass = idProperties.get(0).getType();
if (!typeClass.isAnnotationPresent(PrimaryKeyClass.class)
&& CassandraSimpleTypeHolder.getDataTypeFor(typeClass) == null) {
exceptions.add(new MappingException(
"Fields annotated with @PrimaryKey must be simple CassandraTypes or @PrimaryKeyClass type"));
} else {
/*
* We have no PKs & only PKC(s) -- ensure at least one is of type PARTITIONED
*/
if (partitionKeyColumnCount == 0) {
exceptions.add(new MappingException(String
.format("@Table/@Persistent types must define at least one @PrimaryKeyColumn of type PARTITIONED")));
}
}
}

View File

@@ -1,29 +1,38 @@
/*
* 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.repository;
import java.io.Serializable;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.repository.support.BasicMapId;
/**
* Cassandra-specific extension of the {@link CrudRepository} interface.
* Basic Cassandra repository interface.
* <p/>
* This interface uses {@link MapId} for the id type, allowing you to annotate entity fields or properties with
* {@link PrimaryKeyColumn @PrimaryKeyColumn}. For a full discussion of this interface, including the use of custom
* primary key classes, see {@link TypedIdCassandraRepository}.
* <p/>
* Steps to use this interface:
* <ul>
* <li>Define your entity, including a field or property for each column, including those for partition and (optional)
* cluster columns.</li>
* <li>Annotate each partition &amp; cluster field or property with {@link PrimaryKeyColumn @PrimaryKeyColumn}</li>
* <li>Define your repository interface to be a subinterface of this interface, which uses a provided id type,
* {@link MapId} (implemented by {@link BasicMapId}).</li>
* <li>Whenever you need a {@link MapId}, you can use the static factory method {@link BasicMapId#id()} (which is
* convenient if you import statically) and the builder method {@link MapId#with(String, Serializable)} to easily
* construct an id.</li>
* <li>Optionally, entity class authors can have their entities implement {@link MapIdentifiable}, to make it easier and
* quicker for entity clients to get the entity's identity.</li>
* </ul>
*
* @param <T> The type of the persistent entity.
*
* @see TypedIdCassandraRepository
* @see MapId
* @See {@link MapIdentifiable}
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public interface CassandraRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
public interface CassandraRepository<T> extends TypedIdCassandraRepository<T, MapId> {
}

View File

@@ -0,0 +1,22 @@
package org.springframework.data.cassandra.repository;
import java.io.Serializable;
import java.util.Map;
/**
* Interface that represents the id of a persistent entity, where the keys correspond to the entity's JavaBean
* properties.
*
* @author Matthew T. Adams
*/
public interface MapId extends Serializable, Map<String, Serializable> {
/**
* Builder method that adds the value for the named property, then returns <code>this</code>.
*
* @param name The property name containing the value.
* @param value The property value.
* @return <code>this</code>
*/
MapId with(String name, Serializable value);
}

View File

@@ -0,0 +1,16 @@
package org.springframework.data.cassandra.repository;
/**
* Interface that entity classes may choose to implement in order to allow a client of the entity to easily get the
* entity's {@link MapId}.
*
* @author Matthew T. Adams
*/
public interface MapIdentifiable {
/**
* Gets the identity of this instance. Throws {@link IllegalStateException} if this instance does not use
* {@link MapId}.
*/
MapId getMapId();
}

View File

@@ -0,0 +1,68 @@
/*
* 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.repository;
import java.io.Serializable;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.PrimaryKeyClass;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.data.cassandra.repository.support.BasicMapId;
import org.springframework.data.domain.Persistable;
import org.springframework.data.repository.CrudRepository;
/**
* Cassandra-specific extension of the {@link CrudRepository} interface that allows the specification of a type for the
* identity of the {@link Table @Table} (or {@link Persistable @Persistable}) type.
* <p/>
* If a single column comprises the identity of the entity, then you must do one of two things:
* <ul>
* <li>annotate the field or property in your entity with {@link PrimaryKey @PrimaryKey} and declare your repository
* interface to be a subinterface of <em>this</em> interface, specifying the entity type and id type, or</li>
* <li>annotate the field or property in your entity with {@link PrimaryKeyColumn @PrimaryKeyColumn} and declare your
* repository interface to be a subinterface of {@link CassandraRepository}.</li>
* </ul>
* If multiple columns comprise the identity of the entity, then you must employ one of the following two strategies.
* <ul>
* <li><em>Strategy: use an explicit primary key class</em>
* <ul>
* <li>Define a primary key class (annotated with {@link PrimaryKeyClass @PrimaryKeyClass}) that represents your
* entity's identity.</li>
* <li>Define your entity to include a field or property of the type of your primary key class, and annotate that field
* with {@link PrimaryKey @PrimaryKey}.</li>
* <li>Define your repository interface to be a subinterface of <em>this</em> interface, including your entity type and
* your primary key class type.</li>
* </ul>
* <li>
* <em>Strategy: embed identity fields or properties directly in your entity and use {@link CassandraRepository}</em></li>
* <ul>
* <li>Define your entity, including a field or property for each column, including those for partition and (optional)
* cluster columns.</li>
* <li>Annotate each partition &amp; cluster field or property with {@link PrimaryKeyColumn @PrimaryKeyColumn}</li>
* <li>Define your repository interface to be a subinterface of {@link CassandraRepository}, which uses a provided id
* type, {@link MapId} (implemented by {@link BasicMapId}).</li>
* <li>Whenever you need a {@link MapId}, you can use the static factory method {@link BasicMapId#id()} (which is
* convenient if you import statically) and the builder method {@link MapId#with(String, Serializable)} to easily
* construct an id.</li>
* </ul>
* </ul>
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public interface TypedIdCassandraRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
}

View File

@@ -17,7 +17,6 @@ package org.springframework.data.cassandra.repository.query;
import java.io.Serializable;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.repository.core.EntityInformation;
/**
@@ -26,12 +25,7 @@ import org.springframework.data.repository.core.EntityInformation;
* @author Alex Shvid
*
*/
public interface CassandraEntityInformation<T, ID extends Serializable> extends EntityInformation<T, ID> {
public interface CassandraEntityInformation<T, ID extends Serializable> extends EntityInformation<T, ID>,
CassandraEntityMetadata<T> {
/**
* Returns the name of the table the entity shall be persisted to.
*
* @return
*/
CqlIdentifier getTableName();
}

View File

@@ -15,12 +15,14 @@
*/
package org.springframework.data.cassandra.repository.query;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.repository.core.EntityMetadata;
/**
* Extension of {@link EntityMetadata} to additionally expose the table name an entity shall be persisted to.
*
* @author Alex Shvid
* @author Matthew T. Adams
*
* @param <T>
*/
@@ -31,5 +33,5 @@ public interface CassandraEntityMetadata<T> extends EntityMetadata<T> {
*
* @return
*/
String getTableName();
CqlIdentifier getTableName();
}

View File

@@ -0,0 +1,168 @@
package org.springframework.data.cassandra.repository.support;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.repository.MapId;
import org.springframework.util.Assert;
/**
* Simple implementation of {@link MapId}.
* <p/>
* <em>Note:</em> This could be extended in various cool ways, like one that takes a type and validates that the given
* name corresponds to an actual field or bean property on that type. There could also be another one that uses a
* {@link CassandraPersistentEntity} and {@link CassandraPersistentProperty} instead of a String name.
*
* @author Matthew T. Adams
*/
@SuppressWarnings("serial")
public class BasicMapId implements MapId {
/**
* Factory method. Convenient if imported statically.
*
* @return {@link BasicMapId}
*/
public static MapId id() {
return new BasicMapId();
}
/**
* Factory method. Convenient if imported statically.
*
* @return {@link BasicMapId}
*/
public static MapId id(String name, Serializable value) {
return new BasicMapId().with(name, value);
}
/**
* Factory method. Convenient if imported statically.
*
* @return {@link BasicMapId}
*/
public static MapId id(MapId id) {
return new BasicMapId(id);
}
protected Map<String, Serializable> map = new HashMap<String, Serializable>();
public BasicMapId() {
}
public BasicMapId(Map<String, Serializable> map) {
Assert.notNull(map);
map.putAll(map);
}
@Override
public BasicMapId with(String name, Serializable value) {
put(name, value);
return this;
}
@Override
public void clear() {
map.clear();
}
@Override
public boolean containsKey(Object name) {
return map.containsKey(name);
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
@Override
public Set<java.util.Map.Entry<String, Serializable>> entrySet() {
return map.entrySet();
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (!(that instanceof Map)) { // we can be equal to a Map
return false;
}
return map.equals(that);
}
@Override
public Serializable get(Object name) {
return map.get(name);
}
@Override
public int hashCode() {
return map.hashCode();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public Set<String> keySet() {
return map.keySet();
}
@Override
public Serializable put(String name, Serializable value) {
return map.put(name, value);
}
@Override
public void putAll(Map<? extends String, ? extends Serializable> source) {
map.putAll(source);
}
@Override
public Serializable remove(Object name) {
return map.remove(name);
}
@Override
public int size() {
return map.size();
}
@Override
public Collection<Serializable> values() {
return map.values();
}
@Override
public String toString() {
StringBuilder s = new StringBuilder("{ ");
boolean first = true;
for (Map.Entry<String, Serializable> entry : map.entrySet()) {
if (first) {
first = false;
} else {
s.append(", ");
}
s.append(entry.getKey()).append(" : ").append(entry.getValue());
}
return s.append(" }").toString();
}
}

View File

@@ -20,7 +20,7 @@ import java.io.Serializable;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.repository.CassandraRepository;
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.mapping.model.MappingException;
@@ -29,10 +29,10 @@ import org.springframework.data.repository.core.support.RepositoryFactorySupport
import org.springframework.util.Assert;
/**
* Factory to create {@link CassandraRepository} instances.
* Factory to create {@link TypedIdCassandraRepository} instances.
*
* @author Alex Shvid
*
* @author Matthew T. Adams
*/
public class CassandraRepositoryFactory extends RepositoryFactorySupport {
@@ -68,10 +68,6 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
*/
@Override
@SuppressWarnings("unchecked")
public <T, ID extends Serializable> CassandraEntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
@@ -83,6 +79,7 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
domainClass.getName()));
}
return new MappingCassandraEntityInformation<T, ID>((CassandraPersistentEntity<T>) entity);
return new MappingCassandraEntityInformation<T, ID>((CassandraPersistentEntity<T>) entity,
cassandraTemplate.getConverter());
}
}

View File

@@ -18,14 +18,14 @@ package org.springframework.data.cassandra.repository.support;
import java.io.Serializable;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.util.Assert;
/**
* {@link org.springframework.beans.factory.FactoryBean} to create {@link CassandraRepository} instances.
* {@link org.springframework.beans.factory.FactoryBean} to create {@link TypedIdCassandraRepository} instances.
*
* @author Alex Shvid
*

View File

@@ -18,8 +18,10 @@ package org.springframework.data.cassandra.repository.support;
import java.io.Serializable;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.repository.MapId;
import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.repository.core.support.AbstractEntityInformation;
@@ -31,34 +33,25 @@ import org.springframework.util.Assert;
* the {@link CassandraPersistentEntity} if given.
*
* @author Alex Shvid
*
* @author Matthew T. Adams
*/
public class MappingCassandraEntityInformation<T, ID extends Serializable> extends AbstractEntityInformation<T, ID>
implements CassandraEntityInformation<T, ID> {
private final CassandraPersistentEntity<T> entityMetadata;
private final CqlIdentifier customTableName;
private CassandraConverter converter;
/**
* Creates a new {@link MappingCassandraEntityInformation} for the given {@link CassandraPersistentEntity}.
*
* @param entity must not be {@literal null}.
*/
public MappingCassandraEntityInformation(CassandraPersistentEntity<T> entity) {
this(entity, null);
}
public MappingCassandraEntityInformation(CassandraPersistentEntity<T> entity, CassandraConverter converter) {
/**
* Creates a new {@link MappingCassandraEntityInformation} for the given {@link CassandraPersistentEntity} and custom
* table name.
*
* @param entity must not be {@literal null}.
* @param customTableName
*/
public MappingCassandraEntityInformation(CassandraPersistentEntity<T> entity, CqlIdentifier customTableName) {
super(entity.getType());
this.entityMetadata = entity;
this.customTableName = customTableName;
this.converter = converter;
}
@SuppressWarnings("unchecked")
@@ -68,21 +61,21 @@ public class MappingCassandraEntityInformation<T, ID extends Serializable> exten
Assert.notNull(entity);
CassandraPersistentProperty idProperty = entityMetadata.getIdProperty();
if (idProperty == null) {
return null;
if (idProperty != null) {
return (ID) BeanWrapper.create(entity, null).getProperty(idProperty);
}
return (ID) BeanWrapper.create(entity, null).getProperty(idProperty);
return (ID) converter.getId(entity, entityMetadata);
}
@SuppressWarnings("unchecked")
@Override
public Class<ID> getIdType() {
return (Class<ID>) entityMetadata.getIdProperty().getType();
return (Class<ID>) (entityMetadata.getIdProperty() == null ? MapId.class : entityMetadata.getIdProperty().getType());
}
@Override
public CqlIdentifier getTableName() {
return customTableName == null ? entityMetadata.getTableName() : customTableName;
return entityMetadata.getTableName();
}
}

View File

@@ -21,7 +21,7 @@ import java.util.List;
import org.springframework.cassandra.core.util.CollectionUtils;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
import org.springframework.util.Assert;
@@ -33,7 +33,7 @@ import com.datastax.driver.core.querybuilder.Select;
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class SimpleCassandraRepository<T, ID extends Serializable> implements CassandraRepository<T, ID> {
public class SimpleCassandraRepository<T, ID extends Serializable> implements TypedIdCassandraRepository<T, ID> {
protected CassandraOperations template;
protected CassandraEntityInformation<T, ID> entityInformation;

View File

@@ -89,9 +89,9 @@ public class CollectionsRowValueProviderTest extends AbstractSpringDataEmbeddedC
Assert.assertNotNull(b.getCheckOuts());
log.info("Checkouts map data");
log.debug("Checkouts map data");
for (String username : b.getCheckOuts().keySet()) {
log.info(username + " has " + b.getCheckOuts().get(username) + " checkouts of this book.");
log.debug(username + " has " + b.getCheckOuts().get(username) + " checkouts of this book.");
}
Assert.assertEquals(b.getTitle(), "Spring Data Cassandra Guide");
@@ -132,14 +132,14 @@ public class CollectionsRowValueProviderTest extends AbstractSpringDataEmbeddedC
Assert.assertNotNull(b.getReferences());
Assert.assertNotNull(b.getBookmarks());
log.info("Bookmark List<Integer> Data");
log.debug("Bookmark List<Integer> Data");
for (Integer mark : b.getBookmarks()) {
log.info("Bookmark set on page " + mark);
log.debug("Bookmark set on page " + mark);
}
log.info("Reference Set<String> Data");
log.debug("Reference Set<String> Data");
for (String ref : b.getReferences()) {
log.info("Reference -> " + ref);
log.debug("Reference -> " + ref);
}
Assert.assertEquals(b.getTitle(), "Spring Data Cassandra Guide");

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.cassandra.test.integration.composites;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
public interface CommentRepository extends CassandraRepository<Comment, CommentKey> {
public interface CommentRepository extends TypedIdCassandraRepository<Comment, CommentKey> {
}

View File

@@ -1,6 +1,6 @@
package org.springframework.data.cassandra.test.integration.forcequote.compositeprimarykey;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
public interface ExplicitRepository extends CassandraRepository<Explicit, ExplicitKey> {
public interface ExplicitRepository extends TypedIdCassandraRepository<Explicit, ExplicitKey> {
}

View File

@@ -1,6 +1,6 @@
package org.springframework.data.cassandra.test.integration.forcequote.compositeprimarykey;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
public interface ImplicitRepository extends CassandraRepository<Implicit, ImplicitKey> {
public interface ImplicitRepository extends TypedIdCassandraRepository<Implicit, ImplicitKey> {
}

View File

@@ -1,6 +1,6 @@
package org.springframework.data.cassandra.test.integration.forcequote.config;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
public interface ExplicitPropertiesRepository extends CassandraRepository<ExplicitProperties, String> {
public interface ExplicitPropertiesRepository extends TypedIdCassandraRepository<ExplicitProperties, String> {
}

View File

@@ -1,6 +1,6 @@
package org.springframework.data.cassandra.test.integration.forcequote.config;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
public interface ExplicitRepository extends CassandraRepository<Explicit, String> {
public interface ExplicitRepository extends TypedIdCassandraRepository<Explicit, String> {
}

View File

@@ -1,6 +1,6 @@
package org.springframework.data.cassandra.test.integration.forcequote.config;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
public interface ImplicitPropertiesRepository extends CassandraRepository<ImplicitProperties, String> {
public interface ImplicitPropertiesRepository extends TypedIdCassandraRepository<ImplicitProperties, String> {
}

View File

@@ -1,6 +1,6 @@
package org.springframework.data.cassandra.test.integration.forcequote.config;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
public interface ImplicitRepository extends CassandraRepository<Implicit, String> {
public interface ImplicitRepository extends TypedIdCassandraRepository<Implicit, String> {
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.cassandra.test.integration.mapping;
import static org.junit.Assert.*;
import java.io.Serializable;
import org.junit.Before;
@@ -23,6 +25,7 @@ import org.springframework.cassandra.core.Ordering;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.PrimaryKeyClass;
@@ -82,6 +85,36 @@ public class BasicCassandraPersistentEntityVerifierIntegrationTest {
}
@Test(expected = MappingException.class)
public void testNoPartitionKey() {
mappingContext.getPersistentEntity(NoPartitionKey.class);
}
@Test(expected = MappingException.class)
public void testPkAndPkc() {
mappingContext.getPersistentEntity(PkAndPkc.class);
}
@Test
public void testOnePkc() {
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(OnePkc.class);
assertNull(entity.getIdProperty());
}
@Test
public void testMultiPkc() {
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(MultiPkc.class);
assertNull(entity.getIdProperty());
}
static class NonPersistentClass {
@Id
@@ -147,13 +180,44 @@ public class BasicCassandraPersistentEntityVerifierIntegrationTest {
@PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING)
private String color;
}
@Table
@PrimaryKeyClass
static class TooManyAnnotations {
}
@Table
public static class NoPartitionKey {
@PrimaryKeyColumn(ordinal = 0)
String key;
}
@Table
public static class PkAndPkc {
@PrimaryKey
String primaryKey;
@PrimaryKeyColumn(ordinal = 0)
String primaryKeyColumn;
}
@Table
public static class OnePkc {
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0)
String pk;
}
@Table
public static class MultiPkc {
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0)
String pk0;
@PrimaryKeyColumn(ordinal = 1)
String pk1;
}
}

View File

@@ -0,0 +1,56 @@
package org.springframework.data.cassandra.test.integration.mapping.mapid.repo;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;
@Table
public class MultiPkc {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
String key0;
@PrimaryKeyColumn(ordinal = 1)
String key1;
@Column
String value;
/**
* @deprecated for persistence use only
*/
@Deprecated
@SuppressWarnings("unused")
private MultiPkc() {
}
public MultiPkc(String key0, String key1) {
setKey0(key0);
setKey1(key1);
}
public String getKey0() {
return key0;
}
public void setKey0(String key0) {
this.key0 = key0;
}
public String getKey1() {
return key1;
}
public void setKey1(String key1) {
this.key1 = key1;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,6 @@
package org.springframework.data.cassandra.test.integration.mapping.mapid.repo;
import org.springframework.data.cassandra.repository.CassandraRepository;
public interface MultiPkcRepository extends CassandraRepository<MultiPkc> {
}

View File

@@ -0,0 +1,114 @@
package org.springframework.data.cassandra.test.integration.mapping.mapid.repo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.springframework.data.cassandra.repository.support.BasicMapId.id;
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.MapId;
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;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class RepositoryMapIdIntegrationTest extends AbstractSpringDataEmbeddedCassandraIntegrationTest {
@Configuration
@EnableCassandraRepositories(basePackageClasses = RepositoryMapIdIntegrationTest.class)
public static class Config extends IntegrationTestConfig {
@Override
public String[] getEntityBasePackages() {
return new String[] { SinglePkc.class.getPackage().getName() };
}
}
@Autowired
CassandraOperations t;
@Autowired
SinglePkcRepository sr;
@Autowired
MultiPkcRepository mr;
@Before
public void before() {
assertNotNull(t);
assertNotNull(sr);
assertNotNull(mr);
}
@Test
public void testSinglePkc() {
// insert
SinglePkc inserted = new SinglePkc(uuid());
inserted.setValue(uuid());
SinglePkc saved = sr.save(inserted);
assertSame(saved, inserted);
// select
MapId id = id("key", saved.getKey());
SinglePkc selected = sr.findOne(id);
assertNotSame(selected, saved);
assertEquals(saved.getKey(), selected.getKey());
assertEquals(saved.getValue(), selected.getValue());
// update
selected.setValue(uuid());
SinglePkc updated = sr.save(selected);
assertSame(updated, selected);
selected = sr.findOne(id);
assertNotSame(selected, updated);
assertEquals(updated.getValue(), selected.getValue());
// delete
sr.delete(selected);
assertNull(sr.findOne(id));
}
@Test
public void testMultiPkc() {
// insert
MultiPkc inserted = new MultiPkc(uuid(), uuid());
inserted.setValue(uuid());
MultiPkc saved = mr.save(inserted);
assertSame(saved, inserted);
// select
MapId id = id("key0", saved.getKey0()).with("key1", saved.getKey1());
MultiPkc selected = mr.findOne(id);
assertNotSame(selected, saved);
assertEquals(saved.getKey0(), selected.getKey0());
assertEquals(saved.getKey1(), selected.getKey1());
assertEquals(saved.getValue(), selected.getValue());
// update
selected.setValue(uuid());
MultiPkc updated = mr.save(selected);
assertSame(updated, selected);
selected = mr.findOne(id);
assertNotSame(selected, updated);
assertEquals(updated.getValue(), selected.getValue());
// delete
t.delete(selected);
assertNull(mr.findOne(id));
}
}

View File

@@ -0,0 +1,44 @@
package org.springframework.data.cassandra.test.integration.mapping.mapid.repo;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;
@Table
public class SinglePkc {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
String key;
@Column
String value;
/**
* @deprecated for persistence use only
*/
@Deprecated
@SuppressWarnings("unused")
private SinglePkc() {
}
public SinglePkc(String key) {
setKey(key);
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,6 @@
package org.springframework.data.cassandra.test.integration.mapping.mapid.repo;
import org.springframework.data.cassandra.repository.CassandraRepository;
public interface SinglePkcRepository extends CassandraRepository<SinglePkc> {
}

View File

@@ -0,0 +1,195 @@
package org.springframework.data.cassandra.test.integration.mapping.mapid.template;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.springframework.data.cassandra.repository.support.BasicMapId.id;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.data.cassandra.repository.MapId;
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;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CassandraTemplateMapIdIntegrationTest extends AbstractSpringDataEmbeddedCassandraIntegrationTest {
@Configuration
public static class Config extends IntegrationTestConfig {
@Override
public String[] getEntityBasePackages() {
return new String[] { SinglePkc.class.getPackage().getName() };
}
}
@Autowired
CassandraOperations t;
@Before
public void before() {
assertNotNull(t);
}
@Test
public void testSinglePkc() {
// insert
SinglePkc inserted = new SinglePkc(uuid());
inserted.setValue(uuid());
SinglePkc saved = t.insert(inserted);
assertSame(saved, inserted);
// select
MapId id = id("key", saved.getKey());
SinglePkc selected = t.selectOneById(SinglePkc.class, id);
assertNotSame(selected, saved);
assertEquals(saved.getKey(), selected.getKey());
assertEquals(saved.getValue(), selected.getValue());
// update
selected.setValue(uuid());
SinglePkc updated = t.update(selected);
assertSame(updated, selected);
selected = t.selectOneById(SinglePkc.class, id);
assertNotSame(selected, updated);
assertEquals(updated.getValue(), selected.getValue());
// delete
t.delete(selected);
assertNull(t.selectOneById(SinglePkc.class, id));
}
@Table
public static class SinglePkc {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
String key;
@Column
String value;
/**
* @deprecated for persistence use only
*/
@Deprecated
@SuppressWarnings("unused")
private SinglePkc() {
}
public SinglePkc(String key) {
setKey(key);
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
@Test
public void testMultiPkc() {
// insert
MultiPkc inserted = new MultiPkc(uuid(), uuid());
inserted.setValue(uuid());
MultiPkc saved = t.insert(inserted);
assertSame(saved, inserted);
// select
MapId id = id("key0", saved.getKey0()).with("key1", saved.getKey1());
MultiPkc selected = t.selectOneById(MultiPkc.class, id);
assertNotSame(selected, saved);
assertEquals(saved.getKey0(), selected.getKey0());
assertEquals(saved.getKey1(), selected.getKey1());
assertEquals(saved.getValue(), selected.getValue());
// update
selected.setValue(uuid());
MultiPkc updated = t.update(selected);
assertSame(updated, selected);
selected = t.selectOneById(MultiPkc.class, id);
assertNotSame(selected, updated);
assertEquals(updated.getValue(), selected.getValue());
// delete
t.delete(selected);
assertNull(t.selectOneById(MultiPkc.class, id));
}
@Table
public static class MultiPkc {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
String key0;
@PrimaryKeyColumn(ordinal = 1)
String key1;
@Column
String value;
/**
* @deprecated for persistence use only
*/
@Deprecated
@SuppressWarnings("unused")
private MultiPkc() {
}
public MultiPkc(String key0, String key1) {
setKey0(key0);
setKey1(key1);
}
public String getKey0() {
return key0;
}
public void setKey0(String key0) {
this.key0 = key0;
}
public String getKey1() {
return key1;
}
public void setKey1(String key1) {
this.key1 = key1;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.cassandra.test.integration.minimal.config.entities;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
public interface AbsMinRepository extends CassandraRepository<AbsMin, String> {
public interface AbsMinRepository extends TypedIdCassandraRepository<AbsMin, String> {
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.cassandra.test.integration.repository;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.TypedIdCassandraRepository;
/**
* Sample repository managing {@link User} entities.
@@ -23,5 +23,5 @@ import org.springframework.data.cassandra.repository.CassandraRepository;
* @author Alex Shvid
*
*/
public interface UserRepository extends CassandraRepository<User, String> {
public interface UserRepository extends TypedIdCassandraRepository<User, String> {
}

View File

@@ -606,8 +606,8 @@ public class CassandraDataOperationsTest extends AbstractSpringDataEmbeddedCassa
Book b = template.selectOne(select.getQueryString(), Book.class);
log.info("SingleSelect Book Title -> " + b.getTitle());
log.info("SingleSelect Book Author -> " + b.getAuthor());
log.debug("SingleSelect Book Title -> " + b.getTitle());
log.debug("SingleSelect Book Author -> " + b.getAuthor());
Assert.assertEquals(b.getTitle(), "Spring Data Cassandra Guide");
Assert.assertEquals(b.getAuthor(), "Cassandra Guru");
@@ -625,7 +625,7 @@ public class CassandraDataOperationsTest extends AbstractSpringDataEmbeddedCassa
List<Book> bookz = template.select(select.getQueryString(), Book.class);
log.info("Book Count -> " + bookz.size());
log.debug("Book Count -> " + bookz.size());
Assert.assertEquals(bookz.size(), 20);