All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javaxt.sql.Recordset Maven / Gradle / Ivy

package javaxt.sql;

import java.sql.ResultSet;
import java.sql.SQLException;

//******************************************************************************
//**  Recordset Class
//*****************************************************************************/
/**
 * Used to query and update records in a database.
 *
 ******************************************************************************/

public class Recordset {

	private java.sql.ResultSet rs = null;
	private java.sql.Connection Conn = null;
	private java.sql.Statement stmt = null;
	private int x;
	private boolean isReadOnly = true;
	private String sqlString = null;
	// private Parser sqlParser = null;

	private Connection Connection = null;
	private Driver driver = null;
	private boolean autoCommit = true;

	private Value GeneratedKey;

	/**
	 * Returns a value that describes if the Recordset object is open, closed,
	 * connecting, executing or retrieving data
	 */
	public int State = 0;

	/**
	 * Returns true if the current record position is after the last record,
	 * otherwise false.
	 */
	public boolean EOF = false;

	/**
	 * An array of fields. Each field contains information about a column in a
	 * Recordset object. There is one Field object for each column in the
	 * Recordset.
	 */
	private Field[] Fields = null;

	/**
	 * Sets or returns the maximum number of records to return to a Recordset
	 * object from a query.
	 */
	public int MaxRecords = 1000000000;

	/**
	 * Returns the number of records in a Recordset object. This property is a
	 * bit unreliable. Recommend using the getRecordCount() method instead.
	 */
	public int RecordCount;

	/**
	 * Returns the time it took to execute a given query. Units are in
	 * milliseconds
	 */
	public long QueryResponseTime;

	/**
	 * Returns the total elapsed time between open and close operations. Units
	 * are in milliseconds
	 */
	public long EllapsedTime;

	/**
	 * Returns the elapsed time it took to retrieve additional metadata not
	 * correctly supported by the jdbc driver. Units are in milliseconds.
	 */
	public long MetadataQueryTime;
	private long startTime, endTime;

	// **************************************************************************
	// ** Constructor
	// **************************************************************************
	/** Creates a new instance of this class. */

	public Recordset() {
	}

	// **************************************************************************
	// ** isOpen
	// **************************************************************************
	/**
	 * Returns true if the recordset is open. This method is only supported on
	 * Java 1.6 or higher. Otherwise, the method will return false.
	 */
	public boolean isOpen() {
		if (State != 0) {
			// return !rs.isClosed();

			String[] arr = System.getProperty("java.version").split("\\.");
			if (Integer.valueOf(arr[0]).intValue() == 1 && Integer.valueOf(arr[1]).intValue() < 6)
				return false;
			else {
				try {
					return !((Boolean) rs.getClass().getMethod("isClosed").invoke(rs, null));
				} catch (Exception e) {
					return false;
				}
			}
		} else
			return false;
	}

	// **************************************************************************
	// ** isReadOnly
	// **************************************************************************
	/** Returns true if records are read-only. */

	public boolean isReadOnly() {
		return isReadOnly;
	}

	// **************************************************************************
	// ** Open
	// **************************************************************************
	/**
	 * Used to execute a query and access records in the database. Records
	 * fetched using this method cannot be updated or deleted and new records
	 * cannot be inserted into the database.
	 *
	 * @param sql
	 *            SQL Query. Example: "SELECT * FROM EMPLOYEE"
	 * @param conn
	 *            An active connection to the database.
	 */
	public java.sql.ResultSet open(String sql, Connection conn) throws SQLException {
		return open(sql, conn, true);
	}

