Java tutorial
/* * Copyright (c) 2008-2011 Simon Ritchie. * All rights reserved. * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 program. If not, see http://www.gnu.org/licenses/>. */ package org.rimudb.generic; import java.sql.*; import org.apache.commons.logging.*; import org.rimudb.*; import org.rimudb.exception.*; import org.rimudb.generic.binders.*; /** * This class is used to allow the caller to iterate over the result set. It is designed for * handling large result sets where the entire set may be too large to be held in memory. * * @author Simon Ritchie * */ public class IterativeQuery extends AbstractGenericBase { private static Log log = LogFactory.getLog(IterativeQuery.class); private Class<? extends DataObject>[] dataObjectClasses = null; private Class<? extends DataObject> dataObjectClass = null; private IIterativeResultSetBinder binder; /** * Create an IterativeQuery. * * @param database Database * @throws RimuDBException */ public IterativeQuery(Database database) throws RimuDBException { super(database); } /** * Create an IterativeQuery. * * @param database Database * @param sql String * @throws RimuDBException */ public IterativeQuery(Database database, String sql) throws RimuDBException { super(database, sql); } /** * Create an IterativeQuery. * * @param database Database * @param dataObjectClasses Class<? extends DataObject>[] * @param sql String * @throws RimuDBException */ public IterativeQuery(Database database, Class<? extends DataObject>[] dataObjectClasses, String sql) throws RimuDBException { super(database, sql); this.dataObjectClasses = dataObjectClasses; } /** * Create an IterativeQuery. * * @param database Database * @param dataObjectClass Class<? extends DataObject> * @param sql String * @throws RimuDBException */ public IterativeQuery(Database database, Class<? extends DataObject> dataObjectClass, String sql) throws RimuDBException { super(database, sql); this.dataObjectClass = dataObjectClass; } /** * Create an IterativeQuery. * * @param database Database * @param binder IIterativeResultSetBinder * @param sql String * @throws RimuDBException */ public IterativeQuery(Database database, IIterativeResultSetBinder binder, String sql) throws RimuDBException { super(database, sql); this.binder = binder; } /** * Use this method to return an Iterator that will return multiple rows each containing * multiple Data Objects. If the SQL statement contains a join of two or more tables then * multiple Data Objects can be returned. * * Note that in order for this to work, the select statement must return all the columns * required by the Data Objects. * * @param parameters * @return DataObjectArrayIterator * @throws RimuDBException */ public DataObjectArrayIterator createDataObjectArrayIterator(Object... parameters) throws RimuDBException { if (dataObjectClasses == null) { throw new IllegalArgumentException( "Cannot use this method unless an array of Data Object class has been configured for the query"); } binder = new IterativeDataObjectListArrayBinder(dataObjectClasses); binder.initialize(getDatabase()); DataObjectArrayIterator iterator = new DataObjectArrayIterator(); iterator.setResultSetBinder(binder); loadIterator(iterator, parameters); return iterator; } /** * Use this method if the SQL statement will return multiple rows each containing a DataObject. * * Note that in order for this to work, the select statement must return all the columns * required by the Data Object. * * @param parameters * @return DataObjectIterator * @throws RimuDBException */ public DataObjectIterator createDataObjectIterator(Object... parameters) throws RimuDBException { if (dataObjectClass == null) { throw new IllegalArgumentException( "Cannot use this method unless a Data Object class has been configured for the query"); } binder = new IterativeDataObjectListBinder(dataObjectClass); binder.initialize(getDatabase()); DataObjectIterator iterator = new DataObjectIterator(); iterator.setResultSetBinder(binder); loadIterator(iterator, parameters); return iterator; } /** * Use this method if the SQL statement will return multiple rows each containing a selection * of the available columns or scalar results. This is the most flexible form of the methods, * allowing any any values that can be returned by a select statement to be returned. It does * however, require that a bean class and a binder class are created for handling the selection. * The bean class must implement IResultSetBean and the binder must implement * IIterativeResultSetBinder. * * @param parameters * @return ObjectIterator * @throws RimuDBException */ public ObjectIterator createObjectIteratorWithBinder(Object... parameters) throws RimuDBException { if (getBinder() == null) { throw new IllegalArgumentException( "Cannot use this method unless a binder has been configured for the query"); } ObjectIterator iterator = new ObjectIterator(); iterator.setResultSetBinder(binder); loadIterator(iterator, parameters); return iterator; } /** * Use this method if the SQL statement will return multiple rows each containing a single column. * * @param parameters * @return ObjectIterator * @throws RimuDBException */ public ObjectIterator createObjectIterator(Object... parameters) throws RimuDBException { binder = new IterativeSingleValueBinder(); return createObjectIteratorWithBinder(parameters); } /** * Load the iterator with the SQL Statement and fetch the result set. * * @param queryIterator AbstractQueryIterator * @param parameters Object[] * @throws RimuDBException */ private void loadIterator(AbstractQueryIterator queryIterator, Object parameters[]) throws RimuDBException { if (getSQL() == null || getSQL().trim().length() == 0) { throw new RimuDBException("SQL statement has not been set"); } Connection con = null; PreparedStatement stmt = null; ResultSet rs = null; // Parse the SQL to replace table names String psql = parse(getSQL()); try { con = getDatabase().getDatabaseConnection(); queryIterator.setConnection(con); stmt = con.prepareStatement(psql); stmt.setFetchSize(getFetchSize()); queryIterator.setPreparedStatement(stmt); if (parameters != null) { boolean charUsesSetObject = getDatabase().getSQLAdapter().isCharUsesSetObject(); for (int i = 0; i < parameters.length; i++) { RecordBinder.bindValue(stmt, i + 1, parameters[i], charUsesSetObject); } } rs = stmt.executeQuery(); queryIterator.setResultSet(rs); } catch (Exception e) { if (rs != null) { try { rs.close(); } catch (SQLException sqle) { } rs = null; } if (stmt != null) { try { stmt.close(); } catch (SQLException sqle) { } stmt = null; } if (con != null) { try { con.close(); } catch (SQLException sqle) { } con = null; } StringBuilder sb = new StringBuilder(); sb.append("SQL statement="); sb.append(psql); // Log the parameters for the SQL statement that failed if (parameters != null) { for (int i = 0; i < parameters.length; i++) { sb.append(" Parameters[" + i + "]=" + parameters[i]); } } sb.append(" "); // Include all the sql info in the exception throw new RimuDBException(e, sb.toString()); } } /** * Return the binder. * * @return IIterativeResultSetBinder */ public IIterativeResultSetBinder getBinder() { return binder; } /** * Set the binder. * * @param binder IIterativeResultSetBinder */ public void setBinder(IIterativeResultSetBinder binder) { this.binder = binder; } }