0001: /* ====================================================================
0002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
0003: *
0004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * 1. Redistributions of source code must retain the above copyright
0011: * notice, this list of conditions and the following disclaimer.
0012: *
0013: * 2. Redistributions in binary form must reproduce the above copyright
0014: * notice, this list of conditions and the following disclaimer in
0015: * the documentation and/or other materials provided with the
0016: * distribution.
0017: *
0018: * 3. The end-user documentation included with the redistribution,
0019: * if any, must include the following acknowledgment:
0020: * "This product includes software developed by Jcorporate Ltd.
0021: * (http://www.jcorporate.com/)."
0022: * Alternately, this acknowledgment may appear in the software itself,
0023: * if and wherever such third-party acknowledgments normally appear.
0024: *
0025: * 4. "Jcorporate" and product names such as "Expresso" must
0026: * not be used to endorse or promote products derived from this
0027: * software without prior written permission. For written permission,
0028: * please contact info@jcorporate.com.
0029: *
0030: * 5. Products derived from this software may not be called "Expresso",
0031: * or other Jcorporate product names; nor may "Expresso" or other
0032: * Jcorporate product names appear in their name, without prior
0033: * written permission of Jcorporate Ltd.
0034: *
0035: * 6. No product derived from this software may compete in the same
0036: * market space, i.e. framework, without prior written permission
0037: * of Jcorporate Ltd. For written permission, please contact
0038: * partners@jcorporate.com.
0039: *
0040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
0044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
0046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0051: * SUCH DAMAGE.
0052: * ====================================================================
0053: *
0054: * This software consists of voluntary contributions made by many
0055: * individuals on behalf of the Jcorporate Ltd. Contributions back
0056: * to the project(s) are encouraged when you make modifications.
0057: * Please send them to support@jcorporate.com. For more information
0058: * on Jcorporate Ltd. and its products, please see
0059: * <http://www.jcorporate.com/>.
0060: *
0061: * Portions of this software are based upon other open source
0062: * products and are subject to their respective licenses.
0063: */
0064: package com.jcorporate.expresso.core.dataobjects.jdbc;
0065:
0066: import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
0067: import com.jcorporate.expresso.core.dataobjects.BaseDataObject;
0068: import com.jcorporate.expresso.core.dataobjects.DataException;
0069: import com.jcorporate.expresso.core.dataobjects.DataExecutorInterface;
0070: import com.jcorporate.expresso.core.dataobjects.DataFieldMetaData;
0071: import com.jcorporate.expresso.core.dataobjects.DataObjectMetaData;
0072: import com.jcorporate.expresso.core.dataobjects.DataQueryInterface;
0073: import com.jcorporate.expresso.core.db.DBConnection;
0074: import com.jcorporate.expresso.core.db.DBConnectionPool;
0075: import com.jcorporate.expresso.core.db.DBException;
0076: import com.jcorporate.expresso.core.db.config.JDBCConfig;
0077: import com.jcorporate.expresso.core.dbobj.DBField;
0078: import com.jcorporate.expresso.core.dbobj.DBObjectDef;
0079: import com.jcorporate.expresso.core.misc.Base64;
0080: import com.jcorporate.expresso.core.misc.ConfigJdbc;
0081: import com.jcorporate.expresso.core.misc.ConfigManager;
0082: import com.jcorporate.expresso.core.misc.ConfigurationException;
0083: import com.jcorporate.expresso.core.misc.DateTime;
0084: import com.jcorporate.expresso.core.misc.StringUtil;
0085: import com.jcorporate.expresso.core.registry.RequestRegistry;
0086: import com.jcorporate.expresso.core.security.CryptoManager;
0087: import com.jcorporate.expresso.kernel.exception.ChainedException;
0088: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
0089: import org.apache.log4j.Logger;
0090:
0091: import java.io.InputStream;
0092: import java.sql.CallableStatement;
0093: import java.util.ArrayList;
0094: import java.util.HashMap;
0095: import java.util.Iterator;
0096:
0097: /**
0098: * Base class for JDBC-based data objects.
0099: *
0100: * @author Michael Rimov
0101: * @since Expresso 5.1
0102: * <p/>
0103: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
0104: */
0105:
0106: abstract public class JDBCDataObject extends BaseDataObject {
0107:
0108: private static transient final Logger log = Logger
0109: .getLogger(JDBCDataObject.class);
0110:
0111: public static int LONGBINARY_READ_DEFAULT_SIZE = 262144;
0112:
0113: /**
0114: * a local connection pool. Often used to save on going back and forth
0115: * to the connection pool, or if you want JDBC transactions.
0116: */
0117: transient private DBConnectionPool myPool = null;
0118:
0119: /**
0120: * Normally originalDBKey is the same as DB key, except where the DB object is mapped
0121: * to another database specifically, in which case originalDBKey can be used
0122: * to determine the database to be used to find Expresso's own tables
0123: */
0124: private String mappedDataContext = null;
0125:
0126: /**
0127: * If we are using a custom where clause for this query, it's stored here.
0128: * If null, then build the where clause
0129: */
0130: protected String customWhereClause = null;
0131:
0132: /**
0133: * Flag to indicate whether we append any custom WHERE clause to
0134: * the one generated, or just use the supplied custom WHERE clause
0135: */
0136: protected boolean appendCustomWhere = false;
0137:
0138: /**
0139: * dbKey is used to indicate that this object is actually stored in
0140: * other than the default database. Null indicates it's in the default
0141: * database
0142: */
0143: protected String dbKey = null;
0144:
0145: /**
0146: * The ArrayList of DB objects retrieved by the last searchAndRetrieve method
0147: */
0148: protected ArrayList recordSet = null;
0149:
0150: /**
0151: * We use getClass().getName() a lot in this class, however, it takes
0152: * actually significant time to use this over the time it takes to do, say,
0153: * a concurrentReaderHashMap lookup. So we precalculate this value at
0154: * instantiation and go from there.
0155: */
0156: transient protected String myClassName = getClass().getName();
0157:
0158: /**
0159: * Map of any distinct fields for retrieval.
0160: */
0161: protected HashMap distinctFields = null;
0162:
0163: /**
0164: * The actual fields retrieved
0165: */
0166: public HashMap retrieveFields = null;
0167:
0168: /**
0169: * DBObjects themselves do not contain the "meta data" or the definition of
0170: * what fields they contain and other information. Instead, a DBObjectRef object
0171: * (one per TYPE of DBObject) contains this info and is looked up as needed.
0172: * This HashMap contains the DBObjectRef objects for all initialized DBObjects
0173: *
0174: * @todo move this static variable outside DBObject so we can wrap it for
0175: * EJBs
0176: */
0177: volatile transient protected static ConcurrentReaderHashMap sMetadataMap = new ConcurrentReaderHashMap();
0178:
0179: /**
0180: * If setFieldsToRetrieve has been used to specify fields which will be
0181: * retrieve by the next query.
0182: */
0183: protected boolean anyFieldsToRetrieve = false;
0184:
0185: /**
0186: * Local connection that we use if it's initialized, but
0187: * if it's null we generate our own connection
0188: */
0189: transient protected DBConnection localConnection = null;
0190:
0191: /**
0192: * The number of records we must skip over before we start reading
0193: * the <code>ResultSet</code> proper in a searchAndRetrieve.
0194: * 0 means no limit
0195: * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
0196: */
0197: transient protected int offsetRecord = 0;
0198:
0199: /* The max number of records we retrieve in a searchAndRetrieve.
0200: * 0 means no limit
0201: */
0202: transient protected int maxRecords = 0;
0203:
0204: /**
0205: * The list of fields by which this object should be sorted when
0206: * records are retrieved
0207: */
0208: transient protected ArrayList sortKeys = null;
0209:
0210: /**
0211: * Holds the FieldUpdate objects representing any changes made to this
0212: * object since it was retrieved
0213: */
0214: transient protected ArrayList myUpdates = null;
0215:
0216: /**
0217: * If setFieldDistinct has been used to specify distinct/unique fields for
0218: * the next query.
0219: */
0220: public boolean anyFieldsDistinct = false;
0221:
0222: /**
0223: * This flag tells the buildWhereClause method(s) to either be case
0224: * sensitive (normal queries) or to be case insensitive. The case
0225: * insensitive query is useful when you want to do a search and retreive
0226: * and want case insensitive matching.
0227: */
0228: protected boolean caseSensitiveQuery = true;
0229:
0230: /**
0231: * Utility class that currently provides help for getFieldDate() function.
0232: */
0233: transient static private JDBCUtil sJdbcUtil = JDBCUtil
0234: .getInstance();
0235:
0236: /**
0237: * Helper component that provides the specific interactions between the
0238: * DBObject and the underlying datasource, JDBC or later on, otherwise
0239: *
0240: * @since Expresso 5.0
0241: */
0242: transient private static DataExecutorInterface sDataExecutor = new JDBCExecutor();
0243:
0244: /**
0245: * Helper component that provides the specific querying capabilities
0246: * against any particular datasrouce, JDBC, or later on, others.
0247: *
0248: * @since Expresso 5.0
0249: */
0250: transient private static DataQueryInterface sDataQueryObject = new JDBCQuery();
0251:
0252: /**
0253: * Default constructor
0254: */
0255: public JDBCDataObject() {
0256: }
0257:
0258: /**
0259: * Returns the name of the physical database that we're talking with. This
0260: * is opposed to getDataContext() which returns the security context as well.
0261: * getMappedDataContext() is strictly used to get at the low level database
0262: * connection.
0263: *
0264: * @return java.lang.String... the underlying data context that is mapped
0265: * to the physical database
0266: */
0267: public String getMappedDataContext() {
0268:
0269: if (mappedDataContext == null) {
0270: this .setDataContext(RequestRegistry.getDataContext());
0271: if (log.isDebugEnabled()) {
0272: log
0273: .debug("Setting Database Context from Request Registry ");
0274: }
0275: }
0276: return mappedDataContext;
0277: }
0278:
0279: /**
0280: * Retrieve the connection pool associated with this DBObject.
0281: *
0282: * @return DBConnectionPool instance for the current mapped context.
0283: * @throws DataException upon error getting the connection pool instance
0284: */
0285: public DBConnectionPool getConnectionPool() throws DataException {
0286: try {
0287: if (myPool == null) {
0288: if (this .getMappedDataContext() == null) {
0289: this .setDataContext("default");
0290: }
0291: String myDataContext = this .getMappedDataContext();
0292: myPool = DBConnectionPool.getInstance(myDataContext);
0293: }
0294: } catch (DBException ex) {
0295: throw new DataException("Error getting Connection Pool", ex);
0296: }
0297:
0298: return myPool;
0299: }
0300:
0301: /**
0302: * Sets the connection pool associated with this dbobject. Is only settable
0303: * from derived classes as we normally create our own as needed.
0304: *
0305: * @param newPool the new DBConnectionPool to set
0306: */
0307: protected void setDBConnectionPool(DBConnectionPool newPool) {
0308: myPool = newPool;
0309: }
0310:
0311: /**
0312: * This function is called whenever the DBField is about to be written to
0313: * the database. It may do additional processing such as encryption depending
0314: * on the field attributes.
0315: *
0316: * @param theField A DBField object
0317: * @return the value to write to the data source.
0318: * @throws DataException upon error
0319: * @todo This is not completely implemented yet, and this responsibility for
0320: * formating a field should probably be
0321: * put into DBField for better object orientation.
0322: */
0323: public String getSerialForm(DataFieldMetaData theField)
0324: throws DataException {
0325: try {
0326: if (theField.isEncrypted()) {
0327: return Base64.encodeNoPadding(CryptoManager
0328: .getInstance().getStringEncryption()
0329: .encryptString(
0330: this .getDataField(theField.getName())
0331: .asString()));
0332: } else {
0333: String result = getDataField(theField.getName())
0334: .asString();
0335: if (theField.isBooleanType()) {
0336: // do we have to convert from true/false to 'Y'/'N' ??
0337: try {
0338: boolean nativeBoolean = ConfigManager
0339: .getContext(getMappedDataContext())
0340: .getJdbc().isNativeBool();
0341: if (!nativeBoolean) {
0342: // fix up a true/false into what we expect to serialize
0343: if ("true".equalsIgnoreCase(result)) {
0344: result = "Y";
0345: }
0346: if ("false".equalsIgnoreCase(result)) {
0347: result = "N";
0348: }
0349: }
0350: } catch (ConfigurationException ce) {
0351: throw new DataException(ce);
0352: }
0353: }
0354: return result;
0355: }
0356: } catch (ChainedException e) {
0357: throw new DataException(
0358: "Error getting Serialized Form for field: "
0359: + theField.getName(), e);
0360: }
0361: } /* getSerializedForm() */
0362:
0363: /**
0364: * Set the database name/context for this db object. If setDBName is not called,
0365: * the "default" db name and context is used.
0366: * See com.jcorporate.expresso.core.misc.ConfigManager for information about
0367: * multiple contexts. Note that setting a db/context name only affects the
0368: * object when it allocates it's own db connections - if a specific connection
0369: * is used (via the setConnection(DBConnection) method) then that connection must
0370: * be already associated with the correct db/context.
0371: * If there is an entry in the DBOtherMap table for this object, it is forced to
0372: * that database, and a warning is logged if any other database is specified.
0373: *
0374: * @param newOther The name of the context or database to use
0375: */
0376: public synchronized void setDBName(String newOther)
0377: throws DBException {
0378:
0379: /* Blank or null gets interpreted as "default" */
0380: if (StringUtil.notNull(newOther).equals("")) {
0381: newOther = "default";
0382: }
0383:
0384: if (log.isDebugEnabled()) {
0385: log.debug("Object '" + myClassName + "' requesting db '"
0386: + newOther + "'");
0387: }
0388: String mappedContext = newOther;
0389: dbKey = newOther;
0390: /* We don't allow an alternate database name to be specified for DBOtherMap itself! */
0391: if (!"com.jcorporate.expresso.services.dbobj.DBOtherMap"
0392: .equals(myClassName)) {
0393: String otherdbname = StringUtil.notNull(ConfigManager
0394: .getOtherDbLocation(newOther, myClassName));
0395:
0396: if (!otherdbname.equals("")) {
0397: mappedContext = otherdbname;
0398:
0399: if (log.isDebugEnabled()) {
0400: log.debug("Object '" + myClassName
0401: + "' mapped to database '" + dbKey + "'");
0402: }
0403: }
0404: } /* if we are not DBOtherMap */
0405:
0406: //Clear the connectionPool if it doesn't exist.
0407: myPool = null;
0408:
0409: this .setMappedDataContext(mappedContext);
0410: } /* setDBName(String) */
0411:
0412: /**
0413: * Retrieve the database object's metadata
0414: *
0415: * @return a built DataObjectMetaData
0416: */
0417: public final DataObjectMetaData getMetaData() {
0418: return getDef();
0419: }
0420:
0421: /**
0422: * Retrieve the JDBCObjectMetaData
0423: *
0424: * @return the JDBCObjectMetadata. [Actually an instance of DataObjectMetaData,
0425: * but this way it's type correct]
0426: */
0427: public final JDBCObjectMetaData getJDBCMetaData() {
0428: return (JDBCObjectMetaData) getMetaData();
0429: }
0430:
0431: /**
0432: * Return the DBObjectRef object that contains the definition of the current
0433: * DBObject. If there isn't one in the dbobjMetadata hashmap, initialize a new one
0434: * as required
0435: *
0436: * @return static the DBObject Definition
0437: */
0438: protected final DBObjectDef getDef() {
0439: DBObjectDef myDef = (DBObjectDef) sMetadataMap
0440: .get(this .myClassName);
0441:
0442: return myDef;
0443: } /* getRef() */
0444:
0445: /**
0446: * Construction method to allow for specialized metadata with specialized
0447: * fields other than DBObjectDef. Override in classes that need custom
0448: * derived from DBObjectDef classes.
0449: *
0450: * @return DBOBjectDef derived instance
0451: * @throws DBException upon construction error
0452: */
0453: protected DBObjectDef constructNewMetaData() throws DBException {
0454: return new DBObjectDef();
0455: }
0456:
0457: /**
0458: * Returns the checkzero update as defined by the object's metadata.
0459: *
0460: * @return true if checkZeroUpdate is unabled
0461: */
0462: public boolean checkZeroUpdate() {
0463: return getDef().checkZeroUpdate();
0464: }
0465:
0466: /**
0467: * Set the name of the db context that was the "original" context
0468: * for this object - e.g. before any database mapping took place.
0469: *
0470: * @param newOriginalName new value to set
0471: */
0472: protected void setOriginalDBName(String newOriginalName) {
0473: mappedDataContext = newOriginalName;
0474: }
0475:
0476: protected void setMappedDataContext(String newMappedName) {
0477: mappedDataContext = newMappedName;
0478: }
0479:
0480: /**
0481: * This tells the buildWhereClause to either respect case (true) or
0482: * ignore case (false). You can call this method before doing a search and
0483: * retrieve if you want to match without worrying about case. For example
0484: * if you were to call this method with isCaseSensitiveQuery = FALSE
0485: * then this comparison would match in the search:
0486: * <p/>
0487: * vendor_name actual value = "My Name"
0488: * <p/>
0489: * query value = "my name"
0490: * <p/>
0491: * This would match in a search and retrieve.
0492: * <p/>
0493: * author Adam Rossi, PlatinumSolutions
0494: *
0495: * @param caseSensitiveQuery boolean
0496: */
0497: public void setCaseSensitiveQuery(boolean caseSensitiveQuery) {
0498:
0499: this .caseSensitiveQuery = caseSensitiveQuery;
0500: }
0501:
0502: /**
0503: * Build an appropriate String for use in the select part of an SQL statement
0504: * by doing the
0505: *
0506: * @param fieldName The name of the field to be handled
0507: * @return The portion of the select clause with the appropriate function
0508: * wrapped around it
0509: */
0510: public String selectFieldString(String fieldName)
0511: throws DBException {
0512: DataFieldMetaData oneField = getFieldMetaData(fieldName);
0513:
0514: try {
0515: JDBCConfig myConfig = null;
0516: // String fieldType = oneField.getTypeString();
0517:
0518: if (oneField.isDateOnlyType()) {
0519: myConfig = ConfigManager
0520: .getJdbcRequired(getDataContext());
0521:
0522: if (!StringUtil.notNull(
0523: myConfig.getDateSelectFunction()).equals("")) {
0524: return StringUtil.replace(myConfig
0525: .getDateSelectFunction(), "%s", fieldName);
0526: }
0527: }
0528: if (oneField.isTimeType()) {
0529: myConfig = ConfigManager
0530: .getJdbcRequired(getDataContext());
0531:
0532: if (!StringUtil.notNull(
0533: myConfig.getTimeSelectFunction()).equals("")) {
0534: return StringUtil.replace(myConfig
0535: .getTimeSelectFunction(), "%s", fieldName);
0536: }
0537: }
0538: if (oneField.isDateTimeType()) {
0539: myConfig = ConfigManager
0540: .getJdbcRequired(getDataContext());
0541:
0542: if (!StringUtil.notNull(
0543: myConfig.getDateTimeSelectFunction())
0544: .equals("")) {
0545: return StringUtil.replace(myConfig
0546: .getDateTimeSelectFunction(), "%s",
0547: fieldName);
0548: }
0549: }
0550: } catch (ConfigurationException ce) {
0551: throw new DBException(ce);
0552: }
0553:
0554: return fieldName;
0555: } /* selectFieldString(String) */
0556:
0557: /**
0558: * Return the value of this field, placing double quotes around it if the
0559: * field's datatype requires it.
0560: *
0561: * @param fieldName The name of the field to be used
0562: * @param rangeString the appropriately formatted string
0563: * @return A string, quoted if necessary, to be used in building an SQL statement
0564: * @throws DBException If there is no such field or it's value cannot be
0565: * determined
0566: */
0567: public String quoteIfNeeded(String fieldName, String rangeString)
0568: throws DBException {
0569: DataFieldMetaData oneField = getFieldMetaData(fieldName);
0570: if (oneField == null) {
0571: throw new DBException("(" + this .myClassName
0572: + ") No such field as " + fieldName);
0573: }
0574:
0575: boolean noTrim = false;
0576: if (!oneField.isMasked() && !isGlobalMasked()) {
0577: try {
0578: noTrim = ConfigManager.getJdbcRequired(
0579: this .getMappedDataContext()).isStringNotTrim();
0580: } catch (ConfigurationException ce) {
0581: throw new DataException(ce);
0582: }
0583: }
0584:
0585: String fieldValue = getSerialForm(oneField);
0586:
0587: /* if the field is null, we don't need to worry about quotes */
0588: if (fieldValue == null) {
0589: return null;
0590: }
0591:
0592: if (rangeString != null) {
0593: fieldValue = fieldValue.substring(rangeString.length());
0594: }
0595:
0596: /* if the field is null, we don't need to worry about quotes */
0597: if (fieldValue == null) {
0598: return null;
0599: }
0600:
0601: if ((oneField.isBooleanType() || "bit"
0602: .equalsIgnoreCase(oneField.getTypeString()))
0603: && fieldValue.length() == 0) {
0604: return null;
0605: }
0606:
0607: if (oneField.isNumericType()) {
0608: if (fieldValue.length() == 0) {
0609: return null;
0610: }
0611:
0612: return fieldValue.trim();
0613: } /* if a numeric type */
0614:
0615: if (oneField.isQuotedTextType()) {
0616: if (rangeString != null) {
0617: return fieldValue;
0618: }
0619:
0620: //
0621: //Fix so we don't get escaped double-single quotes accidentally
0622: //
0623: if (fieldValue.length() == 0) {
0624: return "''";
0625: }
0626:
0627: FastStringBuffer returnValue = FastStringBuffer
0628: .getInstance();
0629: String returnString = null;
0630: try {
0631: String value = "";
0632: if (noTrim) {
0633: value = fieldValue;
0634: } else {
0635: value = fieldValue.trim();
0636: }
0637: returnValue.append("\'");
0638: // returnValue.append(this.getConnectionPool().getEscapeHandler().escapeString(fieldValue.trim()));
0639: returnValue.append(this .getConnectionPool()
0640: .getEscapeHandler().escapeString(value));
0641: returnValue.append("\'");
0642: returnString = returnValue.toString();
0643: } finally {
0644: returnValue.release();
0645: returnValue = null;
0646: }
0647: return returnString;
0648: } /* if a quoted type */
0649:
0650: if (oneField.isDateType()) {
0651: if (rangeString != null) {
0652: return fieldValue;
0653: }
0654: FastStringBuffer returnValue = FastStringBuffer
0655: .getInstance();
0656: String returnString = null;
0657: try {
0658: returnValue.append("\'");
0659: returnValue.append(fieldValue);
0660: returnValue.append("\'");
0661: returnString = returnValue.toString();
0662: } finally {
0663: returnValue.release();
0664: returnValue = null;
0665: }
0666: return returnString;
0667: }
0668:
0669: //
0670: //We don't care about rangestrings in boolean types.... they don't
0671: //exactly make sense.
0672: //
0673: if (oneField.isBooleanType()) {
0674: try {
0675: boolean nativeBoolean = ConfigManager.getContext(
0676: this .getDataContext()).getJdbc().isNativeBool();
0677:
0678: if (!nativeBoolean) {
0679: FastStringBuffer returnValue = FastStringBuffer
0680: .getInstance();
0681: String returnString = null;
0682: try {
0683: returnValue.append("\'");
0684: returnValue.append(fieldValue.trim());
0685: returnValue.append("\'");
0686: returnString = returnValue.toString();
0687: } finally {
0688: returnValue.release();
0689: returnValue = null;
0690: }
0691: return returnString;
0692: }
0693: } catch (ConfigurationException ce) {
0694: throw new DBException(ce);
0695: }
0696: }
0697:
0698: if (noTrim) {
0699: return fieldValue;
0700: } else {
0701: return fieldValue.trim();
0702: }
0703: } /* quoteIfNeeded(String) */
0704:
0705: /**
0706: * Set a specific DB connection for use with this db object. If you do not set
0707: * a connection, the db object will request it's own connection from the
0708: * appropriate connection pool & release it again after every operation (e.g.
0709: * add, update, etc). It is important to use your own explicit connection when
0710: * dealing with a database transactional environment (e.g. commit(), rollback()).
0711: *
0712: * @param newConnection The new DBConnection object to be used by this DB Object
0713: */
0714: public synchronized void setConnection(DBConnection newConnection)
0715: throws DBException {
0716: setConnection(newConnection, newConnection.getDataContext());
0717: } /* setConnection(DBConnection) */
0718:
0719: /**
0720: * <p/>
0721: * Set a specific DB connection for use with this db object. If you do not set
0722: * a connection, the db object will request it's own connection from the
0723: * appropriate connection pool & release it again after every operation (e.g.
0724: * add, update, etc). It is important to use your own explicit connection when
0725: * dealing with a database transactional environment (e.g. commit(), rollback()).
0726: * </p>
0727: * <p>The difference between this and setConnection(DBConnection) is that this
0728: * is used for using otherDB capabilities within a transaction. So you use
0729: * a dbconnection from your other pool, but the setup tables are in a different
0730: * context</p>
0731: *
0732: * @param newConnection The new DBConnection object to be used by this DB Object
0733: * @param setupTablesContext the data context that is used for the expresso setup tables.
0734: * @see #setConnection(DBConnection)
0735: */
0736: public synchronized void setConnection(DBConnection newConnection,
0737: String setupTablesContext) throws DBException {
0738: localConnection = newConnection;
0739: this .setDataContext(setupTablesContext);
0740: }
0741:
0742: /**
0743: * Refactoring to split the execution of a query statement into the query
0744: * part and the load part. The DBConnection returned will have the query
0745: * already executed.
0746: * <p/>
0747: * SIDE-EFFECT: custom 'where' clause is set to null.
0748: *
0749: * @param retrievedFieldList instantiate an ArrayList and the function will
0750: * fill out the field order that the SQL statement will have it's fields in.
0751: * @return connection that has already executed the search statement.
0752: * <p/>
0753: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
0754: * @throws DBException upon database communication error
0755: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
0756: */
0757: public DBConnection createAndExecuteSearch(
0758: java.util.ArrayList retrievedFieldList) throws DBException {
0759: boolean needComma = false;
0760:
0761: if (recordSet == null) {
0762: recordSet = new ArrayList();
0763: } else {
0764: recordSet.clear();
0765: }
0766:
0767: myUpdates = null;
0768:
0769: DBConnection myConnection = null;
0770: JDBCObjectMetaData myMetadata = this .getJDBCMetaData();
0771: FastStringBuffer myStatement = FastStringBuffer.getInstance();
0772: try {
0773: if (localConnection != null) {
0774: myConnection = localConnection;
0775: } else {
0776: myConnection = this .getConnectionPool().getConnection(
0777: this .myClassName);
0778: }
0779:
0780: myStatement.append("SELECT ");
0781:
0782: if (myConnection.getLimitationPosition() == DBConnection.LIMITATION_AFTER_SELECT
0783: && (offsetRecord > 0 || maxRecords > 0)) {
0784:
0785: // Insert limitation stub after table nomination
0786: String limitStub = makeLimitationStub(myConnection);
0787:
0788: myStatement.append(" ");
0789: myStatement.append(limitStub);
0790: myStatement.append(" ");
0791: }
0792:
0793: if (anyFieldsDistinct) {
0794: String oneFieldName = null;
0795: myStatement.append(" ");
0796: myStatement.append(getConnectionPool()
0797: .getDistinctRowsetKeyword());
0798: myStatement.append(" ");
0799:
0800: for (Iterator i = getDistinctFieldArrayList()
0801: .iterator(); i.hasNext();) {
0802: oneFieldName = (String) i.next();
0803: retrievedFieldList.add(oneFieldName);
0804:
0805: if (needComma) {
0806: myStatement.append(", ");
0807: }
0808:
0809: myStatement.append(selectFieldString(oneFieldName));
0810: needComma = true;
0811: }
0812: } else if (anyFieldsToRetrieve) { /* for each distinct field */
0813: String oneFieldName = null;
0814:
0815: for (Iterator i = getFieldsToRetrieveIterator(); i
0816: .hasNext();) {
0817: oneFieldName = (String) i.next();
0818:
0819: if (needComma) {
0820: myStatement.append(", ");
0821: }
0822:
0823: retrievedFieldList.add(oneFieldName);
0824: myStatement.append(selectFieldString(oneFieldName));
0825: needComma = true;
0826: }
0827: } else { /* for each field */
0828: for (Iterator i = myMetadata.getAllFieldsMap().values()
0829: .iterator(); i.hasNext();) {
0830: DBField oneField = (DBField) i.next();
0831:
0832: if (!oneField.isVirtual()
0833: && !oneField.isBinaryObjectType()) {
0834: if (needComma) {
0835: myStatement.append(", ");
0836: }
0837:
0838: retrievedFieldList.add(oneField.getName());
0839: myStatement.append(selectFieldString(oneField
0840: .getName()));
0841: needComma = true;
0842: } /* if field is not virtual */
0843:
0844: } /* for each field */
0845:
0846: } /* else this is a regular (non-distinct) search */
0847:
0848: myStatement.append(" FROM ");
0849: myStatement.append(myMetadata.getTargetSQLTable(this
0850: .getDataContext()));
0851: if (myConnection.getLimitationPosition() == DBConnection.LIMITATION_AFTER_TABLE
0852: && (offsetRecord > 0 || maxRecords > 0)) {
0853:
0854: // Insert limitation stub after table nomination
0855: String limitStub = makeLimitationStub(myConnection);
0856: myStatement.append(" ");
0857: myStatement.append(limitStub);
0858: myStatement.append(" ");
0859: }
0860:
0861: String whereClause;
0862:
0863: if (customWhereClause != null) {
0864: if (appendCustomWhere) {
0865: whereClause = buildWhereClause(true)
0866: + customWhereClause;
0867: appendCustomWhere = false;
0868: } else {
0869: whereClause = customWhereClause;
0870: }
0871: myStatement.append(whereClause);
0872: customWhereClause = null;
0873: } else {
0874: whereClause = buildWhereClause(true);
0875: myStatement.append(whereClause);
0876: }
0877: if (myConnection.getLimitationPosition() == DBConnection.LIMITATION_AFTER_WHERE
0878: && (offsetRecord > 0 || maxRecords > 0)) {
0879:
0880: // Insert limitation stub after table nomination
0881: String limitStub = makeLimitationStub(myConnection);
0882:
0883: if (whereClause.length() > 0) {
0884: myStatement.append(" AND");
0885: }
0886:
0887: myStatement.append(" ");
0888: myStatement.append(limitStub);
0889: myStatement.append(" ");
0890: }
0891: /* Add the ORDER BY clause if any sortKeys are specified */
0892: if (sortKeys != null && sortKeys.size() > 0) {
0893: myStatement.append(" ORDER BY ");
0894:
0895: boolean needComma2 = false;
0896:
0897: for (Iterator i = sortKeys.iterator(); i.hasNext();) {
0898: if (needComma2) {
0899: myStatement.append(", ");
0900: }
0901:
0902: myStatement.append((String) i.next());
0903: needComma2 = true;
0904: }
0905: if (myConnection.getLimitationPosition() == DBConnection.LIMITATION_AFTER_ORDER_BY
0906: && (offsetRecord > 0 || maxRecords > 0)) {
0907: myStatement.append(" ");
0908: myStatement
0909: .append(makeLimitationStub(myConnection));
0910: }
0911: }
0912:
0913: myConnection.execute(myStatement.toString());
0914: return myConnection;
0915: } catch (DBException ex) {
0916: if (myConnection != null && localConnection == null) {
0917: myConnection.release();
0918: }
0919: log.error("Error building and executing search statement",
0920: ex);
0921: throw ex;
0922: } finally {
0923: myStatement.release();
0924: }
0925: }
0926:
0927: /**
0928: * Fills the given constructed data object with data from the connection
0929: * given the field order specified in retrievedFieldList. Similar to
0930: * loadFromConnection but much faster because the connection fields are
0931: * retrieved via number instead of name.
0932: *
0933: * @param myObj the Object to have the values filled out. [in/out parameter]
0934: * @param myConnection [in] the connection to retrieve the fields from
0935: * @param retrievedFieldList [in] An array of Strings representing the field
0936: * names to retrieve in the order they exist in the connection.
0937: * @throws DBException upon error
0938: */
0939: public void loadFromConnection(JDBCDataObject myObj,
0940: DBConnection myConnection, ArrayList retrievedFieldList)
0941: throws DBException {
0942: int i = 1;
0943: String oneFieldName = null;
0944: Object tmpData = null;
0945:
0946: for (Iterator it = retrievedFieldList.iterator(); it.hasNext();) {
0947: oneFieldName = (String) it.next();
0948: DataFieldMetaData oneDBField = getFieldMetaData(oneFieldName);
0949:
0950: try {
0951: // * @author Yves Henri AMAIZO
0952: // Handle correctly date from resultSet data retrieve from Database
0953: if (oneDBField.isDateType()) {
0954: tmpData = getCustomStringFieldValue(myConnection,
0955: oneDBField.getName());
0956: } else {
0957: if (!oneDBField.isLongBinaryType()
0958: && !oneDBField.isLongCharacterType()) {
0959: if (myConnection.isStringNotTrim()) {
0960: tmpData = myConnection.getStringNoTrim(i);
0961: } else {
0962: tmpData = myConnection.getString(i);
0963: }
0964: } else {
0965: if (oneDBField.isLongBinaryType()) {
0966: tmpData = null;
0967: InputStream is = myConnection
0968: .getBinaryStream(i);
0969: if (is != null) {
0970: byte[] bstr = new byte[LONGBINARY_READ_DEFAULT_SIZE];
0971: int j = is.read(bstr);
0972: if (j > 0) {
0973: byte[] content = new byte[j];
0974: System.arraycopy(bstr, 0, content,
0975: 0, j);
0976: tmpData = content;
0977: }
0978: }
0979: } else {
0980: tmpData = myConnection.getStringNoTrim(i);
0981: }
0982: }
0983:
0984: }
0985: } catch (DBException de) {
0986: throw new DBException("Error retrieving field '"
0987: + oneFieldName, de);
0988: } catch (Exception de) {
0989: throw new DBException(
0990: "Not DBException Error retrieving field '"
0991: + oneFieldName, de);
0992: }
0993:
0994: i++;
0995: myObj.set(oneFieldName, tmpData);
0996: } /* for each retrieved field name */
0997:
0998: myObj.setDataContext(this .getDataContext());
0999: myObj.setStatus(BaseDataObject.STATUS_CURRENT);
1000: }
1001:
1002: /**
1003: * Creates the limitation syntax optimisation stub
1004: * to embed inside the SQL command that performs
1005: * search and retrieve.
1006: * <p/>
1007: * <p>This method takes the limitation syntax string
1008: * and performs a string replacement on the following
1009: * tokens
1010: * <p/>
1011: * <ul>
1012: * <p/>
1013: * <li><b>%offset%</b><li><br>
1014: * the number of rows in the <code>ResultSet</code> to skip
1015: * before reading the data.
1016: * <p/>
1017: * <li><b>%maxrecord%</b><li><br>
1018: * the maximum number of rows to read from the <code>ResultSet</code>.
1019: * Also known as the <i>rowlength</i>.
1020: * <p/>
1021: * <li><b>%endrecord%</b><li><br>
1022: * the last record of in the <code>ResultSet</code> that the
1023: * search and retrieved should retrieve. The end record number
1024: * is equal to <code>( %offset% + %maxrecord% - 1 )</code>
1025: * <p/>
1026: * </ul>
1027: * <p/>
1028: * </p>
1029: * <p/>
1030: * <p/>
1031: * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
1032: *
1033: * @param theConnection the db connection to make this stub from
1034: * @return the limitation syntax stub string
1035: * @see #searchAndRetrieveList()
1036: * @see #setOffsetRecord( int )
1037: * @see #setMaxRecords( int )
1038: */
1039: protected String makeLimitationStub(DBConnection theConnection) {
1040: String limit = theConnection.getLimitationSyntax();
1041: int offset = this .getOffsetRecord();
1042: int maxrec = this .getMaxRecords();
1043: int endrec = offset + maxrec - 1;
1044: limit = StringUtil.replace(limit, "%offset%", Integer
1045: .toString(offset));
1046: limit = StringUtil.replace(limit, "%maxrecords%", Integer
1047: .toString(maxrec));
1048:
1049: // limit = StringUtil.replace( limit, "%length%", Integer.toString( maxrec ) );
1050: limit = StringUtil.replace(limit, "%endrecord%", Integer
1051: .toString(endrec));
1052:
1053: return limit;
1054: } /* makeLimitationStub(DBConnection) */
1055:
1056: /**
1057: * Get a special <code>ArrayList</code> object list of all
1058: * of the fields in this object that are set to <b>distinct</b>
1059: *
1060: * @return An Iterator of all the
1061: * <b>distinct</b> fieldNames in this object
1062: * @throws DBException If the list cannot be retrieved
1063: * <p/>
1064: * author Peter Pilgrim <peter.pilgrim@db.com>
1065: */
1066: public ArrayList getDistinctFieldArrayList() throws DBException {
1067: ArrayList arl = new ArrayList();
1068:
1069: if (distinctFields == null) {
1070: return arl;
1071: }
1072: for (Iterator i = this .getMetaData().getFieldListArray()
1073: .iterator(); i.hasNext();) {
1074: String fieldName = (String) i.next();
1075:
1076: if (distinctFields.containsKey(fieldName)) {
1077: arl.add(fieldName);
1078: }
1079: }
1080:
1081: return arl;
1082: }
1083:
1084: /**
1085: * Get a special <code>Iterator</code> object list of all
1086: * of the fields in this object that are set to <b>retrieve</b>
1087: * <p>Author Yves henri Amaizo <amy_amaizo@compuserve.com></p>
1088: *
1089: * @return An Iterator of all the
1090: * <b>retrieve</b> fieldNames in this object
1091: * @throws DBException If the list cannot be retrieved
1092: */
1093: public Iterator getFieldsToRetrieveIterator() throws DBException {
1094:
1095: //Do a dummy so we don't throw null pointer exceptions
1096: if (retrieveFields == null) {
1097: return new HashMap().keySet().iterator();
1098: }
1099:
1100: return retrieveFields.keySet().iterator();
1101: }
1102:
1103: /**
1104: * Build and return a string consisting of an SQL 'where' clause
1105: * using the current field values as criteria for the search. See
1106: * setCustomWhereClause for information on specifying a more complex where clause.
1107: *
1108: * @param useAllFields True if all fields are to be used,
1109: * false for only key fields
1110: * @return The where clause to use in a query.
1111: */
1112: public String buildWhereClause(boolean useAllFields)
1113: throws DBException {
1114:
1115: return sJdbcUtil.buildWhereClause(this , useAllFields);
1116: } /* buildWhereClause(boolean) */
1117:
1118: /**
1119: * Build and return a FastStringBuffer ring consisting of an SQL 'where' clause
1120: * using the current field values as criteria for the search. See
1121: * setCustomWhereClause for information on specifying a more complex where clause.
1122: *
1123: * @param useAllFields True if all fields are to be used,
1124: * false for only key fields
1125: * @param allocatedBuffer - An already allocated FastStringBuffer to fill out.
1126: * This allows for compatability with, for example, object pools.
1127: * @return A FastStringBuffer containing the "where" clause for the SQL statement
1128: */
1129: protected FastStringBuffer buildWhereClauseBuffer(
1130: boolean useAllFields, FastStringBuffer allocatedBuffer)
1131: throws DBException {
1132:
1133: try {
1134: return sJdbcUtil.buildWhereClauseBuffer(this , useAllFields,
1135: allocatedBuffer);
1136: } catch (DataException ex) {
1137: throw new DBException(ex.getMessage());
1138: }
1139: }
1140:
1141: /**
1142: * Get the JDBC Util functions
1143: *
1144: * @return JDBCUtil class.
1145: */
1146: protected JDBCUtil getJDBCUtil() {
1147: return sJdbcUtil;
1148: }
1149:
1150: /**
1151: * Use this function to acquire the Executor interface that is associated
1152: * with this data object
1153: *
1154: * @return DataExecutorInterface or null if no Executor has been associated
1155: * with this object
1156: */
1157: public DataExecutorInterface getExecutor() {
1158: return sDataExecutor;
1159: }
1160:
1161: /**
1162: * Use this function to acquire the DataQueryInterface that is associated
1163: * with this data object
1164: *
1165: * @return DataQueryInterface or null if no QueryInterface has been
1166: * associated with this object
1167: */
1168: public DataQueryInterface getQueryInterface() {
1169: return sDataQueryObject;
1170: }
1171:
1172: /**
1173: * <p>This convenience method retrieve through the resultSet.MetaData
1174: * the date value of field define as date or time <code>DBObject</code>
1175: *
1176: * @param connection The DBConnection
1177: * @param oneFieldName the field name to get the custom field value from
1178: * @return Custom String value retrieve from resultSet according to metaData.
1179: * @throws DBException author Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1180: */
1181: public String getCustomStringFieldValue(DBConnection connection,
1182: String oneFieldName) throws DBException {
1183: ConfigJdbc myConfig = null;
1184: String oneFieldValue = null;
1185: DataFieldMetaData fieldMetadata = this
1186: .getFieldMetaData(oneFieldName);
1187: try {
1188: myConfig = ConfigManager.getJdbcRequired(getDataContext());
1189: } catch (ConfigurationException ce) {
1190: throw new DBException(ce);
1191: }
1192: if (fieldMetadata.isDateTimeType()) {
1193: if (StringUtil.notNull(myConfig.getDateTimeSelectFormat())
1194: .length() > 0) {
1195: oneFieldValue = DateTime.getDateTimeForDB(connection
1196: .getTimestamp(oneFieldName), dbKey);
1197: } else {
1198: oneFieldValue = connection.getString(oneFieldName);
1199: }
1200: }
1201: if (fieldMetadata.isTimeType()) {
1202: if (!StringUtil.notNull(myConfig.getTimeSelectFormat())
1203: .equals("")) {
1204: oneFieldValue = DateTime.getTimeForDB(connection
1205: .getTime(oneFieldName), dbKey);
1206: } else {
1207: oneFieldValue = connection.getString(oneFieldName);
1208: }
1209: }
1210: if (fieldMetadata.isDateOnlyType()) {
1211: if (!StringUtil.notNull(myConfig.getDateSelectFormat())
1212: .equals("")) {
1213: oneFieldValue = DateTime.getDateForDB(connection
1214: .getDate(oneFieldName), dbKey);
1215: } else {
1216: oneFieldValue = connection.getString(oneFieldName);
1217: }
1218: }
1219: return oneFieldValue;
1220: } /* getCustomStringFieldValue() */
1221:
1222: /**
1223: * <p>Return local DBConnection value
1224: * <p/>
1225: * author Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1226: *
1227: * @return DBConnection
1228: */
1229: public DBConnection getLocalConnection() {
1230: return localConnection;
1231: }
1232:
1233: /**
1234: * Refactoring to split the execution of a query statement into the query
1235: * part and the load part. The DBConnection returned will have the query
1236: * already executed, it is ready to
1237: *
1238: * @param retrievedFieldList instantiate an ArrayList and the function will
1239: * fill out the field order that the SQL statement will have it's fields in.
1240: * @return connection that has already executed the search statement.
1241: * @throws DBException upon database communication error
1242: */
1243: public DBConnection createAndRunStoreProcedure(
1244: java.util.ArrayList retrievedFieldList) throws DBException {
1245: if (recordSet == null) {
1246: recordSet = new ArrayList();
1247: } else {
1248: recordSet.clear();
1249: }
1250:
1251: myUpdates = null;
1252:
1253: DBConnection myConnection = null;
1254: FastStringBuffer myStatement = FastStringBuffer.getInstance();
1255: try {
1256: if (localConnection != null) {
1257: myConnection = localConnection;
1258: } else {
1259: myConnection = this .getConnectionPool().getConnection(
1260: this .myClassName);
1261: }
1262:
1263: int nbParams = this .getDef().getFieldListArray().size();
1264: if (this .getDef().isReturningValue()) {
1265: myStatement.append("{? = call ");
1266: nbParams--;
1267: } else {
1268: myStatement.append("{call ");
1269: }
1270: myStatement.append(this .getJDBCMetaData().getTargetTable());
1271:
1272: if (nbParams > 0) {
1273: myStatement.append("(?");
1274: for (int i = 1; i < nbParams; i++) {
1275: myStatement.append(", ?");
1276: }
1277: myStatement.append(")");
1278: }
1279: myStatement.append("}");
1280:
1281: CallableStatement theStroreProcedureStatement = myConnection
1282: .createCallableStatement(myStatement.toString());
1283: sJdbcUtil.buildStoreProcedureCallableStatement(this ,
1284: theStroreProcedureStatement);
1285: myConnection.executeProcedure();
1286:
1287: return myConnection;
1288: } catch (DBException ex) {
1289: if (myConnection != null && localConnection == null) {
1290: myConnection.release();
1291: }
1292: log
1293: .error(
1294: "Error building and running store procedure statement",
1295: ex);
1296: throw ex;
1297: } finally {
1298: myStatement.release();
1299: }
1300: } /* createAndRunStoreProcedure(java.util.ArrayList) */
1301:
1302: /**
1303: * Add a new field to the list of fields that are part of this
1304: * object's list of input parameters. Called after all of the "addField" calls in the setupFields()
1305: * method to specify which fields make up the primary key of this object.
1306: *
1307: * @param inFieldName The name of the field to add as part of the key
1308: * @throws DBException if the field name is not valid or the field
1309: * allows nulls
1310: */
1311: protected synchronized void addInParam(String inFieldName)
1312: throws DBException {
1313: getDef().addInParam(inFieldName);
1314: } /* addInParam(String) */
1315:
1316: /**
1317: * Add a new field to the list of fields that are part of this
1318: * object's list of output parameter. Called after all of the "addField" calls in the setupFields()
1319: * method to specify which fields make up the primary key of this object.
1320: *
1321: * @param outFieldName The name of the field to add as part of the key
1322: * @throws DBException if the field name is not valid or the field
1323: * allows nulls
1324: */
1325: protected synchronized void addOutParam(String outFieldName)
1326: throws DBException {
1327: getDef().addOutParam(outFieldName);
1328: } /* addOutParam(String) */
1329:
1330: /**
1331: * Set the target store procedure for this DBObject.
1332: *
1333: * @param theStoreProcedure Table for this object
1334: * @throws DBException upon execution error.
1335: */
1336: public synchronized void setTargetStoreProcedure(
1337: String theStoreProcedure) throws DBException {
1338: getDef().setTargetStoreProcedure(theStoreProcedure);
1339: } /* setTargetStoreProcedure(String) */
1340:
1341: /**
1342: * Run a particular store procedure in the database into this object's fields
1343: *
1344: * @throws DBException If the record could not be retrieved.
1345: */
1346: public void runStoredProcedure() throws DBException {
1347:
1348: this .getExecutor().runStoreProcedure(this );
1349:
1350: if (getDef().isLoggingEnabled()) {
1351: myUpdates = null;
1352: }
1353:
1354: this .setStatus(BaseDataObject.STATUS_CURRENT);
1355: }
1356:
1357: /**
1358: * Run a particular store procedure in the database into this object's fields
1359: *
1360: * @return Vector A vector of new database objects containing the results
1361: * of the search
1362: * @throws DBException If the record could not be retrieved.
1363: * @todo one line below was adapted during move from dbobject.java, and is probably wrong.; after correction, make method public 10/04 Larry
1364: */
1365: protected synchronized ArrayList runStoredProcedureAndRetrieveList()
1366: throws DBException {
1367:
1368: ArrayList retrievedFieldList = new ArrayList();
1369:
1370: DBConnection myConnection = null;
1371: try {
1372: myConnection = this
1373: .createAndRunStoreProcedure(retrievedFieldList);
1374:
1375: int recordCount = 0;
1376: int retrieveCount = 0;
1377:
1378: while (myConnection.next()) {
1379: recordCount++;
1380: retrieveCount++;
1381:
1382: //If there's limitation syntax on, then the first record will be the
1383: //maximum record.
1384: if (retrieveCount < offsetRecord
1385: && offsetRecord > 0
1386: && myConnection.getLimitationPosition() == DBConnection.LIMITATION_DISABLED) {
1387: continue;
1388: } else if (retrieveCount == offsetRecord
1389: && offsetRecord > 0
1390: && myConnection.getLimitationPosition() == DBConnection.LIMITATION_DISABLED) {
1391: recordCount = 0; //Reset count for counting for max records.
1392: continue; //Skip this record... next one, we will start loading.
1393: }
1394: if ((recordCount > maxRecords) && (maxRecords > 0)) {
1395: this .setAttribute("More Records", "Y");
1396: break;
1397: }
1398:
1399: //Only allocate if we're gonna load this record
1400: JDBCDataObject myObj = (JDBCDataObject) this .getClass()
1401: .newInstance(); // @todo Henri, change as necessary; larry 10/04
1402: this .loadFromConnection(myObj, myConnection,
1403: retrievedFieldList);
1404: recordSet.add(myObj);
1405: }
1406: } catch (DBException de) {
1407: log
1408: .error(
1409: "Error performing runStoreProcedureAndRetrieveList",
1410: de);
1411: throw new DBException(de);
1412: } catch (Throwable t) {
1413: log
1414: .error(
1415: "Error performing runStoreProcedureAndRetrieveList",
1416: t);
1417: throw new DBException(
1418: "Error performing runStoreProcedureAndRetrieveList",
1419: t);
1420: } finally {
1421: if (localConnection == null) {
1422: if (myConnection != null) {
1423: this .getConnectionPool().release(myConnection);
1424: }
1425: }
1426: }
1427:
1428: return recordSet;
1429: } /* runStoreProcedureAndRetrieveList() */
1430: }
|