	// **************************************************************************
	// ** Open
	// **************************************************************************
	/**
	 * Used to execute a query and access records in the database.
	 *
	 * @param sqlString
	 *            SQL Query. Example: "SELECT * FROM EMPLOYEE"
	 * @param Connection
	 *            An active connection to the database.
	 * @param ReadOnly
	 *            Set whether the records are read-only. If true, records
	 *            fetched using this method cannot be updated or deleted and new
	 *            records cannot be inserted into the database. If false,
	 *            records can be updated or deleted and new records can be
	 *            inserted into the database.
	 */
	public java.sql.ResultSet open(String sqlString, Connection Connection, boolean ReadOnly) throws SQLException {

		rs = null;
		stmt = null;
		State = 0;
		EOF = true;
		this.sqlString = sqlString;
		this.Connection = Connection;
		this.isReadOnly = ReadOnly;
		this.driver = Connection.getDatabase().getDriver();
		if (driver == null)
			driver = new Driver("", "", "");

		if (Connection == null)
			throw new java.sql.SQLException("Connection is null.");
		if (Connection.isClosed())
			throw new java.sql.SQLException("Connection is closed.");

		startTime = System.currentTimeMillis();
		Conn = Connection.getConnection();
		autoCommit = Conn.getAutoCommit();

		// Wrap table and column names in quotes (Special Case for PostgreSQL)
		/*
		 * if (driver.equals("PostgreSQL")){ try{ Parser sqlParser = new
		 * Parser(sqlString); boolean wrapElements = false; String[]
		 * exposedElements = sqlParser.getExposedDataElements(); for (int i=0;
		 * i 0)
			this.fetchSize = fetchSize;
	}

	// **************************************************************************
	// ** getConnection
	// **************************************************************************
	/** Returns the JDBC Connection used to create/open the recordset. */

	public Connection getConnection() {
		return Connection;
	}

	// **************************************************************************
	// ** Commit
	// **************************************************************************
	/**
	 * Used to explicitely commit an sql statement. May be useful for bulk
	 * update and update statements, depending on the underlying DBMS.
	 */
	public void commit() {
		try {
			// stmt.executeQuery("COMMIT");
			Conn.commit();
		} catch (Exception e) {
			// System.out.println(e.toString());
		}
	}

	private boolean InsertOnUpdate = false;

	// **************************************************************************
	// ** AddNew
	// **************************************************************************
	/**
	 * Used to prepare the driver to insert new records to the database. Used in
	 * conjunction with the update method.
	 */
	public void addNew() {
		if (State == 1) {
			InsertOnUpdate = true;
			for (int i = 1; i <= Fields.length; i++) {
				Field Field = Fields[i - 1];
				Field.Value = null;
				Field.RequiresUpdate = false;
			}
		}
	}

	// **************************************************************************
	// ** Update
	// **************************************************************************
	/**
	 * Used to add or update a record in a table. Note that inserts can be
	 * batched using the setBatch() method to improve performance. When
	 * performing batch inserts, the update statements are queued and executed
	 * only after the batch size is reached.
	 */
	public void update() throws java.sql.SQLException {
		if (isReadOnly)
			throw new java.sql.SQLException("Read only!");
		if (State != 1)
			throw new java.sql.SQLException("Recordset is closed!");
		if (!isDirty())
			return;

		// Generate list of fields that require updates
		java.util.ArrayList fields = new java.util.ArrayList();
		for (Field field : Fields) {
			if (field.getName() != null && field.RequiresUpdate)
				fields.add(field);
		}
		int numUpdates = fields.size();

		// Get table name
		String tableName = Fields[0].getTable();
		if (tableName == null) {
			updateFields();
			tableName = Fields[0].getTable();
		}
		if (tableName.contains(" "))
			tableName = "[" + tableName + "]";

		// Construct a SQL insert/update statement
		StringBuffer sql = new StringBuffer();
		if (InsertOnUpdate) {
			sql.append("INSERT INTO " + tableName + " (");
			for (int i = 0; i < numUpdates; i++) {
				String colName = escape(fields.get(i).getName());
				sql.append(colName);
				if (numUpdates > 1 && i < numUpdates - 1) {
					sql.append(",");
				}
			}
			sql.append(") VALUES (");
			for (int i = 0; i < numUpdates; i++) {
				if (i > 0)
					sql.append(",");
				sql.append(getQ(fields.get(i)));
			}
			sql.append(")");
		} else {
			sql.append("UPDATE " + tableName + " SET ");
			for (int i = 0; i < numUpdates; i++) {
				String colName = escape(fields.get(i).getName());
				sql.append(colName);
				sql.append("=");
				sql.append(getQ(fields.get(i)));
				if (numUpdates > 1 && i < numUpdates - 1) {
					sql.append(", ");
				}
			}

			// Find primary key for the table. This slows things down
			// quite a bit but we need it for the "where" clause.
			java.util.ArrayList keys = new java.util.ArrayList();
			try {
				java.sql.DatabaseMetaData dbmd = Conn.getMetaData();
				java.sql.ResultSet r2 = dbmd.getTables(null, null, Fields[0].getTable(), new String[] { "TABLE" });
				if (r2.next()) {
					Table table = new Table(r2, dbmd);
					Key[] arr = table.getPrimaryKeys();
					if (arr != null) {
						for (int i = 0; i < arr.length; i++) {
							Key key = arr[i];
							Field field = getField(key.getColumn());
							if (field != null)
								keys.add(field);
						}
					}
				}
				r2.close();
			} catch (Exception e) {
			}

			// Build the where clause
			if (!keys.isEmpty()) {
				sql.append(" WHERE ");
				for (int i = 0; i < keys.size(); i++) {
					Field field = keys.get(i);
					fields.add(field);
					if (i > 0)
						sql.append(" AND ");
					String colName = escape(field.getName());
					sql.append(colName);
					sql.append("=?");
				}
			} else {

				// Since we don't have any keys, use the original where clause
				String where = new Parser(this.sqlString).getWhereString();
				if (where != null) {
					sql.append(" WHERE ");
					sql.append(where);
				}

				// Find how many records will be affected by this update
				int numRecords;
				java.sql.ResultSet r2 = stmt
				        .executeQuery("SELECT COUNT(*) FROM " + tableName + (where == null ? "" : " WHERE " + where));
				try {
					numRecords = r2.getInt(1);
				} catch (Exception e) {
					try {
						r2.first(); // SQLServer needs this!
						numRecords = r2.getInt(1);
					} catch (Exception ex) {
						numRecords = Integer.MAX_VALUE;
					}
				}
				r2.close();

				// Warn user that there might be a problem with the update
				if (numRecords > 1) {
					StringBuffer msg = new StringBuffer();
					msg.append("WARNING: Updating " + tableName + " table without a unique key.\r\n");
					msg.append("Multiple rows may be affected with this update.\r\n");
					try {
						int x = 1 / 0;
					} catch (Exception e) {
						java.io.ByteArrayOutputStream bas = new java.io.ByteArrayOutputStream();
						java.io.PrintStream s = new java.io.PrintStream(bas, true);
						e.printStackTrace(s);
						s.close();
						boolean append = false;
						for (String line : bas.toString().split("\n")) {
							if (append) {
								msg.append("\t");
								msg.append(line.trim());
								msg.append("\r\n");
							}
							if (!append && line.contains(this.getClass().getCanonicalName()))
								append = true;
						}
						System.err.println(msg);
					}
				}
			}
		}

		// Get prepared statement
		java.sql.PreparedStatement stmt;
		if (batchSize > 1) {
			if (batchedStatements == null)
				batchedStatements = new java.util.HashMap();
			stmt = batchedStatements.get(sql.toString());
			if (stmt == null) {
				stmt = Conn.prepareStatement(sql.toString());
				batchedStatements.put(sql.toString(), stmt);
				Conn.setAutoCommit(false);
			}
		} else {
			stmt = Conn.prepareStatement(sql.toString(), java.sql.Statement.RETURN_GENERATED_KEYS);
		}

		// Set values using a prepared statement
		int id = 1;
		for (int i = 0; i < fields.size(); i++) {

			Field field = fields.get(i);
			String FieldType = field.Class.toLowerCase();
			if (FieldType.contains("."))
				FieldType = FieldType.substring(FieldType.lastIndexOf(".") + 1);
			Value FieldValue = field.getValue();

			// Special case for SQL Functions
			if (FieldValue.toObject() instanceof Function) {
				Function function = (Function) FieldValue.toObject();
				if (function.hasValues()) {
					for (Object obj : function.getValues()) {
						stmt.setObject(id, obj);
						id++;
					}
				} else {
					// Do nothing!
				}
				continue; // Prevent the id from incrementing
			}

			if (FieldType.indexOf("string") >= 0)
				stmt.setString(id, FieldValue.toString());

			else if (FieldType.indexOf("int") >= 0) {
				Integer val = FieldValue.toInteger();
				if (val == null)
					stmt.setNull(id, java.sql.Types.INTEGER);
				else
					stmt.setInt(id, val);
			}

			else if (FieldType.indexOf("short") >= 0) {
				Short val = FieldValue.toShort();
				if (val == null)
					stmt.setNull(id, java.sql.Types.SMALLINT);
				else
					stmt.setShort(id, val);
			}

			else if (FieldType.indexOf("long") >= 0) {
				Long val = FieldValue.toLong();
				if (val == null)
					stmt.setNull(id, java.sql.Types.BIGINT);
				else
					stmt.setLong(id, val);
			}

			else if (FieldType.indexOf("double") >= 0) {
				Double val = FieldValue.toDouble();
				if (val == null)
					stmt.setNull(id, java.sql.Types.DOUBLE);
				else
					stmt.setDouble(id, val);
			}

			else if (FieldType.indexOf("float") >= 0) {
				Float val = FieldValue.toFloat();
				if (val == null)
					stmt.setNull(id, java.sql.Types.FLOAT);
				else
					stmt.setFloat(id, val);
			}

			else if (FieldType.indexOf("bool") >= 0) {
				Boolean val = FieldValue.toBoolean();
				if (val == null)
					stmt.setNull(id, java.sql.Types.BIT);
				else
					stmt.setBoolean(id, val);
			}

			else if (FieldType.indexOf("decimal") >= 0)
				stmt.setBigDecimal(id, FieldValue.toBigDecimal());

			else if (FieldType.indexOf("timestamp") >= 0)
				stmt.setTimestamp(id, FieldValue.toTimeStamp());

			else if (FieldType.indexOf("date") >= 0)
				stmt.setDate(id, new java.sql.Date(FieldValue.toDate().getTime()));

			else if (FieldType.indexOf("object") >= 0)
				stmt.setObject(id, FieldValue.toObject());

			else if (FieldType.indexOf("map") >= 0) // PostgreSQL HStore
				stmt.setObject(id, FieldValue.toString(), java.sql.Types.OTHER);

			else {
				// System.out.println(i + " " + field.getName() + " " +
				// FieldType);
				stmt.setObject(id, FieldValue.toObject());
			}

			id++;
		}

		// Update
		if (batchSize == 1) {
			try {
				stmt.executeUpdate();
			} catch (SQLException e) {

				StringBuffer err = new StringBuffer();
				err.append("Error executing update:\n");
				err.append(sql.toString());
				err.append("\n");
				// err.append("\n Values:\n");
				for (int i = 0; i < fields.size(); i++) {
					if (i > 0)
						err.append("\n");
					Field field = fields.get(i);
					err.append("  - " + field.getName() + ": ");
					String val = field.getValue().toString();
					if (val != null && val.length() > 100)
						val = val.substring(0, 100) + "...";
					err.append(val);
				}

				e.setNextException(new SQLException(err.toString()));
				throw e;
			}

			if (InsertOnUpdate) {
				java.sql.ResultSet generatedKeys = stmt.getGeneratedKeys();
				if (generatedKeys.next()) {
					this.GeneratedKey = new Value(generatedKeys.getString(1));
				}
				InsertOnUpdate = false;
			}

		} else {
			stmt.addBatch();
			numBatches++;

			if (numBatches == batchSize) {
				executeBatch();
			}
		}
	}

	// **************************************************************************
	// ** escape
	// **************************************************************************

	private String escape(String colName) {
		String[] keywords = javaxt.sql.Database.getReservedKeywords(Connection);
		if (colName.contains(" "))
			colName = "[" + colName + "]";
		for (String keyWord : keywords) {
			if (colName.equalsIgnoreCase(keyWord)) {
				colName = "\"" + colName + "\"";
				break;
			}
		}
		return colName;
	}

	// **************************************************************************
	// ** getQ
	// **************************************************************************
	/**
	 * Returns an SQL fragment used to generate prepared statements. Typically,
	 * this method simply returns a "?". However, if the value for the field
	 * contains a function, or contains a spatial data type, additional
	 * processing is required to generate a valid SQL statement.
	 */
	private String getQ(Field field) {

		if (field == null || field.getValue().isNull())
			return "?";

		// Find out what kind of data we're dealing with
		Object value = field.getValue().toObject();

		// Special case for SQL Functions
		if (value instanceof Function) {
			Function function = (Function) value;
			return function.getFunction();
		}

		// Special case for geometry types
		java.lang.Package _package = value.getClass().getPackage();
		String packageName = _package == null ? "" : _package.getName();
		if (packageName.startsWith("javaxt.geospatial.geometry")) {
			String STGeomFromText = getSTGeomFromText(field);
			field.Value = new Value(value.toString());
			field.Class = "java.lang.String";
			return STGeomFromText + "(?,4326)";
		} else if (packageName.startsWith("com.vividsolutions.jts.geom")) {
			String STGeomFromText = getSTGeomFromText(field);
			field.Value = new Value(value.toString());
			field.Class = "java.lang.String";
			int srid = 4326; // getSRID();
			try {
				java.lang.reflect.Method method = value.getClass().getMethod("getSRID");
				if (method != null) {
					Object obj = method.invoke(value, null);
					if (obj != null) {
						srid = (Integer) obj;
						if (srid == 0)
							srid = 4326;
					}
				}
			} catch (Exception e) {
			}
			return STGeomFromText + "(?," + srid + ")";
		}

		return "?";
	}

	// **************************************************************************
	// ** getSTGeomFromText
	// **************************************************************************
	/**
	 * Returns a vendor-specific STGeomFromText fragment for inserting/updating
	 * geometry data.
	 */
	private String getSTGeomFromText(Field field) {
		if (driver.equals("SQLServer")) {
			String geo = field.Class.toLowerCase();
			if (!geo.equals("geometry") && !geo.equals("geography")) {
				geo = null;
				try {
					Recordset rs = new Recordset();
					rs.open("SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS " + "WHERE TABLE_NAME='"
					        + field.getTable() + "' AND COLUMN_NAME='" + field.getName() + "'", Connection);
					geo = rs.getValue(0).toString();
					rs.close();
				} catch (SQLException e) {
					// e.printStackTrace();
				}
				if (geo == null)
					geo = "geometry";
				else
					geo = geo.toLowerCase();
			}
			return geo + "::STGeomFromText"; // geometry vs geography
		} else if (driver.equals("DB2")) {
			return "db2GSE.ST_GeomFromText";
		}

		return "ST_GeomFromText"; // PostgreSQL
	}

	private int numBatches = 0;
	private int batchSize = 1;
	private java.util.HashMap batchedStatements;

	// **************************************************************************
	// ** setBatchSize
	// **************************************************************************
	/**
	 * Used to set the number of records to insert in a batch. By default, this
	 * value is set to 1 so that records are inserted one at a time. By setting
	 * a larger number, more records are inserted at a time which can
	 * significantly improve performance.
	 */
	public void setBatchSize(int batchSize) {
		if (batchSize > 0)
			this.batchSize = batchSize;
	}

	public int getBatchSize() {
		return batchSize;
	}

	// **************************************************************************
	// ** executeBatch
	// **************************************************************************
	/**
	 * Returns the total number of rows that were updated.
	 */
	private int executeBatch() throws java.sql.SQLException {
		if (batchedStatements == null)
			return 0;
		int ttl = 0;
		java.util.Iterator it = batchedStatements.keySet().iterator();
		while (it.hasNext()) {
			java.sql.PreparedStatement stmt = batchedStatements.get(it.next());

			int[] rowsUpdated = stmt.executeBatch();
			if (rowsUpdated.length > 0)
				ttl += rowsUpdated.length;

			if (Conn.getAutoCommit() == false) {
				Conn.commit();
			}
		}
		batchedStatements.clear();
		numBatches = 0;
		return ttl;
	}

	// **************************************************************************
	// ** getGeneratedKey
	// **************************************************************************
	/**
	 * Returns an auto-generated key created after inserting a record in the
	 * database. If this Statement object did not generate any keys, an empty
	 * Value object is returned.
	 */
	public Value getGeneratedKey() {
		return GeneratedKey;
	}

	// **************************************************************************
	// ** getFields
	// **************************************************************************
	/**
	 * Used to retrieve the an array of fields in the current record.
	 */
	public Field[] getFields() {
		return Fields;
	}

	// **************************************************************************
	// ** getField
	// **************************************************************************
	/**
	 * Returns a specific field in the array of fields. Returns null if the
	 * field name is not found.
	 */
	public Field getField(String FieldName) {
		if (Fields == null || Fields.length == 0)
			return null;

		if (FieldName == null)
			return null;
		FieldName = FieldName.trim();
		if (FieldName.length() == 0)
			return null;

		String[] arr = FieldName.split("\\.");

		for (Field field : Fields) {

			String fieldName = field.getName();
			if (fieldName == null)
				continue;

			String tableName = field.getTable() == null ? "" : field.getTable();
			String schemaName = field.getSchema() == null ? "" : field.getSchema();

			if (arr.length == 3) {
				if (fieldName.equalsIgnoreCase(arr[2]) && tableName.equalsIgnoreCase(arr[1])
				        && schemaName.equalsIgnoreCase(arr[0])) {
					return field;
				}
			} else if (arr.length == 2) {
				if (fieldName.equalsIgnoreCase(arr[1]) && tableName.equalsIgnoreCase(arr[0])) {
					return field;
				}
			} else if (arr.length == 1) {
				if (fieldName.equalsIgnoreCase(arr[0]))
					return field;
			}
		}

		return null;
	}

	// **************************************************************************
	// ** getField
	// **************************************************************************
	/**
	 * Returns a specific field in the array of fields. Returns null if the
	 * index is out of range.
	 */
	public Field getField(int i) {
		if (Fields != null && i < Fields.length) {
			return Fields[i];
		} else {
			return null;
		}
	}

	// **************************************************************************
	// ** getValue
	// **************************************************************************
	/**
	 * Returns the Value associated with a given field. Note the if the field
	 * doesn't exist in the result set, the method will return still return a
	 * Value. You can use the isNull() method on the Value to determine whether
	 * the value is null.
	 */
	public Value getValue(String FieldName) {
		Field field = getField(FieldName);
		if (field != null)
			return field.getValue();
		return new Value(null);
	}

	// **************************************************************************
	// ** getValue
	// **************************************************************************
	/**
	 * Returns the Value associated with a given field. Note the if the field
	 * doesn't exist in the result set, the method will return still return a
	 * Value. You can use the isNull() method on the Value to determine whether
	 * the value is null.
	 */
	public Value getValue(int i) {
		if (Fields != null && i < Fields.length) {
			return Fields[i].getValue();
		}
		return new Value(null);
	}

	// **************************************************************************
	// ** isDirty
	// **************************************************************************
	/**
	 * Returns true if any of the fields have been modified. You can find which
	 * field has been modified using the Field.isDirty() method. Example:
	 * 
	 * 
	 * if (rs.isDirty()) {
	 *     for (javaxt.sql.Field field : rs.getFields()) {
	 *         if (field.isDirty()) {
	 *             String val = field.getValue().toString();
	 *             System.out.println(field.getName() + ": " + val);
	 *         }
	 *     }
	 * }
	 * 
*/ public boolean isDirty() { for (Field field : Fields) { if (field.isDirty()) return true; } return false; } // ************************************************************************** // ** SetValue // ************************************************************************** public void setValue(String FieldName, Value FieldValue) { if (State == 1) { for (int i = 0; i < Fields.length; i++) { String name = Fields[i].getName(); if (name != null) { if (name.equalsIgnoreCase(FieldName)) { if (FieldValue == null) FieldValue = new Value(null); // Update the Field Value as needed. if (!Fields[i].getValue().equals(FieldValue)) { Fields[i].Value = FieldValue; Fields[i].RequiresUpdate = true; } break; } } } } } // ************************************************************************** // ** SetValue // ************************************************************************** /** Set Value with an Object value. */ public void setValue(String FieldName, Object FieldValue) { setValue(FieldName, new Value(FieldValue)); } // ************************************************************************** // ** SetValue // ************************************************************************** /** Set Value with a Boolean value */ public void setValue(String FieldName, boolean FieldValue) { setValue(FieldName, new Value(FieldValue)); } // ************************************************************************** // ** SetValue // ************************************************************************** /** Set Value with a Long value */ public void setValue(String FieldName, long FieldValue) { setValue(FieldName, new Value(FieldValue)); } // ************************************************************************** // ** SetValue // ************************************************************************** /** Set Value with an Integer value */ public void setValue(String FieldName, int FieldValue) { setValue(FieldName, new Value(FieldValue)); } // ************************************************************************** // ** SetValue // ************************************************************************** /** Set Value with a Double value */ public void setValue(String FieldName, double FieldValue) { setValue(FieldName, new Value(FieldValue)); } // ************************************************************************** // ** SetValue // ************************************************************************** /** Set Value with a Short value */ public void setValue(String FieldName, short FieldValue) { setValue(FieldName, new Value(FieldValue)); } // ************************************************************************** // ** hasNext // ************************************************************************** /** * Returns true if the recordset has more records. */ public boolean hasNext() { return !EOF; } // ************************************************************************** // ** MoveNext // ************************************************************************** /** Move the cursor to the next record in the recordset. */ public boolean moveNext() { if (EOF == true) return false; if (x >= MaxRecords - 1) { EOF = true; return false; } else { try { if (rs.next()) { for (int i = 1; i <= Fields.length; i++) { Field Field = Fields[i - 1]; Field.Value = new Value(rs.getObject(i)); Field.RequiresUpdate = false; } x += 1; return true; } else { EOF = true; return false; } } catch (Exception e) { EOF = true; return false; // System.out.println("ERROR MoveNext: " + e.toString()); } } // return false; } // ************************************************************************** // ** Move // ************************************************************************** /** * Moves the cursor to n-number of rows in the database. Typically this * method is called before iterating through a recordset. */ public void move(int numRecords) { boolean tryAgain = false; // Scroll to record using the standard absolute() method // Does NOT work with rs.TYPE_FORWARD_ONLY cursors try { rs.absolute(numRecords); x += numRecords; } catch (Exception e) { tryAgain = true; // System.err.println("ERROR Move: " + e.toString()); } // Scroll to record using an iterator // Workaround for rs.TYPE_FORWARD_ONLY cursors try { if (tryAgain) { int rowPosition = rs.getRow(); while (rs.getRow() < (numRecords + rowPosition)) { if (rs.next()) { x++; } else { EOF = true; break; } } } } catch (Exception e) { } // Update Field try { for (int i = 1; i <= Fields.length; i++) { Field Field = Fields[i - 1]; Field.Value = new Value(rs.getObject(i)); Field.RequiresUpdate = false; } } catch (Exception e) { } } // ************************************************************************** // ** updateFields // ************************************************************************** /** * Used to populate the Table and Schema attributes for each Field in the * Fields Array. */ private void updateFields() { if (Fields == null) return; // Check whether any of the fields are missing table or schema // information boolean updateFields = false; for (Field field : Fields) { // Check the field name. If it's missing, then the field is probably // derived from a function. In some cases, it may be trivial to find // the column name (e.g. COUNT, SUM, MIN, MAX, etc). In other cases, // it is not so simple (e.g. "SELECT (FIRSTNAME || ' ' || // LASTNAME)"). // In any event, this function cannot currently find table or schema // for a field without a name. String fieldName = field.getName(); if (fieldName == null) continue; if (field.getTable() == null) { updateFields = true; break; } if (field.getSchema() == null) { updateFields = true; break; } } if (!updateFields) return; // Get selected tables from the SQL String[] selectedTables = new Parser(sqlString).getTables(); // Match selected tables to tables found in this database java.util.ArrayList tables = new java.util.ArrayList
(); for (Table table : Database.getTables(Connection)) { for (String selectedTable : selectedTables) { if (selectedTable.contains(".")) selectedTable = selectedTable.substring(selectedTable.indexOf(".")); if (selectedTable.equalsIgnoreCase(table.getName())) { tables.add(table); } } } // Iterate through all the fields and update the Table and Schema // attributes for (Field field : Fields) { if (field.getTable() == null) { // Update Table and Schema Column[] columns = getColumns(field, tables); if (columns != null) { Column column = columns[0]; // <-- Need to implement logic // to field.setTableName(column.getTable().getName()); field.setSchemaName(column.getTable().getSchema()); } } if (field.getSchema() == null) { // Update Schema for (Table table : tables) { if (table.getName().equalsIgnoreCase(field.getTable())) { field.setSchemaName(table.getSchema()); break; } } } } tables.clear(); tables = null; } // ************************************************************************** // ** getColumns // ************************************************************************** /** * Used to find a column in the database that corresponds to a given field. * This method is only used when a field/column's parent table is unknown. */ private Column[] getColumns(Field field, java.util.ArrayList
tables) { java.util.ArrayList matches = new java.util.ArrayList(); for (Table table : tables) { for (Column column : table.getColumns()) { if (column.getName().equalsIgnoreCase(field.getName())) { matches.add(column); } } } if (matches.isEmpty()) return null; if (matches.size() == 1) return new Column[] { matches.get(0) }; if (matches.size() > 1) { java.util.ArrayList columns = new java.util.ArrayList(); for (Column column : matches) { if (column.getType().equalsIgnoreCase(field.Type)) { columns.add(column); } } if (columns.isEmpty()) return null; else return columns.toArray(new Column[columns.size()]); } return null; } // ************************************************************************** // ** getRecordCount // ************************************************************************** /** * Used to retrieve the total record count. Note that this method may be * slow. */ public int getRecordCount() { try { int currRow = rs.getRow(); rs.last(); int size = rs.getRow(); rs.absolute(currRow); // go back to the old row return size; } catch (Exception e) { Integer numRecords = null; String sql = new Parser(sqlString).setSelect("count(*)"); Recordset rs = new Recordset(); try { rs.open(sql, Connection); numRecords = rs.getValue(0).toInteger(); rs.close(); } catch (SQLException ex) { rs.close(); } if (numRecords != null) return numRecords; else return -1; } } // //************************************************************************** // //** Finalize // //************************************************************************** // /** Method *should* be called by Java garbage collector once this class // is // * disposed. // */ // protected void finalize() throws Throwable { // close(); // super.finalize(); // } }