src/main/java/net/ttddyy/dsproxy/asserts/BatchParameterHolder.java
public interface BatchParameterHolder {
    List<BatchExecutionEntry> getBatchExecutionEntries();
}
src/main/java/net/ttddyy/dsproxy/asserts/QueryExecution.java
public interface QueryExecution {

    boolean isSuccess();

    boolean isBatch();

}
src/main/java/net/ttddyy/dsproxy/asserts/StatementBatchExecution.java
public class StatementBatchExecution extends BaseQueryExecution implements QueriesHolder, BatchExecution {

    private List<String> queries = new ArrayList<>();

    @Override
    public boolean isBatch() {
        return true;
    }

    @Override
    public List<String> getQueries() {
        return this.queries;
    }

    public void setQueries(List<String> queries) {
        this.queries = queries;
    }

}
src/main/java/net/ttddyy/dsproxy/asserts/QueryHolder.java
public interface QueryHolder {

    String getQuery();

}
src/main/java/net/ttddyy/dsproxy/asserts/PreparedBatchExecution.java
public class PreparedBatchExecution extends BaseQueryExecution implements QueryHolder, BatchParameterHolder, BatchExecution {

    private String query;
    private List<BatchExecutionEntry> batchExecutionEntries = new ArrayList<>();

    @Override
    public boolean isBatch() {
        return true;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    @Override
    public String getQuery() {
        return this.query;
    }

    @Override
    public List<BatchExecutionEntry> getBatchExecutionEntries() {
        return this.batchExecutionEntries;
    }

    public boolean addBatchExecutionEntry(PreparedBatchExecutionEntry entry) {
        return this.batchExecutionEntries.add(entry);
    }

    @SuppressWarnings("unchecked")
    public List<PreparedBatchExecutionEntry> getPrepareds() {

        return (List<PreparedBatchExecutionEntry>) (List<?>) this.batchExecutionEntries;
    }

}
src/main/java/net/ttddyy/dsproxy/asserts/ParameterHolder.java
public interface ParameterHolder {

    SortedSet<ParameterKeyValue> getAllParameters();

    SortedSet<ParameterKeyValue> getSetParams();

    SortedSet<ParameterKeyValue> getSetNullParams();

}
src/main/java/net/ttddyy/dsproxy/asserts/PreparedExecution.java
public class PreparedExecution extends BaseQueryExecution implements QueryHolder, ParameterByIndexHolder {

    private String query;
    private SortedSet<ParameterKeyValue> parameters = new TreeSet<>();

    @Override
    public boolean isBatch() {
        return false;
    }

    @Override
    public String getQuery() {
        return this.query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    @Override
    public SortedSet<ParameterKeyValue> getAllParameters() {
        return this.parameters;
    }

    @Override
    public SortedSet<ParameterKeyValue> getSetParams() {
        return filterBy(this.parameters, ParameterKeyValue.OperationType.SET_PARAM);
    }

    @Override
    public SortedSet<ParameterKeyValue> getSetNullParams() {
        return filterBy(this.parameters, ParameterKeyValue.OperationType.SET_NULL);
    }

    @Override
    public Map<Integer, Object> getSetParamsByIndex() {
        return toKeyIndexMap(filterByKeyType(getSetParams(), ParameterKey.ParameterKeyType.BY_INDEX));
    }

    @Override
    public Map<Integer, Integer> getSetNullParamsByIndex() {
        return toKeyIndexMap(filterByKeyType(getSetNullParams(), ParameterKey.ParameterKeyType.BY_INDEX));
    }

    @Override
    public List<Integer> getParamIndexes() {
        List<Integer> indexes = new ArrayList<>();
        indexes.addAll(getSetParamsByIndex().keySet());
        indexes.addAll(getSetNullParamsByIndex().keySet());
        return indexes;
    }

}
src/main/java/net/ttddyy/dsproxy/asserts/QueriesHolder.java
public interface QueriesHolder {

    List<String> getQueries();

}
src/main/java/net/ttddyy/dsproxy/asserts/ProxyTestDataSource.java
public class ProxyTestDataSource extends ProxyDataSource {

    private QueryExecutionFactoryListener queryExecutionFactoryListener = new QueryExecutionFactoryListener();


    public ProxyTestDataSource() {
        initialize();
    }

