/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.commons.dbcp2;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

A base delegating implementation of Statement.

All of the methods from the Statement interface simply check to see that the Statement is active, and call the corresponding method on the "delegate" provided in my constructor.

Extends AbandonedTrace to implement Statement tracking and logging of code which created the Statement. Tracking the Statement ensures that the Connection which created it can close any open Statement's on Connection close.

Since:2.0
/** * A base delegating implementation of {@link Statement}. * <p> * All of the methods from the {@link Statement} interface simply check to see that the {@link Statement} is active, and * call the corresponding method on the "delegate" provided in my constructor. * <p> * Extends AbandonedTrace to implement Statement tracking and logging of code which created the Statement. Tracking the * Statement ensures that the Connection which created it can close any open Statement's on Connection close. * * @since 2.0 */
public class DelegatingStatement extends AbandonedTrace implements Statement {
My delegate.
/** My delegate. */
private Statement statement;
The connection that created me.
/** The connection that created me. **/
private DelegatingConnection<?> connection; private boolean closed = false;
Create a wrapper for the Statement which traces this Statement to the Connection which created it and the code which created it.
Params:
/** * Create a wrapper for the Statement which traces this Statement to the Connection which created it and the code * which created it. * * @param statement * the {@link Statement} to delegate all calls to. * @param connection * the {@link DelegatingConnection} that created this statement. */
public DelegatingStatement(final DelegatingConnection<?> connection, final Statement statement) { super(connection); this.statement = statement; this.connection = connection; }
Throws:
  • SQLException – thrown by the delegating statement.
Since:2.4.0 made public, was protected in 2.3.0.
/** * * @throws SQLException * thrown by the delegating statement. * @since 2.4.0 made public, was protected in 2.3.0. */
public void activate() throws SQLException { if (statement instanceof DelegatingStatement) { ((DelegatingStatement) statement).activate(); } } @Override public void addBatch(final String sql) throws SQLException { checkOpen(); try { statement.addBatch(sql); } catch (final SQLException e) { handleException(e); } } @Override public void cancel() throws SQLException { checkOpen(); try { statement.cancel(); } catch (final SQLException e) { handleException(e); } } protected void checkOpen() throws SQLException { if (isClosed()) { throw new SQLException(this.getClass().getName() + " with address: \"" + this.toString() + "\" is closed."); } } @Override public void clearBatch() throws SQLException { checkOpen(); try { statement.clearBatch(); } catch (final SQLException e) { handleException(e); } } @Override public void clearWarnings() throws SQLException { checkOpen(); try { statement.clearWarnings(); } catch (final SQLException e) { handleException(e); } }
Close this DelegatingStatement, and close any ResultSets that were not explicitly closed.
/** * Close this DelegatingStatement, and close any ResultSets that were not explicitly closed. */
@Override public void close() throws SQLException { if (isClosed()) { return; } final List<Exception> thrown = new ArrayList<>(); try { if (connection != null) { connection.removeTrace(this); connection = null; } // The JDBC spec requires that a statement close any open // ResultSet's when it is closed. // FIXME The PreparedStatement we're wrapping should handle this for us. // See bug 17301 for what could happen when ResultSets are closed twice. final List<AbandonedTrace> resultSetList = getTrace(); if (resultSetList != null) { final int size = resultSetList.size(); final ResultSet[] resultSets = resultSetList.toArray(new ResultSet[size]); for (final ResultSet resultSet : resultSets) { if (resultSet != null) { try { resultSet.close(); } catch (Exception e) { if (connection != null) { // Does not rethrow e. connection.handleExceptionNoThrow(e); } thrown.add(e); } } } clearTrace(); } if (statement != null) { try { statement.close(); } catch (Exception e) { if (connection != null) { // Does not rethrow e. connection.handleExceptionNoThrow(e); } thrown.add(e); } } } finally { closed = true; statement = null; if (!thrown.isEmpty()) { throw new SQLExceptionList(thrown); } } } @Override public void closeOnCompletion() throws SQLException { checkOpen(); try { Jdbc41Bridge.closeOnCompletion(statement); } catch (final SQLException e) { handleException(e); } } @Override public boolean execute(final String sql) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.execute(sql); } catch (final SQLException e) { handleException(e); return false; } } @Override public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.execute(sql, autoGeneratedKeys); } catch (final SQLException e) { handleException(e); return false; } } @Override public boolean execute(final String sql, final int columnIndexes[]) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.execute(sql, columnIndexes); } catch (final SQLException e) { handleException(e); return false; } } @Override public boolean execute(final String sql, final String columnNames[]) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.execute(sql, columnNames); } catch (final SQLException e) { handleException(e); return false; } } @Override public int[] executeBatch() throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.executeBatch(); } catch (final SQLException e) { handleException(e); throw new AssertionError(); } }
Since:2.5.0
/** * @since 2.5.0 */
@Override public long[] executeLargeBatch() throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.executeLargeBatch(); } catch (final SQLException e) { handleException(e); return null; } }
Since:2.5.0
/** * @since 2.5.0 */
@Override public long executeLargeUpdate(final String sql) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.executeLargeUpdate(sql); } catch (final SQLException e) { handleException(e); return 0; } }
Since:2.5.0
/** * @since 2.5.0 */
@Override public long executeLargeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.executeLargeUpdate(sql, autoGeneratedKeys); } catch (final SQLException e) { handleException(e); return 0; } }
Since:2.5.0
/** * @since 2.5.0 */
@Override public long executeLargeUpdate(final String sql, final int[] columnIndexes) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.executeLargeUpdate(sql, columnIndexes); } catch (final SQLException e) { handleException(e); return 0; } }
Since:2.5.0
/** * @since 2.5.0 */
@Override public long executeLargeUpdate(final String sql, final String[] columnNames) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.executeLargeUpdate(sql, columnNames); } catch (final SQLException e) { handleException(e); return 0; } } @Override public ResultSet executeQuery(final String sql) throws SQLException { checkOpen(); setLastUsedInParent(); try { return DelegatingResultSet.wrapResultSet(this, statement.executeQuery(sql)); } catch (final SQLException e) { handleException(e); throw new AssertionError(); } } @Override public int executeUpdate(final String sql) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.executeUpdate(sql); } catch (final SQLException e) { handleException(e); return 0; } } @Override public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.executeUpdate(sql, autoGeneratedKeys); } catch (final SQLException e) { handleException(e); return 0; } } @Override public int executeUpdate(final String sql, final int columnIndexes[]) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.executeUpdate(sql, columnIndexes); } catch (final SQLException e) { handleException(e); return 0; } } @Override public int executeUpdate(final String sql, final String columnNames[]) throws SQLException { checkOpen(); setLastUsedInParent(); try { return statement.executeUpdate(sql, columnNames); } catch (final SQLException e) { handleException(e); return 0; } } @Override protected void finalize() throws Throwable { // This is required because of statement pooling. The poolable // statements will always be strongly held by the statement pool. If the // delegating statements that wrap the poolable statement are not // strongly held they will be garbage collected but at that point the // poolable statements need to be returned to the pool else there will // be a leak of statements from the pool. Closing this statement will // close all the wrapped statements and return any poolable statements // to the pool. close(); super.finalize(); } @Override public Connection getConnection() throws SQLException { checkOpen(); return getConnectionInternal(); // return the delegating connection that created this } protected DelegatingConnection<?> getConnectionInternal() { return connection; }
Returns my underlying Statement.
See Also:
Returns:my underlying Statement.
/** * Returns my underlying {@link Statement}. * * @return my underlying {@link Statement}. * @see #getInnermostDelegate */
public Statement getDelegate() { return statement; } @Override public int getFetchDirection() throws SQLException { checkOpen(); try { return statement.getFetchDirection(); } catch (final SQLException e) { handleException(e); return 0; } } @Override public int getFetchSize() throws SQLException { checkOpen(); try { return statement.getFetchSize(); } catch (final SQLException e) { handleException(e); return 0; } } @Override public ResultSet getGeneratedKeys() throws SQLException { checkOpen(); try { return DelegatingResultSet.wrapResultSet(this, statement.getGeneratedKeys()); } catch (final SQLException e) { handleException(e); throw new AssertionError(); } }
If my underlying Statement is not a DelegatingStatement, returns it, otherwise recursively invokes this method on my delegate.

Hence this method will return the first delegate that is not a DelegatingStatement or null when no non-DelegatingStatement delegate can be found by traversing this chain.

This method is useful when you may have nested DelegatingStatements, and you want to make sure to obtain a "genuine" Statement.

See Also:
Returns:The innermost delegate.
/** * If my underlying {@link Statement} is not a {@code DelegatingStatement}, returns it, otherwise recursively * invokes this method on my delegate. * <p> * Hence this method will return the first delegate that is not a {@code DelegatingStatement} or {@code null} when * no non-{@code DelegatingStatement} delegate can be found by traversing this chain. * </p> * <p> * This method is useful when you may have nested {@code DelegatingStatement}s, and you want to make sure to obtain * a "genuine" {@link Statement}. * </p> * * @return The innermost delegate. * * @see #getDelegate */
@SuppressWarnings("resource") public Statement getInnermostDelegate() { Statement s = statement; while (s != null && s instanceof DelegatingStatement) { s = ((DelegatingStatement) s).getDelegate(); if (this == s) { return null; } } return s; }
Since:2.5.0
/** * @since 2.5.0 */
@Override public long getLargeMaxRows() throws SQLException { checkOpen(); try { return statement.getLargeMaxRows(); } catch (final SQLException e) { handleException(e); return 0; } }
Since:2.5.0
/** * @since 2.5.0 */
@Override public long getLargeUpdateCount() throws SQLException { checkOpen(); try { return statement.getLargeUpdateCount(); } catch (final SQLException e) { handleException(e); return 0; } } @Override public int getMaxFieldSize() throws SQLException { checkOpen(); try { return statement.getMaxFieldSize(); } catch (final SQLException e) { handleException(e); return 0; } } @Override public int getMaxRows() throws SQLException { checkOpen(); try { return statement.getMaxRows(); } catch (final SQLException e) { handleException(e); return 0; } } @Override public boolean getMoreResults() throws SQLException { checkOpen(); try { return statement.getMoreResults(); } catch (final SQLException e) { handleException(e); return false; } } @Override public boolean getMoreResults(final int current) throws SQLException { checkOpen(); try { return statement.getMoreResults(current); } catch (final SQLException e) { handleException(e); return false; } } @Override public int getQueryTimeout() throws SQLException { checkOpen(); try { return statement.getQueryTimeout(); } catch (final SQLException e) { handleException(e); return 0; } } @Override public ResultSet getResultSet() throws SQLException { checkOpen(); try { return DelegatingResultSet.wrapResultSet(this, statement.getResultSet()); } catch (final SQLException e) { handleException(e); throw new AssertionError(); } } @Override public int getResultSetConcurrency() throws SQLException { checkOpen(); try { return statement.getResultSetConcurrency(); } catch (final SQLException e) { handleException(e); return 0; } } @Override public int getResultSetHoldability() throws SQLException { checkOpen(); try { return statement.getResultSetHoldability(); } catch (final SQLException e) { handleException(e); return 0; } } @Override public int getResultSetType() throws SQLException { checkOpen(); try { return statement.getResultSetType(); } catch (final SQLException e) { handleException(e); return 0; } } @Override public int getUpdateCount() throws SQLException { checkOpen(); try { return statement.getUpdateCount(); } catch (final SQLException e) { handleException(e); return 0; } } @Override public SQLWarning getWarnings() throws SQLException { checkOpen(); try { return statement.getWarnings(); } catch (final SQLException e) { handleException(e); throw new AssertionError(); } } protected void handleException(final SQLException e) throws SQLException { if (connection != null) { connection.handleException(e); } else { throw e; } } /* * Note: This method was protected prior to JDBC 4. */ @Override public boolean isClosed() throws SQLException { return closed; } protected boolean isClosedInternal() { return closed; } @Override public boolean isCloseOnCompletion() throws SQLException { checkOpen(); try { return Jdbc41Bridge.isCloseOnCompletion(statement); } catch (final SQLException e) { handleException(e); return false; } } @Override public boolean isPoolable() throws SQLException { checkOpen(); try { return statement.isPoolable(); } catch (final SQLException e) { handleException(e); return false; } } @Override public boolean isWrapperFor(final Class<?> iface) throws SQLException { if (iface.isAssignableFrom(getClass())) { return true; } else if (iface.isAssignableFrom(statement.getClass())) { return true; } else { return statement.isWrapperFor(iface); } }
Throws:
  • SQLException – thrown by the delegating statement.
Since:2.4.0 made public, was protected in 2.3.0.
/** * * @throws SQLException * thrown by the delegating statement. * @since 2.4.0 made public, was protected in 2.3.0. */
public void passivate() throws SQLException { if (statement instanceof DelegatingStatement) { ((DelegatingStatement) statement).passivate(); } } protected void setClosedInternal(final boolean closed) { this.closed = closed; } @Override public void setCursorName(final String name) throws SQLException { checkOpen(); try { statement.setCursorName(name); } catch (final SQLException e) { handleException(e); } }
Sets my delegate.
Params:
  • statement – my delegate.
/** * Sets my delegate. * * @param statement * my delegate. */
public void setDelegate(final Statement statement) { this.statement = statement; } @Override public void setEscapeProcessing(final boolean enable) throws SQLException { checkOpen(); try { statement.setEscapeProcessing(enable); } catch (final SQLException e) { handleException(e); } } @Override public void setFetchDirection(final int direction) throws SQLException { checkOpen(); try { statement.setFetchDirection(direction); } catch (final SQLException e) { handleException(e); } } @Override public void setFetchSize(final int rows) throws SQLException { checkOpen(); try { statement.setFetchSize(rows); } catch (final SQLException e) { handleException(e); } }
Since:2.5.0
/** * @since 2.5.0 */
@Override public void setLargeMaxRows(final long max) throws SQLException { checkOpen(); try { statement.setLargeMaxRows(max); } catch (final SQLException e) { handleException(e); } } private void setLastUsedInParent() { if (connection != null) { connection.setLastUsed(); } } @Override public void setMaxFieldSize(final int max) throws SQLException { checkOpen(); try { statement.setMaxFieldSize(max); } catch (final SQLException e) { handleException(e); } } @Override public void setMaxRows(final int max) throws SQLException { checkOpen(); try { statement.setMaxRows(max); } catch (final SQLException e) { handleException(e); } } @Override public void setPoolable(final boolean poolable) throws SQLException { checkOpen(); try { statement.setPoolable(poolable); } catch (final SQLException e) { handleException(e); } } @Override public void setQueryTimeout(final int seconds) throws SQLException { checkOpen(); try { statement.setQueryTimeout(seconds); } catch (final SQLException e) { handleException(e); } }
Returns a String representation of this object.
Returns:String
/** * Returns a String representation of this object. * * @return String */
@Override public synchronized String toString() { return statement == null ? "NULL" : statement.toString(); } @Override public <T> T unwrap(final Class<T> iface) throws SQLException { if (iface.isAssignableFrom(getClass())) { return iface.cast(this); } else if (iface.isAssignableFrom(statement.getClass())) { return iface.cast(statement); } else { return statement.unwrap(iface); } } }