From 3394c76b7dacdaaef507e4beb1fa1ad8c03fd28e Mon Sep 17 00:00:00 2001 From: David Webb Date: Fri, 22 Nov 2013 23:47:00 -0500 Subject: [PATCH 01/11] DATACASS-39 : WIP : New Class Layout. --- .../cassandra/core/BoundStatementFactory.java | 31 +++++++++++++++++++ .../core/PreparedStatementCreatorFactory.java | 3 ++ 2 files changed, 34 insertions(+) create mode 100644 src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java diff --git a/src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java b/src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java new file mode 100644 index 000000000..21c4fd1cd --- /dev/null +++ b/src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java @@ -0,0 +1,31 @@ +/* + * 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.cassandra.core; + +/** + * This is the primary class in core for binding many values to a Cassandra PreparedStatement. + * + *

+ * This factory will hold a cached version of the PreparedStatement, and bind many value sets to that statement + * returning a BoundStatement that can be passed to a Session.execute(Query). + *

+ * + * @author David Webb + * + */ +public class BoundStatementFactory { + +} diff --git a/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java b/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java index 974b0436e..677a20d7e 100644 --- a/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java +++ b/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java @@ -120,6 +120,8 @@ public class PreparedStatementCreatorFactory { private final List parameters; + private PreparedStatement preparedStatement; + public PreparedStatementCreatorImpl(List parameters) { this(cql, parameters); } @@ -147,6 +149,7 @@ public class PreparedStatementCreatorFactory { + " parameters but expected " + declaredParameters.size()); } } + } /* (non-Javadoc) From c1dfebb8228603b63781f389bcee4477079134a0 Mon Sep 17 00:00:00 2001 From: David Webb Date: Sat, 23 Nov 2013 01:02:55 -0500 Subject: [PATCH 02/11] Created BoundStatementFactory for high performance write ingestion. --- .../cassandra/core/BoundStatementFactory.java | 97 ++++++++++++++++++- .../cassandra/core/CassandraOperations.java | 2 + .../cassandra/core/CassandraTemplate.java | 16 +++ .../core/PreparedStatementCreatorFactory.java | 2 + .../template/CassandraOperationsTest.java | 14 +++ 5 files changed, 130 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java b/src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java index 21c4fd1cd..df861ea69 100644 --- a/src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java +++ b/src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java @@ -15,6 +15,16 @@ */ package org.springframework.cassandra.core; +import java.util.LinkedList; +import java.util.List; + +import org.springframework.util.CollectionUtils; + +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.exceptions.DriverException; + /** * This is the primary class in core for binding many values to a Cassandra PreparedStatement. * @@ -26,6 +36,91 @@ package org.springframework.cassandra.core; * @author David Webb * */ -public class BoundStatementFactory { +public class BoundStatementFactory implements PreparedStatementCreator, CqlProvider { + private final String cql; + private PreparedStatement preparedStatement; + private List> values = new LinkedList>(); + + public BoundStatementFactory(String cql) { + this.cql = cql; + } + + public void addValues(List... values) { + this.values.add(CollectionUtils.arrayToList(values)); + } + + public void addValues(Object[]... values) { + for (int i = 0; values != null && i < values.length; i++) { + this.values.add(CollectionUtils.arrayToList(values[i])); + } + } + + public void replaceValues(List... values) { + this.values = CollectionUtils.arrayToList(values); + } + + public void replaceValues(Object[]... values) { + noValues(); + for (int i = 0; values != null && i < values.length; i++) { + this.values.add(CollectionUtils.arrayToList(values[i])); + } + + } + + public void noValues() { + this.values = new LinkedList>(); + } + + /* (non-Javadoc) + * @see org.springframework.cassandra.core.CqlProvider#getCql() + */ + @Override + public String getCql() { + return this.cql; + } + + /* (non-Javadoc) + * @see org.springframework.cassandra.core.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session) + */ + @Override + public PreparedStatement createPreparedStatement(Session session) throws DriverException { + if (preparedStatement == null) { + preparedStatement = session.prepare(this.cql); + } + return preparedStatement; + } + + /** + * Bind all values with the single CQL (PreparedStatement) and return BoundStatements read for execution. + * + * @return + * @throws DriverException + */ + public List bindValues() throws DriverException { + + LinkedList boundStatements = new LinkedList(); + + for (List list : this.values) { + + // Test the type of the first value + Object v = list.get(0); + + Object[] vls; + if (v instanceof CqlParameterValue) { + LinkedList valuesList = new LinkedList(); + for (Object value : list) { + valuesList.add(((CqlParameterValue) value).getValue()); + } + vls = valuesList.toArray(); + } else { + vls = list.toArray(); + } + + boundStatements.add(preparedStatement.bind(vls)); + + } + + return boundStatements; + } } diff --git a/src/main/java/org/springframework/cassandra/core/CassandraOperations.java b/src/main/java/org/springframework/cassandra/core/CassandraOperations.java index 991c9ec95..c5e547fde 100644 --- a/src/main/java/org/springframework/cassandra/core/CassandraOperations.java +++ b/src/main/java/org/springframework/cassandra/core/CassandraOperations.java @@ -48,6 +48,8 @@ public interface CassandraOperations { */ void execute(final String cql) throws DataAccessException; + void execute(BoundStatementFactory bsf); + /** * Executes the supplied CQL Query Asynchronously and returns nothing. * diff --git a/src/main/java/org/springframework/cassandra/core/CassandraTemplate.java b/src/main/java/org/springframework/cassandra/core/CassandraTemplate.java index 0e599926d..7c474210a 100644 --- a/src/main/java/org/springframework/cassandra/core/CassandraTemplate.java +++ b/src/main/java/org/springframework/cassandra/core/CassandraTemplate.java @@ -559,4 +559,20 @@ public class CassandraTemplate extends CassandraAccessor implements CassandraOpe } }); } + + /* (non-Javadoc) + * @see org.springframework.cassandra.core.CassandraOperations#execute(org.springframework.cassandra.core.BoundStatementFactory) + */ + @Override + public void execute(BoundStatementFactory bsf) { + + bsf.createPreparedStatement(getSession()); + + List statements = bsf.bindValues(); + + for (BoundStatement bs : statements) { + getSession().execute(bs); + } + + } } \ No newline at end of file diff --git a/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java b/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java index 677a20d7e..cbf34c58d 100644 --- a/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java +++ b/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java @@ -33,6 +33,8 @@ import com.datastax.driver.core.exceptions.DriverException; /** * @author David Webb * + * @deprecated - Pattern from JDBC Template, but DW doesn't like it. Use {@link BoundStatementFactory} + * */ public class PreparedStatementCreatorFactory { diff --git a/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java b/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java index 3945a1942..db2b14583 100644 --- a/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java +++ b/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java @@ -43,6 +43,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.interceptor.DefaultKeyGenerator; +import org.springframework.cassandra.core.BoundStatementFactory; import org.springframework.cassandra.core.CachedPreparedStatementCreator; import org.springframework.cassandra.core.CassandraOperations; import org.springframework.cassandra.core.CqlParameter; @@ -195,6 +196,19 @@ public class CassandraOperationsTest { } + @Test + public void boundStatementFactoryTest() { + + String cql = "insert into book (isbn, title, author, pages) values (?, ?, ?, ?)"; + + BoundStatementFactory bsf = new BoundStatementFactory(cql); + bsf.addValues(new Object[] { "1234", "Moby Dick", "Herman Manville", new Integer(456) }, new Object[] { "2345", + "War and Peace", "Russian Dude", new Integer(456) }, new Object[] { "3456", "Jane Ayre", "Charlotte", + new Integer(456) }); + + cassandraTemplate.execute(bsf); + } + // @Test public void cachedPreparedStatementTest() { From fa9248941aa8666f624ca988c9d2af2232a242fe Mon Sep 17 00:00:00 2001 From: David Webb Date: Mon, 25 Nov 2013 11:19:27 -0500 Subject: [PATCH 03/11] DATACASS-39 : WIP : Temporarily introducing failing test for CI POC. --- .../template/CassandraOperationsTest.java | 103 +----------------- 1 file changed, 5 insertions(+), 98 deletions(-) diff --git a/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java b/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java index db2b14583..0ad8033cd 100644 --- a/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java +++ b/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java @@ -41,15 +41,11 @@ import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.cache.interceptor.DefaultKeyGenerator; import org.springframework.cassandra.core.BoundStatementFactory; -import org.springframework.cassandra.core.CachedPreparedStatementCreator; import org.springframework.cassandra.core.CassandraOperations; import org.springframework.cassandra.core.CqlParameter; import org.springframework.cassandra.core.CqlParameterValue; import org.springframework.cassandra.core.HostMapper; -import org.springframework.cassandra.core.PreparedStatementBinder; import org.springframework.cassandra.core.PreparedStatementCreatorFactory; import org.springframework.cassandra.core.ResultSetExtractor; import org.springframework.cassandra.core.RingMember; @@ -60,13 +56,10 @@ 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.BoundStatement; import com.datastax.driver.core.DataType; import com.datastax.driver.core.Host; -import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Row; -import com.datastax.driver.core.Session; import com.datastax.driver.core.exceptions.DriverException; /** @@ -196,6 +189,11 @@ public class CassandraOperationsTest { } + @Test + public void failingTest() { + assertNotNull(null); + } + @Test public void boundStatementFactoryTest() { @@ -209,97 +207,6 @@ public class CassandraOperationsTest { cassandraTemplate.execute(bsf); } - // @Test - public void cachedPreparedStatementTest() { - - log.info(echoString("Hello")); - log.info(echoString("Hello")); - - String cql = "select * from book where isbn = ?"; - - CachedPreparedStatementCreator cpsc = new CachedPreparedStatementCreator(cql); - - Book b = cassandraTemplate.query(cpsc, new PreparedStatementBinder() { - - @Override - public BoundStatement bindValues(PreparedStatement ps) throws DriverException { - return ps.bind("999999999"); - } - }, new ResultSetExtractor() { - - @Override - public Book extractData(ResultSet rs) throws DriverException, DataAccessException { - Row r = rs.one(); - Book b = new Book(); - b.setIsbn(r.getString("isbn")); - b.setTitle(r.getString("title")); - b.setAuthor(r.getString("author")); - b.setPages(r.getInt("pages")); - return b; - } - }); - - assertNotNull(b); - - log.info(b.toString()); - - try { - DefaultKeyGenerator generator = new DefaultKeyGenerator(); - - // TODO Why does method have to be public to work? Options? - Object cacheKey = generator.generate(CachedPreparedStatementCreator.class, - CachedPreparedStatementCreator.class.getMethod("getCachedPreparedStatement", Session.class, String.class), - cassandraTemplate.getSession(), cql); - - log.info("cacheKey -> " + cacheKey); - - // ConcurrentMapCache cache = (ConcurrentMapCache) cacheManager.getCache("sdc-pstmts"); - // ConcurrentMap cacheMap = cache.getNativeCache(); - // assertNotNull(cacheMap); - // log.info("CacheMap.size() -> " + cacheMap.size()); - // ValueWrapper vw = cache.get(cacheKey); - // PreparedStatement pstmt = (PreparedStatement) vw.get(); - // assertNotNull(pstmt); - // log.info(pstmt.getQueryString()); - // assertEquals(pstmt.getQueryString(), cql); - } catch (NoSuchMethodException e) { - log.error("Failed to find method", e); - } - - CachedPreparedStatementCreator cpsc2 = new CachedPreparedStatementCreator(cql); - - Book b2 = cassandraTemplate.query(cpsc2, new PreparedStatementBinder() { - - @Override - public BoundStatement bindValues(PreparedStatement ps) throws DriverException { - return ps.bind("999999999"); - } - }, new ResultSetExtractor() { - - @Override - public Book extractData(ResultSet rs) throws DriverException, DataAccessException { - Row r = rs.one(); - Book b = new Book(); - b.setIsbn(r.getString("isbn")); - b.setTitle(r.getString("title")); - b.setAuthor(r.getString("author")); - b.setPages(r.getInt("pages")); - return b; - } - }); - - assertNotNull(b2); - - log.info(b2.toString()); - - } - - @Cacheable("sdc-pstmts") - public String echoString(String s) { - log.info("In EchoString"); - return s; - } - @After public void clearCassandra() { EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); From 3a1c4fa888a32e4768857ed90f4cacbde52448da Mon Sep 17 00:00:00 2001 From: David Webb Date: Mon, 25 Nov 2013 11:19:27 -0500 Subject: [PATCH 04/11] DATACASS-39 : WIP : Removing failing test for CI POC. --- .../template/CassandraOperationsTest.java | 98 ------------------- 1 file changed, 98 deletions(-) diff --git a/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java b/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java index db2b14583..e850028a1 100644 --- a/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java +++ b/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java @@ -41,15 +41,11 @@ import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.cache.interceptor.DefaultKeyGenerator; import org.springframework.cassandra.core.BoundStatementFactory; -import org.springframework.cassandra.core.CachedPreparedStatementCreator; import org.springframework.cassandra.core.CassandraOperations; import org.springframework.cassandra.core.CqlParameter; import org.springframework.cassandra.core.CqlParameterValue; import org.springframework.cassandra.core.HostMapper; -import org.springframework.cassandra.core.PreparedStatementBinder; import org.springframework.cassandra.core.PreparedStatementCreatorFactory; import org.springframework.cassandra.core.ResultSetExtractor; import org.springframework.cassandra.core.RingMember; @@ -60,13 +56,10 @@ 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.BoundStatement; import com.datastax.driver.core.DataType; import com.datastax.driver.core.Host; -import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Row; -import com.datastax.driver.core.Session; import com.datastax.driver.core.exceptions.DriverException; /** @@ -209,97 +202,6 @@ public class CassandraOperationsTest { cassandraTemplate.execute(bsf); } - // @Test - public void cachedPreparedStatementTest() { - - log.info(echoString("Hello")); - log.info(echoString("Hello")); - - String cql = "select * from book where isbn = ?"; - - CachedPreparedStatementCreator cpsc = new CachedPreparedStatementCreator(cql); - - Book b = cassandraTemplate.query(cpsc, new PreparedStatementBinder() { - - @Override - public BoundStatement bindValues(PreparedStatement ps) throws DriverException { - return ps.bind("999999999"); - } - }, new ResultSetExtractor() { - - @Override - public Book extractData(ResultSet rs) throws DriverException, DataAccessException { - Row r = rs.one(); - Book b = new Book(); - b.setIsbn(r.getString("isbn")); - b.setTitle(r.getString("title")); - b.setAuthor(r.getString("author")); - b.setPages(r.getInt("pages")); - return b; - } - }); - - assertNotNull(b); - - log.info(b.toString()); - - try { - DefaultKeyGenerator generator = new DefaultKeyGenerator(); - - // TODO Why does method have to be public to work? Options? - Object cacheKey = generator.generate(CachedPreparedStatementCreator.class, - CachedPreparedStatementCreator.class.getMethod("getCachedPreparedStatement", Session.class, String.class), - cassandraTemplate.getSession(), cql); - - log.info("cacheKey -> " + cacheKey); - - // ConcurrentMapCache cache = (ConcurrentMapCache) cacheManager.getCache("sdc-pstmts"); - // ConcurrentMap cacheMap = cache.getNativeCache(); - // assertNotNull(cacheMap); - // log.info("CacheMap.size() -> " + cacheMap.size()); - // ValueWrapper vw = cache.get(cacheKey); - // PreparedStatement pstmt = (PreparedStatement) vw.get(); - // assertNotNull(pstmt); - // log.info(pstmt.getQueryString()); - // assertEquals(pstmt.getQueryString(), cql); - } catch (NoSuchMethodException e) { - log.error("Failed to find method", e); - } - - CachedPreparedStatementCreator cpsc2 = new CachedPreparedStatementCreator(cql); - - Book b2 = cassandraTemplate.query(cpsc2, new PreparedStatementBinder() { - - @Override - public BoundStatement bindValues(PreparedStatement ps) throws DriverException { - return ps.bind("999999999"); - } - }, new ResultSetExtractor() { - - @Override - public Book extractData(ResultSet rs) throws DriverException, DataAccessException { - Row r = rs.one(); - Book b = new Book(); - b.setIsbn(r.getString("isbn")); - b.setTitle(r.getString("title")); - b.setAuthor(r.getString("author")); - b.setPages(r.getInt("pages")); - return b; - } - }); - - assertNotNull(b2); - - log.info(b2.toString()); - - } - - @Cacheable("sdc-pstmts") - public String echoString(String s) { - log.info("In EchoString"); - return s; - } - @After public void clearCassandra() { EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); From 27e53520ccdeda68deaa20c125fb063c1baf8bed Mon Sep 17 00:00:00 2001 From: David Webb Date: Mon, 25 Nov 2013 12:20:46 -0500 Subject: [PATCH 05/11] DATACASS-39 : WIP : Removed failing test. --- .../test/integration/template/CassandraOperationsTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java b/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java index 0ad8033cd..e850028a1 100644 --- a/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java +++ b/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java @@ -189,11 +189,6 @@ public class CassandraOperationsTest { } - @Test - public void failingTest() { - assertNotNull(null); - } - @Test public void boundStatementFactoryTest() { From e7c9572489d039860ebcc4d362479aebdee9c91d Mon Sep 17 00:00:00 2001 From: David Webb Date: Mon, 25 Nov 2013 12:28:36 -0500 Subject: [PATCH 06/11] DATACASS-39 : WIP : Cleaning up javadoc build task to work. --- build.gradle | 17 +- src/main/doc/resources/background.gif | Bin 0 -> 2313 bytes src/main/doc/resources/tab.gif | Bin 0 -> 291 bytes src/main/doc/resources/titlebar.gif | Bin 0 -> 10701 bytes src/main/doc/resources/titlebar_end.gif | Bin 0 -> 849 bytes src/main/doc/spring-javadoc.css | 474 ++++++++++++++++++++++++ 6 files changed, 488 insertions(+), 3 deletions(-) create mode 100644 src/main/doc/resources/background.gif create mode 100644 src/main/doc/resources/tab.gif create mode 100644 src/main/doc/resources/titlebar.gif create mode 100644 src/main/doc/resources/titlebar_end.gif create mode 100644 src/main/doc/spring-javadoc.css diff --git a/build.gradle b/build.gradle index a98d09edf..3f9fd0a9f 100644 --- a/build.gradle +++ b/build.gradle @@ -69,14 +69,15 @@ sourceCompatibility = 1.6 targetCompatibility = 1.6 javadoc { - ext.srcDir = file("${projectDir}/docs/src/api") - destinationDir = file("${buildDir}/api") + ext.srcDir = file("${projectDir}/src/main/doc") + ext.destinationDir = file("${buildDir}/api") ext.tmpDir = file("${buildDir}/api-work") configure(options) { - //stylesheetFile = file("${srcDir}/spring-javadoc.css") + stylesheetFile = file("${srcDir}/spring-javadoc.css") //overview = "${srcDir}/overview.html" docFilesSubDirs = true + outputLevel = org.gradle.external.javadoc.JavadocOutputLevel.QUIET breakIterator = true showFromProtected() @@ -94,6 +95,16 @@ javadoc { exclude "org/springframework/data/cassandra/config/**" } + logger.error("BuildDir => ${buildDir}"); + logger.error("DestDir => ${destinationDir}"); + logger.error("ExtDestDir => ${ext.destinationDir}"); + + copy { + from "src/main/doc/resources" + into "${ext.destinationDir}/resources" + include '**/*' + } + title = "${rootProject.description} ${version} API" } diff --git a/src/main/doc/resources/background.gif b/src/main/doc/resources/background.gif new file mode 100644 index 0000000000000000000000000000000000000000..f471940fde2f39ef8943a6af9569bcf986b1579b GIT binary patch literal 2313 zcmV+k3HJ6!Nk%w1VKM-40OkMy00030|NlK(aXwsfKV5S}VtGJbbVOr%L0@%CZH88Q zl{{NzcR^uxNo<2iYk@pjY)*5FJz8x~bc{)B zfk z+1T6M-s9WdW8dcJ-wO*3@9+W*5AY543-j^$^!EPz_4eHZ2#>)41`h@dc!2OAgN6$a zCS2I?;lqgx6IR4nkpTe;1RN0f=zxMq2O=q`94V5d$&e>Unta)^<;;^G3>e7yp=ZvW z6DIW3xpSvaogXF?_4%`@(V;s}NR^5J!3hrtJV@1QRV&r5S*L!zYE|rss${iFkg&!? zTN5V#)~=bmMorwgZsEpdOE)iExo+FO-8;8Kga{=HbSQCnF=E6W3?o*|ID%uwi5**> zJXy127Y9m+=HQ|PhXWi+xNwoWv}n_%Pq%(e+H~mGqhq5kv4Mo|-n~g|7!F*xZ{xv< zCpXS~dGg^IGK?4@J-T%b(XnUHFul6n<@2&4)zzyO2) z3Q8`i0+UKY*`$}e9mmp;tg*))`|PsK1|hAo%u0K$vDwm4gaSkm0j{`26k#qAKmbuhxZ#cquDR>B zD{s8+&TH-uNg$C#68QG}1HMBHfrP&L@@w$F_!itRzXdCN@V|LDAu%3!IDtq1#1UV7 z#1RxvT=B(DWbCoU5l=ia$Pp`Hgb_?Mp@hmtxZDI2N-)v#$}PXVvdm1d>@v(v`0TUJ zF)Pu89(q`zv=w^nVTIF3@3BYIPA}c`(@ZCAwbNBEt@PDUKe5CTR8aB66IE1!w%Amt zy+jpcn~k>GZpVFg+H6x{_uOksvBlq0OyT$6TyQZ37k(cOxZr|JEx1sGm<(M9gH z-~PMqyn|tT=))UN`|-FFFUA#KToK0fUOaz=7}Z~KeHhVC&%O27cTfHQ^WBU8z4p&T zp#>D|V}XShTD;Hx745Iz{`>K-Z$A|7!*Boo{mY;G21vjH8t{M!OrQc6$iN0V@PQDF zpadsK!3tXNf*8!81~qnXWuHZ)kytd=_y+ADWvw31ouV;CdZ#ya*(l7-A-C-Y^+iit8O zBy3*`Ls$|5Hn4m_^I^|C7{m7EFn|5vTk;|oywIgCc9Bb*=L+Y$)M>9GC<|HGs@6NB zHLY%03!dDf=eDRt2O6lVSFRcsuWZEwU?=z$CZ0W?#VJfdN>HG(l%oKpyiftJc|Y)xkjSJYCrQal-0PC~()T9xwF!Jf zVi1UA#3BBbh(i8r5&v#Pz!cF41KjbCc?4u2@@Q~oKLirt2TM30;y6b+zyX2`Yl9u; z`0$3;v0-YUp&7NdPT#q`cZlbij$jvbRk6R>8g*>}*b9E+WDwmpHAAxYzyT aU_pX{M6b8i>#Dq3onfZy}_nli%!Q$ZV%e&!tN2 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 YpWXKgmku8Vj|K)Vje`mzEKCg608Q#dYybcN literal 0 HcmV?d00001 diff --git a/src/main/doc/spring-javadoc.css b/src/main/doc/spring-javadoc.css new file mode 100644 index 000000000..0aeaa97fe --- /dev/null +++ b/src/main/doc/spring-javadoc.css @@ -0,0 +1,474 @@ +/* Javadoc style sheet */ +/* +Overall document style +*/ +body { + background-color:#ffffff; + color:#353833; + font-family:Arial, Helvetica, sans-serif; + font-size:76%; + margin:0; +} +a:link, a:visited { + text-decoration:none; + color:#4c6b87; +} +a:hover, a:focus { + text-decoration:none; + color:#bb7a2a; +} +a:active { + text-decoration:none; + color:#4c6b87; +} +a[name] { + color:#353833; +} +a[name]:hover { + text-decoration:none; + color:#353833; +} +pre { + font-size:1.3em; +} +h1 { + font-size:1.8em; +} +h2 { + font-size:1.5em; +} +h3 { + font-size:1.4em; +} +h4 { + font-size:1.3em; +} +h5 { + font-size:1.2em; +} +h6 { + font-size:1.1em; +} +ul { + list-style-type:disc; +} +code, tt { + font-size:1.2em; +} +dt code { + font-size:1.2em; +} +table tr td dt code { + font-size:1.2em; + vertical-align:top; +} +sup { + font-size:.6em; +} +/* +Document title and Copyright styles +*/ +.clear { + clear:both; + height:0px; + overflow:hidden; +} +.aboutLanguage { + float:right; + padding:0px 21px; + font-size:.8em; + z-index:200; + margin-top:-7px; +} +.legalCopy { + margin-left:.5em; +} +.bar a, .bar a:link, .bar a:visited, .bar a:active { + color:#FFFFFF; + text-decoration:none; +} +.bar a:hover, .bar a:focus { + color:#bb7a2a; +} +.tab { + background-color:#0066FF; + background-image:url(resources/titlebar.gif); + background-position:left top; + background-repeat:no-repeat; + color:#ffffff; + padding:8px; + width:5em; + font-weight:bold; +} +/* +Navigation bar styles +*/ +.bar { + background-image:url(resources/background.gif); + background-repeat:repeat-x; + color:#FFFFFF; + padding:.8em .5em .4em .8em; + height:auto;/*height:1.8em;*/ + font-size:1em; + margin:0; +} +.topNav { + background-image:url(resources/background.gif); + background-repeat:repeat-x; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; +} +.bottomNav { + margin-top:10px; + background-image:url(resources/background.gif); + background-repeat:repeat-x; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; +} +.subNav { + background-color:#dee3e9; + border-bottom:1px solid #9eadc0; + float:left; + width:100%; + overflow:hidden; +} +.subNav div { + clear:left; + float:left; + padding:0 0 5px 6px; +} +ul.navList, ul.subNavList { + float:left; + margin:0 25px 0 0; + padding:0; +} +ul.navList li{ + list-style:none; + float:left; + padding:3px 6px; +} +ul.subNavList li{ + list-style:none; + float:left; + font-size:90%; +} +.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { + color:#FFFFFF; + text-decoration:none; +} +.topNav a:hover, .bottomNav a:hover { + text-decoration:none; + color:#bb7a2a; +} +.navBarCell1Rev { + background-image:url(resources/tab.gif); + background-color:#a88834; + color:#FFFFFF; + margin: auto 5px; + border:1px solid #c9aa44; +} +/* +Page header and footer styles +*/ +.header, .footer { + clear:both; + margin:0 20px; + padding:5px 0 0 0; +} +.indexHeader { + margin:10px; + position:relative; +} +.indexHeader h1 { + font-size:1.3em; +} +.title { + color:#2c4557; + margin:10px 0; +} +.subTitle { + margin:5px 0 0 0; +} +.header ul { + margin:0 0 25px 0; + padding:0; +} +.footer ul { + margin:20px 0 5px 0; +} +.header ul li, .footer ul li { + list-style:none; + font-size:1.2em; +} +/* +Heading styles +*/ +div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { + background-color:#dee3e9; + border-top:1px solid #9eadc0; + border-bottom:1px solid #9eadc0; + margin:0 0 6px -8px; + padding:2px 5px; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + background-color:#dee3e9; + border-top:1px solid #9eadc0; + border-bottom:1px solid #9eadc0; + margin:0 0 6px -8px; + padding:2px 5px; +} +ul.blockList ul.blockList li.blockList h3 { + padding:0; + margin:15px 0; +} +ul.blockList li.blockList h2 { + padding:0px 0 20px 0; +} +/* +Page layout container styles +*/ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + clear:both; + padding:10px 20px; + position:relative; +} +.indexContainer { + margin:10px; + position:relative; + font-size:1.0em; +} +.indexContainer h2 { + font-size:1.1em; + padding:0 0 3px 0; +} +.indexContainer ul { + margin:0; + padding:0; +} +.indexContainer ul li { + list-style:none; +} +.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { + font-size:1.1em; + font-weight:bold; + margin:10px 0 0 0; + color:#4E4E4E; +} +.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { + margin:10px 0 10px 20px; +} +.serializedFormContainer dl.nameValue dt { + margin-left:1px; + font-size:1.1em; + display:inline; + font-weight:bold; +} +.serializedFormContainer dl.nameValue dd { + margin:0 0 0 1px; + font-size:1.1em; + display:inline; +} +/* +List styles +*/ +ul.horizontal li { + display:inline; + font-size:0.9em; +} +ul.inheritance { + margin:0; + padding:0; +} +ul.inheritance li { + display:inline; + list-style:none; +} +ul.inheritance li ul.inheritance { + margin-left:15px; + padding-left:15px; + padding-top:1px; +} +ul.blockList, ul.blockListLast { + margin:10px 0 10px 0; + padding:0; +} +ul.blockList li.blockList, ul.blockListLast li.blockList { + list-style:none; + margin-bottom:25px; +} +ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { + padding:0px 20px 5px 10px; + border:1px solid #9eadc0; + background-color:#f9f9f9; +} +ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { + padding:0 0 5px 8px; + background-color:#ffffff; + border:1px solid #9eadc0; + border-top:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { + margin-left:0; + padding-left:0; + padding-bottom:15px; + border:none; + border-bottom:1px solid #9eadc0; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { + list-style:none; + border-bottom:none; + padding-bottom:0; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top:0; + margin-bottom:1px; +} +/* +Table styles +*/ +.contentContainer table, .classUseContainer table, .constantValuesContainer table { + border-bottom:1px solid #9eadc0; + width:100%; +} +.contentContainer ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table { + width:100%; +} +.contentContainer .description table, .contentContainer .details table { + border-bottom:none; +} +.contentContainer ul li table th.colOne, .contentContainer ul li table th.colFirst, .contentContainer ul li table th.colLast, .classUseContainer ul li table th, .constantValuesContainer ul li table th, .contentContainer ul li table td.colOne, .contentContainer ul li table td.colFirst, .contentContainer ul li table td.colLast, .classUseContainer ul li table td, .constantValuesContainer ul li table td{ + vertical-align:top; + padding-right:20px; +} +.contentContainer ul li table th.colLast, .classUseContainer ul li table th.colLast,.constantValuesContainer ul li table th.colLast, +.contentContainer ul li table td.colLast, .classUseContainer ul li table td.colLast,.constantValuesContainer ul li table td.colLast, +.contentContainer ul li table th.colOne, .classUseContainer ul li table th.colOne, +.contentContainer ul li table td.colOne, .classUseContainer ul li table td.colOne { + padding-right:3px; +} +.overviewSummary caption, .packageSummary caption, .contentContainer ul.blockList li.blockList caption, .summary caption, .classUseContainer caption, .constantValuesContainer caption { + position:relative; + text-align:left; + background-repeat:no-repeat; + color:#FFFFFF; + font-weight:bold; + clear:none; + overflow:hidden; + padding:0px; + margin:0px; +} +caption a:link, caption a:hover, caption a:active, caption a:visited { + color:#FFFFFF; +} +.overviewSummary caption span, .packageSummary caption span, .contentContainer ul.blockList li.blockList caption span, .summary caption span, .classUseContainer caption span, .constantValuesContainer caption span { + white-space:nowrap; + padding-top:8px; + padding-left:8px; + display:block; + float:left; + background-image:url(resources/titlebar.gif); + height:18px; +} +.overviewSummary .tabEnd, .packageSummary .tabEnd, .contentContainer ul.blockList li.blockList .tabEnd, .summary .tabEnd, .classUseContainer .tabEnd, .constantValuesContainer .tabEnd { + width:10px; + background-image:url(resources/titlebar_end.gif); + background-repeat:no-repeat; + background-position:top right; + position:relative; + float:left; +} +ul.blockList ul.blockList li.blockList table { + margin:0 0 12px 0px; + width:100%; +} +.tableSubHeadingColor { + background-color: #EEEEFF; +} +.altColor { + background-color:#eeeeef; +} +.rowColor { + background-color:#ffffff; +} +.overviewSummary td, .packageSummary td, .contentContainer ul.blockList li.blockList td, .summary td, .classUseContainer td, .constantValuesContainer td { + text-align:left; + padding:3px 3px 3px 7px; +} +th.colFirst, th.colLast, th.colOne, .constantValuesContainer th { + background:#dee3e9; + border-top:1px solid #9eadc0; + border-bottom:1px solid #9eadc0; + text-align:left; + padding:3px 3px 3px 7px; +} +td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { + font-weight:bold; +} +td.colFirst, th.colFirst { + border-left:1px solid #9eadc0; + white-space:nowrap; +} +td.colLast, th.colLast { + border-right:1px solid #9eadc0; +} +td.colOne, th.colOne { + border-right:1px solid #9eadc0; + border-left:1px solid #9eadc0; +} +table.overviewSummary { + padding:0px; + margin-left:0px; +} +table.overviewSummary td.colFirst, table.overviewSummary th.colFirst, +table.overviewSummary td.colOne, table.overviewSummary th.colOne { + width:25%; + vertical-align:middle; +} +table.packageSummary td.colFirst, table.overviewSummary th.colFirst { + width:25%; + vertical-align:middle; +} +/* +Content styles +*/ +.description pre { + margin-top:0; +} +.deprecatedContent { + margin:0; + padding:10px 0; +} +.docSummary { + padding:0; +} +/* +Formatting effect styles +*/ +.sourceLineNo { + color:green; + padding:0 30px 0 0; +} +h1.hidden { + visibility:hidden; + overflow:hidden; + font-size:.9em; +} +.block { + display:block; + margin:3px 0 0 0; +} +.strong { + font-weight:bold; +} From 0faa02568ea913eb32306aaae1ce8a9eac67de27 Mon Sep 17 00:00:00 2001 From: Alex Shvid Date: Mon, 25 Nov 2013 17:33:46 -0800 Subject: [PATCH 07/11] PreparedStatementCreatorImpl null return fix --- .../cassandra/core/PreparedStatementCreatorImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorImpl.java b/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorImpl.java index 2bedb7f67..cbf5afd43 100644 --- a/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorImpl.java +++ b/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorImpl.java @@ -47,7 +47,7 @@ public class PreparedStatementCreatorImpl implements PreparedStatementCreator, C public BoundStatement bindValues(PreparedStatement ps) throws DriverException { // Nothing to set if there are no values if (values == null) { - return null; + return new BoundStatement(ps); } return ps.bind(values.toArray()); From abbdb3f735c6d24cb99e078f66a7d8ff4495dad5 Mon Sep 17 00:00:00 2001 From: Alex Shvid Date: Mon, 25 Nov 2013 17:55:58 -0800 Subject: [PATCH 08/11] ColumnSpecification constant name fix --- .../cassandra/core/keyspace/ColumnSpecification.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java b/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java index bdbcf0220..b0b8dfe30 100644 --- a/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java +++ b/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java @@ -27,7 +27,7 @@ public class ColumnSpecification { /** * Default ordering of primary key fields; value is {@link Ordering#ASCENDING}. */ - public static final Ordering DFAULT_ORDERING = ASCENDING; + public static final Ordering DEFAULT_ORDERING = ASCENDING; private String name; private DataType type; // TODO: determining if we should be coupling this to Datastax Java Driver type? @@ -80,12 +80,12 @@ public class ColumnSpecification { /** * Identifies this column as a primary key column with default ordering. Sets the column's {@link #keyType} to - * {@link KeyType#PRIMARY} and its {@link #ordering} to {@link #DFAULT_ORDERING}. + * {@link KeyType#PRIMARY} and its {@link #ordering} to {@link #DEFAULT_ORDERING}. * * @return this */ public ColumnSpecification primary() { - return primary(DFAULT_ORDERING); + return primary(DEFAULT_ORDERING); } /** From 97ea65bdcfd4ab33f7823b4423b78f1cdbd96394 Mon Sep 17 00:00:00 2001 From: Alex Shvid Date: Mon, 25 Nov 2013 18:12:26 -0800 Subject: [PATCH 09/11] CassandraExceptionTranslator wrong order fix --- .../cassandra/support/CassandraExceptionTranslator.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/cassandra/support/CassandraExceptionTranslator.java b/src/main/java/org/springframework/cassandra/support/CassandraExceptionTranslator.java index 1733e2b76..b2fe93910 100644 --- a/src/main/java/org/springframework/cassandra/support/CassandraExceptionTranslator.java +++ b/src/main/java/org/springframework/cassandra/support/CassandraExceptionTranslator.java @@ -70,14 +70,14 @@ public class CassandraExceptionTranslator implements PersistenceExceptionTransla */ public DataAccessException translateExceptionIfPossible(RuntimeException x) { - if (!(x instanceof DriverException)) { - return null; - } - if (x instanceof DataAccessException) { return (DataAccessException) x; } + if (!(x instanceof DriverException)) { + return null; + } + // Remember: subclasses must come before superclasses, otherwise the // superclass would match before the subclass! From 6d020745cf373558c30b34e643198655b91f7f3e Mon Sep 17 00:00:00 2001 From: David Webb Date: Mon, 25 Nov 2013 22:44:02 -0500 Subject: [PATCH 10/11] DONE - issue DATACASS-39: Better PreparedStatement Support https://jira.springsource.org/browse/DATACASS-39 --- .../cassandra/core/BoundStatementFactory.java | 126 -------- .../core/CachedPreparedStatementCreator.java | 80 ----- .../cassandra/core/CassandraOperations.java | 42 ++- .../cassandra/core/CassandraTemplate.java | 54 +++- .../cassandra/core/CqlParameter.java | 139 --------- .../cassandra/core/CqlParameterValue.java | 68 ----- .../core/PreparedStatementCreatorFactory.java | 207 ------------- .../cassandra/core/RowIterator.java | 29 ++ ...tractEmbeddedCassandraIntegrationTest.java | 14 +- ...eateTableCqlGeneratorIntegrationTests.java | 1 + .../template/CassandraOperationsTest.java | 275 ++++++++++++++++++ .../test/integration/config/TestConfig.java | 4 +- .../template/CassandraOperationsTest.java | 215 -------------- 13 files changed, 405 insertions(+), 849 deletions(-) delete mode 100644 src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java delete mode 100644 src/main/java/org/springframework/cassandra/core/CachedPreparedStatementCreator.java delete mode 100644 src/main/java/org/springframework/cassandra/core/CqlParameter.java delete mode 100644 src/main/java/org/springframework/cassandra/core/CqlParameterValue.java delete mode 100644 src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java create mode 100644 src/main/java/org/springframework/cassandra/core/RowIterator.java rename src/test/java/org/springframework/cassandra/test/integration/{core/cql/generator => }/AbstractEmbeddedCassandraIntegrationTest.java (79%) create mode 100644 src/test/java/org/springframework/cassandra/test/integration/core/template/CassandraOperationsTest.java delete mode 100644 src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java diff --git a/src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java b/src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java deleted file mode 100644 index df861ea69..000000000 --- a/src/main/java/org/springframework/cassandra/core/BoundStatementFactory.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2011-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cassandra.core; - -import java.util.LinkedList; -import java.util.List; - -import org.springframework.util.CollectionUtils; - -import com.datastax.driver.core.BoundStatement; -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.exceptions.DriverException; - -/** - * This is the primary class in core for binding many values to a Cassandra PreparedStatement. - * - *

- * This factory will hold a cached version of the PreparedStatement, and bind many value sets to that statement - * returning a BoundStatement that can be passed to a Session.execute(Query). - *

- * - * @author David Webb - * - */ -public class BoundStatementFactory implements PreparedStatementCreator, CqlProvider { - - private final String cql; - private PreparedStatement preparedStatement; - private List> values = new LinkedList>(); - - public BoundStatementFactory(String cql) { - this.cql = cql; - } - - public void addValues(List... values) { - this.values.add(CollectionUtils.arrayToList(values)); - } - - public void addValues(Object[]... values) { - for (int i = 0; values != null && i < values.length; i++) { - this.values.add(CollectionUtils.arrayToList(values[i])); - } - } - - public void replaceValues(List... values) { - this.values = CollectionUtils.arrayToList(values); - } - - public void replaceValues(Object[]... values) { - noValues(); - for (int i = 0; values != null && i < values.length; i++) { - this.values.add(CollectionUtils.arrayToList(values[i])); - } - - } - - public void noValues() { - this.values = new LinkedList>(); - } - - /* (non-Javadoc) - * @see org.springframework.cassandra.core.CqlProvider#getCql() - */ - @Override - public String getCql() { - return this.cql; - } - - /* (non-Javadoc) - * @see org.springframework.cassandra.core.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session) - */ - @Override - public PreparedStatement createPreparedStatement(Session session) throws DriverException { - if (preparedStatement == null) { - preparedStatement = session.prepare(this.cql); - } - return preparedStatement; - } - - /** - * Bind all values with the single CQL (PreparedStatement) and return BoundStatements read for execution. - * - * @return - * @throws DriverException - */ - public List bindValues() throws DriverException { - - LinkedList boundStatements = new LinkedList(); - - for (List list : this.values) { - - // Test the type of the first value - Object v = list.get(0); - - Object[] vls; - if (v instanceof CqlParameterValue) { - LinkedList valuesList = new LinkedList(); - for (Object value : list) { - valuesList.add(((CqlParameterValue) value).getValue()); - } - vls = valuesList.toArray(); - } else { - vls = list.toArray(); - } - - boundStatements.add(preparedStatement.bind(vls)); - - } - - return boundStatements; - } -} diff --git a/src/main/java/org/springframework/cassandra/core/CachedPreparedStatementCreator.java b/src/main/java/org/springframework/cassandra/core/CachedPreparedStatementCreator.java deleted file mode 100644 index 90fdba895..000000000 --- a/src/main/java/org/springframework/cassandra/core/CachedPreparedStatementCreator.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2011-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cassandra.core; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.Assert; - -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.exceptions.DriverException; - -/** - * Created a PreparedStatement and retrieved the PreparedStatement from cache if the statement has been prepared - * previously. In general, this creator should be used over the {@link SimplePreparedStatementCreator} as it provides - * better performance. - * - *

- * There is overhead in Cassandra when Preparing a Statement. This is negligible on a single data center configuration, - * but when your cluster spans multiple data centers, preparing the same statement over and over again is not necessary - * and causes performance issues in high throughput use cases. - *

- * - * @author David Webb - * - */ -public class CachedPreparedStatementCreator implements PreparedStatementCreator, CqlProvider { - - private static Logger log = LoggerFactory.getLogger(CachedPreparedStatementCreator.class); - - private final String cql; - - private PreparedStatement cache; - - /** - * Create a CachedPreparedStatementCreator from the provided CQL. - * - * @param cql - */ - public CachedPreparedStatementCreator(String cql) { - Assert.notNull(cql, "CQL is required to create a PreparedStatement"); - this.cql = cql; - } - - /* (non-Javadoc) - * @see org.springframework.cassandra.core.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session) - */ - @Override - public PreparedStatement createPreparedStatement(Session session) throws DriverException { - if (cache == null) { - log.debug("PreparedStatement cache is null, preparing new Statement"); - cache = session.prepare(getCql()); - } else { - log.debug("Using cached PreparedStatement"); - } - return cache; - } - - /* (non-Javadoc) - * @see org.springframework.cassandra.core.CqlProvider#getCql() - */ - @Override - public String getCql() { - return this.cql; - } - -} diff --git a/src/main/java/org/springframework/cassandra/core/CassandraOperations.java b/src/main/java/org/springframework/cassandra/core/CassandraOperations.java index c5e547fde..3d5d8cf41 100644 --- a/src/main/java/org/springframework/cassandra/core/CassandraOperations.java +++ b/src/main/java/org/springframework/cassandra/core/CassandraOperations.java @@ -48,8 +48,6 @@ public interface CassandraOperations { */ void execute(final String cql) throws DataAccessException; - void execute(BoundStatementFactory bsf); - /** * Executes the supplied CQL Query Asynchronously and returns nothing. * @@ -400,4 +398,44 @@ public interface CassandraOperations { */ Session getSession(); + /** + * This is an operation designed for high performance writes. The cql is used to create a PreparedStatement once, then + * all row values are bound to the single PreparedStatement and executed against the Session. + * + *

+ * This is used internally by the other ingest() methods, but can be used if you want to write your own RowIterator. + * The Object[] length returned by the next() implementation must match the number of bind variables in the CQL. + *

+ * + * @param cql The CQL + * @param rowIterator Implementation to provide the Object[] to be bound to the CQL. + */ + void ingest(String cql, RowIterator rowIterator); + + /** + * This is an operation designed for high performance writes. The cql is used to create a PreparedStatement once, then + * all row values are bound to the single PreparedStatement and executed against the Session. + * + *

+ * The List length must match the number of bind variables in the CQL. + *

+ * + * @param cql The CQL + * @param rows List of List with data to bind to the CQL. + */ + void ingest(String cql, List> rows); + + /** + * This is an operation designed for high performance writes. The cql is used to create a PreparedStatement once, then + * all row values are bound to the single PreparedStatement and executed against the Session. + * + *

+ * The Object[] length of the nested array must match the number of bind variables in the CQL. + *

+ * + * @param cql The CQL + * @param rows Object array of Object array of values to bind to the CQL. + */ + void ingest(String cql, Object[][] rows); + } diff --git a/src/main/java/org/springframework/cassandra/core/CassandraTemplate.java b/src/main/java/org/springframework/cassandra/core/CassandraTemplate.java index 7c474210a..d428ec884 100644 --- a/src/main/java/org/springframework/cassandra/core/CassandraTemplate.java +++ b/src/main/java/org/springframework/cassandra/core/CassandraTemplate.java @@ -561,18 +561,58 @@ public class CassandraTemplate extends CassandraAccessor implements CassandraOpe } /* (non-Javadoc) - * @see org.springframework.cassandra.core.CassandraOperations#execute(org.springframework.cassandra.core.BoundStatementFactory) + * @see org.springframework.cassandra.core.CassandraOperations#execute(java.lang.String, org.springframework.cassandra.core.RowProvider, int) */ @Override - public void execute(BoundStatementFactory bsf) { + public void ingest(String cql, RowIterator rowIterator) { - bsf.createPreparedStatement(getSession()); + PreparedStatement preparedStatement = getSession().prepare(cql); - List statements = bsf.bindValues(); - - for (BoundStatement bs : statements) { - getSession().execute(bs); + while (rowIterator.hasNext()) { + getSession().execute(preparedStatement.bind(rowIterator.next())); } } + + /* (non-Javadoc) + * @see org.springframework.cassandra.core.CassandraOperations#execute(java.lang.String, java.util.List) + */ + @Override + public void ingest(String cql, List> rows) { + + Assert.notNull(rows); + Assert.notEmpty(rows); + + Object[][] values = new Object[rows.size()][]; + int i = 0; + for (List row : rows) { + values[i++] = row.toArray(); + } + + ingest(cql, values); + + } + + /* (non-Javadoc) + * @see org.springframework.cassandra.core.CassandraOperations#execute(java.lang.String, java.lang.Object[][]) + */ + @Override + public void ingest(String cql, final Object[][] rows) { + + ingest(cql, new RowIterator() { + + int index = 0; + + @Override + public Object[] next() { + return rows[index++]; + } + + @Override + public boolean hasNext() { + return index < rows.length; + } + + }); + } } \ No newline at end of file diff --git a/src/main/java/org/springframework/cassandra/core/CqlParameter.java b/src/main/java/org/springframework/cassandra/core/CqlParameter.java deleted file mode 100644 index bfd5db193..000000000 --- a/src/main/java/org/springframework/cassandra/core/CqlParameter.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2011-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cassandra.core; - -import java.util.LinkedList; -import java.util.List; - -import org.springframework.util.Assert; - -import com.datastax.driver.core.DataType; - -/** - * @author David Webb - * - */ -public class CqlParameter { - - /** The name of the parameter, if any */ - private String name; - - /** SQL type constant from {@link DataType} */ - private final DataType type; - - /** The scale to apply in case of a NUMERIC or DECIMAL type, if any */ - private Integer scale; - - /** - * Create a new anonymous CqlParameter, supplying the SQL type. - * - * @param type Cassandra Data Type of the parameter according to {@link DataType} - */ - public CqlParameter(DataType type) { - this.type = type; - } - - /** - * Create a new anonymous CqlParameter, supplying the SQL type. - * - * @param type Cassandra Data Type of the parameter according to {@link DataType} - * @param scale the number of digits after the decimal point - */ - public CqlParameter(DataType type, int scale) { - this.type = type; - this.scale = scale; - } - - /** - * Create a new CqlParameter, supplying name and SQL type. - * - * @param name name of the parameter, as used in input and output maps - * @param type Cassandra Data Type of the parameter according to {@link DataType} - */ - public CqlParameter(String name, DataType type) { - this.name = name; - this.type = type; - } - - /** - * Create a new CqlParameter, supplying name and SQL type. - * - * @param name name of the parameter, as used in input and output maps - * @param type Cassandra Data Type of the parameter according to {@link DataType} - * @param scale the number of digits after the decimal point (for DECIMAL and NUMERIC types) - */ - public CqlParameter(String name, DataType type, int scale) { - this.name = name; - this.type = type; - this.scale = scale; - } - - /** - * Copy constructor. - * - * @param otherParam the CqlParameter object to copy from - */ - public CqlParameter(CqlParameter otherParam) { - Assert.notNull(otherParam, "CqlParameter object must not be null"); - this.name = otherParam.name; - this.type = otherParam.type; - this.scale = otherParam.scale; - } - - /** - * Return the name of the parameter. - */ - public String getName() { - return this.name; - } - - /** - * Return the SQL type of the parameter. - */ - public DataType getType() { - return this.type; - } - - /** - * Return the scale of the parameter, if any. - */ - public Integer getScale() { - return this.scale; - } - - /** - * Return whether this parameter holds input values that should be set before execution even if they are {@code null}. - *

- * This implementation always returns {@code true}. - */ - public boolean isInputValueProvided() { - return true; - } - - /** - * Convert a list of JDBC types, as defined in {@code java.sql.Types}, to a List of CqlParameter objects as used in - * this package. - */ - public static List sqlTypesToAnonymousParameterList(DataType[] types) { - List result = new LinkedList(); - if (types != null) { - for (DataType type : types) { - result.add(new CqlParameter(type)); - } - } - return result; - } -} diff --git a/src/main/java/org/springframework/cassandra/core/CqlParameterValue.java b/src/main/java/org/springframework/cassandra/core/CqlParameterValue.java deleted file mode 100644 index c2932815f..000000000 --- a/src/main/java/org/springframework/cassandra/core/CqlParameterValue.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2011-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cassandra.core; - -import com.datastax.driver.core.DataType; - -/** - * @author David Webb - * - */ -public class CqlParameterValue extends CqlParameter { - - private final Object value; - - /** - * Create a new CqlParameterValue, supplying the Cassandra DataType. - * - * @param type Cassandra Data Type of the parameter according to {@link DataType} - * @param value the value object - */ - public CqlParameterValue(DataType type, Object value) { - super(type); - this.value = value; - } - - /** - * Create a new CqlParameterValue, supplying the Cassandra DataType. - * - * @param type Cassandra Data Type of the parameter according to {@link DataType} - * @param scale the number of digits after the decimal point (for DECIMAL and NUMERIC types) - * @param value the value object - */ - public CqlParameterValue(DataType type, int scale, Object value) { - super(type, scale); - this.value = value; - } - - /** - * Create a new CqlParameterValue based on the given CqlParameter declaration. - * - * @param declaredParam the declared CqlParameter to define a value for - * @param value the value object - */ - public CqlParameterValue(CqlParameter declaredParam, Object value) { - super(declaredParam); - this.value = value; - } - - /** - * Return the value object that this parameter value holds. - */ - public Object getValue() { - return this.value; - } -} diff --git a/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java b/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java deleted file mode 100644 index cbf34c58d..000000000 --- a/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorFactory.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2011-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cassandra.core; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.util.Assert; - -import com.datastax.driver.core.BoundStatement; -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.exceptions.DriverException; - -/** - * @author David Webb - * - * @deprecated - Pattern from JDBC Template, but DW doesn't like it. Use {@link BoundStatementFactory} - * - */ -public class PreparedStatementCreatorFactory { - - /** - * The CQL, which won't change when the parameters change - */ - private final String cql; - - /** List of CqlParameter objects. May not be {@code null}. */ - private final List declaredParameters; - - /** - * Create a new factory. - */ - public PreparedStatementCreatorFactory(String cql) { - this.cql = cql; - this.declaredParameters = new LinkedList(); - } - - /** - * Create a new factory with the given CQL and parameters. - * - * @param cql CQL - * @param declaredParameters list of {@link CqlParameter} objects - * @see CqlParameter - */ - public PreparedStatementCreatorFactory(String cql, List declaredParameters) { - this.cql = cql; - this.declaredParameters = declaredParameters; - } - - /** - * Return a new PreparedStatementBinder for the given parameters. - * - * @param params list of parameters (may be {@code null}) - */ - public PreparedStatementBinder newPreparedStatementBinder(List params) { - return new PreparedStatementCreatorImpl(params != null ? params : Collections.emptyList()); - } - - /** - * Return a new PreparedStatementBinder for the given parameters. - * - * @param params the parameter array (may be {@code null}) - */ - public PreparedStatementBinder newPreparedStatementBinder(Object[] params) { - return new PreparedStatementCreatorImpl(params != null ? Arrays.asList(params) : Collections.emptyList()); - } - - /** - * Return a new PreparedStatementCreator for the given parameters. - * - * @param params list of parameters (may be {@code null}) - */ - public PreparedStatementCreator newPreparedStatementCreator(List params) { - return new PreparedStatementCreatorImpl(params != null ? params : Collections.emptyList()); - } - - /** - * Return a new PreparedStatementCreator for the given parameters. - * - * @param params the parameter array (may be {@code null}) - */ - public PreparedStatementCreator newPreparedStatementCreator(Object[] params) { - return new PreparedStatementCreatorImpl(params != null ? Arrays.asList(params) : Collections.emptyList()); - } - - /** - * Return a new PreparedStatementCreator for the given parameters. - * - * @param sqlToUse the actual SQL statement to use (if different from the factory's, for example because of named - * parameter expanding) - * @param params the parameter array (may be {@code null}) - */ - public PreparedStatementCreator newPreparedStatementCreator(String sqlToUse, Object[] params) { - return new PreparedStatementCreatorImpl(sqlToUse, params != null ? Arrays.asList(params) : Collections.emptyList()); - } - - /** - * PreparedStatementCreator implementation returned by this class. - */ - private class PreparedStatementCreatorImpl implements PreparedStatementCreator, PreparedStatementBinder, CqlProvider { - - private final String actualCql; - - private final List parameters; - - private PreparedStatement preparedStatement; - - public PreparedStatementCreatorImpl(List parameters) { - this(cql, parameters); - } - - /** - * @param actualCql - * @param parameters - */ - public PreparedStatementCreatorImpl(String actualCql, List parameters) { - this.actualCql = actualCql; - Assert.notNull(parameters, "Parameters List must not be null"); - this.parameters = parameters; - if (this.parameters.size() != declaredParameters.size()) { - Set names = new HashSet(); - for (int i = 0; i < parameters.size(); i++) { - Object param = parameters.get(i); - if (param instanceof CqlParameterValue) { - names.add(((CqlParameterValue) param).getName()); - } else { - names.add("Parameter #" + i); - } - } - if (names.size() != declaredParameters.size()) { - throw new InvalidDataAccessApiUsageException("CQL [" + cql + "]: given " + names.size() - + " parameters but expected " + declaredParameters.size()); - } - } - - } - - /* (non-Javadoc) - * @see org.springframework.cassandra.core.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session) - */ - @Override - public PreparedStatement createPreparedStatement(Session session) throws DriverException { - return session.prepare(this.actualCql); - } - - /* (non-Javadoc) - * @see org.springframework.cassandra.core.PreparedStatementBinder#bindValues(com.datastax.driver.core.PreparedStatement) - */ - @Override - public BoundStatement bindValues(PreparedStatement ps) throws DriverException { - if (this.parameters == null || this.parameters.size() == 0) { - return ps.bind(); - } - - // Test the type of the first value - Object v = this.parameters.get(0); - Object[] values; - if (v instanceof CqlParameterValue) { - LinkedList valuesList = new LinkedList(); - for (Object value : this.parameters) { - valuesList.add(((CqlParameterValue) value).getValue()); - } - values = valuesList.toArray(); - } else { - values = this.parameters.toArray(); - } - - return ps.bind(values); - } - - /* (non-Javadoc) - * @see org.springframework.cassandra.core.CqlProvider#getCql() - */ - @Override - public String getCql() { - return cql; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("PreparedStatementCreatorFactory.PreparedStatementCreatorImpl: cql=["); - sb.append(cql).append("]; parameters=").append(this.parameters); - return sb.toString(); - } - - } -} diff --git a/src/main/java/org/springframework/cassandra/core/RowIterator.java b/src/main/java/org/springframework/cassandra/core/RowIterator.java new file mode 100644 index 000000000..9fb98ce57 --- /dev/null +++ b/src/main/java/org/springframework/cassandra/core/RowIterator.java @@ -0,0 +1,29 @@ +/* + * 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.cassandra.core; + + +/** + * @author David Webb + * + */ +public interface RowIterator { + + Object[] next(); + + boolean hasNext(); + +} diff --git a/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/AbstractEmbeddedCassandraIntegrationTest.java b/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java similarity index 79% rename from src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/AbstractEmbeddedCassandraIntegrationTest.java rename to src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java index be34c18f3..c636965fc 100644 --- a/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/AbstractEmbeddedCassandraIntegrationTest.java +++ b/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java @@ -1,4 +1,4 @@ -package org.springframework.cassandra.test.integration.core.cql.generator; +package org.springframework.cassandra.test.integration; import java.io.IOException; import java.util.UUID; @@ -16,10 +16,14 @@ import com.datastax.driver.core.Session; public abstract class AbstractEmbeddedCassandraIntegrationTest { + protected final static String CASSANDRA_CONFIG = "cassandra.yaml"; + protected final static String CASSANDRA_HOST = "localhost"; + protected final static int CASSANDRA_NATIVE_PORT = 9042; + @BeforeClass public static void beforeClass() throws ConfigurationException, TTransportException, IOException, InterruptedException { - EmbeddedCassandraServerHelper.startEmbeddedCassandra("cassandra.yaml"); + EmbeddedCassandraServerHelper.startEmbeddedCassandra(CASSANDRA_CONFIG); } /** @@ -51,7 +55,7 @@ public abstract class AbstractEmbeddedCassandraIntegrationTest { } public Cluster cluster() { - return Cluster.builder().addContactPoint("localhost").withPort(9042).build(); + return Cluster.builder().addContactPoint(CASSANDRA_HOST).withPort(CASSANDRA_NATIVE_PORT).build(); } @Before @@ -69,7 +73,9 @@ public abstract class AbstractEmbeddedCassandraIntegrationTest { session.execute("CREATE KEYSPACE " + keyspace + " WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 1};"); session.execute("USE " + keyspace + ";"); - } // else keyspace already exists + } else {// else keyspace already exists + session = cluster.connect(keyspace); + } } } } diff --git a/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateTableCqlGeneratorIntegrationTests.java b/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateTableCqlGeneratorIntegrationTests.java index be7b762d9..82872f163 100644 --- a/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateTableCqlGeneratorIntegrationTests.java +++ b/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateTableCqlGeneratorIntegrationTests.java @@ -3,6 +3,7 @@ package org.springframework.cassandra.test.integration.core.cql.generator; import static org.springframework.cassandra.test.integration.core.cql.generator.CqlTableSpecificationAssertions.assertTable; import org.junit.Test; +import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.BasicTest; import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.CompositePartitionKeyTest; import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.CreateTableTest; diff --git a/src/test/java/org/springframework/cassandra/test/integration/core/template/CassandraOperationsTest.java b/src/test/java/org/springframework/cassandra/test/integration/core/template/CassandraOperationsTest.java new file mode 100644 index 000000000..246d628cb --- /dev/null +++ b/src/test/java/org/springframework/cassandra/test/integration/core/template/CassandraOperationsTest.java @@ -0,0 +1,275 @@ +/* + * 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.cassandra.test.integration.core.template; + +import static org.junit.Assert.assertNotNull; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import junit.framework.Assert; + +import org.cassandraunit.CassandraCQLUnit; +import org.cassandraunit.dataset.cql.ClassPathCQLDataSet; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cassandra.core.CassandraOperations; +import org.springframework.cassandra.core.CassandraTemplate; +import org.springframework.cassandra.core.HostMapper; +import org.springframework.cassandra.core.PreparedStatementBinder; +import org.springframework.cassandra.core.ResultSetExtractor; +import org.springframework.cassandra.core.RingMember; +import org.springframework.cassandra.core.RowIterator; +import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.dao.DataAccessException; +import org.springframework.data.cassandra.test.integration.table.Book; + +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.Host; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.exceptions.DriverException; + +/** + * Unit Tests for CassandraTemplate + * + * @author David Webb + * + */ +public class CassandraOperationsTest extends AbstractEmbeddedCassandraIntegrationTest { + + private CassandraOperations cassandraTemplate; + + private static Logger log = LoggerFactory.getLogger(CassandraOperationsTest.class); + + /* + * Objects used for test data + */ + final Object[] o1 = new Object[] { "1234", "Moby Dick", "Herman Manville", new Integer(456) }; + final Object[] o2 = new Object[] { "2345", "War and Peace", "Russian Dude", new Integer(456) }; + final Object[] o3 = new Object[] { "3456", "Jane Ayre", "Charlotte", new Integer(456) }; + + /** + * This loads any test specific Cassandra objects + */ + @Rule + public CassandraCQLUnit cassandraCQLUnit = new CassandraCQLUnit(new ClassPathCQLDataSet( + "cassandraOperationsTest-cql-dataload.cql", this.keyspace), CASSANDRA_CONFIG, CASSANDRA_HOST, + CASSANDRA_NATIVE_PORT); + + @Before + public void setupTemplate() { + cassandraTemplate = new CassandraTemplate(session); + } + + @Test + public void ringTest() { + + List ring = cassandraTemplate.describeRing(); + + /* + * There must be 1 node in the cluster if the embedded server is + * running. + */ + assertNotNull(ring); + + for (RingMember h : ring) { + log.info("ringTest Host -> " + h.address); + } + } + + @Test + public void hostMapperTest() { + + List ring = (List) cassandraTemplate.describeRing(new HostMapper() { + + @Override + public Collection mapHosts(Set host) throws DriverException { + + List list = new LinkedList(); + + for (Host h : host) { + MyHost mh = new MyHost(); + mh.someName = h.getAddress().getCanonicalHostName(); + list.add(mh); + } + + return list; + } + + }); + + assertNotNull(ring); + Assert.assertTrue(ring.size() > 0); + + for (MyHost h : ring) { + log.info("hostMapperTest Host -> " + h.someName); + } + + } + + @Test + public void ingestionTestListOfList() { + + String cql = "insert into book (isbn, title, author, pages) values (?, ?, ?, ?)"; + + List> values = new LinkedList>(); + + List l1 = new LinkedList(); + l1.add("1234"); + l1.add("Moby Dick"); + l1.add("Herman Manville"); + l1.add(new Integer(456)); + + values.add(l1); + + List l2 = new LinkedList(); + l2.add("2345"); + l2.add("War and Peace"); + l2.add("Russian Dude"); + l2.add(new Integer(456)); + + values.add(l2); + + // values.add(new Object[] { "3456", "Jane Ayre", "Charlotte", new Integer(456) }); + + cassandraTemplate.ingest(cql, values); + + // Assert that the rows were inserted into Cassandra + Book b1 = getBook("1234"); + Book b2 = getBook("2345"); + + Assert.assertEquals(b1.getIsbn(), l1.get(0)); + Assert.assertEquals(b2.getIsbn(), l2.get(0)); + } + + @Test + public void ingestionTestObjectArray() { + + String cql = "insert into book (isbn, title, author, pages) values (?, ?, ?, ?)"; + + Object[][] values = new Object[3][]; + values[0] = o1; + values[1] = o2; + values[2] = o3; + + cassandraTemplate.ingest(cql, values); + + // Assert that the rows were inserted into Cassandra + Book b1 = getBook("1234"); + Book b2 = getBook("2345"); + Book b3 = getBook("3456"); + + Assert.assertEquals(b1.getIsbn(), values[0][0]); + Assert.assertEquals(b2.getTitle(), values[1][1]); + Assert.assertEquals(b3.getAuthor(), values[2][2]); + } + + /** + * This is an implementation of RowIterator for the purposes of testing passing your own Impl to CassandraTemplate + * + * @author David Webb + */ + final class MyRowIterator implements RowIterator { + + private Object[][] values; + + public MyRowIterator(Object[][] values) { + this.values = values; + } + + int index = 0; + + /* (non-Javadoc) + * @see org.springframework.cassandra.core.RowIterator#next() + */ + @Override + public Object[] next() { + return values[index++]; + } + + /* (non-Javadoc) + * @see org.springframework.cassandra.core.RowIterator#hasNext() + */ + @Override + public boolean hasNext() { + return index < values.length; + } + + } + + @Test + public void ingestionTestRowIterator() { + + String cql = "insert into book (isbn, title, author, pages) values (?, ?, ?, ?)"; + + final Object[][] v = new Object[3][]; + v[0] = o1; + v[1] = o2; + v[2] = o3; + RowIterator ri = new MyRowIterator(v); + + cassandraTemplate.ingest(cql, ri); + + // Assert that the rows were inserted into Cassandra + Book b1 = getBook("1234"); + Book b2 = getBook("2345"); + Book b3 = getBook("3456"); + + Assert.assertEquals(b1.getIsbn(), o1[0]); + Assert.assertEquals(b2.getTitle(), o2[1]); + Assert.assertEquals(b3.getAuthor(), o3[2]); + } + + public Book getBook(final String isbn) { + + Book b = this.cassandraTemplate.query("select * from book where isbn = ?", new PreparedStatementBinder() { + + @Override + public BoundStatement bindValues(PreparedStatement ps) throws DriverException { + return ps.bind(isbn); + } + }, new ResultSetExtractor() { + + @Override + public Book extractData(ResultSet rs) throws DriverException, DataAccessException { + Book b = new Book(); + Row r = rs.one(); + b.setIsbn(r.getString("isbn")); + b.setTitle(r.getString("title")); + b.setAuthor(r.getString("author")); + b.setPages(r.getInt("pages")); + return b; + } + }); + + return b; + + } + + /** + * For testing a HostMapper Implementation + */ + public class MyHost { + public String someName; + } +} diff --git a/src/test/java/org/springframework/data/cassandra/test/integration/config/TestConfig.java b/src/test/java/org/springframework/data/cassandra/test/integration/config/TestConfig.java index e69e13cd2..415f5393e 100644 --- a/src/test/java/org/springframework/data/cassandra/test/integration/config/TestConfig.java +++ b/src/test/java/org/springframework/data/cassandra/test/integration/config/TestConfig.java @@ -22,12 +22,14 @@ import com.datastax.driver.core.Cluster.Builder; @Configuration public class TestConfig extends AbstractCassandraConfiguration { + public static final String keyspace = "test"; + /* (non-Javadoc) * @see org.springframework.data.cassandra.config.AbstractCassandraConfiguration#getKeyspaceName() */ @Override protected String getKeyspaceName() { - return "test"; + return keyspace; } /* (non-Javadoc) diff --git a/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java b/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java deleted file mode 100644 index e850028a1..000000000 --- a/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraOperationsTest.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2011-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.cassandra.test.integration.template; - -import static org.junit.Assert.assertNotNull; - -import java.io.IOException; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -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.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.cassandra.core.BoundStatementFactory; -import org.springframework.cassandra.core.CassandraOperations; -import org.springframework.cassandra.core.CqlParameter; -import org.springframework.cassandra.core.CqlParameterValue; -import org.springframework.cassandra.core.HostMapper; -import org.springframework.cassandra.core.PreparedStatementCreatorFactory; -import org.springframework.cassandra.core.ResultSetExtractor; -import org.springframework.cassandra.core.RingMember; -import org.springframework.dao.DataAccessException; -import org.springframework.data.cassandra.test.integration.config.TestConfig; -import org.springframework.data.cassandra.test.integration.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.DataType; -import com.datastax.driver.core.Host; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.Row; -import com.datastax.driver.core.exceptions.DriverException; - -/** - * Unit Tests for CassandraTemplate - * - * @author David Webb - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigContextLoader.class) -public class CassandraOperationsTest { - - /** - * @author David Webb - * - */ - public class MyHost { - - public String someName; - - } - - @Autowired - private CassandraOperations cassandraTemplate; - - private static Logger log = LoggerFactory.getLogger(CassandraOperationsTest.class); - - private final static String CASSANDRA_CONFIG = "cassandra.yaml"; - private final static String KEYSPACE_NAME = "test"; - private final static String CASSANDRA_HOST = "localhost"; - private final static int CASSANDRA_NATIVE_PORT = 9042; - private final static int CASSANDRA_THRIFT_PORT = 9160; - - @Rule - public CassandraCQLUnit cassandraCQLUnit = new CassandraCQLUnit(new ClassPathCQLDataSet( - "cassandraOperationsTest-cql-dataload.cql", KEYSPACE_NAME), CASSANDRA_CONFIG, CASSANDRA_HOST, - CASSANDRA_NATIVE_PORT); - - @BeforeClass - public static void startCassandra() throws IOException, TTransportException, ConfigurationException, - InterruptedException { - - EmbeddedCassandraServerHelper.startEmbeddedCassandra(CASSANDRA_CONFIG); - - /* - * Load data file to creat the test keyspace before we init the template - */ - DataLoader dataLoader = new DataLoader("Test Cluster", CASSANDRA_HOST + ":" + CASSANDRA_THRIFT_PORT); - dataLoader.load(new ClassPathYamlDataSet("cassandra-keyspace.yaml")); - } - - @Test - public void ringTest() { - - List ring = cassandraTemplate.describeRing(); - - /* - * There must be 1 node in the cluster if the embedded server is - * running. - */ - assertNotNull(ring); - - for (RingMember h : ring) { - log.info("ringTest Host -> " + h.address); - } - } - - @Test - public void hostMapperTest() { - - List ring = (List) cassandraTemplate.describeRing(new HostMapper() { - - @Override - public Collection mapHosts(Set host) throws DriverException { - - List list = new LinkedList(); - - for (Host h : host) { - MyHost mh = new MyHost(); - mh.someName = h.getAddress().getCanonicalHostName(); - list.add(mh); - } - - return list; - } - - }); - - assertNotNull(ring); - Assert.assertTrue(ring.size() > 0); - - for (MyHost h : ring) { - log.info("hostMapperTest Host -> " + h.someName); - } - - } - - @Test - public void preparedStatementFactoryTest() { - - String cql = "select * from book where isbn = ?"; - - List parameters = new LinkedList(); - parameters.add(new CqlParameter("isbn", DataType.text())); - - PreparedStatementCreatorFactory factory = new PreparedStatementCreatorFactory(cql, parameters); - - List values = new LinkedList(); - values.add(new CqlParameterValue(DataType.text(), "999999999")); - - Book b = cassandraTemplate.query(factory.newPreparedStatementCreator(values), - factory.newPreparedStatementBinder(values), new ResultSetExtractor() { - - @Override - public Book extractData(ResultSet rs) throws DriverException, DataAccessException { - Row r = rs.one(); - Book b = new Book(); - b.setIsbn(r.getString("isbn")); - b.setTitle(r.getString("title")); - b.setAuthor(r.getString("author")); - b.setPages(r.getInt("pages")); - return b; - } - }); - - log.info(b.toString()); - - } - - @Test - public void boundStatementFactoryTest() { - - String cql = "insert into book (isbn, title, author, pages) values (?, ?, ?, ?)"; - - BoundStatementFactory bsf = new BoundStatementFactory(cql); - bsf.addValues(new Object[] { "1234", "Moby Dick", "Herman Manville", new Integer(456) }, new Object[] { "2345", - "War and Peace", "Russian Dude", new Integer(456) }, new Object[] { "3456", "Jane Ayre", "Charlotte", - new Integer(456) }); - - cassandraTemplate.execute(bsf); - } - - @After - public void clearCassandra() { - EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); - - } - - @AfterClass - public static void stopCassandra() { - // EmbeddedCassandraServerHelper.stopEmbeddedCassandra(); - } -} From ec1487d811425b30a316e5a41a769fff412f8de0 Mon Sep 17 00:00:00 2001 From: David Webb Date: Tue, 26 Nov 2013 09:54:05 -0500 Subject: [PATCH 11/11] Changed project version number. Fixed javadoc build target. --- build.gradle | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 3f9fd0a9f..b2419f178 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ targetCompatibility = 1.6 javadoc { ext.srcDir = file("${projectDir}/src/main/doc") - ext.destinationDir = file("${buildDir}/api") + ext.destinationDir = file("${buildDir}/docs/javadoc") ext.tmpDir = file("${buildDir}/api-work") configure(options) { diff --git a/gradle.properties b/gradle.properties index 883b974a6..30c49caf8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,4 +23,4 @@ nettyVersion = 3.6.2.Final # -------------------- # Project wide version # -------------------- -version=2.0.0.BUILD-SNAPSHOT \ No newline at end of file +version=1.2.0.BUILD-SNAPSHOT \ No newline at end of file