    public ProxyTestDataSource(DataSource dataSource) {
        super(dataSource);
        initialize();
    }

    private void initialize() {
        this.getProxyConfig().getQueryListener().addListener(this.queryExecutionFactoryListener);
    }

    public void reset() {
        this.queryExecutionFactoryListener.reset();
    }

    public List<StatementExecution> getStatements() {
        return getQueryExecutionsFilteredBy(StatementExecution.class);
    }

    public StatementExecution getFirstStatement() {
        return getFirstQueryExecution(StatementExecution.class);
    }

    public StatementExecution getLastStatement() {
        return getLastQueryExecution(StatementExecution.class);
    }

    public List<StatementBatchExecution> getBatchStatements() {
        return getQueryExecutionsFilteredBy(StatementBatchExecution.class);
    }

    public StatementBatchExecution getFirstBatchStatement() {
        return getFirstQueryExecution(StatementBatchExecution.class);
    }

    public StatementBatchExecution getLastBatchStatement() {
        return getLastQueryExecution(StatementBatchExecution.class);
    }

    public List<PreparedExecution> getPrepareds() {
        return getQueryExecutionsFilteredBy(PreparedExecution.class);
    }

    public PreparedExecution getFirstPrepared() {
        return getFirstQueryExecution(PreparedExecution.class);
    }

    public PreparedExecution getLastPrepared() {
        return getLastQueryExecution(PreparedExecution.class);
    }

    public List<PreparedBatchExecution> getBatchPrepareds() {
        return getQueryExecutionsFilteredBy(PreparedBatchExecution.class);
    }

    public PreparedBatchExecution getFirstBatchPrepared() {
        return getFirstQueryExecution(PreparedBatchExecution.class);
    }

    public PreparedBatchExecution getLastBatchPrepared() {
        return getLastQueryExecution(PreparedBatchExecution.class);
    }

    public List<CallableExecution> getCallables() {
        return getQueryExecutionsFilteredBy(CallableExecution.class);
    }

    public CallableExecution getFirstCallable() {
        return getFirstQueryExecution(CallableExecution.class);
    }

    public CallableExecution getLastCallable() {
        return getLastQueryExecution(CallableExecution.class);
    }

    public List<CallableBatchExecution> getBatchCallables() {
        return getQueryExecutionsFilteredBy(CallableBatchExecution.class);
    }

    public CallableBatchExecution getFirstBatchCallable() {
        return getFirstQueryExecution(CallableBatchExecution.class);
    }

    public CallableBatchExecution getLastBatchCallable() {
        return getLastQueryExecution(CallableBatchExecution.class);
    }


    @SuppressWarnings("unchecked")
    private <T extends QueryExecution> List<T> getQueryExecutionsFilteredBy(Class<T> classToFilter) {
        List<T> result = new ArrayList<>();
        for (QueryExecution queryExecution : getQueryExecutions()) {
            if (classToFilter.isAssignableFrom(queryExecution.getClass())) {
                result.add((T) queryExecution);
            }
        }
        return result;
    }

    private <T extends QueryExecution> T getFirstQueryExecution(Class<T> classToFilter) {
        List<T> filtered = getQueryExecutionsFilteredBy(classToFilter);
        if (filtered.isEmpty()) {
            return null;
        }
        return filtered.get(0);
    }

    private <T extends QueryExecution> T getLastQueryExecution(Class<T> classToFilter) {
        List<T> filtered = getQueryExecutionsFilteredBy(classToFilter);
        if (filtered.isEmpty()) {
            return null;
        }
        return filtered.get(filtered.size() - 1);
    }

    public List<QueryExecution> getQueryExecutions() {
        return this.queryExecutionFactoryListener.getQueryExecutions();
    }

    public QueryExecutionFactoryListener getQueryExecutionFactoryListener() {
        return this.queryExecutionFactoryListener;
    }
}
src/main/java/net/ttddyy/dsproxy/asserts/StatementExecution.java
public class StatementExecution extends BaseQueryExecution implements QueryHolder {

    private String query;

    @Override
    public boolean isBatch() {
        return false;
    }

    @Override
    public String getQuery() {
        return this.query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

}
src/main/java/net/ttddyy/dsproxy/asserts/CallableBatchExecution.java
public class CallableBatchExecution extends BaseQueryExecution implements BatchParameterHolder, QueryHolder, BatchExecution {

