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

scriptella.jdbc.StatementWrapper Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2006-2012 The Scriptella Project Team.
 *
 * 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 scriptella.jdbc;

import scriptella.spi.ParametersCallback;
import scriptella.spi.QueryCallback;
import scriptella.util.ExceptionUtils;
import scriptella.util.IOUtils;

import java.io.Closeable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Abstraction for {@link java.sql.Statement} and {@link java.sql.PreparedStatement}.
 *
 * @author Fyodor Kupolov
 * @version 1.0
 */
abstract class StatementWrapper implements Closeable {
    private static final Logger LOG = Logger.getLogger(StatementWrapper.class.getName());
    protected final JdbcTypesConverter converter;
    protected final T statement;

    /**
     * For testing only.
     */
    protected StatementWrapper() {
        converter = null;
        statement = null;
    }

    protected StatementWrapper(T statement, JdbcTypesConverter converter) {
        if (statement == null) {
            throw new IllegalArgumentException("statement cannot be null");
        }
        if (converter == null) {
            throw new IllegalArgumentException("converter cannot be null");
        }
        this.statement = statement;
        this.converter = converter;
    }

    /**
     * Release any resources opened by this statement.
     */
    public void close() {
        JdbcUtils.closeSilent(statement);
    }


    /**
     * Executes the given SQL statement, which may be an INSERT, UPDATE, or DELETE statement
     * or an SQL statement that returns nothing, such as an SQL DDL statement.
     *
     * @return either the row count for INSERT, UPDATE, or DELETE statements or 0 for SQL statements that return nothing.
     * @throws SQLException if JDBC driver fails to execute the operation.
     */
    public abstract int update() throws SQLException;

    /**
     * Executes the query and returns the result set.
     *
     * @return result set with query result.
     * @throws SQLException if JDBC driver fails to execute the operation.
     */
    protected abstract ResultSet query() throws SQLException;

    public void query(final QueryCallback queryCallback, final ParametersCallback parametersCallback) throws SQLException {
        ResultSetAdapter r = null;
        try {
            r = new ResultSetAdapter(query(), parametersCallback, converter);
            while (r.next()) {
                queryCallback.processRow(r);
            }
        } finally {
            IOUtils.closeSilently(r);
        }
    }

    public void setParameters(final List params) throws SQLException {
    }

    /**
     * Clears any transient state variables, e.g. statement parameters etc.
     */
    public void clear() {
    }

    /**
     * Flushes any pending operations.
     *
     * @return number of rows updated
     * @throws SQLException if DB error occurs.
     */
    public int flush() throws SQLException {
        return 0;
    }

    /**
     * @see java.sql.Statement#toString()
     */
    public String toString() {
        return statement.toString();
    }

    /**
     * Helper method for executing a batch.
     *
     * @param statement statement to execute.
     * @return sum of update counts for all commands.
     * @throws SQLException if error occurs
     */
    protected static int executeBatch(Statement statement) throws SQLException {
        int result = 0;
        int[] results = statement.executeBatch();
        for (int r : results) {
            if (r > 0) {
                result += r;
            }
        }
        if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("Batch of " + results.length + " statements executed.");
        }
        return result;
    }


    /**
     * {@link Statement} wrapper.
     */
    static class Simple extends StatementWrapper {
        protected final String sql;

        /**
         * For testing only.
         */
        protected Simple(String sql) {
            this.sql = sql;
        }

        public Simple(Statement s, String sql, JdbcTypesConverter converter) {
            super(s, converter);
            this.sql = sql;
        }

        @Override
        public int update() throws SQLException {
            return statement.executeUpdate(sql);
        }

        @Override
        protected ResultSet query() throws SQLException {
            return statement.executeQuery(sql);
        }

    }

    /**
     * {@link PreparedStatement} wrapper.
     */
    static class Prepared extends StatementWrapper {
        /**
         * For testing only.
         */
        protected Prepared() {
        }

        public Prepared(PreparedStatement s, JdbcTypesConverter converter) {
            super(s, converter);
        }

        /**
         * Sets parameters for this statement.
         * 

Default implementation is noop. * * @param params parameters to set. * @throws SQLException */ @Override public void setParameters(List params) throws SQLException { for (int i = 0, n = params.size(); i < n; i++) { Object o = params.get(i); converter.setObject(statement, i + 1, o); } } @Override public int update() throws SQLException { try { return statement.executeUpdate(); } finally { converter.close(); //Disposing converter } } @Override protected ResultSet query() throws SQLException { return statement.executeQuery(); } @Override public void clear() { try { statement.clearParameters(); } catch (SQLException e) { ExceptionUtils.ignoreThrowable(e); } } } /** * {@link StatementWrapper} for batching. *

This instance is intended to be shared per ETL element. * To overcome this and additional method {@link #setSql(String)} must be called prior to calling update. */ static class Batched extends StatementWrapper { private int maxBatchSize; private int currentBatchSize; private String sql; /** * For testing only. */ protected Batched() { } public Batched(Statement s, JdbcTypesConverter converter, int maxBatchSize) { super(s, converter); this.maxBatchSize = maxBatchSize; } public void setSql(String sql) { this.sql = sql; } @Override public int update() throws SQLException { statement.addBatch(sql); currentBatchSize++; int result = 0; if (currentBatchSize >= maxBatchSize) { result = executeBatch(); } return result; } /** * Executes current batch. * * @return number of rows affected. * @throws SQLException if error occurs */ protected int executeBatch() throws SQLException { try { return executeBatch(statement); } finally { currentBatchSize = 0; converter.close(); //Disposing converter } } @Override protected ResultSet query() throws SQLException { //In a very unlikely case when the same SQL was used for batch updates flush(); return statement.executeQuery(sql); } @Override public void clear() { this.sql = null; } @Override public int flush() throws SQLException { if (currentBatchSize > 0) { return executeBatch(); } return 0; } @Override public void close() { super.close(); } } /** * {@link Prepared} for batching. */ static class BatchedPrepared extends Prepared { private int maxBatchSize; private int currentBatchSize; /** * For testing only. */ protected BatchedPrepared() { } public BatchedPrepared(PreparedStatement s, JdbcTypesConverter converter, int maxBatchSize) { super(s, converter); this.maxBatchSize = maxBatchSize; } @Override public int update() throws SQLException { statement.addBatch(); currentBatchSize++; int result = 0; if (currentBatchSize >= maxBatchSize) { result = executeBatch(); } return result; } /** * Executes current batch. * * @return number of rows affected. * @throws SQLException if error occurs */ protected int executeBatch() throws SQLException { try { return executeBatch(statement); } finally { currentBatchSize = 0; converter.close(); //Disposing converter } } @Override public void clear() { //Do not clear parameters, until the batch is sent } @Override public int flush() throws SQLException { if (currentBatchSize > 0) { return executeBatch(); } return 0; } @Override protected ResultSet query() throws SQLException { //In a very unlikely case when the same SQL was used for batch updates flush(); return super.query(); } @Override public void close() { super.close(); } } }