Merge branch 'DATACASS-32' of https://github.com/shvid/spring-data-cassandra into DATACASS-32

This commit is contained in:
Matthew Adams
2013-11-14 22:57:52 -06:00
17 changed files with 383 additions and 114 deletions

View File

@@ -26,6 +26,7 @@ import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.core.Keyspace;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
@@ -116,7 +117,7 @@ public abstract class AbstractCassandraConfiguration {
* @throws Exception
*/
@Bean
public CassandraTemplate cassandraTemplate() throws Exception {
public CassandraOperations cassandraTemplate() throws Exception {
return new CassandraTemplate(keyspace());
}

View File

@@ -24,9 +24,9 @@ import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
/**
* Main Inteface that should be used for Cassandra interactions
*
* @author Alex Shvid
*/
/**
* @author David Webb
*
*/

View File

@@ -208,6 +208,7 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> void deleteAsychronously(List<T> entities, String tableName) {
insertAsynchronously(entities, tableName, new HashMap<String, Object>());
}
/* (non-Javadoc)
@@ -227,8 +228,7 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> void deleteAsychronously(List<T> entities, String tableName, QueryOptions options) {
// TODO Auto-generated method stub
deleteAsychronously(entities, tableName, options.toMap());
}
/* (non-Javadoc)
@@ -571,8 +571,9 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> List<T> update(List<T> entities) {
// TODO Auto-generated method stub
return null;
String tableName = getTableName(entities.get(0).getClass());
Assert.notNull(tableName);
return update(entities, tableName);
}
/* (non-Javadoc)
@@ -580,8 +581,7 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> List<T> update(List<T> entities, String tableName) {
// TODO Auto-generated method stub
return null;
return update(entities, tableName, new HashMap<String, Object>());
}
/* (non-Javadoc)
@@ -589,8 +589,11 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> List<T> update(List<T> entities, String tableName, Map<String, Object> optionsByName) {
// TODO Auto-generated method stub
return null;
Assert.notNull(entities);
Assert.notEmpty(entities);
Assert.notNull(tableName);
Assert.notNull(optionsByName);
return doBatchUpdate(tableName, entities, optionsByName, false);
}
/* (non-Javadoc)
@@ -598,8 +601,7 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> List<T> update(List<T> entities, String tableName, QueryOptions options) {
// TODO Auto-generated method stub
return null;
return update(entities, tableName, options.toMap());
}
/* (non-Javadoc)
@@ -607,8 +609,9 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> T update(T entity) {
// TODO Auto-generated method stub
return null;
String tableName = getTableName(entity.getClass());
Assert.notNull(tableName);
return update(entity, tableName);
}
/* (non-Javadoc)
@@ -616,8 +619,7 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> T update(T entity, String tableName) {
// TODO Auto-generated method stub
return null;
return update(entity, tableName, new HashMap<String, Object>());
}
/* (non-Javadoc)
@@ -625,8 +627,10 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> T update(T entity, String tableName, Map<String, Object> optionsByName) {
// TODO Auto-generated method stub
return null;
Assert.notNull(entity);
Assert.notNull(tableName);
Assert.notNull(optionsByName);
return doUpdate(tableName, entity, optionsByName, false);
}
/* (non-Javadoc)
@@ -634,8 +638,7 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> T update(T entity, String tableName, QueryOptions options) {
// TODO Auto-generated method stub
return null;
return update(entity, tableName, options.toMap());
}
/* (non-Javadoc)
@@ -643,8 +646,9 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> List<T> updateAsynchronously(List<T> entities) {
// TODO Auto-generated method stub
return null;
String tableName = getTableName(entities.get(0).getClass());
Assert.notNull(tableName);
return updateAsynchronously(entities, tableName);
}
/* (non-Javadoc)
@@ -652,8 +656,7 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> List<T> updateAsynchronously(List<T> entities, String tableName) {
// TODO Auto-generated method stub
return null;
return updateAsynchronously(entities, tableName, new HashMap<String, Object>());
}
/* (non-Javadoc)
@@ -661,8 +664,11 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> List<T> updateAsynchronously(List<T> entities, String tableName, Map<String, Object> optionsByName) {
// TODO Auto-generated method stub
return null;
Assert.notNull(entities);
Assert.notEmpty(entities);
Assert.notNull(tableName);
Assert.notNull(optionsByName);
return doBatchUpdate(tableName, entities, optionsByName, true);
}
/* (non-Javadoc)
@@ -670,8 +676,7 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> List<T> updateAsynchronously(List<T> entities, String tableName, QueryOptions options) {
// TODO Auto-generated method stub
return null;
return updateAsynchronously(entities, tableName, options.toMap());
}
/* (non-Javadoc)
@@ -679,8 +684,9 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> T updateAsynchronously(T entity) {
// TODO Auto-generated method stub
return null;
String tableName = getTableName(entity.getClass());
Assert.notNull(tableName);
return updateAsynchronously(entity, tableName);
}
/* (non-Javadoc)
@@ -688,8 +694,7 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> T updateAsynchronously(T entity, String tableName) {
// TODO Auto-generated method stub
return null;
return updateAsynchronously(entity, tableName, new HashMap<String, Object>());
}
/* (non-Javadoc)
@@ -697,8 +702,10 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> T updateAsynchronously(T entity, String tableName, Map<String, Object> optionsByName) {
// TODO Auto-generated method stub
return null;
Assert.notNull(entity);
Assert.notNull(tableName);
Assert.notNull(optionsByName);
return doUpdate(tableName, entity, optionsByName, true);
}
/* (non-Javadoc)
@@ -706,8 +713,7 @@ public class CassandraTemplate implements CassandraOperations {
*/
@Override
public <T> T updateAsynchronously(T entity, String tableName, QueryOptions options) {
// TODO Auto-generated method stub
return null;
return updateAsynchronously(entity, tableName, options.toMap());
}
/**
@@ -773,7 +779,10 @@ public class CassandraTemplate implements CassandraOperations {
* Insert a row into a Cassandra CQL Table
*
* @param tableName
* @param entity
* @param entities
* @param optionsByName
* @param insertAsychronously
* @return
*/
protected <T> List<T> doBatchInsert(final String tableName, final List<T> entities,
Map<String, Object> optionsByName, final boolean insertAsychronously) {
@@ -811,6 +820,51 @@ public class CassandraTemplate implements CassandraOperations {
}
}
/**
* Update a Batch of rows in a Cassandra CQL Table
*
* @param tableName
* @param entities
* @param optionsByName
* @param updateAsychronously
* @return
*/
protected <T> List<T> doBatchUpdate(final String tableName, final List<T> entities,
Map<String, Object> optionsByName, final boolean updateAsychronously) {
Assert.notEmpty(entities);
CassandraPersistentEntity<?> CPEntity = getEntity(entities.get(0));
Assert.notNull(CPEntity);
try {
final Batch b = CqlUtils.toUpdateBatchQuery(keyspace.getKeyspace(), tableName, entities, CPEntity, optionsByName);
log.info(b.toString());
return execute(new SessionCallback<List<T>>() {
@Override
public List<T> doInSession(Session s) throws DataAccessException {
if (updateAsychronously) {
s.executeAsync(b);
} else {
s.execute(b);
}
return entities;
}
});
} catch (EntityWriterException e) {
throw exceptionTranslator.translateExceptionIfPossible(new RuntimeException(
"Failed to translate Object to Query", e));
}
}
/**
* Perform the removal of a Row.
*
@@ -892,6 +946,50 @@ public class CassandraTemplate implements CassandraOperations {
}
/**
* Update a row into a Cassandra CQL Table
*
* @param tableName
* @param entity
* @param optionsByName
* @param updateAsychronously
* @return
*/
protected <T> T doUpdate(final String tableName, final T entity, final Map<String, Object> optionsByName,
final boolean updateAsychronously) {
CassandraPersistentEntity<?> CPEntity = getEntity(entity);
Assert.notNull(CPEntity);
try {
final Query q = CqlUtils.toUpdateQuery(keyspace.getKeyspace(), tableName, entity, CPEntity, optionsByName);
log.info(q.toString());
return execute(new SessionCallback<T>() {
@Override
public T doInSession(Session s) throws DataAccessException {
if (updateAsychronously) {
s.executeAsync(q);
} else {
s.execute(q);
}
return entity;
}
});
} catch (EntityWriterException e) {
throw exceptionTranslator.translateExceptionIfPossible(new RuntimeException(
"Failed to translate Object to Query", e));
}
}
/**
* Verify the object is not an iterable type
*

View File

@@ -27,6 +27,7 @@ import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Delete.Where;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Update;
/**
*
@@ -263,6 +264,112 @@ public abstract class CqlUtils {
}
/**
* Generates a Query Object for an Update
*
* @param keyspaceName
* @param tableName
* @param entity
* @param objectToSave
* @param optionsByName
* @param mappingContext
* @param beanClassLoader
*
* @return The Query object to run with session.execute();
* @throws EntityWriterException
*/
public static Query toUpdateQuery(String keyspaceName, String tableName, final Object objectToSave,
CassandraPersistentEntity<?> entity, Map<String, Object> optionsByName) throws EntityWriterException {
final Update q = QueryBuilder.update(keyspaceName, tableName);
final Exception innerException = new Exception();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
/*
* See if the object has a value for that column, and if so, add it to the Query
*/
try {
Object o = prop.getGetter().invoke(objectToSave, new Object[0]);
log.info("Getter Invoke [" + prop.getColumnName() + " => " + o);
if (o != null) {
if (prop.isIdProperty()) {
q.where(QueryBuilder.eq(prop.getColumnName(), o));
} else {
q.with(QueryBuilder.set(prop.getColumnName(), o));
}
}
} catch (IllegalAccessException e) {
innerException.initCause(e);
} catch (IllegalArgumentException e) {
innerException.initCause(e);
} catch (InvocationTargetException e) {
innerException.initCause(e);
}
}
});
if (innerException.getCause() != null) {
throw new EntityWriterException("Failed to convert Persistent Entity to CQL/Query", innerException.getCause());
}
/*
* Add Query Options
*/
addQueryOptions(q, optionsByName);
/*
* Add TTL to Insert object
*/
if (optionsByName.get(QueryOptions.QueryOptionMapKeys.TTL) != null) {
q.using(QueryBuilder.ttl((Integer) optionsByName.get(QueryOptions.QueryOptionMapKeys.TTL)));
}
return q;
}
/**
* Generates a Batch Object for multiple Updates
*
* @param keyspaceName
* @param tableName
* @param entity
* @param objectsToSave
* @param mappingContext
* @param beanClassLoader
*
* @return The Query object to run with session.execute();
* @throws EntityWriterException
*/
public static <T> Batch toUpdateBatchQuery(final String keyspaceName, final String tableName,
final List<T> objectsToSave, CassandraPersistentEntity<?> entity, Map<String, Object> optionsByName)
throws EntityWriterException {
/*
* Return variable is a Batch statement
*/
final Batch b = QueryBuilder.batch();
List<Query> queries = new ArrayList<Query>();
for (final T objectToSave : objectsToSave) {
queries.add(toUpdateQuery(keyspaceName, tableName, objectToSave, entity, optionsByName));
}
addQueryOptions(b, optionsByName);
return b;
}
/**
* Generates a Batch Object for multiple inserts
*

View File

@@ -3,6 +3,7 @@ package org.springframework.data.cassandra.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.core.CassandraKeyspaceFactoryBean;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import com.datastax.driver.core.Cluster;
@@ -51,9 +52,9 @@ public class TestConfig extends AbstractCassandraConfiguration {
}
@Bean
public CassandraTemplate cassandraTemplate() {
public CassandraOperations cassandraTemplate() {
CassandraTemplate template = new CassandraTemplate(keyspaceFactoryBean().getObject());
CassandraOperations template = new CassandraTemplate(keyspaceFactoryBean().getObject());
return template;

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2011-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.table;
import org.springframework.data.cassandra.mapping.RowId;
import org.springframework.data.cassandra.mapping.Table;
/**
* @author David Webb
*
*/
@Table(name = "book")
public class Book {
@RowId
private String isbn;
private String title;
private String author;
private int pages;
/**
* @return Returns the isbn.
*/
public String getIsbn() {
return isbn;
}
/**
* @param isbn The isbn to set.
*/
public void setIsbn(String isbn) {
this.isbn = isbn;
}
/**
* @return Returns the title.
*/
public String getTitle() {
return title;
}
/**
* @param title The title to set.
*/
public void setTitle(String title) {
this.title = title;
}
/**
* @return Returns the author.
*/
public String getAuthor() {
return author;
}
/**
* @param author The author to set.
*/
public void setAuthor(String author) {
this.author = author;
}
/**
* @return Returns the pages.
*/
public int getPages() {
return pages;
}
/**
* @param pages The pages to set.
*/
public void setPages(int pages) {
this.pages = pages;
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.test;
package org.springframework.data.cassandra.table;
import java.util.Date;
import java.util.Set;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.test;
package org.springframework.data.cassandra.table;
import java.util.Date;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.test;
package org.springframework.data.cassandra.table;
import java.util.Date;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.test;
package org.springframework.data.cassandra.table;
import java.util.Date;
import java.util.Map;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.test;
package org.springframework.data.cassandra.table;
import java.util.Date;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.test;
package org.springframework.data.cassandra.table;
import java.util.Set;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.test;
package org.springframework.data.cassandra.table;
import java.util.Set;

View File

@@ -64,7 +64,7 @@ public class CassandraOperationsTableTest {
* Load data file to creat the test keyspace before we init the template
*/
DataLoader dataLoader = new DataLoader("Test Cluster", "localhost:9160");
dataLoader.load(new ClassPathYamlDataSet("cassandra-data.yaml"));
dataLoader.load(new ClassPathYamlDataSet("cassandra-keyspace.yaml"));
}
@@ -75,7 +75,7 @@ public class CassandraOperationsTableTest {
* Load data file to creat the test keyspace before we init the template
*/
DataLoader dataLoader = new DataLoader("Test Cluster", "localhost:9160");
dataLoader.load(new ClassPathYamlDataSet("cassandra-data.yaml"));
dataLoader.load(new ClassPathYamlDataSet("cassandra-keyspace.yaml"));
log.info("Creating Table...");

View File

@@ -18,36 +18,33 @@ package org.springframework.data.cassandra.template;
import static org.junit.Assert.assertNotNull;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import junit.framework.Assert;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.thrift.transport.TTransportException;
import org.cassandraunit.CassandraCQLUnit;
import org.cassandraunit.DataLoader;
import org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
import org.cassandraunit.dataset.yaml.ClassPathYamlDataSet;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.cassandra.config.TestConfig;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.RingMember;
import org.springframework.data.cassandra.test.LogEntry;
import org.springframework.data.cassandra.test.User;
import org.springframework.data.cassandra.table.Book;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import com.datastax.driver.core.Session;
/**
* @author David Webb
*
@@ -57,23 +54,27 @@ import com.datastax.driver.core.Session;
public class CassandraOperationsTest {
@Autowired
private CassandraTemplate cassandraTemplate;
private CassandraOperations cassandraTemplate;
private static Logger log = LoggerFactory.getLogger(CassandraOperationsTest.class);
protected Session session;
private final static String KEYSPACE_NAME = "test";
@Rule
public CassandraCQLUnit cassandraCQLUnit = new CassandraCQLUnit(new ClassPathCQLDataSet("cql-dataload.cql", "test"),
"cassandra.yaml", "localhost", 9042);
@BeforeClass
public static void startCassandra() throws IOException, TTransportException, ConfigurationException,
InterruptedException {
EmbeddedCassandraServerHelper.startEmbeddedCassandra("cassandra.yaml");
/*
* Load data file to creat the test keyspace before we init the template
*/
DataLoader dataLoader = new DataLoader("Test Cluster", "localhost:9160");
dataLoader.load(new ClassPathYamlDataSet("cassandra-data.yaml"));
dataLoader.load(new ClassPathYamlDataSet("cassandra-keyspace.yaml"));
}
@Before
@@ -82,15 +83,20 @@ public class CassandraOperationsTest {
/*
* Load data file to creat the test keyspace before we init the template
*/
DataLoader dataLoader = new DataLoader("Test Cluster", "localhost:9160");
dataLoader.load(new ClassPathYamlDataSet("cassandra-data.yaml"));
// DataLoader dataLoader = new DataLoader("Test Cluster", "localhost:9160");
// dataLoader.load(new ClassPathYamlDataSet("cassandra-keyspace.yaml"));
log.info("Creating Table...");
createTables();
// cassandraTemplate.createTable(User.class);
}
// cassandraTemplate.createTable(LogEntry.class);
private void createTables() {
// cassandraTemplate
// .executeQuery("create table users (username text, firstName text, lastName text, PRIMARY KEY (username));");
// cassandraCQLUnit.
}
@Test
@@ -109,59 +115,23 @@ public class CassandraOperationsTest {
}
}
/**
* This test inserts and selects users from the test.users table This is testing the CassandraTemplate:
* <ul>
* <li>insert()</li>
* <li>selectOne()</li>
* <li>select()</li>
* <li>remove()</li>
* </ul>
*/
// @Test
public void UsersTest() {
@Test
public void insertTest() {
User u = new User();
u.setUsername("cassandra");
u.setFirstName("Apache");
u.setLastName("Cassnadra");
u.setAge(40);
/*
* Test Single Insert
*/
Book b = new Book();
b.setIsbn("123456");
b.setTitle("Spring Data Cassandra Guide");
b.setAuthor("Cassandra Guru");
b.setPages(521);
cassandraTemplate.insert(u, "users");
cassandraTemplate.insert(b);
User us = cassandraTemplate.selectOne("select * from test.users where username='cassandra';", User.class);
b.setPages(245);
log.debug("Output from select One");
log.debug(us.getFirstName());
log.debug(us.getLastName());
List<User> users = cassandraTemplate.select("Select * from test.users", User.class);
log.debug("Output from select All");
for (User x : users) {
log.debug(x.getFirstName());
log.debug(x.getLastName());
}
cassandraTemplate.delete(u);
User delUser = cassandraTemplate.selectOne("select * from test.users where username='cassandra';", User.class);
log.info("delUser => " + delUser);
Assert.assertNull(delUser);
}
// @Test
public void multiplePKTest() {
LogEntry l = new LogEntry();
l.setLogDate(new Date());
l.setHostname("localhost");
l.setLogData("Host is Up");
cassandraTemplate.insert(l);
cassandraTemplate.update(b);
}

View File

@@ -0,0 +1 @@
create table book (isbn text, title text, author text, pages int, PRIMARY KEY (isbn));