DATACASS-93 - existing tests pass with addition of forceQuote

This commit is contained in:
Matthew Adams
2014-02-19 11:29:42 -06:00
parent 7e91de62cb
commit 2e29676b7a
33 changed files with 367 additions and 383 deletions

View File

@@ -21,6 +21,7 @@ import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.keyspace.AlterKeyspaceSpecification;
import org.springframework.cassandra.core.keyspace.AlterTableSpecification;
import org.springframework.cassandra.core.keyspace.CreateIndexSpecification;
@@ -808,6 +809,13 @@ public interface CqlOperations {
*/
void ingest(String cql, Object[][] rows, QueryOptions options);
/**
* Delete all rows in the table
*
* @param tableName
*/
void truncate(CqlIdentifier tableName);
/**
* Delete all rows in the table
*
@@ -815,6 +823,14 @@ public interface CqlOperations {
*/
void truncate(String tableName);
/**
* Counts all rows for given table
*
* @param tableName
* @return
*/
long count(CqlIdentifier tableName);
/**
* Counts all rows for given table
*

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.cassandra.core;
import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -27,6 +29,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.cql.generator.AlterKeyspaceCqlGenerator;
import org.springframework.cassandra.core.cql.generator.AlterTableCqlGenerator;
import org.springframework.cassandra.core.cql.generator.CreateIndexCqlGenerator;
@@ -828,7 +831,12 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
@Override
public void truncate(String tableName) throws DataAccessException {
Truncate truncate = QueryBuilder.truncate(tableName);
truncate(cqlId(tableName));
}
@Override
public void truncate(CqlIdentifier tableName) throws DataAccessException {
Truncate truncate = QueryBuilder.truncate(tableName.toCql());
doExecute(truncate.getQueryString(), null);
}
@@ -1016,9 +1024,14 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
});
}
@Override
public long count(CqlIdentifier tableName) {
return selectCount(QueryBuilder.select().countAll().from(tableName.toCql()).getQueryString());
}
@Override
public long count(String tableName) {
return selectCount(QueryBuilder.select().countAll().from(tableName).getQueryString());
return count(cqlId(tableName));
}
protected long selectCount(String countQuery) {

View File

@@ -41,6 +41,15 @@ public final class CqlIdentifier implements Comparable<CqlIdentifier> {
return new CqlIdentifier(identifier);
}
/**
* Factory method for {@link CqlIdentifier}. Convenient if imported statically.
*
* @see #CqlIdentifier(String)
*/
public static CqlIdentifier cqlId(CharSequence identifier, boolean forceQuote) {
return new CqlIdentifier(identifier, forceQuote);
}
/**
* Factory method for a force-quoted {@link CqlIdentifier}. Convenient if imported statically.
*

View File

@@ -19,6 +19,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import com.datastax.driver.core.DataType;
/**
@@ -36,6 +38,22 @@ public class AlterTableSpecification extends TableOptionsSpecification<AlterTabl
return new AlterTableSpecification();
}
/**
* Entry point into the {@link AlterTableSpecification}'s fluent API to alter a table. Convenient if imported
* statically.
*/
public static AlterTableSpecification alterTable(CqlIdentifier tableName) {
return new AlterTableSpecification().name(tableName);
}
/**
* Entry point into the {@link AlterTableSpecification}'s fluent API to alter a table. Convenient if imported
* statically.
*/
public static AlterTableSpecification alterTable(String tableName) {
return new AlterTableSpecification().name(tableName);
}
/**
* The list of column changes.
*/

View File

@@ -38,6 +38,22 @@ public class CreateIndexSpecification extends IndexNameSpecification<CreateIndex
return new CreateIndexSpecification();
}
/**
* Entry point into the {@link CreateIndexSpecification}'s fluent API to create a index. Convenient if imported
* statically.
*/
public static CreateIndexSpecification createIndex(CqlIdentifier name) {
return new CreateIndexSpecification().name(name);
}
/**
* Entry point into the {@link CreateIndexSpecification}'s fluent API to create a index. Convenient if imported
* statically.
*/
public static CreateIndexSpecification createIndex(String name) {
return new CreateIndexSpecification().name(name);
}
private boolean ifNotExists = false;
private boolean custom = false;
private CqlIdentifier tableName;

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.cassandra.core.keyspace;
import org.springframework.cassandra.core.cql.CqlIdentifier;
/**
* Builder class to construct a <code>CREATE TABLE</code> specification.
*
@@ -30,8 +32,29 @@ public class CreateTableSpecification extends TableSpecification<CreateTableSpec
return new CreateTableSpecification();
}
/**
* Entry point into the {@link CreateTableSpecification}'s fluent API to create a table. Convenient if imported
* statically.
*/
public static CreateTableSpecification createTable(CqlIdentifier name) {
return new CreateTableSpecification().name(name);
}
/**
* Entry point into the {@link CreateTableSpecification}'s fluent API to create a table. Convenient if imported
* statically.
*/
public static CreateTableSpecification createTable(String name) {
return new CreateTableSpecification().name(name);
}
private boolean ifNotExists = false;
@Override
public CreateTableSpecification name(CqlIdentifier name) {
return (CreateTableSpecification) super.name(name);
}
/**
* Causes the inclusion of an <code>IF NOT EXISTS</code> clause.
*

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.cassandra.core.keyspace;
import org.springframework.cassandra.core.cql.CqlIdentifier;
/**
* Builder class that supports the construction of <code>DROP TABLE</code> specifications.
*
@@ -47,6 +49,17 @@ public class DropTableSpecification extends TableNameSpecification<DropTableSpec
return new DropTableSpecification();
}
/**
* Entry point into the {@link DropTableSpecification}'s fluent API to drop a table. Convenient if imported
* statically. This static method is shorter than the no-arg form, which would be
* <code>dropTable().name(tableName)</code>.
*
* @param tableName The name of the table to drop.
*/
public static DropTableSpecification dropTable(CqlIdentifier tableName) {
return new DropTableSpecification().name(tableName);
}
/**
* Entry point into the {@link DropTableSpecification}'s fluent API to drop a table. Convenient if imported
* statically. This static method is shorter than the no-arg form, which would be

View File

@@ -25,8 +25,6 @@ import java.util.Map;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.cql.CqlStringUtils;
import com.datastax.driver.core.DataType;
/**
* Abstract builder class to support the construction of table specifications that have table options, that is, those
* options normally specified by <code>WITH ... AND ...</code>.

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.cassandra.config;
import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
import java.util.Collection;
import org.springframework.cassandra.config.CassandraSessionFactoryBean;
@@ -84,7 +86,7 @@ public class CassandraDataSessionFactoryBean extends CassandraSessionFactoryBean
for (TableMetadata table : kmd.getTables()) {
if (dropTables) {
if (dropUnused || mappingContext.usesTable(table)) {
admin.dropTable(table.getName());
admin.dropTable(cqlId(table.getName()));
}
}
}

View File

@@ -95,8 +95,13 @@ public class CassandraMappingContextParser extends AbstractSingleBeanDefinitionP
tableName = null;
}
String forceQuote = table.getAttribute("force-quote");
if (!StringUtils.hasText(forceQuote)) {
forceQuote = "false";
}
// TODO: parse future entity mappings here, like table options
return new EntityMapping(className, tableName);
return new EntityMapping(className, tableName, Boolean.valueOf(forceQuote));
}
}

View File

@@ -17,6 +17,8 @@ package org.springframework.data.cassandra.core;
import java.util.Map;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import com.datastax.driver.core.TableMetadata;
/**
@@ -39,7 +41,7 @@ public interface CassandraAdminOperations extends CassandraOperations {
* @param entityClass The class whose fields determine the columns created.
* @param optionsByName Table options, given by the string option name and the appropriate option value.
*/
void createTable(boolean ifNotExists, String tableName, Class<?> entityClass, Map<String, Object> optionsByName);
void createTable(boolean ifNotExists, CqlIdentifier tableName, Class<?> entityClass, Map<String, Object> optionsByName);
/**
* Add columns to the given table from the given class. If parameter dropRemovedAttributColumns is true, then this
@@ -50,7 +52,7 @@ public interface CassandraAdminOperations extends CassandraOperations {
* @param dropRemovedAttributeColumns Whether to drop columns that exist on the table but that don't have
* corresponding fields in the class. If true, this effectively becomes a synchronziation operation.
*/
void alterTable(String tableName, Class<?> entityClass, boolean dropRemovedAttributeColumns);
void alterTable(CqlIdentifier tableName, Class<?> entityClass, boolean dropRemovedAttributeColumns);
/**
* Drops the existing table with the given name and creates a new one; basically a {@link #dropTable(String)} followed
@@ -60,19 +62,19 @@ public interface CassandraAdminOperations extends CassandraOperations {
* @param entityClass The class whose fields determine the new table's columns.
* @param optionsByName Table options, given by the string option name and the appropriate option value.
*/
void replaceTable(String tableName, Class<?> entityClass, Map<String, Object> optionsByName);
void replaceTable(CqlIdentifier tableName, Class<?> entityClass, Map<String, Object> optionsByName);
/**
* Drops the named table.
*
* @param tableName The name of the table.
*/
void dropTable(String tableName);
void dropTable(CqlIdentifier tableName);
/**
* @param keyspace
* @param tableName
* @return
*/
TableMetadata getTableMetadata(String keyspace, String tableName);
TableMetadata getTableMetadata(String keyspace, CqlIdentifier tableName);
}

