diff --git a/spring-cql/pom.xml b/spring-cql/pom.xml
index b6f9de6c0..343dff8a0 100644
--- a/spring-cql/pom.xml
+++ b/spring-cql/pom.xml
@@ -114,6 +114,11 @@
guava
15.0
+
+ org.liquibase
+ liquibase-core
+ 3.1.1
+
diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/CachedPreparedStatementCreator.java b/spring-cql/src/main/java/org/springframework/cassandra/core/CachedPreparedStatementCreator.java
new file mode 100644
index 000000000..dbdd7c752
--- /dev/null
+++ b/spring-cql/src/main/java/org/springframework/cassandra/core/CachedPreparedStatementCreator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.cassandra.core;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+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;
+
+/**
+ * This Prepared Statement Creator maintains a cache of all prepared statements for the duration of this life of the
+ * container.
+ *
+ * When preparing statements with Cassandra, each Statement should be prepared once and only once due to the overhead of
+ * preparing the statement.
+ *
+ * @author David Webb
+ *
+ */
+public class CachedPreparedStatementCreator implements PreparedStatementCreator {
+
+ private static final Logger log = LoggerFactory.getLogger(CachedPreparedStatementCreator.class);
+
+ private final String cql;
+
+ private static final Map> psMap = new ConcurrentHashMap>();
+
+ /**
+ * Create a PreparedStatementCreator from the provided CQL.
+ *
+ * @param cql
+ */
+ public CachedPreparedStatementCreator(String cql) {
+ Assert.notNull(cql, "CQL is required to create a PreparedStatement");
+ this.cql = cql;
+ }
+
+ public String getCql() {
+ return this.cql;
+ }
+
+ @Override
+ public PreparedStatement createPreparedStatement(Session session) throws DriverException {
+
+ StringBuilder keyspaceCQLKey = new StringBuilder().append(session.getLoggedKeyspace()).append("|").append(this.cql);
+
+ log.debug(String.format("Cachable PreparedStatement in Keyspace [%s]", session.getLoggedKeyspace()));
+
+ Map sessionMap = psMap.get(session);
+ if (sessionMap == null) {
+ sessionMap = new ConcurrentHashMap();
+ psMap.put(session, sessionMap);
+ }
+
+ PreparedStatement pstmt = sessionMap.get(keyspaceCQLKey.toString());
+ if (pstmt == null) {
+ log.debug("No Cached PreparedStatement found...Creating and Caching");
+ pstmt = session.prepare(this.cql);
+ sessionMap.put(keyspaceCQLKey.toString(), pstmt);
+ } else {
+ log.debug("Found cached PreparedStatement");
+ }
+
+ return pstmt;
+ }
+
+}
diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/CqlOperations.java b/spring-cql/src/main/java/org/springframework/cassandra/core/CqlOperations.java
index 6b71f77d0..0c0a0781d 100644
--- a/spring-cql/src/main/java/org/springframework/cassandra/core/CqlOperations.java
+++ b/spring-cql/src/main/java/org/springframework/cassandra/core/CqlOperations.java
@@ -737,7 +737,7 @@ public interface CqlOperations {
List> processListOfMap(ResultSet resultSet) throws DataAccessException;
/**
- * Converts the CQL provided into a {@link SimplePreparedStatementCreator}. This can only be used for CQL
+ * Converts the CQL provided into a {@link CachedPreparedStatementCreator}. This can only be used for CQL
* Statements that do not have data binding. The results of the PreparedStatement are processed with
* PreparedStatementCallback implementation provided by the Application Code.
*
@@ -762,7 +762,7 @@ public interface CqlOperations {
T execute(PreparedStatementCreator psc, PreparedStatementCallback action) throws DataAccessException;
/**
- * Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
+ * Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
* processed with the ResultSetExtractor implementation provided by the Application Code. The can return any object,
* including a List of Objects to support the ResultSet processing.
@@ -776,7 +776,7 @@ public interface CqlOperations {
T query(String cql, PreparedStatementBinder psb, ResultSetExtractor rse) throws DataAccessException;
/**
- * Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
+ * Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
* processed with the ResultSetExtractor implementation provided by the Application Code. The can return any object,
* including a List of Objects to support the ResultSet processing.
@@ -792,7 +792,7 @@ public interface CqlOperations {
throws DataAccessException;
/**
- * Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
+ * Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
* processed with the RowCallbackHandler implementation provided and nothing is returned.
*
@@ -804,7 +804,7 @@ public interface CqlOperations {
void query(String cql, PreparedStatementBinder psb, RowCallbackHandler rch) throws DataAccessException;
/**
- * Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
+ * Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
* processed with the RowCallbackHandler implementation provided and nothing is returned.
*
@@ -818,7 +818,7 @@ public interface CqlOperations {
throws DataAccessException;
/**
- * Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
+ * Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
* processed with the RowMapper implementation provided and a List is returned with elements of Type for each Row
* returned.
@@ -832,7 +832,7 @@ public interface CqlOperations {
List query(String cql, PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException;
/**
- * Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
+ * Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
* processed with the RowMapper implementation provided and a List is returned with elements of Type for each Row
* returned.
diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/CqlTemplate.java b/spring-cql/src/main/java/org/springframework/cassandra/core/CqlTemplate.java
index 8d56be6b0..3d92d931b 100644
--- a/spring-cql/src/main/java/org/springframework/cassandra/core/CqlTemplate.java
+++ b/spring-cql/src/main/java/org/springframework/cassandra/core/CqlTemplate.java
@@ -15,6 +15,8 @@
*/
package org.springframework.cassandra.core;
+import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -72,8 +74,6 @@ import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Truncate;
import com.datastax.driver.core.querybuilder.Update;
-import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
-
/**
* This is the Central class in the Cassandra core package. It simplifies the use of Cassandra and helps to avoid
* common errors. It executes the core Cassandra workflow, leaving application code to provide CQL and result
@@ -843,7 +843,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
@Override
public T execute(String cql, PreparedStatementCallback action) {
- return execute(new SimplePreparedStatementCreator(cql), action);
+ return execute(new CachedPreparedStatementCreator(cql), action);
}
@Override
@@ -882,7 +882,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
@Override
public T query(String cql, PreparedStatementBinder psb, ResultSetExtractor rse, QueryOptions options)
throws DataAccessException {
- return query(new SimplePreparedStatementCreator(cql), psb, rse, options);
+ return query(new CachedPreparedStatementCreator(cql), psb, rse, options);
}
@Override
@@ -893,7 +893,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
@Override
public void query(String cql, PreparedStatementBinder psb, RowCallbackHandler rch, QueryOptions options)
throws DataAccessException {
- query(new SimplePreparedStatementCreator(cql), psb, rch, options);
+ query(new CachedPreparedStatementCreator(cql), psb, rch, options);
}
@Override
@@ -904,7 +904,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
@Override
public List query(String cql, PreparedStatementBinder psb, RowMapper rowMapper, QueryOptions options)
throws DataAccessException {
- return query(new SimplePreparedStatementCreator(cql), psb, rowMapper, options);
+ return query(new CachedPreparedStatementCreator(cql), psb, rowMapper, options);
}
@Override
@@ -915,11 +915,13 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
@Override
public void ingest(String cql, RowIterator rowIterator, WriteOptions options) {
- PreparedStatement preparedStatement = getSession().prepare(cql);
+ CachedPreparedStatementCreator cpsc = new CachedPreparedStatementCreator(cql);
+
+ PreparedStatement preparedStatement = cpsc.createPreparedStatement(getSession());
addPreparedStatementOptions(preparedStatement, options);
while (rowIterator.hasNext()) {
- getSession().execute(preparedStatement.bind(rowIterator.next()));
+ getSession().executeAsync(preparedStatement.bind(rowIterator.next()));
}
}
diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorImpl.java b/spring-cql/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorImpl.java
deleted file mode 100644
index 524020d49..000000000
--- a/spring-cql/src/main/java/org/springframework/cassandra/core/PreparedStatementCreatorImpl.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2013-2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.cassandra.core;
-
-import java.util.List;
-
-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
- *
- */
-public class PreparedStatementCreatorImpl implements PreparedStatementCreator, PreparedStatementBinder {
-
- private final String cql;
- private List values;
-
- public PreparedStatementCreatorImpl(String cql) {
- this.cql = cql;
- }
-
- public PreparedStatementCreatorImpl(String cql, List values) {
- this.cql = cql;
- this.values = values;
- }
-
- /* (non-Javadoc)
- * @see org.springframework.cassandra.core.PreparedStatementSetter#setValues(com.datastax.driver.core.PreparedStatement)
- */
- @Override
- public BoundStatement bindValues(PreparedStatement ps) throws DriverException {
- // Nothing to set if there are no values
- if (values == null) {
- return new BoundStatement(ps);
- }
-
- return ps.bind(values.toArray());
-
- }
-
- 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 {
- return session.prepare(this.cql);
- }
-
-}
diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/SimplePreparedStatementCreator.java b/spring-cql/src/main/java/org/springframework/cassandra/core/SimplePreparedStatementCreator.java
index cbaf64374..4eb35f011 100644
--- a/spring-cql/src/main/java/org/springframework/cassandra/core/SimplePreparedStatementCreator.java
+++ b/spring-cql/src/main/java/org/springframework/cassandra/core/SimplePreparedStatementCreator.java
@@ -22,6 +22,12 @@ import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
/**
+ * This Prepared Statement Creator simply prepares a statement from the CQL string. This should not be used in
+ * Production systems with high volume reads and writes. Use {@link CachedPreparedStatementCreator}
+ *
+ * When preparing statements with Cassandra, each Statement should be prepared once and only once due to the overhead of
+ * preparing the statement.
+ *
* @author David Webb
*
*/
@@ -43,9 +49,6 @@ public class SimplePreparedStatementCreator implements PreparedStatementCreator
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 {
return session.prepare(this.cql);
diff --git a/spring-cql/src/test/resources/logback-test.xml b/spring-cql/src/test/resources/logback-test.xml
index 453120668..24e8a279d 100644
--- a/spring-cql/src/test/resources/logback-test.xml
+++ b/spring-cql/src/test/resources/logback-test.xml
@@ -10,7 +10,7 @@
-
+