com.impetus.client.cassandra.CassandraClientBase.java Source code

Java tutorial

Introduction

Here is the source code for com.impetus.client.cassandra.CassandraClientBase.java

Source

/**
 * Copyright 2012 Impetus Infotech.
 *
 * 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 com.impetus.client.cassandra;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javassist.Modifier;

import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EmbeddableType;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.SingularAttribute;

import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.TypeParser;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.serializers.CollectionSerializer;
import org.apache.cassandra.thrift.Cassandra;
import org.apache.cassandra.thrift.CfDef;
import org.apache.cassandra.thrift.Column;
import org.apache.cassandra.thrift.ColumnDef;
import org.apache.cassandra.thrift.ColumnOrSuperColumn;
import org.apache.cassandra.thrift.ColumnPath;
import org.apache.cassandra.thrift.Compression;
import org.apache.cassandra.thrift.ConsistencyLevel;
import org.apache.cassandra.thrift.CounterColumn;
import org.apache.cassandra.thrift.CounterSuperColumn;
import org.apache.cassandra.thrift.CqlMetadata;
import org.apache.cassandra.thrift.CqlResult;
import org.apache.cassandra.thrift.CqlRow;
import org.apache.cassandra.thrift.IndexClause;
import org.apache.cassandra.thrift.IndexExpression;
import org.apache.cassandra.thrift.IndexType;
import org.apache.cassandra.thrift.InvalidRequestException;
import org.apache.cassandra.thrift.KeySlice;
import org.apache.cassandra.thrift.KsDef;
import org.apache.cassandra.thrift.Mutation;
import org.apache.cassandra.thrift.SchemaDisagreementException;
import org.apache.cassandra.thrift.SuperColumn;
import org.apache.cassandra.thrift.TimedOutException;
import org.apache.cassandra.thrift.UnavailableException;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.impetus.client.cassandra.common.CassandraConstants;
import com.impetus.client.cassandra.common.CassandraUtilities;
import com.impetus.client.cassandra.config.CassandraPropertyReader;
import com.impetus.client.cassandra.datahandler.CassandraDataHandler;
import com.impetus.client.cassandra.schemamanager.CassandraDataTranslator;
import com.impetus.client.cassandra.schemamanager.CassandraValidationClassMapper;
import com.impetus.client.cassandra.thrift.CQLTranslator;
import com.impetus.client.cassandra.thrift.CQLTranslator.TranslationType;
import com.impetus.client.cassandra.thrift.ThriftDataResultHelper;
import com.impetus.client.cassandra.thrift.ThriftRow;
import com.impetus.kundera.Constants;
import com.impetus.kundera.KunderaException;
import com.impetus.kundera.PersistenceProperties;
import com.impetus.kundera.client.Client;
import com.impetus.kundera.client.ClientBase;
import com.impetus.kundera.client.ClientPropertiesSetter;
import com.impetus.kundera.client.EnhanceEntity;
import com.impetus.kundera.db.DataRow;
import com.impetus.kundera.db.RelationHolder;
import com.impetus.kundera.db.SearchResult;
import com.impetus.kundera.graph.Node;
import com.impetus.kundera.lifecycle.states.RemovedState;
import com.impetus.kundera.metadata.KunderaMetadataManager;
import com.impetus.kundera.metadata.model.EntityMetadata;
import com.impetus.kundera.metadata.model.EntityMetadata.Type;
import com.impetus.kundera.metadata.model.MetamodelImpl;
import com.impetus.kundera.metadata.model.PersistenceUnitMetadata;
import com.impetus.kundera.metadata.model.annotation.DefaultEntityAnnotationProcessor;
import com.impetus.kundera.metadata.model.attributes.AbstractAttribute;
import com.impetus.kundera.metadata.model.type.AbstractManagedType;
import com.impetus.kundera.persistence.EntityManagerFactoryImpl.KunderaMetadata;
import com.impetus.kundera.persistence.context.jointable.JoinTableData;
import com.impetus.kundera.property.PropertyAccessException;
import com.impetus.kundera.property.PropertyAccessor;
import com.impetus.kundera.property.PropertyAccessorFactory;
import com.impetus.kundera.property.PropertyAccessorHelper;
import com.impetus.kundera.property.accessor.StringAccessor;
import com.impetus.kundera.utils.KunderaCoreUtils;
import com.impetus.kundera.utils.TimestampGenerator;

/**
 * Base Class for all Cassandra Clients Contains methods that are applicable to
 * (but not specific to) different Cassandra clients.
 * 
 * @author amresh.singh
 */
public abstract class CassandraClientBase extends ClientBase implements ClientPropertiesSetter {

    /** log for this class. */
    private static Logger log = LoggerFactory.getLogger(CassandraClientBase.class);

    /** The cql version. */
    private String cqlVersion = CassandraConstants.CQL_VERSION_2_0;

    /** The consistency level. */
    protected ConsistencyLevel consistencyLevel = ConsistencyLevel.ONE;

    /** The ttl per request. */
    private boolean ttlPerRequest = false;

    /** The ttl per session. */
    private boolean ttlPerSession = false;

    /** The ttl values. */
    private Map<String, Object> ttlValues = new HashMap<String, Object>();

    /** The closed. */
    private volatile boolean closed = false;

    /** list of nodes for batch processing. */
    private List<Node> nodes = new ArrayList<Node>();

    /** batch size. */
    private int batchSize;

    /** The cql client. */
    protected final CQLClient cqlClient;

    /** The generator. */
    protected final TimestampGenerator generator;

    /** The cql metadata. */
    private CqlMetadata cqlMetadata;

    /**
     * constructor using fields.
     * 
     * @param persistenceUnit
     *            the persistence unit
     * @param externalProperties
     *            the external properties
     * @param kunderaMetadata
     *            the kundera metadata
     * @param generator
     *            the generator
     */
    protected CassandraClientBase(String persistenceUnit, Map<String, Object> externalProperties,
            final KunderaMetadata kunderaMetadata, final TimestampGenerator generator) {
        super(kunderaMetadata, externalProperties, persistenceUnit);
        this.cqlClient = new CQLClient();
        this.generator = generator;
        setBatchSize(persistenceUnit, this.externalProperties);
        populateCqlVersion(externalProperties);
    }

    /**
     * Populates foreign key as column.
     * 
     * @param rlName
     *            relation name
     * @param rlValue
     *            relation value
     * @param timestamp
     *            the timestamp
     * @return the column
     * @throws PropertyAccessException
     *             the property access exception
     */
    protected Column populateFkey(String rlName, Object rlValue, long timestamp) throws PropertyAccessException {
        Column col = new Column();
        col.setName(PropertyAccessorFactory.STRING.toBytes(rlName));
        col.setValue(PropertyAccessorHelper.getBytes(rlValue));
        col.setTimestamp(timestamp);
        return col;
    }

    /**
     * On counter column.
     * 
     * @param m
     *            the m
     * @param isRelation
     *            the is relation
     * @param relations
     *            the relations
     * @param ks
     *            the ks
     * @return the list
     */
    protected List<Object> onCounterColumn(EntityMetadata m, boolean isRelation, List<String> relations,
            List<KeySlice> ks) {
        List<Object> entities;

        if (m.getType().isSuperColumnFamilyMetadata()) {
            if (log.isInfoEnabled()) {
                log.info("On counter column for super column family of entity {}.", m.getEntityClazz());
            }

            // TODO:: change it. remove column or super column helper
            Map<byte[], List<CounterSuperColumn>> results = new HashMap<byte[], List<CounterSuperColumn>>();

            List<CounterSuperColumn> counterColumns = null;
            for (KeySlice slice : ks) {
                counterColumns = new ArrayList<CounterSuperColumn>(slice.getColumnsSize());
                for (ColumnOrSuperColumn column : slice.columns) {
                    counterColumns.add(column.counter_super_column);
                }

                results.put(slice.getKey(), counterColumns);
            }

            entities = new ArrayList<Object>(results.size());

            for (byte[] key : results.keySet()) {
                Object e = null;
                Object id = PropertyAccessorHelper.getObject(m.getIdAttribute().getJavaType(), key);
                List<CounterSuperColumn> counterSuperColumns = results.get(key);
                ThriftRow tr = new ThriftRow(id, m.getTableName(), new ArrayList<Column>(0),
                        new ArrayList<SuperColumn>(0), new ArrayList<CounterColumn>(0), counterSuperColumns);
                e = getDataHandler().populateEntity(tr, m, KunderaCoreUtils.getEntity(e), relations, isRelation);
                entities.add(e);
            }
        } else {
            if (log.isInfoEnabled()) {
                log.info("On counter column for column family of entity {}", m.getEntityClazz());
            }

            Map<byte[], List<CounterColumn>> results = new HashMap<byte[], List<CounterColumn>>();

            List<CounterColumn> counterColumns = null;

            for (KeySlice slice : ks) {
                counterColumns = new ArrayList<CounterColumn>(slice.getColumnsSize());
                for (ColumnOrSuperColumn column : slice.columns) {
                    counterColumns.add(column.counter_column);
                }

                results.put(slice.getKey(), counterColumns);
            }

            entities = new ArrayList<Object>(results.size());

            for (byte[] key : results.keySet()) {
                Object e = null;
                Object id = PropertyAccessorHelper.getObject(m.getIdAttribute().getJavaType(), key);

                List<CounterColumn> columns = results.get(key);
                ThriftRow tr = new ThriftRow(id, m.getTableName(), new ArrayList<Column>(0),
                        new ArrayList<SuperColumn>(0), columns, new ArrayList<CounterSuperColumn>(0));
                e = getDataHandler().populateEntity(tr, m, KunderaCoreUtils.getEntity(e), relations, isRelation);

                if (e != null) {
                    entities.add(e);
                }
            }
        }
        return entities;
    }

    /**
     * Compute entity via columns.
     * 
     * @param m
     *            the m
     * @param isRelation
     *            the is relation
     * @param relations
     *            the relations
     * @param entities
     *            the entities
     * @param qResults
     *            the q results
     */
    protected void computeEntityViaColumns(EntityMetadata m, boolean isRelation, List<String> relations,
            List<Object> entities, Map<ByteBuffer, List<Column>> qResults) {
        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata()
                .getMetamodel(m.getPersistenceUnit());

        EntityType entityType = metaModel.entity(m.getEntityClazz());

        List<AbstractManagedType> subManagedType = ((AbstractManagedType) entityType).getSubManagedType();

        for (ByteBuffer key : qResults.keySet()) {
            onColumn(m, isRelation, relations, entities, qResults.get(key), subManagedType, key);
        }
    }