    private String query;

    private List<BatchExecutionEntry> batchExecutionEntries = new ArrayList<>();

    @Override
    public boolean isBatch() {
        return true;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    @Override
    public String getQuery() {
        return this.query;
    }

    @Override
    public List<BatchExecutionEntry> getBatchExecutionEntries() {
        return this.batchExecutionEntries;
    }

    public boolean addBatchExecutionEntry(CallableBatchExecutionEntry entry) {
        return this.batchExecutionEntries.add(entry);
    }

}
src/main/java/net/ttddyy/dsproxy/asserts/CallableExecution.java
public class CallableExecution extends BaseQueryExecution implements QueryHolder, ParameterByIndexHolder, ParameterByNameHolder, OutParameterHolder {

    private String query;
    private SortedSet<ParameterKeyValue> parameters = new TreeSet<>();


    @Override
    public boolean isBatch() {
        return false;
    }

    @Override
    public String getQuery() {
        return this.query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    @Override
    public SortedSet<ParameterKeyValue> getAllParameters() {
        return this.parameters;
    }

    @Override
    public SortedSet<ParameterKeyValue> getSetParams() {
        return filterBy(this.parameters, ParameterKeyValue.OperationType.SET_PARAM);
    }

    @Override
    public SortedSet<ParameterKeyValue> getSetNullParams() {
        return filterBy(this.parameters, ParameterKeyValue.OperationType.SET_NULL);
    }

    @Override
    public SortedSet<ParameterKeyValue> getOutParams() {
        return filterBy(this.parameters, ParameterKeyValue.OperationType.REGISTER_OUT);
    }

    @Override
    public Map<String, Object> getSetParamsByName() {
        return toKeyNameMap(filterByKeyType(getSetParams(), ParameterKey.ParameterKeyType.BY_NAME));
    }

    @Override
    public Map<Integer, Object> getSetParamsByIndex() {
        return toKeyIndexMap(filterByKeyType(getSetParams(), ParameterKey.ParameterKeyType.BY_INDEX));
    }

    @Override
    public Map<String, Integer> getSetNullParamsByName() {
        return toKeyNameMap(filterByKeyType(getSetNullParams(), ParameterKey.ParameterKeyType.BY_NAME));
    }

    @Override
    public Map<Integer, Integer> getSetNullParamsByIndex() {
        return toKeyIndexMap(filterByKeyType(getSetNullParams(), ParameterKey.ParameterKeyType.BY_INDEX));
    }

    @Override
    public Map<String, Object> getOutParamsByName() {
        return toKeyNameMap(filterByKeyType(getOutParams(), ParameterKey.ParameterKeyType.BY_NAME));
    }

    @Override
    public Map<Integer, Object> getOutParamsByIndex() {
        return toKeyIndexMap(filterByKeyType(getOutParams(), ParameterKey.ParameterKeyType.BY_INDEX));
    }

    @Override
    public List<String> getParamNames() {
        List<String> names = new ArrayList<>();
        names.addAll(getSetParamsByName().keySet());
        names.addAll(getSetNullParamsByName().keySet());
        return names;
    }

    @Override
    public List<Integer> getParamIndexes() {
        List<Integer> indexes = new ArrayList<>();
        indexes.addAll(getSetParamsByIndex().keySet());
        indexes.addAll(getSetNullParamsByIndex().keySet());
        return indexes;
    }

    @Override
    public List<String> getOutParamNames() {
        return new ArrayList<>(getOutParamsByName().keySet());
    }

    @Override
    public List<Integer> getOutParamIndexes() {
        return new ArrayList<>(getOutParamsByIndex().keySet());
    }

}


Question: import net.ttddyy.dsproxy.QueryType;
import net.ttddyy.dsproxy.asserts.BatchParameterHolder;
import net.ttddyy.dsproxy.asserts.CallableBatchExecution;
import net.ttddyy.dsproxy.asserts.CallableExecution;
import net.ttddyy.dsproxy.asserts.ParameterHolder;
import net.ttddyy.dsproxy.asserts.PreparedBatchExecution;
import net.ttddyy.dsproxy.asserts.PreparedExecution;
import net.ttddyy.dsproxy.asserts.ProxyTestDataSource;
import net.ttddyy.dsproxy.asserts.QueriesHolder;
import net.ttddyy.dsproxy.asserts.QueryExecution;
import net.ttddyy.dsproxy.asserts.QueryHolder;
import net.ttddyy.dsproxy.asserts.StatementBatchExecution;
import net.ttddyy.dsproxy.asserts.StatementExecution;
import org.hamcrest.Matcher;
import java.math.BigDecimal;
import java.sql.Array;
import java.sql.Date;
import java.sql.SQLType;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Map;