View File

@@ -21,6 +21,7 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.core.SessionCallback;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.DropTableSpecification;
import org.springframework.dao.DataAccessException;
@@ -49,7 +50,7 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
}
@Override
public void createTable(boolean ifNotExists, final String tableName, Class<?> entityClass,
public void createTable(boolean ifNotExists, final CqlIdentifier tableName, Class<?> entityClass,
Map<String, Object> optionsByName) {
final CassandraPersistentEntity<?> entity = getCassandraMappingContext().getPersistentEntity(entityClass);
@@ -68,12 +69,12 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
}
@Override
public void alterTable(String tableName, Class<?> entityClass, boolean dropRemovedAttributeColumns) {
public void alterTable(CqlIdentifier tableName, Class<?> entityClass, boolean dropRemovedAttributeColumns) {
throw new UnsupportedOperationException("not yet implemented");
}
@Override
public void replaceTable(String tableName, Class<?> entityClass, Map<String, Object> optionsByName) {
public void replaceTable(CqlIdentifier tableName, Class<?> entityClass, Map<String, Object> optionsByName) {
dropTable(tableName);
createTable(false, tableName, entityClass, optionsByName);
@@ -85,14 +86,14 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
* @param entityClass
* @param tableName
*/
protected void doAlterTable(Class<?> entityClass, String keyspace, String tableName) {
protected void doAlterTable(Class<?> entityClass, String keyspace, CqlIdentifier tableName) {
CassandraPersistentEntity<?> entity = getCassandraMappingContext().getPersistentEntity(entityClass);
Assert.notNull(entity);
final TableMetadata tableMetadata = getTableMetadata(keyspace, tableName);
final List<String> queryList = CqlUtils.alterTable(tableName, entity, tableMetadata);
final List<String> queryList = CqlUtils.alterTable(tableName.toCql(), entity, tableMetadata);
execute(new SessionCallback<Object>() {
@@ -114,7 +115,7 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
}
@Override
public void dropTable(String tableName) {
public void dropTable(CqlIdentifier tableName) {
log.info("Dropping table => " + tableName);
@@ -122,14 +123,14 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
}
@Override
public TableMetadata getTableMetadata(final String keyspace, final String tableName) {
public TableMetadata getTableMetadata(final String keyspace, final CqlIdentifier tableName) {
Assert.notNull(tableName);
return execute(new SessionCallback<TableMetadata>() {
@Override
public TableMetadata doInSession(Session s) {
return s.getCluster().getMetadata().getKeyspace(keyspace).getTable(tableName);
return s.getCluster().getMetadata().getKeyspace(keyspace).getTable(tableName.toCql());
}
});
}

View File

@@ -19,6 +19,7 @@ import java.util.List;
import org.springframework.cassandra.core.CqlOperations;
import org.springframework.cassandra.core.QueryOptions;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.convert.CassandraConverter;
/**
@@ -37,7 +38,7 @@ public interface CassandraOperations extends CqlOperations {
* @param entityClass must not be {@literal null}.
* @return
*/
String getTableName(Class<?> entityClass);
CqlIdentifier getTableName(Class<?> entityClass);
/**
* Execute query and convert ResultSet to the list of entities

View File

@@ -22,6 +22,7 @@ import java.util.List;
import org.springframework.cassandra.core.CqlTemplate;
import org.springframework.cassandra.core.QueryOptions;
import org.springframework.cassandra.core.SessionCallback;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.util.CollectionUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;
@@ -125,7 +126,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
Select select = QueryBuilder.select().countAll().from(entity.getTableName());
Select select = QueryBuilder.select().countAll().from(entity.getTableName().toCql());
appendIdCriteria(select.where(), entity, id);
return count(select.getQueryString()) != 0;
@@ -133,7 +134,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
@Override
public long count(Class<?> type) {
return count(getTableName(type));
return count(getTableName(type).toCql());
}
@Override
@@ -154,7 +155,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
Delete delete = QueryBuilder.delete().from(entity.getTableName());
Delete delete = QueryBuilder.delete().from(entity.getTableName().toCql());
appendIdCriteria(delete.where(), entity, id);
execute(delete.getQueryString());
@@ -191,7 +192,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
}
@Override
public String getTableName(Class<?> type) {
public CqlIdentifier getTableName(Class<?> type) {
return mappingContext.getPersistentEntity(type).getTableName();
}
@@ -237,7 +238,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
@Override
public <T> List<T> selectAll(Class<T> type) {
return select(QueryBuilder.select().all().from(getTableName(type)).getQueryString(), type);
return select(QueryBuilder.select().all().from(getTableName(type).toCql()).getQueryString(), type);
}
@Override
@@ -260,7 +261,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
entity.getIdProperty().getCompositePrimaryKeyEntity().getType().getName()));
}
Select select = QueryBuilder.select().all().from(entity.getTableName());
Select select = QueryBuilder.select().all().from(entity.getTableName().toCql());
select.where(QueryBuilder.in(entity.getIdProperty().getColumnName(), CollectionUtils.toArray(ids)));
return select(select.getQueryString(), type);
@@ -277,7 +278,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
throw new IllegalArgumentException(String.format("unknown entity class [%s]", type.getName()));
}
Select select = QueryBuilder.select().all().from(entity.getTableName());
Select select = QueryBuilder.select().all().from(entity.getTableName().toCql());
appendIdCriteria(select.where(), entity, id);
return selectOne(select.getQueryString(), type);
@@ -382,16 +383,8 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
return update(entity, options, true);
}
/**
* @param obj
* @return
*/
protected <T> String determineTableName(T obj) {
if (null != obj) {
return determineTableName(obj.getClass());
}
return null;
protected <T> CqlIdentifier determineTableName(T obj) {
return obj == null ? null : determineTableName(obj.getClass());
}
protected <T> List<T> select(final String query, CassandraConverterRowCallback<T> readRowCallback) {
@@ -452,7 +445,8 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notEmpty(entities);
Batch b = createDeleteBatchQuery(getTableName(entities.get(0).getClass()), entities, options, cassandraConverter);
Batch b = createDeleteBatchQuery(getTableName(entities.get(0).getClass()).toCql(), entities, options,
cassandraConverter);
logger.info(b.toString());
@@ -469,7 +463,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entity);
Insert insert = createInsertQuery(getTableName(entity.getClass()), entity, options, cassandraConverter);
Insert insert = createInsertQuery(getTableName(entity.getClass()).toCql(), entity, options, cassandraConverter);
String query = insert.getQueryString();
@@ -486,7 +480,8 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notEmpty(entities);
Batch b = createInsertBatchQuery(getTableName(entities.get(0).getClass()), entities, options, cassandraConverter);
Batch b = createInsertBatchQuery(getTableName(entities.get(0).getClass()).toCql(), entities, options,
cassandraConverter);
String query = b.getQueryString();
logger.info(query);
@@ -513,7 +508,8 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notEmpty(entities);
Batch b = toUpdateBatchQuery(getTableName(entities.get(0).getClass()), entities, options, cassandraConverter);
Batch b = toUpdateBatchQuery(getTableName(entities.get(0).getClass()).toCql(), entities, options,
cassandraConverter);
String query = b.getQueryString();
logger.info(query);
@@ -537,7 +533,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entity);
Delete delete = createDeleteQuery(getTableName(entity.getClass()), entity, options, cassandraConverter);
Delete delete = createDeleteQuery(getTableName(entity.getClass()).toCql(), entity, options, cassandraConverter);
logger.info(delete.toString());
String query = delete.getQueryString();
@@ -562,7 +558,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(entity);
Update q = toUpdateQuery(getTableName(entity.getClass()), entity, options, cassandraConverter);
Update q = toUpdateQuery(getTableName(entity.getClass()).toCql(), entity, options, cassandraConverter);
String query = q.getQueryString();
logger.info(query);
@@ -761,6 +757,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
throw new IllegalArgumentException(String.format("unknown persistent entity class [%s]", clazz.getName()));
}
truncate(mappingContext.getPersistentEntity(clazz).getTableName());
truncate(mappingContext.getPersistentEntity(clazz).getTableName().toCql());
}
}

View File

@@ -15,16 +15,18 @@
*/
package org.springframework.data.cassandra.mapping;
import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.support.exception.UnsupportedCassandraOperationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryAccessor;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.data.cassandra.util.CassandraNamingUtils;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PropertyHandler;
@@ -47,11 +49,12 @@ import org.springframework.util.StringUtils;
public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T, CassandraPersistentProperty> implements
CassandraPersistentEntity<T>, ApplicationContextAware {
protected String tableName;
protected CassandraMappingContext mappingContext;
protected final SpelExpressionParser spelParser;
protected final StandardEvaluationContext spelContext;
protected static final CassandraPersistentEntityMetadataVerifier DEFAULT_VERIFIER = new DefaultCassandraPersistentEntityMetadataVerifier();
protected CqlIdentifier tableName;
protected CassandraMappingContext mappingContext;
protected final StandardEvaluationContext spelContext;
protected final SpelExpressionParser spelParser;
protected CassandraPersistentEntityMetadataVerifier verifier = DEFAULT_VERIFIER;
public BasicCassandraPersistentEntity(TypeInformation<T> typeInformation) {
@@ -85,15 +88,20 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
this.mappingContext = mappingContext;
setVerifier(verifier);
determineTableName();
}
protected void determineTableName() {
protected CqlIdentifier determineTableName() {
Table anno = getType().getAnnotation(Table.class);
this.tableName = anno != null && StringUtils.hasText(anno.value()) ? anno.value() : CassandraNamingUtils
.getPreferredTableName(getType());
if (anno == null || !StringUtils.hasText(anno.value())) {
return cqlId(getType().getSimpleName(), anno == null ? false : anno.forceQuote());
}
Expression expression = spelParser.parseExpression(anno.value(), ParserContext.TEMPLATE_EXPRESSION);
String tableName = expression.getValue(spelContext, String.class);
return cqlId(tableName, anno.forceQuote());
}
@Override
@@ -112,17 +120,25 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
spelContext.addPropertyAccessor(new BeanFactoryAccessor());
spelContext.setBeanResolver(new BeanFactoryResolver(applicationContext));
spelContext.setRootObject(applicationContext);
// this is the earliest time at which we can do this because the table name value may contain a SpEL expression
getTableName();
}
@Override
public String getTableName() {
Expression expression = spelParser.parseExpression(tableName, ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(spelContext, String.class);
public CqlIdentifier getTableName() {
if (tableName != null) {
return tableName;
}
return tableName = determineTableName();
}
@Override
public void setTableName(String tableName) {
Assert.hasText(tableName);
public void setTableName(CqlIdentifier tableName) {
Assert.notNull(tableName);
this.tableName = tableName;
}

View File

@@ -19,11 +19,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.util.TypeInformation;
public class CachingCassandraPersistentEntity<T> extends BasicCassandraPersistentEntity<T> {
protected String tableName;
protected CqlIdentifier tableName;
protected String name;
protected Boolean isCompositePrimaryKey;
protected List<CassandraPersistentProperty> compositePrimaryKeyProperties;
@@ -43,7 +44,7 @@ public class CachingCassandraPersistentEntity<T> extends BasicCassandraPersisten
}
@Override
public String getTableName() {
public CqlIdentifier getTableName() {
if (tableName == null) {
tableName = super.getTableName();
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.cassandra.mapping;
import java.util.List;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.MutablePersistentEntity;
@@ -40,14 +41,9 @@ public interface CassandraPersistentEntity<T> extends MutablePersistentEntity<T,
/**
* Returns the table name to which the entity shall be persisted.
*/
String getTableName();
CqlIdentifier getTableName();
/**
* Sets the table name to which the entity shall be persisted.
*
* @param tableName The table name; must contain a valid Cassandra table name.
*/
void setTableName(String tableName);
void setTableName(CqlIdentifier tableName);
CassandraMappingContext getMappingContext();
}

View File

@@ -51,4 +51,9 @@ public @interface Column {
* The name of the column in the table; must be a valid CQL identifier or quoted identifier.
*/
String value() default "";
/**
* Whether to cause the column name to be force-quoted.
*/
boolean forceQuote() default false;
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.cassandra.mapping;
import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
import static org.springframework.cassandra.core.keyspace.CreateTableSpecification.createTable;
import java.beans.PropertyDescriptor;
@@ -27,6 +28,7 @@ import java.util.Map;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -59,7 +61,7 @@ public class DefaultCassandraMappingContext extends
protected CassandraPersistentEntityMetadataVerifier verifier = new DefaultCassandraPersistentEntityMetadataVerifier();
// useful caches
protected Map<String, Set<CassandraPersistentEntity<?>>> entitySetsByTableName = new HashMap<String, Set<CassandraPersistentEntity<?>>>();
protected Map<CqlIdentifier, Set<CassandraPersistentEntity<?>>> entitySetsByTableName = new HashMap<CqlIdentifier, Set<CassandraPersistentEntity<?>>>();
protected Set<CassandraPersistentEntity<?>> nonPrimaryKeyEntities = new HashSet<CassandraPersistentEntity<?>>();
protected Set<CassandraPersistentEntity<?>> primaryKeyEntities = new HashSet<CassandraPersistentEntity<?>>();
protected Map<Class<?>, CassandraPersistentEntity<?>> entitiesByType = new HashMap<Class<?>, CassandraPersistentEntity<?>>();
@@ -240,7 +242,7 @@ public class DefaultCassandraMappingContext extends
continue;
}
entity.setTableName(tableName);
entity.setTableName(cqlId(tableName, entityMapping.getForceQuote()));
}
}
@@ -274,6 +276,7 @@ public class DefaultCassandraMappingContext extends
/**
* @param verifier The verifier to set.
*/
@Override
public void setVerifier(CassandraPersistentEntityMetadataVerifier verifier) {
this.verifier = verifier;
}

View File

@@ -15,9 +15,14 @@
*/
package org.springframework.data.cassandra.mapping;
import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
import static org.springframework.cassandra.core.cql.CqlIdentifier.quotedCqlId;
import java.util.HashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* Mapping information for an individual entity class.
*
@@ -35,14 +40,25 @@ public class EntityMapping {
*/
protected String tableName;
/**
* Whether to force the table name to be quoted.
*/
protected boolean forceQuote = false;
/**
* The {@link PropertyMapping}s for each persistent property, keyed on property name.
*/
protected Map<String, PropertyMapping> propertyMappings = new HashMap<String, PropertyMapping>();
public EntityMapping(String entityClassName, String tableName) {
this(entityClassName, tableName, false);
}
public EntityMapping(String entityClassName, String tableName, boolean forceQuote) {
setEntityClassName(entityClassName);
setTableName(tableName);
setForceQuote(forceQuote);
}
public String getEntityClassName() {
@@ -50,6 +66,8 @@ public class EntityMapping {
}
public void setEntityClassName(String entityClassName) {
Assert.hasText(entityClassName);
this.entityClassName = entityClassName;
}
@@ -58,9 +76,19 @@ public class EntityMapping {
}
public void setTableName(String tableName) {
Assert.hasText(tableName);
this.tableName = tableName;
}
public boolean getForceQuote() {
return forceQuote;
}
public void setForceQuote(boolean forceQuote) {
this.forceQuote = forceQuote;
}
@Override
public boolean equals(Object that) {
if (that == null) {
@@ -73,13 +101,14 @@ public class EntityMapping {
return false;
}
EntityMapping thatMapping = (EntityMapping) that;
EntityMapping other = (EntityMapping) that;
return this.entityClassName.equals(thatMapping.entityClassName) && this.tableName.equals(thatMapping.tableName);
return this.entityClassName.equals(other.entityClassName)
&& (forceQuote ? quotedCqlId(this.tableName) : cqlId(this.tableName)).equals(other.tableName);
}
@Override
public int hashCode() {
return entityClassName.hashCode() ^ tableName.hashCode();
return entityClassName.hashCode() ^ (forceQuote ? quotedCqlId(this.tableName) : cqlId(this.tableName)).hashCode();
}
}

View File

@@ -53,4 +53,9 @@ public @interface PrimaryKeyColumn {
* Default is {@link Ordering#ASCENDING}.
*/
Ordering ordering() default Ordering.ASCENDING;
/**
* Whether to cause the column name to be force-quoted.
*/
boolean forceQuote() default false;
}

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.cassandra.mapping;
import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
import static org.springframework.cassandra.core.cql.CqlIdentifier.quotedCqlId;
import org.springframework.util.Assert;
/**
@@ -26,18 +29,24 @@ public class PropertyMapping {
protected String propertyName;
protected String columnName;
protected boolean forceQuote;
public PropertyMapping(String propertyName, String columnName) {
this(propertyName, columnName, false);
}
public PropertyMapping(String propertyName, String columnName, boolean forceQuote) {
setPropertyName(propertyName);
setColumnName(columnName);
setForceQuote(forceQuote);
}
public String getPropertyName() {
return propertyName;
}
protected void setPropertyName(String propertyName) {
public void setPropertyName(String propertyName) {
Assert.notNull(propertyName);
this.propertyName = propertyName;
}
@@ -46,8 +55,59 @@ public class PropertyMapping {
return columnName;
}
protected void setColumnName(String columnName) {
public void setColumnName(String columnName) {
Assert.notNull(columnName);
this.columnName = columnName;
}
public boolean getForceQuote() {
return forceQuote;
}
public void setForceQuote(boolean forceQuote) {
this.forceQuote = forceQuote;
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (!(that instanceof PropertyMapping)) {
return false;
}
PropertyMapping other = (PropertyMapping) that;
if (this.propertyName == null) {
if (other.propertyName != null) {
return false;
}
} else if (!this.propertyName.equals(other.propertyName)) {
return false;
}
if (this.columnName == null) {
if (other.columnName != null) {
return false;
}
} else if (!(forceQuote ? quotedCqlId(this.columnName) : cqlId(this.columnName)).equals(other.columnName)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hashCode = 37;
hashCode ^= (propertyName == null ? 0 : propertyName.hashCode());
hashCode ^= (columnName == null ? 0 : (forceQuote ? quotedCqlId(this.columnName) : cqlId(this.columnName))
.hashCode());
return hashCode;
}
}

View File

@@ -39,4 +39,9 @@ public @interface Table {
* The name of the table; must be a valid CQL identifier or quoted identifier.
*/
String value() default "";
/**
* Whether to cause the table name to be force-quoted.
*/
boolean forceQuote() default false;
}

View File

@@ -1,61 +0,0 @@
/*
* 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.mapping;
import java.util.Set;
/**
* Interface that stores information about the mapping between a table and the entity or the entities that are mapped to
* it.
*
* @author Matthew T. Adams
*/
public interface TableMapping {
/**
* Convenience method to return the only member of the set of entity classes. This method can be used when the caller
* knows that there is only one entity class in this mapping. If there are multiple and this method is called, an
* {@link IllegalStateException} is thrown.
*/
Class<?> getEntityClass();
/**
* Convenience method to set this mapping to use a single entity class.
*
* @param entityClass The class; may not be null.
*/
void setEntityClass(Class<?> entityClass);
/**
* Sets the set of entity classes of this mapping.
*/
void setEntityClasses(Set<Class<?>> entityClasses);
/**
* Returns the set of entity classes of this mapping. Never returns null.
*/
Set<Class<?>> getEntityClasses();
/**
* Returns the name of this mapping. Never returns null.
*/
String getTableName();
/**
* Sets the table name of this mapping.
*/
void setTableName(String tableName);
}

View File

@@ -1,112 +0,0 @@
/*
* 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.mapping;
import java.util.HashSet;
import java.util.Set;
import org.springframework.util.Assert;
/**
* Default implementation of {@link TableMapping}.
*
* @author Matthew T. Adams
*/
public class TableMappingImpl implements TableMapping {
private Set<Class<?>> entityClasses = new HashSet<Class<?>>();
private String tableName;
public TableMappingImpl(Class<?> entityClass, String tableName) {
this(tableName);
setEntityClass(entityClass);
}
public TableMappingImpl(Set<Class<?>> entityClasses, String tableName) {
this(tableName);
setEntityClasses(entityClasses);
}
protected TableMappingImpl(String tableName) {
setTableName(tableName);
}
@Override
public Class<?> getEntityClass() {
if (entityClasses.size() != 1) {
throw new IllegalStateException("more than one entity class exists in this TableMapping");
}
return entityClasses.iterator().next();
}
@Override
public Set<Class<?>> getEntityClasses() {
return entityClasses;
}
@Override
public void setEntityClasses(Set<Class<?>> entityClasses) {
this.entityClasses = entityClasses == null ? new HashSet<Class<?>>() : new HashSet<Class<?>>(entityClasses);
}
@Override
public String getTableName() {
return tableName;
}
@Override
public void setTableName(String tableName) {
Assert.notNull(tableName);
this.tableName = tableName;
}
@Override
public void setEntityClass(Class<?> entityClass) {
if (entityClass == null) {
throw new IllegalArgumentException("entity class required");
}
entityClasses.clear();
entityClasses.add(entityClass);
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (!(that instanceof TableMapping)) {
return false;
}
TableMapping thatMapping = (TableMapping) that;
if (!this.tableName.equals(thatMapping.getTableName())) {
return false;
}
return this.entityClasses.equals(thatMapping.getEntityClasses());
}
@Override
public int hashCode() {
return tableName.hashCode() ^ entityClasses.hashCode();
}
}

View File

@@ -1,23 +0,0 @@
/*
* 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.mapping;
public interface TableMappings {
TableMapping getTableMappingByClass(Class<?> entityClass);
TableMapping getTableMappingByTableName(String tableName);
}

View File

@@ -1,63 +0,0 @@
/*
* 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.mapping;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class TableMappingsImpl implements TableMappings {
private Map<Class<?>, TableMapping> mappingsByClass = new HashMap<Class<?>, TableMapping>();
private Map<String, TableMapping> mappingsByTable = new HashMap<String, TableMapping>();
public TableMappingsImpl(Set<TableMapping> mappings) {
setMappings(mappings);
}
public void setMappings(Set<TableMapping> mappings) {
mappingsByClass.clear();
mappingsByTable.clear();
if (mappings == null || mappings.size() == 0) {
return;
}
for (TableMapping mapping : mappings) {
if (mapping == null) {
continue;
}
mappingsByTable.put(mapping.getTableName(), mapping);
for (Class<?> entityClass : mapping.getEntityClasses()) {
mappingsByClass.put(entityClass, mapping);
}
}
}
@Override
public TableMapping getTableMappingByClass(Class<?> entityClass) {
return mappingsByClass.get(entityClass);
}
@Override
public TableMapping getTableMappingByTableName(String tableName) {
return mappingsByTable.get(tableName);
}
}

View File

@@ -17,6 +17,7 @@ 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;
/**
@@ -32,5 +33,5 @@ public interface CassandraEntityInformation<T, ID extends Serializable> extends
*
* @return
*/
String getTableName();
CqlIdentifier getTableName();
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.cassandra.repository.support;
import java.io.Serializable;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
@@ -36,7 +37,7 @@ public class MappingCassandraEntityInformation<T, ID extends Serializable> exten
implements CassandraEntityInformation<T, ID> {
private final CassandraPersistentEntity<T> entityMetadata;
private final String customTableName;
private final CqlIdentifier customTableName;
/**
* Creates a new {@link MappingCassandraEntityInformation} for the given {@link CassandraPersistentEntity}.
@@ -54,7 +55,7 @@ public class MappingCassandraEntityInformation<T, ID extends Serializable> exten
* @param entity must not be {@literal null}.
* @param customTableName
*/
public MappingCassandraEntityInformation(CassandraPersistentEntity<T> entity, String customTableName) {
public MappingCassandraEntityInformation(CassandraPersistentEntity<T> entity, CqlIdentifier customTableName) {
super(entity.getType());
this.entityMetadata = entity;
this.customTableName = customTableName;
@@ -81,7 +82,7 @@ public class MappingCassandraEntityInformation<T, ID extends Serializable> exten
}
@Override
public String getTableName() {
public CqlIdentifier getTableName() {
return customTableName == null ? entityMetadata.getTableName() : customTableName;
}
}

View File

@@ -1,36 +0,0 @@
/*
* 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.util;
/**
* Helper class featuring helper methods for working with Cassandra tables. Mainly intended for internal use within the
* framework.
*
* @author Alex Shvid
*/
public abstract class CassandraNamingUtils {
/**
* Obtains the table name to use for the provided class
*
* @param entityClass The class to determine the preferred table name for
* @return The preferred collection name
*/
public static String getPreferredTableName(Class<?> entityClass) {
return entityClass.getSimpleName().toLowerCase();
}
}

View File

@@ -0,0 +1,41 @@
package org.springframework.data.cassandra.util;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class SpelUtils {
public static final SpelExpressionParser DEFAULT_PARSER = new SpelExpressionParser();
/**
* Evaluates the given value against the given context as a string.
*/
public static String evaluate(CharSequence value, EvaluationContext context) {
return evaluate(value, context, DEFAULT_PARSER);
}
/**
* Evaluates the given value against the given context as a string using the given parser.
*/
public static String evaluate(CharSequence value, EvaluationContext context, ExpressionParser parser) {
return evaluate(value, context, String.class, parser);
}
/**
* Evaluates the given value against the given context as an object of the given class.
*/
public static <T> T evaluate(CharSequence value, EvaluationContext context, Class<T> clazz) {
return evaluate(value, context, clazz, DEFAULT_PARSER);
}
/**
* Evaluates the given value against the given context as an object of the given class using the given parser.
*/
public static <T> T evaluate(CharSequence value, EvaluationContext context, Class<T> clazz, ExpressionParser parser) {
Expression expression = parser.parseExpression(value.toString(), ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(context, clazz);
}
}

View File

@@ -651,6 +651,14 @@ Table name override.
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="force-quote" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether to force-quote the table name.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<!-- TODO: allow specification of C* table options here -->
</xsd:complexType>

View File

@@ -45,7 +45,7 @@ public class BasicCassandraPersistentEntityIntegrationTests extends AbstractSpri
BasicCassandraPersistentEntity<Notification> entity = new BasicCassandraPersistentEntity<Notification>(
ClassTypeInformation.from(Notification.class));
assertThat(entity.getTableName(), is("messages"));
assertThat(entity.getTableName().toCql(), is("messages"));
}
@Test
@@ -53,7 +53,7 @@ public class BasicCassandraPersistentEntityIntegrationTests extends AbstractSpri
BasicCassandraPersistentEntity<Area> entity = new BasicCassandraPersistentEntity<Area>(
ClassTypeInformation.from(Area.class));
assertThat(entity.getTableName(), is("123"));
assertThat(entity.getTableName().toCql(), is("a123"));
}
@Test
@@ -69,26 +69,22 @@ public class BasicCassandraPersistentEntityIntegrationTests extends AbstractSpri
ClassTypeInformation.from(UserLine.class));
entity.setApplicationContext(context);
assertThat(entity.getTableName(), is(bean.tableName));
assertThat(entity.getTableName().toCql(), is(bean.tableName));
}
@Table("messages")
static class Message {
}
static class Notification extends Message {
}
@Table("#{123}")
@Table("#{'a123'}")
static class Area {
}
@Table("#{tableNameHolderThingy.tableName}")
static class UserLine {
}
static class TableNameHolderThingy {
@@ -99,5 +95,4 @@ public class BasicCassandraPersistentEntityIntegrationTests extends AbstractSpri
return tableName;
}
}
}