    /**
     * On column.
     * 
     * @param m
     *            the m
     * @param isRelation
     *            the is relation
     * @param relations
     *            the relations
     * @param entities
     *            the entities
     * @param columns
     *            the columns
     * @param subManagedType
     *            the sub managed type
     * @param key
     *            the key
     */
    protected void onColumn(EntityMetadata m, boolean isRelation, List<String> relations, List<Object> entities,
            List<Column> columns, List<AbstractManagedType> subManagedType, ByteBuffer key) {
        if (!columns.isEmpty()) {
            Object id = PropertyAccessorHelper.getObject(m.getIdAttribute().getJavaType(), key.array());
            ThriftRow tr = new ThriftRow(id, m.getTableName(), columns, new ArrayList<SuperColumn>(0),
                    new ArrayList<CounterColumn>(0), new ArrayList<CounterSuperColumn>(0));
            Object o = null;

            if (!subManagedType.isEmpty()) {
                for (AbstractManagedType subEntity : subManagedType) {
                    EntityMetadata subEntityMetadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata,
                            subEntity.getJavaType());

                    o = getDataHandler().populateEntity(tr, subEntityMetadata, KunderaCoreUtils.getEntity(o),
                            subEntityMetadata.getRelationNames(), isRelation);
                    if (o != null) {
                        break;
                    }
                }
            } else {
                o = getDataHandler().populateEntity(tr, m, KunderaCoreUtils.getEntity(o), relations, isRelation);
            }

            if (log.isInfoEnabled()) {
                log.info("Populating data for entity of clazz {} and row key {}.", m.getEntityClazz(), tr.getId());
            }

            if (o != null) {
                entities.add(o);
            }
        }
    }

    /**
     * Compute entity via super columns.
     * 
     * @param m
     *            the m
     * @param isRelation
     *            the is relation
     * @param relations
     *            the relations
     * @param entities
     *            the entities
     * @param qResults
     *            the q results
     */
    protected void computeEntityViaSuperColumns(EntityMetadata m, boolean isRelation, List<String> relations,
            List<Object> entities, Map<ByteBuffer, List<SuperColumn>> qResults) {
        for (ByteBuffer key : qResults.keySet()) {
            onSuperColumn(m, isRelation, relations, entities, qResults.get(key), key);
        }
    }

    /**
     * On super column.
     * 
     * @param m
     *            the m
     * @param isRelation
     *            the is relation
     * @param relations
     *            the relations
     * @param entities
     *            the entities
     * @param superColumns
     *            the super columns
     * @param key
     *            the key
     */
    protected void onSuperColumn(EntityMetadata m, boolean isRelation, List<String> relations,
            List<Object> entities, List<SuperColumn> superColumns, ByteBuffer key) {
        Object e = null;
        Object id = PropertyAccessorHelper.getObject(m.getIdAttribute().getJavaType(), key.array());

        ThriftRow tr = new ThriftRow(id, m.getTableName(), new ArrayList<Column>(0), superColumns,
                new ArrayList<CounterColumn>(0), new ArrayList<CounterSuperColumn>(0));

        e = getDataHandler().populateEntity(tr, m, KunderaCoreUtils.getEntity(e), relations, isRelation);
        if (log.isInfoEnabled()) {
            log.info("Populating data for super column family of clazz {} and row key {}.", m.getEntityClazz(),
                    tr.getId());
        }

        if (e != null) {
            entities.add(e);
        }
    }

    /**
     * Adds relation foreign key values as thrift column/ value to thrift row.
     * 
     * @param metadata
     *            the metadata
     * @param tf
     *            the tf
     * @param relations
     *            the relations
     */
    protected void addRelationsToThriftRow(EntityMetadata metadata, ThriftRow tf, List<RelationHolder> relations) {
        if (relations != null) {
            long timestamp = generator.getTimestamp();
            MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata()
                    .getMetamodel(metadata.getPersistenceUnit());
            for (RelationHolder rh : relations) {
                String linkName = rh.getRelationName();
                Object linkValue = rh.getRelationValue();

                if (linkName != null && linkValue != null) {
                    if (metaModel.getEmbeddables(metadata.getEntityClazz()).isEmpty()) {
                        if (metadata.isCounterColumnType()) {
                            CounterColumn col = populateCounterFkey(linkName, linkValue);
                            tf.addCounterColumn(col);
                        } else {
                            Column col = populateFkey(linkName, linkValue, timestamp);
                            tf.addColumn(col);
                        }

                    } else {
                        if (metadata.isCounterColumnType()) {
                            CounterSuperColumn counterSuperColumn = new CounterSuperColumn();
                            counterSuperColumn.setName(linkName.getBytes());
                            CounterColumn column = populateCounterFkey(linkName, linkValue);
                            counterSuperColumn.addToColumns(column);
                            tf.addCounterSuperColumn(counterSuperColumn);
                        } else {
                            SuperColumn superColumn = new SuperColumn();
                            superColumn.setName(linkName.getBytes());
                            Column column = populateFkey(linkName, linkValue, timestamp);
                            superColumn.addToColumns(column);
                            tf.addSuperColumn(superColumn);
                        }
                    }
                }
            }
        }
    }

    /**
     * Populate counter fkey.
     * 
     * @param rlName
     *            the rl name
     * @param rlValue
     *            the rl value
     * @return the counter column
     */
    private CounterColumn populateCounterFkey(String rlName, Object rlValue) {
        CounterColumn counterCol = new CounterColumn();
        counterCol.setName(PropertyAccessorFactory.STRING.toBytes(rlName));
        counterCol.setValue((Long) rlValue);
        return counterCol;
    }

    /**
     * Deletes record for given primary key from counter column family.
     * 
     * @param pKey
     *            the key
     * @param tableName
     *            the table name
     * @param metadata
     *            the metadata
     * @param consistencyLevel
     *            the consistency level
     */
    protected void deleteRecordFromCounterColumnFamily(Object pKey, String tableName, EntityMetadata metadata,
            ConsistencyLevel consistencyLevel) {
        ColumnPath path = new ColumnPath(tableName);

        Cassandra.Client conn = null;
        Object pooledConnection = null;
        try {
            pooledConnection = getConnection();
            conn = (org.apache.cassandra.thrift.Cassandra.Client) getConnection(pooledConnection);

            if (log.isInfoEnabled()) {
                log.info("Removing data for counter column family {}.", tableName);
            }

            conn.remove_counter((CassandraUtilities.toBytes(pKey, metadata.getIdAttribute().getJavaType())), path,
                    consistencyLevel);

        } catch (Exception e) {
            log.error("Error during executing delete, Caused by: .", e);
            throw new PersistenceException(e);
        } finally {
            releaseConnection(pooledConnection);
        }
    }

    /**
     * Creates secondary indexes on columns if not already created.
     * 
     * @param m
     *            the m
     * @param tableName
     *            Column family name
     * @param columns
     *            List of columns
     * @param columnType
     *            the column type
     */
    protected void createIndexesOnColumns(EntityMetadata m, String tableName, List<Column> columns,
            Class columnType) {
        Object pooledConnection = null;
        try {
            Cassandra.Client api = null;
            pooledConnection = getConnection();
            api = (org.apache.cassandra.thrift.Cassandra.Client) getConnection(pooledConnection);
            KsDef ksDef = api.describe_keyspace(m.getSchema());
            List<CfDef> cfDefs = ksDef.getCf_defs();

            // Column family definition on which secondary index creation is
            // required
            CfDef columnFamilyDefToUpdate = null;
            boolean isUpdatable = false;
            for (CfDef cfDef : cfDefs) {
                if (cfDef.getName().equals(tableName)) {
                    columnFamilyDefToUpdate = cfDef;
                    break;
                }
            }

            if (columnFamilyDefToUpdate == null) {
                log.error("Join table {} not available.", tableName);
                throw new PersistenceException("table" + tableName + " not found!");
            }
            // create a column family, in case it is not already available.

            // Get list of indexes already created
            List<ColumnDef> columnMetadataList = columnFamilyDefToUpdate.getColumn_metadata();
            List<String> indexList = new ArrayList<String>();

            if (columnMetadataList != null) {
                for (ColumnDef columnDef : columnMetadataList) {
                    indexList.add(new StringAccessor().fromBytes(String.class, columnDef.getName()));
                }
                // need to set them to null else it is giving problem on update
                // column family and trying to add again existing indexes.
                // columnFamilyDefToUpdate.column_metadata = null;
            }

            // Iterate over all columns for creating secondary index on them
            for (Column column : columns) {

                ColumnDef columnDef = new ColumnDef();

                columnDef.setName(column.getName());
                columnDef.setValidation_class(CassandraValidationClassMapper.getValidationClass(columnType, false));
                columnDef.setIndex_type(IndexType.KEYS);

                // Add secondary index only if it's not already created
                // (if already created, it would be there in column family
                // definition)
                if (!indexList.contains(new StringAccessor().fromBytes(String.class, column.getName()))) {
                    isUpdatable = true;
                    columnFamilyDefToUpdate.addToColumn_metadata(columnDef);
                }
            }

            // Finally, update column family with modified column family
            // definition
            if (isUpdatable) {
                columnFamilyDefToUpdate.setKey_validation_class(CassandraValidationClassMapper
                        .getValidationClass(m.getIdAttribute().getJavaType(), isCql3Enabled(m)));
                api.system_update_column_family(columnFamilyDefToUpdate);
            }

        } catch (Exception e) {
            log.warn("Could not create secondary index on column family {}, Caused by: . ", tableName, e);

        } finally {
            releaseConnection(pooledConnection);
        }
    }

    /**
     * Finds an entiry from database.
     * 
     * @param entityClass
     *            the entity class
     * @param rowId
     *            the row id
     * @return the object
     */
    public Object find(Class entityClass, Object rowId) {
        EntityMetadata entityMetadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata, entityClass);
        List<String> relationNames = entityMetadata.getRelationNames();
        return find(entityClass, entityMetadata, rowId, relationNames);
    }

    /**
     * Finds a {@link List} of entities from database.
     * 
     * @param <E>
     *            the element type
     * @param entityClass
     *            the entity class
     * @param columnsToSelect
     *            TODO
     * @param rowIds
     *            the row ids
     * @return the list
     */
    public <E> List<E> findAll(Class<E> entityClass, String[] columnsToSelect, Object... rowIds) {
        EntityMetadata entityMetadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata, entityClass);
        List<E> results = new ArrayList<E>();
        results = find(entityClass, entityMetadata.getRelationNames(),
                entityMetadata.getRelationNames() != null && !entityMetadata.getRelationNames().isEmpty(),
                entityMetadata, rowIds);
        return results.isEmpty() ? null : results;
    }

    /**
     * Find.
     * 
     * @param clazz
     *            the clazz
     * @param metadata
     *            the metadata
     * @param rowId
     *            the row id
     * @param relationNames
     *            the relation names
     * @return the object
     */
    private final Object find(Class<?> clazz, EntityMetadata metadata, Object rowId, List<String> relationNames) {

        List<Object> result = null;
        try {
            MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata()
                    .getMetamodel(metadata.getPersistenceUnit());

            EntityType entityType = metaModel.entity(clazz);

            List<ManagedType> subTypes = ((AbstractManagedType) entityType).getSubManagedType();

            if (!subTypes.isEmpty()) {
                for (ManagedType subEntity : subTypes) {
                    EntityMetadata subEntityMetadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata,
                            subEntity.getJavaType());
                    result = populate(clazz, subEntityMetadata, rowId, subEntityMetadata.getRelationNames(),
                            metaModel);
                    if (result != null && !result.isEmpty()) {
                        break;
                    }
                }
            } else {
                result = populate(clazz, metadata, rowId, relationNames, metaModel);
            }
        } catch (Exception e) {
            log.error("Error while retrieving records from database for entity {} and key {}, Caused by: .", clazz,
                    rowId, e);

            throw new PersistenceException(e);
        }

        return result != null && !result.isEmpty() ? result.get(0) : null;
    }

    /**
     * Populate.
     * 
     * @param clazz
     *            the clazz
     * @param metadata
     *            the metadata
     * @param rowId
     *            the row id
     * @param relationNames
     *            the relation names
     * @param metaModel
     *            the meta model
     * @return the list
     */
    private List<Object> populate(Class<?> clazz, EntityMetadata metadata, Object rowId, List<String> relationNames,
            MetamodelImpl metaModel) {
        List<Object> result;
        if (isCql3Enabled(metadata)) {
            result = cqlClient.find(metaModel, metadata, rowId, relationNames);
        } else {
            result = (List<Object>) find(clazz, relationNames, relationNames != null, metadata, rowId);
        }
        return result;
    }

    /**
     * Returns true in case of, composite Id and if cql3 opted and not a
     * embedded entity.
     * 
     * @param metadata
     *            the metadata
     * @return true, if is cql3 enabled
     */
    public boolean isCql3Enabled(EntityMetadata metadata) {
        if (metadata != null) {

            MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata()
                    .getMetamodel(metadata.getPersistenceUnit());

            if (metaModel.isEmbeddable(metadata.getIdAttribute().getBindableJavaType())) {
                return true;
            }
            // added for embeddables support on cql3
            AbstractManagedType managedType = (AbstractManagedType) metaModel.entity(metadata.getEntityClazz());
            if (managedType.hasEmbeddableAttribute()) {
                return getCqlVersion().equalsIgnoreCase(CassandraConstants.CQL_VERSION_3_0);
            }

            if (getCqlVersion().equalsIgnoreCase(CassandraConstants.CQL_VERSION_3_0)
                    && metadata.getType().equals(Type.SUPER_COLUMN_FAMILY)) {
                log.warn(
                        "Super Columns not supported by cql, Any operation on supercolumn family will be executed using thrift, returning false.");
                return false;
            }
            return getCqlVersion().equalsIgnoreCase(CassandraConstants.CQL_VERSION_3_0);
        }
        return getCqlVersion().equalsIgnoreCase(CassandraConstants.CQL_VERSION_3_0);
    }

    /**
     * Returns true in case of, composite Id and if cql3 opted and not a
     * embedded entity.
     * 
     * @return true, if is cql3 enabled
     */
    public boolean isCql3Enabled() {
        return isCql3Enabled(null);
    }

    /**
     * Find.
     * 
     * @param <E>
     *            the element type
     * @param entityClass
     *            the entity class
     * @param superColumnMap
     *            the super column map
     * @param dataHandler
     *            the data handler
     * @return the list
     */
    public <E> List<E> find(Class<E> entityClass, Map<String, String> superColumnMap,
            CassandraDataHandler dataHandler) {
        List<E> entities = null;
        String entityId = null;
        try {
            EntityMetadata entityMetadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata,
                    getPersistenceUnit(), entityClass);
            entities = new ArrayList<E>();
            for (String superColumnName : superColumnMap.keySet()) {
                entityId = superColumnMap.get(superColumnName);
                List<SuperColumn> superColumnList = loadSuperColumns(entityMetadata.getSchema(),
                        entityMetadata.getTableName(), entityId,
                        new String[] { superColumnName.substring(0, superColumnName.indexOf("|")) });
                E e = (E) dataHandler.fromThriftRow(entityMetadata.getEntityClazz(), entityMetadata,
                        new DataRow<SuperColumn>(entityId, entityMetadata.getTableName(), superColumnList));
                if (e != null) {
                    entities.add(e);
                }
            }
        } catch (Exception e) {
            log.error("Error while retrieving records from database for entity {} and key {}, Caused by: . ",
                    entityClass, entityId, e);
            throw new KunderaException(e);
        }
        return entities;
    }

    /**
     * Executes Select CQL Query.
     * 
     * @param clazz
     *            the clazz
     * @param relationalField
     *            the relational field
     * @param dataHandler
     *            the data handler
     * @param isNative
     *            the is native
     * @param cqlQuery
     *            the cql query
     * @return the list
     */
    public List executeSelectQuery(Class clazz, List<String> relationalField, CassandraDataHandler dataHandler,
            boolean isNative, String cqlQuery) {
        if (log.isDebugEnabled()) {
            log.debug("Executing cql query {}.", cqlQuery);
        }

        List entities = new ArrayList<Object>();

        EntityMetadata entityMetadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata, clazz);

        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata()
                .getMetamodel(entityMetadata.getPersistenceUnit());

        EntityType entityType = metaModel.entity(entityMetadata.getEntityClazz());

        List<AbstractManagedType> subManagedType = ((AbstractManagedType) entityType).getSubManagedType();

        if (subManagedType.isEmpty()) {
            entities.addAll(cqlClient.executeQuery(clazz, relationalField, dataHandler, true, isNative, cqlQuery));
        } else {
            for (AbstractManagedType subEntity : subManagedType) {
                EntityMetadata subEntityMetadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata,
                        subEntity.getJavaType());

                entities.addAll(cqlClient.executeQuery(subEntityMetadata.getEntityClazz(), relationalField,
                        dataHandler, true, isNative, cqlQuery));
            }
        }
        return entities;
    }

    /**
     * Execute scalar query.
     * 
     * @param cqlQuery
     *            the cql query
     * @return the list
     */
    public List executeScalarQuery(String cqlQuery) {
        CqlResult cqlResult = null;
        List results = new ArrayList();
        try {
            if (log.isDebugEnabled()) {
                log.debug("Executing query {}.", cqlQuery);
            }
            cqlResult = (CqlResult) executeCQLQuery(cqlQuery, true);

            if (cqlResult != null && (cqlResult.getRows() != null || cqlResult.getRowsSize() > 0)) {
                results = new ArrayList<Object>(cqlResult.getRowsSize());
                Iterator<CqlRow> iter = cqlResult.getRowsIterator();
                while (iter.hasNext()) {
                    Map<String, Object> entity = new HashMap<String, Object>();

                    CqlRow row = iter.next();
                    for (Column column : row.getColumns()) {
                        if (column != null) {
                            String thriftColumnName = PropertyAccessorFactory.STRING.fromBytes(String.class,
                                    column.getName());

                            if (column.getValue() == null) {
                                entity.put(thriftColumnName, null);
                            } else {
                                entity.put(thriftColumnName, composeColumnValue(cqlResult.getSchema(),
                                        column.getValue(), column.getName()));
                            }
                        }
                    }
                    results.add(entity);
                }
            }
        } catch (Exception e) {
            log.error("Error while executing native CQL query Caused by {}.", e);
            throw new PersistenceException(e);
        }
        return results;
    }

    /**
     * Compose column value.
     * 
     * @param cqlMetadata
     *            the cql metadata
     * @param thriftColumnValue
     *            the thrift column value
     * @param thriftColumnName
     *            the thrift column name
     * @return the object
     */
    private Object composeColumnValue(CqlMetadata cqlMetadata, byte[] thriftColumnValue, byte[] thriftColumnName) {
        Map<ByteBuffer, String> schemaTypes = cqlMetadata.getValue_types();
        AbstractType<?> type = null;
        try {
            type = TypeParser.parse(schemaTypes.get(ByteBuffer.wrap(thriftColumnName)));
        } catch (SyntaxException | ConfigurationException ex) {
            log.error(ex.getMessage());
            throw new KunderaException("Error while deserializing column value " + ex);
        }
        if (type.isCollection()) {
            return ((CollectionSerializer) type.getSerializer())
                    .deserializeForNativeProtocol(ByteBuffer.wrap(thriftColumnValue), 2);
        }
        return type.compose(ByteBuffer.wrap(thriftColumnValue));
    }

    /**
     * Executes Update/ Delete CQL query.
     * 
     * @param cqlQuery
     *            the cql query
     * @return the int
     */
    public int executeUpdateDeleteQuery(String cqlQuery) {
        if (log.isDebugEnabled()) {
            log.debug("Executing cql query {}.", cqlQuery);
        }
        try {
            CqlResult result = (CqlResult) executeCQLQuery(cqlQuery, true);
            return result.getNum();
        } catch (Exception e) {
            log.error("Error while executing updated query: {}, Caused by: . ", cqlQuery, e);
            return 0;
        }

    }

    /**
     * Gets the external properties.
     * 
     * @return the external properties
     */
    public Map<String, Object> getExternalProperties() {
        return externalProperties;
    }

    /**
     * Populate entities from key slices.
     * 
     * @param m
     *            the m
     * @param isWrapReq
     *            the is wrap req
     * @param relations
     *            the relations
     * @param keys
     *            the keys
     * @param dataHandler
     *            the data handler
     * @return the list
     * @throws Exception
     *             the exception
     */
    protected List populateEntitiesFromKeySlices(EntityMetadata m, boolean isWrapReq, List<String> relations,
            List<KeySlice> keys, CassandraDataHandler dataHandler) throws Exception {
        List results;
        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata()
                .getMetamodel(m.getPersistenceUnit());

        Set<String> superColumnAttribs = metaModel.getEmbeddables(m.getEntityClazz()).keySet();
        results = new ArrayList(keys.size());

        ThriftDataResultHelper dataGenerator = new ThriftDataResultHelper();
        for (KeySlice key : keys) {
            List<ColumnOrSuperColumn> columns = key.getColumns();

            byte[] rowKey = key.getKey();

            Object id = PropertyAccessorHelper.getObject(m.getIdAttribute().getJavaType(), rowKey);

            Object e = null;
            Map<ByteBuffer, List<ColumnOrSuperColumn>> data = new HashMap<ByteBuffer, List<ColumnOrSuperColumn>>(1);
            data.put(ByteBuffer.wrap(rowKey), columns);
            ThriftRow tr = new ThriftRow();
            tr.setId(id);
            tr.setColumnFamilyName(m.getTableName());
            tr = dataGenerator.translateToThriftRow(data, m.isCounterColumnType(), m.getType(), tr);

            e = dataHandler.populateEntity(tr, m, KunderaCoreUtils.getEntity(e), relations, isWrapReq);

            if (e != null) {
                results.add(e);
            }
        }
        return results;
    }

    /**
     * Return insert query string for given entity.
     * 
     * @param entityMetadata
     *            the entity metadata
     * @param entity
     *            the entity
     * @param cassandra_client
     *            the cassandra_client
     * @param rlHolders
     *            the rl holders
     * @param ttlColumns
     *            TTL values for each columns
     * @return the list
     */
    protected List<String> createInsertQuery(EntityMetadata entityMetadata, Object entity,
            Cassandra.Client cassandra_client, List<RelationHolder> rlHolders, Object ttlColumns) {
        List<String> insert_Queries = new ArrayList<String>();
        CQLTranslator translator = new CQLTranslator();
        HashMap<TranslationType, Map<String, StringBuilder>> translation = translator.prepareColumnOrColumnValues(
                entity, entityMetadata, TranslationType.ALL, externalProperties, kunderaMetadata);

        Map<String, StringBuilder> columnNamesMap = translation.get(TranslationType.COLUMN);
        Map<String, StringBuilder> columnValuesMap = translation.get(TranslationType.VALUE);

        for (String tableName : columnNamesMap.keySet()) {
            String insert_Query = translator.INSERT_QUERY;

            insert_Query = StringUtils.replace(insert_Query, CQLTranslator.COLUMN_FAMILY,
                    translator.ensureCase(new StringBuilder(), tableName, false).toString());
            String columnNames = columnNamesMap.get(tableName).toString();
            String columnValues = columnValuesMap.get(tableName).toString();

            StringBuilder columnNameBuilder = new StringBuilder(columnNames);
            StringBuilder columnValueBuilder = new StringBuilder(columnValues);

            for (RelationHolder rl : rlHolders) {
                columnValueBuilder = onRelationColumns(columnNames, columnValues, columnNameBuilder,
                        columnValueBuilder, rl);

                columnNameBuilder.append(",");
                columnValueBuilder.append(",");
                translator.appendColumnName(columnNameBuilder, rl.getRelationName());
                translator.appendValue(columnValueBuilder, rl.getRelationValue().getClass(), rl.getRelationValue(),
                        true, false);

            }

            insert_Query = StringUtils.replace(insert_Query, CQLTranslator.COLUMN_VALUES,
                    columnValueBuilder.toString());
            insert_Query = StringUtils.replace(insert_Query, CQLTranslator.COLUMNS, columnNameBuilder.toString());

            if (log.isDebugEnabled()) {
                log.debug("Returning cql query {}.", insert_Query);
            }

            if (ttlColumns != null && ttlColumns instanceof Integer) {
                int ttl = ((Integer) ttlColumns).intValue();
                if (ttl != 0) {
                    insert_Query = insert_Query + " USING TTL " + ttl;
                }
            }
            insert_Queries.add(insert_Query);
        }
        return insert_Queries;
    }

    /**
     * On relation columns.
     * 
     * @param columnNames
     *            the column names
     * @param columnValues
     *            the column values
     * @param columnNameBuilder
     *            the column name builder
     * @param columnValueBuilder
     *            the column value builder
     * @param rl
     *            the rl
     * @return To remove redundant columns in insert query
     */
    private StringBuilder onRelationColumns(String columnNames, String columnValues,
            StringBuilder columnNameBuilder, StringBuilder columnValueBuilder, RelationHolder rl) {
        int relnameIndx = columnNameBuilder.indexOf("\"" + rl.getRelationName() + "\"");
        if (relnameIndx != -1 && rl.getRelationValue() != null) {

            List<String> cNameArray = Arrays.asList(columnNames.split(","));
            List<String> cValueArray = new ArrayList<String>(Arrays.asList(columnValues.split(",")));
            int cValueIndex = cNameArray.indexOf("\"" + rl.getRelationName() + "\"");

            if (cValueArray.get(cValueIndex).equals("null")) {
                columnNameBuilder.delete(relnameIndx - 1, relnameIndx + rl.getRelationName().length() + 2);
                cValueArray.remove(cValueIndex);
                columnValueBuilder = new StringBuilder(
                        cValueArray.toString().substring(1, cValueArray.toString().length() - 1));

            }

        }
        return columnValueBuilder;
    }

    /**
     * Return update query string for given entity intended for counter column
     * family.
     * 
     * @param entityMetadata
     *            the entity metadata
     * @param entity
     *            the entity
     * @param cassandra_client
     *            the cassandra_client
     * @param rlHolders
     *            the rl holders
     * @return the list
     */
    protected List<String> createUpdateQueryForCounter(EntityMetadata entityMetadata, Object entity,
            Cassandra.Client cassandra_client, List<RelationHolder> rlHolders) {
        Map<String, String> builders = new HashMap<String, String>();

        CQLTranslator translator = new CQLTranslator();

        Object rowId = PropertyAccessorHelper.getId(entity, entityMetadata);
        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata()
                .getMetamodel(entityMetadata.getPersistenceUnit());

        EntityType entityType = metaModel.entity(entityMetadata.getEntityClazz());

        Set<Attribute> attributes = entityType.getAttributes();

        for (Attribute attrib : attributes) {
            if (!entityMetadata.getIdAttribute().getName().equals(attrib.getName())
                    && !metaModel.isEmbeddable(attrib.getJavaType()) && !attrib.isAssociation()) {
                String tableName = ((AbstractAttribute) attrib).getTableName() != null
                        ? ((AbstractAttribute) attrib).getTableName()
                        : entityMetadata.getTableName();

                String queryString = builders.get(tableName);
                StringBuilder builder;
                if (queryString == null) {
                    builder = new StringBuilder();
                } else {
                    builder = new StringBuilder(queryString);
                }
                translator.buildSetClauseForCounters(builder, ((AbstractAttribute) attrib).getJPAColumnName(),
                        PropertyAccessorHelper.getObject(entity, attrib.getName()));
                builders.put(tableName, builder.toString());
            }
        }
        for (RelationHolder rl : rlHolders) {
            translator.buildSetClauseForCounters(new StringBuilder(builders.get(entityMetadata.getTableName())),
                    rl.getRelationName(), rl.getRelationValue());
        }

        for (String tableName : builders.keySet()) {
            StringBuilder builder = new StringBuilder(builders.get(tableName));

            String update_Query = translator.UPDATE_QUERY;

            update_Query = StringUtils.replace(update_Query, CQLTranslator.COLUMN_FAMILY,
                    translator.ensureCase(new StringBuilder(), tableName, false).toString());

            // strip last "," clause.
            builder.delete(builder.lastIndexOf(CQLTranslator.COMMA_STR), builder.length());

            builder.append(CQLTranslator.ADD_WHERE_CLAUSE);
            onWhereClause(entityMetadata, rowId, translator, builder, metaModel, entityMetadata.getIdAttribute());

            // strip last "AND" clause.
            builder.delete(builder.lastIndexOf(CQLTranslator.AND_CLAUSE), builder.length());

            StringBuilder queryBuilder = new StringBuilder(update_Query);
            queryBuilder.append(CQLTranslator.ADD_SET_CLAUSE);
            queryBuilder.append(builder);

            if (log.isDebugEnabled()) {
                log.debug("Returning update query {}.", queryBuilder.toString());
            }

            builders.put(tableName, queryBuilder.toString());
        }
        return new ArrayList(builders.values());
    }

    /**
     * Gets the persist queries.
     * 
     * @param entityMetadata
     *            the entity metadata
     * @param entity
     *            the entity
     * @param conn
     *            the conn
     * @param rlHolders
     *            the rl holders
     * @param ttlColumns
     *            the ttl columns
     * @return the persist queries
     */
    protected List<String> getPersistQueries(EntityMetadata entityMetadata, Object entity,
            org.apache.cassandra.thrift.Cassandra.Client conn, List<RelationHolder> rlHolders, Object ttlColumns) {
        List<String> queries;
        if (entityMetadata.isCounterColumnType()) {
            queries = createUpdateQueryForCounter(entityMetadata, entity, conn, rlHolders);
        } else {
            queries = createInsertQuery(entityMetadata, entity, conn, rlHolders, ttlColumns);
        }
        return queries;
    }

    /**
     * Gets the cql version.
     * 
     * @return the cqlVersion
     */
    protected String getCqlVersion() {
        return this.cqlVersion;
    }

    /**
     * Sets the cql version.
     * 
     * @param cqlVersion
     *            the cqlVersion to set
     */
    public void setCqlVersion(String cqlVersion) {
        this.cqlVersion = cqlVersion;
    }

    /**
     * Sets the consistency level.
     * 
     * @param cLevel
     *            the new consistency level
     */
    public void setConsistencyLevel(ConsistencyLevel cLevel) {
        if (cLevel != null) {
            this.consistencyLevel = cLevel;
        } else {
            log.warn("Invalid consistency level {null} provided, default level will be used.");
        }
    }

    /**
     * Close.
     */
    public void close() {
        clear();
        setCqlVersion(CassandraConstants.CQL_VERSION_2_0);
        closed = true;
        externalProperties = null;
    }

    /**
     * Checks if is open.
     * 
     * @return true, if is open
     */
    protected final boolean isOpen() {
        return !closed;
    }

    /**
     * Gets the consistency level.
     * 
     * @return the consistency level
     */
    public ConsistencyLevel getConsistencyLevel() {
        return consistencyLevel;
    }

    /**
     * On delete query.
     * 
     * @param metadata
     *            the metadata
     * @param tableName
     *            TODO
     * @param metaModel
     *            the meta model
     * @param keyObject
     *            the compound key object
     * @return the string
     */
    protected String onDeleteQuery(EntityMetadata metadata, String tableName, MetamodelImpl metaModel,
            Object keyObject) {
        CQLTranslator translator = new CQLTranslator();
        String deleteQuery = CQLTranslator.DELETE_QUERY;

        deleteQuery = StringUtils.replace(deleteQuery, CQLTranslator.COLUMN_FAMILY,
                translator.ensureCase(new StringBuilder(), tableName, false).toString());

        StringBuilder deleteQueryBuilder = new StringBuilder(deleteQuery);

        deleteQueryBuilder.append(CQLTranslator.ADD_WHERE_CLAUSE);
        onWhereClause(metadata, keyObject, translator, deleteQueryBuilder, metaModel, metadata.getIdAttribute());

        // strip last "AND" clause.
        deleteQueryBuilder.delete(deleteQueryBuilder.lastIndexOf(CQLTranslator.AND_CLAUSE),
                deleteQueryBuilder.length());

        if (log.isDebugEnabled()) {
            log.debug("Returning delete query {}.", deleteQueryBuilder.toString());
        }
        return deleteQueryBuilder.toString();
    }

    /**
     * On where clause.
     * 
     * @param metadata
     *            the metadata
     * @param key
     *            the compound key object
     * @param translator
     *            the translator
     * @param queryBuilder
     *            the query builder
     * @param metaModel
     *            the meta model
     * @param attribute
     *            the attribute
     */
    protected void onWhereClause(EntityMetadata metadata, Object key, CQLTranslator translator,
            StringBuilder queryBuilder, MetamodelImpl metaModel, SingularAttribute attribute) {
        // SingularAttribute idAttribute = metadata.getIdAttribute();
        if (metaModel.isEmbeddable(attribute.getBindableJavaType())) {
            Field[] fields = attribute.getBindableJavaType().getDeclaredFields();
            EmbeddableType compoundKey = metaModel.embeddable(attribute.getBindableJavaType());

            for (Field field : fields) {
                if (field != null && !Modifier.isStatic(field.getModifiers())
                        && !Modifier.isTransient(field.getModifiers())
                        && !field.isAnnotationPresent(Transient.class)) {
                    attribute = (SingularAttribute) compoundKey.getAttribute(field.getName());
                    Object valueObject = PropertyAccessorHelper.getObject(key, field);
                    if (metaModel.isEmbeddable(((AbstractAttribute) attribute).getBindableJavaType())) {
                        onWhereClause(metadata, valueObject, translator, queryBuilder, metaModel, attribute);
                    } else {
                        String columnName = ((AbstractAttribute) attribute).getJPAColumnName();
                        translator.buildWhereClause(queryBuilder, field.getType(), columnName, valueObject,
                                CQLTranslator.EQ_CLAUSE, false);
                    }
                }
            }
        } else {
            translator.buildWhereClause(queryBuilder, ((AbstractAttribute) attribute).getBindableJavaType(),
                    CassandraUtilities.getIdColumnName(kunderaMetadata, metadata, getExternalProperties(),
                            isCql3Enabled(metadata)),
                    key, translator.EQ_CLAUSE, false);
        }
    }

    /**
     * Find.
     * 
     * @param entityClass
     *            the entity class
     * @param relationNames
     *            the relation names
     * @param isWrapReq
     *            the is wrap req
     * @param metadata
     *            the metadata
     * @param rowIds
     *            the row ids
     * @return the list
     */
    public abstract List find(Class entityClass, List<String> relationNames, boolean isWrapReq,
            EntityMetadata metadata, Object... rowIds);

    /**
     * Load super columns.
     * 
     * @param keyspace
     *            the keyspace
     * @param columnFamily
     *            the column family
     * @param rowId
     *            the row id
     * @param superColumnNames
     *            the super column names
     * @return the list
     */
    protected abstract List<SuperColumn> loadSuperColumns(String keyspace, String columnFamily, String rowId,
            String... superColumnNames);

    /**
     * Query related methods.
     * 
     * @param clazz
     *            the clazz
     * @param relationalField
     *            the relational field
     * @param isNative
     *            the is native
     * @param cqlQuery
     *            the cql query
     * @return the list
     */
    public abstract List executeQuery(Class clazz, List<String> relationalField, boolean isNative, String cqlQuery);

    /**
     * Find.
     * 
     * @param ixClause
     *            the ix clause
     * @param m
     *            the m
     * @param isRelation
     *            the is relation
     * @param relations
     *            the relations
     * @param maxResult
     *            the max result
     * @param columns
     *            the columns
     * @return the list
     */
    public abstract List find(List<IndexClause> ixClause, EntityMetadata m, boolean isRelation,
            List<String> relations, int maxResult, List<String> columns);

    /**
     * Find by range.
     * 
     * @param muinVal
     *            the muin val
     * @param maxVal
     *            the max val
     * @param m
     *            the m
     * @param isWrapReq
     *            the is wrap req
     * @param relations
     *            the relations
     * @param columns
     *            the columns
     * @param conditions
     *            the conditions
     * @param maxResults
     *            the max results
     * @return the list
     * @throws Exception
     *             the exception
     */
    public abstract List findByRange(byte[] muinVal, byte[] maxVal, EntityMetadata m, boolean isWrapReq,
            List<String> relations, List<String> columns, List<IndexExpression> conditions, int maxResults)
            throws Exception;

    /**
     * Search in inverted index.
     * 
     * @param columnFamilyName
     *            the column family name
     * @param m
     *            the m
     * @param indexClauseMap
     *            the index clause map
     * @return the list
     */
    public abstract List<SearchResult> searchInInvertedIndex(String columnFamilyName, EntityMetadata m,
            Map<Boolean, List<IndexClause>> indexClauseMap);

    /**
     * Find.
     * 
     * @param m
     *            the m
     * @param relationNames
     *            the relation names
     * @param conditions
     *            the conditions
     * @param maxResult
     *            the max result
     * @param columns
     *            the columns
     * @return the list
     */
    public abstract List<EnhanceEntity> find(EntityMetadata m, List<String> relationNames,
            List<IndexClause> conditions, int maxResult, List<String> columns);

    /**
     * Gets the data handler.
     * 
     * @return the data handler
     */
    protected abstract CassandraDataHandler getDataHandler();

    /**
     * Delete.
     * 
     * @param entity
     *            the entity
     * @param pKey
     *            the key
     */
    protected abstract void delete(Object entity, Object pKey);

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.impetus.kundera.persistence.api.Batcher#addBatch(com.impetus.kundera
     * .graph.Node)
     */
    /**
     * Adds the batch.
     * 
     * @param node
     *            the node
     */
    public void addBatch(Node node) {

        if (node != null) {
            nodes.add(node);
        }

        onBatchLimit();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.impetus.kundera.persistence.api.Batcher#getBatchSize()
     */
    /**
     * Gets the batch size.
     * 
     * @return the batch size
     */
    public int getBatchSize() {
        return batchSize;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.impetus.kundera.persistence.api.Batcher#clear()
     */
    /**
     * Clear.
     */
    public void clear() {
        if (nodes != null) {
            nodes.clear();
            nodes = new ArrayList<Node>();
        }

        if (ttlPerSession) {
            ttlValues.clear();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.impetus.kundera.persistence.api.Batcher#executeBatch()
     */
    /**
     * Execute batch.
     * 
     * @return the int
     */
    public int executeBatch() {
        Cassandra.Client conn = null;
        Object pooledConnection = null;

        /**
         * Key -> Entity Class Value -> Map containing Row ID as Key and
         * Mutation List as Value
         */
        Map<Class<?>, Map<ByteBuffer, Map<String, List<Mutation>>>> batchMutationMap = new HashMap<Class<?>, Map<ByteBuffer, Map<String, List<Mutation>>>>();

        int recordsExecuted = 0;
        boolean setCounter = true;
        String batchQuery = CQLTranslator.BATCH_QUERY;
        batchQuery = StringUtils.replace(batchQuery, CQLTranslator.STATEMENT, "");
        StringBuilder batchQueryBuilder = new StringBuilder(batchQuery);
        try {
            boolean isCql3Enabled = false;
            for (Node node : nodes) {
                if (node.isDirty()) {
                    node.handlePreEvent();
                    Object entity = node.getData();
                    Object id = node.getEntityId();
                    EntityMetadata metadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata,
                            node.getDataClass());
                    if (metadata.isCounterColumnType() && setCounter) {
                        batchQueryBuilder = new StringBuilder(StringUtils.replace(batchQueryBuilder.toString(),
                                CQLTranslator.BEGIN_BATCH, CQLTranslator.BEGIN_COUNTER_BATCH));
                        setCounter = false;
                    }
                    persistenceUnit = metadata.getPersistenceUnit();
                    isUpdate = node.isUpdate();

                    MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata()
                            .getMetamodel(metadata.getPersistenceUnit());

                    // delete can not be executed in batch

                    if (isCql3Enabled(metadata)) {
                        isCql3Enabled = true;
                        List<RelationHolder> relationHolders = getRelationHolders(node);
                        if (node.isInState(RemovedState.class)) {
                            String query;
                            query = onDeleteQuery(metadata, metadata.getTableName(), metaModel, id);
                            batchQueryBuilder.append(Constants.SPACE);
                            batchQueryBuilder.append(query);
                        } else {
                            List<String> insertQueries = getPersistQueries(metadata, entity, conn, relationHolders,
                                    getTtlValues().get(metadata.getTableName()));
                            for (String query : insertQueries) {
                                batchQueryBuilder.append(Constants.SPACE);
                                batchQueryBuilder.append(query);
                            }
                        }
                    } else {
                        if (node.isInState(RemovedState.class)) {
                            delete(entity, id);
                        } else {
                            List<RelationHolder> relationHolders = getRelationHolders(node);
                            Map<ByteBuffer, Map<String, List<Mutation>>> mutationMap = new HashMap<ByteBuffer, Map<String, List<Mutation>>>();
                            mutationMap = prepareMutation(metadata, entity, id, relationHolders, mutationMap);

                            recordsExecuted += mutationMap.size();
                            if (!batchMutationMap.containsKey(metadata.getEntityClazz())) {
                                batchMutationMap.put(metadata.getEntityClazz(), mutationMap);
                            } else {
                                batchMutationMap.get(metadata.getEntityClazz()).putAll(mutationMap);
                            }

                            indexNode(node, metadata);
                        }
                    }
                    node.handlePostEvent();
                }
            }

            // Write Mutation map to database

            if (!batchMutationMap.isEmpty()) {
                pooledConnection = getConnection();
                conn = (org.apache.cassandra.thrift.Cassandra.Client) getConnection(pooledConnection);

                for (Class<?> entityClass : batchMutationMap.keySet()) {
                    conn.batch_mutate(batchMutationMap.get(entityClass), consistencyLevel);
                }
            }

            if (!nodes.isEmpty() && isCql3Enabled) {
                batchQueryBuilder.append(CQLTranslator.APPLY_BATCH);
                executeCQLQuery(batchQueryBuilder.toString(), isCql3Enabled);
            }
        } catch (Exception e) {
            log.error("Error while persisting record. Caused by: .", e);
            throw new KunderaException(e);
        } finally {
            clear();
            if (pooledConnection != null) {
                releaseConnection(pooledConnection);
            }
        }

        return recordsExecuted;
    }

    /**
     * Prepare mutation.
     * 
     * @param entityMetadata
     *            the entity metadata
     * @param entity
     *            the entity
     * @param id
     *            the id
     * @param relationHolders
     *            the relation holders
     * @param mutationMap
     *            the mutation map
     * @return the map
     */
    protected Map<ByteBuffer, Map<String, List<Mutation>>> prepareMutation(EntityMetadata entityMetadata,
            Object entity, Object id, List<RelationHolder> relationHolders,
            Map<ByteBuffer, Map<String, List<Mutation>>> mutationMap) {

        if (!isOpen()) {
            throw new PersistenceException("ThriftClient is closed.");
        }

        // check for counter column
        if (isUpdate && entityMetadata.isCounterColumnType()) {
            log.warn("Invalid operation! {} is not possible over counter column of entity {}.", "Merge",
                    entityMetadata.getEntityClazz());
            throw new UnsupportedOperationException(
                    "Invalid operation! Merge is not possible over counter column.");
        }

        Collection<ThriftRow> tfRows = null;
        try {
            String columnFamily = entityMetadata.getTableName();
            tfRows = getDataHandler().toThriftRow(entity, id, entityMetadata, columnFamily,
                    getTtlValues().get(columnFamily));
        } catch (Exception e) {
            log.error("Error during persisting record for entity {}, Caused by: .", entityMetadata.getEntityClazz(),
                    entityMetadata.getTableName(), e);
            throw new KunderaException(e);
        }

        Map<String, List<Mutation>> columnFamilyValues = new HashMap<String, List<Mutation>>();

        for (ThriftRow tf : tfRows) {
            if (tf.getColumnFamilyName().equals(entityMetadata.getTableName())) {
                addRelationsToThriftRow(entityMetadata, tf, relationHolders);
            }

            String columnFamily = tf.getColumnFamilyName();
            // Create Insertion List
            List<Mutation> mutationList = new ArrayList<Mutation>();

            /*********** Handling for counter column family ************/

            if (entityMetadata.isCounterColumnType()) {
                List<CounterColumn> thriftCounterColumns = tf.getCounterColumns();
                List<CounterSuperColumn> thriftCounterSuperColumns = tf.getCounterSuperColumns();

                if (thriftCounterColumns != null && !thriftCounterColumns.isEmpty()) {
                    for (CounterColumn column : thriftCounterColumns) {
                        Mutation mut = new Mutation();
                        mut.setColumn_or_supercolumn(new ColumnOrSuperColumn().setCounter_column(column));
                        mutationList.add(mut);
                    }
                }

                if (thriftCounterSuperColumns != null && !thriftCounterSuperColumns.isEmpty()) {
                    for (CounterSuperColumn sc : thriftCounterSuperColumns) {
                        Mutation mut = new Mutation();
                        mut.setColumn_or_supercolumn(new ColumnOrSuperColumn().setCounter_super_column(sc));
                        mutationList.add(mut);
                    }
                }
            } else
            /********* Handling for column family and super column family *********/
            {
                List<Column> thriftColumns = tf.getColumns();
                List<SuperColumn> thriftSuperColumns = tf.getSuperColumns();

                // Populate Insertion list for columns
                if (thriftColumns != null && !thriftColumns.isEmpty()) {
                    for (Column column : thriftColumns) {
                        Mutation mut = new Mutation();
                        mut.setColumn_or_supercolumn(new ColumnOrSuperColumn().setColumn(column));
                        mutationList.add(mut);
                    }
                }

                // Populate Insertion list for super columns
                if (thriftSuperColumns != null && !thriftSuperColumns.isEmpty()) {
                    for (SuperColumn superColumn : thriftSuperColumns) {
                        Mutation mut = new Mutation();
                        mut.setColumn_or_supercolumn(new ColumnOrSuperColumn().setSuper_column(superColumn));
                        mutationList.add(mut);
                    }
                }
            }
            columnFamilyValues.put(columnFamily, mutationList);
        }
        // Create Mutation Map

        ByteBuffer b = CassandraUtilities.toBytes(id, entityMetadata.getIdAttribute().getBindableJavaType());
        mutationMap.put(b, columnFamilyValues);

        return mutationMap;
    }

    /**
     * Check on batch limit.
     */
    private void onBatchLimit() {
        if (batchSize > 0 && batchSize == nodes.size()) {
            executeBatch();
            nodes.clear();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.impetus.kundera.client.ClientPropertiesSetter#populateClientProperties
     * (com.impetus.kundera.client.Client, java.util.Map)
     */
    @Override
    public void populateClientProperties(Client client, Map<String, Object> properties) {
        new CassandraClientProperties().populateClientProperties(client, properties);
    }

    /**
     * Returns raw cassandra client from thrift connection pool.
     * 
     * @param schema
     *            schema or keyspace.
     * @return raw cassandra client.
     */
    public Cassandra.Client getRawClient(final String schema) {
        Cassandra.Client client = null;
        Object pooledConnection;
        pooledConnection = getConnection();
        client = (org.apache.cassandra.thrift.Cassandra.Client) getConnection(pooledConnection);
        try {
            client.set_cql_version(getCqlVersion());
        } catch (Exception e) {
            log.error("Error during borrowing a connection , Caused by: {}.", e);
            throw new KunderaException(e);
        } finally {
            releaseConnection(pooledConnection);
        }
        return client;

    }

    /**
     * Executes query string using cql3.
     * 
     * @param cqlQuery
     *            the cql query
     * @param isCql3Enabled
     *            the is cql3 enabled
     * @return the object
     */
    protected Object executeCQLQuery(String cqlQuery, boolean isCql3Enabled) {
        Cassandra.Client conn = null;
        Object pooledConnection = null;
        pooledConnection = getConnection();
        conn = (org.apache.cassandra.thrift.Cassandra.Client) getConnection(pooledConnection);
        try {
            if (isCql3Enabled || isCql3Enabled()) {
                return execute(cqlQuery, conn);
            }
            KunderaCoreUtils.printQuery(cqlQuery, showQuery);
            if (log.isDebugEnabled()) {
                log.debug("Executing cql query {}.", cqlQuery);
            }
            return conn.execute_cql_query(ByteBufferUtil.bytes(cqlQuery),
                    org.apache.cassandra.thrift.Compression.NONE);
        } catch (Exception ex) {
            if (log.isErrorEnabled()) {
                log.error("Error during executing query {}, Caused by: {} .", cqlQuery, ex);
            }
            throw new PersistenceException(ex);
        } finally {
            releaseConnection(pooledConnection);
        }
    }

    /**
     * Find List of objects based on value {@columnValue} of column
     * {@columnName}.
     * 
     * @param m
     *            the m
     * @param columnName
     *            the column name
     * @param columnValue
     *            the column value
     * @param clazz
     *            the clazz
     * @param dataHandler
     *            the data handler
     * @return the list
     */
    protected List<Object> findByRelationQuery(EntityMetadata m, String columnName, Object columnValue, Class clazz,
            CassandraDataHandler dataHandler) {
        return cqlClient.findByRelationQuery(m, columnName, columnValue, clazz, dataHandler);
    }

    /**
     * Sets the batch size.
     * 
     * @param persistenceUnit
     *            the persistence unit
     * @param puProperties
     *            the pu properties
     */
    private void setBatchSize(String persistenceUnit, Map<String, Object> puProperties) {
        String batch_Size = null;

        PersistenceUnitMetadata puMetadata = KunderaMetadataManager.getPersistenceUnitMetadata(kunderaMetadata,
                persistenceUnit);

        String externalBatchSize = puProperties != null
                ? (String) puProperties.get(PersistenceProperties.KUNDERA_BATCH_SIZE)
                : null;

        Integer intbatch = null;
        if (puMetadata.getBatchSize() > 0) {
            intbatch = new Integer(puMetadata.getBatchSize());
        }
        batch_Size = (String) (externalBatchSize != null ? externalBatchSize
                : intbatch != null ? intbatch.toString() : null);

        setBatchSize(batch_Size);
    }

    /**
     * Sets the batch size.
     * 
     * @param batch_Size
     *            the new batch size
     */
    void setBatchSize(String batch_Size) {
        if (!StringUtils.isBlank(batch_Size)) {
            batchSize = Integer.valueOf(batch_Size);
            if (batchSize == 0) {
                throw new IllegalArgumentException("kundera.batch.size property must be numeric and > 0.");
            }
        }
    }

    /**
     * Populate cql version.
     * 
     * @param externalProperties
     *            the external properties
     */
    private void populateCqlVersion(Map<String, Object> externalProperties) {
        String cqlVersion = externalProperties != null
                ? (String) externalProperties.get(CassandraConstants.CQL_VERSION)
                : null;
        if (cqlVersion == null || !(cqlVersion != null && (cqlVersion.equals(CassandraConstants.CQL_VERSION_2_0)
                || cqlVersion.equals(CassandraConstants.CQL_VERSION_3_0)))) {
            cqlVersion = (CassandraPropertyReader.csmd != null ? CassandraPropertyReader.csmd.getCqlVersion()
                    : CassandraConstants.CQL_VERSION_2_0);
        }

        if (cqlVersion.equals(CassandraConstants.CQL_VERSION_3_0)) {
            setCqlVersion(CassandraConstants.CQL_VERSION_3_0);
        } else {
            setCqlVersion(CassandraConstants.CQL_VERSION_2_0);
        }
    }

    /**
     * Gets the connection.
     * 
     * @return the connection
     */
    protected abstract Object getConnection();

    /**
     * Gets the connection.
     * 
     * @param connection
     *            the connection
     * @return the connection
     */
    protected abstract Object getConnection(Object connection);

    /**
     * Release connection.
     * 
     * @param conn
     *            the conn
     */
    protected abstract void releaseConnection(Object conn);

    /**
     * Use CqlClient class for crud when cql enable.
     * 
     * 
     * @author Kuldeep Mishra
     * 
     */
    protected class CQLClient {

        /**
         * Persist.
         * 
         * @param entityMetadata
         *            the entity metadata
         * @param entity
         *            the entity
         * @param conn
         *            the conn
         * @param rlHolders
         *            the rl holders
         * @param ttlColumns
         *            the ttl columns
         * @throws UnsupportedEncodingException
         *             the unsupported encoding exception
         * @throws InvalidRequestException
         *             the invalid request exception
         * @throws TException
         *             the t exception
         * @throws UnavailableException
         *             the unavailable exception
         * @throws TimedOutException
         *             the timed out exception
         * @throws SchemaDisagreementException
         *             the schema disagreement exception
         */
        public void persist(EntityMetadata entityMetadata, Object entity,
                org.apache.cassandra.thrift.Cassandra.Client conn, List<RelationHolder> rlHolders,
                Object ttlColumns) throws UnsupportedEncodingException, InvalidRequestException, TException,
                UnavailableException, TimedOutException, SchemaDisagreementException {
            List<String> queries = getPersistQueries(entityMetadata, entity, conn, rlHolders, ttlColumns);

            for (String query : queries) {
                execute(query, conn);

            }
        }

        /**
         * Execute query and Return list of Objects.
         * 
         * @param clazz
         *            the clazz
         * @param relationalField
         *            the relational field
         * @param dataHandler
         *            the data handler
         * @param isCql3Enabled
         *            the is cql3 enabled
         * @param isNative
         *            the is native
         * @param cqlQuery
         *            the cql query
         * @return the list
         */
        public List executeQuery(Class clazz, List<String> relationalField, CassandraDataHandler dataHandler,
                boolean isCql3Enabled, boolean isNative, String cqlQuery) {
            EntityMetadata entityMetadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata, clazz);

            CqlResult result = null;
            List returnedEntities = new ArrayList();
            try {
                if (log.isDebugEnabled()) {
                    log.debug("Executing query {}.", cqlQuery);
                }
                result = (CqlResult) executeCQLQuery(cqlQuery, isCql3Enabled);

                setCqlMetadata(result.getSchema());

                if (result != null && (result.getRows() != null || result.getRowsSize() > 0)) {
                    returnedEntities = new ArrayList<Object>(result.getRowsSize());
                    Iterator<CqlRow> iter = result.getRowsIterator();
                    while (iter.hasNext()) {
                        Object e = null;

                        CqlRow row = iter.next();
                        Object rowKey = null;

                        ThriftRow thriftRow = null;
                        thriftRow = new ThriftRow(rowKey, entityMetadata.getTableName(), row.getColumns(),
                                new ArrayList<SuperColumn>(0), new ArrayList<CounterColumn>(0),
                                new ArrayList<CounterSuperColumn>(0));
                        // send cqlmetadata

                        e = dataHandler.populateEntity(thriftRow, entityMetadata, KunderaCoreUtils.getEntity(e),
                                relationalField, relationalField != null && !relationalField.isEmpty());

                        e = populateSecondaryTableData(relationalField, dataHandler, isCql3Enabled, entityMetadata,
                                e);

                        if (e != null) {
                            returnedEntities.add(e);
                        } else if (isNative) {
                            returnedEntities.add(row.getColumns().get(0));
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Error while executing native CQL query Caused by {}.", e);
                throw new PersistenceException(e);
            }
            return returnedEntities;
        }

        /**
         * Populate secondary table data.
         * 
         * @param relationalField
         *            the relational field
         * @param dataHandler
         *            the data handler
         * @param isCql3Enabled
         *            the is cql3 enabled
         * @param entityMetadata
         *            the entity metadata
         * @param e
         *            the e
         * @return the object
         */
        private Object populateSecondaryTableData(List<String> relationalField, CassandraDataHandler dataHandler,
                boolean isCql3Enabled, EntityMetadata entityMetadata, Object e) {
            CqlResult result;
            // For secondary tables.
            MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata()
                    .getMetamodel(entityMetadata.getPersistenceUnit());
            if (!metaModel.isEmbeddable(entityMetadata.getIdAttribute().getBindableJavaType())) {
                AbstractManagedType managedType = (AbstractManagedType) metaModel
                        .entity(entityMetadata.getEntityClazz());
                List<String> secondaryTables = ((DefaultEntityAnnotationProcessor) managedType
                        .getEntityAnnotation()).getSecondaryTablesName();

                CQLTranslator translator = new CQLTranslator();

                for (String tableName : secondaryTables) {
                    // Building query.
                    StringBuilder queryBuilder = new StringBuilder("select * from \"" + tableName + "\" where ");
                    Attribute attribute = entityMetadata.getIdAttribute();
                    translator.buildWhereClause(queryBuilder,
                            ((AbstractAttribute) entityMetadata.getIdAttribute()).getBindableJavaType(),
                            CassandraUtilities.getIdColumnName(kunderaMetadata, entityMetadata,
                                    getExternalProperties(), isCql3Enabled(entityMetadata)),
                            PropertyAccessorHelper.getId(e, entityMetadata), translator.EQ_CLAUSE, false);
                    // strip last "AND" clause.
                    queryBuilder.delete(queryBuilder.lastIndexOf(CQLTranslator.AND_CLAUSE), queryBuilder.length());

                    // Executing.
                    result = (CqlResult) executeCQLQuery(queryBuilder.toString(), isCql3Enabled);

                    if (result != null && (result.getRows() != null || result.getRowsSize() > 0)) {
                        Iterator<CqlRow> iterator = result.getRowsIterator();
                        while (iterator.hasNext()) {
                            CqlRow cqlRow = iterator.next();

                            ThriftRow tr = null;
                            tr = new ThriftRow(null, entityMetadata.getTableName(), cqlRow.getColumns(),
                                    new ArrayList<SuperColumn>(0), new ArrayList<CounterColumn>(0),
                                    new ArrayList<CounterSuperColumn>(0));

                            e = dataHandler.populateEntity(tr, entityMetadata, KunderaCoreUtils.getEntity(e),
                                    relationalField, relationalField != null && !relationalField.isEmpty());
                            break;
                        }
                    }
                }
            }
            return e;
        }

        /**
         * Finds entity on the basis of rowid and return list of objects.
         * 
         * @param metaModel
         *            the meta model
         * @param metadata
         *            the metadata
         * @param rowId
         *            the row id
         * @param relationNames
         *            the relation names
         * @return the list
         */
        public List<Object> find(MetamodelImpl metaModel, EntityMetadata metadata, Object rowId,
                List<String> relationNames) {
            CQLTranslator translator = new CQLTranslator();

            String tableName = metadata.getTableName();
            String select_Query = translator.SELECTALL_QUERY;
            select_Query = StringUtils.replace(select_Query, CQLTranslator.COLUMN_FAMILY,
                    translator.ensureCase(new StringBuilder(), tableName, false).toString());
            StringBuilder builder = new StringBuilder(select_Query);
            builder.append(CQLTranslator.ADD_WHERE_CLAUSE);
            onWhereClause(metadata, rowId, translator, builder, metaModel, metadata.getIdAttribute());

            // strip last "AND" clause.
            builder.delete(builder.lastIndexOf(CQLTranslator.AND_CLAUSE), builder.length());
            return CassandraClientBase.this.executeQuery(metadata.getEntityClazz(), relationNames, false,
                    builder.toString());
        }

        /**
         * Find List of objects based on value {@columnValue} of column
         * {@columnName}.
         * 
         * @param m
         *            the m
         * @param columnName
         *            the column name
         * @param columnValue
         *            the column value
         * @param clazz
         *            the clazz
         * @param dataHandler
         *            the data handler
         * @return the list
         */
        protected List<Object> findByRelationQuery(EntityMetadata m, String columnName, Object columnValue,
                Class clazz, CassandraDataHandler dataHandler) {
            CQLTranslator translator = new CQLTranslator();
            String selectQuery = translator.SELECTALL_QUERY;
            selectQuery = StringUtils.replace(selectQuery, CQLTranslator.COLUMN_FAMILY,
                    translator.ensureCase(new StringBuilder(), m.getTableName(), false).toString());

            StringBuilder selectQueryBuilder = new StringBuilder(selectQuery);
            selectQueryBuilder.append(CQLTranslator.ADD_WHERE_CLAUSE);

            translator.buildWhereClause(selectQueryBuilder, columnValue.getClass(), columnName, columnValue,
                    CQLTranslator.EQ_CLAUSE, false);
            selectQueryBuilder.delete(selectQueryBuilder.lastIndexOf(CQLTranslator.AND_CLAUSE),
                    selectQueryBuilder.length());
            return this.executeQuery(clazz, m.getRelationNames(), dataHandler, true, false,
                    selectQueryBuilder.toString());
        }
    }

    /**
     * Checks if is ttl per request.
     * 
     * @return the ttlPerRequest
     */
    public boolean isTtlPerRequest() {
        return ttlPerRequest;
    }

    /**
     * Sets the ttl per request.
     * 
     * @param ttlPerRequest
     *            the ttlPerRequest to set
     */
    public void setTtlPerRequest(boolean ttlPerRequest) {
        this.ttlPerRequest = ttlPerRequest;
    }

    /**
     * Checks if is ttl per session.
     * 
     * @return the ttlPerSession
     */
    public boolean isTtlPerSession() {
        return ttlPerSession;
    }

    /**
     * Sets the ttl per session.
     * 
     * @param ttlPerSession
     *            the ttlPerSession to set
     */
    public void setTtlPerSession(boolean ttlPerSession) {
        this.ttlPerSession = ttlPerSession;
    }

    /**
     * Gets the ttl values.
     * 
     * @return the ttlValues
     */
    public Map<String, Object> getTtlValues() {
        return ttlValues;
    }

    /**
     * Sets the ttl values.
     * 
     * @param ttlValues
     *            the ttlValues to set
     */
    public void setTtlValues(Map<String, Object> ttlValues) {
        this.ttlValues = ttlValues;
    }

    /**
     * Finds a {@link List} of entities from database.
     * 
     * @param entityClass
     *            the entity class
     * @param relationNames
     *            the relation names
     * @param isWrapReq
     *            the is wrap req
     * @param metadata
     *            the metadata
     * @param rowIds
     *            the row ids
     * @return the list
     */
    public final List findByRowKeys(Class entityClass, List<String> relationNames, boolean isWrapReq,
            EntityMetadata metadata, Object... rowIds) {
        List entities = null;

        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata()
                .getMetamodel(metadata.getPersistenceUnit());

        EntityType entityType = metaModel.entity(metadata.getEntityClazz());

        List<AbstractManagedType> subManagedType = ((AbstractManagedType) entityType).getSubManagedType();

        try {
            if (!subManagedType.isEmpty()) {
                for (AbstractManagedType subEntity : subManagedType) {
                    EntityMetadata subEntityMetadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata,
                            subEntity.getJavaType());
                    entities = getDataHandler().fromThriftRow(entityClass, subEntityMetadata,
                            subEntityMetadata.getRelationNames(), isWrapReq, getConsistencyLevel(), rowIds);

                    if (entities != null && !entities.isEmpty()) {
                        break;
                    }
                }
            } else {
                entities = getDataHandler().fromThriftRow(entityClass, metadata, relationNames, isWrapReq,
                        getConsistencyLevel(), rowIds);
            }
        } catch (Exception e) {
            log.error("Error while retrieving records for entity {}, row keys {}", entityClass, rowIds);
            throw new KunderaException(e);
        }
        return entities;
    }

    /**
     * Execute.
     * 
     * @param <T>
     *            the generic type
     * @param query
     *            the query
     * @param connection
     *            the connection
     * @return the t
     */
    public <T> T execute(final String query, Object connection) {
        try {
            org.apache.cassandra.thrift.Cassandra.Client conn = (org.apache.cassandra.thrift.Cassandra.Client) connection;
            conn.set_cql_version(CassandraConstants.CQL_VERSION_3_0);
            KunderaCoreUtils.printQuery(query, showQuery);
            return (T) conn.execute_cql3_query(ByteBuffer.wrap(query.getBytes(Constants.CHARSET_UTF8)),
                    Compression.NONE, getConsistencyLevel());
        } catch (Exception e) {
            log.error("Error while executing query {}", query);
            throw new KunderaException(e);
        }
    }

    /**
     * Persist join table by cql.
     * 
     * @param joinTableData
     *            the join table data
     * @param conn
     *            the conn
     */
    protected void persistJoinTableByCql(JoinTableData joinTableData, Cassandra.Client conn) {
        String joinTableName = joinTableData.getJoinTableName();
        String invJoinColumnName = joinTableData.getInverseJoinColumnName();
        Map<Object, Set<Object>> joinTableRecords = joinTableData.getJoinTableRecords();

        EntityMetadata entityMetadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata,
                joinTableData.getEntityClass());

        // need to bring in an insert query for this
        // add columns & execute query
        CQLTranslator translator = new CQLTranslator();

        String batch_Query = CQLTranslator.BATCH_QUERY;

        String insert_Query = translator.INSERT_QUERY;

        StringBuilder builder = new StringBuilder();
        builder.append(CQLTranslator.DEFAULT_KEY_NAME);
        builder.append(CQLTranslator.COMMA_STR);
        builder.append(translator.ensureCase(new StringBuilder(), joinTableData.getJoinColumnName(), false));
        builder.append(CQLTranslator.COMMA_STR);
        builder.append(translator.ensureCase(new StringBuilder(), joinTableData.getInverseJoinColumnName(), false));

        insert_Query = StringUtils.replace(insert_Query, CQLTranslator.COLUMN_FAMILY,
                translator.ensureCase(new StringBuilder(), joinTableName, false).toString());

        insert_Query = StringUtils.replace(insert_Query, CQLTranslator.COLUMNS, builder.toString());

        StringBuilder columnValueBuilder = new StringBuilder();

        StringBuilder statements = new StringBuilder();

        // insert query for each row key

        for (Object key : joinTableRecords.keySet()) {
            PropertyAccessor accessor = PropertyAccessorFactory
                    .getPropertyAccessor((Field) entityMetadata.getIdAttribute().getJavaMember());

            Set<Object> values = joinTableRecords.get(key); // join column value

            for (Object value : values) {
                if (value != null) {
                    String insertQuery = insert_Query;
                    columnValueBuilder.append(CQLTranslator.QUOTE_STR);
                    columnValueBuilder.append(PropertyAccessorHelper.getString(key) + "\001"
                            + PropertyAccessorHelper.getString(value));
                    columnValueBuilder.append(CQLTranslator.QUOTE_STR);
                    columnValueBuilder.append(CQLTranslator.COMMA_STR);
                    translator.appendValue(columnValueBuilder, key.getClass(), key, true, false);
                    columnValueBuilder.append(CQLTranslator.COMMA_STR);
                    translator.appendValue(columnValueBuilder, value.getClass(), value, true, false);

                    insertQuery = StringUtils.replace(insertQuery, CQLTranslator.COLUMN_VALUES,
                            columnValueBuilder.toString());
                    statements.append(insertQuery);
                    statements.append(" ");
                }
            }
        }

        if (!StringUtils.isBlank(statements.toString())) {
            batch_Query = StringUtils.replace(batch_Query, CQLTranslator.STATEMENT, statements.toString());
            StringBuilder batchBuilder = new StringBuilder();
            batchBuilder.append(batch_Query);
            batchBuilder.append(CQLTranslator.APPLY_BATCH);
            execute(batchBuilder.toString(), conn);
        }

    }

    /**
     * Find inverse join column values for join column.
     * 
     * @param <E>
     *            the element type
     * @param schemaName
     *            the schema name
     * @param tableName
     *            the table name
     * @param pKeyColumnName
     *            the key column name
     * @param columnName
     *            the column name
     * @param pKeyColumnValue
     *            the key column value
     * @param columnJavaType
     *            the column java type
     * @return the columns by id using cql
     */
    protected <E> List<E> getColumnsByIdUsingCql(String schemaName, String tableName, String pKeyColumnName,
            String columnName, Object pKeyColumnValue, Class columnJavaType) {
        // select columnName from tableName where pKeyColumnName =
        // pKeyColumnValue
        List results = new ArrayList();
        CQLTranslator translator = new CQLTranslator();
        String selectQuery = translator.SELECT_QUERY;
        selectQuery = StringUtils.replace(selectQuery, CQLTranslator.COLUMN_FAMILY,
                translator.ensureCase(new StringBuilder(), tableName, false).toString());
        selectQuery = StringUtils.replace(selectQuery, CQLTranslator.COLUMNS,
                translator.ensureCase(new StringBuilder(), columnName, false).toString());

        StringBuilder selectQueryBuilder = new StringBuilder(selectQuery);

        selectQueryBuilder.append(CQLTranslator.ADD_WHERE_CLAUSE);

        translator.buildWhereClause(selectQueryBuilder, columnJavaType, pKeyColumnName, pKeyColumnValue,
                CQLTranslator.EQ_CLAUSE, false);
        selectQueryBuilder.delete(selectQueryBuilder.lastIndexOf(CQLTranslator.AND_CLAUSE),
                selectQueryBuilder.length());

        CqlResult cqlResult = execute(selectQueryBuilder.toString(), getRawClient(schemaName));

        Iterator<CqlRow> rowIter = cqlResult.getRows().iterator();
        while (rowIter.hasNext()) {
            CqlRow row = rowIter.next();

            if (!row.getColumns().isEmpty()) {
                Column column = row.getColumns().get(0);
                Object columnValue = CassandraDataTranslator.decompose(columnJavaType, column.getValue(), true);
                results.add(columnValue);
            }
        }
        return results;
    }

    /**
     * Find join column values for inverse join column.
     * 
     * @param <E>
     *            the element type
     * @param schemaName
     *            the schema name
     * @param tableName
     *            the table name
     * @param pKeyName
     *            the key name
     * @param columnName
     *            the column name
     * @param columnValue
     *            the column value
     * @param entityClazz
     *            the entity clazz
     * @return the list
     */
    protected <E> List<E> findIdsByColumnUsingCql(String schemaName, String tableName, String pKeyName,
            String columnName, Object columnValue, Class entityClazz) {
        EntityMetadata metadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata, entityClazz);

        return getColumnsByIdUsingCql(schemaName, tableName, columnName,
                ((AbstractAttribute) metadata.getIdAttribute()).getJPAColumnName(), columnValue,
                metadata.getIdAttribute().getBindableJavaType());
    }

    /**
     * Gets the cql metadata.
     * 
     * @return the cql metadata
     */
    public CqlMetadata getCqlMetadata() {
        return cqlMetadata;
    }

    /**
     * Sets the cql metadata.
     * 
     * @param cqlMetadata
     *            the new cql metadata
     */
    public void setCqlMetadata(CqlMetadata cqlMetadata) {
        this.cqlMetadata = cqlMetadata;
    }
}