diff --git a/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java b/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java index dbb5cb6d1..98f512fd3 100644 --- a/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java +++ b/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java @@ -22,9 +22,11 @@ import org.springframework.data.cassandra.convert.CassandraConverter; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSetFuture; +import com.datastax.driver.core.querybuilder.Select; /** - * Main Inteface that should be used for Cassandra interactions + * 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. * * @author Alex Shvid * @author David Webb @@ -70,7 +72,7 @@ public interface CassandraOperations { * @param selectClass must not be {@literal null}, mapped entity type. * @return */ - List select(String query, Class selectClass); + List selectByCQL(String query, Class selectClass); /** * Execute query and convert ResultSet to the entity @@ -79,7 +81,11 @@ public interface CassandraOperations { * @param selectClass must not be {@literal null}, mapped entity type. * @return */ - T selectOne(String query, Class selectClass); + T selectOneByCQL(String query, Class selectClass); + + List select(Select selectQuery, Class selectClass); + + T selectOne(Select selectQuery, Class selectClass); /** * Insert the given object to the table by id. diff --git a/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index b9ff28832..598730268 100644 --- a/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -32,12 +32,10 @@ import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.cassandra.convert.CassandraConverter; -import org.springframework.data.cassandra.core.exceptions.CassandraConnectionFailureException; import org.springframework.data.cassandra.exception.EntityWriterException; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; import org.springframework.data.cassandra.util.CqlUtils; -import org.springframework.data.convert.EntityReader; import org.springframework.data.mapping.context.MappingContext; import org.springframework.util.Assert; @@ -48,8 +46,8 @@ import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.Row; import com.datastax.driver.core.Session; -import com.datastax.driver.core.exceptions.NoHostAvailableException; import com.datastax.driver.core.querybuilder.Batch; +import com.datastax.driver.core.querybuilder.Select; /** * The Cassandra Template is a convenience API for all Cassnadra DML Operations. @@ -59,31 +57,6 @@ import com.datastax.driver.core.querybuilder.Batch; */ public class CassandraTemplate implements CassandraOperations { - /** - * Simple {@link RowCallback} that will transform {@link Row} into the given target type using the given - * {@link EntityReader}. - * - * @author Alex Shvid - */ - private static class ReadRowCallback implements RowCallback { - - private final EntityReader reader; - private final Class type; - - public ReadRowCallback(EntityReader 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; - } - } - private static Logger log = LoggerFactory.getLogger(CassandraTemplate.class); public static final Collection ITERABLE_CLASSES; @@ -700,20 +673,36 @@ public class CassandraTemplate implements CassandraOperations { return insertAsynchronously(entity, tableName, options.toMap()); } + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.CassandraOperations#select(com.datastax.driver.core.querybuilder.Select, java.lang.Class) + */ + @Override + public List select(Select selectQuery, Class selectClass) { + return selectByCQL(selectQuery.getQueryString(), selectClass); + } + /* (non-Javadoc) * @see org.springframework.data.cassandra.core.CassandraOperations#select(java.lang.String, java.lang.Class) */ @Override - public List select(String query, Class selectClass) { - return selectInternal(query, new ReadRowCallback(cassandraConverter, selectClass)); + public List selectByCQL(String query, Class selectClass) { + return doSelect(query, new ReadRowCallback(cassandraConverter, selectClass)); + } + + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.CassandraOperations#selectOne(com.datastax.driver.core.querybuilder.Select, java.lang.Class) + */ + @Override + public T selectOne(Select selectQuery, Class selectClass) { + return selectOneByCQL(selectQuery.getQueryString(), selectClass); } /* (non-Javadoc) * @see org.springframework.data.cassandra.core.CassandraOperations#selectOne(java.lang.String, java.lang.Class) */ @Override - public T selectOne(String query, Class selectClass) { - return selectOneInternal(query, new ReadRowCallback(cassandraConverter, selectClass)); + public T selectOneByCQL(String query, Class selectClass) { + return doSelectOne(query, new ReadRowCallback(cassandraConverter, selectClass)); } /* (non-Javadoc) @@ -958,6 +947,70 @@ public class CassandraTemplate implements CassandraOperations { return null; } + /** + * @param query + * @param readRowCallback + * @return + */ + private List doSelect(final String query, ReadRowCallback readRowCallback) { + + ResultSet resultSet = execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + return s.execute(query); + } + }); + + if (resultSet == null) { + return null; + } + + List result = new ArrayList(); + Iterator iterator = resultSet.iterator(); + while (iterator.hasNext()) { + Row row = iterator.next(); + result.add(readRowCallback.doWith(row)); + } + + return result; + } + + /** + * @param query + * @param readRowCallback + * @return + */ + private T doSelectOne(final String query, ReadRowCallback readRowCallback) { + + /* + * Run the Query + */ + ResultSet resultSet = execute(new SessionCallback() { + + @Override + public ResultSet doInSession(Session s) throws DataAccessException { + return s.execute(query); + } + }); + + if (resultSet == null) { + return null; + } + + Iterator iterator = resultSet.iterator(); + if (iterator.hasNext()) { + Row row = iterator.next(); + T result = readRowCallback.doWith(row); + if (iterator.hasNext()) { + throw new DuplicateKeyException("found two or more results in query " + query); + } + return result; + } + + return null; + } + private RuntimeException potentiallyConvertRuntimeException(RuntimeException ex) { RuntimeException resolved = this.exceptionTranslator.translateExceptionIfPossible(ex); return resolved == null ? ex : resolved; @@ -1240,51 +1293,4 @@ public class CassandraTemplate implements CassandraOperations { } } - /** - * @param query - * @param readRowCallback - * @return - */ - List selectInternal(String query, ReadRowCallback readRowCallback) { - try { - ResultSet resultSet = session.execute(query); - List result = new ArrayList(); - Iterator iterator = resultSet.iterator(); - while (iterator.hasNext()) { - Row row = iterator.next(); - result.add(readRowCallback.doWith(row)); - } - return result; - } catch (NoHostAvailableException e) { - throw new CassandraConnectionFailureException(null, "no host available", e); - } catch (RuntimeException e) { - throw potentiallyConvertRuntimeException(e); - } - } - - /** - * @param query - * @param readRowCallback - * @return - */ - T selectOneInternal(String query, ReadRowCallback readRowCallback) { - try { - ResultSet resultSet = session.execute(query); - Iterator iterator = resultSet.iterator(); - if (iterator.hasNext()) { - Row row = iterator.next(); - T result = readRowCallback.doWith(row); - if (iterator.hasNext()) { - throw new DuplicateKeyException("found two or more results in query " + query); - } - return result; - } - return null; - } catch (NoHostAvailableException e) { - throw new CassandraConnectionFailureException(null, "no host available", e); - } catch (RuntimeException e) { - throw potentiallyConvertRuntimeException(e); - } - } - } diff --git a/src/main/java/org/springframework/data/cassandra/core/QueryOptions.java b/src/main/java/org/springframework/data/cassandra/core/QueryOptions.java index 86ea87a6b..5d3755dfb 100644 --- a/src/main/java/org/springframework/data/cassandra/core/QueryOptions.java +++ b/src/main/java/org/springframework/data/cassandra/core/QueryOptions.java @@ -19,7 +19,7 @@ import java.util.HashMap; import java.util.Map; /** - * Contains Query Options for Cassnadra queries. This controls the Consistency Tuning and Retry Policy for a Query. + * Contains Query Options for Cassandra queries. This controls the Consistency Tuning and Retry Policy for a Query. * * @author David Webb * diff --git a/src/main/java/org/springframework/data/cassandra/core/ReadRowCallback.java b/src/main/java/org/springframework/data/cassandra/core/ReadRowCallback.java new file mode 100644 index 000000000..bccc4ded7 --- /dev/null +++ b/src/main/java/org/springframework/data/cassandra/core/ReadRowCallback.java @@ -0,0 +1,46 @@ +/* + * 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.core; + +import org.springframework.data.convert.EntityReader; +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}. + * + * @author Alex Shvid + */ +public class ReadRowCallback implements RowCallback { + + private final EntityReader reader; + private final Class type; + + public ReadRowCallback(EntityReader 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; + } +} \ No newline at end of file diff --git a/src/test/java/org/springframework/data/cassandra/template/CassandraOperationsTest.java b/src/test/java/org/springframework/data/cassandra/template/CassandraOperationsTest.java index 433b89825..7235c9334 100644 --- a/src/test/java/org/springframework/data/cassandra/template/CassandraOperationsTest.java +++ b/src/test/java/org/springframework/data/cassandra/template/CassandraOperationsTest.java @@ -24,6 +24,8 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import junit.framework.Assert; + import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.thrift.transport.TTransportException; import org.cassandraunit.CassandraCQLUnit; @@ -51,8 +53,11 @@ 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.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; + /** - * Unit Tests for CassnadraTemplate + * Unit Tests for CassandraTemplate * * @author David Webb * @@ -880,6 +885,33 @@ public class CassandraOperationsTest { } + @Test + public void selectTest() { + + /* + * 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); + + Select select = QueryBuilder.select().all().from("book"); + select.where(QueryBuilder.eq("isbn", "123456-1")); + + Book b = cassandraTemplate.selectOne(select, Book.class); + + log.info("SingleSelect Book Title -> " + b.getTitle()); + log.info("SingleSelect Book Author -> " + b.getAuthor()); + + Assert.assertEquals(b.getTitle(), "Spring Data Cassandra Guide"); + Assert.assertEquals(b.getAuthor(), "Cassandra Guru"); + + } + @After public void clearCassandra() { EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();