package com.netflix.astyanax.cql.reads;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.RegularStatement;
import com.datastax.driver.core.Session;
/**
* Template for {@link PreparedStatement} caching for a query Q.
* The class provides the basic functionality to store a cached reference to the PreparedStatement
* that is generated by the extending class. Hence actual logic for constructing the PrepatedStatement
* and binding values to that statement is not defined here. That must be provided by the extending classes.
*
* @author poberai
*
* @param <Q>
*/
public abstract class QueryGenCache<Q> {
private static final Logger LOG = LoggerFactory.getLogger(QueryGenCache.class);
// reference to the session object. This is required for "preparing" a statement
private AtomicReference<Session> sessionRef = new AtomicReference<Session>(null);
// The cached reference to the query constructed by extending classes
private final AtomicReference<PreparedStatement> cachedStatement = new AtomicReference<PreparedStatement>(null);
/**
* Constructor
* @param sessionR
*/
public QueryGenCache(AtomicReference<Session> sessionR) {
this.sessionRef = sessionR;
}
/**
* Get the bound statement from the prepared statement
* @param query
* @param useCaching
* @return BoundStatement
*/
public BoundStatement getBoundStatement(Q query, boolean useCaching) {
PreparedStatement pStatement = getPreparedStatement(query, useCaching);
return bindValues(pStatement, query);
}
/**
* Get the bound statemnent by either constructing the query or using the cached statement underneath.
* Note that the caller can provide useCaching as a knob to turn caching ON/OFF.
* If false, then the query is just constructed using the extending class and returned.
* If true, then the cached reference is consulted. If the cache is empty, then the query is constructed
* and used to seed the cache.
*
* @param query
* @param useCaching
* @return PreparedStatement
*/
public PreparedStatement getPreparedStatement(Q query, boolean useCaching) {
PreparedStatement pStatement = null;
if (useCaching) {
pStatement = cachedStatement.get();
}
if (pStatement == null) {
try {
RegularStatement stmt = getQueryGen(query).call();
if (LOG.isDebugEnabled()) {
LOG.debug("Query: " + stmt.getQueryString());
}
pStatement = sessionRef.get().prepare(stmt.getQueryString());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
if (useCaching && cachedStatement.get() == null) {
cachedStatement.set(pStatement);
}
return pStatement;
}
/**
* Extending classes must implement this with logic for constructing the java driver query from the given Astyanax query
* @param query
* @return Callable<RegularStatement>
*/
public abstract Callable<RegularStatement> getQueryGen(Q query);
/**
* Extending classes must implement this with logic for binding the right Astyanax query data with the pre-constructed
* prepared statement in the right order.
* @param pStatement
* @param query
* @return BoundStatement
*/
public abstract BoundStatement bindValues(PreparedStatement pStatement, Q query);
}