From 95978416d14d81da47bbff4a3ee01ee33faf1a39 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 25 Jul 2016 16:40:41 +0200 Subject: [PATCH] #203 - Revise Cassandra Examples. Simplify examples by adopting Spring Boot 1.4 improvements. Upgrade Cassandra Java driver to 3.0.3. Add examples for Ingalls (Query derivation, projection, Java 8 feature support). Cassandra example setup is now self-contained by requiring just a running Cassandra instance. Keyspace and tables are created during the tests. Examples also check if Cassandra is running and some examples additionally check the required version. Test execution is skipped if conditions are not met. Cassandra (2.x) is started with TravisCI. --- .travis.yml | 3 +- cassandra/example/Readme.MD | 25 +-- cassandra/example/pom.xml | 20 ++- .../BasicConfiguration.java} | 26 ++- .../cassandra/basic/BasicUserRepository.java | 58 ++++++ .../cassandra/{ => basic}/User.java | 4 +- .../cassandra/{ => basic}/package-info.java | 2 +- .../Addressbook.java} | 26 ++- .../springdata/cassandra/convert/Contact.java | 34 ++++ .../convert/ConverterConfiguration.java | 116 ++++++++++++ .../cassandra/convert/CustomAddressbook.java | 28 +++ .../cassandra/convert/package-info.java | 4 + .../cassandra/projection/Customer.java | 32 ++++ .../projection/CustomerProjection.java | 26 +++ .../projection/CustomerRepository.java | 72 ++++++++ .../cassandra/projection/CustomerSummary.java | 29 +++ .../projection/ProjectionConfiguration.java | 58 ++++++ .../cassandra/projection/package-info.java | 4 + .../cassandra/SimpleUserRepositoryTests.java | 65 ------- .../basic/BasicUserRepositoryTests.java | 129 ++++++++++++++ .../CassandraOperationsIntegrationTests.java | 166 ++++++++++++++++++ .../convert/ConversionIntegrationTests.java | 119 +++++++++++++ .../CustomerRepositoryIntegrationTest.java | 99 +++++++++++ cassandra/java8/README.md | 36 ++++ cassandra/java8/pom.xml | 26 +++ .../java8/CassandraConfiguration.java | 45 +++++ .../springdata/cassandra/java8/Order.java | 40 +++++ .../cassandra/java8/OrderRepository.java | 47 +++++ .../springdata/cassandra/java8/Person.java | 37 ++++ .../cassandra/java8/PersonRepository.java | 65 +++++++ .../cassandra/java8/package-info.java | 4 + .../java8/Java8IntegrationTests.java | 87 +++++++++ .../java8/Jsr310IntegrationTests.java | 76 ++++++++ cassandra/pom.xml | 15 +- cassandra/util/pom.xml | 22 +++ .../cassandra/util/CassandraVersion.java | 50 ++++++ .../util/RequiresCassandraKeyspace.java | 124 +++++++++++++ pom.xml | 1 + 38 files changed, 1708 insertions(+), 112 deletions(-) rename cassandra/example/src/main/java/example/springdata/cassandra/{SimpleConfiguration.java => basic/BasicConfiguration.java} (65%) create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/basic/BasicUserRepository.java rename cassandra/example/src/main/java/example/springdata/cassandra/{ => basic}/User.java (92%) rename cassandra/example/src/main/java/example/springdata/cassandra/{ => basic}/package-info.java (71%) rename cassandra/example/src/main/java/example/springdata/cassandra/{SimpleUserRepository.java => convert/Addressbook.java} (57%) create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/convert/Contact.java create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/convert/ConverterConfiguration.java create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/convert/CustomAddressbook.java create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/convert/package-info.java create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/projection/Customer.java create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerProjection.java create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerRepository.java create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerSummary.java create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/projection/ProjectionConfiguration.java create mode 100644 cassandra/example/src/main/java/example/springdata/cassandra/projection/package-info.java delete mode 100644 cassandra/example/src/test/java/example/springdata/cassandra/SimpleUserRepositoryTests.java create mode 100644 cassandra/example/src/test/java/example/springdata/cassandra/basic/BasicUserRepositoryTests.java create mode 100644 cassandra/example/src/test/java/example/springdata/cassandra/basic/CassandraOperationsIntegrationTests.java create mode 100644 cassandra/example/src/test/java/example/springdata/cassandra/convert/ConversionIntegrationTests.java create mode 100644 cassandra/example/src/test/java/example/springdata/cassandra/projection/CustomerRepositoryIntegrationTest.java create mode 100644 cassandra/java8/README.md create mode 100644 cassandra/java8/pom.xml create mode 100644 cassandra/java8/src/main/java/example/springdata/cassandra/java8/CassandraConfiguration.java create mode 100644 cassandra/java8/src/main/java/example/springdata/cassandra/java8/Order.java create mode 100644 cassandra/java8/src/main/java/example/springdata/cassandra/java8/OrderRepository.java create mode 100644 cassandra/java8/src/main/java/example/springdata/cassandra/java8/Person.java create mode 100644 cassandra/java8/src/main/java/example/springdata/cassandra/java8/PersonRepository.java create mode 100644 cassandra/java8/src/main/java/example/springdata/cassandra/java8/package-info.java create mode 100644 cassandra/java8/src/test/java/example/springdata/cassandra/java8/Java8IntegrationTests.java create mode 100644 cassandra/java8/src/test/java/example/springdata/cassandra/java8/Jsr310IntegrationTests.java create mode 100644 cassandra/util/pom.xml create mode 100644 cassandra/util/src/main/java/example/springdata/cassandra/util/CassandraVersion.java create mode 100644 cassandra/util/src/main/java/example/springdata/cassandra/util/RequiresCassandraKeyspace.java diff --git a/.travis.yml b/.travis.yml index 9f82cf44..41503a71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ jdk: services: - redis-server + - cassandra cache: directories: @@ -14,4 +15,4 @@ sudo: false install: true -script: "mvn clean dependency:list test -Dsort" \ No newline at end of file +script: "mvn clean dependency:list test -Dsort" diff --git a/cassandra/example/Readme.MD b/cassandra/example/Readme.MD index ed96c1d5..be5d0a2b 100644 --- a/cassandra/example/Readme.MD +++ b/cassandra/example/Readme.MD @@ -6,8 +6,6 @@ Before we can start we have to install Cassandra, e.g. via brew on Max OS. ``` brew install cassandra -pip install cassandra-driver -brew install cassandra-driver More details can be found here: https://wiki.apache.org/cassandra/GettingStarted ``` @@ -17,26 +15,5 @@ More details can be found here: https://wiki.apache.org/cassandra/GettingStarted /usr/local/bin/cassandra -f ``` -### Start a Cassandra shell cqlsh -``` -/usr/local/bin/cqlsh -``` - -### Setup Keyspace and Tables - -``` -CREATE KEYSPACE example -WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; - -USE example; - -CREATE TABLE users ( - user_id int PRIMARY KEY, - uname text, - fname text, - lname text -); -``` - That should be enough to get you started. -Now you can simply type ```mvn clean install``` to run the example. \ No newline at end of file +Now you can simply type ```mvn clean install``` to run the example. diff --git a/cassandra/example/pom.xml b/cassandra/example/pom.xml index 5caab14a..d3624fcc 100644 --- a/cassandra/example/pom.xml +++ b/cassandra/example/pom.xml @@ -12,6 +12,22 @@ Spring Data Cassandra - Example - Small sample project showing the usage of Spring Data Cassandra. - + Basic sample project showing the usage of Spring Data Cassandra. + + + + + ${project.groupId} + spring-data-cassandra-example-utils + ${project.version} + test + + + + com.fasterxml.jackson.core + jackson-databind + + + + diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/SimpleConfiguration.java b/cassandra/example/src/main/java/example/springdata/cassandra/basic/BasicConfiguration.java similarity index 65% rename from cassandra/example/src/main/java/example/springdata/cassandra/SimpleConfiguration.java rename to cassandra/example/src/main/java/example/springdata/cassandra/basic/BasicConfiguration.java index 259934a2..38a27f8a 100644 --- a/cassandra/example/src/main/java/example/springdata/cassandra/SimpleConfiguration.java +++ b/cassandra/example/src/main/java/example/springdata/cassandra/basic/BasicConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2013-2016 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. @@ -13,37 +13,51 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package example.springdata.cassandra; +package example.springdata.cassandra.basic; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.cassandra.config.java.AbstractCqlTemplateConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.cassandra.config.SchemaAction; +import org.springframework.data.cassandra.config.java.AbstractCassandraConfiguration; import org.springframework.data.cassandra.core.CassandraTemplate; import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; import com.datastax.driver.core.Session; /** + * Basic {@link Configuration} to create the necessary schema for the {@link User} table. + * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ @Configuration @EnableAutoConfiguration -class SimpleConfiguration { +class BasicConfiguration { @Configuration @EnableCassandraRepositories - static class CassandraConfig extends AbstractCqlTemplateConfiguration { + static class CassandraConfig extends AbstractCassandraConfiguration { @Override public String getKeyspaceName() { return "example"; } - + @Bean public CassandraTemplate cassandraTemplate(Session session) { return new CassandraTemplate(session); } + + @Override + public String[] getEntityBasePackages() { + return new String[] { User.class.getPackage().getName() }; + } + + @Override + public SchemaAction getSchemaAction() { + return SchemaAction.RECREATE; + } } } diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/basic/BasicUserRepository.java b/cassandra/example/src/main/java/example/springdata/cassandra/basic/BasicUserRepository.java new file mode 100644 index 00000000..a02b580a --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/basic/BasicUserRepository.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2016 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 example.springdata.cassandra.basic; + +import java.util.List; + +import org.springframework.data.cassandra.repository.Query; +import org.springframework.data.repository.CrudRepository; + +/** + * Simple repository interface for {@link User} instances. The interface is used to declare so called query methods, + * methods to retrieve single entities or collections of them. + * + * @author Thomas Darimont + */ +public interface BasicUserRepository extends CrudRepository { + + /** + * Sample method annotated with {@link Query}. This method executes the CQL from the {@link Query} value. + * + * @param id + * @return + */ + @Query("SELECT * from users where user_id in(?0)") + User findUserByIdIn(long id); + + /** + * Derived query method. This query corresponds with {@code SELECT * FROM users WHERE uname = ?0}. + * {@link User#username} is not part of the primary so it requires a secondary index. + * + * @param username + * @return + */ + User findUserByUsername(String username); + + /** + * Derived query method using SASI (SSTable Attached Secondary Index) features through the {@code LIKE} keyword. This + * query corresponds with {@code SELECT * FROM users WHERE uname LIKE '?0%'}. {@link User#username} is not part of the + * primary so it requires a secondary index. + * + * @param lastnamePrefix + * @return + */ + List findUsersByLastnameStartsWith(String lastnamePrefix); +} diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/User.java b/cassandra/example/src/main/java/example/springdata/cassandra/basic/User.java similarity index 92% rename from cassandra/example/src/main/java/example/springdata/cassandra/User.java rename to cassandra/example/src/main/java/example/springdata/cassandra/basic/User.java index e4662e1f..7203c6b3 100644 --- a/cassandra/example/src/main/java/example/springdata/cassandra/User.java +++ b/cassandra/example/src/main/java/example/springdata/cassandra/basic/User.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2013-2016 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package example.springdata.cassandra; +package example.springdata.cassandra.basic; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/package-info.java b/cassandra/example/src/main/java/example/springdata/cassandra/basic/package-info.java similarity index 71% rename from cassandra/example/src/main/java/example/springdata/cassandra/package-info.java rename to cassandra/example/src/main/java/example/springdata/cassandra/basic/package-info.java index 6e1df637..177b26e6 100644 --- a/cassandra/example/src/main/java/example/springdata/cassandra/package-info.java +++ b/cassandra/example/src/main/java/example/springdata/cassandra/basic/package-info.java @@ -1,5 +1,5 @@ /** * Package showing a simple repository interface to use basic query method execution functionality. */ -package example.springdata.cassandra; +package example.springdata.cassandra.basic; diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/SimpleUserRepository.java b/cassandra/example/src/main/java/example/springdata/cassandra/convert/Addressbook.java similarity index 57% rename from cassandra/example/src/main/java/example/springdata/cassandra/SimpleUserRepository.java rename to cassandra/example/src/main/java/example/springdata/cassandra/convert/Addressbook.java index 3989172f..0caff593 100644 --- a/cassandra/example/src/main/java/example/springdata/cassandra/SimpleUserRepository.java +++ b/cassandra/example/src/main/java/example/springdata/cassandra/convert/Addressbook.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2016 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. @@ -13,14 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package example.springdata.cassandra; +package example.springdata.cassandra.convert; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.annotation.Id; +import org.springframework.data.cassandra.mapping.Table; + +import lombok.Data; + +import java.util.List; /** - * Simple repository interface for {@link User} instances. The interface is used to declare so called query methods, - * methods to retrieve single entities or collections of them. + * Sample Addressbook class. * - * @author Thomas Darimont + * @author Mark Paluch */ -public interface SimpleUserRepository extends CrudRepository {} +@Data +@Table +public class Addressbook { + + @Id String id; + + Contact me; + List friends; +} diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/convert/Contact.java b/cassandra/example/src/main/java/example/springdata/cassandra/convert/Contact.java new file mode 100644 index 00000000..1a4ebf58 --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/convert/Contact.java @@ -0,0 +1,34 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.convert; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Sample Contact class. + * + * @author Mark Paluch + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Contact { + + String firstname; + String lastname; +} diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/convert/ConverterConfiguration.java b/cassandra/example/src/main/java/example/springdata/cassandra/convert/ConverterConfiguration.java new file mode 100644 index 00000000..b813798b --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/convert/ConverterConfiguration.java @@ -0,0 +1,116 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.convert; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.cassandra.config.SchemaAction; +import org.springframework.data.cassandra.config.java.AbstractCassandraConfiguration; +import org.springframework.data.cassandra.convert.CustomConversions; +import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; +import org.springframework.util.StringUtils; + +import com.datastax.driver.core.Row; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * {@link Configuration} class to register custom converters. + * + * @author Mark Paluch + */ +@Configuration +@EnableCassandraRepositories +class ConverterConfiguration extends AbstractCassandraConfiguration { + + @Override + public String getKeyspaceName() { + return "example"; + } + + @Override + public String[] getEntityBasePackages() { + return new String[] { Addressbook.class.getPackage().getName() }; + } + + @Override + public SchemaAction getSchemaAction() { + return SchemaAction.RECREATE; + } + + @Override + public CustomConversions customConversions() { + + List> converters = new ArrayList<>(); + converters.add(new PersonWriteConverter()); + converters.add(new PersonReadConverter()); + converters.add(new CustomAddressbookReadConverter()); + + return new CustomConversions(converters); + } + + /** + * Write a {@link Contact} into its {@link String} representation. + */ + static class PersonWriteConverter implements Converter { + + public String convert(Contact source) { + + try { + return new ObjectMapper().writeValueAsString(source); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } + + /** + * Read a {@link Contact} from its {@link String} representation. + */ + static class PersonReadConverter implements Converter { + + public Contact convert(String source) { + + if (StringUtils.hasText(source)) { + try { + return new ObjectMapper().readValue(source, Contact.class); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + return null; + } + } + + /** + * Perform custom mapping by reading a {@link Row} into a custom class. + */ + static class CustomAddressbookReadConverter implements Converter { + + public CustomAddressbook convert(Row source) { + + CustomAddressbook result = new CustomAddressbook(); + + result.setTheId(source.getString("id")); + result.setMyDetailsAsJson(source.getString("me")); + + return result; + } + } +} diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/convert/CustomAddressbook.java b/cassandra/example/src/main/java/example/springdata/cassandra/convert/CustomAddressbook.java new file mode 100644 index 00000000..3a0e15ea --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/convert/CustomAddressbook.java @@ -0,0 +1,28 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.convert; + +import lombok.Data; + +/** + * @author Mark Paluch + */ +@Data +public class CustomAddressbook { + + String theId; + String myDetailsAsJson; +} diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/convert/package-info.java b/cassandra/example/src/main/java/example/springdata/cassandra/convert/package-info.java new file mode 100644 index 00000000..414f660e --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/convert/package-info.java @@ -0,0 +1,4 @@ +/** + * Package showing conversion features. + */ +package example.springdata.cassandra.convert; diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/projection/Customer.java b/cassandra/example/src/main/java/example/springdata/cassandra/projection/Customer.java new file mode 100644 index 00000000..8255dfb3 --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/projection/Customer.java @@ -0,0 +1,32 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.projection; + +import org.springframework.data.annotation.Id; +import org.springframework.data.cassandra.mapping.Table; + +import lombok.Value; + +/** + * @author Mark Paluch + */ +@Value +@Table +class Customer { + + @Id String id; + String firstname, lastname; +} diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerProjection.java b/cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerProjection.java new file mode 100644 index 00000000..4a2f9d36 --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerProjection.java @@ -0,0 +1,26 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.projection; + +/** + * An example projection interface containing only the firstname. + * + * @author Mark Paluch + */ +interface CustomerProjection { + + String getFirstname(); +} diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerRepository.java b/cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerRepository.java new file mode 100644 index 00000000..fee61443 --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerRepository.java @@ -0,0 +1,72 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.projection; + +import java.util.Collection; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.repository.CrudRepository; + +/** + * Sample repository managing customers to show projecting functionality of Spring Data Cassandra. + * + * @author Mark Paluch + */ +interface CustomerRepository extends CrudRepository { + + /** + * Uses a projection interface to indicate the fields to be returned. As the projection doesn't use any dynamic + * fields, the query execution will be restricted to only the fields needed by the projection. + * + * @return + */ + Collection findAllProjectedBy(); + + /** + * When a projection is used that contains dynamic properties (i.e. SpEL expressions in an {@link Value} annotation), + * the normal target entity will be loaded but dynamically projected so that the target can be referred to in the + * expression. + * + * @return + */ + Collection findAllSummarizedBy(); + + /** + * Passes in the projection type dynamically. + * + * @param id + * @param projection + * @return + */ + Collection findById(String id, Class projection); + + /** + * Projection for a single entity. + * + * @param id + * @return + */ + CustomerProjection findProjectedById(String id); + + /** + * Dynamic projection for a single entity. + * + * @param id + * @param projection + * @return + */ + T findProjectedById(String id, Class projection); +} diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerSummary.java b/cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerSummary.java new file mode 100644 index 00000000..f0a5c816 --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/projection/CustomerSummary.java @@ -0,0 +1,29 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.projection; + +import org.springframework.beans.factory.annotation.Value; + +/** + * An example of using SpEL with projections. + * + * @author Mark Paluch + */ +interface CustomerSummary { + + @Value("#{target.firstname + ' ' + target.lastname}") + String getFullName(); +} diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/projection/ProjectionConfiguration.java b/cassandra/example/src/main/java/example/springdata/cassandra/projection/ProjectionConfiguration.java new file mode 100644 index 00000000..0758a458 --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/projection/ProjectionConfiguration.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.projection; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.cassandra.config.SchemaAction; +import org.springframework.data.cassandra.config.java.AbstractCassandraConfiguration; +import org.springframework.data.cassandra.core.CassandraTemplate; +import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; + +import com.datastax.driver.core.Session; + +import example.springdata.cassandra.basic.User; + +/** + * Basic {@link Configuration} to create the necessary schema for the {@link Customer} table. + * + * @author Mark Paluch + */ +@Configuration +@EnableAutoConfiguration +class ProjectionConfiguration { + + @Configuration + @EnableCassandraRepositories + static class CassandraConfig extends AbstractCassandraConfiguration { + + @Override + public String getKeyspaceName() { + return "example"; + } + + @Override + public String[] getEntityBasePackages() { + return new String[] { Customer.class.getPackage().getName() }; + } + + @Override + public SchemaAction getSchemaAction() { + return SchemaAction.RECREATE; + } + } +} diff --git a/cassandra/example/src/main/java/example/springdata/cassandra/projection/package-info.java b/cassandra/example/src/main/java/example/springdata/cassandra/projection/package-info.java new file mode 100644 index 00000000..e81c8c64 --- /dev/null +++ b/cassandra/example/src/main/java/example/springdata/cassandra/projection/package-info.java @@ -0,0 +1,4 @@ +/** + * Package showing projection features. + */ +package example.springdata.cassandra.projection; diff --git a/cassandra/example/src/test/java/example/springdata/cassandra/SimpleUserRepositoryTests.java b/cassandra/example/src/test/java/example/springdata/cassandra/SimpleUserRepositoryTests.java deleted file mode 100644 index 8fad8bac..00000000 --- a/cassandra/example/src/test/java/example/springdata/cassandra/SimpleUserRepositoryTests.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2013-2014 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.springdata.cassandra; - -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; - -import example.springdata.cassandra.SimpleConfiguration; -import example.springdata.cassandra.SimpleUserRepository; -import example.springdata.cassandra.User; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * Integration test showing the basic usage of {@link SimpleUserRepository}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - */ -@Ignore -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = SimpleConfiguration.class) -public class SimpleUserRepositoryTests { - - @Autowired SimpleUserRepository repository; - User user; - - @Before - public void setUp() { - - user = new User(); - user.setId(42L); - user.setUsername("foobar"); - user.setFirstname("firstname"); - user.setLastname("lastname"); - } - - @Test - public void findSavedUserById() { - - user = repository.save(user); - - assertThat(repository.findOne(user.getId()), is(user)); - } -} diff --git a/cassandra/example/src/test/java/example/springdata/cassandra/basic/BasicUserRepositoryTests.java b/cassandra/example/src/test/java/example/springdata/cassandra/basic/BasicUserRepositoryTests.java new file mode 100644 index 00000000..bcb98018 --- /dev/null +++ b/cassandra/example/src/test/java/example/springdata/cassandra/basic/BasicUserRepositoryTests.java @@ -0,0 +1,129 @@ +/* + * Copyright 2013-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.cassandra.basic; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.junit.Assume.*; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.util.Version; +import org.springframework.test.context.junit4.SpringRunner; + +import com.datastax.driver.core.Session; + +import example.springdata.cassandra.util.CassandraVersion; +import example.springdata.cassandra.util.RequiresCassandraKeyspace; + +/** + * Integration test showing the basic usage of {@link BasicUserRepository}. + * + * @author Oliver Gierke + * @author Thomas Darimont + * @author Christoph Strobl + * @author Mark Paluch + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BasicConfiguration.class) +public class BasicUserRepositoryTests { + + public final static Version CASSANDRA_3_4 = Version.parse("3.4"); + + @ClassRule public final static RequiresCassandraKeyspace CASSANDRA_KEYSPACE = RequiresCassandraKeyspace.onLocalhost(); + + @Autowired BasicUserRepository repository; + @Autowired Session session; + User user; + + @Before + public void setUp() { + + user = new User(); + user.setId(42L); + user.setUsername("foobar"); + user.setFirstname("firstname"); + user.setLastname("lastname"); + } + + /** + * Saving an object using the Cassandra Repository will create a persistent representation of the object in Cassandra. + */ + @Test + public void findSavedUserById() { + + user = repository.save(user); + + assertThat(repository.findOne(user.getId()), is(user)); + } + + /** + * Cassandra can be queries by using query methods annotated with {@link @Query}. + */ + @Test + public void findByAnnotatedQueryMethod() { + + repository.save(user); + + assertThat(repository.findUserByIdIn(1000), is(nullValue())); + assertThat(repository.findUserByIdIn(42), is(equalTo(user))); + } + + /** + * Spring Data Cassandra supports query derivation so annotating query methods with + * {@link org.springframework.data.cassandra.repository.Query} is optional. Querying columns other than the primary + * key requires a secondary index. + */ + @Test + public void findByDerivedQueryMethod() throws InterruptedException { + + session.execute("CREATE INDEX IF NOT EXISTS user_username ON users (uname);"); + /* + Cassandra secondary indexes are created in the background without the possibility to check + whether they are available or not. So we are forced to just wait. *sigh* + */ + Thread.sleep(1000); + + repository.save(user); + + assertThat(repository.findUserByUsername(user.getUsername()), is(user)); + } + + /** + * Spring Data Cassandra supports {@code LIKE} and {@code CONTAINS} query keywords to for SASI indexes. + */ + @Test + public void findByDerivedQueryMethodWithSASI() throws InterruptedException { + + assumeTrue(CassandraVersion.getReleaseVersion(session).isGreaterThanOrEqualTo(CASSANDRA_3_4)); + + session.execute("CREATE CUSTOM INDEX ON users (lname) USING 'org.apache.cassandra.index.sasi.SASIIndex';"); + /* + Cassandra secondary indexes are created in the background without the possibility to check + whether they are available or not. So we are forced to just wait. *sigh* + */ + Thread.sleep(1000); + + repository.save(user); + + assertThat(repository.findUsersByLastnameStartsWith("last"), hasItem(user)); + } +} diff --git a/cassandra/example/src/test/java/example/springdata/cassandra/basic/CassandraOperationsIntegrationTests.java b/cassandra/example/src/test/java/example/springdata/cassandra/basic/CassandraOperationsIntegrationTests.java new file mode 100644 index 00000000..ee007a7a --- /dev/null +++ b/cassandra/example/src/test/java/example/springdata/cassandra/basic/CassandraOperationsIntegrationTests.java @@ -0,0 +1,166 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.basic; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.cassandra.core.CassandraOperations; +import org.springframework.data.cassandra.core.CassandraTemplate; +import org.springframework.data.cassandra.core.WriteListener; +import org.springframework.test.context.junit4.SpringRunner; + +import com.datastax.driver.core.Row; +import com.datastax.driver.core.querybuilder.Insert; +import com.datastax.driver.core.querybuilder.QueryBuilder; + +import example.springdata.cassandra.util.RequiresCassandraKeyspace; + +/** + * Integration test showing the basic usage of {@link CassandraTemplate}. + * + * @author Mark Paluch + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BasicConfiguration.CassandraConfig.class) +public class CassandraOperationsIntegrationTests { + + @ClassRule public final static RequiresCassandraKeyspace CASSANDRA_KEYSPACE = RequiresCassandraKeyspace.onLocalhost(); + + @Autowired CassandraOperations template; + + @Before + public void setUp() throws Exception { + template.truncate("users"); + } + + /** + * Cassandra {@link com.datastax.driver.core.Statement}s can be used together with {@link CassandraTemplate} and the + * mapping layer. + */ + @Test + public void insertAndSelect() { + + Insert insert = QueryBuilder.insertInto("users").value("user_id", 42L) // + .value("uname", "heisenberg") // + .value("fname", "Walter") // + .value("lname", "White") // + .ifNotExists(); // + + template.execute(insert); + + User user = template.selectOneById(User.class, 42L); + assertThat(user.getUsername(), is(equalTo("heisenberg"))); + + List users = template.select(QueryBuilder.select().from("users"), User.class); + assertThat(users, hasSize(1)); + assertThat(users.get(0), is(equalTo(user))); + } + + /** + * Objects can be inserted and updated using {@link CassandraTemplate}. What you {@code update} is what you + * {@code select}. + */ + @Test + public void insertAndUpdate() { + + User user = new User(); + user.setId(42L); + user.setUsername("heisenberg"); + user.setFirstname("Walter"); + user.setLastname("White"); + + template.insert(user); + + user.setFirstname(null); + template.update(user); + + User loaded = template.selectOneById(User.class, 42L); + assertThat(loaded.getUsername(), is(equalTo("heisenberg"))); + assertThat(loaded.getFirstname(), is(nullValue())); + } + + /** + * Asynchronous query execution using callbacks. + */ + @Test + public void insertAsynchronously() throws InterruptedException { + + User user = new User(); + user.setId(42L); + user.setUsername("heisenberg"); + user.setFirstname("Walter"); + user.setLastname("White"); + + final CountDownLatch countDownLatch = new CountDownLatch(1); + + template.insertAsynchronously(user, new WriteListener() { + + @Override + public void onWriteComplete(Collection entities) { + countDownLatch.countDown(); + } + + @Override + public void onException(Exception x) {} + }); + + countDownLatch.await(5, TimeUnit.SECONDS); + + User loaded = template.selectOneById(User.class, user.getId()); + assertThat(loaded, is(equalTo(user))); + } + + /** + * {@link CassandraTemplate} allows selection of projections on template-level. All basic data types including + * {@link Row} can be selected. + */ + @Test + @SuppressWarnings("unchecked") + public void selectProjections() { + + User user = new User(); + user.setId(42L); + user.setUsername("heisenberg"); + user.setFirstname("Walter"); + user.setLastname("White"); + + template.insert(user); + + Long id = template.selectOne(QueryBuilder.select("user_id").from("users"), Long.class); + assertThat(id, is(user.getId())); + + Row row = template.selectOne(QueryBuilder.select("user_id").from("users"), Row.class); + assertThat(row.getLong(0), is(user.getId())); + + Map map = template.selectOne(QueryBuilder.select().from("users"), Map.class); + assertThat(map, hasEntry("user_id", user.getId())); + assertThat(map, hasEntry("fname", "Walter")); + } +} diff --git a/cassandra/example/src/test/java/example/springdata/cassandra/convert/ConversionIntegrationTests.java b/cassandra/example/src/test/java/example/springdata/cassandra/convert/ConversionIntegrationTests.java new file mode 100644 index 00000000..7ea4777d --- /dev/null +++ b/cassandra/example/src/test/java/example/springdata/cassandra/convert/ConversionIntegrationTests.java @@ -0,0 +1,119 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.convert; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.Arrays; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.cassandra.core.CassandraOperations; +import org.springframework.test.context.junit4.SpringRunner; + +import com.datastax.driver.core.Row; +import com.datastax.driver.core.querybuilder.QueryBuilder; + +import example.springdata.cassandra.util.RequiresCassandraKeyspace; + +/** + * @author Mark Paluch + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = ConverterConfiguration.class) +public class ConversionIntegrationTests { + + @ClassRule public final static RequiresCassandraKeyspace CASSANDRA_KEYSPACE = RequiresCassandraKeyspace.onLocalhost(); + + @Autowired CassandraOperations operations; + + @Before + public void setUp() throws Exception { + operations.truncate("addressbook"); + } + + /** + * Creates and stores a new {@link Addressbook} inside of Cassandra. {@link Contact} classes are converted using the + * custom {@link example.springdata.cassandra.convert.ConverterConfiguration.PersonWriteConverter}. + */ + @Test + public void shouldCreateAddressbook() { + + Addressbook addressbook = new Addressbook(); + addressbook.setId("private"); + + addressbook.setMe(new Contact("Walter", "White")); + addressbook.setFriends(Arrays.asList(new Contact("Jesse", "Pinkman"), new Contact("Saul", "Goodman"))); + + operations.insert(addressbook); + + Row row = operations.selectOne(QueryBuilder.select().from("addressbook"), Row.class); + + assertThat(row, is(notNullValue())); + + assertThat(row.getString("id"), is(equalTo("private"))); + assertThat(row.getString("me"), containsString("\"firstname\":\"Walter\"")); + assertThat(row.getList("friends", String.class), hasSize(2)); + } + + /** + * Creates and loads a new {@link Addressbook} inside of Cassandra. {@link Contact} classes are converted using the + * custom {@link example.springdata.cassandra.convert.ConverterConfiguration.PersonReadConverter}. + */ + @Test + public void shouldReadAddressbook() { + + Addressbook addressbook = new Addressbook(); + addressbook.setId("private"); + + addressbook.setMe(new Contact("Walter", "White")); + addressbook.setFriends(Arrays.asList(new Contact("Jesse", "Pinkman"), new Contact("Saul", "Goodman"))); + + operations.insert(addressbook); + + Addressbook loaded = operations.selectOne(QueryBuilder.select().from("addressbook"), Addressbook.class); + + assertThat(loaded.getMe(), is(equalTo(addressbook.getMe()))); + assertThat(loaded.getFriends(), is(equalTo(addressbook.getFriends()))); + } + + /** + * Creates and stores a new {@link Addressbook} inside of Cassandra. The {@link Addressbook} is read back to a + * {@link CustomAddressbook} class using the + * {@link example.springdata.cassandra.convert.ConverterConfiguration.CustomAddressbookReadConverter}. + */ + @Test + public void shouldReadCustomAddressbook() { + + Addressbook addressbook = new Addressbook(); + addressbook.setId("private"); + + addressbook.setMe(new Contact("Walter", "White")); + + operations.insert(addressbook); + + CustomAddressbook loaded = operations.selectOne(QueryBuilder.select().from("addressbook"), CustomAddressbook.class); + + assertThat(loaded.getTheId(), is(equalTo(addressbook.getId()))); + assertThat(loaded.getMyDetailsAsJson(), containsString("\"firstname\":\"Walter\"")); + } +} diff --git a/cassandra/example/src/test/java/example/springdata/cassandra/projection/CustomerRepositoryIntegrationTest.java b/cassandra/example/src/test/java/example/springdata/cassandra/projection/CustomerRepositoryIntegrationTest.java new file mode 100644 index 00000000..cf197fc9 --- /dev/null +++ b/cassandra/example/src/test/java/example/springdata/cassandra/projection/CustomerRepositoryIntegrationTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.projection; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.Collection; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.projection.TargetAware; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import example.springdata.cassandra.util.RequiresCassandraKeyspace; + +/** + * Integration tests for {@link CustomerRepository} to show projection capabilities. + * + * @author Mark Paluch + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = ProjectionConfiguration.class) +public class CustomerRepositoryIntegrationTest { + + @ClassRule public final static RequiresCassandraKeyspace CASSANDRA_KEYSPACE = RequiresCassandraKeyspace.onLocalhost(); + + @Autowired CustomerRepository customers; + + Customer dave, carter; + + @Before + public void setUp() { + + customers.deleteAll(); + + this.dave = customers.save(new Customer("d", "Dave", "Matthews")); + this.carter = customers.save(new Customer("c", "Carter", "Beauford")); + } + + @Test + public void projectsEntityIntoInterface() { + + Collection result = customers.findAllProjectedBy(); + + assertThat(result, hasSize(2)); + assertThat(result.iterator().next().getFirstname(), is("Carter")); + } + + @Test + public void projectsDynamically() { + + Collection result = customers.findById("d", CustomerProjection.class); + + assertThat(result, hasSize(1)); + assertThat(result.iterator().next().getFirstname(), is("Dave")); + } + + @Test + public void projectsIndividualDynamically() { + + CustomerSummary result = customers.findProjectedById(dave.getId(), CustomerSummary.class); + + assertThat(result, is(notNullValue())); + assertThat(result.getFullName(), is("Dave Matthews")); + + // Proxy backed by original instance as the projection uses dynamic elements + assertThat(((TargetAware) result).getTarget(), is(instanceOf(Customer.class))); + } + + @Test + public void projectIndividualInstance() { + + CustomerProjection result = customers.findProjectedById(dave.getId()); + + assertThat(result, is(notNullValue())); + assertThat(result.getFirstname(), is("Dave")); + assertThat(((TargetAware) result).getTarget(), is(instanceOf(Customer.class))); + } + +} diff --git a/cassandra/java8/README.md b/cassandra/java8/README.md new file mode 100644 index 00000000..26043af9 --- /dev/null +++ b/cassandra/java8/README.md @@ -0,0 +1,36 @@ +# Spring Data Cassandra - Java 8 examples + +This project contains samples of Java 8 specific features of Spring Data (Cassandra). + +## Support for JDK 8's `Stream` for repository methods + +Repository methods can use a Java 8 `Stream` as a return type which will cause the reading of the results and the to-object-conversion of rows to happen while iterating over the stream. + +```java +public interface PersonRepository extends CrudRepository { + + @Override + List findAll(); + + //Custom Query method returning a Java 8 Stream + @Query("SELECT * FROM person") + Stream findAllByCustomQueryWithStream(); +} +``` + +The test cases in `PersonRepositoryIntegrationTest` oppose a plain `List` based query method with one that uses a `Stream` and shows how the former pulls all data into memory first and the iteration is done over the pre-populated list. The execution of the `Stream`-based method in contrast shows that the individual elements are read and converted while iterating the stream. + +## Preparation + +### Install Cassandra +Before we can start we have to install Cassandra, e.g. via brew on Max OS. + +More details can be found here: https://wiki.apache.org/cassandra/GettingStarted + +### Start Cassandra +``` +/usr/local/bin/cassandra -f +``` + +That should be enough to get you started. +Now you can simply type ```mvn clean install``` to run the example. diff --git a/cassandra/java8/pom.xml b/cassandra/java8/pom.xml new file mode 100644 index 00000000..6bbc36bf --- /dev/null +++ b/cassandra/java8/pom.xml @@ -0,0 +1,26 @@ + + 4.0.0 + + + org.springframework.data.examples + spring-data-cassandra-examples + 1.0.0.BUILD-SNAPSHOT + ../pom.xml + + + spring-data-cassandra-java8 + Spring Data Cassandra - Java 8 specific features + + + + + ${project.groupId} + spring-data-cassandra-example-utils + ${project.version} + test + + + + + diff --git a/cassandra/java8/src/main/java/example/springdata/cassandra/java8/CassandraConfiguration.java b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/CassandraConfiguration.java new file mode 100644 index 00000000..c008c8a8 --- /dev/null +++ b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/CassandraConfiguration.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.java8; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.cassandra.config.SchemaAction; +import org.springframework.data.cassandra.config.java.AbstractCassandraConfiguration; +import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; + +/** + * @author Mark Paluch + */ +@Configuration +@EnableAutoConfiguration +class CassandraConfiguration { + + @Configuration + @EnableCassandraRepositories + static class CassandraConfig extends AbstractCassandraConfiguration { + + @Override + public String getKeyspaceName() { + return "example"; + } + + @Override + public SchemaAction getSchemaAction() { + return SchemaAction.RECREATE; + } + } +} diff --git a/cassandra/java8/src/main/java/example/springdata/cassandra/java8/Order.java b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/Order.java new file mode 100644 index 00000000..eda71bd9 --- /dev/null +++ b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/Order.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.java8; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.ZoneId; + +import org.springframework.data.annotation.Id; +import org.springframework.data.cassandra.mapping.Table; + +/** + * @author Mark Paluch + */ +@Table("pizza_orders") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Order { + + @Id String id; + LocalDate orderDate; + ZoneId zoneId; +} diff --git a/cassandra/java8/src/main/java/example/springdata/cassandra/java8/OrderRepository.java b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/OrderRepository.java new file mode 100644 index 00000000..365db253 --- /dev/null +++ b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/OrderRepository.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.java8; + +import java.time.LocalDate; +import java.time.ZoneId; + +import org.springframework.data.cassandra.mapping.CassandraType; +import org.springframework.data.cassandra.repository.Query; +import org.springframework.data.repository.Repository; + +/** + * Repository to manage {@link Order} instances. + * + * @author Mark Paluch + */ +public interface OrderRepository extends Repository { + + /** + * Method parameters are converted according the registered Converters into Cassandra types. + */ + @Query("SELECT * from pizza_orders WHERE orderdate = ?0 and zoneid = ?1 ALLOW FILTERING") + Order findOrderByOrderDateAndZoneId(LocalDate orderDate, ZoneId zoneId); + + /** + * Parameter conversion can be overridden by using the {@link CassandraType} annotation. + */ + @Query("SELECT * from pizza_orders WHERE orderdate = ?0 and zoneid = ?1 ALLOW FILTERING") + Order findOrderByDate(com.datastax.driver.core.LocalDate orderDate, String zoneId); + + void deleteAll(); + + Order save(Order order); +} diff --git a/cassandra/java8/src/main/java/example/springdata/cassandra/java8/Person.java b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/Person.java new file mode 100644 index 00000000..a313f24d --- /dev/null +++ b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/Person.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.java8; + +import org.springframework.data.annotation.Id; +import org.springframework.data.cassandra.mapping.Table; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author Mark Paluch + */ +@Table +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Person { + + @Id String id; + String firstname, lastname; +} diff --git a/cassandra/java8/src/main/java/example/springdata/cassandra/java8/PersonRepository.java b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/PersonRepository.java new file mode 100644 index 00000000..1bf32c95 --- /dev/null +++ b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/PersonRepository.java @@ -0,0 +1,65 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.java8; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.springframework.data.cassandra.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.Repository; + +/** + * Repository to manage {@link Person} instances. + * + * @author Mark Paluch + */ +public interface PersonRepository extends Repository { + + /** + * Special customization of {@link CrudRepository#findOne(java.io.Serializable)} to return a JDK 8 {@link Optional}. + * + * @param id + * @return + */ + Optional findOne(String id); + + @Query("select * from person") + Stream streamAllPeople(); + + /** + * Sample method to derive a query from using JDK 8's {@link Optional} as return type. + * + * @param id + * @return + */ + @Query("select * from person where id = ?0") + Optional findById(String id); + + /** + * Sample default method to show JDK 8 feature support. + * + * @param person + * @return + */ + default Optional findByPerson(Person person) { + return findById(person == null ? null : person.id); + } + + void deleteAll(); + + Person save(Person person); +} diff --git a/cassandra/java8/src/main/java/example/springdata/cassandra/java8/package-info.java b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/package-info.java new file mode 100644 index 00000000..476e1287 --- /dev/null +++ b/cassandra/java8/src/main/java/example/springdata/cassandra/java8/package-info.java @@ -0,0 +1,4 @@ +/** + * Package showing Java 8 features. + */ +package example.springdata.cassandra.java8; diff --git a/cassandra/java8/src/test/java/example/springdata/cassandra/java8/Java8IntegrationTests.java b/cassandra/java8/src/test/java/example/springdata/cassandra/java8/Java8IntegrationTests.java new file mode 100644 index 00000000..9d4d693e --- /dev/null +++ b/cassandra/java8/src/test/java/example/springdata/cassandra/java8/Java8IntegrationTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.java8; + +import example.springdata.cassandra.util.RequiresCassandraKeyspace; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.util.Version; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration test to show the usage of Java 8 features with Spring Data Cassandra. + * + * @author Mark Paluch + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = CassandraConfiguration.class) +public class Java8IntegrationTests { + + @ClassRule + public final static RequiresCassandraKeyspace CASSANDRA_KEYSPACE = RequiresCassandraKeyspace.onLocalhost().atLeast(Version.parse("3.0")); + + @Autowired PersonRepository repository; + + @Before + public void setUp() throws Exception { + repository.deleteAll(); + } + + @Test + public void providesFindOneWithOptional() { + + Person homer = repository.save(new Person("1", "Homer", "Simpson")); + + assertThat(repository.findOne(homer.id).isPresent(), is(true)); + assertThat(repository.findOne(homer.id + 1), is(Optional. empty())); + } + + @Test + public void invokesDefaultMethod() { + + Person homer = repository.save(new Person("1", "Homer", "Simpson")); + Optional result = repository.findByPerson(homer); + + assertThat(result.isPresent(), is(true)); + assertThat(result.get(), is(homer)); + } + + /** + * Streaming data from the store by using a repository method that returns a {@link Stream}. Note, that since the + * resulting {@link Stream} contains state it needs to be closed explicitly after use! + */ + @Test + public void useJava8StreamsWithCustomQuery() { + + Person homer = repository.save(new Person("1", "Homer", "Simpson")); + Person bart = repository.save(new Person("2", "Bart", "Simpson")); + + try (Stream stream = repository.streamAllPeople()) { + assertThat(stream.collect(Collectors.toList()), hasItems(homer, bart)); + } + } +} diff --git a/cassandra/java8/src/test/java/example/springdata/cassandra/java8/Jsr310IntegrationTests.java b/cassandra/java8/src/test/java/example/springdata/cassandra/java8/Jsr310IntegrationTests.java new file mode 100644 index 00000000..76221d08 --- /dev/null +++ b/cassandra/java8/src/test/java/example/springdata/cassandra/java8/Jsr310IntegrationTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.java8; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.time.LocalDate; +import java.time.ZoneId; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.util.Version; +import org.springframework.test.context.junit4.SpringRunner; + +import example.springdata.cassandra.util.RequiresCassandraKeyspace; + +/** + * Integration test to show the usage of JSR-310 date/time types with Spring Data Cassandra. + * + * @author Mark Paluch + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = CassandraConfiguration.class) +public class Jsr310IntegrationTests { + + @ClassRule public final static RequiresCassandraKeyspace CASSANDRA_KEYSPACE = RequiresCassandraKeyspace.onLocalhost() + .atLeast(Version.parse("3.0")); + + @Autowired OrderRepository repository; + + @Before + public void setUp() throws Exception { + repository.deleteAll(); + } + + @Test + public void findOneByJsr310Types() { + + Order order = new Order("42", LocalDate.now(), ZoneId.systemDefault()); + + repository.save(order); + + assertThat(repository.findOrderByOrderDateAndZoneId(order.getOrderDate(), order.getZoneId()), is(equalTo(order))); + } + + @Test + public void findOneByConvertedTypes() { + + Order order = new Order("42", LocalDate.of(2010, 1, 2), ZoneId.systemDefault()); + + repository.save(order); + + com.datastax.driver.core.LocalDate date = com.datastax.driver.core.LocalDate.fromYearMonthDay(2010, 1, 2); + String zoneId = order.getZoneId().getId(); + + assertThat(repository.findOrderByDate(date, zoneId), is(equalTo(order))); + } +} diff --git a/cassandra/pom.xml b/cassandra/pom.xml index a861abc0..f30cff29 100644 --- a/cassandra/pom.xml +++ b/cassandra/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-data-cassandra-examples @@ -17,7 +17,9 @@ 2014 + util example + java8 @@ -27,9 +29,14 @@ spring-boot-starter-aop - - org.springframework.data - spring-data-cassandra + + org.springframework.boot + spring-boot-starter-data-cassandra + + + + com.datastax.cassandra + cassandra-driver-core diff --git a/cassandra/util/pom.xml b/cassandra/util/pom.xml new file mode 100644 index 00000000..38bd8544 --- /dev/null +++ b/cassandra/util/pom.xml @@ -0,0 +1,22 @@ + + 4.0.0 + + + org.springframework.data.examples + spring-data-cassandra-examples + 1.0.0.BUILD-SNAPSHOT + ../pom.xml + + + spring-data-cassandra-example-utils + Spring Data Cassandra - Example Utilities + + + + junit + junit + + + + diff --git a/cassandra/util/src/main/java/example/springdata/cassandra/util/CassandraVersion.java b/cassandra/util/src/main/java/example/springdata/cassandra/util/CassandraVersion.java new file mode 100644 index 00000000..c39503cd --- /dev/null +++ b/cassandra/util/src/main/java/example/springdata/cassandra/util/CassandraVersion.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.util; + +import org.springframework.data.util.Version; +import org.springframework.util.Assert; + +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; + +import lombok.experimental.UtilityClass; + +/** + * Utility to retrieve the Cassandra release version. + * + * @author Mark Paluch + */ +@UtilityClass +public class CassandraVersion { + + /** + * Retrieve the Cassandra release version. + * + * @param session must not be {@literal null}. + * @return the release {@link Version}. + */ + public static Version getReleaseVersion(Session session) { + + Assert.notNull(session, "Session must not be null"); + + ResultSet resultSet = session.execute("SELECT release_version FROM system.local;"); + Row row = resultSet.one(); + + return Version.parse(row.getString(0)); + } +} diff --git a/cassandra/util/src/main/java/example/springdata/cassandra/util/RequiresCassandraKeyspace.java b/cassandra/util/src/main/java/example/springdata/cassandra/util/RequiresCassandraKeyspace.java new file mode 100644 index 00000000..91046b17 --- /dev/null +++ b/cassandra/util/src/main/java/example/springdata/cassandra/util/RequiresCassandraKeyspace.java @@ -0,0 +1,124 @@ +/* + * Copyright 2016 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 example.springdata.cassandra.util; + +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.concurrent.TimeUnit; + +import org.junit.AssumptionViolatedException; +import org.junit.rules.ExternalResource; +import org.springframework.data.util.Version; +import org.springframework.util.Assert; + +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.NettyOptions; +import com.datastax.driver.core.Session; + +import io.netty.channel.EventLoopGroup; + +/** + * Implementation of an {@link ExternalResource} to verify Apache Cassandra is running and listening on the given + * contact point. This rule also creates a keyspace if it not exists. + * + * @author Mark Paluch + */ +public class RequiresCassandraKeyspace extends ExternalResource { + + private final int timeout = 30; + private final String host; + private final int port; + private final String keyspaceName; + private Version requiresVersion; + + private RequiresCassandraKeyspace(String host, int port, String keyspaceName) { + + this.host = host; + this.port = port; + this.keyspaceName = keyspaceName; + } + + /** + * Require a running Cassandra instance on {@code localhost:9042}. + * + * @return the {@link RequiresCassandraKeyspace} rule + */ + public static RequiresCassandraKeyspace onLocalhost() { + return new RequiresCassandraKeyspace("localhost", 9042, "example"); + } + + /** + * Setup a version requirement. + * + * @param version must not be {@literal null}. + * @return the {@link RequiresCassandraKeyspace} rule + */ + public RequiresCassandraKeyspace atLeast(Version version) { + + Assert.notNull(version, "Required version must not be null!"); + + this.requiresVersion = version; + return this; + } + + /* + * (non-Javadoc) + * @see org.junit.rules.ExternalResource#before() + */ + @Override + protected void before() throws Throwable { + + try (Socket socket = new Socket()) { + socket.setTcpNoDelay(true); + socket.setSoLinger(true, 0); + socket.connect(new InetSocketAddress(host, port), timeout); + + } catch (Exception e) { + throw new AssumptionViolatedException(String.format("Seems as Cassandra is not running at %s:%s.", host, port), + e); + } + + Cluster cluster = Cluster.builder().addContactPoint(host).withPort(port).withNettyOptions(new NettyOptions() { + @Override + public void onClusterClose(EventLoopGroup eventLoopGroup) { + eventLoopGroup.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS).syncUninterruptibly(); + } + }).build(); + + Session session = cluster.newSession(); + + try { + + if (requiresVersion != null) { + + Version cassandraReleaseVersion = CassandraVersion.getReleaseVersion(session); + + if (cassandraReleaseVersion.isLessThan(requiresVersion)) { + throw new AssumptionViolatedException( + String.format("Cassandra at %s:%s runs in Version %s but we require at least %s", host, port, + cassandraReleaseVersion, requiresVersion)); + } + } + + session.execute(String.format("CREATE KEYSPACE IF NOT EXISTS %s \n" + + "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };", keyspaceName)); + } finally { + session.close(); + cluster.close(); + } + } + +} diff --git a/pom.xml b/pom.xml index b8281e98..0b03dba3 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,7 @@ Ingalls-M1 0.21.0.RELEASE 2.9.0 + 3.0.3