Java tutorial
/** * Copyright The Apache Software Foundation * * 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 com.alibaba.wasp.jdbc; import com.alibaba.wasp.FConstants; import com.alibaba.wasp.SQLErrorCode; import com.alibaba.wasp.jdbc.command.CommandInterface; import com.alibaba.wasp.jdbc.result.JdbcResultSet; import com.alibaba.wasp.jdbc.result.ResultInterface; import com.alibaba.wasp.session.ExecuteSession; import com.alibaba.wasp.session.SessionFactory; import com.alibaba.wasp.util.New; import org.apache.commons.lang.NotImplementedException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Statement; import java.util.ArrayList; /** * Represents a statement. */ public class JdbcStatement implements Statement { private Log log = LogFactory.getLog(JdbcStatement.class); protected JdbcConnection conn; protected ExecuteSession session; protected JdbcResultSet resultSet; protected int maxRows; protected int fetchSize; protected int updateCount; public final int resultSetType; protected final int resultSetConcurrency; protected final boolean closedByResultSet; private int lastExecutedCommandType; private ArrayList<String> batchCommands; private Configuration conf; private boolean autoCommit; JdbcStatement(JdbcConnection conn, int resultSetType, int resultSetConcurrency, boolean closeWithResultSet) { this.conn = conn; this.session = SessionFactory.createExecuteSession(); this.resultSetType = resultSetType; this.resultSetConcurrency = resultSetConcurrency; this.closedByResultSet = closeWithResultSet; this.conf = conn.getConf(); try { this.autoCommit = conn.getAutoCommit(); } catch (SQLException e) { //TODO } this.fetchSize = conf.getInt(FConstants.WASP_JDBC_FETCHSIZE, FConstants.DEFAULT_WASP_JDBC_FETCHSIZE); } /** * Executes a query (select statement) and returns the result set. If another * result set exists for this statement, this will be closed (even if this * statement fails). * * @param sql * the SQL statement to execute * @return the result set */ @Override public ResultSet executeQuery(String sql) throws SQLException { synchronized (session) { checkClosed(); closeOldResultSet(); CommandInterface command = conn.prepareCommand(sql, fetchSize, session); ResultInterface result = null; setExecutingStatement(command); try { result = command.executeQuery(maxRows); session.setSessionId(result.getSessionId()); } finally { setExecutingStatement(null); } command.close(); resultSet = new JdbcResultSet(conn, this, result, closedByResultSet); } return resultSet; } /** * Executes a statement (insert, update, delete, create, drop) and returns the * update count. If another result set exists for this statement, this will be * closed (even if this statement fails). * * If auto commit is on, this statement will be committed. If the statement is * a DDL statement (create, drop, alter) and does not throw an exception, the * current transaction (if any) is committed after executing the statement. * * @param sql * the SQL statement * @return the update count (number of row affected by an insert, update or * delete, or 0 if no rows or the statement was a create, drop, commit * or rollback) * @throws java.sql.SQLException * if a database error occurred or a select statement was executed */ @Override public int executeUpdate(String sql) throws SQLException { return executeUpdateInternal(sql); } /** * Executes an arbitrary statement. If another result set exists for this * statement, this will be closed (even if this statement fails). * * If the statement is a create or drop and does not throw an exception, the * current transaction (if any) is committed after executing the statement. If * auto commit is on, and the statement is not a select, this statement will * be committed. * * @param sql * the SQL statement to execute * @return true if a result set is available, false if not */ @Override public boolean execute(String sql) throws SQLException { try { return executeInternal(sql); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Returns the last result set produces by this statement. * * @return the result set */ @Override public ResultSet getResultSet() throws SQLException { checkClosed(); return resultSet; } /** * Returns the last update count of this statement. * * @return the update count (number of row affected by an insert, update or * delete, or 0 if no rows or the statement was a create, drop, commit * or rollback; -1 if the statement was a select). * @throws java.sql.SQLException * if this object is closed or invalid */ @Override public int getUpdateCount() throws SQLException { checkClosed(); return updateCount; } /** * Closes this statement. All result sets that where created by this statement * become invalid after calling this method. */ @Override public void close() throws SQLException { try { synchronized (session) { closeOldResultSet(); if (conn != null) { conn = null; } } } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Returns the connection that created this object. * * @return the connection */ @Override public Connection getConnection() { return conn; } /** * Gets the first warning reported by calls on this object. This driver does * not support warnings, and will always return null. * * @return null */ @Override public SQLWarning getWarnings() throws SQLException { try { checkClosed(); return null; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Clears all warnings. As this driver does not support warnings, this call is * ignored. */ @Override public void clearWarnings() throws SQLException { try { checkClosed(); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Sets the name of the cursor. This call is ignored. * * @param name * ignored * @throws java.sql.SQLException * if this object is closed */ @Override public void setCursorName(String name) throws SQLException { try { checkClosed(); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Sets the fetch direction. This call is ignored by this driver. * * @param direction * ignored * @throws java.sql.SQLException * if this object is closed */ @Override public void setFetchDirection(int direction) throws SQLException { try { checkClosed(); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Gets the fetch direction. * * @return FETCH_FORWARD * @throws java.sql.SQLException * if this object is closed */ @Override public int getFetchDirection() throws SQLException { try { checkClosed(); return ResultSet.FETCH_FORWARD; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Gets the maximum number of rows for a ResultSet. * * @return the number of rows where 0 means no limit * @throws java.sql.SQLException * if this object is closed */ @Override public int getMaxRows() throws SQLException { try { checkClosed(); return maxRows; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Gets the maximum number of rows for a ResultSet. * * @param maxRows * the number of rows where 0 means no limit * @throws java.sql.SQLException * if this object is closed */ @Override public void setMaxRows(int maxRows) throws SQLException { try { checkClosed(); if (maxRows < 0) { throw JdbcException.getInvalidValueException("maxRows", maxRows); } this.maxRows = maxRows; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Sets the number of rows suggested to read in one step. This value cannot be * higher than the maximum rows (setMaxRows) set by the statement or prepared * statement, otherwise an exception is throws. Setting the value to 0 will * set the default value. The default value can be changed using the system * property h2.serverResultSetFetchSize. * * @param rows * the number of rows * @throws java.sql.SQLException * if this object is closed */ @Override public void setFetchSize(int rows) throws SQLException { try { checkClosed(); if (rows < 0 || (rows > 0 && maxRows > 0 && rows > maxRows)) { throw JdbcException.getInvalidValueException("rows", rows); } if (rows == 0) { rows = conf.getInt(FConstants.WASP_JDBC_FETCHSIZE, FConstants.DEFAULT_WASP_JDBC_FETCHSIZE); } fetchSize = rows; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Gets the number of rows suggested to read in one step. * * @return the current fetch size * @throws java.sql.SQLException * if this object is closed */ @Override public int getFetchSize() throws SQLException { try { checkClosed(); return fetchSize; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Gets the result set concurrency created by this object. * * @return the concurrency */ @Override public int getResultSetConcurrency() throws SQLException { try { checkClosed(); return resultSetConcurrency; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Gets the result set type. * * @return the type * @throws java.sql.SQLException * if this object is closed */ @Override public int getResultSetType() throws SQLException { try { checkClosed(); return resultSetType; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Gets the maximum number of bytes for a result set column. * * @return always 0 for no limit * @throws java.sql.SQLException * if this object is closed */ @Override public int getMaxFieldSize() throws SQLException { try { checkClosed(); return 0; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Sets the maximum number of bytes for a result set column. This method does * currently do nothing for this driver. * * @param max * the maximum size - ignored * @throws java.sql.SQLException * if this object is closed */ @Override public void setMaxFieldSize(int max) throws SQLException { try { checkClosed(); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Enables or disables processing or JDBC escape syntax. See also * Connection.nativeSQL. * * @param enable * - true (default) or false (no conversion is attempted) * @throws java.sql.SQLException * if this object is closed */ @Override public void setEscapeProcessing(boolean enable) throws SQLException { try { checkClosed(); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Cancels a currently running statement. This method must be called from * within another thread than the execute method. Operations on large objects * are not interrupted, only operations that process many rows. * * @throws java.sql.SQLException * if this object is closed */ @Override public void cancel() throws SQLException { throw JdbcException.getUnsupportedException("cancel"); } /** * Gets the current query timeout in seconds. This method will return 0 if no * query timeout is set. The result is rounded to the next second. For * performance reasons, only the first call to this method will query the * database. If the query timeout was changed in another way than calling * setQueryTimeout, this method will always return the last value. * * @return the timeout in seconds * @throws java.sql.SQLException * if this object is closed */ @Override public int getQueryTimeout() throws SQLException { try { checkClosed(); return conn.getQueryTimeout(); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Sets the current query timeout in seconds. Changing the value will affect * all statements of this connection. This method does not commit a * transaction, and rolling back a transaction does not affect this setting. * * @param seconds * the timeout in seconds - 0 means no timeout, values smaller 0 will * throw an exception * @throws java.sql.SQLException * if this object is closed */ @Override public void setQueryTimeout(int seconds) throws SQLException { try { checkClosed(); if (seconds < 0) { throw JdbcException.getInvalidValueException("seconds", seconds); } conn.setQueryTimeout(seconds); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Adds a statement to the batch. * * @param sql * the SQL statement */ @Override public void addBatch(String sql) throws SQLException { try { checkClosed(); if (batchCommands == null) { batchCommands = New.arrayList(); } batchCommands.add(sql); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Clears the batch. */ @Override public void clearBatch() throws SQLException { checkClosed(); batchCommands = null; } /** * Executes the batch. If one of the batched statements fails, this database * will continue. * * @return the array of update counts */ @Override public int[] executeBatch() throws SQLException { try { checkClosedForWrite(); try { if (batchCommands == null) { batchCommands = New.arrayList(); } int size = batchCommands.size(); int[] result = new int[size]; if (autoCommit) { throw new SQLException("batch only support without autoCommit mode"); } closeOldResultSet(); CommandInterface command = conn.prepareCommand(batchCommands, session); synchronized (session) { setExecutingStatement(command); try { updateCount = command.executeTransaction(); } finally { setExecutingStatement(null); } } command.close(); batchCommands = null; //wasp ensures all success or all failure for (int i = 0; i < result.length; i++) { result[i] = (updateCount == size) ? 1 : 0; } return result; } finally { afterWriting(); } } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Return a result set that contains the last generated auto-increment key for * this connection, if there was one. If no key was generated by the last * modification statement, then an empty result set is returned. The returned * result set only contains the data for the very last row. * * @return the result set with one row and one column containing the key * @throws java.sql.SQLException * if this object is closed */ @Override public ResultSet getGeneratedKeys() throws SQLException { throw JdbcException.getUnsupportedException("getGeneratedKeys"); } /** * Moves to the next result set - however there is always only one result set. * This call also closes the current result set (if there is one). Returns * true if there is a next result set (that means - it always returns false). * * @return false * @throws java.sql.SQLException * if this object is closed. */ @Override public boolean getMoreResults() throws SQLException { try { checkClosed(); closeOldResultSet(); return false; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Move to the next result set. This method always returns false. * * @param current * Statement.CLOSE_CURRENT_RESULT, Statement.KEEP_CURRENT_RESULT, or * Statement.CLOSE_ALL_RESULTS * @return false */ @Override public boolean getMoreResults(int current) throws SQLException { try { switch (current) { case Statement.CLOSE_CURRENT_RESULT: case Statement.CLOSE_ALL_RESULTS: checkClosed(); closeOldResultSet(); break; case Statement.KEEP_CURRENT_RESULT: // nothing to do break; default: throw JdbcException.getInvalidValueException("current", current); } return false; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Executes a statement and returns the update count. This method just calls * executeUpdate(String sql) internally. The method getGeneratedKeys supports * at most one columns and row. * * @param sql * the SQL statement * @param autoGeneratedKeys * ignored * @return the update count (number of row affected by an insert, update or * delete, or 0 if no rows or the statement was a create, drop, commit * or rollback) * @throws java.sql.SQLException * if a database error occurred or a select statement was executed */ @Override public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { try { return executeUpdateInternal(sql); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Executes a statement and returns the update count. This method just calls * executeUpdate(String sql) internally. The method getGeneratedKeys supports * at most one columns and row. * * @param sql * the SQL statement * @param columnIndexes * ignored * @return the update count (number of row affected by an insert, update or * delete, or 0 if no rows or the statement was a create, drop, commit * or rollback) * @throws java.sql.SQLException * if a database error occurred or a select statement was executed */ @Override public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { try { return executeUpdateInternal(sql); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Executes a statement and returns the update count. This method just calls * executeUpdate(String sql) internally. The method getGeneratedKeys supports * at most one columns and row. * * @param sql * the SQL statement * @param columnNames * ignored * @return the update count (number of row affected by an insert, update or * delete, or 0 if no rows or the statement was a create, drop, commit * or rollback) * @throws java.sql.SQLException * if a database error occurred or a select statement was executed */ @Override public int executeUpdate(String sql, String[] columnNames) throws SQLException { try { return executeUpdateInternal(sql); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } private int executeUpdateInternal(String sql) throws SQLException { checkClosedForWrite(); try { closeOldResultSet(); CommandInterface command = conn.prepareCommand(sql, fetchSize, session); synchronized (session) { setExecutingStatement(command); try { updateCount = command.executeUpdate(); } finally { setExecutingStatement(null); } } command.close(); return updateCount; } finally { afterWriting(); } } /** * Executes a statement and returns the update count. This method just calls * execute(String sql) internally. The method getGeneratedKeys supports at * most one columns and row. * * @param sql * the SQL statement * @param autoGeneratedKeys * ignored * @return the update count (number of row affected by an insert, update or * delete, or 0 if no rows or the statement was a create, drop, commit * or rollback) * @throws java.sql.SQLException * if a database error occurred or a select statement was executed */ @Override public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { try { return executeInternal(sql); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } private boolean executeInternal(String sql) throws SQLException { checkClosedForWrite(); try { closeOldResultSet(); CommandInterface command = conn.prepareCommand(sql, fetchSize, session); boolean returnsResultSet; synchronized (session) { setExecutingStatement(command); try { if (command.isQuery()) { returnsResultSet = true; ResultInterface result = command.executeQuery(maxRows); resultSet = new JdbcResultSet(conn, this, result, closedByResultSet); } else { returnsResultSet = false; updateCount = command.executeUpdate(); } } finally { setExecutingStatement(null); } } command.close(); return returnsResultSet; } finally { afterWriting(); } } /** * Executes a statement and returns the update count. This method just calls * execute(String sql) internally. The method getGeneratedKeys supports at * most one columns and row. * * @param sql * the SQL statement * @param columnIndexes * ignored * @return the update count (number of row affected by an insert, update or * delete, or 0 if no rows or the statement was a create, drop, commit * or rollback) * @throws java.sql.SQLException * if a database error occurred or a select statement was executed */ @Override public boolean execute(String sql, int[] columnIndexes) throws SQLException { try { return executeInternal(sql); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Executes a statement and returns the update count. This method just calls * execute(String sql) internally. The method getGeneratedKeys supports at * most one columns and row. * * @param sql * the SQL statement * @param columnNames * ignored * @return the update count (number of row affected by an insert, update or * delete, or 0 if no rows or the statement was a create, drop, commit * or rollback) * @throws java.sql.SQLException * if a database error occurred or a select statement was executed */ @Override public boolean execute(String sql, String[] columnNames) throws SQLException { try { return executeInternal(sql); } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Gets the result set holdability. * * @return the holdability */ @Override public int getResultSetHoldability() throws SQLException { try { checkClosed(); return ResultSet.HOLD_CURSORS_OVER_COMMIT; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * Check if this connection is closed. The next operation is a read request. * * @return true if the session was re-connected * @throws JdbcException * if the connection or session is closed */ public boolean checkClosed() { return checkClosed(false); } /** * Check if this connection is closed. The next operation may be a write * request. * * @return true if the session was re-connected * @throws JdbcException * if the connection or session is closed */ boolean checkClosedForWrite() { return checkClosed(true); } /** * INTERNAL. Check if the statement is closed. * * @param write * if the next operation is possibly writing * @return true if a reconnect was required * @throws JdbcException * if it is closed */ protected boolean checkClosed(boolean write) { if (conn == null) { throw JdbcException.get(SQLErrorCode.OBJECT_CLOSED); } session.checkClosed(); conn.checkClosed(write); return session.isClosed(); } /** * Called after each write operation. */ void afterWriting() { if (conn != null) { conn.afterWriting(); } } /** * INTERNAL. Close and old result set if there is still one open. */ protected void closeOldResultSet() throws SQLException { try { if (!closedByResultSet) { if (resultSet != null) { resultSet.closeInternal(); } } } finally { resultSet = null; updateCount = -1; } } /** * INTERNAL. Get the command type of the last executed command. */ public int getLastExecutedCommandType() { return lastExecutedCommandType; } /** * Returns whether this statement is closed. * * @return true if the statement is closed */ @Override public boolean isClosed() throws SQLException { try { return conn == null; } catch (Exception e) { throw Logger.logAndConvert(log, e); } } /** * [Not supported] Return an object of this class if possible. */ @Override public <T> T unwrap(Class<T> iface) throws SQLException { throw JdbcException.getUnsupportedException("unwrap"); } /** * [Not supported] Checks if unwrap can return an object of this class. */ @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { throw JdbcException.getUnsupportedException("isWrapperFor"); } /** * Returns whether this object is poolable. * * @return false */ @Override public boolean isPoolable() { return false; } /** * Requests that this object should be pooled or not. This call is ignored. * * @param poolable * the requested value */ @Override public void setPoolable(boolean poolable) { } /** * INTERNAL. Set the statement that is currently running. * * @param c * the command */ protected void setExecutingStatement(CommandInterface c) { if (c == null) { conn.setExecutingStatement(null); } else { conn.setExecutingStatement(this); lastExecutedCommandType = c.getCommandType(); } } /** * INTERNAL */ @Override public String toString() { return ""; } public void closeOnCompletion() throws SQLException { throw new NotImplementedException(); } public boolean isCloseOnCompletion() throws SQLException { throw new NotImplementedException(); } }