Author: tomdz
Date: Sun Jan 1 06:43:05 2006
New Revision: 360485
URL: http://svn.apache.org/viewcvs?rev=360485&view=rev
Log:
Renamed and moved resultset iterator
Moved method that extracts values from a resultset to the platform to make it
easier to customize it for specific databases
Added:
db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/ModelBasedResultSetIterator.java
Removed:
db/ddlutils/trunk/src/java/org/apache/ddlutils/dynabean/DynaSqlIterator.java
Modified:
db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java
Added:
db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/ModelBasedResultSetIterator.java
URL:
http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/ModelBasedResultSetIterator.java?rev=360485&view=auto
==============================================================================
---
db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/ModelBasedResultSetIterator.java
(added)
+++
db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/ModelBasedResultSetIterator.java
Sun Jan 1 06:43:05 2006
@@ -0,0 +1,353 @@
+package org.apache.ddlutils.platform;
+
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.
+ */
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.beanutils.BasicDynaBean;
+import org.apache.commons.beanutils.BasicDynaClass;
+import org.apache.commons.beanutils.DynaBean;
+import org.apache.commons.beanutils.DynaClass;
+import org.apache.commons.beanutils.DynaProperty;
+import org.apache.commons.collections.map.ListOrderedMap;
+import org.apache.ddlutils.DynaSqlException;
+import org.apache.ddlutils.dynabean.SqlDynaBean;
+import org.apache.ddlutils.dynabean.SqlDynaClass;
+import org.apache.ddlutils.model.Column;
+import org.apache.ddlutils.model.Database;
+import org.apache.ddlutils.model.Table;
+
+/**
+ * This is an iterator that is specifically targeted at traversing result sets.
+ * If the query is against a known table, then [EMAIL PROTECTED]
org.apache.ddlutils.dynabean.SqlDynaBean} instances
+ * are created from the rows, otherwise normal [EMAIL PROTECTED]
org.apache.commons.beanutils.DynaBean} instances
+ * are created.
+ *
+ * @author Thomas Dudziak
+ * @version $Revision: 289996 $
+ */
+public class ModelBasedResultSetIterator implements Iterator
+{
+ /** The platform. */
+ private PlatformImplBase _platform;
+ /** The base result set. */
+ private ResultSet _resultSet;
+ /** The dyna class to use for creating beans. */
+ private DynaClass _dynaClass;
+ /** Maps column names to properties. */
+ private Map _columnsToProperties = new ListOrderedMap();
+ /** Whether the next call to hasNext or next needs advancement. */
+ private boolean _needsAdvancing = true;
+ /** Whether we're already at the end of the result set. */
+ private boolean _isAtEnd = false;
+ /** Whether to close the statement and connection after finishing. */
+ private boolean _cleanUpAfterFinish;
+
+ /**
+ * Creates a new iterator.
+ *
+ * @param platform The platform
+ * @param model The database model
+ * @param resultSet The result set
+ * @param queryHints The tables that were queried in the query
that produced the given result set
+ * (optional)
+ * @param cleanUpAfterFinish Whether to close the statement and connection
after finishing
+ * the iteration, upon on exception, or when
this iterator is garbage collected
+ */
+ public ModelBasedResultSetIterator(PlatformImplBase platform, Database
model, ResultSet resultSet, Table[] queryHints, boolean cleanUpAfterFinish)
throws DynaSqlException
+ {
+ if (resultSet != null)
+ {
+ _platform = platform;
+ _resultSet = resultSet;
+ _cleanUpAfterFinish = cleanUpAfterFinish;
+
+ try
+ {
+ initFromMetaData(model, queryHints);
+ }
+ catch (SQLException ex)
+ {
+ cleanUp();
+ throw new DynaSqlException("Could not read the metadata of the
result set", ex);
+ }
+ }
+ else
+ {
+ _isAtEnd = true;
+ }
+ }
+
+ /**
+ * Initializes this iterator from the resultset metadata.
+ *
+ * @param model The database model
+ * @param queryHints The tables that were queried in the query that
produced the given result set
+ */
+ private void initFromMetaData(Database model, Table[] queryHints) throws
SQLException
+ {
+ ResultSetMetaData metaData = _resultSet.getMetaData();
+ String tableName = null;
+ boolean singleKnownTable = true;
+ boolean caseSensitive =
_platform.getPlatformInfo().isUseDelimitedIdentifiers();
+ Map preparedQueryHints = prepareQueryHints(queryHints,
caseSensitive);
+
+ for (int idx = 1; idx <= metaData.getColumnCount(); idx++)
+ {
+ String columnName = metaData.getColumnName(idx);
+ String tableOfColumn = metaData.getTableName(idx);
+ Table table = null;
+
+ if ((tableOfColumn != null) && (tableOfColumn.length() > 0))
+ {
+ // the JDBC driver gave us enough meta data info
+ table = model.findTable(tableOfColumn, caseSensitive);
+ }
+ else
+ {
+ // not enough info in the meta data of the result set, lets
try the
+ // user-supplied query hints
+ table = (Table)preparedQueryHints.get(caseSensitive ?
columnName : columnName.toLowerCase());
+ tableOfColumn = (table == null ? null : table.getName());
+ }
+ if (tableName == null)
+ {
+ tableName = tableOfColumn;
+ }
+ else if (!tableName.equals(tableOfColumn))
+ {
+ singleKnownTable = false;
+ }
+
+ String propName = columnName;
+
+ if (table != null)
+ {
+ Column column = table.findColumn(columnName, caseSensitive);
+
+ if (column != null)
+ {
+ propName = column.getName();
+ }
+ }
+ _columnsToProperties.put(columnName, propName);
+ }
+ if (singleKnownTable && (tableName != null))
+ {
+ _dynaClass = model.getDynaClassFor(tableName);
+ }
+ else
+ {
+ DynaProperty[] props = new
DynaProperty[_columnsToProperties.size()];
+ int idx = 0;
+
+ for (Iterator it = _columnsToProperties.values().iterator();
it.hasNext(); idx++)
+ {
+ props[idx] = new DynaProperty((String)it.next());
+ }
+ _dynaClass = new BasicDynaClass("result", BasicDynaBean.class,
props);
+ }
+ }
+
+ /**
+ * Prepares the query hints by extracting the column names and using them
as keys
+ * into the resulting map pointing to the corresponding table.
+ *
+ * @param queryHints The query hints
+ * @param caseSensitive Whether the case of the column names is important
+ * @return The column name -> table map
+ */
+ private Map prepareQueryHints(Table[] queryHints, boolean caseSensitive)
+ {
+ Map result = new HashMap();
+
+ for (int tableIdx = 0; (queryHints != null) && (tableIdx <
queryHints.length); tableIdx++)
+ {
+ for (int columnIdx = 0; columnIdx <
queryHints[tableIdx].getColumnCount(); columnIdx++)
+ {
+ String columnName =
queryHints[tableIdx].getColumn(columnIdx).getName();
+
+ if (!caseSensitive)
+ {
+ columnName = columnName.toLowerCase();
+ }
+ if (!result.containsKey(columnName))
+ {
+ result.put(columnName, queryHints[tableIdx]);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * [EMAIL PROTECTED]
+ */
+ public boolean hasNext() throws DynaSqlException
+ {
+ advanceIfNecessary();
+ return !_isAtEnd;
+ }
+
+ /**
+ * [EMAIL PROTECTED]
+ */
+ public Object next() throws DynaSqlException
+ {
+ advanceIfNecessary();
+ if (_isAtEnd)
+ {
+ throw new NoSuchElementException("No more elements in the
resultset");
+ }
+ else
+ {
+ try
+ {
+ DynaBean bean = _dynaClass.newInstance();
+ Table table = null;
+
+ if (bean instanceof SqlDynaBean)
+ {
+ SqlDynaClass dynaClass =
(SqlDynaClass)((SqlDynaBean)bean).getDynaClass();
+
+ table = dynaClass.getTable();
+ }
+
+ for (Iterator it = _columnsToProperties.entrySet().iterator();
it.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry)it.next();
+ String columnName = (String)entry.getKey();
+ String propName = (String)entry.getKey();
+ Object value =
_platform.getObjectFromResultSet(_resultSet, columnName, table);
+
+ bean.set(propName, value);
+ }
+ _needsAdvancing = true;
+ return bean;
+ }
+ catch (Exception ex)
+ {
+ cleanUp();
+ throw new DynaSqlException("Exception while reading the row
from the resultset", ex);
+ }
+ }
+ }
+
+ /**
+ * Advances the result set if necessary.
+ */
+ private void advanceIfNecessary() throws DynaSqlException
+ {
+ if (_needsAdvancing && !_isAtEnd)
+ {
+ try
+ {
+ _isAtEnd = !_resultSet.next();
+ _needsAdvancing = false;
+ }
+ catch (SQLException ex)
+ {
+ cleanUp();
+ throw new DynaSqlException("Could not retrieve next row from
result set", ex);
+ }
+ if (_isAtEnd)
+ {
+ cleanUp();
+ }
+ }
+ }
+
+ /**
+ * [EMAIL PROTECTED]
+ */
+ public void remove() throws DynaSqlException
+ {
+ try
+ {
+ _resultSet.deleteRow();
+ }
+ catch (SQLException ex)
+ {
+ cleanUp();
+ throw new DynaSqlException("Failed to delete current row", ex);
+ }
+ }
+
+ /**
+ * Closes the resources (connection, statement, resultset).
+ */
+ public void cleanUp()
+ {
+ if (_cleanUpAfterFinish && (_resultSet != null))
+ {
+ try
+ {
+ Statement stmt = _resultSet.getStatement();
+ Connection conn = stmt.getConnection();
+
+ // also closes the resultset
+ stmt.close();
+ conn.close();
+ }
+ catch (SQLException ex)
+ {
+ // we ignore it
+ }
+ _resultSet = null;
+ }
+ }
+
+ /**
+ * [EMAIL PROTECTED]
+ */
+ protected void finalize() throws Throwable
+ {
+ cleanUp();
+ }
+
+ /**
+ * Determines whether the connection is still open.
+ *
+ * @return <code>true</code> if the connection is still open
+ */
+ public boolean isConnectionOpen()
+ {
+ if (_resultSet == null)
+ {
+ return false;
+ }
+ try
+ {
+ Statement stmt = _resultSet.getStatement();
+ Connection conn = stmt.getConnection();
+
+ return !conn.isClosed();
+ }
+ catch (SQLException ex)
+ {
+ return false;
+ }
+ }
+}
Modified:
db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java
URL:
http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java?rev=360485&r1=360484&r2=360485&view=diff
==============================================================================
---
db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java
(original)
+++
db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java
Sun Jan 1 06:43:05 2006
@@ -20,12 +20,15 @@
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
+import java.sql.Blob;
+import java.sql.Clob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
+import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -44,13 +47,13 @@
import org.apache.ddlutils.DynaSqlException;
import org.apache.ddlutils.Platform;
import org.apache.ddlutils.PlatformInfo;
-import org.apache.ddlutils.dynabean.DynaSqlIterator;
import org.apache.ddlutils.dynabean.SqlDynaClass;
import org.apache.ddlutils.dynabean.SqlDynaProperty;
import org.apache.ddlutils.model.Column;
import org.apache.ddlutils.model.Database;
import org.apache.ddlutils.model.Table;
import org.apache.ddlutils.model.TypeMap;
+import org.apache.ddlutils.util.Jdbc3Utils;
import org.apache.ddlutils.util.JdbcSupport;
/**
@@ -1523,6 +1526,14 @@
// Derby doesn't like BigDecimal's in setObject
statement.setBigDecimal(sqlIndex, (BigDecimal)value);
}
+ else if (value instanceof Float)
+ {
+ statement.setFloat(sqlIndex, ((Float)value).floatValue());
+ }
+ else if (value instanceof Double)
+ {
+ statement.setDouble(sqlIndex, ((Double)value).doubleValue());
+ }
else
{
statement.setObject(sqlIndex, value, typeCode);
@@ -1530,6 +1541,137 @@
}
/**
+ * Helper method esp. for the [EMAIL PROTECTED]
ModelBasedResultSetIterator} class that retrieves
+ * the value for a column from the given result set. If a table was
specified,
+ * and it contains the column, then the jdbc type defined for the column
is used for extracting
+ * the value, otherwise the object directly retrieved from the result set
is returned.<br/>
+ * The method is defined here rather than in the [EMAIL PROTECTED]
ModelBasedResultSetIterator} class
+ * so that concrete platforms can modify its behavior.
+ *
+ * @param resultSet The result set
+ * @param columnName The name of the column
+ * @param table The table
+ * @return The value
+ */
+ protected Object getObjectFromResultSet(ResultSet resultSet, String
columnName, Table table) throws SQLException
+ {
+ Column column = (table == null ? null : table.findColumn(columnName,
getPlatformInfo().isCaseSensitive()));
+ Object value = null;
+
+ if (column != null)
+ {
+ int originalJdbcType = column.getTypeCode();
+ int targetJdbcType =
getPlatformInfo().getTargetJdbcType(originalJdbcType);
+ int jdbcType = originalJdbcType;
+
+ // in general we're trying to retrieve the value using the
original type
+ // but sometimes we also need the target type:
+ if ((originalJdbcType == Types.BLOB) && (targetJdbcType !=
Types.BLOB))
+ {
+ // we should not use the Blob interface if the database
doesn't map to this type
+ jdbcType = targetJdbcType;
+ }
+ if ((originalJdbcType == Types.CLOB) && (targetJdbcType !=
Types.CLOB))
+ {
+ // we should not use the Clob interface if the database
doesn't map to this type
+ jdbcType = targetJdbcType;
+ }
+ switch (jdbcType)
+ {
+ case Types.CHAR:
+ case Types.VARCHAR:
+ case Types.LONGVARCHAR:
+ value = resultSet.getString(columnName);
+ break;
+ case Types.NUMERIC:
+ case Types.DECIMAL:
+ value = resultSet.getBigDecimal(columnName);
+ break;
+ case Types.BIT:
+ value = new Boolean(resultSet.getBoolean(columnName));
+ break;
+ case Types.TINYINT:
+ case Types.SMALLINT:
+ case Types.INTEGER:
+ value = new Integer(resultSet.getInt(columnName));
+ break;
+ case Types.BIGINT:
+ value = new Long(resultSet.getLong(columnName));
+ break;
+ case Types.REAL:
+ value = new Float(resultSet.getFloat(columnName));
+ break;
+ case Types.FLOAT:
+ case Types.DOUBLE:
+ value = new Double(resultSet.getDouble(columnName));
+ break;
+ case Types.BINARY:
+ case Types.VARBINARY:
+ case Types.LONGVARBINARY:
+ value = resultSet.getBytes(columnName);
+ break;
+ case Types.DATE:
+ value = resultSet.getDate(columnName);
+ break;
+ case Types.TIME:
+ value = resultSet.getTime(columnName);
+ break;
+ case Types.TIMESTAMP:
+ value = resultSet.getTimestamp(columnName);
+ break;
+ case Types.CLOB:
+ Clob clob = resultSet.getClob(columnName);
+
+ if ((clob == null) || (clob.length() > Integer.MAX_VALUE))
+ {
+ value = clob;
+ }
+ else
+ {
+ value = clob.getSubString(1l, (int)clob.length());
+ }
+ break;
+ case Types.BLOB:
+ Blob blob = resultSet.getBlob(columnName);
+
+ if ((blob == null) || (blob.length() > Integer.MAX_VALUE))
+ {
+ value = blob;
+ }
+ else
+ {
+ value = blob.getBytes(1l, (int)blob.length());
+ }
+ break;
+ case Types.ARRAY:
+ value = resultSet.getArray(columnName);
+ break;
+ case Types.REF:
+ value = resultSet.getRef(columnName);
+ break;
+ default:
+ // special handling for Java 1.4/JDBC 3 types
+ if (Jdbc3Utils.supportsJava14JdbcTypes() &&
+ (jdbcType == Jdbc3Utils.determineBooleanTypeCode()))
+ {
+ value = new Boolean(resultSet.getBoolean(columnName));
+ }
+ else
+ {
+ value = resultSet.getObject(columnName);
+ }
+ break;
+ }
+ }
+ else
+ {
+ value = resultSet.getObject(columnName);
+ }
+ return value;
+ }
+
+
+ /**
* Creates an iterator over the given result set.
*
* @param model The database model
@@ -1538,8 +1680,8 @@
* given result set (optional)
* @return The iterator
*/
- protected DynaSqlIterator createResultSetIterator(Database model,
ResultSet resultSet, Table[] queryHints)
+ protected ModelBasedResultSetIterator createResultSetIterator(Database
model, ResultSet resultSet, Table[] queryHints)
{
- return new DynaSqlIterator(getPlatformInfo(), model, resultSet,
queryHints, true);
+ return new ModelBasedResultSetIterator(this, model, resultSet,
queryHints, true);
}
}