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:
0065: package com.jcorporate.expresso.core.dataobjects.jdbc;
0066:
0067: import com.jcorporate.expresso.core.dataobjects.DataException;
0068: import com.jcorporate.expresso.core.dataobjects.DataExecutorInterface;
0069: import com.jcorporate.expresso.core.dataobjects.DataField;
0070: import com.jcorporate.expresso.core.dataobjects.DataFieldMetaData;
0071: import com.jcorporate.expresso.core.dataobjects.DataObject;
0072: import com.jcorporate.expresso.core.dataobjects.DataObjectMetaData;
0073: import com.jcorporate.expresso.core.dataobjects.DuplicateKeyException;
0074: import com.jcorporate.expresso.core.db.DBConnection;
0075: import com.jcorporate.expresso.core.db.DBConnectionPool;
0076: import com.jcorporate.expresso.core.db.DBException;
0077: import com.jcorporate.expresso.core.db.TypeMapper;
0078: import com.jcorporate.expresso.core.dbobj.DBField;
0079: import com.jcorporate.expresso.core.dbobj.DBObject;
0080: import com.jcorporate.expresso.core.dbobj.NextNumber;
0081: import com.jcorporate.expresso.core.misc.ConfigJdbc;
0082: import com.jcorporate.expresso.core.misc.ConfigManager;
0083: import com.jcorporate.expresso.core.misc.ConfigurationException;
0084: import com.jcorporate.expresso.core.misc.StringUtil;
0085: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
0086: import org.apache.log4j.Logger;
0087:
0088: import java.io.ByteArrayInputStream;
0089: import java.io.InputStream;
0090: import java.io.StringReader;
0091: import java.sql.BatchUpdateException;
0092: import java.sql.CallableStatement;
0093: import java.sql.Date;
0094: import java.sql.SQLException;
0095: import java.sql.SQLWarning;
0096: import java.sql.Timestamp;
0097: import java.util.ArrayList;
0098: import java.util.HashMap;
0099: import java.util.Iterator;
0100: import java.util.List;
0101: import java.util.Map;
0102:
0103: /**
0104: * Initial separation of DBObjects from the underlying JDBC code that gets executed. This is
0105: * part number 1 where we move a lot of the JDBC code over to this helper class. For round
0106: * #1 iteration, we're assuming that the valueObject is actually a DBObject that implements
0107: * the DataObject interface (which it does). This is because we're interested in
0108: * getting something working and demonstratable, and then slowly flesh out the DataObject
0109: * interface so that we can eventually completely prune this class completely
0110: * away from the DBObject class and work strictly with the DataObject Interface.
0111: *
0112: * @author Michael Rimov
0113: * @author Yves Henri AMAIZO <amy_amaizo@compuserve.com>
0114: * @since Expresso 5.0
0115: */
0116:
0117: public class JDBCExecutor implements DataExecutorInterface {
0118: private static final String myName = JDBCExecutor.class.getName()
0119: + ".";
0120: private static Logger log = Logger.getLogger(JDBCExecutor.class);
0121:
0122: transient public static final String LONGVARBINARY_TYPE = "longvarbinary";
0123: transient public static final String VARBINARY_TYPE = "varbinary";
0124: transient public static final String LONGVARCHAR_TYPE = "longvarchar";
0125:
0126: public static int LONGBINARY_READ_DEFAULT_SIZE = 262144;
0127:
0128: public JDBCExecutor() {
0129: }
0130:
0131: /**
0132: * Takes a <code>DataObject</code> and adds it to the underlying data source
0133: *
0134: * @param valueObject the <code>DataObject</code> to add.
0135: * @throws DataException upon error adding the object to the data source
0136: * @throws DuplicateKeyException if the object already existed in the data
0137: * source.
0138: * <p/>
0139: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
0140: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
0141: */
0142: public void add(DataObject valueObject) throws DataException,
0143: DuplicateKeyException {
0144: DBConnection myConnection = null;
0145: DBConnectionPool myPool = null;
0146: DBConnection localConnection = null;
0147:
0148: try {
0149: if (valueObject == null) {
0150: throw new IllegalArgumentException(myName
0151: + "add(DataObject): valueObject was null");
0152: }
0153:
0154: DBObject theObject = (DBObject) valueObject;
0155: JDBCObjectMetaData metadata = (JDBCObjectMetaData) valueObject
0156: .getMetaData();
0157:
0158: boolean needComma = false;
0159:
0160: //
0161: //The names of the database fiels that require prepared statements
0162: //to Add
0163: //
0164: List lobFieldOrder = null;
0165: List lobFieldType = null;
0166:
0167: //
0168: //The list of Strings to add to the file
0169: //
0170: List lobAdds = null;
0171:
0172: if (!haveAllKeysExceptAutoInc(theObject)) {
0173: throw new DataException("All key fields not present - "
0174: + "cannot add new record");
0175: }
0176:
0177: //
0178: //If myPool isn't set yet, then we need to use getDBName to
0179: //reset. FIXME: Have the system throw exceptions if DBName isn't
0180: //set instead.
0181: //
0182: myPool = DBConnectionPool.getInstance(theObject
0183: .getMappedDataContext());
0184: localConnection = theObject.getLocalConnection();
0185:
0186: theObject.checkAllRefsPublic();
0187:
0188: DBField oneField = null;
0189:
0190: /**
0191: * sqlCommand will eventually = sqlCommand + valuesCommand. The two
0192: * are assembled in parallel in the loop below.
0193: */
0194: FastStringBuffer sqlCommand = FastStringBuffer
0195: .getInstance();
0196: FastStringBuffer valuesCommand = FastStringBuffer
0197: .getInstance();
0198: String theSqlCommand = null;
0199: try {
0200:
0201: sqlCommand.append("INSERT INTO ");
0202: sqlCommand.append(metadata.getTargetSQLTable(theObject
0203: .getDataContext()));
0204: sqlCommand.append(" (");
0205:
0206: valuesCommand.append(") VALUES (");
0207:
0208: boolean needCommaValues = false;
0209: for (Iterator i = metadata.getAllFieldsMap().values()
0210: .iterator(); i.hasNext();) {
0211: oneField = (DBField) i.next();
0212:
0213: if (!oneField.isVirtual()) {
0214: theObject.checkField(oneField.getName(),
0215: theObject.getField(oneField.getName()));
0216:
0217: if (oneField.isBinaryObjectType()) {
0218: if (log.isDebugEnabled()) {
0219: log.debug("Skipping BINARY Object: "
0220: + oneField.getName());
0221: }
0222: continue;
0223: }
0224:
0225: if (needComma) {
0226: sqlCommand.append(", ");
0227: }
0228:
0229: sqlCommand.append(oneField.getName());
0230: needComma = true;
0231:
0232: if (needCommaValues) {
0233: valuesCommand.append(", ");
0234: }
0235: if (oneField.isDateType()) {
0236: Object tmpData = valueObject.get(oneField
0237: .getName());
0238: String data;
0239: //
0240: //FIXME allow for appropriate support of other data types.
0241: //
0242: if (tmpData == null) {
0243: data = null;
0244: } else if (tmpData instanceof String) {
0245: data = (String) tmpData;
0246: } else {
0247: data = tmpData.toString();
0248: }
0249:
0250: if (data == null || (data.length() == 0)) {
0251: valuesCommand.append("null");
0252: } else {
0253: valuesCommand.append(JDBCUtil
0254: .getInstance().formatDateTime(
0255: valueObject,
0256: oneField.getName()));
0257: }
0258: } else if (oneField.isAutoIncremented()) {
0259: String nextNumValue = Long
0260: .toString(NextNumber
0261: .getInstance()
0262: .getNext(
0263: theObject
0264: .getDataContext(),
0265: theObject,
0266: oneField.getName()));
0267: valueObject.set(oneField.getName(),
0268: nextNumValue);
0269: valuesCommand.append(theObject
0270: .quoteIfNeeded(oneField.getName(),
0271: null));
0272: } else if (oneField.isCharacterLongObjectType()
0273: || oneField.isLongBinaryType()) {
0274: Object tmpData = valueObject.get(oneField
0275: .getName());
0276: String data;
0277: //
0278: //FIXME allow for appropriate support of other data types.
0279: //
0280: if (tmpData == null) {
0281: data = null;
0282: } else if (tmpData instanceof String) {
0283: data = (String) tmpData;
0284: } else {
0285: data = tmpData.toString();
0286: }
0287: if (data != null) {
0288: if (lobAdds == null) {
0289: lobAdds = new ArrayList();
0290: lobFieldOrder = new ArrayList();
0291: lobFieldType = new ArrayList();
0292: }
0293: lobAdds.add(tmpData);
0294: lobFieldOrder.add(oneField.getName());
0295: lobFieldType.add(oneField
0296: .getTypeString());
0297: valuesCommand.append("?");
0298: } else {
0299: valuesCommand.append("null");
0300: }
0301: } else {
0302: valuesCommand.append(theObject
0303: .quoteIfNeeded(oneField.getName(),
0304: null));
0305: }
0306:
0307: needCommaValues = true;
0308: } /* if field is not virtual */
0309:
0310: } /* for each field */
0311:
0312: //Now we merge the values of the parallel loops
0313: sqlCommand.append(valuesCommand);
0314: sqlCommand.append(")");
0315: theSqlCommand = sqlCommand.toString();
0316: } catch (NullPointerException npe) {
0317: log.error("NPE", npe);
0318: throw new DataException("Null Pointer Exception", npe);
0319: } catch (DBException dbe) {
0320: log.error("DBException", dbe);
0321: throw dbe;
0322: } finally {
0323: sqlCommand.release();
0324: sqlCommand = null;
0325: valuesCommand.release();
0326: valuesCommand = null;
0327: }
0328:
0329: if (localConnection != null) {
0330: myConnection = localConnection;
0331: } else {
0332: try {
0333: myConnection = myPool.getConnection(getClass()
0334: .getName());
0335: } catch (java.util.ConcurrentModificationException cme) {
0336: cme.printStackTrace();
0337: log.error(cme);
0338: throw new DataException(
0339: "JDBCExecutor.add() ConcurrentModificationException",
0340: cme);
0341: }
0342: }
0343:
0344: if (lobAdds != null) {
0345: java.sql.PreparedStatement prepStatement = myConnection
0346: .createPreparedStatement(theSqlCommand);
0347: if (prepStatement == null) {
0348: try {
0349: SQLWarning sqlWarnings = myConnection
0350: .getConnection().getWarnings();
0351: while (sqlWarnings != null) {
0352: log
0353: .warn("SQL Warning creating prepared statement: "
0354: + sqlWarnings.toString());
0355: sqlWarnings = sqlWarnings.getNextWarning();
0356: }
0357: } catch (SQLException ex) {
0358: log
0359: .debug("Error getting warnings when prepared statement was null");
0360: }
0361:
0362: throw new DataException(
0363: "Returned Prepared Statement for "
0364: + theSqlCommand + " was null");
0365: }
0366:
0367: int size = lobFieldOrder.size();
0368: TypeMapper typeMapper = TypeMapper
0369: .getInstance(myConnection.getDataContext());
0370: for (int i = 0; i < size; i++) {
0371: String fieldName = (String) lobFieldOrder.get(i);
0372: DataFieldMetaData metaData = valueObject
0373: .getFieldMetaData(fieldName);
0374: Object nullValue = lobAdds.get(i);
0375: String myType = (String) lobFieldType.get(i);
0376: if (nullValue == null
0377: || "null".equals(nullValue)
0378: || (nullValue.toString().length() == 0 && (metaData
0379: .isNumericType() || metaData
0380: .isDateType()))) {
0381: int typeCode = typeMapper
0382: .getJavaSQLType(metaData
0383: .getTypeString());
0384: prepStatement.setNull(i + 1, typeCode);
0385: } else {
0386: if (!myType
0387: .equalsIgnoreCase(LONGVARBINARY_TYPE)
0388: && !myType
0389: .equalsIgnoreCase(VARBINARY_TYPE)
0390: && !myType
0391: .equalsIgnoreCase(LONGVARCHAR_TYPE)) {
0392: String value = (String) lobAdds.get(i);
0393: prepStatement.setString(i + 1, value);
0394: } else {
0395: if (myType
0396: .equalsIgnoreCase(LONGVARBINARY_TYPE)
0397: || myType
0398: .equalsIgnoreCase(VARBINARY_TYPE)) {
0399: byte[] data = (byte[]) lobAdds.get(i);
0400: ByteArrayInputStream theData = new ByteArrayInputStream(
0401: data);
0402: prepStatement.setBinaryStream(i + 1,
0403: theData, data.length);
0404: } else {
0405: if (myType
0406: .equalsIgnoreCase(LONGVARCHAR_TYPE)) {
0407: if (("oracle.jdbc.driver.OracleDriver"
0408: .equals(myConnection
0409: .getDBDriver()))) {
0410: String value = (String) lobAdds
0411: .get(i);
0412: java.io.Reader theData = new StringReader(
0413: value);
0414: prepStatement
0415: .setCharacterStream(
0416: i + 1, theData,
0417: value.length());
0418: } else {
0419: if ("interbase.interclient.Driver"
0420: .equals(myConnection
0421: .getDBDriver())) {
0422: String value = (String) lobAdds
0423: .get(i);
0424: byte[] data = value
0425: .getBytes();
0426: java.io.ByteArrayInputStream theData = new java.io.ByteArrayInputStream(
0427: data);
0428: prepStatement
0429: .setAsciiStream(
0430: i + 1,
0431: theData,
0432: data.length);
0433: } else {
0434: String value = (String) lobAdds
0435: .get(i);
0436: prepStatement.setString(
0437: i + 1, value);
0438: }
0439: }
0440: } else {
0441: String value = (String) lobAdds
0442: .get(i);
0443: prepStatement.setString(i + 1,
0444: value);
0445: }
0446: }
0447: }
0448:
0449: }
0450: // if ("interbase.interclient.Driver".equals(myConnection.getDBDriver())) {
0451: //
0452: //Workaround, interclient 2 doesn't support setCharacterStream()
0453: //
0454: // byte[] data = value.getBytes();
0455: // java.io.ByteArrayInputStream bis = new java.io.ByteArrayInputStream(data);
0456: // prepStatement.setAsciiStream(1,bis,data.length);
0457: // } else {
0458: // java.io.Reader r = new java.io.StringReader(value);
0459: // prepStatement.setCharacterStream(i + 1,r, value.length());
0460: // }
0461:
0462: }
0463: myConnection.executeUpdate(null);
0464: myConnection.clearPreparedStatement();
0465:
0466: } else {
0467: myConnection.executeUpdate(theSqlCommand);
0468: }
0469:
0470: } catch (NullPointerException npe) {
0471: log.error("NPE", npe);
0472: throw new DataException("Null Pointer Exception", npe);
0473: } catch (SQLException ex) {
0474: log.error("Error executing LOB set Character Stream", ex);
0475: throw new DataException(
0476: "Error setting character stream for for DBObject: "
0477: + getClass().getName(), ex);
0478: } catch (DBException ex) {
0479: String msg = myName + "add(DataObject) error: ";
0480: log.error(msg, ex);
0481: throw new DataException(ex.getMessage());
0482: } finally {
0483: if (localConnection == null) {
0484: if (myPool != null && myConnection != null) {
0485: myPool.release(myConnection);
0486: }
0487: }
0488: }
0489:
0490: }
0491:
0492: /**
0493: * Takes a <code>DataObject</code> and deletes it from the underlying data source.
0494: * <b>Note:</b> The current implementation only expects a <code>DBObject</code>
0495: * and actually routes the call back to that object. Will be fixed in the future
0496: * as the interface becomes more rich.
0497: *
0498: * @param valueObject the <code>DataObject</code> to delete.
0499: * @throws DataException upon error deleting the object to the data source
0500: */
0501: public void delete(DataObject valueObject) throws DataException {
0502: if (valueObject == null) {
0503: throw new IllegalArgumentException(myName
0504: + "delete(DataObject): valueObject was null");
0505: }
0506: DBObject theObject = (DBObject) valueObject;
0507: try {
0508: theObject.delete(true);
0509: } catch (DBException ex) {
0510: String msg = myName + "delete(DataObject) error";
0511: log.error(msg, ex);
0512: throw new DataException(msg, ex);
0513: }
0514: }
0515:
0516: /**
0517: * Takes a <code>DataObject</code> and updates it to the underlying data source
0518: *
0519: * @param valueObject the <code>DataObject</code> to update.
0520: * @param updateChangedFieldsOnly if true only modified fields (isChanged = true)
0521: * will be included in the update
0522: * @throws DataException upon error updating the object to the data source
0523: * <p/>
0524: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
0525: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
0526: */
0527: public void update(DataObject valueObject,
0528: boolean updateChangedFieldsOnly) throws DataException {
0529: if (valueObject == null) {
0530: throw new IllegalArgumentException(myName
0531: + "update(DataObject): valueObject was null");
0532: }
0533: DBObject theObject = (DBObject) valueObject;
0534:
0535: boolean needComma = false;
0536:
0537: try {
0538: //
0539: //If myPool isn't set yet, then we need to use getDBName to
0540: //reset. FIXME: Have the system throw exceptions if DBName isn't
0541: //set instead.
0542: //
0543: DBConnectionPool myPool = DBConnectionPool
0544: .getInstance(theObject.getMappedDataContext());
0545: DBConnection localConnection = theObject
0546: .getLocalConnection();
0547:
0548: /* build an update statement - be sure we have all of the keys */
0549: if (!theObject.haveAllKeys()) {
0550: throw new DataException(
0551: "("
0552: + getClass().getName()
0553: + ") All key fields not present - cannot update record");
0554: }
0555:
0556: theObject.checkAllRefsPublic();
0557:
0558: List lobFieldOrder = null;
0559: List lobFieldType = null;
0560: List lobUpdates = null;
0561: DataFieldMetaData oneField = null;
0562: FastStringBuffer sqlCommand = FastStringBuffer
0563: .getInstance();
0564: String theSqlString = null;
0565: boolean fieldsUpdated = false;
0566: try {
0567: sqlCommand.append("UPDATE ");
0568: sqlCommand.append(theObject.getJDBCMetaData()
0569: .getTargetSQLTable(theObject.getDataContext()));
0570: sqlCommand.append(" SET ");
0571:
0572: for (Iterator i = valueObject.getMetaData()
0573: .getFieldListArray().iterator(); i.hasNext();) {
0574: String oneFieldName = (String) i.next();
0575: oneField = theObject.getFieldMetaData(oneFieldName);
0576:
0577: /* We skip any key fields in the update part (not allowed to */
0578: /* update them). Also skip any virtual fields */
0579: if ((!oneField.isKey()) && (!oneField.isVirtual())
0580: && (!oneField.isAutoIncremented())
0581: && (!oneField.isBinaryObjectType())) {
0582:
0583: if (updateChangedFieldsOnly) {
0584: // Only include changed fields in the update if required
0585: if (valueObject.getDataField(oneFieldName)
0586: .isValueSet()
0587: && theObject.getStatus().equals(
0588: DataObject.STATUS_NEW)) {
0589: ; // noop
0590: } else {
0591: if (!valueObject.getDataField(
0592: oneFieldName).isChanged()) {
0593: continue;
0594: }
0595: }
0596: }
0597: theObject.checkField(oneFieldName, theObject
0598: .getField(oneFieldName));
0599:
0600: if (needComma) {
0601: sqlCommand.append(", ");
0602: }
0603: if (!oneField.isKey()) {
0604: fieldsUpdated = true;
0605: sqlCommand.append(oneField.getName());
0606: sqlCommand.append(" = ");
0607:
0608: if (oneField.isDateType()) {
0609: Object tmpData = valueObject
0610: .get(oneField.getName());
0611: String data;
0612: //
0613: //FIXME allow for appropriate support of other data types.
0614: //
0615: if (tmpData == null) {
0616: data = null;
0617: } else if (tmpData instanceof String) {
0618: data = (String) tmpData;
0619: } else {
0620: data = tmpData.toString();
0621: }
0622: if (data == null || data.length() == 0) {
0623: sqlCommand.append("null");
0624: } else {
0625: sqlCommand
0626: .append(JDBCUtil
0627: .getInstance()
0628: .formatDateTime(
0629: theObject,
0630: oneField
0631: .getName()));
0632: }
0633: } else if (oneField
0634: .isCharacterLongObjectType()
0635: || oneField.isLongObjectType()) {
0636: //
0637: //If we have a CLOB object, then we will go ahead and process
0638: //it, but we execute it in a separate update
0639: //
0640: if (lobUpdates == null) {
0641: lobUpdates = new ArrayList();
0642: lobFieldOrder = new ArrayList();
0643: lobFieldType = new ArrayList();
0644: }
0645: Object tmpData = valueObject
0646: .get(oneField.getName());
0647: String data;
0648: //
0649: //FIXME allow for appropriate support of other data types.
0650: //
0651: if (tmpData == null) {
0652: data = null;
0653: } else if (tmpData instanceof String) {
0654: data = (String) tmpData;
0655: } else {
0656: data = tmpData.toString();
0657: }
0658:
0659: lobUpdates.add(tmpData);
0660: lobFieldOrder.add(oneFieldName);
0661: lobFieldType.add(oneField
0662: .getTypeString());
0663:
0664: sqlCommand.append("?");
0665:
0666: } else {
0667: sqlCommand.append(theObject
0668: .quoteIfNeeded(oneField
0669: .getName(), null));
0670: }
0671:
0672: sqlCommand.append(" ");
0673: needComma = true;
0674: }
0675: } /* if it's not a key field */
0676:
0677: }
0678:
0679: if (!fieldsUpdated) {
0680: return;
0681: }
0682: sqlCommand.append(theObject.buildWhereClause(false));
0683: theSqlString = sqlCommand.toString();
0684: } finally {
0685: sqlCommand.release();
0686: sqlCommand = null;
0687: }
0688:
0689: DBConnection myConnection = null;
0690:
0691: try {
0692: if (localConnection != null) {
0693: myConnection = localConnection;
0694: } else {
0695: myConnection = myPool.getConnection(getClass()
0696: .getName());
0697: }
0698:
0699: if (lobUpdates != null) {
0700:
0701: java.sql.PreparedStatement prepStatement = null;
0702: try {
0703: prepStatement = myConnection
0704: .createPreparedStatement(theSqlString);
0705:
0706: int size = lobFieldOrder.size();
0707: String value = "";
0708: for (int i = 0; i < size; i++) {
0709: String myType = (String) lobFieldType
0710: .get(i);
0711: try {
0712: if (!myType
0713: .equalsIgnoreCase(LONGVARBINARY_TYPE)
0714: && !myType
0715: .equalsIgnoreCase(VARBINARY_TYPE)
0716: && !myType
0717: .equalsIgnoreCase(LONGVARCHAR_TYPE)) {
0718: value = (String) lobUpdates.get(i);
0719: prepStatement.setString(i + 1,
0720: value);
0721: } else {
0722: if (myType
0723: .equalsIgnoreCase(LONGVARBINARY_TYPE)
0724: || myType
0725: .equalsIgnoreCase(VARBINARY_TYPE)) {
0726: byte[] data = (byte[]) lobUpdates
0727: .get(i);
0728: ByteArrayInputStream theData = new ByteArrayInputStream(
0729: data);
0730: prepStatement.setBinaryStream(
0731: i + 1, theData,
0732: data.length);
0733: } else {
0734: value = (String) lobUpdates
0735: .get(i);
0736: if (myType
0737: .equalsIgnoreCase(LONGVARCHAR_TYPE)) {
0738: if (("oracle.jdbc.driver.OracleDriver"
0739: .equals(myConnection
0740: .getDBDriver()))) {
0741: java.io.Reader theData = new StringReader(
0742: value);
0743: prepStatement
0744: .setCharacterStream(
0745: i + 1,
0746: theData,
0747: value
0748: .length());
0749: } else {
0750: if ("interbase.interclient.Driver"
0751: .equals(myConnection
0752: .getDBDriver())) {
0753: byte[] data = value
0754: .getBytes();
0755: java.io.ByteArrayInputStream theData = new java.io.ByteArrayInputStream(
0756: data);
0757: prepStatement
0758: .setAsciiStream(
0759: i + 1,
0760: theData,
0761: data.length);
0762: } else {
0763: prepStatement
0764: .setString(
0765: i + 1,
0766: value);
0767: }
0768: }
0769: } else {
0770: prepStatement.setString(
0771: i + 1, value);
0772: }
0773: }
0774: }
0775:
0776: } catch (SQLException ex) {
0777: String key = (String) lobFieldOrder
0778: .get(i);
0779: log
0780: .error(
0781: "Error executing LOB set Character Stream",
0782: ex);
0783: throw new DataException(
0784: "Error setting character stream for field: "
0785: + key + " with value: "
0786: + value
0787: + " for DBObject: "
0788: + getClass().getName(),
0789: ex);
0790: } catch (Throwable t) {
0791: String key = (String) lobFieldOrder
0792: .get(i);
0793: log.error(
0794: "Error executing LOB set Character Stream. SQL="
0795: + theSqlString, t);
0796: throw new DataException(
0797: "Error setting character stream for field: "
0798: + key + " with value: "
0799: + value
0800: + " for DBObject: "
0801: + getClass().getName()
0802: + " SQL="
0803: + theSqlString, t);
0804: }
0805: }
0806: myConnection.executeUpdate(null);
0807: } finally {
0808: myConnection.clearPreparedStatement();
0809: }
0810: } else {
0811: myConnection.executeUpdate(theSqlString);
0812: }
0813:
0814: if ((myConnection.getUpdateCount() == 0)
0815: && (theObject.checkZeroUpdate())) {
0816: log.error("No record updated for SQL '"
0817: + theSqlString + "'");
0818: throw new DataException("(" + getClass().getName()
0819: + ") No records updated for"
0820: + theObject.forKey());
0821: }
0822: if (theObject.isCached()) {
0823: theObject.removeFromCache(theObject);
0824: }
0825: } catch (NullPointerException npe) {
0826: npe.printStackTrace();
0827: throw new DataException(npe);
0828: } finally {
0829: if (localConnection == null) {
0830: if (myPool != null && myConnection != null) {
0831: myPool.release(myConnection);
0832: }
0833: }
0834: }
0835: } catch (DBException ex) {
0836: String msg = myName + "update(DataObject) error";
0837: log.error(msg, ex);
0838: throw new DataException(ex.getMessage());
0839: }
0840: }
0841:
0842: /**
0843: * Retrieves the object with keys specified by the valueObject parameter.
0844: * This has the same semantics as <code>DBObject</code>'s retrieve method.
0845: *
0846: * @param valueObject the DataObject who's keys are already set to retrieve
0847: * @return true if the data object was successfully retrieved
0848: * @throws DataException Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
0849: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
0850: */
0851: public boolean retrieve(DataObject valueObject)
0852: throws DataException {
0853: if (valueObject == null) {
0854: throw new IllegalArgumentException(myName
0855: + "retrieve(DataObject): valueObject was null");
0856: }
0857: DBObject theObject = (DBObject) valueObject;
0858: JDBCObjectMetaData metadata = (JDBCObjectMetaData) valueObject
0859: .getMetaData();
0860:
0861: boolean needComma = false;
0862: DataFieldMetaData oneField = null;
0863:
0864: boolean anyFieldsDistinct = false;
0865: try {
0866: ArrayList distinctFields = theObject
0867: .getDistinctFieldArrayList();
0868: anyFieldsDistinct = (distinctFields.size() > 0);
0869: if (!theObject.haveAllKeys() && !anyFieldsDistinct) {
0870: throw new DataException(
0871: "("
0872: + getClass().getName()
0873: + ") All key fields not present - cannot retrieve record");
0874: }
0875: if (theObject.retrieveFromCache()) {
0876: return true;
0877: }
0878:
0879: // FastStringBuffer myStatement = new FastStringBuffer(128);
0880: FastStringBuffer myStatement = FastStringBuffer
0881: .getInstance();
0882: String theSqlStatement = null;
0883: try {
0884: myStatement.append("SELECT ");
0885:
0886: if (theObject.getFieldsToRetrieveCount() > 0) {
0887: String oneFieldName = null;
0888: Map retrieveFieldNames = new HashMap(10);
0889:
0890: for (Iterator i = theObject
0891: .getFieldsToRetrieveIterator(); i.hasNext();) {
0892: oneFieldName = (String) i.next();
0893: retrieveFieldNames.put(oneFieldName, null);
0894:
0895: if (needComma) {
0896: myStatement.append(", ");
0897: }
0898:
0899: myStatement.append(theObject
0900: .selectFieldString(oneFieldName));
0901: needComma = true;
0902: } /* for each field */
0903:
0904: for (Iterator i = theObject.getMetaData()
0905: .getKeyFieldListArray().iterator(); i
0906: .hasNext();) {
0907: String curFieldName = (String) i.next();
0908: oneField = theObject
0909: .getFieldMetaData(curFieldName);
0910:
0911: if (!retrieveFieldNames
0912: .containsKey(curFieldName)) {
0913: if (needComma) {
0914: myStatement.append(", ");
0915: }
0916:
0917: myStatement.append(theObject
0918: .selectFieldString(curFieldName));
0919: needComma = true;
0920: }
0921: }
0922: } else { /* for each key field */
0923: for (Iterator i = metadata.getAllFieldsMap()
0924: .values().iterator(); i.hasNext();) {
0925: oneField = (DBField) i.next();
0926: //We aren't retrieving long objects
0927: if (oneField.isBinaryObjectType()) {
0928: continue;
0929: }
0930:
0931: if (!oneField.isVirtual()) {
0932: if (needComma) {
0933: myStatement.append(", ");
0934: }
0935:
0936: myStatement.append(theObject
0937: .selectFieldString(oneField
0938: .getName()));
0939: needComma = true;
0940: } /* if field is not virtual */
0941:
0942: } /* for */
0943:
0944: } /* if anyFieldsToRetrieve */
0945:
0946: myStatement.append(" FROM ");
0947: myStatement.append(theObject.getJDBCMetaData()
0948: .getTargetSQLTable(theObject.getDataContext()));
0949:
0950: String customWhereClause = theObject
0951: .getCustomWhereClause();
0952:
0953: if (customWhereClause != null) {
0954: log
0955: .warn("Custom Where clauses don't make sense for "
0956: + "DataObject.retrieve(), since it's based upon key fields. Ignoring:"
0957: + customWhereClause);
0958: }
0959:
0960: myStatement.append(theObject.buildWhereClause(false));
0961: theSqlStatement = myStatement.toString();
0962: } finally {
0963: myStatement.release();
0964: myStatement = null;
0965: }
0966:
0967: DBConnection myConnection = null;
0968:
0969: //
0970: //If myPool isn't set yet, then we need to use getDBName to
0971: //reset. FIXME: Have the system throw exceptions if DBName isn't
0972: //set instead.
0973: //
0974: DBConnectionPool myPool = DBConnectionPool
0975: .getInstance(theObject.getMappedDataContext());
0976: DBConnection localConnection = theObject
0977: .getLocalConnection();
0978: if (localConnection != null) {
0979: myConnection = localConnection;
0980: } else {
0981: myConnection = myPool.getConnection(getClass()
0982: .getName());
0983: }
0984:
0985: try {
0986:
0987: myConnection.execute(theSqlStatement);
0988:
0989: FastStringBuffer oneKeyString = FastStringBuffer
0990: .getInstance();
0991: try {
0992: String oneFieldValue = "";
0993: Object tmpData = null;
0994: if (myConnection.next()) {
0995: //Clear all non-key fields
0996: for (Iterator allFields = theObject
0997: .getMetaData().getFieldListArray()
0998: .iterator(); allFields.hasNext();) {
0999: String oneFieldName = (String) allFields
1000: .next();
1001: DataFieldMetaData fieldMetaData = theObject
1002: .getFieldMetaData(oneFieldName);
1003: if (!fieldMetaData.isKey()
1004: && !fieldMetaData
1005: .isBinaryObjectType()
1006: && !fieldMetaData.isVirtual()) {
1007: theObject.set(oneFieldName, null);
1008: }
1009: }
1010:
1011: int i = 1;
1012:
1013: Iterator it;
1014: if (theObject.getFieldsToRetrieveCount() > 0) {
1015: //
1016: //We have to load up a metadata array based upon the
1017: //order of the field names as specified in the the
1018: //getFieldsToRetrieveIterator() function.
1019: //
1020: ArrayList dbFieldArray = new ArrayList(
1021: theObject
1022: .getFieldsToRetrieveCount());
1023: for (Iterator j = theObject
1024: .getFieldsToRetrieveIterator(); j
1025: .hasNext();) {
1026: String theFieldName = (String) j.next();
1027: dbFieldArray
1028: .add(theObject
1029: .getFieldMetaData(theFieldName));
1030: }
1031:
1032: it = dbFieldArray.iterator();
1033:
1034: } else {
1035: it = metadata.getAllFieldsMap().values()
1036: .iterator();
1037: }
1038:
1039: while (it.hasNext()) {
1040: oneField = (DBField) it.next();
1041:
1042: if (!oneField.isVirtual()) {
1043: if (oneField.isBinaryObjectType()) {
1044: continue;
1045: }
1046:
1047: try {
1048: // * @author Yves Henri AMAIZO
1049: // Handle correctly date from resultSet data retrieve from Database
1050: if (oneField.isDateType()) {
1051: tmpData = theObject
1052: .getCustomStringFieldValue(
1053: myConnection,
1054: oneField
1055: .getName());
1056: } else {
1057: if (!oneField
1058: .isLongBinaryType()
1059: && !oneField
1060: .isLongCharacterType()) {
1061: if (myConnection
1062: .isStringNotTrim()) {
1063: tmpData = myConnection
1064: .getStringNoTrim(i);
1065: } else {
1066: tmpData = myConnection
1067: .getString(i);
1068: }
1069: } else {
1070: if (oneField
1071: .isLongBinaryType()) {
1072: tmpData = null;
1073: InputStream is = myConnection
1074: .getBinaryStream(i);
1075: if (is != null) {
1076: byte[] bstr = new byte[LONGBINARY_READ_DEFAULT_SIZE];
1077: int j = is
1078: .read(bstr);
1079: if (j > 0) {
1080: byte[] content = new byte[j];
1081: System
1082: .arraycopy(
1083: bstr,
1084: 0,
1085: content,
1086: 0,
1087: j);
1088: tmpData = content;
1089: }
1090: }
1091: } else {
1092: tmpData = myConnection
1093: .getStringNoTrim(i);
1094: }
1095:
1096: }
1097: }
1098: } catch (DBException de1) {
1099: throw new DataException(
1100: "("
1101: + getClass()
1102: .getName()
1103: + ") Error retrieving field '"
1104: + oneField
1105: .getName()
1106: + "' index " + i
1107: + ":"
1108: + de1.getMessage(),
1109: de1.getDBMessage());
1110: } catch (Exception de1) {
1111: throw new DataException(
1112: "Not DBException("
1113: + getClass()
1114: .getName()
1115: + ") Error retrieving field '"
1116: + oneField
1117: .getName()
1118: + "' index " + i
1119: + ":"
1120: + de1.getMessage(),
1121: de1.getMessage());
1122: }
1123:
1124: i++;
1125: // theObject.set(oneField.getName(), oneFieldValue);
1126: theObject.set(oneField.getName(),
1127: tmpData);
1128: }
1129:
1130: if (oneField.isKey()) {
1131: if (i > 1) {
1132: oneKeyString.append("/");
1133: }
1134:
1135: oneKeyString.append(oneFieldValue);
1136: }
1137: }
1138: theObject.addFoundKeys(oneKeyString.toString());
1139: } else {
1140: //There were zero results returned by the retrieve() call.
1141: return false;
1142: }
1143: } finally {
1144: oneKeyString.release();
1145: oneKeyString = null;
1146: }
1147:
1148: } finally {
1149: if (localConnection == null) {
1150: if (myPool != null && myConnection != null) {
1151: myPool.release(myConnection);
1152: }
1153: }
1154: }
1155: } catch (DBException ex) {
1156: String msg = myName + "retrieve(DataObject) error";
1157: log.error(msg, ex);
1158: throw new DataException(msg, ex);
1159: }
1160:
1161: return true;
1162: }
1163:
1164: /**
1165: * Adds an entire batch of <code>DataObject</code>s to the underlying JDBC data source
1166: *
1167: * @param valueObjectList A list of <code>DataObject</code>s to add to the underlying
1168: * data source <b>NOTE:</b> you will get best performance if valueObjectList is
1169: * all one dataobject underneath.
1170: * <p/>
1171: * WARNING: I DON'T UNDERSTAND WHY LOOP VAR ISN'T USED BELOW, AND SUSPECT THIS METHOD ISN'T DOING ALL IT PURPORTS TO DO; SEE "todo" (Larry Hamel, 3/03)
1172: * @throws DataException upon error communicating with the underlying data source
1173: * @throws DuplicateKeyException if one of the records was already in the
1174: * data source
1175: */
1176: public void addBatch(List valueObjectList) throws DataException,
1177: DuplicateKeyException {
1178: if (valueObjectList == null) {
1179: throw new IllegalArgumentException(myName
1180: + "addBatch(List): valueObjectArray was null");
1181: }
1182: addBatch(valueObjectList, false);
1183: }
1184:
1185: /**
1186: * Updates an entire batch of <code>DataObject</code>s
1187: *
1188: * @param valueObjectList A list of <code>DataObject</code>s to update to the underlying
1189: * data source
1190: * @throws DataException upon error updating the data source
1191: */
1192: public void updateBatch(List valueObjectList) throws DataException {
1193: if (valueObjectList == null) {
1194: throw new IllegalArgumentException(myName
1195: + "updateBatch(List): valueObjectArray was null");
1196: }
1197: updateBatch(valueObjectList, false);
1198:
1199: }
1200:
1201: /**
1202: * Helper function to build a prepared update statement for batch updates.
1203: *
1204: * @param oneObjectType A single <code>DataObject</code> that is used to model the prepared
1205: * statement. Only metadata about the <code>DataObject</code> is used.
1206: * @param updateChangedFieldsOnly true if the changed fields should be the
1207: * only ones sent through the SQL query.
1208: * @return A <code>FastStringBuffer</code> containing the proper SQL to create
1209: * a preparedStatement
1210: * @throws DBException upon error
1211: * <p/>
1212: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1213: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
1214: */
1215: protected String buildPreparedUpdateSQL(DataObject oneObjectType,
1216: boolean updateChangedFieldsOnly) throws DBException {
1217: DBObject theObject = (DBObject) oneObjectType;
1218:
1219: FastStringBuffer sqlCommand = FastStringBuffer.getInstance();
1220: FastStringBuffer whereClause = FastStringBuffer.getInstance();
1221: String returnValue = null;
1222: try {
1223: sqlCommand.append("UPDATE ");
1224: sqlCommand.append(theObject.getJDBCMetaData()
1225: .getTargetSQLTable(theObject.getDataContext()));
1226: sqlCommand.append(" SET ");
1227:
1228: whereClause.append(" WHERE ");
1229:
1230: DataFieldMetaData oneField = null;
1231: boolean needComma = false;
1232: boolean needWhereComma = false;
1233:
1234: for (Iterator i = theObject.getMetaData()
1235: .getFieldListArray().iterator(); i.hasNext();) {
1236: String oneFieldName = (String) i.next();
1237: oneField = theObject.getFieldMetaData(oneFieldName);
1238:
1239: // Only include changed fields in the update if required
1240: if (updateChangedFieldsOnly) {
1241: if (theObject.getDataField(oneFieldName)
1242: .isValueSet()
1243: && theObject.getStatus().equals(
1244: DataObject.STATUS_NEW)) {
1245: ;
1246: } else {
1247: if (!theObject.getDataField(oneFieldName)
1248: .isChanged()) {
1249: continue;
1250: }
1251: }
1252: }
1253:
1254: /* We skip any key fields in the update part (not allowed to */
1255: /* upate them). Also skip any virtual fields */
1256: if ((!oneField.isKey()) && (!oneField.isVirtual())
1257: && (!oneField.isAutoIncremented())
1258: && (!oneField.isBinaryObjectType())) {
1259:
1260: if (needComma) {
1261: sqlCommand.append(", ");
1262: }
1263:
1264: sqlCommand.append(oneField.getName());
1265: sqlCommand.append(" = ? ");
1266: needComma = true;
1267: } else if (oneField.isKey()) {
1268: if (needWhereComma) {
1269: // whereClause.append(", ");
1270: whereClause.append(" AND ");
1271: }
1272:
1273: whereClause.append(oneField.getName());
1274: whereClause.append(" = ? ");
1275: needWhereComma = true;
1276: }
1277:
1278: }
1279:
1280: sqlCommand.append(whereClause);
1281: returnValue = sqlCommand.toString();
1282: } finally {
1283: whereClause.release();
1284: sqlCommand.release();
1285: whereClause = null;
1286: sqlCommand = null;
1287: }
1288:
1289: return returnValue;
1290: }
1291:
1292: /**
1293: * Helper function to build a prepared delete statement for batch deletes.
1294: *
1295: * @param oneObjectType A single <code>DataObject</code> that is used to model the prepared
1296: * statement. Only metadata about the <code>DataObject</code> is used.
1297: * @return A <code>FastStringBuffer</code> containing the proper SQL to create
1298: * a preparedStatement
1299: * @throws DBException upon error
1300: * <p/>
1301: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1302: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
1303: */
1304: protected String buildPreparedDeleteSQL(DataObject oneObjectType,
1305: boolean deleteWithSetFieldsOnly) throws DBException {
1306: DBObject theObject = (DBObject) oneObjectType;
1307:
1308: FastStringBuffer sqlCommand = FastStringBuffer.getInstance();
1309: FastStringBuffer whereClause = FastStringBuffer.getInstance();
1310: String returnValue = null;
1311: try {
1312: sqlCommand.append("DELETE FROM ");
1313: sqlCommand.append(theObject.getJDBCMetaData()
1314: .getTargetSQLTable(theObject.getDataContext()));
1315:
1316: whereClause.append(" WHERE ");
1317:
1318: DataFieldMetaData oneField = null;
1319: boolean needComma = false;
1320: boolean needWhereComma = false;
1321:
1322: for (Iterator i = theObject.getMetaData()
1323: .getFieldListArray().iterator(); i.hasNext();) {
1324: String oneFieldName = (String) i.next();
1325: oneField = theObject.getFieldMetaData(oneFieldName);
1326:
1327: // Only include changed fields in the update if required
1328: if (deleteWithSetFieldsOnly) {
1329: if (theObject.getDataField(oneFieldName)
1330: .isValueSet()
1331: && theObject.getStatus().equals(
1332: DataObject.STATUS_NEW)) {
1333: ;
1334: } else {
1335: if (!theObject.getDataField(oneFieldName)
1336: .isChanged()) {
1337: continue;
1338: }
1339: }
1340: }
1341:
1342: /* We skip any key fields in the update part (not allowed to */
1343: /* upate them). Also skip any virtual fields */
1344: if ((!oneField.isKey()) && (!oneField.isVirtual())
1345: && (!oneField.isAutoIncremented())
1346: && (!oneField.isBinaryObjectType())) {
1347:
1348: if (needComma) {
1349: sqlCommand.append(", ");
1350: }
1351:
1352: sqlCommand.append(oneField.getName());
1353: sqlCommand.append(" = ? ");
1354: needComma = true;
1355: } else if (oneField.isKey()) {
1356: if (needWhereComma) {
1357: whereClause.append(" AND ");
1358: }
1359:
1360: whereClause.append(oneField.getName());
1361: whereClause.append(" = ? ");
1362: needWhereComma = true;
1363: }
1364:
1365: }
1366:
1367: sqlCommand.append(whereClause);
1368: returnValue = sqlCommand.toString();
1369: } finally {
1370: whereClause.release();
1371: sqlCommand.release();
1372: whereClause = null;
1373: sqlCommand = null;
1374: }
1375:
1376: return returnValue;
1377: }
1378:
1379: /**
1380: * Helper Function to build a prepared statement's SQL for an ADD statement.
1381: *
1382: * @param oneObjectType A single <code>DataObject</code> that is used to model the prepared
1383: * statement. Only metadata about the <code>DataObject</code> is used.
1384: * @param addChangedFieldsOnly flag to signify if only fields whose value
1385: * has changed should be included in the add.
1386: * @return a FastStringBuffer containing the build sql statement
1387: * @throws DBException upon error
1388: * <p/>
1389: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1390: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
1391: */
1392: protected String buildPreparedAddSQL(DataObject oneObjectType,
1393: boolean addChangedFieldsOnly) throws DBException {
1394: DBObject theObject = (DBObject) oneObjectType;
1395: /**
1396: * sqlCommand will eventually = sqlCommand + valuesCommand. The two
1397: * are assembled in parallel in the loop below.
1398: */
1399: FastStringBuffer sqlCommand = FastStringBuffer.getInstance();
1400: FastStringBuffer valuesCommand = FastStringBuffer.getInstance();
1401: String returnValue = null;
1402: try {
1403: sqlCommand.append("INSERT INTO ");
1404: sqlCommand.append(theObject.getJDBCMetaData()
1405: .getTargetSQLTable(theObject.getDataContext()));
1406: sqlCommand.append(" (");
1407:
1408: valuesCommand.append(") VALUES (");
1409:
1410: DataFieldMetaData oneField = null;
1411: boolean needCommaValues = false;
1412: for (Iterator i = theObject.getMetaData()
1413: .getFieldListArray().iterator(); i.hasNext();) {
1414: String oneFieldName = (String) i.next();
1415: oneField = theObject.getFieldMetaData(oneFieldName);
1416:
1417: // Only include changed fields in the add if required
1418: if (addChangedFieldsOnly) {
1419: if (theObject.getDataField(oneFieldName)
1420: .isValueSet()
1421: && theObject.getStatus().equals(
1422: DataObject.STATUS_NEW)) {
1423: ;
1424: } else {
1425: if (!theObject.getDataField(oneFieldName)
1426: .isChanged()) {
1427: continue;
1428: }
1429: }
1430: }
1431:
1432: if (!oneField.isVirtual()
1433: && !oneField.isBinaryObjectType()) {
1434: if (needCommaValues) {
1435: sqlCommand.append(", ");
1436: valuesCommand.append(", ");
1437: }
1438: sqlCommand.append(oneField.getName());
1439: valuesCommand.append(" ? ");
1440: needCommaValues = true;
1441: }
1442:
1443: } /* for each field */
1444:
1445: //Now we merge the values of the parallel loops
1446: sqlCommand.append(valuesCommand);
1447: sqlCommand.append(")");
1448: returnValue = sqlCommand.toString();
1449: } finally {
1450: sqlCommand.release();
1451: valuesCommand.release();
1452: sqlCommand = null;
1453: valuesCommand = null;
1454: }
1455:
1456: return returnValue;
1457:
1458: }
1459:
1460: /**
1461: * Format the field for storage into a prepared statement. This is similar
1462: * to the old <code>DBObject.quoteIfNeeded()</code> but it does not insert
1463: * quotes around the values since the prepared statements will take care
1464: * of special characters like that
1465: *
1466: * @param oneField The metadata of the field to retrieve
1467: * @param theObj The <code>DBObject</code> that contains the data to store.
1468: * @return java.lang.String for the appropriate data value.
1469: */
1470: protected String prepareForStorage(DataFieldMetaData oneField,
1471: DBObject theObj) throws DBException {
1472: return prepareForStorage(oneField, theObj, true);
1473: }
1474:
1475: /**
1476: * Format the field for storage into a prepared statement. This is similar
1477: * to the old <code>DBObject.quoteIfNeeded()</code> but it does not insert
1478: * quotes around the values since the prepared statements will take care
1479: * of special characters like that
1480: *
1481: * @param oneField The metadata of the field to retrieve
1482: * @param theObj The <code>DBObject</code> that contains the data to store.
1483: * @return java.lang.String for the appropriate data value.
1484: */
1485: protected String prepareForStorage(DataFieldMetaData oneField,
1486: DBObject theObj, boolean dateFormatted) throws DBException {
1487: //String fieldName = oneField.getName();
1488: String fieldValue = theObj
1489: .getSerializedForm((DBField) oneField);
1490:
1491: /* if the field is null, we don't need to worry about quotes */
1492: if (fieldValue == null) {
1493: return null;
1494: }
1495:
1496: if (oneField.isNumericType()) {
1497: if (fieldValue.equals("")) {
1498: return "0";
1499: }
1500:
1501: return fieldValue.trim();
1502: } /* if a numeric type */
1503:
1504: if (oneField.isDateType()) {
1505: if (dateFormatted) {
1506: return JDBCUtil.getInstance().formatDateTime(theObj,
1507: oneField.getName(), false);
1508: }
1509: return fieldValue.trim();
1510: } else if (oneField.isBooleanType()) {
1511: try {
1512: boolean nativeBoolean = ConfigManager.getContext(
1513: theObj.getDataContext()).getJdbc()
1514: .isNativeBool();
1515:
1516: if (!nativeBoolean) {
1517: FastStringBuffer returnValue = FastStringBuffer
1518: .getInstance();
1519: String returnString = null;
1520: try {
1521: returnValue.append(fieldValue.trim());
1522: returnString = returnValue.toString();
1523: } finally {
1524: returnValue.release();
1525: returnValue = null;
1526: }
1527: return returnString;
1528: }
1529: } catch (ConfigurationException ce) {
1530: throw new DBException(ce);
1531: }
1532: }
1533:
1534: return fieldValue.trim();
1535: } /* quoteIfNeeded(String) */
1536:
1537: private boolean haveAllKeysExceptAutoInc(DBObject valueObject)
1538: throws DBException {
1539: DBField oneField = null;
1540:
1541: for (Iterator i = valueObject.getJDBCMetaData().getAllKeysMap()
1542: .values().iterator(); i.hasNext();) {
1543: oneField = (DBField) i.next();
1544: DataField df = valueObject.getDataField(oneField.getName());
1545: if (df == null
1546: || (df.isNull() && !df.getFieldMetaData()
1547: .isAutoIncremented())) {
1548: return false;
1549: }
1550: }
1551:
1552: return true;
1553: }
1554:
1555: /**
1556: * Takes a <code>DataObject</code> and updates all to the underlying data source
1557: *
1558: * @param valueObject the <code>DataObject</code> to update.
1559: * @param updateChangedCache if true only modified fields (isChanged = true)
1560: * will be included in the update
1561: * @throws DataException upon error updating the object to the data source
1562: * <p/>
1563: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1564: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
1565: */
1566: public void updateAll(DataObject valueObject,
1567: boolean updateChangedCache) throws DataException {
1568: if (valueObject == null) {
1569: throw new IllegalArgumentException(myName
1570: + "updateAll(DataObject): valueObject was null");
1571: }
1572: DBObject theObject = (DBObject) valueObject;
1573:
1574: try {
1575: theObject.updateAll(false);
1576: } catch (DBException ex) {
1577: String msg = myName + "updateAll(DataObject) error";
1578: log.error(msg, ex);
1579: throw new DataException(msg, ex);
1580: }
1581:
1582: }
1583:
1584: /**
1585: * Takes a <code>DataObject</code> and deletes it from the underlying data source.
1586: * <b>Note:</b> The current implementation only expects a <code>DBObject</code>
1587: * and actually routes the call back to that object. Will be fixed in the future
1588: * as the interface becomes more rich.
1589: *
1590: * @param valueObject the <code>DataObject</code> to delete.
1591: * @throws DataException upon error deleting the object to the data source
1592: */
1593: public void deleteAll(DataObject valueObject,
1594: boolean deleteChangedCache) throws DataException {
1595: if (valueObject == null) {
1596: throw new IllegalArgumentException(myName
1597: + "deleteAll(DataObject): valueObject was null");
1598: }
1599: DBObject theObject = (DBObject) valueObject;
1600: try {
1601: theObject.deleteAll(false);
1602: } catch (DBException ex) {
1603: String msg = myName + "deleteAll(DataObject) error";
1604: log.error(msg, ex);
1605: throw new DataException(msg, ex);
1606: }
1607:
1608: }
1609:
1610: /**
1611: * Helper Function to build a prepared statement's SQL for running STore Procedure statement.
1612: *
1613: * @param oneObjectType A single <code>DataObject</code> that is used to model the prepared
1614: * statement. Only metadata about the <code>DataObject</code> is used.
1615: * @return a FastStringBuffer containing the build sql statement
1616: * @throws DBException upon error
1617: */
1618: protected String buildPreparedStoreProcedureSQL(
1619: DataObject oneObjectType) throws DBException {
1620: DBObject theObject = (DBObject) oneObjectType;
1621: /**
1622: * sqlCommand
1623: *
1624: */
1625: FastStringBuffer sqlCommand = FastStringBuffer.getInstance();
1626: FastStringBuffer valuesCommand = FastStringBuffer.getInstance();
1627: String returnValue = null;
1628: try {
1629: int nbParams = theObject.getDef().getFieldListArray()
1630: .size();
1631:
1632: if (theObject.getDef().isReturningValue()) {
1633: sqlCommand.append("{? = call ");
1634: nbParams--;
1635: } else {
1636: sqlCommand.append("{call ");
1637: }
1638: sqlCommand.append(theObject.getJDBCMetaData()
1639: .getTargetTable());
1640:
1641: if (nbParams > 0) {
1642: sqlCommand.append("(?");
1643: for (int i = 1; i < nbParams; i++) {
1644: sqlCommand.append(", ?");
1645: }
1646: sqlCommand.append(")");
1647: }
1648: sqlCommand.append("}");
1649:
1650: returnValue = sqlCommand.toString();
1651: } finally {
1652: sqlCommand.release();
1653: valuesCommand.release();
1654: sqlCommand = null;
1655: valuesCommand = null;
1656: }
1657:
1658: return returnValue;
1659:
1660: }
1661:
1662: /**
1663: * Build and return a FastStringBuffer ring consisting of an SQL 'where' clause
1664: * using the current field values as criteria for the search. See
1665: * setCustomWhereClause for information on specifying a more complex where clause.
1666: *
1667: * @param criteria the JDBCDataObject to build from
1668: * @param myCallableStatement the statement
1669: * @return A FastStringBuffer containing the "where" clause for the SQL statement
1670: * @throws DataException upon error
1671: */
1672: protected void buildStoreProcedureCallableStatement(
1673: DBObject criteria, CallableStatement myCallableStatement)
1674: throws DataException {
1675: Iterator fieldsToUse = null;
1676: FastStringBuffer myStatement = FastStringBuffer.getInstance();
1677:
1678: fieldsToUse = criteria.getMetaData().getFieldListArray()
1679: .iterator();
1680:
1681: /* Now go thru each field - if it is non-empty, add it's criteria */
1682:
1683: DataFieldMetaData oneField = null;
1684: String oneFieldName = null;
1685: String oneFieldValue = null;
1686: boolean postgresql = false;
1687: boolean skipText = false;
1688: boolean skipField = false;
1689: boolean inField = false;
1690: boolean outField = false;
1691: TypeMapper typeMapper = null;
1692:
1693: try {
1694: ConfigJdbc myConfig = ConfigManager
1695: .getJdbcRequired(criteria.getMappedDataContext());
1696: skipText = myConfig.skipText();
1697: //We have to do this because postgres won't be smart enough to
1698: //cast floating point literals to truly a floating point value. :(
1699: if ("org.postgresql.Driver".equals(myConfig.getDriver())) {
1700: postgresql = true;
1701: }
1702: typeMapper = TypeMapper.getInstance(criteria
1703: .getDataContext());
1704: } catch (ConfigurationException ce) {
1705: throw new DataException(ce);
1706: } catch (DBException de) {
1707: throw new DataException(de);
1708: }
1709:
1710: try {
1711: while (fieldsToUse.hasNext()) {
1712: oneFieldName = (String) fieldsToUse.next();
1713: oneField = criteria.getFieldMetaData(oneFieldName);
1714: skipField = false;
1715: skipText = false;
1716: inField = false;
1717: outField = false;
1718: if (oneField.isVirtual()) {
1719: skipField = true;
1720: }
1721: if (criteria.getDef().isInField(oneField.getName())) {
1722: inField = true;
1723: }
1724: if (criteria.getDef().isOutField(oneField.getName())) {
1725: outField = true;
1726: }
1727: try {
1728: oneFieldValue = StringUtil.notNull(criteria
1729: .getDataField(oneField.getName())
1730: .asString());
1731: } catch (DBException ex) {
1732: if (ex instanceof DataException) {
1733: throw ((DataException) ex);
1734: } else {
1735: throw new DataException(
1736: "Error getting field value", ex);
1737: }
1738: }
1739:
1740: if (oneField.isNumericType()
1741: || oneField.isFloatingPointType()
1742: || oneField.isDateType()) {
1743: if ((oneFieldValue == null
1744: || "null".equals(oneFieldValue) || oneFieldValue
1745: .length() == 0)
1746: && inField) {
1747: myCallableStatement.setNull(Integer
1748: .parseInt(oneFieldName), typeMapper
1749: .getJavaSQLType(oneField
1750: .getTypeString()));
1751: } else {
1752: if (oneField.isNumericType()) {
1753: if (inField) {
1754: // myCallableStatement.setInt(Integer.parseInt(oneFieldName), Integer.parseInt(oneFieldValue));
1755: if (oneField.getTypeString()
1756: .equalsIgnoreCase("integer")
1757: || oneField.getTypeString()
1758: .equalsIgnoreCase(
1759: "numeric")
1760: || oneField.getTypeString()
1761: .equalsIgnoreCase(
1762: "tinyint")
1763: || oneField.getTypeString()
1764: .equalsIgnoreCase(
1765: "smallint")) {
1766: myCallableStatement
1767: .setInt(
1768: Integer
1769: .parseInt(oneFieldName),
1770: Integer
1771: .parseInt(oneFieldValue));
1772: } else if (oneField.getTypeString()
1773: .equalsIgnoreCase("float")
1774: || oneField.getTypeString()
1775: .equalsIgnoreCase(
1776: "real")) {
1777: myCallableStatement
1778: .setFloat(
1779: Integer
1780: .parseInt(oneFieldName),
1781: Float
1782: .parseFloat(oneFieldValue));
1783: } else if (oneField.getTypeString()
1784: .equalsIgnoreCase("bigint")
1785: || oneField.getTypeString()
1786: .equalsIgnoreCase(
1787: "decimal")) {
1788: myCallableStatement
1789: .setLong(
1790: Integer
1791: .parseInt(oneFieldName),
1792: Long
1793: .parseLong(oneFieldValue));
1794: } else if (oneField.getTypeString()
1795: .equalsIgnoreCase("double")) {
1796: myCallableStatement
1797: .setDouble(
1798: Integer
1799: .parseInt(oneFieldName),
1800: Double
1801: .parseDouble(oneFieldValue));
1802: }
1803: } else {
1804: myCallableStatement
1805: .registerOutParameter(
1806: Integer
1807: .parseInt(oneFieldName),
1808: typeMapper
1809: .getJavaSQLType(oneField
1810: .getTypeString()));
1811: }
1812: } else if (oneField.isDateTimeType()) {
1813: if (inField) {
1814: if (oneField.isDateOnlyType()) {
1815: myCallableStatement
1816: .setDate(
1817: Integer
1818: .parseInt(oneFieldName),
1819: java.sql.Date
1820: .valueOf(oneFieldValue));
1821: }
1822: if (oneField.isDateTimeType()) {
1823: myCallableStatement
1824: .setTimestamp(
1825: Integer
1826: .parseInt(oneFieldName),
1827: java.sql.Timestamp
1828: .valueOf(oneFieldValue));
1829: }
1830: } else {
1831: myCallableStatement
1832: .registerOutParameter(
1833: Integer
1834: .parseInt(oneFieldName),
1835: typeMapper
1836: .getJavaSQLType(oneField
1837: .getTypeString()));
1838: }
1839: } else if (oneField.isFloatingPointType()) {
1840: if (inField) {
1841: myCallableStatement.setFloat(Integer
1842: .parseInt(oneFieldName), Float
1843: .parseFloat(oneFieldValue));
1844: } else {
1845: myCallableStatement
1846: .registerOutParameter(
1847: Integer
1848: .parseInt(oneFieldName),
1849: typeMapper
1850: .getJavaSQLType(oneField
1851: .getTypeString()),
1852: oneField.getPrecision());
1853: }
1854: }
1855: }
1856: } else if (oneField.isBooleanType()) {
1857: ;
1858: } else {
1859: if (oneField.getTypeString().equalsIgnoreCase(
1860: "text")) {
1861: if (oneFieldValue.indexOf("\n") > 0) {
1862: oneFieldValue = StringUtil.replace(
1863: oneFieldValue, "\n", "");
1864: }
1865: if (oneFieldValue.indexOf("\r") > 0) {
1866: oneFieldValue = StringUtil.replace(
1867: oneFieldValue, "\r", "");
1868: }
1869: }
1870: if (inField) {
1871: myCallableStatement.setString(Integer
1872: .parseInt(oneFieldName), oneFieldValue);
1873: }
1874: if (outField) {
1875: myCallableStatement.registerOutParameter(
1876: Integer.parseInt(oneFieldName),
1877: typeMapper.getJavaSQLType(oneField
1878: .getTypeString()));
1879: }
1880: }
1881: } /* end of while(fieldsToUse.hasNext() */
1882: /* for each field */
1883: } catch (DBException e) {
1884: throw new DataException(e);
1885: } catch (SQLException ce) {
1886: throw new DataException(ce);
1887: } finally {
1888: myStatement.release();
1889: myStatement = null;
1890: }
1891: if (log.isDebugEnabled()) {
1892: log.debug("Built callable statement for store procedure ");
1893: }
1894: } /* buildStoreProcedureCallableStatement(DBObject, boolean, CallableStatement) */
1895:
1896: /**
1897: * Run a store procedure identify by the object with keys specified by the
1898: * valueObject parameter.
1899: *
1900: * @param valueObject the DataObject who's keys are already set to retrieve
1901: * @return true if the data object was successfully retrieved
1902: */
1903: public void runStoreProcedure(DataObject valueObject)
1904: throws DataException {
1905: if (valueObject == null) {
1906: throw new IllegalArgumentException(myName
1907: + "run(DataObject): valueObject was null");
1908: }
1909: DBObject theObject = (DBObject) valueObject;
1910: DataFieldMetaData oneField = null;
1911:
1912: try {
1913: String theSqlStatement = buildPreparedStoreProcedureSQL(valueObject);
1914:
1915: DBConnection myConnection = null;
1916:
1917: DBConnectionPool myPool = DBConnectionPool
1918: .getInstance(theObject.getMappedDataContext());
1919: DBConnection localConnection = theObject
1920: .getLocalConnection();
1921: if (localConnection != null) {
1922: myConnection = localConnection;
1923: } else {
1924: myConnection = myPool.getConnection(getClass()
1925: .getName());
1926: }
1927:
1928: try {
1929: CallableStatement myStatement = myConnection
1930: .createCallableStatement(theSqlStatement);
1931: buildStoreProcedureCallableStatement(theObject,
1932: myStatement);
1933: String oneFieldValue = null;
1934: String oneFieldName = null;
1935: myConnection.executeProcedure();
1936: try {
1937: if (myConnection.getResultSet() != null) {
1938: //Clear all non-key fields
1939: for (Iterator allFields = theObject
1940: .getMetaData().getFieldListArray()
1941: .iterator(); allFields.hasNext();) {
1942: oneFieldName = (String) allFields.next();
1943: DataFieldMetaData fieldMetaData = theObject
1944: .getFieldMetaData(oneFieldName);
1945: if (!fieldMetaData.isKey()
1946: && !fieldMetaData
1947: .isBinaryObjectType()
1948: && !fieldMetaData.isVirtual()) {
1949: theObject.set(oneFieldName, null);
1950: }
1951: }
1952: int fieldNum = 0;
1953: Iterator it;
1954: if (theObject.getDef().getOutParamFieldsCount() > 0) {
1955: //
1956: //We have to load up a metadata array based upon the
1957: //order of the field names as specified in the the
1958: //getOutParamFieldsIterator() function.
1959: //
1960: ArrayList dbFieldArray = new ArrayList(
1961: theObject.getDef()
1962: .getOutParamFieldsCount());
1963: for (Iterator j = theObject.getDef()
1964: .getOutParamFieldListIterator(); j
1965: .hasNext();) {
1966: String theFieldName = (String) j.next();
1967: dbFieldArray
1968: .add(theObject
1969: .getFieldMetaData(theFieldName));
1970: }
1971: it = dbFieldArray.iterator();
1972: while (it.hasNext()) {
1973: oneField = (DBField) it.next();
1974: fieldNum = Integer.parseInt(oneField
1975: .getName());
1976: if (!oneField.isVirtual()) {
1977: if (oneField.isBinaryObjectType()) {
1978: continue;
1979: }
1980: try {
1981: // * @author Yves Henri AMAIZO
1982: // Handle correctly date from resultSet data retrieve from Database
1983: if (oneField.isDateType()) {
1984: oneFieldValue = theObject
1985: .getCustomStringFieldValue(
1986: myConnection,
1987: oneField
1988: .getName());
1989: } else {
1990: if (myConnection
1991: .isStringNotTrim()) {
1992: oneFieldValue = myConnection
1993: .getStringNoTrim(fieldNum);
1994: } else {
1995: oneFieldValue = myConnection
1996: .getString(fieldNum);
1997: }
1998: }
1999: } catch (DBException de1) {
2000: throw new DataException(
2001: "("
2002: + getClass()
2003: .getName()
2004: + ") Error retrieving field '"
2005: + oneField
2006: .getName()
2007: + "' index "
2008: + fieldNum
2009: + ":"
2010: + de1
2011: .getMessage(),
2012: de1.getDBMessage());
2013: }
2014: theObject.set(oneField.getName(),
2015: oneFieldValue);
2016: }
2017: }
2018: }
2019: } else {
2020: //There were zero results returned by the retrieve() call.
2021: return;
2022: }
2023: } finally {
2024: ;
2025: }
2026: } finally {
2027: if (localConnection == null) {
2028: if (myPool != null && myConnection != null) {
2029: myPool.release(myConnection);
2030: }
2031: }
2032: }
2033: } catch (DBException ex) {
2034: String msg = myName + "run(DataObject) error";
2035: log.error(msg, ex);
2036: throw new DataException(msg, ex);
2037: }
2038: } /* runStoreProcedure(DataObject) */
2039:
2040: /**
2041: * Adds an entire batch of <code>DataObject</code>s to the underlying JDBC data source
2042: *
2043: * @param valueObjectList A list of <code>DataObject</code>s to add to the underlying
2044: * data source <b>NOTE:</b> you will get best performance if valueObjectList is
2045: * all one dataobject underneath.
2046: * @param addChangedFieldsOnly flag to signify if only fields whose value
2047: * has changed should be included in the add.
2048: * <p/>
2049: * WARNING: I DON'T UNDERSTAND WHY LOOP VAR ISN'T USED BELOW, AND SUSPECT THIS METHOD ISN'T DOING ALL IT PURPORTS TO DO; SEE "todo" (Larry Hamel, 3/03)
2050: * @throws DataException upon error communicating with the underlying data source
2051: * @throws DuplicateKeyException if one of the records was already in the
2052: * data source
2053: */
2054: public void addBatch(List valueObjectList,
2055: boolean addChangedFieldsOnly) throws DataException,
2056: DuplicateKeyException {
2057: if (valueObjectList == null) {
2058: throw new IllegalArgumentException(myName
2059: + "addBatch(List): valueObjectArray was null");
2060: }
2061:
2062: DBObject checkObject = null;
2063: DBConnectionPool myPool = null;
2064: DBConnection localConnection = null;
2065: java.sql.PreparedStatement prepStatement = null;
2066: DataObjectMetaData metadata = null;
2067:
2068: ArrayList fieldData = new ArrayList(6);
2069: ArrayList fieldDataMetaData = new ArrayList(6);
2070: ArrayList keyData = new ArrayList(3);
2071: ArrayList keyDataMetaData = new ArrayList(3);
2072: FastStringBuffer valuesCommand = FastStringBuffer.getInstance();
2073: String theStatementString = "";
2074:
2075: try {
2076: for (Iterator i = valueObjectList.iterator(); i.hasNext();) {
2077: DBObject oneObj = (DBObject) i.next();
2078: //
2079: //If we're on the first object, then we need to build the prepared statement
2080: //
2081: if (checkObject == null) {
2082: localConnection = oneObj.getLocalConnection();
2083: if (localConnection == null) {
2084: myPool = DBConnectionPool.getInstance(oneObj
2085: .getMappedDataContext());
2086: localConnection = myPool
2087: .getConnection("Batch Add");
2088: }
2089: localConnection.clear();
2090: if (localConnection.supportsTransactions()) {
2091: if (!localConnection.getImmortal()) {
2092: localConnection.setAutoCommit(false);
2093: }
2094: }
2095:
2096: //
2097: //If the database doesn't explicitly support batch updates,
2098: //then we call add multiple times.
2099: //
2100: java.sql.DatabaseMetaData md = localConnection
2101: .getConnection().getMetaData();
2102: if (!md.supportsBatchUpdates()) {
2103: for (Iterator l = valueObjectList.iterator(); l
2104: .hasNext();) {
2105: DataObject oneDataObject = (DataObject) l
2106: .next();
2107: oneDataObject.add();
2108: }
2109: if (localConnection.supportsTransactions()) {
2110: if (!localConnection.getImmortal()) {
2111: localConnection.commit();
2112: localConnection.setAutoCommit(true);
2113: }
2114: }
2115: return;
2116: }
2117: theStatementString = buildPreparedAddSQL(oneObj,
2118: addChangedFieldsOnly);
2119: prepStatement = localConnection
2120: .createPreparedStatement(theStatementString);
2121: checkObject = oneObj;
2122: metadata = checkObject.getMetaData();
2123: }
2124:
2125: boolean needCommaValues = false;
2126: valuesCommand.append(" VALUES (");
2127:
2128: //
2129: //we have to make sure that the classes are the same time or
2130: //we're in trouble.
2131: //
2132: if (!(checkObject.getClass().getName().equals(oneObj
2133: .getClass().getName()))) {
2134: throw new IllegalArgumentException(
2135: "JDBC Excecutor.addBatch()"
2136: + " all objects in the valueObjectList must be the same type.");
2137: }
2138:
2139: //
2140: //Now we iterate through and set all the parameters
2141: //
2142: for (Iterator j = metadata.getFieldListArray()
2143: .iterator(); j.hasNext();) {
2144: String oneFieldName = (String) j.next();
2145:
2146: // Only include changed fields in the update if required
2147: if (addChangedFieldsOnly) {
2148: if (oneObj.getDataField(oneFieldName)
2149: .isValueSet()
2150: && oneObj.getStatus().equals(
2151: DataObject.STATUS_NEW)) {
2152: ;
2153: } else {
2154: if (!oneObj.getDataField(oneFieldName)
2155: .isChanged()) {
2156: continue;
2157: }
2158: }
2159: }
2160:
2161: DataFieldMetaData oneField = oneObj
2162: .getFieldMetaData(oneFieldName);
2163: oneObj.checkField(oneField.getName(), oneObj
2164: .getField(oneField.getName()));
2165:
2166: //Sematics . if oneStringValue == null then
2167: //we skip the field.
2168: //if oneStringValue.equals("null"); then we add a nullable
2169: //section to the PreparedStatement.
2170: String oneStringValue = null;
2171:
2172: oneStringValue = prepareForStorage(oneField,
2173: oneObj, false);
2174:
2175: if (oneStringValue == null) {
2176: oneStringValue = "null";
2177: }
2178:
2179: if (log.isDebugEnabled()) {
2180: if (needCommaValues) {
2181: valuesCommand.append(", ");
2182: }
2183: valuesCommand.append(oneStringValue);
2184: needCommaValues = true;
2185: }
2186:
2187: if (oneField.isKey()) {
2188: keyDataMetaData.add(oneField);
2189: keyData.add(oneStringValue);
2190: } else {
2191: //We don't mess with auto-inc fields, virtual fields, or
2192: //binary fields.
2193: if (oneField.isVirtual()
2194: || oneField.isBinaryObjectType()
2195: || oneField.isAutoIncremented()) {
2196: continue;
2197: }
2198: }
2199: fieldDataMetaData.add(oneField);
2200: fieldData.add(oneStringValue);
2201: }
2202:
2203: //We're done iterating through paramters/
2204:
2205: int size = fieldData.size();
2206: // try {
2207: // prepStatement.clearParameters();
2208: // } catch (NullPointerException ex) {
2209: // //
2210: // //Workaround for known Oracle bug. Clear parameters
2211: // //throws NPE's
2212: // //
2213: // if (log.isDebugEnabled()) {
2214: // log.debug("NPE. Oracle Driver?", ex);
2215: // }
2216: // }
2217:
2218: TypeMapper typeMapper = TypeMapper
2219: .getInstance(localConnection.getDataContext());
2220: for (int j = 0; j < size; j++) {
2221: String stringValue = (String) fieldData.get(j);
2222:
2223: DBField singleFieldMeta = (DBField) fieldDataMetaData
2224: .get(j);
2225:
2226: int typeCode = typeMapper
2227: .getJavaSQLType(singleFieldMeta
2228: .getTypeString());
2229: if ("null".equals(stringValue)) {
2230: prepStatement.setNull(j + 1, typeCode);
2231: } else if (singleFieldMeta.isNumericType()) {
2232: if (singleFieldMeta.getTypeString()
2233: .equalsIgnoreCase("integer")
2234: || singleFieldMeta.getTypeString()
2235: .equalsIgnoreCase("tinyint")
2236: || singleFieldMeta.getTypeString()
2237: .equalsIgnoreCase("smallint")) {
2238: prepStatement.setInt(j + 1, Integer
2239: .parseInt(stringValue));
2240: } else if (singleFieldMeta.getTypeString()
2241: .equalsIgnoreCase("float")
2242: || singleFieldMeta.getTypeString()
2243: .equalsIgnoreCase("real")) {
2244: prepStatement.setFloat(j + 1, Float
2245: .parseFloat(stringValue));
2246: } else if (singleFieldMeta.getTypeString()
2247: .equalsIgnoreCase("bigint")
2248: || singleFieldMeta.getTypeString()
2249: .equalsIgnoreCase("numeric")
2250: || singleFieldMeta.getTypeString()
2251: .equalsIgnoreCase("decimal")) {
2252: prepStatement.setLong(j + 1, Long
2253: .parseLong(stringValue));
2254: } else if (singleFieldMeta.getTypeString()
2255: .equalsIgnoreCase("double")) {
2256: prepStatement.setDouble(j + 1, Double
2257: .parseDouble(stringValue));
2258: }
2259: } else if (singleFieldMeta.isDateType()) {
2260: if (singleFieldMeta.isDateOnlyType()) {
2261: Date dtOnly = java.sql.Date
2262: .valueOf(stringValue);
2263: prepStatement.setDate(j + 1, dtOnly);
2264: }
2265: if (singleFieldMeta.isDateTimeType()) {
2266: Timestamp dts = Timestamp
2267: .valueOf(stringValue);
2268: prepStatement.setTimestamp(j + 1, dts);
2269: }
2270: } else if (singleFieldMeta.isFloatingPointType()) {
2271: prepStatement.setFloat(j + 1, Float
2272: .parseFloat(stringValue));
2273: } else {
2274: if (singleFieldMeta.getTypeString()
2275: .equalsIgnoreCase("text")) {
2276: if (stringValue.indexOf("\n") > 0) {
2277: stringValue = StringUtil.replace(
2278: stringValue, "\n", "");
2279: }
2280: if (stringValue.indexOf("\r") > 0) {
2281: stringValue = StringUtil.replace(
2282: stringValue, "\r", "");
2283: }
2284: }
2285: prepStatement.setString(j + 1, stringValue);
2286:
2287: }
2288: }
2289:
2290: //Ok, we're set, now it's time to add the batch
2291:
2292: prepStatement.addBatch();
2293: if (log.isDebugEnabled()) {
2294: valuesCommand.append(") ");
2295: log.debug("Batch Add '" + theStatementString + " "
2296: + valuesCommand + " request in ("
2297: + localConnection.getDescription()
2298: + ", db/context '"
2299: + localConnection.getDataContext() + "')");
2300: valuesCommand.clear();
2301: }
2302: fieldData.clear();
2303: fieldDataMetaData.clear();
2304: keyData.clear();
2305: keyDataMetaData.clear();
2306: }
2307:
2308: prepStatement.executeBatch();
2309: localConnection.clearPreparedStatement();
2310: if (!localConnection.getImmortal()) {
2311: localConnection.commit();
2312: }
2313: valuesCommand.release();
2314: } catch (BatchUpdateException ex) {
2315: log.error("Batch Add Exception", ex);
2316: try {
2317: if (!localConnection.getImmortal()) {
2318: localConnection.rollback();
2319: }
2320: } catch (DBException ex1) {
2321: log
2322: .error(
2323: "Error rolling back transaction on local connection",
2324: ex1);
2325: }
2326: String msg = myName
2327: + "addBatch(DataObject) BatchUpdateException. ";
2328: log.error(msg, ex);
2329: throw new DataException(msg, ex);
2330: } catch (java.lang.ClassCastException cce) {
2331: cce.printStackTrace();
2332: throw cce;
2333: } catch (NullPointerException npe) {
2334: npe.printStackTrace();
2335: throw npe;
2336: } catch (SQLException ex) {
2337: ex.printStackTrace();
2338: try {
2339: if (!localConnection.getImmortal()) {
2340: localConnection.rollback();
2341: }
2342: } catch (DBException ex1) {
2343: log
2344: .error(
2345: "Error rolling back transaction on local connection",
2346: ex1);
2347: }
2348: String msg = myName
2349: + "addBatch(DataObject) SQLException. ";
2350: log.error(msg, ex);
2351: throw new DataException(msg, ex);
2352: } catch (DBException ex) {
2353: try {
2354: if (!localConnection.getImmortal()) {
2355: localConnection.rollback();
2356: }
2357: } catch (DBException ex1) {
2358: log
2359: .error(
2360: "Error rolling back transaction on local connection",
2361: ex1);
2362: }
2363: String msg = myName + "addBatch(DataObject) error";
2364: log.error(msg, ex);
2365: throw new DataException(msg, ex);
2366: } finally {
2367: if (prepStatement != null) {
2368: try {
2369: if (!localConnection.getImmortal()) {
2370: prepStatement.close();
2371: }
2372: } catch (SQLException ex) {
2373: log.error("Error closing prepared statement", ex);
2374: }
2375: }
2376:
2377: if (localConnection != null) {
2378: localConnection.clearPreparedStatement();
2379: }
2380:
2381: if (myPool != null) {
2382: if (localConnection != null) {
2383: //Auto-commit is automatically turned back on by
2384: //release
2385: if (!localConnection.getImmortal()) {
2386: myPool.release(localConnection);
2387: }
2388: }
2389: }
2390: }
2391: }
2392:
2393: /**
2394: * Updates an entire batch of <code>DataObject</code>s
2395: *
2396: * @param valueObjectList A list of <code>DataObject</code>s to update to the underlying
2397: * data source
2398: * @param updateChangedFieldsOnly flag to signify if only fields whose value
2399: * has changed should be included in the update.
2400: * @throws DataException upon error updating the data source
2401: */
2402: public void updateBatch(List valueObjectList,
2403: boolean updateChangedFieldsOnly) throws DataException {
2404: if (valueObjectList == null) {
2405: throw new IllegalArgumentException(
2406: myName
2407: + "updateBatch(List, boolean): valueObjectArray was null");
2408: }
2409:
2410: DBObject checkObject = null;
2411: DBConnectionPool myPool = null;
2412: DBConnection localConnection = null;
2413: DataObjectMetaData metadata = null;
2414: java.sql.PreparedStatement prepStatement = null;
2415: ArrayList fieldData = new ArrayList(6);
2416: ArrayList fieldDataMetaData = new ArrayList(6);
2417: ArrayList keyData = new ArrayList(3);
2418: ArrayList keyDataMetaData = new ArrayList(3);
2419: FastStringBuffer valuesCommand = FastStringBuffer.getInstance();
2420: String theStatementString = "";
2421:
2422: try {
2423: for (Iterator i = valueObjectList.iterator(); i.hasNext();) {
2424: DBObject oneObj = (DBObject) i.next();
2425: fieldData.clear();
2426: fieldDataMetaData.clear();
2427: keyData.clear();
2428: keyDataMetaData.clear();
2429:
2430: //
2431: //If we're on the first object, then we need to build the prepared statement
2432: //
2433: if (checkObject == null) {
2434: localConnection = oneObj.getLocalConnection();
2435: if (localConnection == null) {
2436: myPool = DBConnectionPool.getInstance(oneObj
2437: .getMappedDataContext());
2438: localConnection = myPool
2439: .getConnection("Batch Update");
2440: }
2441: localConnection.clear();
2442: if (localConnection.supportsTransactions()) {
2443: if (!localConnection.getImmortal()) {
2444: localConnection.setAutoCommit(false);
2445: }
2446: }
2447: //
2448: //If the database doesn't explicitly support batch updates,
2449: //then we call add multiple times.
2450: //
2451: java.sql.DatabaseMetaData md = localConnection
2452: .getConnection().getMetaData();
2453: if (!md.supportsBatchUpdates()) {
2454: for (Iterator l = valueObjectList.iterator(); l
2455: .hasNext();) {
2456: DataObject oneDataObject = (DataObject) l
2457: .next();
2458: oneDataObject.update();
2459: }
2460: if (localConnection.supportsTransactions()) {
2461: if (!localConnection.getImmortal()) {
2462: localConnection.commit();
2463: localConnection.setAutoCommit(true);
2464: }
2465: }
2466: return;
2467: }
2468:
2469: theStatementString = buildPreparedUpdateSQL(oneObj,
2470: updateChangedFieldsOnly);
2471: prepStatement = localConnection
2472: .createPreparedStatement(theStatementString);
2473: checkObject = oneObj;
2474: metadata = checkObject.getMetaData();
2475: }
2476:
2477: boolean needCommaValues = false;
2478: valuesCommand.append(" VALUES (");
2479:
2480: //
2481: //we have to make sure that the classes are the same time or
2482: //we're in trouble.
2483: //
2484: if (!(checkObject.getClass().getName().equals(oneObj
2485: .getClass().getName()))) {
2486: throw new IllegalArgumentException(
2487: "JDBC Excecutor.updateBatch(List, boolean)"
2488: + " all objects in the valueObjectList must be the same type.");
2489: }
2490:
2491: //
2492: //Now we iterate through and set all the parameters
2493: //
2494: for (Iterator j = metadata.getFieldListArray()
2495: .iterator(); j.hasNext();) {
2496: String oneFieldName = (String) j.next();
2497:
2498: // Only include changed fields in the update if required
2499: if (updateChangedFieldsOnly) {
2500: if (oneObj.getDataField(oneFieldName)
2501: .isValueSet()
2502: && oneObj.getStatus().equals(
2503: DataObject.STATUS_NEW)) {
2504: ;
2505: } else {
2506: if (!oneObj.getDataField(oneFieldName)
2507: .isChanged()) {
2508: continue;
2509: }
2510: }
2511: }
2512:
2513: DataFieldMetaData oneField = oneObj
2514: .getFieldMetaData(oneFieldName);
2515: oneObj.checkField(oneField.getName(), oneObj
2516: .getField(oneField.getName()));
2517:
2518: //Sematics . if oneStringValue == null then
2519: //we skip the field.
2520: //if oneStringValue.equals("null"); then we add a nullable
2521: //section to the PreparedStatement.
2522: String oneStringValue = null;
2523:
2524: oneStringValue = prepareForStorage(oneField,
2525: oneObj, false);
2526:
2527: if (oneStringValue == null) {
2528: oneStringValue = "null";
2529: }
2530:
2531: if (log.isDebugEnabled()) {
2532: if (needCommaValues) {
2533: valuesCommand.append(", ");
2534: }
2535: valuesCommand.append(oneStringValue);
2536: needCommaValues = true;
2537: }
2538:
2539: if (oneField.isKey()) {
2540: keyDataMetaData.add(oneField);
2541: keyData.add(oneStringValue);
2542: } else {
2543: //We don't mess with auto-inc fields, virtual fields, or
2544: //binary fields.
2545: if (oneField.isVirtual()
2546: || oneField.isBinaryObjectType()
2547: || oneField.isAutoIncremented()) {
2548: continue;
2549: }
2550:
2551: fieldDataMetaData.add(oneField);
2552: fieldData.add(oneStringValue);
2553: }
2554: }
2555: //
2556: //Ok, order of prepared statement is: data fields first, then
2557: //key fields so we concatenate to make things easier to cope
2558: //with logic-wise
2559: //
2560: fieldData.addAll(keyData);
2561: fieldDataMetaData.addAll(keyDataMetaData);
2562:
2563: int size = fieldData.size();
2564: // try {
2565: // prepStatement.clearParameters();
2566: // } catch (NullPointerException ex) {
2567: // //
2568: // //Workaround for known Oracle bug. Clear parameters
2569: // //throws NPE's
2570: // //
2571: // if (log.isDebugEnabled()) {
2572: // log.debug("NPE. Oracle Driver?", ex);
2573: // }
2574: // }
2575: TypeMapper typeMapper = TypeMapper
2576: .getInstance(localConnection.getDataContext());
2577: for (int j = 0; j < size; j++) {
2578: String stringValue = (String) fieldData.get(j);
2579:
2580: DBField singleFieldMeta = (DBField) fieldDataMetaData
2581: .get(j);
2582:
2583: int typeCode = typeMapper
2584: .getJavaSQLType(singleFieldMeta
2585: .getTypeString());
2586: if ("null".equals(stringValue)) {
2587: prepStatement.setNull(j + 1, typeCode);
2588: } else if (singleFieldMeta.isNumericType()) {
2589: if (singleFieldMeta.getTypeString()
2590: .equalsIgnoreCase("integer")
2591: || singleFieldMeta.getTypeString()
2592: .equalsIgnoreCase("numeric")
2593: || singleFieldMeta.getTypeString()
2594: .equalsIgnoreCase("tinyint")
2595: || singleFieldMeta.getTypeString()
2596: .equalsIgnoreCase("smallint")) {
2597: prepStatement.setInt(j + 1, Integer
2598: .parseInt(stringValue));
2599: } else if (singleFieldMeta.getTypeString()
2600: .equalsIgnoreCase("float")
2601: || singleFieldMeta.getTypeString()
2602: .equalsIgnoreCase("real")) {
2603: prepStatement.setFloat(j + 1, Float
2604: .parseFloat(stringValue));
2605: } else if (singleFieldMeta.getTypeString()
2606: .equalsIgnoreCase("bigint")
2607: || singleFieldMeta.getTypeString()
2608: .equalsIgnoreCase("decimal")) {
2609: prepStatement.setLong(j + 1, Long
2610: .parseLong(stringValue));
2611: } else if (singleFieldMeta.getTypeString()
2612: .equalsIgnoreCase("double")) {
2613: prepStatement.setDouble(j + 1, Double
2614: .parseDouble(stringValue));
2615: }
2616: } else if (singleFieldMeta.isDateType()) {
2617: if (singleFieldMeta.isDateOnlyType()) {
2618: Date dtOnly = java.sql.Date
2619: .valueOf(stringValue);
2620: prepStatement.setDate(j + 1, dtOnly);
2621: }
2622: if (singleFieldMeta.isDateTimeType()) {
2623: Timestamp dts = Timestamp
2624: .valueOf(stringValue);
2625: prepStatement.setTimestamp(j + 1, dts);
2626: }
2627: } else if (singleFieldMeta.isFloatingPointType()) {
2628: prepStatement.setFloat(j + 1, Float
2629: .parseFloat(stringValue));
2630: } else {
2631: if (singleFieldMeta.getTypeString()
2632: .equalsIgnoreCase("text")) {
2633: if (stringValue.indexOf("\n") > 0) {
2634: stringValue = StringUtil.replace(
2635: stringValue, "\n", "");
2636: }
2637: if (stringValue.indexOf("\r") > 0) {
2638: stringValue = StringUtil.replace(
2639: stringValue, "\r", "");
2640: }
2641: }
2642: prepStatement.setString(j + 1, stringValue);
2643:
2644: }
2645: }
2646:
2647: //Ok, we're set, now it's time to add the batch
2648: prepStatement.addBatch();
2649: if (oneObj.isCached()) {
2650: oneObj.removeFromCache(oneObj);
2651: }
2652: if (log.isDebugEnabled()) {
2653: valuesCommand.append(") ");
2654: log.debug("Batch Update '" + theStatementString
2655: + " " + valuesCommand + " request in ("
2656: + localConnection.getDescription()
2657: + ", db/context '"
2658: + localConnection.getDataContext() + "')");
2659: valuesCommand.clear();
2660: }
2661: fieldData.clear();
2662: fieldDataMetaData.clear();
2663: keyData.clear();
2664: keyDataMetaData.clear();
2665: }
2666:
2667: prepStatement.executeBatch();
2668: localConnection.clearPreparedStatement();
2669: if (!localConnection.getImmortal()) {
2670: localConnection.commit();
2671: }
2672: valuesCommand.release();
2673: } catch (NullPointerException npe) {
2674: log.error("NPE Error performing batch update", npe);
2675: throw npe;
2676: } catch (NumberFormatException nfe) {
2677: log
2678: .error(
2679: "NumberFormatException Error performing batch update",
2680: nfe);
2681: throw nfe;
2682: } catch (java.lang.ArrayIndexOutOfBoundsException ex) {
2683: log
2684: .error(
2685: "ArrayIndexOutOfBoundsException performing batch update",
2686: ex);
2687: throw ex;
2688: } catch (ClassCastException cce) {
2689: log.error("Class Cast Exception performing batch update",
2690: cce);
2691: throw cce;
2692: } catch (SQLException ex) {
2693: try {
2694: if (!localConnection.getImmortal()) {
2695: localConnection.rollback();
2696: }
2697: } catch (DBException ex1) {
2698: log.error(
2699: "Error rolling back update batch transaction",
2700: ex1);
2701: }
2702: ex.printStackTrace();
2703: String msg = myName
2704: + "updateBatch(DataObject, updateChangedFieldsOnly) SQLException. ";
2705: log.error(msg, ex);
2706: throw new DataException(msg, ex);
2707: } catch (DBException ex) {
2708: try {
2709: if (!localConnection.getImmortal()) {
2710: localConnection.rollback();
2711: }
2712: } catch (DBException ex1) {
2713: log.error(
2714: "Error rolling back update batch transaction",
2715: ex1);
2716: }
2717: String msg = myName
2718: + "updateBatch(DataObject, updateChangedFieldsOnly) error";
2719: log.error(msg, ex);
2720: throw new DataException(msg, ex);
2721: } catch (Throwable t) {
2722: log.error("Caught Throwable performing batch update", t);
2723: throw new DataException(t);
2724: } finally {
2725: if (prepStatement != null) {
2726: try {
2727: prepStatement.close();
2728: } catch (SQLException ex) {
2729: log
2730: .error(
2731: "Error closing prepared statement for batch update",
2732: ex);
2733: }
2734: }
2735: if (localConnection != null) {
2736: localConnection.clearPreparedStatement();
2737: }
2738:
2739: if (myPool != null) {
2740: if (localConnection != null) {
2741: //Auto-commit is automatically turned back on by
2742: //release
2743: if (!localConnection.getImmortal()) {
2744: myPool.release(localConnection);
2745: }
2746: }
2747: }
2748: }
2749: }
2750:
2751: /**
2752: * Updates an entire batch of <code>DataObject</code>s
2753: *
2754: * @param valueObjectList A list of <code>DataObject</code>s to update to the underlying
2755: * data source
2756: * @param deleteWithSetFieldsOnly Only include changed fields in the update
2757: * @throws DataException upon error updating the data source
2758: */
2759: public void deleteBatch(List valueObjectList,
2760: boolean deleteWithSetFieldsOnly) throws DataException {
2761: if (valueObjectList == null) {
2762: throw new IllegalArgumentException(
2763: myName
2764: + "updateBatch(List, boolean): valueObjectArray was null");
2765: }
2766:
2767: DBObject checkObject = null;
2768: DBConnectionPool myPool = null;
2769: DBConnection localConnection = null;
2770: DataObjectMetaData metadata = null;
2771: java.sql.PreparedStatement prepStatement = null;
2772: ArrayList fieldData = new ArrayList(6);
2773: ArrayList fieldDataMetaData = new ArrayList(6);
2774: ArrayList keyData = new ArrayList(3);
2775: ArrayList keyDataMetaData = new ArrayList(3);
2776: FastStringBuffer valuesCommand = FastStringBuffer.getInstance();
2777: String theStatementString = "";
2778:
2779: try {
2780: for (Iterator i = valueObjectList.iterator(); i.hasNext();) {
2781: DBObject oneObj = (DBObject) i.next();
2782: fieldData.clear();
2783: fieldDataMetaData.clear();
2784: keyData.clear();
2785: keyDataMetaData.clear();
2786:
2787: //
2788: //If we're on the first object, then we need to build the prepared statement
2789: //
2790: if (checkObject == null) {
2791: localConnection = oneObj.getLocalConnection();
2792: if (localConnection == null) {
2793: myPool = DBConnectionPool.getInstance(oneObj
2794: .getMappedDataContext());
2795: localConnection = myPool
2796: .getConnection("Batch Delete");
2797: }
2798: localConnection.clear();
2799: if (localConnection.supportsTransactions()) {
2800: if (!localConnection.getImmortal()) {
2801: localConnection.setAutoCommit(false);
2802: }
2803: }
2804: //
2805: //If the database doesn't explicitly support batch updates,
2806: //then we call add multiple times.
2807: //
2808: java.sql.DatabaseMetaData md = localConnection
2809: .getConnection().getMetaData();
2810: if (!md.supportsBatchUpdates()) {
2811: for (Iterator l = valueObjectList.iterator(); l
2812: .hasNext();) {
2813: DataObject oneDataObject = (DataObject) l
2814: .next();
2815: oneDataObject.update();
2816: }
2817: if (localConnection.supportsTransactions()) {
2818: if (!localConnection.getImmortal()) {
2819: localConnection.commit();
2820: localConnection.setAutoCommit(true);
2821: }
2822: }
2823: return;
2824: }
2825:
2826: theStatementString = buildPreparedDeleteSQL(oneObj,
2827: deleteWithSetFieldsOnly);
2828: prepStatement = localConnection
2829: .createPreparedStatement(theStatementString);
2830: checkObject = oneObj;
2831: metadata = checkObject.getMetaData();
2832: }
2833:
2834: boolean needCommaValues = false;
2835: valuesCommand.append(" VALUES (");
2836:
2837: //
2838: //we have to make sure that the classes are the same time or
2839: //we're in trouble.
2840: //
2841: if (!(checkObject.getClass().getName().equals(oneObj
2842: .getClass().getName()))) {
2843: throw new IllegalArgumentException(
2844: "JDBC Excecutor.deleteBatch(List, boolean)"
2845: + " all objects in the valueObjectList must be the same type.");
2846: }
2847:
2848: //
2849: //Now we iterate through and set all the parameters
2850: //
2851: for (Iterator j = metadata.getFieldListArray()
2852: .iterator(); j.hasNext();) {
2853: String oneFieldName = (String) j.next();
2854:
2855: // Only include changed fields in the update if required
2856: if (deleteWithSetFieldsOnly) {
2857: if (oneObj.getDataField(oneFieldName)
2858: .isValueSet()
2859: && oneObj.getStatus().equals(
2860: DataObject.STATUS_NEW)) {
2861: ;
2862: } else {
2863: if (!oneObj.getDataField(oneFieldName)
2864: .isChanged()) {
2865: continue;
2866: }
2867: }
2868: }
2869:
2870: DataFieldMetaData oneField = oneObj
2871: .getFieldMetaData(oneFieldName);
2872: oneObj.checkField(oneField.getName(), oneObj
2873: .getField(oneField.getName()));
2874:
2875: //Sematics . if oneStringValue == null then
2876: //we skip the field.
2877: //if oneStringValue.equals("null"); then we add a nullable
2878: //section to the PreparedStatement.
2879: String oneStringValue = null;
2880:
2881: oneStringValue = prepareForStorage(oneField,
2882: oneObj, false);
2883:
2884: if (oneStringValue == null) {
2885: oneStringValue = "null";
2886: }
2887:
2888: if (log.isDebugEnabled()) {
2889: if (needCommaValues) {
2890: valuesCommand.append(", ");
2891: }
2892: valuesCommand.append(oneStringValue);
2893: needCommaValues = true;
2894: }
2895:
2896: if (oneField.isKey()) {
2897: keyDataMetaData.add(oneField);
2898: keyData.add(oneStringValue);
2899: } else {
2900: //We don't mess with auto-inc fields, virtual fields, or
2901: //binary fields.
2902: if (oneField.isVirtual()
2903: || oneField.isBinaryObjectType()
2904: || oneField.isAutoIncremented()) {
2905: continue;
2906: }
2907:
2908: // fieldDataMetaData.add(oneField);
2909: // fieldData.add(oneStringValue);
2910: }
2911: }
2912: //
2913: //Ok, order of prepared statement is: data fields first, then
2914: //key fields so we concatenate to make things easier to cope
2915: //with logic-wise
2916: //
2917: fieldData.addAll(keyData);
2918: fieldDataMetaData.addAll(keyDataMetaData);
2919:
2920: int size = fieldData.size();
2921: // try {
2922: // prepStatement.clearParameters();
2923: // } catch (NullPointerException ex) {
2924: // //
2925: // //Workaround for known Oracle bug. Clear parameters
2926: // //throws NPE's
2927: // //
2928: // if (log.isDebugEnabled()) {
2929: // log.debug("NPE. Oracle Driver?", ex);
2930: // }
2931: // }
2932: TypeMapper typeMapper = TypeMapper
2933: .getInstance(localConnection.getDataContext());
2934: for (int j = 0; j < size; j++) {
2935: String stringValue = (String) fieldData.get(j);
2936:
2937: DBField singleFieldMeta = (DBField) fieldDataMetaData
2938: .get(j);
2939:
2940: int typeCode = typeMapper
2941: .getJavaSQLType(singleFieldMeta
2942: .getTypeString());
2943: if ("null".equals(stringValue)) {
2944: prepStatement.setNull(j + 1, typeCode);
2945: } else if (singleFieldMeta.isNumericType()) {
2946: if (singleFieldMeta.getTypeString()
2947: .equalsIgnoreCase("integer")
2948: || singleFieldMeta.getTypeString()
2949: .equalsIgnoreCase("numeric")
2950: || singleFieldMeta.getTypeString()
2951: .equalsIgnoreCase("tinyint")
2952: || singleFieldMeta.getTypeString()
2953: .equalsIgnoreCase("smallint")) {
2954: prepStatement.setInt(j + 1, Integer
2955: .parseInt(stringValue));
2956: } else if (singleFieldMeta.getTypeString()
2957: .equalsIgnoreCase("float")
2958: || singleFieldMeta.getTypeString()
2959: .equalsIgnoreCase("real")) {
2960: prepStatement.setFloat(j + 1, Float
2961: .parseFloat(stringValue));
2962: } else if (singleFieldMeta.getTypeString()
2963: .equalsIgnoreCase("bigint")
2964: || singleFieldMeta.getTypeString()
2965: .equalsIgnoreCase("decimal")) {
2966: prepStatement.setLong(j + 1, Long
2967: .parseLong(stringValue));
2968: } else if (singleFieldMeta.getTypeString()
2969: .equalsIgnoreCase("double")) {
2970: prepStatement.setDouble(j + 1, Double
2971: .parseDouble(stringValue));
2972: }
2973: } else if (singleFieldMeta.isDateType()) {
2974: if (singleFieldMeta.isDateOnlyType()) {
2975: Date dtOnly = java.sql.Date
2976: .valueOf(stringValue);
2977: prepStatement.setDate(j + 1, dtOnly);
2978: }
2979: if (singleFieldMeta.isDateTimeType()) {
2980: Timestamp dts = Timestamp
2981: .valueOf(stringValue);
2982: prepStatement.setTimestamp(j + 1, dts);
2983: }
2984: } else if (singleFieldMeta.isFloatingPointType()) {
2985: prepStatement.setFloat(j + 1, Float
2986: .parseFloat(stringValue));
2987: } else {
2988: if (singleFieldMeta.getTypeString()
2989: .equalsIgnoreCase("text")) {
2990: if (stringValue.indexOf("\n") > 0) {
2991: stringValue = StringUtil.replace(
2992: stringValue, "\n", "");
2993: }
2994: if (stringValue.indexOf("\r") > 0) {
2995: stringValue = StringUtil.replace(
2996: stringValue, "\r", "");
2997: }
2998: }
2999: prepStatement.setString(j + 1, stringValue);
3000:
3001: }
3002: }
3003:
3004: //Ok, we're set, now it's time to add the batch
3005: prepStatement.addBatch();
3006: if (oneObj.isCached()) {
3007: oneObj.removeFromCache(oneObj);
3008: }
3009: if (log.isDebugEnabled()) {
3010: valuesCommand.append(") ");
3011: log.debug("Batch Delete '" + theStatementString
3012: + " " + valuesCommand + " request in ("
3013: + localConnection.getDescription()
3014: + ", db/context '"
3015: + localConnection.getDataContext() + "')");
3016: valuesCommand.clear();
3017: }
3018: fieldData.clear();
3019: fieldDataMetaData.clear();
3020: keyData.clear();
3021: keyDataMetaData.clear();
3022: }
3023:
3024: prepStatement.executeBatch();
3025: localConnection.clearPreparedStatement();
3026: if (!localConnection.getImmortal()) {
3027: localConnection.commit();
3028: }
3029: valuesCommand.release();
3030: } catch (NullPointerException npe) {
3031: log.error("NPE Error performing batch delete", npe);
3032: throw npe;
3033: } catch (NumberFormatException nfe) {
3034: log
3035: .error(
3036: "NumberFormatException Error performing batch delete",
3037: nfe);
3038: throw nfe;
3039: } catch (java.lang.ArrayIndexOutOfBoundsException ex) {
3040: log
3041: .error(
3042: "ArrayIndexOutOfBoundsException performing batch delete",
3043: ex);
3044: throw ex;
3045: } catch (ClassCastException cce) {
3046: log.error("Class Cast Exception performing batch delete",
3047: cce);
3048: throw cce;
3049: } catch (SQLException ex) {
3050: try {
3051: if (!localConnection.getImmortal()) {
3052: localConnection.rollback();
3053: }
3054: } catch (DBException ex1) {
3055: log.error(
3056: "Error rolling back delete batch transaction",
3057: ex1);
3058: }
3059: ex.printStackTrace();
3060: String msg = myName
3061: + "deleteBatch(DataObject, deleteWithSetFieldsOnly) SQLException. ";
3062: log.error(msg, ex);
3063: throw new DataException(msg, ex);
3064: } catch (DBException ex) {
3065: try {
3066: if (!localConnection.getImmortal()) {
3067: localConnection.rollback();
3068: }
3069: } catch (DBException ex1) {
3070: log.error(
3071: "Error rolling back delete batch transaction",
3072: ex1);
3073: }
3074: String msg = myName
3075: + "deleteBatch(DataObject, deleteWithSetFieldsOnly) error";
3076: log.error(msg, ex);
3077: throw new DataException(msg, ex);
3078: } catch (Throwable t) {
3079: log.error("Caught Throwable performing batch delete", t);
3080: throw new DataException(t);
3081: } finally {
3082: if (prepStatement != null) {
3083: try {
3084: prepStatement.close();
3085: } catch (SQLException ex) {
3086: log
3087: .error(
3088: "Error closing prepared statement for batch delete",
3089: ex);
3090: }
3091: }
3092: if (localConnection != null) {
3093: localConnection.clearPreparedStatement();
3094: }
3095:
3096: if (myPool != null) {
3097: if (localConnection != null) {
3098: //Auto-commit is automatically turned back on by
3099: //release
3100: if (!localConnection.getImmortal()) {
3101: myPool.release(localConnection);
3102: }
3103: }
3104: }
3105: }
3106:
3107: }
3108:
3109: /**
3110: * Deletes an entire batch of <code>DataObject</code>s
3111: *
3112: * @param valueObjectList A list of <code>DataObject</code>s to delete to the underlying
3113: * data source
3114: * @throws DataException upon error deleting the data source
3115: */
3116: public void deleteBatch(List valueObjectList) throws DataException {
3117: if (valueObjectList == null) {
3118: throw new IllegalArgumentException(myName
3119: + "deleteBatch(List): valueObjectArray was null");
3120: }
3121: deleteBatch(valueObjectList, false);
3122:
3123: }
3124:
3125: }
|