📄 cachedresultset.java
字号:
//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.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.23 2005/06/15 14:56:57 alin_sinpalean 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();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -