From a6cd162c5204cade8ea8de3c4d980be6c8d39c49 Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Wed, 5 Feb 2014 20:03:46 -0600 Subject: [PATCH 1/5] DATACASS-33 - refactored a bunch & ready to begin testing compound primary key support --- .../cassandra/core/CqlOperations.java | 63 ++ .../cassandra/core/CqlTemplate.java | 122 +++ .../cassandra/core/cql/CqlStringUtils.java | 34 + .../generator/AlterKeyspaceCqlGenerator.java | 5 + .../cql/generator/AlterTableCqlGenerator.java | 5 + .../generator/CreateIndexCqlGenerator.java | 5 + .../generator/CreateKeyspaceCqlGenerator.java | 5 + .../generator/CreateTableCqlGenerator.java | 5 + .../cql/generator/DropIndexCqlGenerator.java | 5 + .../generator/DropKeyspaceCqlGenerator.java | 5 + .../cql/generator/DropTableCqlGenerator.java | 5 + .../keyspace/CreateTableSpecification.java | 5 + .../core/keyspace/DropTableSpecification.java | 11 + .../cassandra/support/CassandraAccessor.java | 5 +- .../CassandraDataSessionFactoryBean.java | 2 + ...tractSpringDataCassandraConfiguration.java | 5 +- .../xml/CassandraDataSessionParser.java | 4 +- ...ersistentEntityParameterValueProvider.java | 15 + .../convert/MappingCassandraConverter.java | 181 +++-- .../core/CassandraAdminTemplate.java | 19 +- .../cassandra/core/CassandraOperations.java | 12 +- .../cassandra/core/CassandraTemplate.java | 700 ++++++++++++------ .../exception/EntityWriterException.java | 50 -- .../BasicCassandraPersistentEntity.java | 72 +- .../BasicCassandraPersistentProperty.java | 109 ++- .../CachingCassandraPersistentEntity.java | 64 ++ .../CachingCassandraPersistentProperty.java | 21 +- .../CassandraColumnAnnotationComparator.java | 21 + .../mapping/CassandraPersistentEntity.java | 15 +- .../mapping/CassandraPersistentProperty.java | 37 +- ...CassandraPersistentPropertyComparator.java | 87 +++ ...PrimaryKeyColumnAnnotationComparator.java} | 12 +- .../DefaultCassandraMappingContext.java | 10 +- ...draPersistentPropertyColumnComparator.java | 22 - .../{config => mapping}/EntityMapping.java | 17 +- .../{config => mapping}/Mapping.java | 2 +- .../cassandra/mapping/PropertyMapping.java | 38 + .../repository/CassandraRepository.java | 5 +- .../query/CassandraEntityInformation.java | 8 - .../MappingCassandraEntityInformation.java | 18 - .../support/SimpleCassandraRepository.java | 93 +-- .../data/cassandra/util/CqlUtils.java | 288 +------ ...raCompositePrimaryKeyIntegrationTests.java | 126 ++++ .../CompoundPrimaryKeyIntegrationTests.java | 3 +- 44 files changed, 1483 insertions(+), 853 deletions(-) create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraPersistentEntityParameterValueProvider.java delete mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/exception/EntityWriterException.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CachingCassandraPersistentEntity.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraColumnAnnotationComparator.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPersistentPropertyComparator.java rename spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/{DefaultCassandraPrimaryKeyColumnComparator.java => CassandraPrimaryKeyColumnAnnotationComparator.java} (69%) delete mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraPersistentPropertyColumnComparator.java rename spring-data-cassandra/src/main/java/org/springframework/data/cassandra/{config => mapping}/EntityMapping.java (72%) rename spring-data-cassandra/src/main/java/org/springframework/data/cassandra/{config => mapping}/Mapping.java (95%) create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PropertyMapping.java create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/CassandraCompositePrimaryKeyIntegrationTests.java diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java index 594ffc77d..fa9b5423b 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java @@ -21,6 +21,14 @@ import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import org.springframework.cassandra.core.keyspace.AlterKeyspaceSpecification; +import org.springframework.cassandra.core.keyspace.AlterTableSpecification; +import org.springframework.cassandra.core.keyspace.CreateIndexSpecification; +import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; +import org.springframework.cassandra.core.keyspace.CreateTableSpecification; +import org.springframework.cassandra.core.keyspace.DropIndexSpecification; +import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; +import org.springframework.cassandra.core.keyspace.DropTableSpecification; import org.springframework.dao.DataAccessException; import com.datastax.driver.core.ResultSet; @@ -754,4 +762,59 @@ public interface CqlOperations { */ void truncate(String tableName); + /** + * Convenience method to convert the given specification to CQL and execute it. + * + * @param specification The specification to execute; must not be null. + */ + ResultSet execute(DropTableSpecification specification); + + /** + * Convenience method to convert the given specification to CQL and execute it. + * + * @param specification The specification to execute; must not be null. + */ + ResultSet execute(CreateTableSpecification specification); + + /** + * Convenience method to convert the given specification to CQL and execute it. + * + * @param specification The specification to execute; must not be null. + */ + ResultSet execute(AlterTableSpecification specification); + + /** + * Convenience method to convert the given specification to CQL and execute it. + * + * @param specification The specification to execute; must not be null. + */ + ResultSet execute(DropKeyspaceSpecification specification); + + /** + * Convenience method to convert the given specification to CQL and execute it. + * + * @param specification The specification to execute; must not be null. + */ + ResultSet execute(CreateKeyspaceSpecification specification); + + /** + * Convenience method to convert the given specification to CQL and execute it. + * + * @param specification The specification to execute; must not be null. + */ + ResultSet execute(AlterKeyspaceSpecification specification); + + /** + * Convenience method to convert the given specification to CQL and execute it. + * + * @param specification The specification to execute; must not be null. + */ + ResultSet execute(DropIndexSpecification specification); + + /** + * Convenience method to convert the given specification to CQL and execute it. + * + * @param specification The specification to execute; must not be null. + */ + ResultSet execute(CreateIndexSpecification specification); } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java index 6b90dee2a..52e8b034e 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java @@ -27,6 +27,22 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.springframework.cassandra.core.cql.generator.AlterKeyspaceCqlGenerator; +import org.springframework.cassandra.core.cql.generator.AlterTableCqlGenerator; +import org.springframework.cassandra.core.cql.generator.CreateIndexCqlGenerator; +import org.springframework.cassandra.core.cql.generator.CreateKeyspaceCqlGenerator; +import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator; +import org.springframework.cassandra.core.cql.generator.DropIndexCqlGenerator; +import org.springframework.cassandra.core.cql.generator.DropKeyspaceCqlGenerator; +import org.springframework.cassandra.core.cql.generator.DropTableCqlGenerator; +import org.springframework.cassandra.core.keyspace.AlterKeyspaceSpecification; +import org.springframework.cassandra.core.keyspace.AlterTableSpecification; +import org.springframework.cassandra.core.keyspace.CreateIndexSpecification; +import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; +import org.springframework.cassandra.core.keyspace.CreateTableSpecification; +import org.springframework.cassandra.core.keyspace.DropIndexSpecification; +import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; +import org.springframework.cassandra.core.keyspace.DropTableSpecification; import org.springframework.cassandra.support.CassandraAccessor; import org.springframework.dao.DataAccessException; import org.springframework.dao.QueryTimeoutException; @@ -278,6 +294,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { process(doExecute(cql, options), rch); } + @Override public void query(String cql, RowCallbackHandler rch) throws DataAccessException { query(cql, rch, null); } @@ -287,26 +304,32 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { return process(doExecute(cql, options), rowMapper); } + @Override public List query(String cql, RowMapper rowMapper) throws DataAccessException { return query(cql, rowMapper, null); } + @Override public List> queryForListOfMap(String cql) throws DataAccessException { return processListOfMap(doExecute(cql, null)); } + @Override public List queryForList(String cql, Class elementType) throws DataAccessException { return processList(doExecute(cql, null), elementType); } + @Override public Map queryForMap(String cql) throws DataAccessException { return processMap(doExecute(cql, null)); } + @Override public T queryForObject(String cql, Class requiredType) throws DataAccessException { return processOne(doExecute(cql, null), requiredType); } + @Override public T queryForObject(String cql, RowMapper rowMapper) throws DataAccessException { return processOne(doExecute(cql, null), rowMapper); } @@ -766,6 +789,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { logger.debug("Executing prepared CQL query"); return execute(psc, new PreparedStatementCallback() { + @Override public T doInPreparedStatement(PreparedStatement ps) throws DriverException { ResultSet rs = null; BoundStatement bs = null; @@ -794,6 +818,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { logger.debug("Executing prepared CQL query"); execute(psc, new PreparedStatementCallback() { + @Override public Object doInPreparedStatement(PreparedStatement ps) throws DriverException { ResultSet rs = null; BoundStatement bs = null; @@ -822,6 +847,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { logger.debug("Executing prepared CQL query"); return execute(psc, new PreparedStatementCallback>() { + @Override public List doInPreparedStatement(PreparedStatement ps) throws DriverException { ResultSet rs = null; BoundStatement bs = null; @@ -843,4 +869,100 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { return query(psc, psb, rowMapper, null); } + @Override + public ResultSet execute(final DropTableSpecification specification) { + + return execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + return s.execute(DropTableCqlGenerator.toCql(specification)); + } + }); + } + + @Override + public ResultSet execute(final CreateTableSpecification specification) { + + return execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + return s.execute(CreateTableCqlGenerator.toCql(specification)); + } + }); + } + + @Override + public ResultSet execute(final AlterTableSpecification specification) { + + return execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + return s.execute(AlterTableCqlGenerator.toCql(specification)); + } + }); + } + + @Override + public ResultSet execute(final DropKeyspaceSpecification specification) { + + return execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + return s.execute(DropKeyspaceCqlGenerator.toCql(specification)); + } + }); + } + + @Override + public ResultSet execute(final CreateKeyspaceSpecification specification) { + + return execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + return s.execute(CreateKeyspaceCqlGenerator.toCql(specification)); + } + }); + } + + @Override + public ResultSet execute(final AlterKeyspaceSpecification specification) { + + return execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + return s.execute(AlterKeyspaceCqlGenerator.toCql(specification)); + } + }); + } + + @Override + public ResultSet execute(final DropIndexSpecification specification) { + + return execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + return s.execute(DropIndexCqlGenerator.toCql(specification)); + } + }); + } + + @Override + public ResultSet execute(final CreateIndexSpecification specification) { + + return execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + return s.execute(CreateIndexCqlGenerator.toCql(specification)); + } + }); + } + } \ No newline at end of file diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java index b711c5a41..61180cc7f 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java @@ -17,6 +17,8 @@ package org.springframework.cassandra.core.cql; import java.util.regex.Pattern; +import com.datastax.driver.core.DataType; + public class CqlStringUtils { protected static final String SINGLE_QUOTE = "\'"; @@ -24,6 +26,8 @@ public class CqlStringUtils { protected static final String DOUBLE_QUOTE = "\""; protected static final String DOUBLE_DOUBLE_QUOTE = "\"\""; protected static final String EMPTY_STRING = ""; + protected static final String TYPE_PARAMETER_PREFIX = "<"; + protected static final String TYPE_PARAMETER_SUFFIX = ">"; public static StringBuilder noNull(StringBuilder sb) { return sb == null ? new StringBuilder() : sb; @@ -137,4 +141,34 @@ public class CqlStringUtils { public static String removeSingleQuotes(Object thing) { return thing == null ? (String) null : ((String) thing).replaceAll(SINGLE_QUOTE, EMPTY_STRING); } + + /** + * Renders the given {@link DataType} as a CQL string. + * + * @param dataType The {@link DataType} to render; must not be null. + */ + public static String toCql(DataType dataType) { + + if (dataType.getTypeArguments().isEmpty()) { + return dataType.getName().name(); + } + + StringBuilder s = new StringBuilder(); + s.append(dataType.getName().name()).append(TYPE_PARAMETER_PREFIX); + + boolean first = true; + + for (DataType argDataType : dataType.getTypeArguments()) { + + if (first) { + first = false; + } else { + s.append(','); + } + + s.append(argDataType.getName().name()); + } + + return s.append(TYPE_PARAMETER_SUFFIX).toString(); + } } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/AlterKeyspaceCqlGenerator.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/AlterKeyspaceCqlGenerator.java index 596c99174..35d54be10 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/AlterKeyspaceCqlGenerator.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/AlterKeyspaceCqlGenerator.java @@ -29,10 +29,15 @@ import org.springframework.cassandra.core.keyspace.Option; */ public class AlterKeyspaceCqlGenerator extends KeyspaceOptionsCqlGenerator { + public static String toCql(AlterKeyspaceSpecification specification) { + return new AlterKeyspaceCqlGenerator(specification).toCql(); + } + public AlterKeyspaceCqlGenerator(AlterKeyspaceSpecification specification) { super(specification); } + @Override public StringBuilder toCql(StringBuilder cql) { cql = noNull(cql); diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGenerator.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGenerator.java index ad8404d2f..0fb0050a9 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGenerator.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGenerator.java @@ -34,10 +34,15 @@ import org.springframework.cassandra.core.keyspace.TableOption; */ public class AlterTableCqlGenerator extends TableOptionsCqlGenerator { + public static String toCql(AlterTableSpecification specification) { + return new AlterTableCqlGenerator(specification).toCql(); + } + public AlterTableCqlGenerator(AlterTableSpecification specification) { super(specification); } + @Override public StringBuilder toCql(StringBuilder cql) { cql = noNull(cql); diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateIndexCqlGenerator.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateIndexCqlGenerator.java index 61636fe3d..25c3151f4 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateIndexCqlGenerator.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateIndexCqlGenerator.java @@ -28,10 +28,15 @@ import org.springframework.util.StringUtils; */ public class CreateIndexCqlGenerator extends IndexNameCqlGenerator { + public static String toCql(CreateIndexSpecification specification) { + return new CreateIndexCqlGenerator(specification).toCql(); + } + public CreateIndexCqlGenerator(CreateIndexSpecification specification) { super(specification); } + @Override public StringBuilder toCql(StringBuilder cql) { cql = noNull(cql); diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateKeyspaceCqlGenerator.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateKeyspaceCqlGenerator.java index d0aa5c22b..58ef610b5 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateKeyspaceCqlGenerator.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateKeyspaceCqlGenerator.java @@ -30,10 +30,15 @@ import org.springframework.cassandra.core.keyspace.Option; */ public class CreateKeyspaceCqlGenerator extends KeyspaceCqlGenerator { + public static String toCql(CreateKeyspaceSpecification specification) { + return new CreateKeyspaceCqlGenerator(specification).toCql(); + } + public CreateKeyspaceCqlGenerator(CreateKeyspaceSpecification specification) { super(specification); } + @Override public StringBuilder toCql(StringBuilder cql) { cql = noNull(cql); diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateTableCqlGenerator.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateTableCqlGenerator.java index c13d456ab..cb490bf54 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateTableCqlGenerator.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/CreateTableCqlGenerator.java @@ -35,10 +35,15 @@ import org.springframework.cassandra.core.keyspace.Option; */ public class CreateTableCqlGenerator extends TableCqlGenerator { + public static String toCql(CreateTableSpecification specification) { + return new CreateTableCqlGenerator(specification).toCql(); + } + public CreateTableCqlGenerator(CreateTableSpecification specification) { super(specification); } + @Override public StringBuilder toCql(StringBuilder cql) { cql = noNull(cql); diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropIndexCqlGenerator.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropIndexCqlGenerator.java index 99c8d2f79..f6d70cb37 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropIndexCqlGenerator.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropIndexCqlGenerator.java @@ -27,10 +27,15 @@ import org.springframework.cassandra.core.keyspace.DropIndexSpecification; */ public class DropIndexCqlGenerator extends IndexNameCqlGenerator { + public static String toCql(DropIndexSpecification specification) { + return new DropIndexCqlGenerator(specification).toCql(); + } + public DropIndexCqlGenerator(DropIndexSpecification specification) { super(specification); } + @Override public StringBuilder toCql(StringBuilder cql) { return noNull(cql).append("DROP INDEX ") // .append(spec().getIfExists() ? "IF EXISTS " : "") diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropKeyspaceCqlGenerator.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropKeyspaceCqlGenerator.java index 63382bd88..9abde57bc 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropKeyspaceCqlGenerator.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropKeyspaceCqlGenerator.java @@ -26,10 +26,15 @@ import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; */ public class DropKeyspaceCqlGenerator extends KeyspaceNameCqlGenerator { + public static String toCql(DropKeyspaceSpecification specification) { + return new DropKeyspaceCqlGenerator(specification).toCql(); + } + public DropKeyspaceCqlGenerator(DropKeyspaceSpecification specification) { super(specification); } + @Override public StringBuilder toCql(StringBuilder cql) { return noNull(cql).append("DROP KEYSPACE ").append(spec().getIfExists() ? "IF EXISTS " : "") .append(spec().getNameAsIdentifier()).append(";"); diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropTableCqlGenerator.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropTableCqlGenerator.java index 57a459b36..708482254 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropTableCqlGenerator.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/DropTableCqlGenerator.java @@ -26,10 +26,15 @@ import org.springframework.cassandra.core.keyspace.DropTableSpecification; */ public class DropTableCqlGenerator extends TableNameCqlGenerator { + public static String toCql(DropTableSpecification specification) { + return new DropTableCqlGenerator(specification).toCql(); + } + public DropTableCqlGenerator(DropTableSpecification specification) { super(specification); } + @Override public StringBuilder toCql(StringBuilder cql) { return noNull(cql).append("DROP TABLE ") // .append(spec().getIfExists() ? "IF EXISTS " : "") diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateTableSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateTableSpecification.java index 10b1b6674..0f87f1e8c 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateTableSpecification.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateTableSpecification.java @@ -47,6 +47,11 @@ public class CreateTableSpecification extends TableSpecificationdropTable().name(tableName). + * + * @param tableName The name of the table to drop. + */ + public static DropTableSpecification dropTable(String tableName) { + return new DropTableSpecification().name(tableName); + } } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/support/CassandraAccessor.java b/spring-cassandra/src/main/java/org/springframework/cassandra/support/CassandraAccessor.java index 359605c01..b2c363a18 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/support/CassandraAccessor.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/support/CassandraAccessor.java @@ -24,7 +24,6 @@ import com.datastax.driver.core.Session; /** * @author David Webb - * */ public class CassandraAccessor implements InitializingBean { @@ -56,9 +55,7 @@ public class CassandraAccessor implements InitializingBean { */ @Override public void afterPropertiesSet() { - if (getSession() == null) { - throw new IllegalArgumentException("Property 'session' is required"); - } + Assert.notNull(session); } /** diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java index cd3794260..8b30a0a98 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java @@ -7,6 +7,8 @@ import org.springframework.data.cassandra.convert.CassandraConverter; import org.springframework.data.cassandra.core.CassandraAdminTemplate; import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; +import org.springframework.data.cassandra.mapping.EntityMapping; +import org.springframework.data.cassandra.mapping.Mapping; import org.springframework.util.Assert; import org.springframework.util.StringUtils; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java index 0de077c04..5c06b1447 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java @@ -15,6 +15,7 @@ */ package org.springframework.data.cassandra.config.java; +import java.lang.annotation.Annotation; import java.util.HashSet; import java.util.Set; @@ -27,7 +28,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.data.annotation.Persistent; import org.springframework.data.cassandra.config.CassandraDataSessionFactoryBean; -import org.springframework.data.cassandra.config.Mapping; import org.springframework.data.cassandra.config.SchemaAction; import org.springframework.data.cassandra.convert.CassandraConverter; import org.springframework.data.cassandra.convert.MappingCassandraConverter; @@ -35,6 +35,8 @@ import org.springframework.data.cassandra.core.CassandraAdminOperations; import org.springframework.data.cassandra.core.CassandraAdminTemplate; import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; +import org.springframework.data.cassandra.mapping.Mapping; +import org.springframework.data.cassandra.mapping.PrimaryKeyClass; import org.springframework.data.cassandra.mapping.Table; import org.springframework.data.mapping.context.MappingContext; import org.springframework.util.ClassUtils; @@ -139,6 +141,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC false); componentProvider.addIncludeFilter(new AnnotationTypeFilter(Table.class)); componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); + componentProvider.addIncludeFilter(new AnnotationTypeFilter(PrimaryKeyClass.class)); for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java index 9e0fe53f0..ea2327801 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java @@ -10,9 +10,9 @@ import org.springframework.beans.factory.xml.ParserContext; import org.springframework.cassandra.config.xml.CassandraSessionParser; import org.springframework.data.cassandra.config.DefaultDataBeanNames; import org.springframework.data.cassandra.config.CassandraDataSessionFactoryBean; -import org.springframework.data.cassandra.config.EntityMapping; -import org.springframework.data.cassandra.config.Mapping; import org.springframework.data.cassandra.config.SchemaAction; +import org.springframework.data.cassandra.mapping.EntityMapping; +import org.springframework.data.cassandra.mapping.Mapping; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Attr; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraPersistentEntityParameterValueProvider.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraPersistentEntityParameterValueProvider.java new file mode 100644 index 000000000..e33499cde --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraPersistentEntityParameterValueProvider.java @@ -0,0 +1,15 @@ +package org.springframework.data.cassandra.convert; + +import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider; +import org.springframework.data.mapping.model.PropertyValueProvider; + +public class CassandraPersistentEntityParameterValueProvider extends + PersistentEntityParameterValueProvider { + + public CassandraPersistentEntityParameterValueProvider(PersistentEntity entity, + PropertyValueProvider provider, Object parent) { + super(entity, provider, parent); + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java index 879cd8c1a..8315f0b19 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java @@ -31,8 +31,6 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.BeanWrapper; import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator; import org.springframework.data.mapping.model.MappingException; -import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider; -import org.springframework.data.mapping.model.PropertyValueProvider; import org.springframework.data.mapping.model.SpELContext; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; @@ -54,14 +52,14 @@ import com.datastax.driver.core.querybuilder.Update; public class MappingCassandraConverter extends AbstractCassandraConverter implements CassandraConverter, ApplicationContextAware, BeanClassLoaderAware { - protected static final Logger log = LoggerFactory.getLogger(MappingCassandraConverter.class); + protected final Logger log = LoggerFactory.getLogger(getClass()); protected final CassandraMappingContext mappingContext; protected ApplicationContext applicationContext; - private SpELContext spELContext; - private boolean useFieldAccessOnly = true; + protected SpELContext spELContext; + protected boolean useFieldAccessOnly = true; - private ClassLoader beanClassLoader; + protected ClassLoader beanClassLoader; /** * Creates a new {@link MappingCassandraConverter} with the given {@link CassandraMappingContext}. @@ -111,45 +109,48 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(row, spELContext); - final PropertyValueProvider propertyProvider = new CassandraPropertyValueProvider(row, - evaluator); + final CassandraPropertyValueProvider propertyProvider = new CassandraPropertyValueProvider(row, evaluator); - PersistentEntityParameterValueProvider parameterProvider = new PersistentEntityParameterValueProvider( + CassandraPersistentEntityParameterValueProvider parameterProvider = new CassandraPersistentEntityParameterValueProvider( entity, propertyProvider, null); EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); S instance = instantiator.createInstance(entity, parameterProvider); final BeanWrapper, S> wrapper = BeanWrapper.create(instance, conversionService); - S result = wrapper.getBean(); + + readPropertiesFromRow(entity, row, propertyProvider, wrapper); + + return wrapper.getBean(); + } + + protected void readPropertiesFromRow(final CassandraPersistentEntity entity, final Row row, + final CassandraPropertyValueProvider propertyProvider, final BeanWrapper wrapper) { entity.doWithProperties(new PropertyHandler() { @Override public void doWithPersistentProperty(CassandraPersistentProperty prop) { - MappingCassandraConverter.this.handlePersistentPropertyRead(row, entity, prop, propertyProvider, wrapper); + MappingCassandraConverter.this.readPropertyFromRow(row, entity, prop, propertyProvider, wrapper); } }); - - return result; } - protected void handlePersistentPropertyRead(final Row row, final CassandraPersistentEntity entity, - final CassandraPersistentProperty prop, - final PropertyValueProvider propertyProvider, final BeanWrapper wrapper) { + protected void readPropertyFromRow(final Row row, final CassandraPersistentEntity entity, + final CassandraPersistentProperty prop, final CassandraPropertyValueProvider propertyProvider, + final BeanWrapper wrapper) { if (entity.isConstructorArgument(prop)) { // skip 'cause prop was set in ctor return; } if (prop.isCompositePrimaryKey()) { - // handle composite primary key properties via recursion into this method - throw new UnsupportedOperationException("composite primary keys are TODO"); + readPropertiesFromRow(prop.getCompositePrimaryKeyEntity(), row, propertyProvider, wrapper); + return; } - boolean hasValueForProperty = row.getColumnDefinitions().contains(prop.getColumnName()); - if (!hasValueForProperty) { + if (!row.getColumnDefinitions().contains(prop.getColumnName())) { return; } @@ -174,103 +175,121 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem } @Override - public void write(Object obj, Object builtStatement) { + public void write(Object source, Object sink) { - if (obj == null) { + if (source == null) { return; } - Class beanClassLoaderClass = transformClassToBeanClassLoaderClass(obj.getClass()); + Class beanClassLoaderClass = transformClassToBeanClassLoaderClass(source.getClass()); CassandraPersistentEntity entity = mappingContext.getPersistentEntity(beanClassLoaderClass); if (entity == null) { - throw new MappingException("No mapping metadata found for " + obj.getClass()); + throw new MappingException("No mapping metadata found for " + source.getClass()); } - if (builtStatement instanceof Insert) { - writeInsertInternal(obj, (Insert) builtStatement, entity); - } else if (builtStatement instanceof Update) { - writeUpdateInternal(obj, (Update) builtStatement, entity); - } else if (builtStatement instanceof Where) { - writeDeleteWhereInternal(obj, (Where) builtStatement, entity); + if (sink instanceof Insert) { + writeInsertFromObject(source, (Insert) sink, entity); + } else if (sink instanceof Update) { + writeUpdateFromObject(source, (Update) sink, entity); + } else if (sink instanceof Where) { + writeDeleteWhereFromObject(source, (Where) sink, entity); } else { - throw new MappingException("Unknown buildStatement " + builtStatement.getClass().getName()); + throw new MappingException("Unknown buildStatement " + sink.getClass().getName()); } } - private void writeInsertInternal(final Object objectToSave, final Insert insert, CassandraPersistentEntity entity) { - - final BeanWrapper, Object> wrapper = BeanWrapper.create(objectToSave, - conversionService); - - // Write the properties - entity.doWithProperties(new PropertyHandler() { - @Override - public void doWithPersistentProperty(CassandraPersistentProperty prop) { - - Object propertyObj = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly); - - if (propertyObj != null) { - insert.value(prop.getColumnName(), propertyObj); - } - - } - }); - + protected void writeInsertFromObject(final Object object, final Insert insert, CassandraPersistentEntity entity) { + writeInsertFromWrapper(BeanWrapper., Object> create(object, conversionService), + insert, entity); } - private void writeUpdateInternal(final Object objectToSave, final Update update, CassandraPersistentEntity entity) { + protected void writeInsertFromWrapper(final BeanWrapper, Object> wrapper, + final Insert insert, CassandraPersistentEntity entity) { - final BeanWrapper, Object> wrapper = BeanWrapper.create(objectToSave, - conversionService); - - // Write the properties entity.doWithProperties(new PropertyHandler() { + @Override public void doWithPersistentProperty(CassandraPersistentProperty prop) { - Object propertyObj = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly); + Object value = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly); - if (propertyObj != null) { - if (prop.isIdProperty()) { - update.where(QueryBuilder.eq(prop.getColumnName(), propertyObj)); + if (prop.isCompositePrimaryKey()) { + writeInsertFromWrapper( + BeanWrapper., Object> create(value, conversionService), insert, + prop.getCompositePrimaryKeyEntity()); + return; + } + + if (value != null) { + insert.value(prop.getColumnName(), value); + } + } + }); + } + + protected void writeUpdateFromObject(final Object object, final Update update, CassandraPersistentEntity entity) { + writeUpdateFromWrapper(BeanWrapper., Object> create(object, conversionService), + update, entity); + } + + protected void writeUpdateFromWrapper(final BeanWrapper, Object> wrapper, + final Update update, final CassandraPersistentEntity entity) { + + entity.doWithProperties(new PropertyHandler() { + + @Override + public void doWithPersistentProperty(CassandraPersistentProperty prop) { + + Object value = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly); + + if (prop.isCompositePrimaryKey()) { + writeUpdateFromWrapper( + BeanWrapper., Object> create(value, conversionService), update, + prop.getCompositePrimaryKeyEntity()); + return; + } + + if (value != null) { + if (prop.isIdProperty() || entity.isCompositePrimaryKey()) { + update.where(QueryBuilder.eq(prop.getColumnName(), value)); } else { - update.with(QueryBuilder.set(prop.getColumnName(), propertyObj)); + update.with(QueryBuilder.set(prop.getColumnName(), value)); } } - } }); - } - private void writeDeleteWhereInternal(final Object objectToSave, final Where whereId, - CassandraPersistentEntity entity) { + protected void writeDeleteWhereFromObject(final Object object, final Where where, CassandraPersistentEntity entity) { + writeDeleteWhereFromWrapper( + BeanWrapper., Object> create(object, conversionService), where, entity); + } - final BeanWrapper, Object> wrapper = BeanWrapper.create(objectToSave, - conversionService); + protected void writeDeleteWhereFromWrapper(final BeanWrapper, Object> wrapper, + final Where where, CassandraPersistentEntity entity) { - // Write the properties - entity.doWithProperties(new PropertyHandler() { - @Override - public void doWithPersistentProperty(CassandraPersistentProperty prop) { + CassandraPersistentProperty idProperty = entity.getIdProperty(); + Object idValue = wrapper.getProperty(idProperty, idProperty.getType(), useFieldAccessOnly); - if (prop.isIdProperty()) { + if (idValue == null) { + String msg = String.format("no id value found in object {}", wrapper.getBean()); + log.error(msg); + throw new IllegalArgumentException(msg); + } - Object propertyObj = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly); - - if (propertyObj != null) { - whereId.and(QueryBuilder.eq(prop.getColumnName(), propertyObj)); - } - } - - } - }); + if (idProperty.isCompositePrimaryKey()) { + writeDeleteWhereFromWrapper( + BeanWrapper., Object> create(idValue, conversionService), where, + idProperty.getCompositePrimaryKeyEntity()); + return; + } + where.and(QueryBuilder.eq(idProperty.getColumnName(), idValue)); } @SuppressWarnings("unchecked") - private Class transformClassToBeanClassLoaderClass(Class entity) { + protected Class transformClassToBeanClassLoaderClass(Class entity) { try { return (Class) ClassUtils.forName(entity.getName(), beanClassLoader); } catch (ClassNotFoundException e) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java index f15171ff7..42ace67b9 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java @@ -6,7 +6,12 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cassandra.core.SessionCallback; +import org.springframework.cassandra.core.cql.generator.AlterTableCqlGenerator; import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator; +import org.springframework.cassandra.core.cql.generator.DropTableCqlGenerator; +import org.springframework.cassandra.core.keyspace.AlterTableSpecification; +import org.springframework.cassandra.core.keyspace.CreateTableSpecification; +import org.springframework.cassandra.core.keyspace.DropTableSpecification; import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.cassandra.convert.CassandraConverter; @@ -60,7 +65,9 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand @Override public void replaceTable(String tableName, Class entityClass, Map optionsByName) { - throw new UnsupportedOperationException("not yet implemented"); + + dropTable(tableName); + createTable(false, tableName, entityClass, optionsByName); } /** @@ -102,15 +109,7 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand log.info("Dropping table => " + tableName); - final String q = CqlUtils.dropTable(tableName); - log.info(q); - - execute(new SessionCallback() { - @Override - public ResultSet doInSession(Session s) { - return s.execute(q); - } - }); + execute(DropTableSpecification.dropTable(tableName)); } @Override diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java index db8a6c052..cdb608d30 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java @@ -30,7 +30,6 @@ import com.datastax.driver.core.querybuilder.Select; * @author Alex Shvid * @author David Webb * @author Matthew Adams - * */ public interface CassandraOperations extends CqlOperations { @@ -58,9 +57,10 @@ public interface CassandraOperations extends CqlOperations { * @param selectClass must not be {@literal null}, mapped entity type. * @return */ - List select(Select selectQuery, Class selectClass); + T selectOneById(Class selectClass, Object id); + /** * Execute query and convert ResultSet to the entity * @@ -72,13 +72,14 @@ public interface CassandraOperations extends CqlOperations { T selectOne(Select selectQuery, Class selectClass); + Long countById(Class clazz, Object id); + /** * Counts rows for given query * * @param selectQuery * @return */ - Long count(Select selectQuery); /** @@ -87,7 +88,6 @@ public interface CassandraOperations extends CqlOperations { * @param tableName * @return */ - Long count(String tableName); /** @@ -457,4 +457,8 @@ public interface CassandraOperations extends CqlOperations { * @return */ CassandraConverter getConverter(); + + void deleteById(Class clazz, Object id); + + List selectByIds(Class clazz, Iterable ids); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index 9898088f1..577e6fafd 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -16,12 +16,9 @@ package org.springframework.data.cassandra.core; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Set; import org.springframework.cassandra.core.CqlTemplate; import org.springframework.cassandra.core.QueryOptions; @@ -30,53 +27,48 @@ import org.springframework.dao.DataAccessException; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.cassandra.convert.CassandraConverter; -import org.springframework.data.cassandra.convert.MappingCassandraConverter; -import org.springframework.data.cassandra.exception.EntityWriterException; import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; -import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; -import org.springframework.data.cassandra.util.CqlUtils; +import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; +import org.springframework.data.convert.EntityWriter; +import org.springframework.data.mapping.PropertyHandler; +import org.springframework.data.mapping.model.BeanWrapper; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; import com.datastax.driver.core.Query; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Row; import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; import com.datastax.driver.core.querybuilder.Batch; +import com.datastax.driver.core.querybuilder.Clause; +import com.datastax.driver.core.querybuilder.Delete; +import com.datastax.driver.core.querybuilder.Delete.Where; +import com.datastax.driver.core.querybuilder.Insert; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.core.querybuilder.Update; /** - * The Cassandra Data Template is a convenience API for all Cassandra Operations using POJOs. For low level Cassandra - * Operations use the {@link CqlTemplate} + * The CassandraTemplate is a convenient API for all Cassandra operations using POJOs with their Spring Data Cassandra + * mapping information. For low-level Cassandra operation, see {@link CqlTemplate}. * * @author Alex Shvid * @author David Webb * @author Matthew T. Adams + * + * @see CqlTemplate */ public class CassandraTemplate extends CqlTemplate implements CassandraOperations { - /* - * List of iterable classes when testing POJOs for specific operations. - */ - public static final Collection ITERABLE_CLASSES; - static { - - Set iterableClasses = new HashSet(); - iterableClasses.add(List.class.getName()); - iterableClasses.add(Collection.class.getName()); - iterableClasses.add(Iterator.class.getName()); - - ITERABLE_CLASSES = Collections.unmodifiableCollection(iterableClasses); - - } - /* * Required elements for successful Template Operations. These can be set with the Constructor, or wired in * later. */ private CassandraConverter cassandraConverter; private CassandraMappingContext mappingContext; + private boolean useFieldAccessOnly = false; /** * Default Constructor for wiring in the required components later @@ -84,15 +76,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation public CassandraTemplate() { } - /** - * Constructor if only session is known at time of Template Creation - * - * @param session must not be {@literal null} - */ - public CassandraTemplate(Session session) { - this(session, new MappingCassandraConverter(new DefaultCassandraMappingContext())); - } - /** * Constructor if only session and converter are known at time of Template Creation * @@ -101,23 +84,60 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation */ public CassandraTemplate(Session session, CassandraConverter converter) { setSession(session); - this.cassandraConverter = converter; - this.mappingContext = cassandraConverter.getCassandraMappingContext(); + setConverter(converter); + } + + public void setConverter(CassandraConverter cassandraConverter) { + + Assert.notNull(cassandraConverter); + this.cassandraConverter = cassandraConverter; + mappingContext = cassandraConverter.getCassandraMappingContext(); + Assert.notNull(mappingContext); + } + + @Override + public CassandraConverter getConverter() { + return cassandraConverter; } public CassandraMappingContext getCassandraMappingContext() { return mappingContext; } + @Override + public void afterPropertiesSet() { + super.afterPropertiesSet(); + + Assert.notNull(cassandraConverter); + Assert.notNull(mappingContext); + } + + @Override + public Long countById(Class clazz, Object id) { + + Assert.notNull(clazz); + Assert.notNull(id); + + CassandraPersistentEntity entity = mappingContext.getPersistentEntity(clazz); + if (entity == null) { + throw new IllegalArgumentException(String.format("unknown persistent class [%s]", clazz.getName())); + } + + Select select = QueryBuilder.select().countAll().from(entity.getTableName()); + appendIdCriteria(select.where(), entity, id); + + return count(select); + } + @Override public Long count(Select selectQuery) { - return doSelectCount(selectQuery); + return selectCount(selectQuery); } @Override public Long count(String tableName) { Select select = QueryBuilder.select().countAll().from(tableName); - return doSelectCount(select); + return selectCount(select); } @Override @@ -136,7 +156,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation @Override public void delete(List entities, String tableName) { - delete(entities, tableName, null); } @@ -145,7 +164,21 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation Assert.notNull(entities); Assert.notEmpty(entities); Assert.notNull(tableName); - doBatchDelete(tableName, entities, options, false); + batchDelete(tableName, entities, options, false); + } + + @Override + public void deleteById(Class clazz, Object id) { + + Assert.notNull(clazz); + Assert.notNull(id); + + CassandraPersistentEntity entity = mappingContext.getPersistentEntity(clazz); + + Delete delete = QueryBuilder.delete().all().from(entity.getTableName()); + appendIdCriteria(delete.where(), entity, id); + + execute(delete.getQueryString()); } @Override @@ -171,7 +204,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation public void delete(T entity, String tableName, QueryOptions options) { Assert.notNull(entity); Assert.notNull(tableName); - doDelete(tableName, entity, options, false); + delete(tableName, entity, options, false); } @Override @@ -198,7 +231,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation Assert.notNull(entities); Assert.notEmpty(entities); Assert.notNull(tableName); - doBatchDelete(tableName, entities, options, true); + batchDelete(tableName, entities, options, true); } @Override @@ -224,7 +257,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation public void deleteAsynchronously(T entity, String tableName, QueryOptions options) { Assert.notNull(entity); Assert.notNull(tableName); - doDelete(tableName, entity, options, true); + delete(tableName, entity, options, true); } /** @@ -246,11 +279,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation return entity.getTableName(); } - @Override - public CassandraConverter getConverter() { - return cassandraConverter; - } - @Override public String getTableName(Class entityClass) { return determineTableName(entityClass); @@ -280,7 +308,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation Assert.notNull(entities); Assert.notEmpty(entities); Assert.notNull(tableName); - return doBatchInsert(tableName, entities, options, false); + return batchInsert(tableName, entities, options, false); } @Override @@ -307,7 +335,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation Assert.notNull(entity); Assert.notNull(tableName); ensureNotIterable(entity); - return doInsert(tableName, entity, options, false); + return insert(tableName, entity, options, false); } @Override @@ -334,7 +362,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation Assert.notNull(entities); Assert.notEmpty(entities); Assert.notNull(tableName); - return doBatchInsert(tableName, entities, options, true); + return batchInsert(tableName, entities, options, true); } @Override @@ -363,7 +391,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation ensureNotIterable(entity); - return doInsert(tableName, entity, options, true); + return insert(tableName, entity, options, true); } @Override @@ -373,7 +401,108 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation @Override public List select(String cql, Class selectClass) { - return doSelect(cql, new ReadRowCallback(cassandraConverter, selectClass)); + return select(cql, new ReadRowCallback(cassandraConverter, selectClass)); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public List selectByIds(Class clazz, Iterable ids) { + + CassandraPersistentEntity entity = mappingContext.getPersistentEntity(clazz); + if (entity == null) { + throw new IllegalArgumentException(String.format("unknown persistent entity class [%s]", clazz.getName())); + } + if (entity.getIdProperty().isCompositePrimaryKey()) { + throw new IllegalArgumentException(String.format( + "entity class [%s] uses a composite primary key class [%s] which this method can't support", clazz.getName(), + entity.getIdProperty().getCompositePrimaryKeyEntity().getType().getName())); + } + + List idList = null; + if (ids instanceof List) { + idList = (List) ids; + } else { + idList = new ArrayList(); + for (Object id : ids) { + idList.add(id); + } + } + + Select select = QueryBuilder.select().all().from(entity.getTableName()); + select.where(QueryBuilder.in(entity.getIdProperty().getColumnName(), idList.toArray())); + + return select(select, clazz); + } + + @Override + public T selectOneById(Class selectClass, Object id) { + + Assert.notNull(selectClass); + Assert.notNull(id); + + CassandraPersistentEntity entityClass = mappingContext.getPersistentEntity(selectClass); + if (entityClass == null) { + throw new IllegalArgumentException(String.format("unknown entity class [%s]", selectClass.getName())); + } + + Select select = QueryBuilder.select().all().from(entityClass.getTableName()); + appendIdCriteria(select.where(), entityClass, id); + + return selectOne(select, selectClass); + } + + protected interface ClauseCallback { + void onClause(Clause clause); + } + + protected void appendIdCriteria(final ClauseCallback clauseCallback, CassandraPersistentEntity entity, Object id) { + + CassandraPersistentProperty idProperty = entity.getIdProperty(); + + if (idProperty.isCompositePrimaryKey()) { + + CassandraPersistentEntity idEntity = idProperty.getCompositePrimaryKeyEntity(); + + final BeanWrapper, Object> idWrapper = BeanWrapper + ., Object> create(id, cassandraConverter.getConversionService()); + + idEntity.doWithProperties(new PropertyHandler() { + + @Override + public void doWithPersistentProperty(CassandraPersistentProperty p) { + + clauseCallback.onClause(QueryBuilder.eq(p.getColumnName(), + idWrapper.getProperty(p, p.getActualType(), useFieldAccessOnly))); + } + }); + + return; + } + + clauseCallback.onClause(QueryBuilder.eq(idProperty.getColumnName(), id)); + } + + protected void appendIdCriteria(final com.datastax.driver.core.querybuilder.Select.Where where, + CassandraPersistentEntity entity, Object id) { + + appendIdCriteria(new ClauseCallback() { + + @Override + public void onClause(Clause clause) { + where.and(clause); + } + }, entity, id); + } + + protected void appendIdCriteria(final Where where, CassandraPersistentEntity entity, Object id) { + + appendIdCriteria(new ClauseCallback() { + + @Override + public void onClause(Clause clause) { + where.and(clause); + } + }, entity, id); } @Override @@ -383,7 +512,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation @Override public T selectOne(String cql, Class selectClass) { - return doSelectOne(cql, new ReadRowCallback(cassandraConverter, selectClass)); + return selectOne(cql, new ReadRowCallback(cassandraConverter, selectClass)); } @Override @@ -410,7 +539,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation Assert.notNull(entities); Assert.notEmpty(entities); Assert.notNull(tableName); - return doBatchUpdate(tableName, entities, options, false); + return batchUpdate(tableName, entities, options, false); } @Override @@ -436,7 +565,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation public T update(T entity, String tableName, QueryOptions options) { Assert.notNull(entity); Assert.notNull(tableName); - return doUpdate(tableName, entity, options, false); + return update(tableName, entity, options, false); } @Override @@ -463,7 +592,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation Assert.notNull(entities); Assert.notEmpty(entities); Assert.notNull(tableName); - return doBatchUpdate(tableName, entities, options, true); + return batchUpdate(tableName, entities, options, true); } @Override @@ -490,7 +619,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation public T updateAsynchronously(T entity, String tableName, QueryOptions options) { Assert.notNull(entity); Assert.notNull(tableName); - return doUpdate(tableName, entity, options, true); + return update(tableName, entity, options, true); } /** @@ -505,12 +634,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation return null; } - /** - * @param query - * @param readRowCallback - * @return - */ - private List doSelect(final String query, ReadRowCallback readRowCallback) { + private List select(final String query, ReadRowCallback readRowCallback) { ResultSet resultSet = doExecute(new SessionCallback() { @@ -534,11 +658,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation return result; } - /** - * @param selectQuery - * @return - */ - private Long doSelectCount(final Select query) { + private Long selectCount(final Select query) { Long count = null; @@ -569,7 +689,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param readRowCallback * @return */ - private T doSelectOne(final String query, ReadRowCallback readRowCallback) { + private T selectOne(final String query, ReadRowCallback readRowCallback) { logger.info(query); @@ -607,36 +727,29 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param tableName * @param objectToRemove */ - protected void doBatchDelete(final String tableName, final List entities, final QueryOptions options, + protected void batchDelete(final String tableName, final List entities, final QueryOptions options, final boolean deleteAsynchronously) { Assert.notEmpty(entities); - try { + final Batch b = createDeleteBatchQuery(tableName, entities, options, cassandraConverter); + logger.info(b.toString()); - final Batch b = CqlUtils.toDeleteBatchQuery(tableName, entities, options, cassandraConverter); - logger.info(b.toString()); + doExecute(new SessionCallback() { - doExecute(new SessionCallback() { - - @Override - public Object doInSession(Session s) throws DataAccessException { - - if (deleteAsynchronously) { - s.executeAsync(b); - } else { - s.execute(b); - } - - return null; + @Override + public Object doInSession(Session s) throws DataAccessException { + if (deleteAsynchronously) { + s.executeAsync(b); + } else { + s.execute(b); } - }); - } catch (EntityWriterException e) { - throw getExceptionTranslator().translateExceptionIfPossible( - new RuntimeException("Failed to translate Object to Query", e)); - } + return null; + + } + }); } /** @@ -648,36 +761,29 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param insertAsychronously * @return */ - protected List doBatchInsert(final String tableName, final List entities, final QueryOptions options, + protected List batchInsert(final String tableName, final List entities, final QueryOptions options, final boolean insertAsychronously) { Assert.notEmpty(entities); - try { + final Batch b = createInsertBatchQuery(tableName, entities, options, cassandraConverter); + logger.info(b.getQueryString()); - final Batch b = CqlUtils.toInsertBatchQuery(tableName, entities, options, cassandraConverter); - logger.info(b.getQueryString()); + return doExecute(new SessionCallback>() { - return doExecute(new SessionCallback>() { - - @Override - public List doInSession(Session s) throws DataAccessException { - - if (insertAsychronously) { - s.executeAsync(b); - } else { - s.execute(b); - } - - return entities; + @Override + public List doInSession(Session s) throws DataAccessException { + if (insertAsychronously) { + s.executeAsync(b); + } else { + s.execute(b); } - }); - } catch (EntityWriterException e) { - throw getExceptionTranslator().translateExceptionIfPossible( - new RuntimeException("Failed to translate Object to Query", e)); - } + return entities; + + } + }); } /** @@ -689,36 +795,29 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param updateAsychronously * @return */ - protected List doBatchUpdate(final String tableName, final List entities, final QueryOptions options, + protected List batchUpdate(final String tableName, final List entities, final QueryOptions options, final boolean updateAsychronously) { Assert.notEmpty(entities); - try { + final Batch b = toUpdateBatchQuery(tableName, entities, options, cassandraConverter); + logger.info(b.toString()); - final Batch b = CqlUtils.toUpdateBatchQuery(tableName, entities, options, cassandraConverter); - logger.info(b.toString()); + return doExecute(new SessionCallback>() { - return doExecute(new SessionCallback>() { - - @Override - public List doInSession(Session s) throws DataAccessException { - - if (updateAsychronously) { - s.executeAsync(b); - } else { - s.execute(b); - } - - return entities; + @Override + public List doInSession(Session s) throws DataAccessException { + if (updateAsychronously) { + s.executeAsync(b); + } else { + s.execute(b); } - }); - } catch (EntityWriterException e) { - throw getExceptionTranslator().translateExceptionIfPossible( - new RuntimeException("Failed to translate Object to Query", e)); - } + return entities; + + } + }); } /** @@ -727,54 +826,26 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param tableName * @param objectToRemove */ - protected void doDelete(final String tableName, final T objectToRemove, final QueryOptions options, + protected void delete(final String tableName, final T objectToRemove, final QueryOptions options, final boolean deleteAsynchronously) { - try { + final Query q = createDeleteQuery(tableName, objectToRemove, options, cassandraConverter); + logger.info(q.toString()); - final Query q = CqlUtils.toDeleteQuery(tableName, objectToRemove, options, cassandraConverter); - logger.info(q.toString()); + doExecute(new SessionCallback() { - doExecute(new SessionCallback() { - - @Override - public Object doInSession(Session s) throws DataAccessException { - - if (deleteAsynchronously) { - s.executeAsync(q); - } else { - s.execute(q); - } - - return null; + @Override + public Object doInSession(Session s) throws DataAccessException { + if (deleteAsynchronously) { + s.executeAsync(q); + } else { + s.execute(q); } - }); - } catch (EntityWriterException e) { - throw getExceptionTranslator().translateExceptionIfPossible( - new RuntimeException("Failed to translate Object to Query", e)); - } - } - - /** - * Execute a command at the Session Level - * - * @param callback - * @return - */ - @Override - protected T doExecute(SessionCallback callback) { - - Assert.notNull(callback); - - try { - - return callback.doInSession(getSession()); - - } catch (DataAccessException e) { - throw translateExceptionIfPossible(e); - } + return null; + } + }); } /** @@ -783,42 +854,34 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param tableName * @param entity */ - protected T doInsert(final String tableName, final T entity, final QueryOptions options, + protected T insert(final String tableName, final T entity, final QueryOptions options, final boolean insertAsychronously) { - try { + final Query q = createInsertQuery(tableName, entity, options, cassandraConverter); - final Query q = CqlUtils.toInsertQuery(tableName, entity, options, cassandraConverter); - - logger.info(q.toString()); - if (q.getConsistencyLevel() != null) { - logger.info(q.getConsistencyLevel().name()); - } - if (q.getRetryPolicy() != null) { - logger.info(q.getRetryPolicy().toString()); - } - - return doExecute(new SessionCallback() { - - @Override - public T doInSession(Session s) throws DataAccessException { - - if (insertAsychronously) { - s.executeAsync(q); - } else { - s.execute(q); - } - - return entity; - - } - }); - - } catch (EntityWriterException e) { - throw getExceptionTranslator().translateExceptionIfPossible( - new RuntimeException("Failed to translate Object to Query", e)); + logger.info(q.toString()); + if (q.getConsistencyLevel() != null) { + logger.info(q.getConsistencyLevel().name()); + } + if (q.getRetryPolicy() != null) { + logger.info(q.getRetryPolicy().toString()); } + return doExecute(new SessionCallback() { + + @Override + public T doInSession(Session s) throws DataAccessException { + + if (insertAsychronously) { + s.executeAsync(q); + } else { + s.execute(q); + } + + return entity; + + } + }); } /** @@ -830,35 +893,27 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param updateAsychronously * @return */ - protected T doUpdate(final String tableName, final T entity, final QueryOptions options, + protected T update(final String tableName, final T entity, final QueryOptions options, final boolean updateAsychronously) { - try { + final Query q = toUpdateQuery(tableName, entity, options, cassandraConverter); + logger.info(q.toString()); - final Query q = CqlUtils.toUpdateQuery(tableName, entity, options, cassandraConverter); - logger.info(q.toString()); + return doExecute(new SessionCallback() { - return doExecute(new SessionCallback() { - - @Override - public T doInSession(Session s) throws DataAccessException { - - if (updateAsychronously) { - s.executeAsync(q); - } else { - s.execute(q); - } - - return entity; + @Override + public T doInSession(Session s) throws DataAccessException { + if (updateAsychronously) { + s.executeAsync(q); + } else { + s.execute(q); } - }); - } catch (EntityWriterException e) { - throw getExceptionTranslator().translateExceptionIfPossible( - new RuntimeException("Failed to translate Object to Query", e)); - } + return entity; + } + }); } /** @@ -867,10 +922,197 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param o */ protected void ensureNotIterable(Object o) { - if (null != o) { - if (o.getClass().isArray() || ITERABLE_CLASSES.contains(o.getClass().getName())) { - throw new IllegalArgumentException("Cannot use a collection here."); - } + + if (null == o) { + return; + } + + if (o.getClass().isArray() || (o instanceof Iterable) || (o instanceof Iterator)) { + throw new IllegalArgumentException("cannot use a multivalued object here."); } } + + /** + * Generates a Query Object for an insert + * + * @param tableName + * @param objectToSave + * @param entity + * @param optionsByName + * + * @return The Query object to run with session.execute(); + */ + public static Query createInsertQuery(String tableName, final Object objectToSave, QueryOptions options, + EntityWriter entityWriter) { + + final Insert q = QueryBuilder.insertInto(tableName); + + /* + * Write properties + */ + entityWriter.write(objectToSave, q); + + /* + * Add Query Options + */ + CqlTemplate.addQueryOptions(q, options); + + /* + * Add TTL to Insert object + */ + if (options != null && options.getTtl() != null) { + q.using(QueryBuilder.ttl(options.getTtl())); + } + + return q; + + } + + /** + * Generates a Query Object for an Update + * + * @param tableName + * @param objectToSave + * @param entity + * @param optionsByName + * + * @return The Query object to run with session.execute(); + */ + public static Query toUpdateQuery(String tableName, final Object objectToSave, QueryOptions options, + EntityWriter entityWriter) { + + final Update q = QueryBuilder.update(tableName); + + /* + * Write properties + */ + entityWriter.write(objectToSave, q); + + /* + * Add Query Options + */ + CqlTemplate.addQueryOptions(q, options); + + /* + * Add TTL to Insert object + */ + if (options != null && options.getTtl() != null) { + q.using(QueryBuilder.ttl(options.getTtl())); + } + + return q; + + } + + /** + * Generates a Batch Object for multiple Updates + * + * @param tableName + * @param objectsToSave + * @param entity + * @param optionsByName + * + * @return The Query object to run with session.execute(); + */ + public static Batch toUpdateBatchQuery(final String tableName, final List objectsToSave, QueryOptions options, + EntityWriter entityWriter) { + + /* + * Return variable is a Batch statement + */ + final Batch b = QueryBuilder.batch(); + + for (final T objectToSave : objectsToSave) { + + b.add((Statement) toUpdateQuery(tableName, objectToSave, options, entityWriter)); + + } + + /* + * Add Query Options + */ + CqlTemplate.addQueryOptions(b, options); + + return b; + + } + + /** + * Generates a Batch Object for multiple inserts + * + * @param tableName + * @param entities + * @param entity + * @param optionsByName + * + * @return The Query object to run with session.execute(); + */ + public static Batch createInsertBatchQuery(final String tableName, final List entities, QueryOptions options, + EntityWriter entityWriter) { + + Batch batch = QueryBuilder.batch(); + + for (T entity : entities) { + batch.add((Statement) createInsertQuery(tableName, entity, options, entityWriter)); + } + + CqlTemplate.addQueryOptions(batch, options); + + return batch; + } + + /** + * Create a Delete Query Object from an annotated POJO + * + * @param tableName + * @param object + * @param entity + * @param optionsByName + * @return + */ + public static Query createDeleteQuery(String tableName, final Object object, QueryOptions options, + EntityWriter entityWriter) { + + Delete.Selection ds = QueryBuilder.delete(); + Delete q = ds.from(tableName); + Where w = q.where(); + + entityWriter.write(object, w); + + CqlTemplate.addQueryOptions(q, options); + + return q; + } + + /** + * Create a Batch Query object for multiple deletes. + * + * @param tableName + * @param entities + * @param entity + * @param optionsByName + * + * @return + */ + public static Batch createDeleteBatchQuery(String tableName, List entities, QueryOptions options, + EntityWriter entityWriter) { + + Batch batch = QueryBuilder.batch(); + + for (T entity : entities) { + batch.add((Statement) createDeleteQuery(tableName, entity, options, entityWriter)); + } + + CqlTemplate.addQueryOptions(batch, options); + + return batch; + } + + public boolean getUseFieldAccessOnly() { + return useFieldAccessOnly; + } + + public void setUseFieldAccessOnly(boolean useFieldAccessOnly) { + this.useFieldAccessOnly = useFieldAccessOnly; + } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/exception/EntityWriterException.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/exception/EntityWriterException.java deleted file mode 100644 index fff15ad98..000000000 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/exception/EntityWriterException.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2011-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.cassandra.exception; - -/** - * Exception to handle failing to write a PersistedEntity to a CQL String or Query object - * - * @author David Webb - * - */ -public class EntityWriterException extends Exception { - - private static final long serialVersionUID = -3068204776019978031L; - - /** - * @param message - */ - public EntityWriterException(String message) { - super(message); - } - - /** - * @param cause - */ - public EntityWriterException(Throwable cause) { - super(cause); - } - - /** - * @param message - * @param cause - */ - public EntityWriterException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java index c0965a4cd..863e8f838 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java @@ -15,6 +15,9 @@ */ package org.springframework.data.cassandra.mapping; +import java.util.ArrayList; +import java.util.List; + import org.springframework.beans.BeansException; import org.springframework.cassandra.support.exception.UnsupportedCassandraOperationException; import org.springframework.context.ApplicationContext; @@ -24,6 +27,7 @@ import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.data.cassandra.util.CassandraNamingUtils; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.AssociationHandler; +import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.util.TypeInformation; import org.springframework.expression.Expression; @@ -34,8 +38,7 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * Cassandra specific {@link BasicPersistentEntity} implementation that adds Cassandra specific metadata such as the - * table name. + * Cassandra specific {@link BasicPersistentEntity} implementation that adds Cassandra specific metadata. * * @author Alex Shvid * @author Matthew T. Adams @@ -43,10 +46,14 @@ import org.springframework.util.StringUtils; public class BasicCassandraPersistentEntity extends BasicPersistentEntity implements CassandraPersistentEntity, ApplicationContextAware { - private String tableName; - private final SpelExpressionParser spelParser; - private final StandardEvaluationContext spelContext; - private final Class type; + protected String tableName; + protected CassandraMappingContext mappingContext; + protected final SpelExpressionParser spelParser; + protected final StandardEvaluationContext spelContext; + + public BasicCassandraPersistentEntity(TypeInformation typeInformation) { + this(typeInformation, null); + } /** * Creates a new {@link BasicCassandraPersistentEntity} with the given {@link TypeInformation}. Will default the table @@ -54,23 +61,22 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity typeInformation) { + public BasicCassandraPersistentEntity(TypeInformation typeInformation, CassandraMappingContext mappingContext) { - super(typeInformation, DefaultCassandraPersistentPropertyColumnComparator.IT); + super(typeInformation, CassandraPersistentPropertyComparator.IT); this.spelParser = new SpelExpressionParser(); this.spelContext = new StandardEvaluationContext(); - - this.type = typeInformation.getType(); + this.mappingContext = mappingContext; determineTableName(); } protected void determineTableName() { - Table anno = type.getAnnotation(Table.class); + Table anno = getType().getAnnotation(Table.class); this.tableName = anno != null && StringUtils.hasText(anno.value()) ? anno.value() : CassandraNamingUtils - .getPreferredTableName(type); + .getPreferredTableName(getType()); } @Override @@ -102,4 +108,46 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity getCompositePrimaryKeyProperties() { + + final List properties = new ArrayList(); + + if (!isCompositePrimaryKey()) { + throw new IllegalStateException(String.format("[%s] does not represent a composite primary key class", this + .getType().getName())); + } + + addCompositePrimaryKeyProperties(this, properties); + + return properties; + } + + protected void addCompositePrimaryKeyProperties(CassandraPersistentEntity compositePrimaryKeyEntity, + final List properties) { + + compositePrimaryKeyEntity.doWithProperties(new PropertyHandler() { + + @Override + public void doWithPersistentProperty(CassandraPersistentProperty p) { + + if (p.isCompositePrimaryKey()) { + addCompositePrimaryKeyProperties(p.getCompositePrimaryKeyEntity(), properties); + } else { + properties.add(p); + } + } + }); + } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentProperty.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentProperty.java index 651ab572b..b1e37bc67 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentProperty.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentProperty.java @@ -17,6 +17,7 @@ package org.springframework.data.cassandra.mapping; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -24,6 +25,7 @@ import org.springframework.cassandra.core.Ordering; import org.springframework.cassandra.core.PrimaryKeyType; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.mapping.Association; +import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; @@ -49,15 +51,20 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP */ public BasicCassandraPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, CassandraPersistentEntity owner, CassandraSimpleTypeHolder simpleTypeHolder) { + super(field, propertyDescriptor, owner, simpleTypeHolder); } + @Override + public CassandraPersistentEntity getOwner() { + return (CassandraPersistentEntity) super.getOwner(); + } + @Override public boolean isCompositePrimaryKey() { return getField().getType().isAnnotationPresent(PrimaryKeyClass.class); } - @Override public Class getCompositePrimaryKeyType() { if (!isCompositePrimaryKey()) { return null; @@ -67,31 +74,23 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP } @Override - public CassandraPersistentEntity getCompositePrimaryKeyEntity() { + public TypeInformation getCompositePrimaryKeyTypeInformation() { if (!isCompositePrimaryKey()) { return null; } - return (CassandraPersistentEntity) ClassTypeInformation.from(getCompositePrimaryKeyType()); + return ClassTypeInformation.from(getCompositePrimaryKeyType()); } @Override public String getColumnName() { - // first check @Column annotation - Column column = findAnnotation(Column.class); - if (column != null && StringUtils.hasText(column.value())) { - return column.value(); + List columnNames = getColumnNames(); + if (columnNames.size() != 1) { + throw new IllegalStateException("property does not have a single column mapping"); } - // else check @PrimaryKeyColumn annotation - PrimaryKeyColumn pk = findAnnotation(PrimaryKeyColumn.class); - if (pk != null && StringUtils.hasText(pk.name())) { - return pk.name(); - } - - // else default - return field.getName().toLowerCase(); // TODO: replace with naming strategy class + return columnNames.get(0); } @Override @@ -199,11 +198,6 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP return isAnnotationPresent(PrimaryKeyColumn.class); } - @Override - protected Association createAssociation() { - return new Association(this, null); - } - protected DataType getDataTypeFor(DataType.Name typeName) { DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(typeName); if (dataType == null) { @@ -230,4 +224,79 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP + this.getName() + "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName()); } } + + @Override + public List getColumnNames() { + + final List columnNames = new ArrayList(); + + if (isCompositePrimaryKey()) { + addCompositePrimaryKeyColumnNames(getCompositePrimaryKeyEntity(), columnNames); + return columnNames; + } + + // else not a composite primary key property -- first check @Column annotation + Column column = findAnnotation(Column.class); + if (column != null && StringUtils.hasText(column.value())) { + columnNames.add(column.value()); + return columnNames; + } + + // else check @PrimaryKeyColumn annotation + PrimaryKeyColumn pk = findAnnotation(PrimaryKeyColumn.class); + if (pk != null && StringUtils.hasText(pk.name())) { + columnNames.add(pk.name()); + return columnNames; + } + + // else default + columnNames.add(field.getName().toLowerCase()); // TODO: replace with naming strategy class + return columnNames; + } + + protected void addCompositePrimaryKeyColumnNames(CassandraPersistentEntity compositePrimaryKeyEntity, + final List columnNames) { + + compositePrimaryKeyEntity.doWithProperties(new PropertyHandler() { + + @Override + public void doWithPersistentProperty(CassandraPersistentProperty p) { + if (p.isCompositePrimaryKey()) { + addCompositePrimaryKeyColumnNames(p.getCompositePrimaryKeyEntity(), columnNames); + } else { + columnNames.add(p.getColumnName()); + } + } + }); + } + + @Override + public List getCompositePrimaryKeyProperties() { + + if (!isCompositePrimaryKey()) { + throw new IllegalStateException(String.format("[%s] does not represent a composite primary key property", + getField())); + } + + return getCompositePrimaryKeyEntity().getCompositePrimaryKeyProperties(); + } + + @Override + public CassandraPersistentEntity getCompositePrimaryKeyEntity() { + CassandraMappingContext mappingContext = getOwner().getMappingContext(); + if (mappingContext == null) { + throw new IllegalStateException("need CassandraMappingContext"); + } + return mappingContext.getPersistentEntity(getCompositePrimaryKeyTypeInformation()); + } + + @Override + public Association getAssociation() { + throw new UnsupportedOperationException("Cassandra does not support associations"); + } + + @Override + protected Association createAssociation() { + return new Association(this, null); + } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CachingCassandraPersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CachingCassandraPersistentEntity.java new file mode 100644 index 000000000..eec3d79a5 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CachingCassandraPersistentEntity.java @@ -0,0 +1,64 @@ +package org.springframework.data.cassandra.mapping; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.data.util.TypeInformation; + +public class CachingCassandraPersistentEntity extends BasicCassandraPersistentEntity { + + protected String tableName; + protected String name; + protected Boolean isCompositePrimaryKey; + protected List compositePrimaryKeyProperties; + protected Map properties = new HashMap(); + + public CachingCassandraPersistentEntity(TypeInformation typeInformation) { + super(typeInformation); + } + + public CachingCassandraPersistentEntity(TypeInformation typeInformation, CassandraMappingContext mappingContext) { + super(typeInformation, mappingContext); + } + + @Override + public String getTableName() { + if (tableName == null) { + tableName = super.getTableName(); + } + return tableName; + } + + @Override + public String getName() { + if (name == null) { + name = super.getName(); + } + return name; + } + + @Override + public boolean isCompositePrimaryKey() { + if (isCompositePrimaryKey == null) { + isCompositePrimaryKey = super.isCompositePrimaryKey(); + } + return isCompositePrimaryKey; + } + + @Override + public List getCompositePrimaryKeyProperties() { + if (compositePrimaryKeyProperties == null) { + compositePrimaryKeyProperties = super.getCompositePrimaryKeyProperties(); + } + return compositePrimaryKeyProperties; + } + + @Override + public CassandraPersistentProperty getPersistentProperty(String name) { + if (properties.get(name) == null) { + properties.put(name, super.getPersistentProperty(name)); + } + return properties.get(name); + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CachingCassandraPersistentProperty.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CachingCassandraPersistentProperty.java index 06ddc4aaa..4ac138f84 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CachingCassandraPersistentProperty.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CachingCassandraPersistentProperty.java @@ -17,8 +17,10 @@ package org.springframework.data.cassandra.mapping; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; +import java.util.List; import org.springframework.cassandra.core.Ordering; +import org.springframework.data.util.TypeInformation; import com.datastax.driver.core.DataType; @@ -37,11 +39,12 @@ public class CachingCassandraPersistentProperty extends BasicCassandraPersistent private Boolean isClusterKeyColumn; private Boolean isPrimaryKeyColumn; private String columnName; + private List columnNames; private Ordering ordering; private boolean orderingCached = false; private DataType dataType; private Class compositePrimaryKeyType; - private CassandraPersistentEntity compositePrimaryKeyEntity; + private TypeInformation compositePrimaryKeyTypeInformation; /** * Creates a new {@link CachingCassandraPersistentProperty}. @@ -52,12 +55,12 @@ public class CachingCassandraPersistentProperty extends BasicCassandraPersistent } @Override - public CassandraPersistentEntity getCompositePrimaryKeyEntity() { + public TypeInformation getCompositePrimaryKeyTypeInformation() { - if (compositePrimaryKeyEntity == null) { - compositePrimaryKeyEntity = super.getCompositePrimaryKeyEntity(); + if (compositePrimaryKeyTypeInformation == null) { + compositePrimaryKeyTypeInformation = super.getCompositePrimaryKeyTypeInformation(); } - return compositePrimaryKeyEntity; + return compositePrimaryKeyTypeInformation; } @Override @@ -150,4 +153,12 @@ public class CachingCassandraPersistentProperty extends BasicCassandraPersistent } return isPartitionKeyColumn; } + + @Override + public List getColumnNames() { + if (columnNames == null) { + columnNames = super.getColumnNames(); + } + return columnNames; + } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraColumnAnnotationComparator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraColumnAnnotationComparator.java new file mode 100644 index 000000000..3cb8f1836 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraColumnAnnotationComparator.java @@ -0,0 +1,21 @@ +package org.springframework.data.cassandra.mapping; + +import java.util.Comparator; + +/** + * {@link Comparator} implementation that uses {@link Column#value()}. + * + * @author Matthew T. Adams + */ +public enum CassandraColumnAnnotationComparator implements Comparator { + + /** + * The sole instance of this class. + */ + IT; + + @Override + public int compare(Column left, Column right) { + return left.value().compareTo(right.value()); + } +} \ No newline at end of file diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPersistentEntity.java index 3c34d1085..d6d60e0d6 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPersistentEntity.java @@ -15,6 +15,9 @@ */ package org.springframework.data.cassandra.mapping; +import java.util.List; + +import org.springframework.context.ApplicationContextAware; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.model.MutablePersistentEntity; @@ -24,7 +27,15 @@ import org.springframework.data.mapping.model.MutablePersistentEntity; * @author Alex Shvid * @author Matthew T. Adams */ -public interface CassandraPersistentEntity extends MutablePersistentEntity { +public interface CassandraPersistentEntity extends MutablePersistentEntity, + ApplicationContextAware { + + /** + * Returns whether this entity represents a composite primary key. + */ + boolean isCompositePrimaryKey(); + + List getCompositePrimaryKeyProperties(); /** * Returns the table name to which the entity shall be persisted. @@ -37,4 +48,6 @@ public interface CassandraPersistentEntity extends MutablePersistentEntity getCompositePrimaryKeyType(); - /** * Returns a {@link CassandraPersistentEntity} representing the composite primary key class of this entity, or null if * this class does not use a composite primary key. @@ -46,22 +43,42 @@ public interface CassandraPersistentProperty extends PersistentProperty getCompositePrimaryKeyEntity(); /** - * The name of the column to which a property is persisted. + * Returns a {@link TypeInformation} representing the type of the composite primary key class of this entity, or null + * if this class does not use a composite primary key. + */ + TypeInformation getCompositePrimaryKeyTypeInformation(); + + /** + * Gets the list of composite primary key properties that this composite primary key field is a placeholder for. + */ + List getCompositePrimaryKeyProperties(); + + /** + * The name of the single column to which the property is persisted. This is a convenience method when the caller + * knows that the property is mapped to a single column. Throws {@link IllegalStateException} if this property is + * mapped to multiple columns. */ String getColumnName(); /** - * The ordering for the column. Valid only for clustered columns. + * The names of the columns to which the property is persisted if this is a composite primary key property. Never + * returns null. + */ + List getColumnNames(); + + /** + * The ordering (ascending or descending) for the column. Valid only for primary key columns; returns null for + * non-primary key columns. */ Ordering getPrimaryKeyOrdering(); /** - * The column's data type. + * The column's data type. Not valid for a composite primary key, in which case this method returns null. */ DataType getDataType(); /** - * Whether the property has secondary index on this column. + * Whether the property has a secondary index on this column. */ boolean isIndexed(); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPersistentPropertyComparator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPersistentPropertyComparator.java new file mode 100644 index 000000000..9b52d6b90 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPersistentPropertyComparator.java @@ -0,0 +1,87 @@ +package org.springframework.data.cassandra.mapping; + +import java.lang.reflect.Field; +import java.util.Comparator; + +/** + * {@link Comparator} implementation that orders {@link CassandraPersistentProperty} instances. + *

+ * Composite primary key properties and primary key properties sort before non-primary key properties. + * + * @author Alex Shvid + * @author Matthew T. Adams + */ +public enum CassandraPersistentPropertyComparator implements Comparator { + + /** + * The sole instance of this class. + */ + IT; + + @Override + public int compare(CassandraPersistentProperty left, CassandraPersistentProperty right) { + + if (left != null && right == null) { + return -1; + } + if (left == null && right != null) { + return 1; + } + if (left == null && right == null) { + return 0; + } + + if (left.equals(right)) { + return 0; + } + + boolean leftIsCompositePrimaryKey = left.isCompositePrimaryKey(); + boolean rightIsCompositePrimaryKey = right.isCompositePrimaryKey(); + + if (leftIsCompositePrimaryKey && rightIsCompositePrimaryKey) { + return 0; + } + + boolean leftIsPrimaryKey = left.isPrimaryKeyColumn(); + boolean rightIsPrimaryKey = right.isPrimaryKeyColumn(); + + Field leftField = left.getField(); + Field rightField = right.getField(); + + if (leftIsPrimaryKey && rightIsPrimaryKey) { + return CassandraPrimaryKeyColumnAnnotationComparator.IT.compare(leftField.getAnnotation(PrimaryKeyColumn.class), + rightField.getAnnotation(PrimaryKeyColumn.class)); + } + + boolean leftIsKey = leftIsCompositePrimaryKey || leftIsPrimaryKey; + boolean rightIsKey = rightIsCompositePrimaryKey || rightIsPrimaryKey; + + if (leftIsKey && !rightIsKey) { + return 1; + } + + if (!leftIsKey && rightIsKey) { + return -1; + } + + // else, neither property is a composite primary key nor a primary key; compare @Column annotations + + Column leftColumn = leftField.getAnnotation(Column.class); + Column rightColumn = rightField.getAnnotation(Column.class); + + if (leftColumn == null && rightColumn == null) { + return leftField.getName().compareTo(rightField.getName()); + } + + if (leftColumn != null && rightColumn != null) { + return CassandraColumnAnnotationComparator.IT.compare(leftColumn, rightColumn); + } + + if (leftColumn != null && rightColumn == null) { + return leftColumn.value().compareTo(rightField.getName()); + } + + // else leftColumn == null && rightColumn != null) + return leftField.getName().compareTo(rightColumn.value()); + } +} \ No newline at end of file diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraPrimaryKeyColumnComparator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPrimaryKeyColumnAnnotationComparator.java similarity index 69% rename from spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraPrimaryKeyColumnComparator.java rename to spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPrimaryKeyColumnAnnotationComparator.java index db70c908e..96a46f9d4 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraPrimaryKeyColumnComparator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraPrimaryKeyColumnAnnotationComparator.java @@ -19,7 +19,7 @@ import org.springframework.cassandra.core.PrimaryKeyType; * * @author Matthew T. Adams */ -public enum DefaultCassandraPrimaryKeyColumnComparator implements Comparator { +public enum CassandraPrimaryKeyColumnAnnotationComparator implements Comparator { /** * The sole instance of this class. @@ -27,23 +27,23 @@ public enum DefaultCassandraPrimaryKeyColumnComparator implements Comparator CassandraPersistentEntity createPersistentEntity(TypeInformation typeInformation) { - BasicCassandraPersistentEntity entity = new BasicCassandraPersistentEntity(typeInformation); + CassandraPersistentEntity entity = new CachingCassandraPersistentEntity(typeInformation, this); if (context != null) { entity.setApplicationContext(context); @@ -104,9 +106,7 @@ public class DefaultCassandraMappingContext extends Assert.notNull(entity); - final CreateTableSpecification spec = new CreateTableSpecification(); - - spec.name(entity.getTableName()); + final CreateTableSpecification spec = createTable().name(entity.getTableName()); entity.doWithProperties(new PropertyHandler() { @@ -124,7 +124,7 @@ public class DefaultCassandraMappingContext extends if (pkProp.isPartitionKeyColumn()) { spec.partitionKeyColumn(pkProp.getColumnName(), pkProp.getDataType()); - } else { + } else { // it's a cluster column spec.clusteredKeyColumn(pkProp.getColumnName(), pkProp.getDataType(), pkProp.getPrimaryKeyOrdering()); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraPersistentPropertyColumnComparator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraPersistentPropertyColumnComparator.java deleted file mode 100644 index 8d83318fc..000000000 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraPersistentPropertyColumnComparator.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.springframework.data.cassandra.mapping; - -import java.util.Comparator; - -/** - * {@link Comparator} implementation that uses the {@link CassandraPersistentProperty}'s column name for ordering. - * - * @author Alex Shvid - * @author Matthew T. Adams - */ -public enum DefaultCassandraPersistentPropertyColumnComparator implements Comparator { - - /** - * The sole instance of this class. - */ - IT; - - @Override - public int compare(CassandraPersistentProperty o1, CassandraPersistentProperty o2) { - return o1.getColumnName().compareTo(o2.getColumnName()); - } -} \ No newline at end of file diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/EntityMapping.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/EntityMapping.java similarity index 72% rename from spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/EntityMapping.java rename to spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/EntityMapping.java index 9431751b9..0894f42e8 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/EntityMapping.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/EntityMapping.java @@ -1,4 +1,7 @@ -package org.springframework.data.cassandra.config; +package org.springframework.data.cassandra.mapping; + +import java.util.HashMap; +import java.util.Map; /** * Mapping information for an individual entity class. @@ -7,9 +10,21 @@ package org.springframework.data.cassandra.config; */ public class EntityMapping { + /** + * The name of the entity's class. + */ protected String entityClassName; + + /** + * The name of the table to which the entity is mapped. + */ protected String tableName; + /** + * The {@link PropertyMapping}s for each persistent property, keyed on property name. + */ + protected Map propertyMappings = new HashMap(); + public EntityMapping(String entityClassName, String tableName) { setEntityClassName(entityClassName); setTableName(tableName); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/Mapping.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/Mapping.java similarity index 95% rename from spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/Mapping.java rename to spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/Mapping.java index 87b9f5254..bb0779185 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/Mapping.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/Mapping.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.config; +package org.springframework.data.cassandra.mapping; import java.util.Collections; import java.util.HashSet; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PropertyMapping.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PropertyMapping.java new file mode 100644 index 000000000..a47c79c3d --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PropertyMapping.java @@ -0,0 +1,38 @@ +package org.springframework.data.cassandra.mapping; + +import org.springframework.util.Assert; + +/** + * Mapping between a persistent entity's property and its column. + * + * @author Matthew T. Adams + */ +public class PropertyMapping { + + protected String propertyName; + protected String columnName; + + public PropertyMapping(String propertyName, String columnName) { + + setPropertyName(propertyName); + setColumnName(columnName); + } + + public String getPropertyName() { + return propertyName; + } + + protected void setPropertyName(String propertyName) { + Assert.notNull(propertyName); + this.propertyName = propertyName; + } + + public String getColumnName() { + return columnName; + } + + protected void setColumnName(String columnName) { + Assert.notNull(columnName); + this.columnName = columnName; + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/CassandraRepository.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/CassandraRepository.java index e8ed41547..5b5c97005 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/CassandraRepository.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/CassandraRepository.java @@ -16,7 +16,6 @@ package org.springframework.data.cassandra.repository; import java.io.Serializable; -import java.util.List; import org.springframework.data.repository.CrudRepository; @@ -24,9 +23,7 @@ import org.springframework.data.repository.CrudRepository; * Cassandra-specific extension of the {@link CrudRepository} interface. * * @author Alex Shvid + * @author Matthew T. Adams */ public interface CassandraRepository extends CrudRepository { - - List findByPartitionKey(ID id); - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraEntityInformation.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraEntityInformation.java index 528defa6d..bc0c37de1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraEntityInformation.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraEntityInformation.java @@ -33,12 +33,4 @@ public interface CassandraEntityInformation extends * @return */ String getTableName(); - - /** - * Returns the column that the id will be persisted to. - * - * @return - */ - String getIdColumn(); - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java index c3385ad4e..5e53f5fa9 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java @@ -59,10 +59,6 @@ public class MappingCassandraEntityInformation exten this.customTableName = customTableName; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.support.EntityInformation#getId(java.lang.Object) - */ @SuppressWarnings("unchecked") @Override public ID getId(T entity) { @@ -80,28 +76,14 @@ public class MappingCassandraEntityInformation exten } } - /* (non-Javadoc) - * @see org.springframework.data.repository.support.EntityInformation#getIdType() - */ @SuppressWarnings("unchecked") @Override public Class getIdType() { return (Class) entityMetadata.getIdProperty().getType(); } - /* (non-Javadoc) - * @see org.springframework.data.mongodb.repository.CassandraEntityInformation#getTableName() - */ @Override public String getTableName() { return customTableName == null ? entityMetadata.getTableName() : customTableName; } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.repository.CassandraEntityInformation#getIdColumn() - */ - public String getIdColumn() { - return entityMetadata.getIdProperty().getName(); - } - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java index fb2269b2d..da529aa6e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java @@ -31,6 +31,7 @@ import com.datastax.driver.core.querybuilder.Clause; import com.datastax.driver.core.querybuilder.Delete; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.core.querybuilder.Select.Where; /** * Repository base implementation for Cassandra. @@ -41,8 +42,8 @@ import com.datastax.driver.core.querybuilder.Select; public class SimpleCassandraRepository implements CassandraRepository { - private final CassandraTemplate cassandraTemplate; - private final CassandraEntityInformation entityInformation; + protected final CassandraTemplate cassandraTemplate; + protected final CassandraEntityInformation entityInformation; /** * Creates a new {@link SimpleCassandraRepository} for the given {@link CassandraEntityInformation} and @@ -51,8 +52,7 @@ public class SimpleCassandraRepository implements Ca * @param metadata must not be {@literal null}. * @param template must not be {@literal null}. */ - public SimpleCassandraRepository(CassandraEntityInformation metadata, - CassandraTemplate cassandraTemplate) { + public SimpleCassandraRepository(CassandraEntityInformation metadata, CassandraTemplate cassandraTemplate) { Assert.notNull(cassandraTemplate); Assert.notNull(metadata); @@ -65,6 +65,7 @@ public class SimpleCassandraRepository implements Ca * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object) */ + @Override public S save(S entity) { Assert.notNull(entity, "Entity must not be null!"); @@ -76,6 +77,7 @@ public class SimpleCassandraRepository implements Ca * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable) */ + @Override public List save(Iterable entities) { Assert.notNull(entities, "The given Iterable of entities not be null!"); @@ -90,78 +92,31 @@ public class SimpleCassandraRepository implements Ca return result; } - private Clause getIdClause(ID id) { - Clause clause = QueryBuilder.eq(entityInformation.getIdColumn(), id); - return clause; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable) - */ - public T findOne(ID id) { - Assert.notNull(id, "The given id must not be null!"); - - Select select = QueryBuilder.select().all().from(entityInformation.getTableName()); - select.where(getIdClause(id)); - - return cassandraTemplate.selectOne(select, entityInformation.getJavaType()); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.repository.CassandraRepository#findByPartitionKey(java.io.Serializable) - */ @Override - public List findByPartitionKey(ID id) { - Assert.notNull(id, "The given id must not be null!"); - - Select select = QueryBuilder.select().all().from(entityInformation.getTableName()); - select.where(getIdClause(id)); - - return cassandraTemplate.select(select, entityInformation.getJavaType()); + public T findOne(ID id) { + return cassandraTemplate.selectOneById(entityInformation.getJavaType(), id); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable) - */ + @Override public boolean exists(ID id) { - - Assert.notNull(id, "The given id must not be null!"); - - Select select = QueryBuilder.select().countAll().from(entityInformation.getTableName()); - select.where(getIdClause(id)); - - Long num = cassandraTemplate.count(select); - return num != null && num.longValue() > 0; + return cassandraTemplate.countById(entityInformation.getJavaType(), id) >= 1; // TODO: == instead of >= ? } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#count() - */ + @Override public long count() { return cassandraTemplate.count(entityInformation.getTableName()); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable) - */ + @Override public void delete(ID id) { - Assert.notNull(id, "The given id must not be null!"); - - Delete delete = QueryBuilder.delete().all().from(entityInformation.getTableName()); - delete.where(getIdClause(id)); - - cassandraTemplate.execute(delete.getQueryString()); + cassandraTemplate.deleteById(entityInformation.getJavaType(), id); } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object) */ + @Override public void delete(T entity) { Assert.notNull(entity, "The given entity must not be null!"); delete(entityInformation.getId(entity)); @@ -171,6 +126,7 @@ public class SimpleCassandraRepository implements Ca * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable) */ + @Override public void delete(Iterable entities) { Assert.notNull(entities, "The given Iterable of entities not be null!"); @@ -184,6 +140,7 @@ public class SimpleCassandraRepository implements Ca * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#deleteAll() */ + @Override public void deleteAll() { cassandraTemplate.truncate(entityInformation.getTableName()); } @@ -192,29 +149,19 @@ public class SimpleCassandraRepository implements Ca * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#findAll() */ + @Override public List findAll() { Select select = QueryBuilder.select().all().from(entityInformation.getTableName()); return findAll(select); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable) - */ + @Override public Iterable findAll(Iterable ids) { - List parameters = new ArrayList(); - for (ID id : ids) { - parameters.add(id); - } - Clause clause = QueryBuilder.in(entityInformation.getIdColumn(), parameters.toArray()); - Select select = QueryBuilder.select().all().from(entityInformation.getTableName()); - select.where(clause); - - return findAll(select); + return cassandraTemplate.selectByIds(entityInformation.getJavaType(), ids); } - private List findAll(Select query) { + protected List findAll(Select query) { if (query == null) { return Collections.emptyList(); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/CqlUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/CqlUtils.java index 5b51d6f35..3aa98506e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/CqlUtils.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/CqlUtils.java @@ -3,25 +3,14 @@ package org.springframework.data.cassandra.util; import java.util.ArrayList; import java.util.List; -import org.springframework.cassandra.core.CqlTemplate; -import org.springframework.cassandra.core.QueryOptions; -import org.springframework.data.cassandra.exception.EntityWriterException; +import org.springframework.cassandra.core.cql.CqlStringUtils; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; -import org.springframework.data.convert.EntityWriter; import org.springframework.data.mapping.PropertyHandler; import com.datastax.driver.core.ColumnMetadata; import com.datastax.driver.core.DataType; -import com.datastax.driver.core.Query; -import com.datastax.driver.core.Statement; import com.datastax.driver.core.TableMetadata; -import com.datastax.driver.core.querybuilder.Batch; -import com.datastax.driver.core.querybuilder.Delete; -import com.datastax.driver.core.querybuilder.Delete.Where; -import com.datastax.driver.core.querybuilder.Insert; -import com.datastax.driver.core.querybuilder.QueryBuilder; -import com.datastax.driver.core.querybuilder.Update; /** * Utilities to convert Cassandra Annotated objects to Queries and CQL. @@ -33,39 +22,7 @@ import com.datastax.driver.core.querybuilder.Update; public abstract class CqlUtils { /** - * Create the List of CQL for the indexes required for Cassandra mapped Table. - * - * @param tableName - * @param entity - * @return The list of CQL statements to run with session.execute() - */ - public static List createIndexes(final String tableName, final CassandraPersistentEntity entity) { - final List result = new ArrayList(); - - entity.doWithProperties(new PropertyHandler() { - @Override - public void doWithPersistentProperty(CassandraPersistentProperty prop) { - - if (prop.isIndexed()) { - - final StringBuilder str = new StringBuilder(); - str.append("CREATE INDEX ON "); - str.append(tableName); - str.append(" ("); - str.append(prop.getColumnName()); - str.append(");"); - - result.add(str.toString()); - } - - } - }); - - return result; - } - - /** - * Alter the table to refelct the entity annotations + * Alter the table to reflect the entity annotations * * @param tableName * @param entity @@ -104,7 +61,7 @@ public abstract class CqlUtils { str.append("TYPE "); } - str.append(toCQL(columnDataType)); + str.append(CqlStringUtils.toCql(columnDataType)); str.append(';'); result.add(str.toString()); @@ -114,243 +71,4 @@ public abstract class CqlUtils { return result; } - - /** - * Generates a Query Object for an insert - * - * @param tableName - * @param objectToSave - * @param entity - * @param optionsByName - * - * @return The Query object to run with session.execute(); - * @throws EntityWriterException - */ - public static Query toInsertQuery(String tableName, final Object objectToSave, QueryOptions options, - EntityWriter entityWriter) throws EntityWriterException { - - final Insert q = QueryBuilder.insertInto(tableName); - - /* - * Write properties - */ - entityWriter.write(objectToSave, q); - - /* - * Add Query Options - */ - CqlTemplate.addQueryOptions(q, options); - - /* - * Add TTL to Insert object - */ - if (options != null && options.getTtl() != null) { - q.using(QueryBuilder.ttl(options.getTtl())); - } - - return q; - - } - - /** - * Generates a Query Object for an Update - * - * @param tableName - * @param objectToSave - * @param entity - * @param optionsByName - * - * @return The Query object to run with session.execute(); - * @throws EntityWriterException - */ - public static Query toUpdateQuery(String tableName, final Object objectToSave, QueryOptions options, - EntityWriter entityWriter) throws EntityWriterException { - - final Update q = QueryBuilder.update(tableName); - - /* - * Write properties - */ - entityWriter.write(objectToSave, q); - - /* - * Add Query Options - */ - CqlTemplate.addQueryOptions(q, options); - - /* - * Add TTL to Insert object - */ - if (options != null && options.getTtl() != null) { - q.using(QueryBuilder.ttl(options.getTtl())); - } - - return q; - - } - - /** - * Generates a Batch Object for multiple Updates - * - * @param tableName - * @param objectsToSave - * @param entity - * @param optionsByName - * - * @return The Query object to run with session.execute(); - * @throws EntityWriterException - */ - public static Batch toUpdateBatchQuery(final String tableName, final List objectsToSave, QueryOptions options, - EntityWriter entityWriter) throws EntityWriterException { - - /* - * Return variable is a Batch statement - */ - final Batch b = QueryBuilder.batch(); - - for (final T objectToSave : objectsToSave) { - - b.add((Statement) toUpdateQuery(tableName, objectToSave, options, entityWriter)); - - } - - /* - * Add Query Options - */ - CqlTemplate.addQueryOptions(b, options); - - return b; - - } - - /** - * Generates a Batch Object for multiple inserts - * - * @param tableName - * @param objectsToSave - * @param entity - * @param optionsByName - * - * @return The Query object to run with session.execute(); - * @throws EntityWriterException - */ - public static Batch toInsertBatchQuery(final String tableName, final List objectsToSave, QueryOptions options, - EntityWriter entityWriter) throws EntityWriterException { - - /* - * Return variable is a Batch statement - */ - final Batch b = QueryBuilder.batch(); - - for (final T objectToSave : objectsToSave) { - - b.add((Statement) toInsertQuery(tableName, objectToSave, options, entityWriter)); - - } - - /* - * Add Query Options - */ - CqlTemplate.addQueryOptions(b, options); - - return b; - - } - - /** - * Create a Delete Query Object from an annotated POJO - * - * @param tableName - * @param objectToRemove - * @param entity - * @param optionsByName - * @return - * @throws EntityWriterException - */ - public static Query toDeleteQuery(String tableName, final Object objectToRemove, QueryOptions options, - EntityWriter entityWriter) throws EntityWriterException { - - final Delete.Selection ds = QueryBuilder.delete(); - final Delete q = ds.from(tableName); - final Where w = q.where(); - - /* - * Write where condition to find by Id - */ - entityWriter.write(objectToRemove, w); - - CqlTemplate.addQueryOptions(q, options); - - return q; - - } - - /** - * @param dataType - * @return - */ - public static String toCQL(DataType dataType) { - if (dataType.getTypeArguments().isEmpty()) { - return dataType.getName().name(); - } else { - StringBuilder str = new StringBuilder(); - str.append(dataType.getName().name()); - str.append('<'); - for (DataType argDataType : dataType.getTypeArguments()) { - if (str.charAt(str.length() - 1) != '<') { - str.append(','); - } - str.append(argDataType.getName().name()); - } - str.append('>'); - return str.toString(); - } - } - - /** - * @param tableName - * @return - */ - public static String dropTable(String tableName) { - - if (tableName == null) { - return null; - } - - StringBuilder str = new StringBuilder(); - str.append("DROP TABLE " + tableName + ";"); - return str.toString(); - } - - /** - * Create a Batch Query object for multiple deletes. - * - * @param tableName - * @param entities - * @param entity - * @param optionsByName - * - * @return - * @throws EntityWriterException - */ - public static Batch toDeleteBatchQuery(String tableName, List entities, QueryOptions options, - EntityWriter entityWriter) throws EntityWriterException { - - /* - * Return variable is a Batch statement - */ - final Batch b = QueryBuilder.batch(); - - for (final T objectToSave : entities) { - - b.add((Statement) toDeleteQuery(tableName, objectToSave, options, entityWriter)); - - } - - CqlTemplate.addQueryOptions(b, options); - - return b; - - } - } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/CassandraCompositePrimaryKeyIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/CassandraCompositePrimaryKeyIntegrationTests.java new file mode 100644 index 000000000..32ca22f1c --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/CassandraCompositePrimaryKeyIntegrationTests.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.cassandra.test.integration.mapping; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.cassandra.core.PrimaryKeyType; +import org.springframework.cassandra.core.keyspace.ColumnSpecification; +import org.springframework.cassandra.core.keyspace.CreateTableSpecification; +import org.springframework.data.cassandra.mapping.BasicCassandraPersistentProperty; +import org.springframework.data.cassandra.mapping.CachingCassandraPersistentProperty; +import org.springframework.data.cassandra.mapping.CassandraMappingContext; +import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; +import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; +import org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder; +import org.springframework.data.cassandra.mapping.Column; +import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; +import org.springframework.data.cassandra.mapping.PrimaryKey; +import org.springframework.data.cassandra.mapping.PrimaryKeyClass; +import org.springframework.data.cassandra.mapping.PrimaryKeyColumn; +import org.springframework.data.cassandra.mapping.Table; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.util.ReflectionUtils; + +import com.datastax.driver.core.DataType; + +/** + * Integration test for {@link BasicCassandraPersistentProperty} with a composite primary key class. + * + * @author Matthew T. Adams + */ +public class CassandraCompositePrimaryKeyIntegrationTests { + + private static final CassandraSimpleTypeHolder SIMPLE_TYPE_HOLDER = new CassandraSimpleTypeHolder(); + + @PrimaryKeyClass + static class Key { + + @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) + String z; + + @PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.CLUSTERED) + String a; + } + + @Table + static class Thing { + + @PrimaryKey + Key id; + + Date time; + + @Column("message") + String text; + } + + CassandraMappingContext context; + CassandraPersistentEntity thing; + CassandraPersistentEntity key; + + @Before + public void setup() { + context = new DefaultCassandraMappingContext(); + thing = context.getPersistentEntity(ClassTypeInformation.from(Thing.class)); + key = context.getPersistentEntity(ClassTypeInformation.from(Key.class)); + } + + @Test + public void validateMappingInfo() { + + Field field = ReflectionUtils.findField(Thing.class, "id"); + CassandraPersistentProperty property = new CachingCassandraPersistentProperty(field, null, thing, + SIMPLE_TYPE_HOLDER); + assertTrue(property.isIdProperty()); + assertTrue(property.isCompositePrimaryKey()); + + List expectedColumnNames = Arrays.asList(new String[] { "z", "a" }); + assertTrue(expectedColumnNames.equals(property.getColumnNames())); + + List actualColumnNames = new ArrayList(); + List properties = property.getCompositePrimaryKeyProperties(); + for (CassandraPersistentProperty p : properties) { + actualColumnNames.addAll(p.getColumnNames()); + } + assertTrue(expectedColumnNames.equals(actualColumnNames)); + + CreateTableSpecification spec = context.getCreateTableSpecificationFor(thing); + + List partitionKeyColumns = spec.getPartitionKeyColumns(); + assertEquals(1, partitionKeyColumns.size()); + ColumnSpecification partitionKeyColumn = partitionKeyColumns.get(0); + assertEquals("z", partitionKeyColumn.getName()); + assertEquals(PrimaryKeyType.PARTITIONED, partitionKeyColumn.getKeyType()); + assertEquals(DataType.text(), partitionKeyColumn.getType()); + + List clusteredKeyColumns = spec.getClusteredKeyColumns(); + assertEquals(1, clusteredKeyColumns.size()); + ColumnSpecification clusteredKeyColumn = clusteredKeyColumns.get(0); + assertEquals("a", clusteredKeyColumn.getName()); + assertEquals(PrimaryKeyType.CLUSTERED, clusteredKeyColumn.getKeyType()); + assertEquals(DataType.text(), partitionKeyColumn.getType()); + } +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/CompoundPrimaryKeyIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/CompoundPrimaryKeyIntegrationTests.java index 60601573c..7b868fb0d 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/CompoundPrimaryKeyIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/CompoundPrimaryKeyIntegrationTests.java @@ -24,6 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.data.cassandra.mapping.BasicCassandraPersistentEntity; import org.springframework.data.cassandra.mapping.BasicCassandraPersistentProperty; +import org.springframework.data.cassandra.mapping.CachingCassandraPersistentProperty; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; import org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder; @@ -76,6 +77,6 @@ public class CompoundPrimaryKeyIntegrationTests { } private CassandraPersistentProperty getPropertyFor(Field field) { - return new BasicCassandraPersistentProperty(field, null, entity, new CassandraSimpleTypeHolder()); + return new CachingCassandraPersistentProperty(field, null, entity, new CassandraSimpleTypeHolder()); } } From 5a2bc91838b31de599da7a7cc05ba3b6ada7a114 Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Wed, 5 Feb 2014 20:39:02 -0600 Subject: [PATCH 2/5] DATACASS-33 - reduced all of SimpleCassandraRepository's methods to one-liners --- .../cassandra/core/util/CollectionUtils.java | 22 ++++ .../cassandra/core/CassandraOperations.java | 2 + .../cassandra/core/CassandraTemplate.java | 23 ++++ .../MappingCassandraEntityInformation.java | 10 +- .../support/SimpleCassandraRepository.java | 120 +++--------------- 5 files changed, 69 insertions(+), 108 deletions(-) create mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/core/util/CollectionUtils.java diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/util/CollectionUtils.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/util/CollectionUtils.java new file mode 100644 index 000000000..8004ca8e9 --- /dev/null +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/util/CollectionUtils.java @@ -0,0 +1,22 @@ +package org.springframework.cassandra.core.util; + +import java.util.ArrayList; +import java.util.List; + +public class CollectionUtils { + + public static List toList(Iterable i) { + + List list = null; + if (i instanceof List) { + list = (List) i; + } else { + list = new ArrayList(); + for (T t : i) { + list.add(t); + } + } + + return list; + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java index cdb608d30..fcbe05844 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java @@ -461,4 +461,6 @@ public interface CassandraOperations extends CqlOperations { void deleteById(Class clazz, Object id); List selectByIds(Class clazz, Iterable ids); + + List selectAll(Class clazz); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index 577e6fafd..2bfc6e9bb 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -142,8 +142,12 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation @Override public void delete(List entities) { + + Assert.notEmpty(entities); + String tableName = getTableName(entities.get(0).getClass()); Assert.notNull(tableName); + delete(entities, tableName); } @@ -394,13 +398,32 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation return insert(tableName, entity, options, true); } + @Override + public List selectAll(Class selectClass) { + + Assert.notNull(selectClass); + + CassandraPersistentEntity entity = mappingContext.getPersistentEntity(selectClass); + if (entity == null) { + throw new IllegalArgumentException(String.format("unknown persistent class [%s]", selectClass.getName())); + } + return select(QueryBuilder.select().all().from(entity.getTableName()), selectClass); + } + @Override public List select(Select cql, Class selectClass) { + + Assert.notNull(cql); + return select(cql.getQueryString(), selectClass); } @Override public List select(String cql, Class selectClass) { + + Assert.hasText(cql); + Assert.notNull(selectClass); + return select(cql, new ReadRowCallback(cassandraConverter, selectClass)); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java index 5e53f5fa9..2d0727ea8 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java @@ -22,6 +22,7 @@ import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; import org.springframework.data.cassandra.repository.query.CassandraEntityInformation; import org.springframework.data.mapping.model.BeanWrapper; import org.springframework.data.repository.core.support.AbstractEntityInformation; +import org.springframework.util.Assert; /** * {@link CassandraEntityInformation} implementation using a {@link CassandraPersistentEntity} instance to lookup the @@ -63,17 +64,14 @@ public class MappingCassandraEntityInformation exten @Override public ID getId(T entity) { - CassandraPersistentProperty idProperty = entityMetadata.getIdProperty(); + Assert.notNull(entity); + CassandraPersistentProperty idProperty = entityMetadata.getIdProperty(); if (idProperty == null) { return null; } - try { - return (ID) BeanWrapper.create(entity, null).getProperty(idProperty); - } catch (Exception e) { - throw new RuntimeException(e); - } + return (ID) BeanWrapper.create(entity, null).getProperty(idProperty); } @SuppressWarnings("unchecked") diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java index da529aa6e..3e5179475 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java @@ -16,34 +16,26 @@ package org.springframework.data.cassandra.repository.support; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import org.springframework.cassandra.core.CqlOperations; -import org.springframework.data.cassandra.core.CassandraOperations; +import org.springframework.cassandra.core.util.CollectionUtils; import org.springframework.data.cassandra.core.CassandraTemplate; import org.springframework.data.cassandra.repository.CassandraRepository; import org.springframework.data.cassandra.repository.query.CassandraEntityInformation; import org.springframework.util.Assert; -import com.datastax.driver.core.querybuilder.Clause; -import com.datastax.driver.core.querybuilder.Delete; -import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; -import com.datastax.driver.core.querybuilder.Select.Where; /** * Repository base implementation for Cassandra. * * @author Alex Shvid - * + * @author Matthew T. Adams */ - public class SimpleCassandraRepository implements CassandraRepository { - protected final CassandraTemplate cassandraTemplate; - protected final CassandraEntityInformation entityInformation; + protected CassandraTemplate template; + protected CassandraEntityInformation entityInformation; /** * Creates a new {@link SimpleCassandraRepository} for the given {@link CassandraEntityInformation} and @@ -52,147 +44,71 @@ public class SimpleCassandraRepository implements Ca * @param metadata must not be {@literal null}. * @param template must not be {@literal null}. */ - public SimpleCassandraRepository(CassandraEntityInformation metadata, CassandraTemplate cassandraTemplate) { + public SimpleCassandraRepository(CassandraEntityInformation metadata, CassandraTemplate template) { - Assert.notNull(cassandraTemplate); + Assert.notNull(template); Assert.notNull(metadata); this.entityInformation = metadata; - this.cassandraTemplate = cassandraTemplate; + this.template = template; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object) - */ @Override public S save(S entity) { - - Assert.notNull(entity, "Entity must not be null!"); - cassandraTemplate.insert(entity, entityInformation.getTableName()); - return entity; + return template.insert(entity, entityInformation.getTableName()); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable) - */ @Override public List save(Iterable entities) { - - Assert.notNull(entities, "The given Iterable of entities not be null!"); - - List result = new ArrayList(); - - for (S entity : entities) { - save(entity); - result.add(entity); - } - - return result; + return template.insert(CollectionUtils.toList(entities)); } @Override public T findOne(ID id) { - return cassandraTemplate.selectOneById(entityInformation.getJavaType(), id); + return template.selectOneById(entityInformation.getJavaType(), id); } @Override public boolean exists(ID id) { - return cassandraTemplate.countById(entityInformation.getJavaType(), id) >= 1; // TODO: == instead of >= ? + return template.countById(entityInformation.getJavaType(), id) >= 1; // TODO: == instead of >= ? } @Override public long count() { - return cassandraTemplate.count(entityInformation.getTableName()); + return template.count(entityInformation.getTableName()); } @Override public void delete(ID id) { - cassandraTemplate.deleteById(entityInformation.getJavaType(), id); + template.deleteById(entityInformation.getJavaType(), id); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object) - */ @Override public void delete(T entity) { - Assert.notNull(entity, "The given entity must not be null!"); delete(entityInformation.getId(entity)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable) - */ @Override public void delete(Iterable entities) { - - Assert.notNull(entities, "The given Iterable of entities not be null!"); - - for (T entity : entities) { - delete(entity); - } + template.delete(CollectionUtils.toList(entities)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#deleteAll() - */ @Override public void deleteAll() { - cassandraTemplate.truncate(entityInformation.getTableName()); + template.truncate(entityInformation.getTableName()); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#findAll() - */ @Override public List findAll() { - Select select = QueryBuilder.select().all().from(entityInformation.getTableName()); - return findAll(select); + return template.selectAll(entityInformation.getJavaType()); } @Override public Iterable findAll(Iterable ids) { - - return cassandraTemplate.selectByIds(entityInformation.getJavaType(), ids); + return template.selectByIds(entityInformation.getJavaType(), ids); } protected List findAll(Select query) { - - if (query == null) { - return Collections.emptyList(); - } - - return cassandraTemplate.select(query, entityInformation.getJavaType()); + return template.select(query, entityInformation.getJavaType()); } - - /** - * Returns the underlying {@link CqlOperations} instance. - * - * @return - */ - protected CqlOperations getCassandraOperations() { - return this.cassandraTemplate; - } - - /** - * Returns the underlying {@link CassandraOperations} instance. - * - * @return - */ - protected CassandraOperations getCassandraDataOperations() { - return this.cassandraTemplate; - } - - /** - * @return the entityInformation - */ - protected CassandraEntityInformation getEntityInformation() { - return entityInformation; - } - } From 471fae991160c4776064869edc5f1cbebc2e2065 Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Thu, 6 Feb 2014 12:04:35 -0600 Subject: [PATCH 3/5] DATACASS-33 - simplifying CassandraOperations --- .../core/AsynchronousQueryListener.java | 7 +- .../cassandra/core/CqlOperations.java | 33 +- .../cassandra/core/CqlTemplate.java | 42 +- .../cassandra/core/util/CollectionUtils.java | 5 + .../convert/MappingCassandraConverter.java | 5 + .../core/CassandraAdminTemplate.java | 20 - ...ava => CassandraConverterRowCallback.java} | 18 +- .../cassandra/core/CassandraOperations.java | 231 +----- .../cassandra/core/CassandraTemplate.java | 716 ++++-------------- .../mapping/CassandraMappingContext.java | 21 + .../DefaultCassandraMappingContext.java | 25 + .../support/SimpleCassandraRepository.java | 9 +- .../template/CassandraDataOperationsTest.java | 166 ++-- 13 files changed, 395 insertions(+), 903 deletions(-) rename spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/{ReadRowCallback.java => CassandraConverterRowCallback.java} (68%) diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/AsynchronousQueryListener.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/AsynchronousQueryListener.java index 3c59206b4..7cf4a587f 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/AsynchronousQueryListener.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/AsynchronousQueryListener.java @@ -18,16 +18,17 @@ package org.springframework.cassandra.core; import com.datastax.driver.core.ResultSetFuture; /** - * @author David Webb + * Interface used to give an implementation access to a {@link ResultSetFuture} after the query has completed. * + * @author David Webb */ public interface AsynchronousQueryListener { /** * Called upon Query Completion. * - * @param rsf The given ResultSetFuture's get methods should return immediately. + * @param rsf The {@link ResultSetFuture}. Since this isn't called until the asynchronous query completes, it can be + * immediately interrogated. */ public void onQueryComplete(ResultSetFuture rsf); - } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java index fa9b5423b..66842d696 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java @@ -56,6 +56,7 @@ public interface CqlOperations { * Executes the supplied CQL Query and returns nothing. * * @param cql + * @see #query(String) */ void execute(String cql) throws DataAccessException; @@ -108,10 +109,10 @@ public interface CqlOperations { ResultSetFuture queryAsynchronously(String cql, QueryOptions options); /** - * Executes the provided CQL Query with the provided Runnable implementations. + * Executes the provided CQL Query with the provided {@link Runnable} implementation. * * @param cql The Query - * @param listener Runnable Listener for handling the query in a separate thread + * @param listener {@link Runnable} listener for handling the query in a separate thread */ void queryAsynchronously(String cql, Runnable listener); @@ -121,7 +122,8 @@ public interface CqlOperations { * query is completed for optimal flexibility. * * @param cql The Query - * @param listener Runnable Listener for handling the query in a separate thread + * @param listener {@link AsynchronousQueryListener} Listener for handling the query's {@link ResultSetFuture} in a + * separate thread */ void queryAsynchronously(String cql, AsynchronousQueryListener listener); @@ -189,6 +191,23 @@ public interface CqlOperations { */ void queryAsynchronously(String cql, AsynchronousQueryListener listener, QueryOptions options, Executor executor); + /** + * Executes the provided CQL query and returns the {@link ResultSet}. + * + * @param cql The query + * @return The {@link ResultSet} + */ + ResultSet query(String cql); + + /** + * Executes the provided CQL query with the given {@link QueryOptions} and returns the {@link ResultSet}. + * + * @param cql The query + * @param options The {@link QueryOptions}; may be null. + * @return The {@link ResultSet} + */ + ResultSet query(String cql, QueryOptions options); + /** * Executes the provided CQL Query, and extracts the results with the ResultSetExtractor. * @@ -762,6 +781,14 @@ public interface CqlOperations { */ void truncate(String tableName); + /** + * Counts all rows for given table + * + * @param tableName + * @return + */ + long count(String tableName); + /** * Convenience method to convert the given specification to CQL and execute it. * diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java index 52e8b034e..b7a4363e3 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java @@ -45,6 +45,7 @@ import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.DropTableSpecification; import org.springframework.cassandra.support.CassandraAccessor; import org.springframework.dao.DataAccessException; +import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.QueryTimeoutException; import org.springframework.util.Assert; @@ -63,6 +64,7 @@ import com.datastax.driver.core.SimpleStatement; import com.datastax.driver.core.Statement; import com.datastax.driver.core.exceptions.DriverException; import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.querybuilder.Truncate; /** @@ -304,6 +306,23 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { return process(doExecute(cql, options), rowMapper); } + @Override + public ResultSet query(String cql) { + return query(cql, (QueryOptions) null); + } + + @Override + public ResultSet query(String cql, QueryOptions options) { + + return query(cql, new ResultSetExtractor() { + + @Override + public ResultSet extractData(ResultSet rs) throws DriverException, DataAccessException { + return rs; + } + }, options); + } + @Override public List query(String cql, RowMapper rowMapper) throws DataAccessException { return query(cql, rowMapper, null); @@ -434,7 +453,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * * @return */ - private Set getHosts() { + protected Set getHosts() { /* * Get the cluster metadata for this session @@ -965,4 +984,25 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { }); } + @Override + public long count(String tableName) { + return selectCount(QueryBuilder.select().countAll().from(tableName).getQueryString()); + } + + protected long selectCount(String countQuery) { + + return query(countQuery, new ResultSetExtractor() { + + @Override + public Long extractData(ResultSet rs) throws DriverException, DataAccessException { + + Row row = rs.one(); + if (row == null) { + throw new InvalidDataAccessApiUsageException(String.format("count query did not return any results")); + } + + return row.getLong(0); + } + }); + } } \ No newline at end of file diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/util/CollectionUtils.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/util/CollectionUtils.java index 8004ca8e9..e9ceb2b9a 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/util/CollectionUtils.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/util/CollectionUtils.java @@ -5,6 +5,11 @@ import java.util.List; public class CollectionUtils { + @SuppressWarnings("unchecked") + public static T[] toArray(Iterable i) { + return (T[]) toList(i).toArray(); + } + public static List toList(Iterable i) { List list = null; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java index 8315f0b19..ec64f8c90 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java @@ -34,6 +34,7 @@ import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.SpELContext; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import com.datastax.driver.core.Row; @@ -67,7 +68,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem * @param mappingContext must not be {@literal null}. */ public MappingCassandraConverter(CassandraMappingContext mappingContext) { + super(new DefaultConversionService()); + + Assert.notNull(mappingContext); + this.mappingContext = mappingContext; this.spELContext = new SpELContext(RowReaderPropertyAccessor.INSTANCE); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java index 42ace67b9..c7641f026 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java @@ -124,24 +124,4 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand } }); } - - /** - * @param entityClass - * @return - */ - @Override - public String determineTableName(Class entityClass) { - - if (entityClass == null) { - throw new InvalidDataAccessApiUsageException( - "No class parameter provided, entity table name can't be determined!"); - } - - CassandraPersistentEntity entity = getCassandraMappingContext().getPersistentEntity(entityClass); - if (entity == null) { - throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class " - + entityClass.getName()); - } - return entity.getTableName(); - } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReadRowCallback.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraConverterRowCallback.java similarity index 68% rename from spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReadRowCallback.java rename to spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraConverterRowCallback.java index a87050ebb..8b02200de 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReadRowCallback.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraConverterRowCallback.java @@ -16,32 +16,34 @@ package org.springframework.data.cassandra.core; import org.springframework.cassandra.core.RowCallback; -import org.springframework.data.convert.EntityReader; +import org.springframework.data.cassandra.convert.CassandraConverter; import org.springframework.util.Assert; import com.datastax.driver.core.Row; /** - * Simple {@link RowCallback} that will transform {@link Row} into the given target type using the given - * {@link EntityReader}. + * Simple {@link RowCallback} that will transform a {@link Row} into the given target type using the given + * {@link CassandraConverter}. * * @author Alex Shvid + * @author Matthew T. Adams */ -public class ReadRowCallback implements RowCallback { +public class CassandraConverterRowCallback implements RowCallback { - private final EntityReader reader; + private final CassandraConverter reader; private final Class type; - public ReadRowCallback(EntityReader reader, Class type) { + public CassandraConverterRowCallback(CassandraConverter reader, Class type) { + Assert.notNull(reader); Assert.notNull(type); + this.reader = reader; this.type = type; } @Override public T doWith(Row row) { - T source = reader.read(type, row); - return source; + return reader.read(type, row); } } \ No newline at end of file diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java index fcbe05844..e0e5d8586 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java @@ -21,8 +21,6 @@ import org.springframework.cassandra.core.CqlOperations; import org.springframework.cassandra.core.QueryOptions; import org.springframework.data.cassandra.convert.CassandraConverter; -import com.datastax.driver.core.querybuilder.Select; - /** * Operations for interacting with Cassandra. These operations are used by the Repository implementation, but can also * be used directly when that is desired by the developer. @@ -45,50 +43,25 @@ public interface CassandraOperations extends CqlOperations { * Execute query and convert ResultSet to the list of entities * * @param query must not be {@literal null}. - * @param selectClass must not be {@literal null}, mapped entity type. + * @param type must not be {@literal null}, mapped entity type. * @return */ - List select(String cql, Class selectClass); + List select(String cql, Class type); - /** - * Execute query and convert ResultSet to the list of entities - * - * @param selectQuery must not be {@literal null}. - * @param selectClass must not be {@literal null}, mapped entity type. - * @return - */ - List select(Select selectQuery, Class selectClass); - - T selectOneById(Class selectClass, Object id); + T selectOneById(Class type, Object id); /** * Execute query and convert ResultSet to the entity * * @param query must not be {@literal null}. - * @param selectClass must not be {@literal null}, mapped entity type. + * @param type must not be {@literal null}, mapped entity type. * @return */ - T selectOne(String cql, Class selectClass); + T selectOne(String cql, Class type); - T selectOne(Select selectQuery, Class selectClass); + boolean exists(Class type, Object id); - Long countById(Class clazz, Object id); - - /** - * Counts rows for given query - * - * @param selectQuery - * @return - */ - Long count(Select selectQuery); - - /** - * Counts all rows for given table - * - * @param tableName - * @return - */ - Long count(String tableName); + long count(Class type); /** * Insert the given object to the table by id. @@ -97,23 +70,6 @@ public interface CassandraOperations extends CqlOperations { */ T insert(T entity); - /** - * Insert the given object to the table by id. - * - * @param entity - * @param tableName - * @return - */ - T insert(T entity, String tableName); - - /** - * @param entity - * @param tableName - * @param options - * @return - */ - T insert(T entity, String tableName, QueryOptions options); - /** * @param entity * @param tableName @@ -130,15 +86,6 @@ public interface CassandraOperations extends CqlOperations { */ List insert(List entities); - /** - * Insert the given list of objects to the table by name. - * - * @param entities - * @param tableName - * @return - */ - List insert(List entities, String tableName); - /** * @param entities * @param tableName @@ -147,14 +94,6 @@ public interface CassandraOperations extends CqlOperations { */ List insert(List entities, QueryOptions options); - /** - * @param entities - * @param tableName - * @param options - * @return - */ - List insert(List entities, String tableName, QueryOptions options); - /** * Insert the given object to the table by id. * @@ -162,13 +101,6 @@ public interface CassandraOperations extends CqlOperations { */ T insertAsynchronously(T entity); - /** - * Insert the given object to the table by id. - * - * @param object - */ - T insertAsynchronously(T entity, String tableName); - /** * @param entity * @param tableName @@ -177,14 +109,6 @@ public interface CassandraOperations extends CqlOperations { */ T insertAsynchronously(T entity, QueryOptions options); - /** - * @param entity - * @param tableName - * @param options - * @return - */ - T insertAsynchronously(T entity, String tableName, QueryOptions options); - /** * Insert the given object to the table by id. * @@ -192,13 +116,6 @@ public interface CassandraOperations extends CqlOperations { */ List insertAsynchronously(List entities); - /** - * Insert the given object to the table by id. - * - * @param object - */ - List insertAsynchronously(List entities, String tableName); - /** * @param entities * @param tableName @@ -207,14 +124,6 @@ public interface CassandraOperations extends CqlOperations { */ List insertAsynchronously(List entities, QueryOptions options); - /** - * @param entities - * @param tableName - * @param options - * @return - */ - List insertAsynchronously(List entities, String tableName, QueryOptions options); - /** * Insert the given object to the table by id. * @@ -222,13 +131,6 @@ public interface CassandraOperations extends CqlOperations { */ T update(T entity); - /** - * Insert the given object to the table by id. - * - * @param object - */ - T update(T entity, String tableName); - /** * @param entity * @param tableName @@ -237,14 +139,6 @@ public interface CassandraOperations extends CqlOperations { */ T update(T entity, QueryOptions options); - /** - * @param entity - * @param tableName - * @param options - * @return - */ - T update(T entity, String tableName, QueryOptions options); - /** * Insert the given object to the table by id. * @@ -252,13 +146,6 @@ public interface CassandraOperations extends CqlOperations { */ List update(List entities); - /** - * Insert the given object to the table by id. - * - * @param object - */ - List update(List entities, String tableName); - /** * @param entities * @param tableName @@ -267,14 +154,6 @@ public interface CassandraOperations extends CqlOperations { */ List update(List entities, QueryOptions options); - /** - * @param entities - * @param tableName - * @param options - * @return - */ - List update(List entities, String tableName, QueryOptions options); - /** * Insert the given object to the table by id. * @@ -282,13 +161,6 @@ public interface CassandraOperations extends CqlOperations { */ T updateAsynchronously(T entity); - /** - * Insert the given object to the table by id. - * - * @param object - */ - T updateAsynchronously(T entity, String tableName); - /** * @param entity * @param tableName @@ -297,14 +169,6 @@ public interface CassandraOperations extends CqlOperations { */ T updateAsynchronously(T entity, QueryOptions options); - /** - * @param entity - * @param tableName - * @param options - * @return - */ - T updateAsynchronously(T entity, String tableName, QueryOptions options); - /** * Insert the given object to the table by id. * @@ -312,13 +176,6 @@ public interface CassandraOperations extends CqlOperations { */ List updateAsynchronously(List entities); - /** - * Insert the given object to the table by id. - * - * @param object - */ - List updateAsynchronously(List entities, String tableName); - /** * @param entities * @param tableName @@ -327,14 +184,6 @@ public interface CassandraOperations extends CqlOperations { */ List updateAsynchronously(List entities, QueryOptions options); - /** - * @param entities - * @param tableName - * @param options - * @return - */ - List updateAsynchronously(List entities, String tableName, QueryOptions options); - /** * Remove the given object from the table by id. * @@ -342,14 +191,6 @@ public interface CassandraOperations extends CqlOperations { */ void delete(T entity); - /** - * Removes the given object from the given table. - * - * @param object - * @param table must not be {@literal null} or empty. - */ - void delete(T entity, String tableName); - /** * @param entity * @param tableName @@ -357,13 +198,6 @@ public interface CassandraOperations extends CqlOperations { */ void delete(T entity, QueryOptions options); - /** - * @param entity - * @param tableName - * @param options - */ - void delete(T entity, String tableName, QueryOptions options); - /** * Remove the given object from the table by id. * @@ -371,14 +205,6 @@ public interface CassandraOperations extends CqlOperations { */ void delete(List entities); - /** - * Removes the given object from the given table. - * - * @param object - * @param table must not be {@literal null} or empty. - */ - void delete(List entities, String tableName); - /** * @param entities * @param tableName @@ -386,13 +212,6 @@ public interface CassandraOperations extends CqlOperations { */ void delete(List entities, QueryOptions options); - /** - * @param entities - * @param tableName - * @param options - */ - void delete(List entities, String tableName, QueryOptions options); - /** * Remove the given object from the table by id. * @@ -407,21 +226,6 @@ public interface CassandraOperations extends CqlOperations { */ void deleteAsynchronously(T entity, QueryOptions options); - /** - * @param entity - * @param tableName - * @param options - */ - void deleteAsynchronously(T entity, String tableName, QueryOptions options); - - /** - * Removes the given object from the given table. - * - * @param object - * @param table must not be {@literal null} or empty. - */ - void deleteAsynchronously(T entity, String tableName); - /** * Remove the given object from the table by id. * @@ -429,14 +233,6 @@ public interface CassandraOperations extends CqlOperations { */ void deleteAsynchronously(List entities); - /** - * Removes the given object from the given table. - * - * @param object - * @param table must not be {@literal null} or empty. - */ - void deleteAsynchronously(List entities, String tableName); - /** * @param entities * @param tableName @@ -444,13 +240,6 @@ public interface CassandraOperations extends CqlOperations { */ void deleteAsynchronously(List entities, QueryOptions options); - /** - * @param entities - * @param tableName - * @param options - */ - void deleteAsynchronously(List entities, String tableName, QueryOptions options); - /** * Returns the underlying {@link CassandraConverter}. * @@ -458,9 +247,9 @@ public interface CassandraOperations extends CqlOperations { */ CassandraConverter getConverter(); - void deleteById(Class clazz, Object id); + void deleteById(Class type, Object id); - List selectByIds(Class clazz, Iterable ids); + List selectByIds(Class type, Iterable ids); - List selectAll(Class clazz); + List selectAll(Class type); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index 2bfc6e9bb..6f5ce78c9 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -16,16 +16,15 @@ package org.springframework.data.cassandra.core; import java.util.ArrayList; -import java.util.Collections; import java.util.Iterator; 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.util.CollectionUtils; import org.springframework.dao.DataAccessException; import org.springframework.dao.DuplicateKeyException; -import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.cassandra.convert.CassandraConverter; import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; @@ -34,13 +33,10 @@ import org.springframework.data.convert.EntityWriter; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.model.BeanWrapper; import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import com.datastax.driver.core.Query; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Row; import com.datastax.driver.core.Session; -import com.datastax.driver.core.Statement; import com.datastax.driver.core.querybuilder.Batch; import com.datastax.driver.core.querybuilder.Clause; import com.datastax.driver.core.querybuilder.Delete; @@ -62,13 +58,9 @@ import com.datastax.driver.core.querybuilder.Update; */ public class CassandraTemplate extends CqlTemplate implements CassandraOperations { - /* - * Required elements for successful Template Operations. These can be set with the Constructor, or wired in - * later. - */ - private CassandraConverter cassandraConverter; - private CassandraMappingContext mappingContext; - private boolean useFieldAccessOnly = false; + protected CassandraConverter cassandraConverter; + protected CassandraMappingContext mappingContext; + protected boolean useFieldAccessOnly = false; /** * Default Constructor for wiring in the required components later @@ -90,9 +82,9 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation public void setConverter(CassandraConverter cassandraConverter) { Assert.notNull(cassandraConverter); + this.cassandraConverter = cassandraConverter; mappingContext = cassandraConverter.getCassandraMappingContext(); - Assert.notNull(mappingContext); } @Override @@ -104,6 +96,19 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation return mappingContext; } + public boolean getUseFieldAccessOnly() { + return useFieldAccessOnly; + } + + /** + * Whether only fields should be used when accessing a persistent entity's data. + * + * @param useFieldAccessOnly + */ + public void setUseFieldAccessOnly(boolean useFieldAccessOnly) { + this.useFieldAccessOnly = useFieldAccessOnly; + } + @Override public void afterPropertiesSet() { super.afterPropertiesSet(); @@ -113,71 +118,41 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation } @Override - public Long countById(Class clazz, Object id) { + public boolean exists(Class type, Object id) { - Assert.notNull(clazz); + Assert.notNull(type); Assert.notNull(id); - CassandraPersistentEntity entity = mappingContext.getPersistentEntity(clazz); - if (entity == null) { - throw new IllegalArgumentException(String.format("unknown persistent class [%s]", clazz.getName())); - } + CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(type); Select select = QueryBuilder.select().countAll().from(entity.getTableName()); appendIdCriteria(select.where(), entity, id); - return count(select); + return count(select.getQueryString()) != 0; } @Override - public Long count(Select selectQuery) { - return selectCount(selectQuery); - } - - @Override - public Long count(String tableName) { - Select select = QueryBuilder.select().countAll().from(tableName); - return selectCount(select); + public long count(Class type) { + return count(getTableName(type)); } @Override public void delete(List entities) { - - Assert.notEmpty(entities); - - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - - delete(entities, tableName); + delete(entities, null); } @Override public void delete(List entities, QueryOptions options) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - delete(entities, tableName, options); + batchDelete(entities, options, false); } @Override - public void delete(List entities, String tableName) { - delete(entities, tableName, null); - } + public void deleteById(Class type, Object id) { - @Override - public void delete(List entities, String tableName, QueryOptions options) { - Assert.notNull(entities); - Assert.notEmpty(entities); - Assert.notNull(tableName); - batchDelete(tableName, entities, options, false); - } - - @Override - public void deleteById(Class clazz, Object id) { - - Assert.notNull(clazz); + Assert.notNull(type); Assert.notNull(id); - CassandraPersistentEntity entity = mappingContext.getPersistentEntity(clazz); + CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(type); Delete delete = QueryBuilder.delete().all().from(entity.getTableName()); appendIdCriteria(delete.where(), entity, id); @@ -187,295 +162,129 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation @Override public void delete(T entity) { - String tableName = getTableName(entity.getClass()); - Assert.notNull(tableName); - delete(entity, tableName); + delete(entity, null); } @Override public void delete(T entity, QueryOptions options) { - String tableName = getTableName(entity.getClass()); - Assert.notNull(tableName); - delete(entity, tableName, options); - } - - @Override - public void delete(T entity, String tableName) { - delete(entity, tableName, null); - } - - @Override - public void delete(T entity, String tableName, QueryOptions options) { - Assert.notNull(entity); - Assert.notNull(tableName); - delete(tableName, entity, options, false); + delete(entity, options, false); } @Override public void deleteAsynchronously(List entities) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - deleteAsynchronously(entities, tableName); + deleteAsynchronously(entities, null); } @Override public void deleteAsynchronously(List entities, QueryOptions options) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - deleteAsynchronously(entities, tableName, options); - } - - @Override - public void deleteAsynchronously(List entities, String tableName) { - deleteAsynchronously(entities, tableName, null); - } - - @Override - public void deleteAsynchronously(List entities, String tableName, QueryOptions options) { - Assert.notNull(entities); - Assert.notEmpty(entities); - Assert.notNull(tableName); - batchDelete(tableName, entities, options, true); + batchDelete(entities, options, true); } @Override public void deleteAsynchronously(T entity) { - String tableName = getTableName(entity.getClass()); - Assert.notNull(tableName); - deleteAsynchronously(entity, tableName); + deleteAsynchronously(entity, null); } @Override public void deleteAsynchronously(T entity, QueryOptions options) { - String tableName = getTableName(entity.getClass()); - Assert.notNull(tableName); - deleteAsynchronously(entity, tableName, options); + delete(entity, options, true); } @Override - public void deleteAsynchronously(T entity, String tableName) { - deleteAsynchronously(entity, tableName, null); - } - - @Override - public void deleteAsynchronously(T entity, String tableName, QueryOptions options) { - Assert.notNull(entity); - Assert.notNull(tableName); - delete(tableName, entity, options, true); - } - - /** - * @param entityClass - * @return - */ - public String determineTableName(Class entityClass) { - - if (entityClass == null) { - throw new InvalidDataAccessApiUsageException( - "No class parameter provided, entity table name can't be determined!"); - } - - CassandraPersistentEntity entity = mappingContext.getPersistentEntity(entityClass); - if (entity == null) { - throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class " - + entityClass.getName()); - } - return entity.getTableName(); - } - - @Override - public String getTableName(Class entityClass) { - return determineTableName(entityClass); + public String getTableName(Class type) { + return mappingContext.getRequiredPersistentEntity(type).getTableName(); } @Override public List insert(List entities) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - return insert(entities, tableName); + return insert(entities, null); } @Override public List insert(List entities, QueryOptions options) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - return insert(entities, tableName, options); - } - - @Override - public List insert(List entities, String tableName) { - return insert(entities, tableName, null); - } - - @Override - public List insert(List entities, String tableName, QueryOptions options) { - Assert.notNull(entities); - Assert.notEmpty(entities); - Assert.notNull(tableName); - return batchInsert(tableName, entities, options, false); + return batchInsert(entities, options, false); } @Override public T insert(T entity) { - String tableName = determineTableName(entity); - Assert.notNull(tableName); - return insert(entity, tableName); + return insert(entity, null); } @Override public T insert(T entity, QueryOptions options) { - String tableName = determineTableName(entity); - Assert.notNull(tableName); - return insert(entity, tableName, options); - } - - @Override - public T insert(T entity, String tableName) { - return insert(entity, tableName, null); - } - - @Override - public T insert(T entity, String tableName, QueryOptions options) { - Assert.notNull(entity); - Assert.notNull(tableName); - ensureNotIterable(entity); - return insert(tableName, entity, options, false); + return insert(entity, options, false); } @Override public List insertAsynchronously(List entities) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - return insertAsynchronously(entities, tableName); + return insertAsynchronously(entities, null); } @Override public List insertAsynchronously(List entities, QueryOptions options) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - return insertAsynchronously(entities, tableName, options); - } - - @Override - public List insertAsynchronously(List entities, String tableName) { - return insertAsynchronously(entities, tableName, null); - } - - @Override - public List insertAsynchronously(List entities, String tableName, QueryOptions options) { - Assert.notNull(entities); - Assert.notEmpty(entities); - Assert.notNull(tableName); - return batchInsert(tableName, entities, options, true); + return batchInsert(entities, options, true); } @Override public T insertAsynchronously(T entity) { - String tableName = determineTableName(entity); - Assert.notNull(tableName); - return insertAsynchronously(entity, tableName); + return insertAsynchronously(entity, null); } @Override public T insertAsynchronously(T entity, QueryOptions options) { - String tableName = determineTableName(entity); - Assert.notNull(tableName); - return insertAsynchronously(entity, tableName, options); + return insert(entity, options, true); } @Override - public T insertAsynchronously(T entity, String tableName) { - return insertAsynchronously(entity, tableName, null); + public List selectAll(Class type) { + return select(QueryBuilder.select().all().from(getTableName(type)).getQueryString(), type); } @Override - public T insertAsynchronously(T entity, String tableName, QueryOptions options) { - Assert.notNull(entity); - Assert.notNull(tableName); - - ensureNotIterable(entity); - - return insert(tableName, entity, options, true); - } - - @Override - public List selectAll(Class selectClass) { - - Assert.notNull(selectClass); - - CassandraPersistentEntity entity = mappingContext.getPersistentEntity(selectClass); - if (entity == null) { - throw new IllegalArgumentException(String.format("unknown persistent class [%s]", selectClass.getName())); - } - return select(QueryBuilder.select().all().from(entity.getTableName()), selectClass); - } - - @Override - public List select(Select cql, Class selectClass) { - - Assert.notNull(cql); - - return select(cql.getQueryString(), selectClass); - } - - @Override - public List select(String cql, Class selectClass) { + public List select(String cql, Class type) { Assert.hasText(cql); - Assert.notNull(selectClass); + Assert.notNull(type); - return select(cql, new ReadRowCallback(cassandraConverter, selectClass)); + return select(cql, new CassandraConverterRowCallback(cassandraConverter, type)); } - @SuppressWarnings({ "rawtypes", "unchecked" }) @Override - public List selectByIds(Class clazz, Iterable ids) { + public List selectByIds(Class type, Iterable ids) { + + CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(type); - CassandraPersistentEntity entity = mappingContext.getPersistentEntity(clazz); - if (entity == null) { - throw new IllegalArgumentException(String.format("unknown persistent entity class [%s]", clazz.getName())); - } if (entity.getIdProperty().isCompositePrimaryKey()) { throw new IllegalArgumentException(String.format( - "entity class [%s] uses a composite primary key class [%s] which this method can't support", clazz.getName(), + "entity class [%s] uses a composite primary key class [%s] which this method can't support", type.getName(), entity.getIdProperty().getCompositePrimaryKeyEntity().getType().getName())); } - List idList = null; - if (ids instanceof List) { - idList = (List) ids; - } else { - idList = new ArrayList(); - for (Object id : ids) { - idList.add(id); - } - } - Select select = QueryBuilder.select().all().from(entity.getTableName()); - select.where(QueryBuilder.in(entity.getIdProperty().getColumnName(), idList.toArray())); + select.where(QueryBuilder.in(entity.getIdProperty().getColumnName(), CollectionUtils.toArray(ids))); - return select(select, clazz); + return select(select.getQueryString(), type); } @Override - public T selectOneById(Class selectClass, Object id) { + public T selectOneById(Class type, Object id) { - Assert.notNull(selectClass); + Assert.notNull(type); Assert.notNull(id); - CassandraPersistentEntity entityClass = mappingContext.getPersistentEntity(selectClass); - if (entityClass == null) { - throw new IllegalArgumentException(String.format("unknown entity class [%s]", selectClass.getName())); + CassandraPersistentEntity entity = mappingContext.getPersistentEntity(type); + if (entity == null) { + throw new IllegalArgumentException(String.format("unknown entity class [%s]", type.getName())); } - Select select = QueryBuilder.select().all().from(entityClass.getTableName()); - appendIdCriteria(select.where(), entityClass, id); + Select select = QueryBuilder.select().all().from(entity.getTableName()); + appendIdCriteria(select.where(), entity, id); - return selectOne(select, selectClass); + return selectOne(select.getQueryString(), type); } protected interface ClauseCallback { - void onClause(Clause clause); + void doWithClause(Clause clause); } protected void appendIdCriteria(final ClauseCallback clauseCallback, CassandraPersistentEntity entity, Object id) { @@ -494,7 +303,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation @Override public void doWithPersistentProperty(CassandraPersistentProperty p) { - clauseCallback.onClause(QueryBuilder.eq(p.getColumnName(), + clauseCallback.doWithClause(QueryBuilder.eq(p.getColumnName(), idWrapper.getProperty(p, p.getActualType(), useFieldAccessOnly))); } }); @@ -502,7 +311,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation return; } - clauseCallback.onClause(QueryBuilder.eq(idProperty.getColumnName(), id)); + clauseCallback.doWithClause(QueryBuilder.eq(idProperty.getColumnName(), id)); } protected void appendIdCriteria(final com.datastax.driver.core.querybuilder.Select.Where where, @@ -511,7 +320,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation appendIdCriteria(new ClauseCallback() { @Override - public void onClause(Clause clause) { + public void doWithClause(Clause clause) { where.and(clause); } }, entity, id); @@ -522,134 +331,62 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation appendIdCriteria(new ClauseCallback() { @Override - public void onClause(Clause clause) { + public void doWithClause(Clause clause) { where.and(clause); } }, entity, id); } @Override - public T selectOne(Select selectQuery, Class selectClass) { - return selectOne(selectQuery.getQueryString(), selectClass); - } - - @Override - public T selectOne(String cql, Class selectClass) { - return selectOne(cql, new ReadRowCallback(cassandraConverter, selectClass)); + public T selectOne(String cql, Class type) { + return selectOne(cql, new CassandraConverterRowCallback(cassandraConverter, type)); } @Override public List update(List entities) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - return update(entities, tableName); + return update(entities, null); } @Override public List update(List entities, QueryOptions options) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - return update(entities, tableName, options); - } - - @Override - public List update(List entities, String tableName) { - return update(entities, tableName, null); - } - - @Override - public List update(List entities, String tableName, QueryOptions options) { - Assert.notNull(entities); - Assert.notEmpty(entities); - Assert.notNull(tableName); - return batchUpdate(tableName, entities, options, false); + return batchUpdate(entities, options, false); } @Override public T update(T entity) { - String tableName = getTableName(entity.getClass()); - Assert.notNull(tableName); - return update(entity, tableName); + return update(entity, null); } @Override public T update(T entity, QueryOptions options) { - String tableName = getTableName(entity.getClass()); - Assert.notNull(tableName); - return update(entity, tableName, options); - } - - @Override - public T update(T entity, String tableName) { - return update(entity, tableName, null); - } - - @Override - public T update(T entity, String tableName, QueryOptions options) { - Assert.notNull(entity); - Assert.notNull(tableName); - return update(tableName, entity, options, false); + return update(entity, options, false); } @Override public List updateAsynchronously(List entities) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - return updateAsynchronously(entities, tableName); + return updateAsynchronously(entities, null); } @Override public List updateAsynchronously(List entities, QueryOptions options) { - String tableName = getTableName(entities.get(0).getClass()); - Assert.notNull(tableName); - return updateAsynchronously(entities, tableName, options); - } - - @Override - public List updateAsynchronously(List entities, String tableName) { - return updateAsynchronously(entities, tableName, null); - } - - @Override - public List updateAsynchronously(List entities, String tableName, QueryOptions options) { - Assert.notNull(entities); - Assert.notEmpty(entities); - Assert.notNull(tableName); - return batchUpdate(tableName, entities, options, true); + return batchUpdate(entities, options, true); } @Override public T updateAsynchronously(T entity) { - String tableName = getTableName(entity.getClass()); - Assert.notNull(tableName); - return updateAsynchronously(entity, tableName); + return updateAsynchronously(entity, null); } @Override public T updateAsynchronously(T entity, QueryOptions options) { - String tableName = getTableName(entity.getClass()); - Assert.notNull(tableName); - return updateAsynchronously(entity, tableName, options); - } - - @Override - public T updateAsynchronously(T entity, String tableName) { - - return updateAsynchronously(entity, tableName, null); - } - - @Override - public T updateAsynchronously(T entity, String tableName, QueryOptions options) { - Assert.notNull(entity); - Assert.notNull(tableName); - return update(tableName, entity, options, true); + return update(entity, options, true); } /** * @param obj * @return */ - private String determineTableName(T obj) { + protected String determineTableName(T obj) { if (null != obj) { return determineTableName(obj.getClass()); } @@ -657,7 +394,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation return null; } - private List select(final String query, ReadRowCallback readRowCallback) { + protected List select(final String query, CassandraConverterRowCallback readRowCallback) { ResultSet resultSet = doExecute(new SessionCallback() { @@ -681,55 +418,16 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation return result; } - private Long selectCount(final Select query) { - - Long count = null; - - ResultSet resultSet = doExecute(new SessionCallback() { - - @Override - public ResultSet doInSession(Session s) throws DataAccessException { - return s.execute(query); - } - }); - - if (resultSet == null) { - return null; - } - - Iterator iterator = resultSet.iterator(); - while (iterator.hasNext()) { - Row row = iterator.next(); - count = row.getLong(0); - } - - return count; - - } - /** * @param query * @param readRowCallback * @return */ - private T selectOne(final String query, ReadRowCallback readRowCallback) { + protected T selectOne(String query, CassandraConverterRowCallback readRowCallback) { logger.info(query); - /* - * Run the Query - */ - ResultSet resultSet = doExecute(new SessionCallback() { - - @Override - public ResultSet doInSession(Session s) throws DataAccessException { - return s.execute(query); - } - }); - - if (resultSet == null) { - return null; - } + ResultSet resultSet = query(query); Iterator iterator = resultSet.iterator(); if (iterator.hasNext()) { @@ -750,63 +448,57 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param tableName * @param objectToRemove */ - protected void batchDelete(final String tableName, final List entities, final QueryOptions options, - final boolean deleteAsynchronously) { + protected void batchDelete(List entities, QueryOptions options, boolean asynchronously) { Assert.notEmpty(entities); - final Batch b = createDeleteBatchQuery(tableName, entities, options, cassandraConverter); + Batch b = createDeleteBatchQuery(getTableName(entities.get(0).getClass()), entities, options, cassandraConverter); + logger.info(b.toString()); - doExecute(new SessionCallback() { + String query = b.getQueryString(); - @Override - public Object doInSession(Session s) throws DataAccessException { - - if (deleteAsynchronously) { - s.executeAsync(b); - } else { - s.execute(b); - } - - return null; - - } - }); + if (asynchronously) { + executeAsynchronously(query); + } else { + execute(query); + } } - /** - * Insert a row into a Cassandra CQL Table - * - * @param tableName - * @param entities - * @param optionsByName - * @param insertAsychronously - * @return - */ - protected List batchInsert(final String tableName, final List entities, final QueryOptions options, - final boolean insertAsychronously) { + protected T insert(T entity, QueryOptions options, boolean asynchronously) { + + Assert.notNull(entity); + + Insert insert = createInsertQuery(getTableName(entity.getClass()), entity, options, cassandraConverter); + + String query = insert.getQueryString(); + + if (asynchronously) { + executeAsynchronously(query); + } else { + execute(query); + } + + return entity; // TODO: fix this! + } + + protected List batchInsert(List entities, QueryOptions options, boolean asychronously) { Assert.notEmpty(entities); - final Batch b = createInsertBatchQuery(tableName, entities, options, cassandraConverter); - logger.info(b.getQueryString()); + Batch b = createInsertBatchQuery(getTableName(entities.get(0).getClass()), entities, options, cassandraConverter); - return doExecute(new SessionCallback>() { + String query = b.getQueryString(); + logger.info(query); - @Override - public List doInSession(Session s) throws DataAccessException { + if (asychronously) { + executeAsynchronously(query); + } else { + execute(query); + } - if (insertAsychronously) { - s.executeAsync(b); - } else { - s.execute(b); - } - - return entities; - - } - }); + return entities; // TODO: fix this! You're not supposed to necessarily return the very same entities that went in + // because the database may assign values. } /** @@ -818,93 +510,44 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param updateAsychronously * @return */ - protected List batchUpdate(final String tableName, final List entities, final QueryOptions options, - final boolean updateAsychronously) { + protected List batchUpdate(List entities, QueryOptions options, boolean asychronously) { Assert.notEmpty(entities); - final Batch b = toUpdateBatchQuery(tableName, entities, options, cassandraConverter); - logger.info(b.toString()); + Batch b = toUpdateBatchQuery(getTableName(entities.get(0).getClass()), entities, options, cassandraConverter); - return doExecute(new SessionCallback>() { + String query = b.getQueryString(); + logger.info(query); - @Override - public List doInSession(Session s) throws DataAccessException { + if (asychronously) { + executeAsynchronously(query); + } else { + execute(query); + } - if (updateAsychronously) { - s.executeAsync(b); - } else { - s.execute(b); - } - - return entities; - - } - }); + return entities; // TODO: fix this! } /** * Perform the removal of a Row. * * @param tableName - * @param objectToRemove - */ - protected void delete(final String tableName, final T objectToRemove, final QueryOptions options, - final boolean deleteAsynchronously) { - - final Query q = createDeleteQuery(tableName, objectToRemove, options, cassandraConverter); - logger.info(q.toString()); - - doExecute(new SessionCallback() { - - @Override - public Object doInSession(Session s) throws DataAccessException { - - if (deleteAsynchronously) { - s.executeAsync(q); - } else { - s.execute(q); - } - - return null; - } - }); - } - - /** - * Insert a row into a Cassandra CQL Table - * - * @param tableName * @param entity */ - protected T insert(final String tableName, final T entity, final QueryOptions options, - final boolean insertAsychronously) { + protected void delete(T entity, QueryOptions options, boolean asynchronously) { - final Query q = createInsertQuery(tableName, entity, options, cassandraConverter); + Assert.notNull(entity); - logger.info(q.toString()); - if (q.getConsistencyLevel() != null) { - logger.info(q.getConsistencyLevel().name()); + Delete delete = createDeleteQuery(getTableName(entity.getClass()), entity, options, cassandraConverter); + logger.info(delete.toString()); + + String query = delete.getQueryString(); + + if (asynchronously) { + executeAsynchronously(query); + } else { + execute(query); } - if (q.getRetryPolicy() != null) { - logger.info(q.getRetryPolicy().toString()); - } - - return doExecute(new SessionCallback() { - - @Override - public T doInSession(Session s) throws DataAccessException { - - if (insertAsychronously) { - s.executeAsync(q); - } else { - s.execute(q); - } - - return entity; - - } - }); } /** @@ -916,43 +559,22 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param updateAsychronously * @return */ - protected T update(final String tableName, final T entity, final QueryOptions options, - final boolean updateAsychronously) { + protected T update(T entity, QueryOptions options, boolean asychronously) { - final Query q = toUpdateQuery(tableName, entity, options, cassandraConverter); - logger.info(q.toString()); + Assert.notNull(entity); - return doExecute(new SessionCallback() { + Update q = toUpdateQuery(getTableName(entity.getClass()), entity, options, cassandraConverter); - @Override - public T doInSession(Session s) throws DataAccessException { + String query = q.getQueryString(); + logger.info(query); - if (updateAsychronously) { - s.executeAsync(q); - } else { - s.execute(q); - } - - return entity; - - } - }); - } - - /** - * Verify the object is not an iterable type - * - * @param o - */ - protected void ensureNotIterable(Object o) { - - if (null == o) { - return; + if (asychronously) { + executeAsynchronously(query); + } else { + execute(query); } - if (o.getClass().isArray() || (o instanceof Iterable) || (o instanceof Iterator)) { - throw new IllegalArgumentException("cannot use a multivalued object here."); - } + return entity; // TODO: fix this! } /** @@ -965,10 +587,10 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * * @return The Query object to run with session.execute(); */ - public static Query createInsertQuery(String tableName, final Object objectToSave, QueryOptions options, + public static Insert createInsertQuery(String tableName, Object objectToSave, QueryOptions options, EntityWriter entityWriter) { - final Insert q = QueryBuilder.insertInto(tableName); + Insert q = QueryBuilder.insertInto(tableName); /* * Write properties @@ -988,7 +610,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation } return q; - } /** @@ -1001,10 +622,10 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * * @return The Query object to run with session.execute(); */ - public static Query toUpdateQuery(String tableName, final Object objectToSave, QueryOptions options, + public static Update toUpdateQuery(String tableName, Object objectToSave, QueryOptions options, EntityWriter entityWriter) { - final Update q = QueryBuilder.update(tableName); + Update q = QueryBuilder.update(tableName); /* * Write properties @@ -1037,17 +658,17 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * * @return The Query object to run with session.execute(); */ - public static Batch toUpdateBatchQuery(final String tableName, final List objectsToSave, QueryOptions options, + public static Batch toUpdateBatchQuery(String tableName, List objectsToSave, QueryOptions options, EntityWriter entityWriter) { /* * Return variable is a Batch statement */ - final Batch b = QueryBuilder.batch(); + Batch b = QueryBuilder.batch(); - for (final T objectToSave : objectsToSave) { + for (T objectToSave : objectsToSave) { - b.add((Statement) toUpdateQuery(tableName, objectToSave, options, entityWriter)); + b.add(toUpdateQuery(tableName, objectToSave, options, entityWriter)); } @@ -1070,13 +691,13 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * * @return The Query object to run with session.execute(); */ - public static Batch createInsertBatchQuery(final String tableName, final List entities, QueryOptions options, + public static Batch createInsertBatchQuery(String tableName, List entities, QueryOptions options, EntityWriter entityWriter) { Batch batch = QueryBuilder.batch(); for (T entity : entities) { - batch.add((Statement) createInsertQuery(tableName, entity, options, entityWriter)); + batch.add(createInsertQuery(tableName, entity, options, entityWriter)); } CqlTemplate.addQueryOptions(batch, options); @@ -1093,7 +714,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation * @param optionsByName * @return */ - public static Query createDeleteQuery(String tableName, final Object object, QueryOptions options, + public static Delete createDeleteQuery(String tableName, Object object, QueryOptions options, EntityWriter entityWriter) { Delete.Selection ds = QueryBuilder.delete(); @@ -1120,22 +741,17 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation public static Batch createDeleteBatchQuery(String tableName, List entities, QueryOptions options, EntityWriter entityWriter) { + Assert.notEmpty(entities); + Assert.hasText(tableName); + Batch batch = QueryBuilder.batch(); for (T entity : entities) { - batch.add((Statement) createDeleteQuery(tableName, entity, options, entityWriter)); + batch.add(createDeleteQuery(tableName, entity, options, entityWriter)); } CqlTemplate.addQueryOptions(batch, options); return batch; } - - public boolean getUseFieldAccessOnly() { - return useFieldAccessOnly; - } - - public void setUseFieldAccessOnly(boolean useFieldAccessOnly) { - this.useFieldAccessOnly = useFieldAccessOnly; - } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java index b0e996964..126605c9a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java @@ -2,6 +2,7 @@ package org.springframework.data.cassandra.mapping; import org.springframework.cassandra.core.keyspace.CreateTableSpecification; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.util.TypeInformation; import com.datastax.driver.core.TableMetadata; @@ -26,4 +27,24 @@ public interface CassandraMappingContext extends * @param table May not be null. */ boolean usesTable(TableMetadata table); + + /** + * Returns the {@link CassandraPersistentEntity} for the given type. If it doesn't exist, this method throws + * {@link IllegalArgumentException}. + * + * @param type The Java type of the persistent entity. + * @return The {@link CassandraPersistentEntity} describing the persistent Java type. + * @throws IllegalArgumentException if the persistent entity is unknown + */ + public CassandraPersistentEntity getRequiredPersistentEntity(Class type); + + /** + * Returns the {@link CassandraPersistentEntity} for the given type. If it doesn't exist, this method throws + * {@link IllegalArgumentException}. + * + * @param type The {@link TypeInformation} of the persistent entity. + * @return The {@link CassandraPersistentEntity} describing the persistent Java type. + * @throws IllegalArgumentException if the persistent entity is unknown + */ + public CassandraPersistentEntity getRequiredPersistentEntity(TypeInformation type); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java index c497b2541..779637227 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java @@ -148,4 +148,29 @@ public class DefaultCassandraMappingContext extends return spec; } + + @Override + public CassandraPersistentEntity getRequiredPersistentEntity(Class type) { + + CassandraPersistentEntity entity = getPersistentEntity(type); + + if (entity == null) { + throw new IllegalArgumentException(String.format("no persistence metadata found for type [%s]", type.getName())); + } + + return entity; + } + + @Override + public CassandraPersistentEntity getRequiredPersistentEntity(TypeInformation type) { + + CassandraPersistentEntity entity = getPersistentEntity(type); + + if (entity == null) { + throw new IllegalArgumentException(String.format("no persistence metadata found for type [%s]", + type.getActualType())); + } + + return entity; + } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java index 3e5179475..c00a5e4f4 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java @@ -19,6 +19,7 @@ import java.io.Serializable; 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.query.CassandraEntityInformation; @@ -34,7 +35,7 @@ import com.datastax.driver.core.querybuilder.Select; */ public class SimpleCassandraRepository implements CassandraRepository { - protected CassandraTemplate template; + protected CassandraOperations template; protected CassandraEntityInformation entityInformation; /** @@ -55,7 +56,7 @@ public class SimpleCassandraRepository implements Ca @Override public S save(S entity) { - return template.insert(entity, entityInformation.getTableName()); + return template.insert(entity); } @Override @@ -70,7 +71,7 @@ public class SimpleCassandraRepository implements Ca @Override public boolean exists(ID id) { - return template.countById(entityInformation.getJavaType(), id) >= 1; // TODO: == instead of >= ? + return template.exists(entityInformation.getJavaType(), id); } @Override @@ -109,6 +110,6 @@ public class SimpleCassandraRepository implements Ca } protected List findAll(Select query) { - return template.select(query, entityInformation.getJavaType()); + return template.select(query.getQueryString(), entityInformation.getJavaType()); } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java index 56ae82be3..b32298259 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java @@ -61,7 +61,7 @@ import com.datastax.driver.core.querybuilder.Select; public class CassandraDataOperationsTest { @Autowired - private CassandraOperations cassandraTemplate; + private CassandraOperations template; private static Logger log = LoggerFactory.getLogger(CassandraDataOperationsTest.class); @@ -92,16 +92,13 @@ public class CassandraDataOperationsTest { @Test public void insertTest() { - /* - * Test Single Insert with entity - */ Book b1 = new Book(); b1.setIsbn("123456-1"); b1.setTitle("Spring Data Cassandra Guide"); b1.setAuthor("Cassandra Guru"); b1.setPages(521); - cassandraTemplate.insert(b1); + template.insert(b1); Book b2 = new Book(); b2.setIsbn("123456-2"); @@ -109,11 +106,8 @@ public class CassandraDataOperationsTest { b2.setAuthor("Cassandra Guru"); b2.setPages(521); - cassandraTemplate.insert(b2, "book_alt"); + template.insert(b2); - /* - * Test Single Insert with entity - */ Book b3 = new Book(); b3.setIsbn("123456-3"); b3.setTitle("Spring Data Cassandra Guide"); @@ -124,34 +118,27 @@ public class CassandraDataOperationsTest { options.setConsistencyLevel(ConsistencyLevel.ONE); options.setRetryPolicy(RetryPolicy.DOWNGRADING_CONSISTENCY); - cassandraTemplate.insert(b3, "book", options); + template.insert(b3, options); - /* - * Test Single Insert with entity - */ Book b5 = new Book(); b5.setIsbn("123456-5"); b5.setTitle("Spring Data Cassandra Guide"); b5.setAuthor("Cassandra Guru"); b5.setPages(265); - cassandraTemplate.insert(b5, options); - + template.insert(b5, options); } @Test public void insertAsynchronouslyTest() { - /* - * Test Single Insert with entity - */ Book b1 = new Book(); b1.setIsbn("123456-1"); b1.setTitle("Spring Data Cassandra Guide"); b1.setAuthor("Cassandra Guru"); b1.setPages(521); - cassandraTemplate.insertAsynchronously(b1); + template.insertAsynchronously(b1); Book b2 = new Book(); b2.setIsbn("123456-2"); @@ -159,7 +146,7 @@ public class CassandraDataOperationsTest { b2.setAuthor("Cassandra Guru"); b2.setPages(521); - cassandraTemplate.insertAsynchronously(b2, "book_alt"); + template.insertAsynchronously(b2); /* * Test Single Insert with entity @@ -174,7 +161,7 @@ public class CassandraDataOperationsTest { options.setConsistencyLevel(ConsistencyLevel.ONE); options.setRetryPolicy(RetryPolicy.DOWNGRADING_CONSISTENCY); - cassandraTemplate.insertAsynchronously(b3, "book", options); + template.insertAsynchronously(b3, options); /* * Test Single Insert with entity @@ -194,7 +181,7 @@ public class CassandraDataOperationsTest { b5.setAuthor("Cassandra Guru"); b5.setPages(265); - cassandraTemplate.insertAsynchronously(b5, options); + template.insertAsynchronously(b5, options); } @@ -209,19 +196,19 @@ public class CassandraDataOperationsTest { books = getBookList(20); - cassandraTemplate.insert(books); + template.insert(books); books = getBookList(20); - cassandraTemplate.insert(books, "book_alt"); + template.insert(books); books = getBookList(20); - cassandraTemplate.insert(books, "book", options); + template.insert(books, options); books = getBookList(20); - cassandraTemplate.insert(books, options); + template.insert(books, options); } @@ -236,19 +223,19 @@ public class CassandraDataOperationsTest { books = getBookList(20); - cassandraTemplate.insertAsynchronously(books); + template.insertAsynchronously(books); books = getBookList(20); - cassandraTemplate.insertAsynchronously(books, "book_alt"); + template.insertAsynchronously(books); books = getBookList(20); - cassandraTemplate.insertAsynchronously(books, "book", options); + template.insertAsynchronously(books, options); books = getBookList(20); - cassandraTemplate.insertAsynchronously(books, options); + template.insertAsynchronously(books, options); } @@ -290,7 +277,7 @@ public class CassandraDataOperationsTest { b1.setAuthor("Cassandra Guru"); b1.setPages(521); - cassandraTemplate.update(b1); + template.update(b1); Book b2 = new Book(); b2.setIsbn("123456-2"); @@ -298,7 +285,7 @@ public class CassandraDataOperationsTest { b2.setAuthor("Cassandra Guru"); b2.setPages(521); - cassandraTemplate.update(b2, "book_alt"); + template.update(b2); /* * Test Single Insert with entity @@ -309,7 +296,7 @@ public class CassandraDataOperationsTest { b3.setAuthor("Cassandra Guru"); b3.setPages(265); - cassandraTemplate.update(b3, "book", options); + template.update(b3, options); /* * Test Single Insert with entity @@ -320,7 +307,7 @@ public class CassandraDataOperationsTest { b5.setAuthor("Cassandra Guru"); b5.setPages(265); - cassandraTemplate.update(b5, options); + template.update(b5, options); } @@ -342,7 +329,7 @@ public class CassandraDataOperationsTest { b1.setAuthor("Cassandra Guru"); b1.setPages(521); - cassandraTemplate.updateAsynchronously(b1); + template.updateAsynchronously(b1); Book b2 = new Book(); b2.setIsbn("123456-2"); @@ -350,7 +337,7 @@ public class CassandraDataOperationsTest { b2.setAuthor("Cassandra Guru"); b2.setPages(521); - cassandraTemplate.updateAsynchronously(b2, "book_alt"); + template.updateAsynchronously(b2); /* * Test Single Insert with entity @@ -361,7 +348,7 @@ public class CassandraDataOperationsTest { b3.setAuthor("Cassandra Guru"); b3.setPages(265); - cassandraTemplate.updateAsynchronously(b3, "book", options); + template.updateAsynchronously(b3, options); /* * Test Single Insert with entity @@ -372,7 +359,7 @@ public class CassandraDataOperationsTest { b5.setAuthor("Cassandra Guru"); b5.setPages(265); - cassandraTemplate.updateAsynchronously(b5, options); + template.updateAsynchronously(b5, options); } @@ -387,35 +374,35 @@ public class CassandraDataOperationsTest { books = getBookList(20); - cassandraTemplate.insert(books); + template.insert(books); alterBooks(books); - cassandraTemplate.update(books); + template.update(books); books = getBookList(20); - cassandraTemplate.insert(books, "book_alt"); + template.insert(books); alterBooks(books); - cassandraTemplate.update(books, "book_alt"); + template.update(books); books = getBookList(20); - cassandraTemplate.insert(books, "book", options); + template.insert(books, options); alterBooks(books); - cassandraTemplate.update(books, "book", options); + template.update(books, options); books = getBookList(20); - cassandraTemplate.insert(books, options); + template.insert(books, options); alterBooks(books); - cassandraTemplate.update(books, options); + template.update(books, options); } @@ -430,35 +417,35 @@ public class CassandraDataOperationsTest { books = getBookList(20); - cassandraTemplate.insert(books); + template.insert(books); alterBooks(books); - cassandraTemplate.updateAsynchronously(books); + template.updateAsynchronously(books); books = getBookList(20); - cassandraTemplate.insert(books, "book_alt"); + template.insert(books); alterBooks(books); - cassandraTemplate.updateAsynchronously(books, "book_alt"); + template.updateAsynchronously(books); books = getBookList(20); - cassandraTemplate.insert(books, "book", options); + template.insert(books, options); alterBooks(books); - cassandraTemplate.updateAsynchronously(books, "book", options); + template.updateAsynchronously(books, options); books = getBookList(20); - cassandraTemplate.insert(books, options); + template.insert(books, options); alterBooks(books); - cassandraTemplate.updateAsynchronously(books, options); + template.updateAsynchronously(books, options); } @@ -489,12 +476,12 @@ public class CassandraDataOperationsTest { Book b1 = new Book(); b1.setIsbn("123456-1"); - cassandraTemplate.delete(b1); + template.delete(b1); Book b2 = new Book(); b2.setIsbn("123456-2"); - cassandraTemplate.delete(b2, "book_alt"); + template.delete(b2); /* * Test Single Insert with entity @@ -502,7 +489,7 @@ public class CassandraDataOperationsTest { Book b3 = new Book(); b3.setIsbn("123456-3"); - cassandraTemplate.delete(b3, "book", options); + template.delete(b3, options); /* * Test Single Insert with entity @@ -510,7 +497,7 @@ public class CassandraDataOperationsTest { Book b5 = new Book(); b5.setIsbn("123456-5"); - cassandraTemplate.delete(b5, options); + template.delete(b5, options); } @@ -529,12 +516,12 @@ public class CassandraDataOperationsTest { Book b1 = new Book(); b1.setIsbn("123456-1"); - cassandraTemplate.deleteAsynchronously(b1); + template.deleteAsynchronously(b1); Book b2 = new Book(); b2.setIsbn("123456-2"); - cassandraTemplate.deleteAsynchronously(b2, "book_alt"); + template.deleteAsynchronously(b2); /* * Test Single Insert with entity @@ -542,7 +529,7 @@ public class CassandraDataOperationsTest { Book b3 = new Book(); b3.setIsbn("123456-3"); - cassandraTemplate.deleteAsynchronously(b3, "book", options); + template.deleteAsynchronously(b3, options); /* * Test Single Insert with entity @@ -550,7 +537,7 @@ public class CassandraDataOperationsTest { Book b5 = new Book(); b5.setIsbn("123456-5"); - cassandraTemplate.deleteAsynchronously(b5, options); + template.deleteAsynchronously(b5, options); } @@ -565,27 +552,27 @@ public class CassandraDataOperationsTest { books = getBookList(20); - cassandraTemplate.insert(books); + template.insert(books); - cassandraTemplate.delete(books); + template.delete(books); books = getBookList(20); - cassandraTemplate.insert(books, "book_alt"); + template.insert(books); - cassandraTemplate.delete(books, "book_alt"); + template.delete(books); books = getBookList(20); - cassandraTemplate.insert(books, "book", options); + template.insert(books, options); - cassandraTemplate.delete(books, "book", options); + template.delete(books, options); books = getBookList(20); - cassandraTemplate.insert(books, options); + template.insert(books, options); - cassandraTemplate.delete(books, options); + template.delete(books, options); } @@ -600,27 +587,27 @@ public class CassandraDataOperationsTest { books = getBookList(20); - cassandraTemplate.insert(books); + template.insert(books); - cassandraTemplate.deleteAsynchronously(books); + template.deleteAsynchronously(books); books = getBookList(20); - cassandraTemplate.insert(books, "book_alt"); + template.insert(books); - cassandraTemplate.deleteAsynchronously(books, "book_alt"); + template.deleteAsynchronously(books); books = getBookList(20); - cassandraTemplate.insert(books, "book", options); + template.insert(books, options); - cassandraTemplate.deleteAsynchronously(books, "book", options); + template.deleteAsynchronously(books, options); books = getBookList(20); - cassandraTemplate.insert(books, options); + template.insert(books, options); - cassandraTemplate.deleteAsynchronously(books, options); + template.deleteAsynchronously(books, options); } @@ -636,12 +623,12 @@ public class CassandraDataOperationsTest { b1.setAuthor("Cassandra Guru"); b1.setPages(521); - cassandraTemplate.insert(b1); + template.insert(b1); Select select = QueryBuilder.select().all().from("book"); select.where(QueryBuilder.eq("isbn", "123456-1")); - Book b = cassandraTemplate.selectOne(select, Book.class); + Book b = template.selectOne(select.getQueryString(), Book.class); log.info("SingleSelect Book Title -> " + b.getTitle()); log.info("SingleSelect Book Author -> " + b.getAuthor()); @@ -656,11 +643,11 @@ public class CassandraDataOperationsTest { List books = getBookList(20); - cassandraTemplate.insert(books); + template.insert(books); Select select = QueryBuilder.select().all().from("book"); - List b = cassandraTemplate.select(select, Book.class); + List b = template.select(select.getQueryString(), Book.class); log.info("Book Count -> " + b.size()); @@ -671,23 +658,16 @@ public class CassandraDataOperationsTest { @Test public void selectCountTest() { - List books = getBookList(20); + int count = 20; + List books = getBookList(count); - cassandraTemplate.insert(books); - - Select select = QueryBuilder.select().countAll().from("book"); - - Long count = cassandraTemplate.count(select); - - log.info("Book Count -> " + count); - - Assert.assertEquals(count, new Long(20)); + template.insert(books); + Assert.assertEquals(count, template.count(Book.class)); } @After public void clearCassandra() { EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); - } } From 708c52e342963453181764a509b2d50597f00333 Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Thu, 6 Feb 2014 14:19:43 -0600 Subject: [PATCH 4/5] DATACASS-33 - minor update to CqlOperations --- .../cassandra/core/CqlOperations.java | 20 ++++++++++++++--- .../cassandra/core/CqlTemplate.java | 22 +++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java index 66842d696..361acda8c 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlOperations.java @@ -31,6 +31,7 @@ import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.DropTableSpecification; import org.springframework.dao.DataAccessException; +import com.datastax.driver.core.Query; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.Session; @@ -56,17 +57,30 @@ public interface CqlOperations { * Executes the supplied CQL Query and returns nothing. * * @param cql - * @see #query(String) */ void execute(String cql) throws DataAccessException; /** - * Executes the supplied CQL Query Asynchronously and returns nothing. + * Executes the supplied Query and returns nothing. * - * @param cql The CQL Statement to execute + * @param query The {@link Query} to execute + */ + void execute(Query query) throws DataAccessException; + + /** + * Executes the supplied Query Asynchronously and returns nothing. + * + * @param cql The {@link Query} to execute */ void executeAsynchronously(String cql) throws DataAccessException; + /** + * Executes the supplied CQL Query Asynchronously and returns nothing. + * + * @param query The {@link Query} to execute + */ + void executeAsynchronously(Query query) throws DataAccessException; + /** * Executes the provided CQL Query, and extracts the results with the ResultSetExtractor. This uses default Query * Options when extracting the ResultSet. diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java index b7a4363e3..a01d1aec9 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlTemplate.java @@ -64,7 +64,6 @@ import com.datastax.driver.core.SimpleStatement; import com.datastax.driver.core.Statement; import com.datastax.driver.core.exceptions.DriverException; import com.datastax.driver.core.querybuilder.QueryBuilder; -import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.querybuilder.Truncate; /** @@ -105,6 +104,11 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { return doExecute(sessionCallback); } + @Override + public void execute(Query query) throws DataAccessException { + doExecute(query, null); + } + @Override public void execute(final String cql) throws DataAccessException { doExecute(cql, null); @@ -399,14 +403,14 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * @param callback * @return */ - protected ResultSet doExecute(final BoundStatement bs, final QueryOptions options) { + protected ResultSet doExecute(final Query q, final QueryOptions options) { return doExecute(new SessionCallback() { @Override public ResultSet doInSession(Session s) throws DataAccessException { - addQueryOptions(bs, options); - return s.execute(bs); + addQueryOptions(q, options); + return s.execute(q); } }); } @@ -492,6 +496,16 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { }); } + @Override + public void executeAsynchronously(final Query query) throws DataAccessException { + execute(new SessionCallback() { + @Override + public Object doInSession(Session s) throws DataAccessException { + return s.executeAsync(query); + } + }); + } + @Override public void process(ResultSet resultSet, RowCallbackHandler rch) throws DataAccessException { try { From b1f9d5bfc36113ddb1b3053420e0234cd06984ee Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Thu, 6 Feb 2014 17:35:50 -0600 Subject: [PATCH 5/5] DATACASS-33 - fixed test classes in preparation for composite primary key testing --- spring-data-cassandra/pom.xml | 6 ++ ...tractSpringDataCassandraConfiguration.java | 6 +- .../cassandra/core/CassandraOperations.java | 2 +- .../cassandra/core/CassandraTemplate.java | 12 +-- .../cassandra/mapping/PrimaryKeyClass.java | 3 + .../support/SimpleCassandraRepository.java | 2 +- ...gDataEmbeddedCassandraIntegrationTest.java | 74 ++++++++++++- .../{table => composites}/Comment.java | 2 +- .../{table => composites}/CommentPK.java | 2 +- .../{table => composites}/Notification.java | 2 +- .../{table => composites}/NotificationPK.java | 2 +- .../{table => composites}/Post.java | 2 +- .../{table => composites}/PostPK.java | 2 +- .../{table => composites}/Timeline.java | 2 +- .../{table => composites}/TimelinePK.java | 2 +- .../config/CassandraNamespaceTests.java | 21 +--- .../test/integration/config/TestConfig.java | 3 +- ...andraPersistentEntityIntegrationTests.java | 19 +--- ...rRepositoryJavaConfigIntegrationTests.java | 2 +- .../{table => simpletons}/Book.java | 2 +- .../{table => simpletons}/LogEntry.java | 2 +- .../integration/support/ManualDataSet.java | 22 ++++ .../template/CassandraAdminTest.java | 102 ------------------ .../template/CassandraDataOperationsTest.java | 77 ++++++------- .../test/resources/cassandra-keyspace.yaml | 2 +- 25 files changed, 166 insertions(+), 207 deletions(-) rename spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/{table => composites}/Comment.java (96%) rename spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/{table => composites}/CommentPK.java (95%) rename spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/{table => composites}/Notification.java (96%) rename spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/{table => composites}/NotificationPK.java (96%) rename spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/{table => composites}/Post.java (97%) rename spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/{table => composites}/PostPK.java (96%) rename spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/{table => composites}/Timeline.java (95%) rename spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/{table => composites}/TimelinePK.java (95%) rename spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/{table => simpletons}/Book.java (96%) rename spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/{table => simpletons}/LogEntry.java (95%) create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/support/ManualDataSet.java delete mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraAdminTest.java diff --git a/spring-data-cassandra/pom.xml b/spring-data-cassandra/pom.xml index 01c29f55c..389d94e2b 100644 --- a/spring-data-cassandra/pom.xml +++ b/spring-data-cassandra/pom.xml @@ -127,6 +127,12 @@ test + + commons-io + commons-io + 2.4 + test + diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java index 5c06b1447..5a505d5c4 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java @@ -68,7 +68,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC * The base package to scan for entities annotated with {@link Table} annotations. By default, returns the package * name of {@literal this} (this.getClass().getPackage().getName()). */ - public String getMappingBasePackage() { + public String getEntityBasePackage() { return getClass().getPackage().getName(); } @@ -127,13 +127,13 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC /** * Scans the mapping base package for entity classes annotated with {@link Table} or {@link Persistent}. * - * @see #getMappingBasePackage() + * @see #getEntityBasePackage() * @return Set<Class<?>> representing the annotated entity classes found. * @throws ClassNotFoundException */ protected Set> getInitialEntitySet() throws ClassNotFoundException { - String basePackage = getMappingBasePackage(); + String basePackage = getEntityBasePackage(); Set> initialEntitySet = new HashSet>(); if (StringUtils.hasText(basePackage)) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java index e0e5d8586..034982f87 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java @@ -249,7 +249,7 @@ public interface CassandraOperations extends CqlOperations { void deleteById(Class type, Object id); - List selectByIds(Class type, Iterable ids); + List selectBySimpleIds(Class type, Iterable ids); List selectAll(Class type); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index 6f5ce78c9..438291ed1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import org.mortbay.log.Log; import org.springframework.cassandra.core.CqlTemplate; import org.springframework.cassandra.core.QueryOptions; import org.springframework.cassandra.core.SessionCallback; @@ -250,7 +251,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation } @Override - public List selectByIds(Class type, Iterable ids) { + public List selectBySimpleIds(Class type, Iterable ids) { CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(type); @@ -479,7 +480,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation execute(query); } - return entity; // TODO: fix this! + return entity; } protected List batchInsert(List entities, QueryOptions options, boolean asychronously) { @@ -497,8 +498,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation execute(query); } - return entities; // TODO: fix this! You're not supposed to necessarily return the very same entities that went in - // because the database may assign values. + return entities; } /** @@ -525,7 +525,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation execute(query); } - return entities; // TODO: fix this! + return entities; } /** @@ -574,7 +574,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation execute(query); } - return entity; // TODO: fix this! + return entity; } /** diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClass.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClass.java index c99049bd5..613809e86 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClass.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClass.java @@ -21,6 +21,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.data.annotation.Persistent; + /** * Annotates a type that represents the identity type of another class whose instances are stored in a table. *

@@ -33,5 +35,6 @@ import java.lang.annotation.Target; @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE }) +@Persistent public @interface PrimaryKeyClass { } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java index c00a5e4f4..0f104af68 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java @@ -106,7 +106,7 @@ public class SimpleCassandraRepository implements Ca @Override public Iterable findAll(Iterable ids) { - return template.selectByIds(entityInformation.getJavaType(), ids); + return template.selectBySimpleIds(entityInformation.getJavaType(), ids); } protected List findAll(Select query) { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/AbstractSpringDataEmbeddedCassandraIntegrationTest.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/AbstractSpringDataEmbeddedCassandraIntegrationTest.java index da701f3ff..caf1cb27b 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/AbstractSpringDataEmbeddedCassandraIntegrationTest.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/AbstractSpringDataEmbeddedCassandraIntegrationTest.java @@ -1,14 +1,84 @@ package org.springframework.data.cassandra.test.integration; +import static org.springframework.cassandra.core.keyspace.DropTableSpecification.dropTable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.cassandra.core.CassandraOperations; import org.springframework.data.cassandra.test.integration.support.SpringDataBuildProperties; +import org.springframework.data.cassandra.test.integration.template.CassandraDataOperationsTest.Config; +import org.springframework.util.Assert; + +import com.datastax.driver.core.TableMetadata; public class AbstractSpringDataEmbeddedCassandraIntegrationTest extends AbstractEmbeddedCassandraIntegrationTest { - static { - // override necessary superclass statics + public static List SCRIPT; + public static List TABLES; + static { SpringDataBuildProperties props = new SpringDataBuildProperties(); CASSANDRA_NATIVE_PORT = props.getCassandraPort(); } + + public Logger log = LoggerFactory.getLogger(getClass()); + + @Autowired + public CassandraOperations template; + + /** + * Saves all table metadata, then drops & creates all tables. + */ + public void recreateAllTables() { + + saveAllTableMetadata(); + + for (TableMetadata table : TABLES) { + template.execute(dropTable(table.getName())); + template.execute(table.asCQLQuery()); + } + } + + public void saveAllTableMetadata() { + saveAllTableMetadata(false); + } + + /** + * Saves all table metadata statically. + */ + public void saveAllTableMetadata(boolean force) { + + if (TABLES != null && !force) { + return; + } + + TABLES = new ArrayList(template.getSession().getCluster().getMetadata() + .getKeyspace(Config.KEYSPACE_NAME).getTables()); + } + + public List readScriptLines(String resourceName) { + return readScriptLines(resourceName); + } + + /** + * Reads the lines from the script referenced by {@link #RESOURCE}. + */ + public List readScriptLines(String resourceName, boolean force) throws IOException { + + if (SCRIPT != null && !force) { + return SCRIPT; + } + + Assert.hasText(resourceName); + + return SCRIPT = FileUtils.readLines(new ClassPathResource(resourceName).getFile()); + } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Comment.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Comment.java similarity index 96% rename from spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Comment.java rename to spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Comment.java index f01875f11..30e8b06ca 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Comment.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Comment.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.test.integration.table; +package org.springframework.data.cassandra.test.integration.composites; import java.util.Date; import java.util.Set; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/CommentPK.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentPK.java similarity index 95% rename from spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/CommentPK.java rename to spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentPK.java index 3a870b9ea..8aa49748e 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/CommentPK.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentPK.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.test.integration.table; +package org.springframework.data.cassandra.test.integration.composites; import java.util.Date; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Notification.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Notification.java similarity index 96% rename from spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Notification.java rename to spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Notification.java index 999623fa9..1a4cd4a44 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Notification.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Notification.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.test.integration.table; +package org.springframework.data.cassandra.test.integration.composites; import java.util.Date; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/NotificationPK.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/NotificationPK.java similarity index 96% rename from spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/NotificationPK.java rename to spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/NotificationPK.java index 1689860e6..be92429b7 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/NotificationPK.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/NotificationPK.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.test.integration.table; +package org.springframework.data.cassandra.test.integration.composites; import java.util.Date; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Post.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Post.java similarity index 97% rename from spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Post.java rename to spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Post.java index 89dcc4cc3..3c841695a 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Post.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Post.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.test.integration.table; +package org.springframework.data.cassandra.test.integration.composites; import java.util.Date; import java.util.Map; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/PostPK.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/PostPK.java similarity index 96% rename from spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/PostPK.java rename to spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/PostPK.java index fcdd78455..aa913449c 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/PostPK.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/PostPK.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.test.integration.table; +package org.springframework.data.cassandra.test.integration.composites; import java.util.Date; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Timeline.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Timeline.java similarity index 95% rename from spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Timeline.java rename to spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Timeline.java index 78d3d6854..18666ea47 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Timeline.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/Timeline.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.test.integration.table; +package org.springframework.data.cassandra.test.integration.composites; import java.util.Date; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/TimelinePK.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/TimelinePK.java similarity index 95% rename from spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/TimelinePK.java rename to spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/TimelinePK.java index 1107868f1..ae6e296bb 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/TimelinePK.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/TimelinePK.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.test.integration.table; +package org.springframework.data.cassandra.test.integration.composites; import java.util.Date; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests.java index 3099814bc..66eb3ee1a 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests.java @@ -1,38 +1,21 @@ package org.springframework.data.cassandra.test.integration.config; -import java.io.IOException; - -import org.apache.cassandra.exceptions.ConfigurationException; -import org.apache.thrift.transport.TTransportException; -import org.cassandraunit.utils.EmbeddedCassandraServerHelper; -import org.junit.After; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import org.springframework.data.cassandra.test.integration.AbstractSpringDataEmbeddedCassandraIntegrationTest; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.Assert; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration -public class CassandraNamespaceTests { +public class CassandraNamespaceTests extends AbstractSpringDataEmbeddedCassandraIntegrationTest { @Autowired private ApplicationContext ctx; - @BeforeClass - public static void startCassandra() throws IOException, TTransportException, ConfigurationException, - InterruptedException { - EmbeddedCassandraServerHelper.startEmbeddedCassandra("spring-cassandra.yaml"); - } - - @After - public void clearCassandra() { - EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); - } - @Test public void test() { Assert.notNull(ctx); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/config/TestConfig.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/config/TestConfig.java index 9ec75b2bc..f83cc04f4 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/config/TestConfig.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/config/TestConfig.java @@ -1,5 +1,6 @@ package org.springframework.data.cassandra.test.integration.config; +import org.springframework.cassandra.test.unit.support.Utils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.cassandra.config.java.AbstractSpringDataCassandraConfiguration; @@ -23,7 +24,7 @@ public class TestConfig extends AbstractSpringDataCassandraConfiguration { public static final int PORT = PROPS.getCassandraPort(); public static final int RPC_PORT = PROPS.getCassandraRpcPort(); - public static final String KEYSPACE_NAME = "test"; + public static final String KEYSPACE_NAME = Utils.randomKeyspaceName(); @Override protected String getKeyspaceName() { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java index b5f2e88d6..69dda8e6b 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java @@ -19,13 +19,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.when; -import java.io.IOException; - -import org.apache.cassandra.exceptions.ConfigurationException; -import org.apache.thrift.transport.TTransportException; import org.cassandraunit.utils.EmbeddedCassandraServerHelper; import org.junit.After; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -33,6 +28,7 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.context.ApplicationContext; import org.springframework.data.cassandra.mapping.BasicCassandraPersistentEntity; import org.springframework.data.cassandra.mapping.Table; +import org.springframework.data.cassandra.test.integration.AbstractSpringDataEmbeddedCassandraIntegrationTest; import org.springframework.data.util.ClassTypeInformation; /** @@ -41,17 +37,11 @@ import org.springframework.data.util.ClassTypeInformation; * @author Alex Shvid */ @RunWith(MockitoJUnitRunner.class) -public class BasicCassandraPersistentEntityIntegrationTests { +public class BasicCassandraPersistentEntityIntegrationTests extends AbstractSpringDataEmbeddedCassandraIntegrationTest { @Mock ApplicationContext context; - @BeforeClass - public static void startCassandra() throws IOException, TTransportException, ConfigurationException, - InterruptedException { - EmbeddedCassandraServerHelper.startEmbeddedCassandra("spring-cassandra.yaml"); - } - @Test public void subclassInheritsAtDocumentAnnotation() { @@ -84,11 +74,6 @@ public class BasicCassandraPersistentEntityIntegrationTests { assertThat(entity.getTableName(), is(bean.tableName)); } - @After - public void clearCassandra() { - EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); - } - @Table("messages") static class Message { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java index 8820a6608..2de6a2a07 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java @@ -68,7 +68,7 @@ public class UserRepositoryJavaConfigIntegrationTests extends AbstractSpringData } @Override - public String getMappingBasePackage() { + public String getEntityBasePackage() { return User.class.getPackage().getName(); } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Book.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/simpletons/Book.java similarity index 96% rename from spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Book.java rename to spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/simpletons/Book.java index 9ba3dd3b9..95fcfd700 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/Book.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/simpletons/Book.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.test.integration.table; +package org.springframework.data.cassandra.test.integration.simpletons; import org.springframework.data.cassandra.mapping.PrimaryKey; import org.springframework.data.cassandra.mapping.Table; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/LogEntry.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/simpletons/LogEntry.java similarity index 95% rename from spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/LogEntry.java rename to spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/simpletons/LogEntry.java index 3ba920b46..b40d9f379 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/table/LogEntry.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/simpletons/LogEntry.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.cassandra.test.integration.table; +package org.springframework.data.cassandra.test.integration.simpletons; import java.util.Date; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/support/ManualDataSet.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/support/ManualDataSet.java new file mode 100644 index 000000000..805b9db13 --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/support/ManualDataSet.java @@ -0,0 +1,22 @@ +package org.springframework.data.cassandra.test.integration.support; + +import org.cassandraunit.dataset.commons.AbstractCommonsParserDataSet; +import org.cassandraunit.dataset.commons.ParsedKeyspace; + +public class ManualDataSet extends AbstractCommonsParserDataSet { + + private String keyspaceName; + private ParsedKeyspace pks; + + public ManualDataSet(String keyspaceName) { + this.keyspaceName = keyspaceName; + + pks = new ParsedKeyspace(); + pks.setName(this.keyspaceName); + } + + @Override + protected ParsedKeyspace getParsedKeyspace() { + return pks; + } +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraAdminTest.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraAdminTest.java deleted file mode 100644 index d5ccd5d79..000000000 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraAdminTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2011-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.cassandra.test.integration.template; - -import java.io.IOException; - -import org.apache.cassandra.exceptions.ConfigurationException; -import org.apache.thrift.transport.TTransportException; -import org.cassandraunit.DataLoader; -import org.cassandraunit.dataset.yaml.ClassPathYamlDataSet; -import org.cassandraunit.utils.EmbeddedCassandraServerHelper; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cassandra.core.CqlOperations; -import org.springframework.context.ApplicationContext; -import org.springframework.data.cassandra.test.integration.config.TestConfig; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.support.AnnotationConfigContextLoader; - -/** - * @author David Webb - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigContextLoader.class) -public class CassandraAdminTest { - - @Autowired - private CqlOperations cassandraTemplate; - - @Mock - ApplicationContext context; - - @BeforeClass - public static void startCassandra() throws IOException, TTransportException, ConfigurationException, - InterruptedException { - EmbeddedCassandraServerHelper.startEmbeddedCassandra("spring-cassandra.yaml"); - - /* - * Load data file to creat the test keyspace before we init the template - */ - DataLoader dataLoader = new DataLoader("Test Cluster", "localhost:" + TestConfig.RPC_PORT); - dataLoader.load(new ClassPathYamlDataSet("cassandra-keyspace.yaml")); - - } - - @Before - public void setupKeyspace() { - - /* - * Load data file to creat the test keyspace before we init the template - */ - DataLoader dataLoader = new DataLoader("Test Cluster", "localhost:" + TestConfig.RPC_PORT); - dataLoader.load(new ClassPathYamlDataSet("cassandra-keyspace.yaml")); - - } - - @Test - public void alterTableTest() { - - // cassandraTemplate.alterTable(UserAlter.class); - - } - - @Test - public void dropTableTest() { - - // cassandraTemplate.dropTable(User.class); - // cassandraTemplate.dropTable("comments"); - - } - - @After - public void clearCassandra() { - EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); - } - - @AfterClass - public static void stopCassandra() { - EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); - } -} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java index b32298259..173729928 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java @@ -15,22 +15,18 @@ */ package org.springframework.data.cassandra.test.integration.template; +import static org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification.createKeyspace; +import static org.springframework.cassandra.core.keyspace.DropTableSpecification.dropTable; + import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.UUID; -import org.apache.cassandra.exceptions.ConfigurationException; -import org.apache.thrift.transport.TTransportException; -import org.cassandraunit.CassandraCQLUnit; -import org.cassandraunit.DataLoader; -import org.cassandraunit.dataset.cql.ClassPathCQLDataSet; -import org.cassandraunit.dataset.yaml.ClassPathYamlDataSet; -import org.cassandraunit.utils.EmbeddedCassandraServerHelper; -import org.junit.After; +import org.apache.commons.io.FileUtils; import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Rule; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; @@ -39,14 +35,19 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cassandra.core.ConsistencyLevel; import org.springframework.cassandra.core.QueryOptions; import org.springframework.cassandra.core.RetryPolicy; +import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.data.cassandra.config.SchemaAction; import org.springframework.data.cassandra.core.CassandraOperations; +import org.springframework.data.cassandra.test.integration.AbstractSpringDataEmbeddedCassandraIntegrationTest; import org.springframework.data.cassandra.test.integration.config.TestConfig; -import org.springframework.data.cassandra.test.integration.support.SpringDataBuildProperties; -import org.springframework.data.cassandra.test.integration.table.Book; +import org.springframework.data.cassandra.test.integration.simpletons.Book; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.support.AnnotationConfigContextLoader; +import com.datastax.driver.core.TableMetadata; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; @@ -57,36 +58,31 @@ import com.datastax.driver.core.querybuilder.Select; * */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigContextLoader.class) -public class CassandraDataOperationsTest { +@ContextConfiguration +public class CassandraDataOperationsTest extends AbstractSpringDataEmbeddedCassandraIntegrationTest { - @Autowired - private CassandraOperations template; + @Configuration + public static class Config extends TestConfig { - private static Logger log = LoggerFactory.getLogger(CassandraDataOperationsTest.class); + @Override + public SchemaAction getSchemaAction() { + return SchemaAction.RECREATE; + } - public static final SpringDataBuildProperties PROPS = new SpringDataBuildProperties(); - private final static String CASSANDRA_CONFIG = "spring-cassandra.yaml"; - private final static String KEYSPACE_NAME = "test"; - private final static String CASSANDRA_HOST = "localhost"; - private final static int CASSANDRA_NATIVE_PORT = PROPS.getCassandraPort(); - private final static int CASSANDRA_THRIFT_PORT = PROPS.getCassandraRpcPort(); + @Override + protected List getKeyspaceCreations() { + return Arrays.asList(createKeyspace().name(getKeyspaceName()).withSimpleReplication()); + } - @Rule - public CassandraCQLUnit cassandraCQLUnit = new CassandraCQLUnit(new ClassPathCQLDataSet("cql-dataload.cql", - KEYSPACE_NAME), CASSANDRA_CONFIG, CASSANDRA_HOST, CASSANDRA_NATIVE_PORT); + @Override + public String getEntityBasePackage() { + return Book.class.getPackage().getName(); + } + } - @BeforeClass - public static void startCassandra() throws IOException, TTransportException, ConfigurationException, - InterruptedException { - - EmbeddedCassandraServerHelper.startEmbeddedCassandra(CASSANDRA_CONFIG); - - /* - * Load data file to creat the test keyspace before we init the template - */ - DataLoader dataLoader = new DataLoader("Test Cluster", CASSANDRA_HOST + ":" + CASSANDRA_THRIFT_PORT); - dataLoader.load(new ClassPathYamlDataSet("cassandra-keyspace.yaml")); + @Before + public void before() throws IOException { + recreateAllTables(); } @Test @@ -665,9 +661,4 @@ public class CassandraDataOperationsTest { Assert.assertEquals(count, template.count(Book.class)); } - - @After - public void clearCassandra() { - EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); - } } diff --git a/spring-data-cassandra/src/test/resources/cassandra-keyspace.yaml b/spring-data-cassandra/src/test/resources/cassandra-keyspace.yaml index a0e13da6e..3442321c5 100644 --- a/spring-data-cassandra/src/test/resources/cassandra-keyspace.yaml +++ b/spring-data-cassandra/src/test/resources/cassandra-keyspace.yaml @@ -1,3 +1,3 @@ -name: test +name: ks9487fb2da1a541e39a05e5545d64411a replicationFactor: 1 strategy: org.apache.cassandra.locator.SimpleStrategy \ No newline at end of file