All Downloads are FREE. Search and download functionalities are using the official Maven repository.

info.archinnov.achilles.context.CQLDaoContext Maven / Gradle / Ivy

/**
 *
 * Copyright (C) 2012-2013 DuyHai DOAN
 *
 * 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 info.archinnov.achilles.context;

import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
import static info.archinnov.achilles.counter.AchillesCounter.CQLQueryType.*;
import info.archinnov.achilles.counter.AchillesCounter.CQLQueryType;
import info.archinnov.achilles.entity.metadata.EntityMeta;
import info.archinnov.achilles.entity.metadata.PropertyMeta;
import info.archinnov.achilles.exception.AchillesException;
import info.archinnov.achilles.statement.CQLStatementGenerator;
import info.archinnov.achilles.statement.cache.CacheManager;
import info.archinnov.achilles.statement.cache.StatementCacheKey;
import info.archinnov.achilles.statement.prepared.BoundStatementWrapper;
import info.archinnov.achilles.statement.prepared.CQLPreparedStatementBinder;
import info.archinnov.achilles.type.ConsistencyLevel;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

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.Query;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.Update;
import com.datastax.driver.core.querybuilder.Update.Assignments;
import com.google.common.base.Optional;
import com.google.common.cache.Cache;

public class CQLDaoContext {
	public static final String ACHILLES_DML_STATEMENT = "ACHILLES_DML_STATEMENT";

	private static final Logger dmlLogger = LoggerFactory.getLogger(ACHILLES_DML_STATEMENT);

	private Map, PreparedStatement> insertPSs;
	private Cache dynamicPSCache;
	private Map, PreparedStatement> selectEagerPSs;
	private Map, Map> removePSs;
	private Map counterQueryMap;
	private Map, Map> clusteredCounterQueryMap;
	private Session session;

	private CQLPreparedStatementBinder binder = new CQLPreparedStatementBinder();
	private CacheManager cacheManager = new CacheManager();
	private CQLStatementGenerator statementGenerator = new CQLStatementGenerator();

	public CQLDaoContext(Map, PreparedStatement> insertPSs,
			Cache dynamicPSCache,
			Map, PreparedStatement> selectEagerPSs, Map, Map> removePSs,
			Map counterQueryMap,
			Map, Map> clusteredCounterQueryMap, Session session) {
		this.insertPSs = insertPSs;
		this.dynamicPSCache = dynamicPSCache;
		this.selectEagerPSs = selectEagerPSs;
		this.removePSs = removePSs;
		this.counterQueryMap = counterQueryMap;
		this.clusteredCounterQueryMap = clusteredCounterQueryMap;
		this.session = session;
	}

	public void pushInsertStatement(CQLPersistenceContext context) {
		EntityMeta entityMeta = context.getEntityMeta();
		Class entityClass = context.getEntityClass();
		Optional ttlO = context.getTtt();
		Optional timestampO = context.getTimestamp();
		ConsistencyLevel writeLevel = getWriteConsistencyLevel(context, entityMeta);
		if (ttlO.isPresent() || timestampO.isPresent()) {
			Insert insert = statementGenerator.generateInsert(context.getEntity(), entityMeta);
			Insert.Options options = null;

			if (ttlO.isPresent() && timestampO.isPresent())
				options = insert.using(ttl(ttlO.get())).and(timestamp(timestampO.get()));
			else if (ttlO.isPresent())
				options = insert.using(ttl(ttlO.get()));
			else if (timestampO.isPresent())
				options = insert.using(timestamp(timestampO.get()));

			context.pushStatement(options, writeLevel);
		} else {
			PreparedStatement ps = insertPSs.get(entityClass);
			BoundStatementWrapper bsWrapper = binder.bindForInsert(ps, entityMeta, context.getEntity());
			context.pushBoundStatement(bsWrapper, writeLevel);
		}
	}

	public void pushUpdateStatement(CQLPersistenceContext context, List pms) {
		EntityMeta entityMeta = context.getEntityMeta();
		Optional ttlO = context.getTtt();
		Optional timestampO = context.getTimestamp();
		ConsistencyLevel writeLevel = getWriteConsistencyLevel(context, entityMeta);
		if (ttlO.isPresent() || timestampO.isPresent()) {
			Assignments update = statementGenerator.generateUpdateFields(context.getEntity(), entityMeta, pms);
			Update.Options options = null;

			if (ttlO.isPresent() && timestampO.isPresent())
				options = update.using(ttl(ttlO.get())).and(timestamp(timestampO.get()));
			else if (ttlO.isPresent())
				options = update.using(ttl(ttlO.get()));
			else if (timestampO.isPresent())
				options = update.using(timestamp(timestampO.get()));

			context.pushStatement(options, writeLevel);
		} else {
			PreparedStatement ps = cacheManager.getCacheForFieldsUpdate(session, dynamicPSCache, context, pms);
			BoundStatementWrapper bsWrapper = binder.bindForUpdate(ps, entityMeta, pms, context.getEntity());
			context.pushBoundStatement(bsWrapper, writeLevel);
		}
	}

	public Row loadProperty(CQLPersistenceContext context, PropertyMeta pm) {
		PreparedStatement ps = cacheManager.getCacheForFieldSelect(session, dynamicPSCache, context, pm);
		ConsistencyLevel readLevel = getReadConsistencyLevel(context, pm);
		List rows = executeReadWithConsistency(context, ps, readLevel);
		return returnFirstRowOrNull(rows);
	}

	public void bindForRemoval(CQLPersistenceContext context, String tableName) {
		EntityMeta entityMeta = context.getEntityMeta();
		Class entityClass = context.getEntityClass();
		Map psMap = removePSs.get(entityClass);

		if (psMap.containsKey(tableName)) {
			BoundStatementWrapper bsWrapper = binder.bindStatementWithOnlyPKInWhereClause(psMap.get(tableName),
					entityMeta, context.getPrimaryKey());
			ConsistencyLevel writeLevel = getWriteConsistencyLevel(context, entityMeta);
			context.pushBoundStatement(bsWrapper, writeLevel);
		} else {
			throw new AchillesException("Cannot find prepared statement for deletion for table '" + tableName + "'");
		}
	}

	// Simple counter
	public void bindForSimpleCounterIncrement(CQLPersistenceContext context, EntityMeta meta, PropertyMeta counterMeta,
			Long increment) {
		PreparedStatement ps = counterQueryMap.get(INCR);
		BoundStatementWrapper bsWrapper = binder.bindForSimpleCounterIncrementDecrement(ps, meta, counterMeta,
				context.getPrimaryKey(), increment);

		ConsistencyLevel writeLevel = getWriteConsistencyLevel(context, counterMeta);
		context.pushBoundStatement(bsWrapper, writeLevel);
	}

	public void incrementSimpleCounter(CQLPersistenceContext context, EntityMeta meta, PropertyMeta counterMeta,
			Long increment, ConsistencyLevel consistencyLevel) {
		PreparedStatement ps = counterQueryMap.get(INCR);
		BoundStatementWrapper bsWrapper = binder.bindForSimpleCounterIncrementDecrement(ps, meta, counterMeta,
				context.getPrimaryKey(), increment);
		context.executeImmediateWithConsistency(bsWrapper, consistencyLevel);
	}

	public void decrementSimpleCounter(CQLPersistenceContext context, EntityMeta meta, PropertyMeta counterMeta,
			Long decrement, ConsistencyLevel consistencyLevel) {
		PreparedStatement ps = counterQueryMap.get(DECR);
		BoundStatementWrapper bsWrapper = binder.bindForSimpleCounterIncrementDecrement(ps, meta, counterMeta,
				context.getPrimaryKey(), decrement);
		context.executeImmediateWithConsistency(bsWrapper, consistencyLevel);
	}

	public Row getSimpleCounter(CQLPersistenceContext context, PropertyMeta counterMeta,
			ConsistencyLevel consistencyLevel) {
		PreparedStatement ps = counterQueryMap.get(SELECT);
		BoundStatementWrapper bsWrapper = binder.bindForSimpleCounterSelect(ps, context.getEntityMeta(), counterMeta,
				context.getPrimaryKey());
		ResultSet resultSet = context.executeImmediateWithConsistency(bsWrapper, consistencyLevel);

		return returnFirstRowOrNull(resultSet.all());
	}

	public void bindForSimpleCounterDelete(CQLPersistenceContext context, EntityMeta meta, PropertyMeta counterMeta,
			Object primaryKey) {
		PreparedStatement ps = counterQueryMap.get(DELETE);
		BoundStatementWrapper bsWrapper = binder.bindForSimpleCounterDelete(ps, meta, counterMeta, primaryKey);

		ConsistencyLevel writeLevel = getWriteConsistencyLevel(context, counterMeta);
		context.pushBoundStatement(bsWrapper, writeLevel);
	}

	// Clustered counter
	public void pushClusteredCounterIncrementStatement(CQLPersistenceContext context, EntityMeta meta,
			PropertyMeta counterMeta, Long increment) {
		ConsistencyLevel writeLevel = getWriteConsistencyLevel(context, counterMeta);
		PreparedStatement ps = clusteredCounterQueryMap.get(meta.getEntityClass()).get(INCR);
		BoundStatementWrapper bsWrapper = binder.bindForClusteredCounterIncrementDecrement(ps, meta, counterMeta,
				context.getPrimaryKey(), increment);
		context.pushBoundStatement(bsWrapper, writeLevel);
	}

	public void incrementClusteredCounter(CQLPersistenceContext context, EntityMeta meta, PropertyMeta counterMeta,
			Long increment, ConsistencyLevel consistencyLevel) {
		PreparedStatement ps = clusteredCounterQueryMap.get(meta.getEntityClass()).get(INCR);
		BoundStatementWrapper bsWrapper = binder.bindForClusteredCounterIncrementDecrement(ps, meta, counterMeta,
				context.getPrimaryKey(), increment);
		context.executeImmediateWithConsistency(bsWrapper, consistencyLevel);
	}

	public void decrementClusteredCounter(CQLPersistenceContext context, EntityMeta meta, PropertyMeta counterMeta,
			Long decrement, ConsistencyLevel consistencyLevel) {
		PreparedStatement ps = clusteredCounterQueryMap.get(meta.getEntityClass()).get(DECR);
		BoundStatementWrapper bsWrapper = binder.bindForClusteredCounterIncrementDecrement(ps, meta, counterMeta,
				context.getPrimaryKey(), decrement);
		context.executeImmediateWithConsistency(bsWrapper, consistencyLevel);
	}

	public Row getClusteredCounter(CQLPersistenceContext context, PropertyMeta counterMeta,
			ConsistencyLevel consistencyLevel) {
		EntityMeta entityMeta = context.getEntityMeta();
		PreparedStatement ps = clusteredCounterQueryMap.get(entityMeta.getEntityClass()).get(SELECT);
		BoundStatementWrapper bsWrapper = binder.bindForClusteredCounterSelect(ps, entityMeta, counterMeta,
				context.getPrimaryKey());
		ResultSet resultSet = context.executeImmediateWithConsistency(bsWrapper, consistencyLevel);

		return returnFirstRowOrNull(resultSet.all());
	}

	public void bindForClusteredCounterDelete(CQLPersistenceContext context, EntityMeta meta, PropertyMeta counterMeta,
			Object primaryKey) {
		PreparedStatement ps = clusteredCounterQueryMap.get(meta.getEntityClass()).get(DELETE);
		BoundStatementWrapper bsWrapper = binder.bindForClusteredCounterDelete(ps, meta, counterMeta, primaryKey);
		ConsistencyLevel writeLevel = getWriteConsistencyLevel(context, counterMeta);
		context.pushBoundStatement(bsWrapper, writeLevel);
	}

	public Row eagerLoadEntity(CQLPersistenceContext context) {
		EntityMeta meta = context.getEntityMeta();
		Class entityClass = context.getEntityClass();
		PreparedStatement ps = selectEagerPSs.get(entityClass);

		ConsistencyLevel readLevel = getReadConsistencyLevel(context, meta);
		List rows = executeReadWithConsistency(context, ps, readLevel);
		return returnFirstRowOrNull(rows);
	}

	private List executeReadWithConsistency(CQLPersistenceContext context, PreparedStatement ps,
			ConsistencyLevel readLevel) {
		EntityMeta entityMeta = context.getEntityMeta();
		BoundStatementWrapper bsWrapper = binder.bindStatementWithOnlyPKInWhereClause(ps, entityMeta,
				context.getPrimaryKey());

		return context.executeImmediateWithConsistency(bsWrapper, readLevel).all();
	}

	private Row returnFirstRowOrNull(List rows) {
		if (rows.isEmpty()) {
			return null;
		} else {
			return rows.get(0);
		}
	}

	public ResultSet execute(Query query, Object... boundValues) {
		logDMLStatement(query, boundValues);
		return session.execute(query);
	}

	public PreparedStatement prepare(Statement statement) {
		return session.prepare(statement.getQueryString());
	}

	public ResultSet bindAndExecute(PreparedStatement ps, Object... params) {
		BoundStatement bs = ps.bind(params);

		logDMLStatement(bs);
		return session.execute(bs);

	}

	public Session getSession() {
		return session;
	}

	private void logDMLStatement(Query query, Object... boundValues) {
		if (dmlLogger.isDebugEnabled()) {
			String queryType;
			String queryString;
			String consistencyLevel;
			if (BoundStatement.class.isInstance(query)) {
				PreparedStatement ps = BoundStatement.class.cast(query).preparedStatement();
				queryType = "Prepared statement";
				queryString = ps.getQueryString();
				consistencyLevel = query.getConsistencyLevel() == null ? "DEFAULT" : query.getConsistencyLevel().name();

			} else if (Statement.class.isInstance(query)) {
				Statement statement = Statement.class.cast(query);
				queryType = "Simple query";
				queryString = statement.getQueryString();
				consistencyLevel = statement.getConsistencyLevel() == null ? "DEFAULT" : statement
						.getConsistencyLevel().name();
			} else {
				queryType = "Unknown query";
				queryString = "???";
				consistencyLevel = "UNKNWON";
			}

			dmlLogger.debug("{} : [{}] with CONSISTENCY LEVEL [{}]", queryType, queryString, consistencyLevel);
			if (boundValues != null && boundValues.length > 0) {
				dmlLogger.debug("\t bound values: {}", Arrays.asList(boundValues));
			}

		}
	}

	private ConsistencyLevel getReadConsistencyLevel(CQLPersistenceContext context, EntityMeta entityMeta) {
		ConsistencyLevel readLevel = context.getConsistencyLevel().isPresent() ? context.getConsistencyLevel().get()
				: entityMeta.getReadConsistencyLevel();
		return readLevel;
	}

	private ConsistencyLevel getWriteConsistencyLevel(CQLPersistenceContext context, EntityMeta entityMeta) {
		ConsistencyLevel writeLevel = context.getConsistencyLevel().isPresent() ? context.getConsistencyLevel().get()
				: entityMeta.getWriteConsistencyLevel();
		return writeLevel;
	}

	private ConsistencyLevel getReadConsistencyLevel(CQLPersistenceContext context, PropertyMeta pm) {
		ConsistencyLevel consistency = context.getConsistencyLevel().isPresent() ? context.getConsistencyLevel().get()
				: pm.getReadConsistencyLevel();
		return consistency;
	}

	private ConsistencyLevel getWriteConsistencyLevel(CQLPersistenceContext context, PropertyMeta counterMeta) {
		ConsistencyLevel consistency = context.getConsistencyLevel().isPresent() ? context.getConsistencyLevel().get()
				: counterMeta.getWriteConsistencyLevel();
		return consistency;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy