#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.
This commit is contained in:
Mark Paluch
2016-07-25 16:40:41 +02:00
parent d2e5c4b28f
commit 95978416d1
38 changed files with 1708 additions and 112 deletions

View File

@@ -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;
}
}
}

View File

@@ -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<User, Long> {
/**
* 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<User> findUsersByLastnameStartsWith(String lastnamePrefix);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<User, Long> {}
@Data
@Table
public class Addressbook {
@Id String id;
Contact me;
List<Contact> friends;
}

View File

@@ -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;
}

View File

@@ -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<Converter<?, ?>> 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<Contact, String> {
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<String, Contact> {
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<Row, CustomAddressbook> {
public CustomAddressbook convert(Row source) {
CustomAddressbook result = new CustomAddressbook();
result.setTheId(source.getString("id"));
result.setMyDetailsAsJson(source.getString("me"));
return result;
}
}
}

View File

@@ -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;
}

View File

@@ -0,0 +1,4 @@
/**
* Package showing conversion features.
*/
package example.springdata.cassandra.convert;

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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<Customer, String> {
/**
* 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<CustomerProjection> 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<CustomerSummary> findAllSummarizedBy();
/**
* Passes in the projection type dynamically.
*
* @param id
* @param projection
* @return
*/
<T> Collection<T> findById(String id, Class<T> 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> T findProjectedById(String id, Class<T> projection);
}

View File

@@ -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();
}

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,4 @@
/**
* Package showing projection features.
*/
package example.springdata.cassandra.projection;

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View File

@@ -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<User> 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<User>() {
@Override
public void onWriteComplete(Collection<User> 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<String, Object> map = template.selectOne(QueryBuilder.select().from("users"), Map.class);
assertThat(map, hasEntry("user_id", user.getId()));
assertThat(map, hasEntry("fname", "Walter"));
}
}

View File

@@ -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\""));
}
}

View File

@@ -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<CustomerProjection> result = customers.findAllProjectedBy();
assertThat(result, hasSize(2));
assertThat(result.iterator().next().getFirstname(), is("Carter"));
}
@Test
public void projectsDynamically() {
Collection<CustomerProjection> 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)));
}
}