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 index 324fbbfbb..7a4df513e 100644 --- a/spring-cql/src/main/java/org/springframework/cassandra/core/CachedPreparedStatementCreator.java +++ b/spring-cql/src/main/java/org/springframework/cassandra/core/CachedPreparedStatementCreator.java @@ -39,15 +39,16 @@ import com.datastax.driver.core.exceptions.DriverException; */ public class CachedPreparedStatementCreator implements PreparedStatementCreator { - private static final Logger log = LoggerFactory.getLogger(CachedPreparedStatementCreator.class); private static final Map> CACHE = new ConcurrentHashMap>(); + protected final Logger log = LoggerFactory.getLogger(getClass()); + private final String cql; /** * Create a {@link PreparedStatementCreator} from the provided CQL. * - * @param cql must not be empty and not {@literal null}. + * @param cql must not be empty or {@literal null}. */ public CachedPreparedStatementCreator(String cql) { @@ -56,6 +57,11 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator this.cql = cql; } + /** + * Returns the CQL statement on which the {@link PreparedStatement} will be based. + * + * @return a String containing the CQL of the {@link PreparedStatement}. + */ public String getCql() { return this.cql; } @@ -66,14 +72,16 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator @Override public PreparedStatement createPreparedStatement(Session session) throws DriverException { - StringBuilder cacheKey = new StringBuilder().append(session.getLoggedKeyspace()).append("|").append(this.cql); + String cacheKey = String.valueOf(session.getLoggedKeyspace()).concat("|").concat(this.cql); - log.debug("Cachable PreparedStatement in Keyspace {}", session.getLoggedKeyspace()); + log.debug("Cacheable PreparedStatement in Keyspace {}", session.getLoggedKeyspace()); Map sessionCache = getOrCreateSessionLocalCache(session); - return getOrPrepareStatement(session, cacheKey.toString(), sessionCache); + + return getOrPrepareStatement(session, cacheKey, sessionCache); } + @SuppressWarnings("all") private Map getOrCreateSessionLocalCache(Session session) { Map sessionMap = CACHE.get(session); @@ -85,7 +93,6 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator if (CACHE.containsKey(session)) { sessionMap = CACHE.get(session); } else { - sessionMap = new ConcurrentHashMap(); CACHE.put(session, sessionMap); } @@ -95,32 +102,28 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator return sessionMap; } + @SuppressWarnings("all") private PreparedStatement getOrPrepareStatement(Session session, String cacheKey, Map sessionCache) { - PreparedStatement pstmt = sessionCache.get(cacheKey); + PreparedStatement preparedStatement = sessionCache.get(cacheKey); - if (pstmt == null) { + if (preparedStatement == null) { synchronized (sessionCache) { if (sessionCache.containsKey(cacheKey)) { - log.debug("Found cached PreparedStatement"); - return sessionCache.get(cacheKey); + preparedStatement = sessionCache.get(cacheKey); + } + else { + log.debug("No cached PreparedStatement found... creating and caching"); + preparedStatement = session.prepare(this.cql); + sessionCache.put(cacheKey, preparedStatement); } - - log.debug("No Cached PreparedStatement found...Creating and Caching"); - - pstmt = session.prepare(this.cql); - sessionCache.put(cacheKey, pstmt); - - return pstmt; } } - log.debug("Found cached PreparedStatement"); - - return pstmt; + return preparedStatement; } } diff --git a/spring-cql/src/test/java/org/springframework/cassandra/core/CachedPreparedStatementCreatorUnitTests.java b/spring-cql/src/test/java/org/springframework/cassandra/core/CachedPreparedStatementCreatorUnitTests.java index 64fc9a4c6..46ca6b4a6 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/core/CachedPreparedStatementCreatorUnitTests.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/core/CachedPreparedStatementCreatorUnitTests.java @@ -41,6 +41,7 @@ import edu.umd.cs.mtc.MultithreadedTestCase; * Unit tests for {@link CachedPreparedStatementCreator}. * * @author Mark Paluch + * @see DATACASS-253 */ @RunWith(MockitoJUnitRunner.class) public class CachedPreparedStatementCreatorUnitTests { @@ -95,6 +96,7 @@ public class CachedPreparedStatementCreatorUnitTests { cachedPreparedStatementCreator.createPreparedStatement(sessionMock); cachedPreparedStatementCreator.createPreparedStatement(sessionMock); + PreparedStatement result = cachedPreparedStatementCreator.createPreparedStatement(sessionMock); assertThat(result, is(sameInstance(preparedStatement))); @@ -117,9 +119,9 @@ public class CachedPreparedStatementCreatorUnitTests { @SuppressWarnings("unused") private static class CreatePreparedStatementIsThreadSafe extends MultithreadedTestCase { + final AtomicInteger atomicInteger = new AtomicInteger(); final CachedPreparedStatementCreator preparedStatementCreator; final Session session; - final AtomicInteger atomicInteger = new AtomicInteger(); public CreatePreparedStatementIsThreadSafe(final PreparedStatement preparedStatement, CachedPreparedStatementCreator preparedStatementCreator) { @@ -132,7 +134,6 @@ public class CachedPreparedStatementCreatorUnitTests { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("prepare") && args.length == 1) { - waitForTick(2); atomicInteger.incrementAndGet(); return preparedStatement; @@ -150,7 +151,6 @@ public class CachedPreparedStatementCreatorUnitTests { preparedStatementCreator.createPreparedStatement(session); assertThat(atomicInteger.get(), is(1)); - } public void thread2() { @@ -180,6 +180,7 @@ public class CachedPreparedStatementCreatorUnitTests { } } + @SuppressWarnings("unchecked") private static T newProxy(Class theClass, InvocationHandler invocationHandler) { return (T) Proxy.newProxyInstance(CachedPreparedStatementCreatorUnitTests.class.getClassLoader(), new Class[] { theClass }, invocationHandler);