/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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 software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.ejb.plugins.cmp.jdbc; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.StringTokenizer; import java.util.Collections; import java.util.Iterator; import java.util.AbstractCollection; import java.util.NoSuchElementException; import java.util.Set; import javax.ejb.FinderException; import javax.ejb.EJBException; import javax.transaction.Synchronization; import org.jboss.deployment.DeploymentException; import org.jboss.ejb.EntityEnterpriseContext; import org.jboss.ejb.GenericEntityObjectFactory; import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge; import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge; import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMRFieldBridge; import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge; import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCQueryMetaData; import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCLeftJoinMetaData; import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData; import org.jboss.ejb.plugins.cmp.ejbql.SelectFunction; import org.jboss.logging.Logger; /** * Abstract superclass of finder commands that return collections. * Provides the handleResult() implementation that these all need. * * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a> * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a> * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a> * @author <a href="mailto:shevlandj@kpi.com.au">Joe Shevland</a> * @author <a href="mailto:justin@j-m-f.demon.co.uk">Justin Forder</a> * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a> * @version $Revision: 81030 $ */ public abstract class JDBCAbstractQueryCommand implements JDBCQueryCommand { private JDBCQueryMetaData queryMetaData; protected Logger log; private JDBCStoreManager selectManager; private JDBCEntityBridge selectEntity; private JDBCCMPFieldBridge selectField; private SelectFunction selectFunction; private boolean[] eagerLoadMask; private String eagerLoadGroup; private String sql; private int offsetParam; private int offsetValue; private int limitParam; private int limitValue; private List parameters = new ArrayList(0); private List onFindCMRList = Collections.EMPTY_LIST; private QueryCollectionFactory collectionFactory; public JDBCAbstractQueryCommand(JDBCStoreManager manager, JDBCQueryMetaData q) throws DeploymentException { this.log = Logger.getLogger(this.getClass().getName() + "." + manager.getMetaData().getName() + "#" + q.getMethod().getName()); queryMetaData = q; collectionFactory = q.isLazyResultSetLoading() ? new LazyCollectionFactory() : (QueryCollectionFactory) new EagerCollectionFactory(); // setDefaultOffset(q.getOffsetParam()); // setDefaultLimit(q.getLimitParam()); setSelectEntity((JDBCEntityBridge) manager.getEntityBridge()); } public void setOffsetValue(int offsetValue) { this.offsetValue = offsetValue; } public void setLimitValue(int limitValue) { this.limitValue = limitValue; } public void setOffsetParam(int offsetParam) { this.offsetParam = offsetParam; } public void setLimitParam(int limitParam) { this.limitParam = limitParam; } public void setOnFindCMRList(List onFindCMRList) { this.onFindCMRList = onFindCMRList; } public JDBCStoreManager getSelectManager() { return selectManager; } public Collection execute(Method finderMethod, Object[] args, EntityEnterpriseContext ctx, GenericEntityObjectFactory factory) throws FinderException { int offset = toInt(args, offsetParam, offsetValue); int limit = toInt(args, limitParam, limitValue); return execute(sql, args, offset, limit, selectEntity, selectField, selectFunction, selectManager, eagerLoadMask, parameters, onFindCMRList, queryMetaData, factory, log); } protected static int toInt(Object[] params, int paramNumber, int defaultValue) { if(paramNumber == 0) { return defaultValue; } Integer arg = (Integer) params[paramNumber - 1]; return arg.intValue(); } protected Collection execute(String sql, Object[] args, int offset, int limit, JDBCEntityBridge selectEntity, JDBCCMPFieldBridge selectField, SelectFunction selectFunction, JDBCStoreManager selectManager, boolean[] eagerLoadMask, List parameters, List onFindCMRList, JDBCQueryMetaData queryMetaData, GenericEntityObjectFactory factory, Logger log) throws FinderException { int count = offset; Connection con = null; PreparedStatement ps = null; ResultSet rs = null; final JDBCEntityBridge entityBridge = (JDBCEntityBridge)selectManager.getEntityBridge(); boolean throwRuntimeExceptions = entityBridge.getMetaData().getThrowRuntimeExceptions(); // if metadata is true, the getconnection is done inside // its own try catch block to throw a runtime exception (EJBException) if (throwRuntimeExceptions) { try { con = entityBridge.getDataSource().getConnection(); } catch (SQLException sqle) { javax.ejb.EJBException ejbe = new javax.ejb.EJBException("Could not get a connection; " + sqle); ejbe.initCause(sqle); throw ejbe; } } try { // create the statement if(log.isDebugEnabled()) { log.debug("Executing SQL: " + sql); if(limit != 0 || offset != 0) { log.debug("Query offset=" + offset + ", limit=" + limit); } } // if metadata is false, the getconnection is done inside this try catch block if ( ! throwRuntimeExceptions) { con = entityBridge.getDataSource().getConnection(); } ps = con.prepareStatement(sql); // Set the fetch size of the statement if(entityBridge.getFetchSize() > 0) { ps.setFetchSize(entityBridge.getFetchSize()); } // set the parameters for(int i = 0; i < parameters.size(); i++) { QueryParameter parameter = (QueryParameter) parameters.get(i); parameter.set(log, ps, i + 1, args); } // execute statement rs = ps.executeQuery(); // skip 'offset' results while(count > 0 && rs.next()) { count--; } count = limit; } catch(Exception e) { JDBCUtil.safeClose(rs); JDBCUtil.safeClose(ps); JDBCUtil.safeClose(con); log.error("Find failed", e); FinderException fe = new FinderException("Find failed: " + e); fe.initCause(e); throw fe; } return collectionFactory.createCollection(con, ps, rs, limit, count, selectEntity, selectField, selectFunction, selectManager, onFindCMRList, eagerLoadMask, factory); } protected Logger getLog() { return log; } protected void setSQL(String sql) { this.sql = sql; if(log.isDebugEnabled()) { log.debug("SQL: " + sql); } } protected void setParameterList(List p) { for(int i = 0; i < p.size(); i++) { if(!(p.get(i) instanceof QueryParameter)) { throw new IllegalArgumentException("Element " + i + " of list " + "is not an instance of QueryParameter, but " + p.get(i).getClass().getName()); } } parameters = new ArrayList(p); } protected JDBCEntityBridge getSelectEntity() { return selectEntity; } protected void setSelectEntity(JDBCEntityBridge selectEntity) throws DeploymentException { if(queryMetaData.getMethod().getName().startsWith("find") && this.selectEntity != null && this.selectEntity != selectEntity) { throw new DeploymentException("Finder " + queryMetaData.getMethod().getName() + " defined on " + this.selectEntity.getEntityName() + " should return only instances of " + this.selectEntity.getEntityName() + " but the query results in instances of " + selectEntity.getEntityName()); } this.selectField = null; this.selectFunction = null; this.selectEntity = selectEntity; this.selectManager = (JDBCStoreManager) selectEntity.getManager(); } protected JDBCCMPFieldBridge getSelectField() { return selectField; } protected void setSelectField(JDBCCMPFieldBridge selectField) { this.selectEntity = null; this.selectFunction = null; this.selectField = selectField; this.selectManager = (JDBCStoreManager) selectField.getManager(); } protected void setSelectFunction(SelectFunction func, JDBCStoreManager manager) { this.selectEntity = null; this.selectField = null; this.selectFunction = func; this.selectManager = manager; } protected void setEagerLoadGroup(String eagerLoadGroup) { this.eagerLoadGroup = eagerLoadGroup; boolean[] originalMask = selectEntity.getLoadGroupMask(eagerLoadGroup); this.eagerLoadMask = new boolean[originalMask.length]; System.arraycopy(originalMask, 0, eagerLoadMask, 0, eagerLoadMask.length); } protected String getEagerLoadGroup() { return eagerLoadGroup; } protected boolean[] getEagerLoadMask() { return this.eagerLoadMask; } /** * Replaces the parameters in the specifiec sql with question marks, and * initializes the parameter setting code. Parameters are encoded in curly * brackets use a zero based index. * * @param sql the sql statement that is parsed for parameters * @return the original sql statement with the parameters replaced with a * question mark * @throws DeploymentException if a error occures while parsing the sql */ protected String parseParameters(String sql) throws DeploymentException { StringBuffer sqlBuf = new StringBuffer(); ArrayList params = new ArrayList(); // Replace placeholders {0} with ? if(sql != null) { sql = sql.trim(); StringTokenizer tokens = new StringTokenizer(sql, "{}", true); while(tokens.hasMoreTokens()) { String token = tokens.nextToken(); if(token.equals("{")) { token = tokens.nextToken(); if(Character.isDigit(token.charAt(0))) { QueryParameter parameter = new QueryParameter(selectManager, queryMetaData.getMethod(), token); // of if we are here we can assume that we have // a parameter and not a function sqlBuf.append("?"); params.add(parameter); if(!tokens.nextToken().equals("}")) { throw new DeploymentException("Invalid parameter - missing closing '}' : " + sql); } } else { // ok we don't have a parameter, we have a function // push the tokens on the buffer and continue sqlBuf.append("{").append(token); } } else { // not parameter... just append it sqlBuf.append(token); } } } parameters = params; return sqlBuf.toString(); } // Static public static List getLeftJoinCMRNodes(JDBCEntityBridge entity, String path, Iterator leftJoinIter, Set declaredPaths) throws DeploymentException { List leftJoinCMRNodes; if(leftJoinIter.hasNext()) { leftJoinCMRNodes = new ArrayList(); while(leftJoinIter.hasNext()) { JDBCLeftJoinMetaData leftJoin = (JDBCLeftJoinMetaData) leftJoinIter.next(); JDBCCMRFieldBridge cmrField = entity.getCMRFieldByName(leftJoin.getCmrField()); if(cmrField == null) { throw new DeploymentException("cmr-field in left-join was not found: cmr-field=" + leftJoin.getCmrField() + ", entity=" + entity.getEntityName()); } List subNodes; JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity(); String childPath = path + '.' + cmrField.getFieldName(); if(declaredPaths != null) { declaredPaths.add(childPath); } subNodes = getLeftJoinCMRNodes(relatedEntity, childPath, leftJoin.getLeftJoins(), declaredPaths); boolean[] mask = relatedEntity.getLoadGroupMask(leftJoin.getEagerLoadGroup()); LeftJoinCMRNode node = new LeftJoinCMRNode(childPath, cmrField, mask, subNodes); leftJoinCMRNodes.add(node); } } else { leftJoinCMRNodes = Collections.EMPTY_LIST; } return leftJoinCMRNodes; } public static final void leftJoinCMRNodes(String alias, List onFindCMRNodes, AliasManager aliasManager, StringBuffer sb) { for(int i = 0; i < onFindCMRNodes.size(); ++i) { LeftJoinCMRNode node = (LeftJoinCMRNode) onFindCMRNodes.get(i); JDBCCMRFieldBridge cmrField = node.cmrField; JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity(); String relatedAlias = aliasManager.getAlias(node.path); JDBCRelationMetaData relation = cmrField.getMetaData().getRelationMetaData(); if(relation.isTableMappingStyle()) { String relTableAlias = aliasManager.getRelationTableAlias(node.path); sb.append(" LEFT OUTER JOIN ") .append(cmrField.getQualifiedTableName()) .append(' ') .append(relTableAlias) .append(" ON "); SQLUtil.getRelationTableJoinClause(cmrField, alias, relTableAlias, sb); sb.append(" LEFT OUTER JOIN ") .append(relatedEntity.getQualifiedTableName()) .append(' ') .append(relatedAlias) .append(" ON "); SQLUtil.getRelationTableJoinClause(cmrField.getRelatedCMRField(), relatedAlias, relTableAlias, sb); } else { // foreign key mapping style sb.append(" LEFT OUTER JOIN ") .append(relatedEntity.getQualifiedTableName()) .append(' ') .append(relatedAlias) .append(" ON "); SQLUtil.getJoinClause(cmrField, alias, relatedAlias, sb); } List subNodes = node.onFindCMRNodes; if(!subNodes.isEmpty()) { leftJoinCMRNodes(relatedAlias, subNodes, aliasManager, sb); } } } public static final void appendLeftJoinCMRColumnNames(List onFindCMRNodes, AliasManager aliasManager, StringBuffer sb) { for(int i = 0; i < onFindCMRNodes.size(); ++i) { LeftJoinCMRNode node = (LeftJoinCMRNode) onFindCMRNodes.get(i); JDBCCMRFieldBridge cmrField = node.cmrField; JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity(); String childAlias = aliasManager.getAlias(node.path); // primary key fields SQLUtil.appendColumnNamesClause(relatedEntity.getPrimaryKeyFields(), childAlias, sb); // eager load group if(node.eagerLoadMask != null) { SQLUtil.appendColumnNamesClause(relatedEntity.getTableFields(), node.eagerLoadMask, childAlias, sb); } List subNodes = node.onFindCMRNodes; if(!subNodes.isEmpty()) { appendLeftJoinCMRColumnNames(subNodes, aliasManager, sb); } } } private static int loadOnFindCMRFields(Object pk, List onFindCMRNodes, ResultSet rs, int index, Logger log) { Object[] ref = new Object[1]; for(int nodeInd = 0; nodeInd < onFindCMRNodes.size(); ++nodeInd) { LeftJoinCMRNode node = (LeftJoinCMRNode) onFindCMRNodes.get(nodeInd); JDBCCMRFieldBridge cmrField = node.cmrField; ReadAheadCache myCache = cmrField.getJDBCStoreManager().getReadAheadCache(); JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity(); ReadAheadCache relatedCache = cmrField.getRelatedManager().getReadAheadCache(); // load related id ref[0] = null; index = relatedEntity.loadPrimaryKeyResults(rs, index, ref); Object relatedId = ref[0]; boolean cacheRelatedData = relatedId != null; if(pk != null) { if(cmrField.getMetaData().getRelatedRole().isMultiplicityOne()) { // cacheRelatedData the value myCache.addPreloadData(pk, cmrField, relatedId == null ? Collections.EMPTY_LIST : Collections.singletonList(relatedId)); } else { Collection cachedValue = myCache.getCachedCMRValue(pk, cmrField); if(cachedValue == null) { cachedValue = new ArrayList(); myCache.addPreloadData(pk, cmrField, cachedValue); } if(relatedId != null) { if(cachedValue.contains(relatedId)) { cacheRelatedData = false; } else { cachedValue.add(relatedId); } } } } // load eager load group if(node.eagerLoadMask != null) { JDBCFieldBridge[] tableFields = relatedEntity.getTableFields(); for(int fieldInd = 0; fieldInd < tableFields.length; ++fieldInd) { if(node.eagerLoadMask[fieldInd]) { JDBCFieldBridge field = tableFields[fieldInd]; ref[0] = null; index = field.loadArgumentResults(rs, index, ref); if(cacheRelatedData) { if(log.isTraceEnabled()) { log.trace("Caching " + relatedEntity.getEntityName() + '[' + relatedId + "]." + field.getFieldName() + "=" + ref[0]); } relatedCache.addPreloadData(relatedId, field, ref[0]); } } } } List subNodes = node.onFindCMRNodes; if(!subNodes.isEmpty()) { index = loadOnFindCMRFields(relatedId, subNodes, rs, index, log); } } return index; } public static final class LeftJoinCMRNode { public final String path; public final JDBCCMRFieldBridge cmrField; public final boolean[] eagerLoadMask; public final List onFindCMRNodes; public LeftJoinCMRNode(String path, JDBCCMRFieldBridge cmrField, boolean[] eagerLoadMask, List onFindCMRNodes) { this.path = path; this.cmrField = cmrField; this.eagerLoadMask = eagerLoadMask; this.onFindCMRNodes = onFindCMRNodes; } public boolean equals(Object o) { boolean result; if(o == this) { result = true; } else if(o instanceof LeftJoinCMRNode) { LeftJoinCMRNode other = (LeftJoinCMRNode) o; result = cmrField == other.cmrField; } else { result = false; } return result; } public int hashCode() { return cmrField == null ? Integer.MIN_VALUE : cmrField.hashCode(); } public String toString() { return '[' + cmrField.getFieldName() + ": " + onFindCMRNodes + ']'; } } interface QueryCollectionFactory { Collection createCollection(Connection con, PreparedStatement ps, ResultSet rs, int limit, int count, JDBCEntityBridge selectEntity, JDBCCMPFieldBridge selectField, SelectFunction selectFunction, JDBCStoreManager selectManager, List onFindCMRList, boolean[] eagerLoadMask, GenericEntityObjectFactory factory) throws FinderException; } class EagerCollectionFactory implements QueryCollectionFactory { public Collection createCollection(Connection con, PreparedStatement ps, ResultSet rs, int limit, int count, JDBCEntityBridge selectEntity, JDBCCMPFieldBridge selectField, SelectFunction selectFunction, JDBCStoreManager selectManager, List onFindCMRList, boolean[] eagerLoadMask, GenericEntityObjectFactory factory) throws FinderException { try { List results = new ArrayList(); if(selectEntity != null) { ReadAheadCache selectReadAheadCache = selectManager.getReadAheadCache(); List ids = new ArrayList(); boolean loadOnFindCmr = !onFindCMRList.isEmpty(); Object[] ref = new Object[1]; Object prevPk = null; while((limit == 0 || count-- > 0) && rs.next()) { int index = 1; // get the pk index = selectEntity.loadPrimaryKeyResults(rs, index, ref); Object pk = ref[0]; boolean addPk = (loadOnFindCmr ? !pk.equals(prevPk) : true); if(addPk) { ids.add(pk); results.add(factory.getEntityEJBObject(pk)); prevPk = pk; } // read the preload fields if(eagerLoadMask != null) { JDBCFieldBridge[] tableFields = selectEntity.getTableFields(); for(int i = 0; i < eagerLoadMask.length; i++) { if(eagerLoadMask[i]) { JDBCFieldBridge field = tableFields[i]; ref[0] = null; // read the value and store it in the readahead cache index = field.loadArgumentResults(rs, index, ref); if(addPk) { selectReadAheadCache.addPreloadData(pk, field, ref[0]); } } } if(!onFindCMRList.isEmpty()) { index = loadOnFindCMRFields(pk, onFindCMRList, rs, index, log); } } } // add the results list to the cache selectReadAheadCache.addFinderResults(ids, queryMetaData.getReadAhead()); } else if(selectField != null) { // load the field Object[] valueRef = new Object[1]; while((limit == 0 || count-- > 0) && rs.next()) { valueRef[0] = null; selectField.loadArgumentResults(rs, 1, valueRef); results.add(valueRef[0]); } } else { while(rs.next()) { results.add(selectFunction.readResult(rs)); } } if(log.isDebugEnabled() && limit != 0 && count == 0) { log.debug("Query result was limited to " + limit + " row(s)"); } return results; } catch(Exception e) { log.error("Find failed", e); throw new FinderException("Find failed: " + e); } finally { JDBCUtil.safeClose(rs); JDBCUtil.safeClose(ps); JDBCUtil.safeClose(con); } } } class LazyCollectionFactory implements QueryCollectionFactory { public Collection createCollection(Connection con, PreparedStatement ps, ResultSet rs, int limit, int count, JDBCEntityBridge selectEntity, JDBCCMPFieldBridge selectField, SelectFunction selectFunction, JDBCStoreManager selectManager, List onFindCMRList, boolean[] eagerLoadMask, GenericEntityObjectFactory factory) throws FinderException { return new LazyCollection(con, ps, rs, limit, count, selectEntity, selectField, selectFunction, selectManager, eagerLoadMask, factory); } private class LazyCollection extends AbstractCollection { private final Connection con; private final PreparedStatement ps; private final ResultSet rs; private final int limit; private int count; private final JDBCEntityBridge selectEntity; private final JDBCCMPFieldBridge selectField; private final SelectFunction selectFunction; private final JDBCStoreManager selectManager; private final boolean[] eagerLoadMask; private final GenericEntityObjectFactory factory; private Object prevPk; private Object curPk; private Object currentResult; Object[] ref = new Object[1]; boolean loadOnFindCmr; private List results = null; private Iterator firstIterator; private int size; private boolean resourcesClosed; public LazyCollection(final Connection con, final PreparedStatement ps, final ResultSet rs, int limit, int count, JDBCEntityBridge selectEntity, JDBCCMPFieldBridge selectField, SelectFunction selectFunction, JDBCStoreManager selectManager, boolean[] eagerLoadMask, GenericEntityObjectFactory factory) { this.con = con; this.ps = ps; this.rs = rs; this.limit = limit; this.count = count; this.selectEntity = selectEntity; this.selectField = selectField; this.selectFunction = selectFunction; this.selectManager = selectManager; this.eagerLoadMask = eagerLoadMask; this.factory = factory; loadOnFindCmr = !onFindCMRList.isEmpty(); firstIterator = getFirstIterator(); if(firstIterator.hasNext()) { try { size = rs.getInt(1); } catch(SQLException e) { throw new EJBException("Failed to read ResultSet.", e); } if(limit > 0 && size > limit) { size = limit; } } if(size < 1) { firstIterator = null; results = new ArrayList(0); closeResources(); } else { results = new ArrayList(size); try { selectManager.getContainer().getTransactionManager().getTransaction().registerSynchronization(new Synchronization() { public void beforeCompletion() { closeResources(); } public void afterCompletion(int status) { closeResources(); } }); } catch(Exception e) { throw new EJBException("Failed to obtain current transaction", e); } } } private void closeResources() { if(!resourcesClosed) { JDBCUtil.safeClose(rs); JDBCUtil.safeClose(ps); JDBCUtil.safeClose(con); resourcesClosed = true; } } public Iterator iterator() { return firstIterator != null ? firstIterator : results.iterator(); } public int size() { return firstIterator != null ? size : results.size(); } public boolean add(Object o) { if(firstIterator == null) { return results.add(o); } throw new IllegalStateException("Can't modify collection while the first iterator is not exhausted."); } public boolean remove(Object o) { if(firstIterator == null) { return results.remove(o); } throw new IllegalStateException("Can't modify collection while the first iterator is not exhausted."); } private boolean hasNextResult() { try { boolean has = (limit == 0 || count-- > 0) && rs.next(); if(!has) { if(log.isTraceEnabled()) { log.trace("first iterator exhausted!"); } firstIterator = null; closeResources(); } return has; } catch(Exception e) { log.error("Failed to read ResultSet.", e); throw new EJBException("Failed to read ResultSet: " + e.getMessage()); } } private Object readNext() { try { if(selectEntity != null) { ReadAheadCache selectReadAheadCache = selectManager.getReadAheadCache(); // first one is size int index = 2; // get the pk index = selectEntity.loadPrimaryKeyResults(rs, index, ref); curPk = ref[0]; boolean addPk = (loadOnFindCmr ? !curPk.equals(prevPk) : true); if(addPk) { prevPk = curPk; currentResult = factory.getEntityEJBObject(curPk); } // read the preload fields if(eagerLoadMask != null) { JDBCFieldBridge[] tableFields = selectEntity.getTableFields(); for(int i = 0; i < eagerLoadMask.length; i++) { if(eagerLoadMask[i]) { JDBCFieldBridge field = tableFields[i]; ref[0] = null; // read the value and store it in the readahead cache index = field.loadArgumentResults(rs, index, ref); if(addPk) { selectReadAheadCache.addPreloadData(curPk, field, ref[0]); } } } if(!onFindCMRList.isEmpty()) { index = loadOnFindCMRFields(curPk, onFindCMRList, rs, index, log); } } } else if(selectField != null) { // load the field selectField.loadArgumentResults(rs, 2, ref); currentResult = ref[0]; } else { currentResult = selectFunction.readResult(rs); } if(log.isTraceEnabled() && limit != 0 && count == 0) { log.trace("Query result was limited to " + limit + " row(s)"); } return currentResult; } catch(Exception e) { log.error("Failed to read ResultSet", e); throw new EJBException("Failed to read ResultSet: " + e.getMessage()); } } private Iterator getFirstIterator() { return new Iterator() { private boolean hasNext; private Object cursor; public boolean hasNext() { return hasNext ? hasNext : (hasNext = hasNextResult()); } public Object next() { if(!hasNext()) { throw new NoSuchElementException(); } hasNext = false; cursor = readNext(); results.add(cursor); return cursor; } public void remove() { --size; results.remove(cursor); } }; } } } }