cachedresultset.java

来自「jtds的源码 是你学习java的好东西」· Java 代码 · 共 1,448 行 · 第 1/4 页

JAVA
1,448
字号
//jTDS JDBC Driver for Microsoft SQL Server and Sybase
//Copyright (C) 2004 The jTDS Project
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
package net.sourceforge.jtds.jdbc;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashSet;

/**
 * A memory cached scrollable/updateable result set.
 * <p/>
 * Notes:
 * <ol>
 *   <li>For maximum performance use the scroll insensitive result set type.
 *   <li>As the result set is cached in memory this implementation is limited
 *     to small result sets.
 *   <li>Updateable or scroll sensitive result sets are limited to selects
 *     which reference one table only.
 *   <li>Scroll sensitive result sets must have primary keys.
 *   <li>Updates are optimistic. To guard against lost updates it is
 *     recommended that the table includes a timestamp column.
 *   <li>This class is a plug-in replacement for the MSCursorResultSet class
 *     which may be advantageous in certain applications as the scroll
 *     insensitive result set implemented here is much faster than the server
 *     side cursor.
 *   <li>Updateable result sets cannot be built from the output of stored
 *     procedures.
 *   <li>This implementation uses 'select ... for browse' to obtain the column
 *     meta data needed to generate update statements etc.
 *   <li>Named forward updateable cursors are also supported in which case
 *     positioned updates and deletes are used referencing a server side
 *     declared cursor.
 *   <li>Named forward read only declared cursors can have a larger fetch size
 *     specified allowing a cursor alternative to the default direct select
 *     method.
 * </ol>
 *
 * @author Mike Hutchinson
 * @version $Id: CachedResultSet.java,v 1.26 2007/07/08 17:28:23 bheineman Exp $
 * @todo Should add a "close statement" flag to the constructors
 */
public class CachedResultSet extends JtdsResultSet {

    /** Indicates currently inserting. */
    protected boolean onInsertRow;
    /** Buffer row used for inserts. */
    protected ParamInfo[] insertRow;
    /** The "update" row. */
    protected ParamInfo[] updateRow;
    // FIXME Remember if the row was updated/deleted for each row in the ResultSet
    /** Indicates that row has been updated. */
    protected boolean rowUpdated;
    /** Indicates that row has been deleted. */
    protected boolean rowDeleted;
    /** The row count of the initial result set. */
    protected int initialRowCnt;
    /** True if this is a local temporary result set. */
    protected final boolean tempResultSet;
    /** Cursor TdsCore object. */
    protected final TdsCore cursorTds;
    /** Updates TdsCore object used for positioned updates. */
    protected final TdsCore updateTds;
    /** Flag to indicate Sybase. */
    protected boolean isSybase;
    /** Fetch size has been changed. */
    protected boolean sizeChanged;
    /** Original SQL statement. */
    protected String sql;
    /** Original procedure name. */
    protected final String procName;
    /** Original parameters. */
    protected final ParamInfo[] procedureParams;
    /** Table is keyed. */
    protected boolean isKeyed;
    /** First table name in select. */
    protected String tableName;
    /** The parent connection object */
    protected ConnectionJDBC2 connection;

    /**
     * Constructs a new cached result set.
     * <p/>
     * This result set will either be cached in memory or, if the cursor name
     * is set, can be a forward only server side cursor. This latter form of
     * cursor can also support positioned updates.
     *
     * @param statement       the parent statement object
     * @param sql             the SQL statement used to build the result set
     * @param procName        an optional stored procedure name
     * @param procedureParams parameters for prepared statements
     * @param resultSetType   the result set type eg scrollable
     * @param concurrency     the result set concurrency eg updateable
     * @exception SQLException if an error occurs
     */
    CachedResultSet(JtdsStatement statement,
            String sql,
            String procName,
            ParamInfo[] procedureParams,
            int resultSetType,
            int concurrency) throws SQLException {
        super(statement, resultSetType, concurrency, null);
        this.connection = (ConnectionJDBC2) statement.getConnection();
        this.cursorTds = statement.getTds();
        this.sql = sql;
        this.procName = procName;
        this.procedureParams = procedureParams;
        if (resultSetType == ResultSet.TYPE_FORWARD_ONLY
                && concurrency != ResultSet.CONCUR_READ_ONLY
                && cursorName != null) {
            // Need an addtional TDS for positioned updates
            this.updateTds = new TdsCore(connection, statement.getMessages());
        } else {
            this.updateTds = this.cursorTds;
        }
        this.isSybase = Driver.SYBASE == connection.getServerType();
        this.tempResultSet = false;
        //
        // Now create the specified type of cursor
        //
        cursorCreate();
    }

    /**
     * Constructs a cached result set based on locally generated data.
     *
     * @param statement the parent statement object
     * @param colName   array of column names
     * @param colType   array of corresponding data types
     * @exception SQLException if an error occurs
     */
    CachedResultSet(JtdsStatement statement,
                    String[] colName, int[] colType) throws SQLException {
        super(statement, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE, null);
        //
        // Construct the column descriptor array
        //
        this.columns = new ColInfo[colName.length];
        for (int i = 0; i < colName.length; i++) {
            ColInfo ci = new ColInfo();
            ci.name     = colName[i];
            ci.realName = colName[i];
            ci.jdbcType = colType[i];
            ci.isCaseSensitive = false;
            ci.isIdentity = false;
            ci.isWriteable = false;
            ci.nullable = 2;
            ci.scale = 0;
            TdsData.fillInType(ci);
            columns[i] = ci;
        }
        this.columnCount   = getColumnCount(columns);
        this.rowData       = new ArrayList(INITIAL_ROW_COUNT);
        this.rowsInResult  = 0;
        this.initialRowCnt = 0;
        this.pos           = POS_BEFORE_FIRST;
        this.tempResultSet = true;
        this.cursorName    = null;
        this.cursorTds     = null;
        this.updateTds     = null;
        this.procName      = null;
        this.procedureParams = null;
    }

    /**
     * Creates a cached result set with the same columns (and optionally data)
     * as an existing result set.
     *
     * @param rs   the result set to copy
     * @param load load data from the supplied result set
     * @throws SQLException if an error occurs
     */
    CachedResultSet(JtdsResultSet rs, boolean load) throws SQLException {
        super((JtdsStatement)rs.getStatement(),
                rs.getStatement().getResultSetType(),
                rs.getStatement().getResultSetConcurrency(), null);
        //
        JtdsStatement stmt = ((JtdsStatement) rs.getStatement());
        //
        // OK If the user requested an updateable result set tell them
        // they can't have one!
        //
        if (concurrency != ResultSet.CONCUR_READ_ONLY) {
            concurrency = ResultSet.CONCUR_READ_ONLY;
            stmt.addWarning(new SQLWarning(
                Messages.get("warning.cursordowngraded",
                             "CONCUR_READ_ONLY"), "01000"));
        }
        //
        // If the user requested a scroll sensitive cursor tell them
        // they can't have that either!
        //
        if (resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE) {
            resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
            stmt.addWarning(new SQLWarning(
                Messages.get("warning.cursordowngraded",
                             "TYPE_SCROLL_INSENSITIVE"), "01000"));
        }

        this.columns       = rs.getColumns();
        this.columnCount   = getColumnCount(columns);
        this.rowData       = new ArrayList(INITIAL_ROW_COUNT);
        this.rowsInResult  = 0;
        this.initialRowCnt = 0;
        this.pos           = POS_BEFORE_FIRST;
        this.tempResultSet = true;
        this.cursorName    = null;
        this.cursorTds     = null;
        this.updateTds     = null;
        this.procName      = null;
        this.procedureParams = null;
        //
        // Load result set into buffer
        //
        if (load) {
            while (rs.next()) {
                rowData.add(copyRow(rs.getCurrentRow()));
            }
            this.rowsInResult  = rowData.size();
            this.initialRowCnt = rowsInResult;
        }
    }

