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 + -
显示快捷键?