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); }