    /**
     * Creates a cached result set containing one row.
     *
     * @param statement the parent statement object
     * @param columns   the column descriptor array
     * @param data      the row data
     * @throws SQLException if an error occurs
     */
    CachedResultSet(JtdsStatement statement,
            ColInfo columns[], Object data[]) throws SQLException {
        super(statement, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, null);
        this.columns       = columns;
        this.columnCount   = getColumnCount(columns);
        this.rowData       = new ArrayList(1);
        this.rowsInResult  = 1;
        this.initialRowCnt = 1;
        this.pos           = POS_BEFORE_FIRST;
        this.tempResultSet = true;
        this.cursorName    = null;
        this.rowData.add(copyRow(data));
        this.cursorTds     = null;
        this.updateTds     = null;
        this.procName      = null;
        this.procedureParams = null;
    }

    /**
     * Modify the concurrency of the result set.
     * <p/>
     * Use to make result set read only once loaded.
     *
     * @param concurrency the concurrency value eg
     *                    <code>ResultSet.CONCUR_READ_ONLY</code>
     */
    void setConcurrency(int concurrency) {
        this.concurrency = concurrency;
    }

    /**
     * Creates a new scrollable result set in memory or a named server cursor.
     *
     * @exception SQLException if an error occurs
     */
    private void cursorCreate()
            throws SQLException {
        //
        boolean isSelect = false;
        int requestedConcurrency = concurrency;
        int requestedType = resultSetType;

        //
        // If the useCursor property is set we will try and use a server
        // side cursor for forward read only cursors. With the default
        // fetch size of 100 this is a reasonable emulation of the
        // MS fast forward cursor.
        //
        if (cursorName == null
                && connection.getUseCursors()
                && resultSetType == ResultSet.TYPE_FORWARD_ONLY
                && concurrency == ResultSet.CONCUR_READ_ONLY) {
        	// The useCursors connection property was set true
        	// so we need to create a private cursor name
        	cursorName = connection.getCursorName();
        }
        //
        // Validate the SQL statement to ensure we have a select.
        //
        if (resultSetType != ResultSet.TYPE_FORWARD_ONLY
            || concurrency != ResultSet.CONCUR_READ_ONLY
            || cursorName != null) {
            //
            // We are going to need access to a SELECT statement for
            // this to work. Reparse the SQL now and check.
            //
            String tmp[] = SQLParser.parse(sql, new ArrayList(),
                    (ConnectionJDBC2) statement.getConnection(), true);

            if ("select".equals(tmp[2])) {
                isSelect = true;
            	if (tmp[3] != null && tmp[3].length() > 0) {
            		// OK We have a select with at least one table.
            		tableName = tmp[3];
            	} else {
            		// Can't find a table name so can't update
                    concurrency = ResultSet.CONCUR_READ_ONLY;
            	}
            } else {
                // No good we can't update and we can't declare a cursor
                cursorName = null;
                concurrency = ResultSet.CONCUR_READ_ONLY;
                if (resultSetType != ResultSet.TYPE_FORWARD_ONLY) {
                    resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
                }
            }
        }
        //
        // If a cursor name is specified we try and declare a conventional cursor.
        // A server error will occur if we try to create a named cursor on a non
        // select statement.
        //
        if (cursorName != null) {
            //
            // Create and execute DECLARE CURSOR
            //
            StringBuffer cursorSQL =
                    new StringBuffer(sql.length() + cursorName.length()+ 128);
            cursorSQL.append("DECLARE ").append(cursorName)
                    .append(" CURSOR FOR ");
            //
            // We need to adjust any parameter offsets now as the prepended
            // DECLARE CURSOR will throw the parameter positions off.
            //
            ParamInfo[] parameters = procedureParams;
            if (procedureParams != null && procedureParams.length > 0) {
                parameters = new ParamInfo[procedureParams.length];
                int offset = cursorSQL.length();
                for (int i = 0; i < parameters.length; i++) {
                    // Clone parameters to avoid corrupting offsets in original
                    parameters[i] = (ParamInfo) procedureParams[i].clone();
                    parameters[i].markerPos += offset;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?