public BoundStatementWrapper bindForClusteredCounterSelect(
      PersistentStateHolder context,
      PreparedStatement ps,
      boolean onlyStaticColumns,
      ConsistencyLevel consistencyLevel) {
    EntityMeta entityMeta = context.getEntityMeta();
    Object primaryKey = context.getPrimaryKey();

    log.trace(
        "Bind prepared statement {} for clustered counter read for {} using primary key {}",
        ps.getQueryString(),
        entityMeta,
        primaryKey);

    List<Object> primaryKeys =
        bindPrimaryKey(primaryKey, entityMeta.getIdMeta(), onlyStaticColumns);
    Object[] boundValues = primaryKeys.toArray();

    BoundStatement bs = ps.bind(boundValues);
    return new BoundStatementWrapper(
        context.getEntityClass(),
        bs,
        boundValues,
        getCQLLevel(consistencyLevel),
        NO_LISTENER,
        NO_SERIAL_CONSISTENCY);
  }
  public BoundStatementWrapper bindForUpdate(
      PersistentStateHolder context, PreparedStatement ps, List<PropertyMeta> pms) {
    EntityMeta entityMeta = context.getEntityMeta();
    Object entity = context.getEntity();

    log.trace(
        "Bind prepared statement {} for properties {} update of entity {}",
        ps.getQueryString(),
        pms,
        entity);

    ConsistencyLevel consistencyLevel = overrider.getWriteLevel(context);

    List<Object> values = new ArrayList<>();

    final int staticColumnsCount =
        FluentIterable.from(pms).filter(PropertyMeta.STATIC_COLUMN_FILTER).size();
    final boolean onlyStaticColumns = staticColumnsCount > 0 && pms.size() == staticColumnsCount;

    values.addAll(fetchTTLAndTimestampValues(context));
    values.addAll(fetchPropertiesValues(pms, entity));
    values.addAll(fetchPrimaryKeyValues(entityMeta, entity, onlyStaticColumns));
    values.addAll(fetchCASConditionsValues(context, entityMeta));
    BoundStatement bs = ps.bind(values.toArray());

    return new BoundStatementWrapper(
        context.getEntityClass(),
        bs,
        values.toArray(),
        getCQLLevel(consistencyLevel),
        context.getCASResultListener(),
        context.getSerialConsistencyLevel());
  }
  public BoundStatementWrapper bindForClusteredCounterIncrementDecrement(
      PersistentStateHolder context,
      PreparedStatement ps,
      PropertyMeta counterMeta,
      Long increment) {

    EntityMeta entityMeta = context.getEntityMeta();
    Object primaryKey = context.getPrimaryKey();

    log.trace(
        "Bind prepared statement {} for clustered counter increment/decrement for {} using primary key {} and value {}",
        ps.getQueryString(),
        entityMeta,
        primaryKey,
        increment);

    ConsistencyLevel consistencyLevel = overrider.getWriteLevel(context);

    List<Object> primaryKeys =
        bindPrimaryKey(
            primaryKey, entityMeta.getIdMeta(), counterMeta.structure().isStaticColumn());
    Object[] keys = addAll(new Object[] {increment}, primaryKeys.toArray());

    BoundStatement bs = ps.bind(keys);

    return new BoundStatementWrapper(
        context.getEntityClass(),
        bs,
        keys,
        getCQLLevel(consistencyLevel),
        NO_LISTENER,
        NO_SERIAL_CONSISTENCY);
  }
  public BoundStatementWrapper bindForSimpleCounterIncrementDecrement(
      PersistentStateHolder context,
      PreparedStatement ps,
      PropertyMeta pm,
      Long increment,
      ConsistencyLevel consistencyLevel) {

    EntityMeta entityMeta = context.getEntityMeta();
    Object primaryKey = context.getPrimaryKey();

    log.trace(
        "Bind prepared statement {} for simple counter increment of {} using primary key {} and value {}",
        ps.getQueryString(),
        pm,
        primaryKey,
        increment);
    Object[] boundValues =
        ArrayUtils.add(
            extractValuesForSimpleCounterBinding(entityMeta, pm, primaryKey), 0, increment);

    BoundStatement bs = ps.bind(boundValues);
    return new BoundStatementWrapper(
        context.getEntityClass(),
        bs,
        boundValues,
        getCQLLevel(consistencyLevel),
        NO_LISTENER,
        NO_SERIAL_CONSISTENCY);
  }
  public BoundStatementWrapper bindStatementWithOnlyPKInWhereClause(
      PersistentStateHolder context,
      PreparedStatement ps,
      boolean onlyStaticColumns,
      ConsistencyLevel consistencyLevel) {

    Object primaryKey = context.getPrimaryKey();

    log.trace("Bind prepared statement {} with primary key {}", ps.getQueryString(), primaryKey);

    PropertyMeta idMeta = context.getIdMeta();
    List<Object> values = bindPrimaryKey(primaryKey, idMeta, onlyStaticColumns);

    BoundStatement bs = ps.bind(values.toArray());
    return new BoundStatementWrapper(
        context.getEntityClass(),
        bs,
        values.toArray(),
        getCQLLevel(consistencyLevel),
        context.getCASResultListener(),
        context.getSerialConsistencyLevel());
  }
  public BoundStatementWrapper bindForSimpleCounterDelete(
      PersistentStateHolder context, PreparedStatement ps, PropertyMeta pm) {
    EntityMeta entityMeta = context.getEntityMeta();
    Object primaryKey = context.getPrimaryKey();

    log.trace(
        "Bind prepared statement {} for simple counter delete for {} using primary key {}",
        ps.getQueryString(),
        pm,
        primaryKey);

    ConsistencyLevel consistencyLevel = overrider.getWriteLevel(context);

    Object[] boundValues = extractValuesForSimpleCounterBinding(entityMeta, pm, primaryKey);
    BoundStatement bs = ps.bind(boundValues);
    return new BoundStatementWrapper(
        context.getEntityClass(),
        bs,
        boundValues,
        getCQLLevel(consistencyLevel),
        NO_LISTENER,
        NO_SERIAL_CONSISTENCY);
  }
  public BoundStatementWrapper bindForInsert(
      PersistentStateHolder context, PreparedStatement ps, List<PropertyMeta> pms) {

    EntityMeta entityMeta = context.getEntityMeta();
    Object entity = context.getEntity();

    log.trace("Bind prepared statement {} for insert of entity {}", ps.getQueryString(), entity);

    ConsistencyLevel consistencyLevel = overrider.getWriteLevel(context);

    List<Object> values = new ArrayList<>();
    values.addAll(fetchPrimaryKeyValues(entityMeta, entity, false));
    values.addAll(fetchPropertiesValues(pms, entity));
    values.addAll(fetchTTLAndTimestampValues(context));

    BoundStatement bs = ps.bind(values.toArray());
    return new BoundStatementWrapper(
        context.getEntityClass(),
        bs,
        values.toArray(),
        getCQLLevel(consistencyLevel),
        context.getCASResultListener(),
        context.getSerialConsistencyLevel());
  }
  public BoundStatementWrapper bindForClusteredCounterDelete(
      PersistentStateHolder context, PreparedStatement ps) {
    EntityMeta entityMeta = context.getEntityMeta();
    Object primaryKey = context.getPrimaryKey();

    log.trace(
        "Bind prepared statement {} for simple counter delete for {} using primary key {}",
        ps.getQueryString(),
        entityMeta,
        primaryKey);

    ConsistencyLevel consistencyLevel = overrider.getWriteLevel(context);
    List<Object> primaryKeys = bindPrimaryKey(primaryKey, entityMeta.getIdMeta(), false);
    Object[] boundValues = primaryKeys.toArray(new Object[primaryKeys.size()]);
    BoundStatement bs = ps.bind(boundValues);

    return new BoundStatementWrapper(
        context.getEntityClass(),
        bs,
        boundValues,
        getCQLLevel(consistencyLevel),
        NO_LISTENER,
        NO_SERIAL_CONSISTENCY);
  }
  public BoundStatementWrapper bindForCollectionAndMapUpdate(
      PersistentStateHolder context, PreparedStatement ps, DirtyCheckChangeSet changeSet) {
    EntityMeta entityMeta = context.getEntityMeta();
    Object entity = context.getEntity();

    log.trace(
        "Bind prepared statement {} for collection/map update of entity {}",
        ps.getQueryString(),
        entity);

    ConsistencyLevel consistencyLevel = overrider.getWriteLevel(context);

    List<Object> values = new ArrayList<>();
    final CollectionAndMapChangeType changeType = changeSet.getChangeType();

    values.addAll(fetchTTLAndTimestampValues(context));

    switch (changeType) {
      case ASSIGN_VALUE_TO_LIST:
        values.add(changeSet.getEncodedListChanges());
        break;
      case ASSIGN_VALUE_TO_SET:
        values.add(changeSet.getEncodedSetChanges());
        break;
      case ASSIGN_VALUE_TO_MAP:
        values.add(changeSet.getEncodedMapChanges());
        break;
      case REMOVE_COLLECTION_OR_MAP:
        values.add(null);
        break;
      case ADD_TO_SET:
      case REMOVE_FROM_SET:
        values.add(changeSet.getEncodedSetChanges());
        break;
      case APPEND_TO_LIST:
      case PREPEND_TO_LIST:
      case REMOVE_FROM_LIST:
        values.add(changeSet.getEncodedListChanges());
        break;
      case SET_TO_LIST_AT_INDEX:
        // No prepared statement for set list element at index
        throw new IllegalStateException("Cannot bind statement to set element at index for list");
      case REMOVE_FROM_LIST_AT_INDEX:
        // No prepared statement for set list element at index
        throw new IllegalStateException(
            "Cannot bind statement to remove element at index for list");
      case ADD_TO_MAP:
        values.add(changeSet.getEncodedMapChanges());
        break;
      case REMOVE_FROM_MAP:
        values.add(changeSet.getEncodedMapChanges().keySet().iterator().next());
        values.add(null);
        break;
    }

    values.addAll(
        fetchPrimaryKeyValues(
            entityMeta, entity, changeSet.getPropertyMeta().structure().isStaticColumn()));
    values.addAll(fetchCASConditionsValues(context, entityMeta));
    BoundStatement bs = ps.bind(values.toArray());

    return new BoundStatementWrapper(
        context.getEntityClass(),
        bs,
        values.toArray(),
        getCQLLevel(consistencyLevel),
        context.getCASResultListener(),
        context.getSerialConsistencyLevel());
  }