You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ddlutils-dev@db.apache.org by to...@apache.org on 2006/01/01 15:43:13 UTC
svn commit: r360485 - in /db/ddlutils/trunk/src/java/org/apache/ddlutils:
dynabean/DynaSqlIterator.java platform/ModelBasedResultSetIterator.java
platform/PlatformImplBase.java
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 {@link org.apache.ddlutils.dynabean.SqlDynaBean} instances
+ * are created from the rows, otherwise normal {@link 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;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasNext() throws DynaSqlException
+ {
+ advanceIfNecessary();
+ return !_isAtEnd;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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 {@link 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 {@link 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);
}
}
Re: svn commit: r360485 - in /db/ddlutils/trunk/src/java/org/apache/ddlutils:
dynabean/DynaSqlIterator.java platform/ModelBasedResultSetIterator.java platform/PlatformImplBase.java
Posted by Martin van den Bemt <ml...@mvdb.net>.
Hi Tom,
First of all : happy new year :)
Wasn't a real good ending for me, since I was bound to bed with the flue, but at least the new year started quite healthy this way :)
About the new year : we need to set the copyright year to 2006 on at least the files that changed in 2006, don't know for sure if it needs to be updated for all files though..
Mvgr,
Martin
tomdz@apache.org wrote:
> Author: tomdz
> Date: Sun Jan 1 06:43:05 2006
> New Revision: 360485