    /**
     * Matcher to examine the query type.
     *
     * Example:
     * <pre> assertThat(qe, queryType(SELECT)); </pre>
     *
     * @param expectedType expected {@link QueryType}
     * @return matcher for {@link QueryHolder}
     */
    public static Matcher<? super QueryHolder> queryType(QueryType expectedType) {
        return QueryHolderAssertions.queryType(expectedType);
    }

    /**
     * Matcher to examine the query type is SELECT.
     *
     * Example:
     * <pre> assertThat(qe, select()); </pre>
     *
     * @return matcher for {@link QueryHolder}
     */
    public static Matcher<? super QueryHolder> select() {
        return QueryHolderAssertions.select();
    }

    /**
     * Matcher to examine the query type is SELECT.
     *
     * Example:
     * <pre> assertThat(qe, insert()); </pre>
     *
     * @return matcher for {@link QueryHolder}
     */
    public static Matcher<? super QueryHolder> insert() {
        return QueryHolderAssertions.insert();
    }

    /**
     * Matcher to examine the query type is SELECT.
     *
     * Example:
     * <pre> assertThat(qe, update()); </pre>
     *
     * @return matcher for {@link QueryHolder}
     */
    public static Matcher<? super QueryHolder> update() {
        return QueryHolderAssertions.update();
    }

    /**
     * Matcher to examine the query type is SELECT.
     *
     * Example:
     * <pre> assertThat(qe, delete()); </pre>
     *
     * @return matcher for {@link QueryHolder}
     */
    public static Matcher<? super QueryHolder> delete() {
        return QueryHolderAssertions.delete();
    }

    /**
     * Matcher to examine the query type is SELECT.
     *
     * Example:
     * <pre> assertThat(qe, other()); </pre>
     *
     * @return matcher for {@link QueryHolder}
     */
    public static Matcher<? super QueryHolder> other() {
        return QueryHolderAssertions.other();
    }

    /////////////////////////////////////////////////////////////////////////////
    // QueriesHolderAssertions
    /////////////////////////////////////////////////////////////////////////////

    public static Matcher<? super QueriesHolder> queries(int index, Matcher<String> stringMatcher) {
        return QueriesHolderAssertions.queries(index, stringMatcher);
    }

    public static Matcher<? super QueriesHolder> queries(
            Matcher<? super Collection<String>> collectionMatcher) {
        return QueriesHolderAssertions.queries(collectionMatcher);
    }

    public static Matcher<? super QueriesHolder> queryTypes(int index,
            Matcher<? super QueryHolder> queryHolderMatcher) {
        return QueriesHolderAssertions.queryTypes(index, queryHolderMatcher);
    }

    /////////////////////////////////////////////////////////////////////////////
    // BatchParameterHolderAssertions
    /////////////////////////////////////////////////////////////////////////////

    /**
     * Matcher to check the batch size.
     *
     * Example:
     * <pre> assertThat(ds.getBatchStatements(), batchSize(3)); </pre>
     *
     * @param batchSize batch size
     * @return matcher for {@link BatchParameterHolder}
     */
    public static Matcher<? super BatchParameterHolder> batchSize(int batchSize) {
        return BatchParameterHolderAssertions.batchSize(batchSize);
    }

    /**
     * Matcher to check the given index in batch matches with given parameter matcher.
     *
     * Example:
     * <pre> assertThat(ds.getBatchStatements(), batch(0, param(1, String.class, is("FOO")))); </pre>
     *
     * @param index index
     * @param parameterHolderMatcher matcher for {@link ParameterHolder}
     * @return matcher for {@link BatchParameterHolder}
     */
    public static Matcher<? super BatchParameterHolder> batch(int index,
Answer:             Matcher<? super ParameterHolder> parameterHolderMatcher) {