0001: /* ====================================================================
0002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
0003: *
0004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * 1. Redistributions of source code must retain the above copyright
0011: * notice, this list of conditions and the following disclaimer.
0012: *
0013: * 2. Redistributions in binary form must reproduce the above copyright
0014: * notice, this list of conditions and the following disclaimer in
0015: * the documentation and/or other materials provided with the
0016: * distribution.
0017: *
0018: * 3. The end-user documentation included with the redistribution,
0019: * if any, must include the following acknowledgment:
0020: * "This product includes software developed by Jcorporate Ltd.
0021: * (http://www.jcorporate.com/)."
0022: * Alternately, this acknowledgment may appear in the software itself,
0023: * if and wherever such third-party acknowledgments normally appear.
0024: *
0025: * 4. "Jcorporate" and product names such as "Expresso" must
0026: * not be used to endorse or promote products derived from this
0027: * software without prior written permission. For written permission,
0028: * please contact info@jcorporate.com.
0029: *
0030: * 5. Products derived from this software may not be called "Expresso",
0031: * or other Jcorporate product names; nor may "Expresso" or other
0032: * Jcorporate product names appear in their name, without prior
0033: * written permission of Jcorporate Ltd.
0034: *
0035: * 6. No product derived from this software may compete in the same
0036: * market space, i.e. framework, without prior written permission
0037: * of Jcorporate Ltd. For written permission, please contact
0038: * partners@jcorporate.com.
0039: *
0040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
0044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
0046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0051: * SUCH DAMAGE.
0052: * ====================================================================
0053: *
0054: * This software consists of voluntary contributions made by many
0055: * individuals on behalf of the Jcorporate Ltd. Contributions back
0056: * to the project(s) are encouraged when you make modifications.
0057: * Please send them to support@jcorporate.com. For more information
0058: * on Jcorporate Ltd. and its products, please see
0059: * <http://www.jcorporate.com/>.
0060: *
0061: * Portions of this software are based upon other open source
0062: * products and are subject to their respective licenses.
0063: */
0064: package com.jcorporate.expresso.core.dataobjects.jdbc;
0065:
0066: import com.jcorporate.expresso.core.dataobjects.DataException;
0067: import com.jcorporate.expresso.core.dataobjects.DataFieldMetaData;
0068: import com.jcorporate.expresso.core.db.DBConnection;
0069: import com.jcorporate.expresso.core.db.DBConnectionPool;
0070: import com.jcorporate.expresso.core.db.DBException;
0071: import com.jcorporate.expresso.core.db.TypeMapper;
0072: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
0073: import org.apache.log4j.Logger;
0074:
0075: import java.io.IOException;
0076: import java.io.InputStream;
0077: import java.io.OutputStream;
0078: import java.io.StringReader;
0079: import java.io.Writer;
0080: import java.lang.reflect.InvocationTargetException;
0081: import java.lang.reflect.Method;
0082: import java.sql.Blob;
0083: import java.sql.Clob;
0084: import java.sql.PreparedStatement;
0085: import java.sql.ResultSet;
0086: import java.sql.SQLException;
0087:
0088: /**
0089: * This class provides a low level BLOB capability while still keeping the
0090: * programmer isolated from SQL syntax details.
0091: * <p>The general usage is as follows:<br/>
0092: * <code>
0093: * MyDBObject myObj.setField("key1",1);<br/>
0094: * LobField query = new LobField();<br/>
0095: * query.setCriteria(myObj);<br/>
0096: * java.io.InputStream inputStream = getBlobStream("blobFieldName");
0097: * //Do whatever you want with the stream <br/>
0098: * inputStream.flush();
0099: * inputStream.close();
0100: * query.close();
0101: * </code>
0102: * </p>
0103: * <p>This class requires a JDBC 2 compliant driver for the BLOB/CLOB data types.
0104: * Some drivers do not support these features, at which point you'll want
0105: * to use the getBlobStream/getClobStream/getClobAsciiStream methods instead
0106: * </p>
0107: *
0108: * @author Michael Rimov
0109: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
0110: */
0111:
0112: public class LobField {
0113:
0114: /**
0115: * The dataobject to use to build the SQL search/update statements.
0116: */
0117: protected JDBCDataObject criteria = null;
0118:
0119: /**
0120: * The log4j Logger to use.
0121: */
0122: private Logger log = Logger.getLogger(LobField.class);
0123:
0124: boolean alreadyInTransaction = false;
0125:
0126: protected DBConnection myConnection = null;
0127:
0128: /**
0129: * Default constructor. Currently does nothing.
0130: */
0131: public LobField() {
0132:
0133: }
0134:
0135: /**
0136: * Set the search criteria for the blob. All key fields must be present
0137: * as this does a full retrieve() rather than a search on the data. Otherwise
0138: * the object will throw an exception .
0139: *
0140: * @param newCriteria a filled out JDBCObject (DBObject derived classes work)
0141: * with all keys present
0142: * @throws DataException if all keys are not present.
0143: */
0144: public void setCriteria(JDBCDataObject newCriteria)
0145: throws DataException {
0146: if (newCriteria == null) {
0147: throw new IllegalArgumentException(
0148: "Criteria cannot be null");
0149: }
0150:
0151: criteria = newCriteria;
0152: if (newCriteria.getLocalConnection() != null) {
0153: myConnection = newCriteria.getLocalConnection();
0154: }
0155: }
0156:
0157: /**
0158: * Protected method to get at the criteria object from any derived classes
0159: *
0160: * @return JDBCDataObject or null if no criteria has been set
0161: */
0162: protected JDBCDataObject getCriteria() {
0163: return criteria;
0164: }
0165:
0166: /**
0167: * Retrieves a <code>java.sql.Blob</code> object given the criteria object
0168: * set previously.
0169: *
0170: * @param fieldName the name of the field to retrieve
0171: * @return java.sql.Blob for the field
0172: * @throws DataException if there is an error finding the object, an error
0173: * retrieving the Blob from the system, or other database communication
0174: * errors.
0175: */
0176: public Blob getBlob(String fieldName) throws DataException {
0177: if (getCriteria() == null) {
0178: throw new IllegalArgumentException(
0179: "Criteria must be set before calling getBLob");
0180: }
0181:
0182: try {
0183: if (myConnection == null) {
0184: myConnection = DBConnectionPool.getInstance(
0185: getCriteria().getMappedDataContext())
0186: .getConnection("LOB Field Connection");
0187: }
0188: } catch (DBException ex) {
0189: throw new DataException("Error getting Database"
0190: + " Connection for BLOB Retrieval", ex);
0191: }
0192:
0193: return getBLOB(getCriteria(), fieldName, myConnection);
0194: }
0195:
0196: /**
0197: * Retrieves a <code>java.sql.Clob</code> object given the criteria object
0198: * set previously.
0199: *
0200: * @param fieldName the name of the field to retrieve
0201: * @return java.sql.Clob for the field
0202: * @throws DataException if there is an error finding the object, an error
0203: * retrieving the Clob from the system, or other database communication
0204: * errors.
0205: */
0206: public Clob getClob(String fieldName) throws DataException {
0207: if (getCriteria() == null) {
0208: throw new IllegalArgumentException(
0209: "Criteria must be set before calling getClob");
0210: }
0211:
0212: try {
0213: if (myConnection == null) {
0214: myConnection = DBConnectionPool.getInstance(
0215: getCriteria().getMappedDataContext())
0216: .getConnection("LOB Field Connection");
0217: }
0218: } catch (DBException ex) {
0219: throw new DataException("Error getting Database"
0220: + " Connection for CLOB Retrieval", ex);
0221: }
0222:
0223: return getCLOB(getCriteria(), fieldName, myConnection);
0224: }
0225:
0226: /**
0227: * Retrieve an input stream for a binary object stored in the database.
0228: *
0229: * @param fieldName the field name to retrieve.
0230: * @return java.io.InputStream representing the BLOB object
0231: * @throws DataException upon error
0232: */
0233: public InputStream getBlobStream(String fieldName)
0234: throws DataException {
0235: if (getCriteria() == null) {
0236: throw new IllegalArgumentException(
0237: "Criteria must be set before calling getBLob");
0238: }
0239:
0240: try {
0241: if (myConnection == null) {
0242: myConnection = DBConnectionPool.getInstance(
0243: getCriteria().getMappedDataContext())
0244: .getConnection("LOB Field Connection");
0245: }
0246: } catch (DBException ex) {
0247: throw new DataException("Error getting Database"
0248: + " Connection for BLOB Retrieval", ex);
0249: }
0250:
0251: try {
0252: prepSelectResultSet(getCriteria(), fieldName, myConnection);
0253: if (myConnection.next()) {
0254: return myConnection.getBinaryStream(1);
0255: } else {
0256: return null;
0257: }
0258: } catch (DBException ex) {
0259: throw new DataException("Error getting binary stream", ex);
0260: }
0261:
0262: }
0263:
0264: /**
0265: * Retrieve a java.io.Reader a.k.a Unicode stream for a CLOB field.
0266: *
0267: * @param fieldName the name of the field to retrieve.
0268: * @return java.io.Reader for the Unicode CLOB stream stored in the database
0269: * @throws DataException upon error retrieving the CLOB
0270: */
0271: public java.io.Reader getClobStream(String fieldName)
0272: throws DataException {
0273: if (getCriteria() == null) {
0274: throw new IllegalArgumentException(
0275: "Criteria must be set before calling getBLob");
0276: }
0277:
0278: try {
0279: if (myConnection == null) {
0280: myConnection = DBConnectionPool.getInstance(
0281: getCriteria().getMappedDataContext())
0282: .getConnection("LOB Field Connection");
0283: }
0284: } catch (DBException ex) {
0285: throw new DataException("Error getting Database"
0286: + " Connection for BLOB Retrieval", ex);
0287: }
0288:
0289: return this .getCLOBReader(getCriteria(), fieldName,
0290: myConnection);
0291: }
0292:
0293: /**
0294: * CLOB convenience method. Reads the entire stream into a string. Note
0295: * that if your field is large, this may take large amounts of memory
0296: * to perform this operation. It is recommended to use getClobStream()
0297: * for most purposes.
0298: * <p>Note that this method is not supported by InterBase/InterClient 2 which
0299: * does not support Unicode streams. Use getClobAsciiStream instead</p>
0300: *
0301: * @param fieldName the name of the field to retrieve
0302: * @return java.lang.String containing the entire contents of the CLOB
0303: * field.
0304: * @throws DataException upon error.
0305: */
0306: public String getClobString(String fieldName) throws DataException {
0307: java.io.Reader is = this .getClobStream(fieldName);
0308: if (is == null) {
0309: return null;
0310: }
0311:
0312: FastStringBuffer fsb = FastStringBuffer.getInstance();
0313: try {
0314:
0315: char[] buf = new char[1024]; // 1K buffer
0316: int bytesRead;
0317:
0318: while ((bytesRead = is.read(buf)) != -1) {
0319: fsb.append(buf, 0, bytesRead);
0320: }
0321:
0322: return fsb.toString();
0323: } catch (java.io.IOException ex) {
0324: throw new DataException(
0325: "I/O Exception reading Character Stream");
0326: } finally {
0327: fsb.release();
0328: }
0329:
0330: }
0331:
0332: /**
0333: * Retrieve a java.io.InputStream a.k.a. ASCII stream for a CLOB field.
0334: *
0335: * @param fieldName the name of the field to retrieve.
0336: * @return java.io.Reader for the ASCII CLOB stream stored in the database
0337: * @throws DataException upon error retrieving the CLOB
0338: */
0339: public InputStream getClobAsciiStream(String fieldName)
0340: throws DataException {
0341: try {
0342: Clob theClob = getClob(fieldName);
0343: return theClob.getAsciiStream();
0344: } catch (SQLException ex) {
0345: throw new DataException("Error getting clob ascii stream: "
0346: + fieldName, ex);
0347: }
0348: }
0349:
0350: /**
0351: * Saves an InputStream into the database given the criteria and the fieldname
0352: * (Criteria should have been previously set).
0353: *
0354: * @param fieldName The name of the field to save the Stream to.
0355: * @param data a java.io.InputStream object to save to the field. May be null
0356: * if you want the field to be null.
0357: * @param dataSize the length of the stream to save.
0358: * @throws DataException upon database communications error.
0359: * @throws IllegalArgumentException if fieldName is null.
0360: */
0361: public void saveBlob(String fieldName, InputStream data,
0362: int dataSize) throws DataException {
0363: try {
0364: if (myConnection == null) {
0365: myConnection = DBConnectionPool.getInstance(
0366: getCriteria().getMappedDataContext())
0367: .getConnection("LOB Field Connection");
0368:
0369: if ("org.hsqldb.jdbcDriver".equals(myConnection
0370: .getDBDriver())) {
0371: //check the database size.
0372: //Hypersonic has a maximum practical capability of 200K MAX
0373: if (dataSize > 1024 * 200) {
0374: throw new DataException(
0375: "HSQLDB can only store maxium of 200K files");
0376: }
0377: }
0378: }
0379: } catch (DBException ex) {
0380: throw new DataException("Error getting Database"
0381: + " Connection for BLOB Retrieval", ex);
0382: }
0383:
0384: try {
0385: alreadyInTransaction = !(myConnection.getAutoCommit());
0386: } catch (DBException ex) {
0387: throw new DataException(
0388: "Error getting Database"
0389: + " Connection transaction parameter for BLOB Retrieval",
0390: ex);
0391: }
0392:
0393: // if running Oracle, use Oracle specific methods because of Oracle's
0394: // incompatibility with standard jdbc LOB mothods
0395: if ("oracle.jdbc.driver.OracleDriver".equals(myConnection
0396: .getDBDriver())) {
0397: oracleSaveBlob(fieldName, data, dataSize);
0398: return;
0399: }
0400:
0401: PreparedStatement preparedStatement = prepUpdate(getCriteria(),
0402: fieldName, myConnection);
0403:
0404: try {
0405: if (data == null) {
0406: DataFieldMetaData metaData = getCriteria()
0407: .getFieldMetaData(fieldName);
0408: int typeCode = TypeMapper.getInstance(
0409: getCriteria().getMappedDataContext())
0410: .getJavaSQLType(metaData.getTypeString());
0411: preparedStatement.setNull(1, typeCode);
0412: } else {
0413: preparedStatement.setBinaryStream(1, data, dataSize);
0414: }
0415: } catch (SQLException ex) {
0416: throw new DataException(
0417: "Unable to set CharacterStream to CLOB object.", ex);
0418: } catch (DBException ex) {
0419: throw new DataException(
0420: "Unable to get Type Mapping information for Clob field",
0421: ex);
0422: }
0423:
0424: finalizeUpdate(myConnection);
0425:
0426: }
0427:
0428: /**
0429: * Saves a <code>java.sql.Blob</code> to the record matching the criteria
0430: * earlier set.
0431: *
0432: * @param fieldName the name of the field to save to
0433: * @param data the <code>java.sql.Blob</code> based object to save to the
0434: * database.
0435: * @throws DataException upon database communication error
0436: * @throws IllegalArgumentException if data is null
0437: */
0438: public void saveBlob(String fieldName, Blob data)
0439: throws DataException {
0440: try {
0441: if (myConnection == null) {
0442: myConnection = DBConnectionPool.getInstance(
0443: getCriteria().getMappedDataContext())
0444: .getConnection("LOB Field Connection");
0445: }
0446: } catch (DBException ex) {
0447: throw new DataException("Error getting Database"
0448: + " Connection for BLOB Retrieval", ex);
0449: }
0450:
0451: try {
0452: alreadyInTransaction = !(myConnection.getAutoCommit());
0453: } catch (DBException ex) {
0454: throw new DataException(
0455: "Error getting Database"
0456: + " Connection transaction parameter for BLOB Retrieval",
0457: ex);
0458: }
0459: PreparedStatement preparedStatement = prepUpdate(getCriteria(),
0460: fieldName, myConnection);
0461:
0462: try {
0463: if (data == null) {
0464: DataFieldMetaData metaData = getCriteria()
0465: .getFieldMetaData(fieldName);
0466: int typeCode = TypeMapper.getInstance(
0467: getCriteria().getMappedDataContext())
0468: .getJavaSQLType(metaData.getTypeString());
0469: preparedStatement.setNull(1, typeCode);
0470: } else {
0471: preparedStatement.setBlob(1, data);
0472: }
0473: } catch (SQLException ex) {
0474: throw new DataException(
0475: "Unable to set CharacterStream to CLOB object.", ex);
0476: } catch (DBException ex) {
0477: throw new DataException(
0478: "Unable to get type mapping information", ex);
0479: }
0480:
0481: finalizeUpdate(myConnection);
0482:
0483: }
0484:
0485: /**
0486: * Saves an InputStream into the database given the criteria and the fieldname
0487: * (Criteria should have been previously set).
0488: *
0489: * @param fieldName The name of the field to save the Stream to.
0490: * @param data a java.io.InputStream object to save to the field. May be null
0491: * if you want the field to be null.
0492: * @param length The size of the CLOB stream to save to the database
0493: * @throws IllegalArgumentException if fieldName is null.
0494: * @throws DataException upon database communications error.
0495: */
0496: public void saveClob(String fieldName, InputStream data, int length)
0497: throws DataException {
0498:
0499: try {
0500: if (myConnection == null) {
0501: myConnection = DBConnectionPool.getInstance(
0502: getCriteria().getMappedDataContext())
0503: .getConnection("LOB Field Connection");
0504: }
0505: } catch (DBException ex) {
0506: throw new DataException("Error getting Database"
0507: + " Connection for BLOB Retrieval", ex);
0508: }
0509:
0510: try {
0511: alreadyInTransaction = !(myConnection.getAutoCommit());
0512: } catch (DBException ex) {
0513: throw new DataException(
0514: "Error getting Database"
0515: + " Connection transaction parameter for BLOB Retrieval",
0516: ex);
0517: }
0518:
0519: // if running Oracle, use Oracle specific methods because of Oracle's incompatibility
0520: // with standard jdbc LOB methods
0521: if ("oracle.jdbc.driver.OracleDriver".equals(myConnection
0522: .getDBDriver())) {
0523: oracleSaveClob(fieldName, data, length);
0524: return;
0525: }
0526:
0527: PreparedStatement preparedStatement = prepUpdate(getCriteria(),
0528: fieldName, myConnection);
0529:
0530: try {
0531: if (data == null) {
0532: DataFieldMetaData metaData = getCriteria()
0533: .getFieldMetaData(fieldName);
0534: int typeCode = TypeMapper.getInstance(
0535: getCriteria().getMappedDataContext())
0536: .getJavaSQLType(metaData.getTypeString());
0537: preparedStatement.setNull(1, typeCode);
0538: } else {
0539: preparedStatement.setAsciiStream(1, data, length);
0540: }
0541: } catch (SQLException ex) {
0542: throw new DataException(
0543: "Unable to set CharacterStream to CLOB object.", ex);
0544: } catch (DBException ex) {
0545: throw new DataException(
0546: "Unable to get type mapping information for CLOB object",
0547: ex);
0548: }
0549:
0550: finalizeUpdate(myConnection);
0551:
0552: }
0553:
0554: /**
0555: * Saves an InputStream into the database given the criteria and the fieldname
0556: * (Criteria should have been previously set).
0557: *
0558: * @param fieldName The name of the field to save the Stream to.
0559: * @param data a java.io.Reader object to save to the field. May be null
0560: * if you want the field to be null.
0561: * @param length The size of the data stream to save to the database
0562: * @throws DataException upon database communications error.
0563: * @throws IllegalArgumentException if fieldName is null.
0564: */
0565: public void saveClob(String fieldName, java.io.Reader data,
0566: int length) throws DataException {
0567:
0568: try {
0569: if (myConnection == null) {
0570: myConnection = DBConnectionPool.getInstance(
0571: getCriteria().getMappedDataContext())
0572: .getConnection("LOB Field Connection");
0573: }
0574: } catch (DBException ex) {
0575: throw new DataException("Error getting Database"
0576: + " Connection for BLOB Retrieval", ex);
0577: }
0578:
0579: try {
0580: alreadyInTransaction = !(myConnection.getAutoCommit());
0581: } catch (DBException ex) {
0582: throw new DataException(
0583: "Error getting Database"
0584: + " Connection transaction parameter for BLOB Retrieval",
0585: ex);
0586: }
0587:
0588: if ("interbase.interclient.Driver".equals(myConnection
0589: .getDBDriver())) {
0590: throw new DataException(
0591: "InterBase Interclient 2 cannot support unicode data. "
0592: + " Must use saveClob(String, InputStream, length)"
0593: + "instead");
0594: }
0595:
0596: // if running Oracle, use Oracle specific methods because of Oracle's
0597: // incompatibility with standard jdbc LOB mothods
0598: if ("oracle.jdbc.driver.OracleDriver".equals(myConnection
0599: .getDBDriver())) {
0600: if (data == null) {
0601: oracleSaveClob(fieldName, (StringReader) null, 0);
0602: } else {
0603: oracleSaveClob(fieldName, data, length);
0604: }
0605: return;
0606: }
0607:
0608: PreparedStatement preparedStatement = prepUpdate(getCriteria(),
0609: fieldName, myConnection);
0610:
0611: try {
0612: if (data == null) {
0613: DataFieldMetaData metaData = getCriteria()
0614: .getFieldMetaData(fieldName);
0615: int typeCode = TypeMapper.getInstance(
0616: getCriteria().getMappedDataContext())
0617: .getJavaSQLType(metaData.getTypeString());
0618: preparedStatement.setNull(1, typeCode);
0619: } else {
0620: preparedStatement.setCharacterStream(1, data, length);
0621: }
0622: } catch (SQLException ex) {
0623: throw new DataException(
0624: "Unable to set CharacterStream to CLOB object.", ex);
0625: } catch (DBException ex) {
0626: throw new DataException(
0627: "Unable to get type mapping information for CLOB field",
0628: ex);
0629: }
0630:
0631: finalizeUpdate(myConnection);
0632: }
0633:
0634: /**
0635: * Saves a string to a CLOB field.
0636: *
0637: * @param fieldName the name of the field to save to.
0638: * @param data the String value to save to the field.
0639: * @throws DataException upon error
0640: */
0641: public void saveClob(String fieldName, String data)
0642: throws DataException {
0643: try {
0644: if (myConnection == null) {
0645: myConnection = DBConnectionPool.getInstance(
0646: getCriteria().getMappedDataContext())
0647: .getConnection("LOB Field Connection");
0648: }
0649: } catch (DBException ex) {
0650: throw new DataException("Error getting Database"
0651: + " Connection for BLOB Retrieval", ex);
0652: }
0653:
0654: try {
0655: alreadyInTransaction = !(myConnection.getAutoCommit());
0656: } catch (DBException ex) {
0657: throw new DataException(
0658: "Error getting Database"
0659: + " Connection transaction parameter for BLOB Retrieval",
0660: ex);
0661: }
0662:
0663: // if running Oracle, use Oracle specific methods because of Oracle's
0664: // incompatibility with standard jdbc LOB mothods
0665: if ("oracle.jdbc.driver.OracleDriver".equals(myConnection
0666: .getDBDriver())) {
0667: if (data == null) {
0668: oracleSaveClob(fieldName, (StringReader) null, 0);
0669: } else {
0670: oracleSaveClob(fieldName, new StringReader(data), data
0671: .length());
0672: }
0673: return;
0674: }
0675:
0676: PreparedStatement preparedStatement = prepUpdate(getCriteria(),
0677: fieldName, myConnection);
0678:
0679: try {
0680: if (data == null) {
0681: DataFieldMetaData metaData = getCriteria()
0682: .getFieldMetaData(fieldName);
0683: int typeCode = TypeMapper.getInstance(
0684: getCriteria().getMappedDataContext())
0685: .getJavaSQLType(metaData.getTypeString());
0686: preparedStatement.setNull(1, typeCode);
0687: } else {
0688: if ("interbase.interclient.Driver".equals(myConnection
0689: .getDBDriver())) {
0690: //
0691: //Workaround, interclient 2 doesn't support setCharacterStream()
0692: //
0693: byte[] dataArray = data.getBytes();
0694: java.io.ByteArrayInputStream bis = new java.io.ByteArrayInputStream(
0695: dataArray);
0696: preparedStatement.setAsciiStream(1, bis,
0697: dataArray.length);
0698: } else {
0699: java.io.Reader r = new java.io.StringReader(data);
0700: preparedStatement.setCharacterStream(1, r, data
0701: .length());
0702: }
0703: }
0704: } catch (SQLException ex) {
0705: throw new DataException(
0706: "Unable to set CharacterStream to CLOB object.", ex);
0707: } catch (DBException ex) {
0708: throw new DataException(
0709: "Unable to get type information for CLOB field", ex);
0710: }
0711:
0712: finalizeUpdate(myConnection);
0713:
0714: }
0715:
0716: /**
0717: * Saves an InputStream into the database given the criteria and the fieldname
0718: * (Criteria should have been previously set).
0719: *
0720: * @param fieldName The name of the field to save the Stream to.
0721: * @param data a java.io.Reader object to save to the field. May be null
0722: * if you want the field to be null.
0723: * @throws DataException upon database communications error.
0724: * @throws IllegalArgumentException if fieldName is null.
0725: */
0726: public void saveClob(String fieldName, Clob data)
0727: throws DataException {
0728:
0729: try {
0730: if (myConnection == null) {
0731: myConnection = DBConnectionPool.getInstance(
0732: getCriteria().getMappedDataContext())
0733: .getConnection("LOB Field Connection");
0734: }
0735: } catch (DBException ex) {
0736: throw new DataException("Error getting Database"
0737: + " Connection for BLOB Retrieval", ex);
0738: }
0739:
0740: try {
0741: alreadyInTransaction = !(myConnection.getAutoCommit());
0742: } catch (DBException ex) {
0743: throw new DataException(
0744: "Error getting Database"
0745: + " Connection transaction parameter for BLOB Retrieval",
0746: ex);
0747: }
0748:
0749: PreparedStatement preparedStatement = prepUpdate(getCriteria(),
0750: fieldName, myConnection);
0751:
0752: try {
0753: if (data == null) {
0754: DataFieldMetaData metaData = getCriteria()
0755: .getFieldMetaData(fieldName);
0756: int typeCode = TypeMapper.getInstance(
0757: getCriteria().getMappedDataContext())
0758: .getJavaSQLType(metaData.getTypeString());
0759: preparedStatement.setNull(1, typeCode);
0760: } else {
0761: preparedStatement.setClob(1, data);
0762: }
0763: } catch (SQLException ex) {
0764: throw new DataException(
0765: "Unable to set CharacterStream to CLOB object.", ex);
0766: } catch (DBException ex) {
0767: throw new DataException(
0768: "Unable to get type mapping information for CLOB field",
0769: ex);
0770: }
0771:
0772: finalizeUpdate(myConnection);
0773: }
0774:
0775: /**
0776: * Close the query resources held by the object. This should be wrapped
0777: * in a try/finally block so that database connection resources are not
0778: * left floating in limbo.
0779: */
0780: public void close() {
0781: if (myConnection != null) {
0782: if (log.isDebugEnabled()) {
0783: log.debug("Closing and releasing LOB connection");
0784: }
0785: try {
0786: if (!alreadyInTransaction
0787: && myConnection.supportsTransactions()) {
0788: myConnection.release();
0789: }
0790: } catch (DBException ex) {
0791: // throw new DataException("Error getting Database" +
0792: // " Connection transaction parameter for BLOB Retrieval", ex);
0793: }
0794: myConnection = null;
0795: }
0796: }
0797:
0798: /**
0799: * Override of base object finalization to make sure that the database
0800: * resources are closed if for some reason they haven't had this done
0801: * to them already.
0802: */
0803: protected void finalize() throws java.lang.Throwable {
0804: if (myConnection != null) {
0805: log
0806: .warn("LobField was not closed before Garbage Collection!");
0807: close();
0808: }
0809: super .finalize();
0810: }
0811:
0812: /**
0813: * Retrieves the CLOB data for the specified DBObject and the specified
0814: * fieldName
0815: *
0816: * @param baseObject The dbobject containing all the table information, and
0817: * key fields set.
0818: * @param fieldName The name of the field to retrieve as a LOB.
0819: * @param theConnection a <code>DBConnection</code> object that has already
0820: * been retrieved from the <code>DBConnectionPool</code>
0821: * @return a java.sql.CLOB object.
0822: * @throws DataException if there's an error executing the statement
0823: */
0824: private Clob getCLOB(JDBCDataObject baseObject, String fieldName,
0825: DBConnection theConnection) throws DataException {
0826: prepSelectResultSet(baseObject, fieldName, theConnection);
0827:
0828: try {
0829: if (theConnection.next()) {
0830: return theConnection.getClob(fieldName);
0831: }
0832: } catch (DBException ex) {
0833: throw new DataException(
0834: "Error getting CLOB object from connection", ex);
0835: }
0836:
0837: return null;
0838: }
0839:
0840: /**
0841: * Retrieves the CLOB data for the specified DBObject and the specified
0842: * fieldName
0843: *
0844: * @param baseObject The dbobject containing all the table information, and
0845: * key fields set.
0846: * @param fieldName The name of the field to retrieve as a LOB.
0847: * @param theConnection a <code>DBConnection</code> object that has already
0848: * been retrieved from the <code>DBConnectionPool</code>
0849: * @return a Reader object.
0850: * @throws DataException if there's an error executing the statement
0851: */
0852: private java.io.Reader getCLOBReader(JDBCDataObject baseObject,
0853: String fieldName, DBConnection theConnection)
0854: throws DataException {
0855: prepSelectResultSet(baseObject, fieldName, theConnection);
0856: ResultSet rs = theConnection.getResultSet();
0857: try {
0858: if (rs.next()) {
0859: try {
0860: return rs.getCharacterStream(1);
0861: } catch (SQLException ex) {
0862: return new java.io.StringReader(rs.getString(1));
0863: }
0864: }
0865: } catch (java.sql.SQLException ex) {
0866: throw new DataException(
0867: "Error getting CLOB object from connection", ex);
0868: }
0869:
0870: return null;
0871: }
0872:
0873: /**
0874: * Returns a single stream for a LOB object. This single LOB object is
0875: * not wrapped by any javax.activation code and therefore is strictly up to
0876: * the code creator on how to use the Input Stream.
0877: *
0878: * @param baseObject The dbobject containing all the table information, and
0879: * key fields set.
0880: * @param fieldName The name of the field to retrieve as a LOB.
0881: * @param theConnection a <code>DBConnection</code> object that has already
0882: * been retrieved from the <code>DBConnectionPool</code>
0883: * @return an array of bytes that will contain the BLOB
0884: * @throws DataException if there's an error executing the statement
0885: */
0886: private Blob getBLOB(JDBCDataObject baseObject, String fieldName,
0887: DBConnection theConnection) throws DataException {
0888:
0889: try {
0890: prepSelectResultSet(baseObject, fieldName, theConnection);
0891: if (theConnection.next()) {
0892: return theConnection.getBlob(1);
0893: } else {
0894: return null;
0895: }
0896: } catch (DBException ex) {
0897: throw new DataException("Error getting Blob Field", ex);
0898: }
0899:
0900: }
0901:
0902: /**
0903: * Internal helper function that does the guts of the work
0904: *
0905: * @param baseObject The object that contains the metadata for this BLOB
0906: * @param fieldName the name of the field that is the BLOB field
0907: * @param theConnection an already allocated DBConnection object. <b>This
0908: * function modifies the state of theConnection by allocating a prepared
0909: * statement</b>
0910: * @throws DataException upon error
0911: * <p/>
0912: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
0913: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
0914: */
0915: protected void prepSelectResultSet(JDBCDataObject baseObject,
0916: String fieldName, DBConnection theConnection)
0917: throws DataException {
0918: try {
0919: FastStringBuffer prepStatement = FastStringBuffer
0920: .getInstance();
0921: prepStatement.append("SELECT ");
0922: prepStatement.append(fieldName);
0923: prepStatement.append(" from ");
0924: prepStatement.append(baseObject.getJDBCMetaData()
0925: .getTargetSQLTable(baseObject.getDataContext()));
0926: String whereClause = JDBCUtil.getInstance()
0927: .buildWhereClause(baseObject, false);
0928: prepStatement.append(whereClause);
0929: String thePrepString = prepStatement.toString();
0930: prepStatement.release();
0931:
0932: if (log.isDebugEnabled()) {
0933: log.debug("Preparing prepared statement: "
0934: + thePrepString);
0935: }
0936:
0937: PreparedStatement prep = theConnection
0938: .createPreparedStatement(thePrepString);
0939: if (prep == null) {
0940: throw new DataException(
0941: "Unable to create prepared statement for CLOB retrieval."
0942: + " Check DBConnection log for details");
0943: }
0944: theConnection.execute();
0945:
0946: if (log.isDebugEnabled()) {
0947: log.debug("Succesfully executed prepared statement");
0948: }
0949: } catch (DBException ex) {
0950: throw new DataException("Error prepping SELECT ResultSet",
0951: ex);
0952: }
0953: }
0954:
0955: /**
0956: * Internal helper function to prepare a LOB update
0957: *
0958: * @param baseObject The object that contains the metadata for this BLOB
0959: * @param fieldName the name of the field that is the BLOB field
0960: * @param theConnection an already allocated DBConnection object
0961: * @return a created PreparedStatement object
0962: * @throws DataException Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
0963: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
0964: */
0965: protected PreparedStatement prepUpdate(JDBCDataObject baseObject,
0966: String fieldName, DBConnection theConnection)
0967: throws DataException {
0968: try {
0969: String whereClause = JDBCUtil.getInstance()
0970: .buildWhereClause(baseObject, false);
0971:
0972: FastStringBuffer prepStatement = FastStringBuffer
0973: .getInstance();
0974: String theSQL = null;
0975: try {
0976: prepStatement.append("UPDATE ");
0977: prepStatement
0978: .append(baseObject.getJDBCMetaData()
0979: .getTargetSQLTable(
0980: baseObject.getDataContext()));
0981: prepStatement.append(" SET ");
0982: prepStatement.append(fieldName);
0983: prepStatement.append(" = ? ");
0984: prepStatement.append(whereClause);
0985: theSQL = prepStatement.toString();
0986: } finally {
0987: prepStatement.release();
0988: prepStatement = null;
0989: }
0990:
0991: return theConnection.createPreparedStatement(theSQL);
0992: } catch (DBException ex) {
0993: throw new DataException("Error prepping LOB update", ex);
0994: }
0995: }
0996:
0997: protected void finalizeUpdate(DBConnection theConnection)
0998: throws DataException {
0999:
1000: try {
1001: boolean success = false;
1002:
1003: //
1004: //SAPDB and others REQUIRE that BLOB updates be done within a transaction. So
1005: //If we aren't already within one, we enter one now. Then commit
1006: //if a transaction wasn't already in process
1007: //
1008: if (!alreadyInTransaction
1009: && theConnection.supportsTransactions()) {
1010: if (log.isDebugEnabled()) {
1011: log.debug("Turning off auto-commit");
1012: }
1013: theConnection.setAutoCommit(false);
1014: }
1015:
1016: try {
1017: theConnection.executeUpdate(null);
1018: success = true;
1019: } finally {
1020: if (success == false) {
1021: if (!alreadyInTransaction
1022: && theConnection.supportsTransactions()) {
1023: if (log.isDebugEnabled()) {
1024: log.debug("rolling back");
1025: }
1026: theConnection.rollback();
1027: }
1028: }
1029: if (!alreadyInTransaction
1030: && theConnection.supportsTransactions()) {
1031: if (log.isDebugEnabled()) {
1032: log
1033: .debug("Finishing commit and turning auto-commit back to true");
1034: }
1035: theConnection.commit();
1036: theConnection.setAutoCommit(true);
1037: }
1038: }
1039: } catch (DBException ex) {
1040: throw new DataException("Error finalizing LOB update", ex);
1041: }
1042:
1043: }
1044:
1045: /**
1046: * Updates the underlying table with a BLOB object. Same as the byte[] method
1047: * but uses InputStreams intead
1048: *
1049: * @param baseObject The object that contains the metadata for this BLOB
1050: * @param fieldName the name of the field that is the BLOB field
1051: * @param theData the data as an InputStream
1052: * @param dataLength the total length to be read from the InputStream
1053: * @param theConnection an already allocated DBConnection object
1054: * @throws DBException upon error
1055: */
1056: private void setBLOB(JDBCDataObject baseObject, String fieldName,
1057: InputStream theData, int dataLength,
1058: DBConnection theConnection) throws DBException {
1059: PreparedStatement preparedStatement = prepUpdate(baseObject,
1060: fieldName, theConnection);
1061:
1062: try {
1063:
1064: preparedStatement.setBinaryStream(1, theData, dataLength);
1065: } catch (SQLException ex) {
1066: throw new DBException("Error setting BLOB object", ex);
1067: }
1068:
1069: finalizeUpdate(theConnection);
1070: }
1071:
1072: /**
1073: * Writes a LONG Character string to the database.
1074: *
1075: * @param baseObject The object that contains the metadata for this BLOB
1076: * @param fieldName the name of the field that is the BLOB field
1077: * @param theData the data in a <code>String</code>
1078: * @param theConnection an already allocated DBConnection object Currently
1079: * may <b>not</b> be null
1080: * @throws DBException upon error
1081: */
1082: private void setCLOB(JDBCDataObject baseObject, String fieldName,
1083: String theData, DBConnection theConnection)
1084: throws DBException {
1085:
1086: PreparedStatement preparedStatement = prepUpdate(baseObject,
1087: fieldName, theConnection);
1088:
1089: try {
1090: if ("interbase.interclient.Driver".equals(theConnection
1091: .getDBDriver())) {
1092: //
1093: //Workaround, interclient 2 doesn't support setCharacterStream()
1094: //
1095: byte[] data = theData.getBytes();
1096: java.io.ByteArrayInputStream bis = new java.io.ByteArrayInputStream(
1097: data);
1098: preparedStatement.setAsciiStream(1, bis, data.length);
1099: } else {
1100: java.io.Reader r = new java.io.StringReader(theData);
1101: preparedStatement.setCharacterStream(1, r, theData
1102: .length());
1103: }
1104: } catch (SQLException ex) {
1105: throw new DBException(
1106: "Unable to set CharacterStream to CLOB object.", ex);
1107: }
1108:
1109: finalizeUpdate(theConnection);
1110: }
1111:
1112: /**
1113: * Saves an InputStream into the database given the criteria and the fieldname
1114: * (Criteria should have been previously set).
1115: * <p/>
1116: * Specific to Oracle. Oracle (as of 9.2) does not support standard jdbc LOB
1117: * functionality.
1118: *
1119: * @param fieldName The name of the field to save the Stream to.
1120: * @param data a java.io.InputStream object to save to the field. May be null
1121: * if you want the field to be null.
1122: * @param dataSize the length of the stream to save.
1123: * @throws DataException upon database communications error.
1124: * @throws IllegalArgumentException if fieldName is null.
1125: */
1126: private void oracleSaveBlob(String fieldName, InputStream data,
1127: int dataSize) throws DataException {
1128: try {
1129: if (myConnection == null) {
1130: myConnection = DBConnectionPool.getInstance(
1131: getCriteria().getMappedDataContext())
1132: .getConnection("LOB Field Connection");
1133: }
1134: } catch (DBException ex) {
1135: throw new DataException("Error getting Database"
1136: + " Connection for BLOB Retrieval", ex);
1137: }
1138:
1139: // update the BLOB with empty_blob() in case a LOB Locator doesn't yet
1140: // exist for the record
1141: oraclePrepUpdateEmptyLob(getCriteria(), fieldName, myConnection);
1142:
1143: try {
1144: if (data != null) {
1145: oraclePrepSelectForUpdate(getCriteria(), fieldName,
1146: myConnection);
1147: if (myConnection.next()) {
1148: // use reflection here so that Oracle classes are not needed for compilation. Performs the following:
1149: // - oracle.sql.BLOB blob = ((oracle.jdbc.driver.OracleResultSet)myConnection.getResultSet()).getBLOB(1);
1150: // - OutputStream oBlob = blob.getBinaryOutputStream();
1151: // - byte[] chunk = new byte[blob.getChunkSize()];
1152: Class oracleResultSetClass = Class
1153: .forName("oracle.jdbc.driver.OracleResultSet");
1154: Class[] parameterTypes = new Class[] { int.class };
1155: Object[] arguments = new Object[] { new Integer(1) };
1156: Method getBlobMethod = oracleResultSetClass
1157: .getMethod("getBlob", parameterTypes);
1158: Object blob = getBlobMethod.invoke(
1159: (Object) myConnection.getResultSet(),
1160: arguments);
1161:
1162: parameterTypes = new Class[] {};
1163: arguments = new Object[] {};
1164: Class oracleBlobClass = Class
1165: .forName("oracle.sql.BLOB");
1166: Method getBinaryOutputStreamMethod = oracleBlobClass
1167: .getMethod("getBinaryOutputStream",
1168: parameterTypes);
1169: OutputStream oBlob = (OutputStream) getBinaryOutputStreamMethod
1170: .invoke(blob, arguments);
1171:
1172: Method getChunkSizeMethod = oracleBlobClass
1173: .getMethod("getChunkSize", parameterTypes);
1174: byte[] chunk = new byte[((Integer) getChunkSizeMethod
1175: .invoke(blob, arguments)).intValue()];
1176:
1177: int i = -1;
1178: while ((i = data.read(chunk)) != -1) {
1179: oBlob.write(chunk, 0, i);
1180: }
1181:
1182: oBlob.close();
1183: data.close();
1184: // release the row lock that the select for update created
1185: if (!alreadyInTransaction
1186: && myConnection.supportsTransactions()) {
1187: myConnection.commit();
1188: }
1189: } else {
1190: throw new DataException(
1191: "Error SELECTing record for update.");
1192: }
1193: }
1194: } catch (DBException ex) {
1195: throw new DataException(
1196: "Error SELECTing record for update.", ex);
1197: } catch (NoSuchMethodException ex) {
1198: throw new DataException(
1199: "Reflection error on oracle classes.", ex);
1200: } catch (IllegalAccessException ex) {
1201: throw new DataException(
1202: "Reflection error on oracle classes.", ex);
1203: } catch (InvocationTargetException ex) {
1204: throw new DataException(
1205: "Reflection error on oracle classes.", ex);
1206: } catch (ClassNotFoundException ex) {
1207: throw new DataException(
1208: "Reflection error on oracle classes.", ex);
1209: } catch (IOException ex) {
1210: throw new DataException("Error reading from InputStream.",
1211: ex);
1212: }
1213: }
1214:
1215: /**
1216: * Saves an InputStream into the database given the criteria and the fieldname
1217: * (Criteria should have been previously set).
1218: * <p/>
1219: * Specific to Oracle. Oracle (as of 9.2) does not support standard jdbc LOB
1220: * functionality.
1221: *
1222: * @param fieldName The name of the field to save the Stream to.
1223: * @param data a java.io.InputStream object to save to the field. May be null
1224: * if you want the field to be null.
1225: * @param length The size of the CLOB stream to save to the database
1226: * @throws IllegalArgumentException if fieldName is null.
1227: * @throws DataException upon database communications error.
1228: */
1229: private void oracleSaveClob(String fieldName, InputStream data,
1230: int length) throws DataException {
1231:
1232: try {
1233: if (myConnection == null) {
1234: myConnection = DBConnectionPool.getInstance(
1235: getCriteria().getMappedDataContext())
1236: .getConnection("LOB Field Connection");
1237: }
1238: } catch (DBException ex) {
1239: throw new DataException("Error getting Database"
1240: + " Connection for CLOB Retrieval", ex);
1241: }
1242:
1243: if (data == null) {
1244: oraclePrepUpdateNullClob(getCriteria(), fieldName,
1245: myConnection);
1246: return;
1247: }
1248:
1249: // update the CLOB with empty_clob() in case a LOB Locator doesn't yet
1250: // exist for the record
1251: oraclePrepUpdateEmptyLob(getCriteria(), fieldName, myConnection);
1252:
1253: try {
1254: oraclePrepSelectForUpdate(getCriteria(), fieldName,
1255: myConnection);
1256: if (myConnection.next()) {
1257: // use reflection here so that Oracle classes are not needed for compilation. Performs the following:
1258: // - oracle.sql.BLOB blob = ((oracle.jdbc.driver.OracleResultSet)myConnection.getResultSet()).getCLOB(1);
1259: // - OutputStream oBlob = blob.getBinaryOutputStream();
1260: // - byte[] chunk = new byte[blob.getChunkSize()];
1261: Class oracleResultSetClass = Class
1262: .forName("oracle.jdbc.driver.OracleResultSet");
1263: Class[] parameterTypes = new Class[] { int.class };
1264: Object[] arguments = new Object[] { new Integer(1) };
1265: Method getClobMethod = oracleResultSetClass.getMethod(
1266: "getClob", parameterTypes);
1267: Object clob = getClobMethod
1268: .invoke((Object) myConnection.getResultSet(),
1269: arguments);
1270:
1271: parameterTypes = new Class[] {};
1272: arguments = new Object[] {};
1273: Class oracleClobClass = Class
1274: .forName("oracle.sql.CLOB");
1275: Method getAsciiOutputStreamMethod = oracleClobClass
1276: .getMethod("getAsciiOutputStreamMethod",
1277: parameterTypes);
1278: OutputStream oClob = (OutputStream) getAsciiOutputStreamMethod
1279: .invoke(clob, arguments);
1280:
1281: Method getChunkSizeMethod = oracleClobClass.getMethod(
1282: "getChunkSize", parameterTypes);
1283: byte[] chunk = new byte[((Integer) getChunkSizeMethod
1284: .invoke(clob, arguments)).intValue()];
1285:
1286: int i = -1;
1287: while ((i = data.read(chunk)) != -1) {
1288: oClob.write(chunk, 0, i);
1289: }
1290:
1291: oClob.close();
1292: data.close();
1293: // release the row lock that the select for update created
1294: if (!alreadyInTransaction
1295: && myConnection.supportsTransactions()) {
1296: myConnection.commit();
1297: }
1298: } else {
1299: throw new DataException(
1300: "Error SELECTing record for update.");
1301: }
1302: } catch (DBException ex) {
1303: throw new DataException(
1304: "Error SELECTing record for update.", ex);
1305: } catch (NoSuchMethodException ex) {
1306: throw new DataException(
1307: "Reflection error on oracle classes.", ex);
1308: } catch (IllegalAccessException ex) {
1309: throw new DataException(
1310: "Reflection error on oracle classes.", ex);
1311: } catch (InvocationTargetException ex) {
1312: throw new DataException(
1313: "Reflection error on oracle classes.", ex);
1314: } catch (ClassNotFoundException ex) {
1315: throw new DataException(
1316: "Reflection error on oracle classes.", ex);
1317: } catch (IOException ex) {
1318: throw new DataException("Error reading from InputStream.",
1319: ex);
1320: }
1321: }
1322:
1323: /**
1324: * Saves a Reader into the database given the criteria and the fieldname
1325: * <p/>
1326: * Specific to Oracle. Oracle (as of 9.2) does not support standard jdbc LOB
1327: * functionality.
1328: * <p/>
1329: * (Criteria should have been previously set).
1330: *
1331: * @param fieldName The name of the field to save the Stream to.
1332: * @param data a java.io.Reader object to save to the field. May be null
1333: * if you want the field to be null.
1334: * @param length The size of the data stream to save to the database
1335: * @throws DataException upon database communications error.
1336: * @throws IllegalArgumentException if fieldName is null.
1337: */
1338: private void oracleSaveClob(String fieldName, java.io.Reader data,
1339: int length) throws DataException {
1340: try {
1341: if (myConnection == null) {
1342: myConnection = DBConnectionPool.getInstance(
1343: getCriteria().getMappedDataContext())
1344: .getConnection("LOB Field Connection");
1345: }
1346: } catch (DBException ex) {
1347: throw new DataException("Error getting Database"
1348: + " Connection for CLOB Retrieval", ex);
1349: }
1350:
1351: if (data == null) {
1352: oraclePrepUpdateNullClob(getCriteria(), fieldName,
1353: myConnection);
1354: return;
1355: }
1356:
1357: // update the CLOB with empty_clob() in case a LOB Locator doesn't yet
1358: // exist for the record
1359: oraclePrepUpdateEmptyLob(getCriteria(), fieldName, myConnection);
1360:
1361: try {
1362: oraclePrepSelectForUpdate(getCriteria(), fieldName,
1363: myConnection);
1364: if (myConnection.next()) {
1365: // use reflection here so that Oracle classes are not needed for compilation. Performs the following:
1366: // - oracle.sql.BLOB blob = ((oracle.jdbc.driver.OracleResultSet)myConnection.getResultSet()).getCLOB(1);
1367: // - Writer oBlob = getCharacterOutputStream();
1368: // - char[] chunk = new char[blob.getChunkSize()];
1369: Class oracleResultSetClass = Class
1370: .forName("oracle.jdbc.driver.OracleResultSet");
1371: Class[] parameterTypes = new Class[] { int.class };
1372: Object[] arguments = new Object[] { new Integer(1) };
1373: Method getClobMethod = oracleResultSetClass.getMethod(
1374: "getClob", parameterTypes);
1375: Object clob = getClobMethod
1376: .invoke((Object) myConnection.getResultSet(),
1377: arguments);
1378:
1379: parameterTypes = new Class[] {};
1380: arguments = new Object[] {};
1381: Class oracleClobClass = Class
1382: .forName("oracle.sql.CLOB");
1383: Method getCharacterOutputStream = oracleClobClass
1384: .getMethod("getCharacterOutputStream",
1385: parameterTypes);
1386: Writer oClob = (Writer) getCharacterOutputStream
1387: .invoke(clob, arguments);
1388:
1389: Method getChunkSizeMethod = oracleClobClass.getMethod(
1390: "getChunkSize", parameterTypes);
1391: char[] chunk = new char[((Integer) getChunkSizeMethod
1392: .invoke(clob, arguments)).intValue()];
1393:
1394: int i = -1;
1395: while ((i = data.read(chunk)) != -1) {
1396: oClob.write(chunk, 0, i);
1397: }
1398:
1399: oClob.close();
1400: data.close();
1401: // release the row lock that the select for update created
1402: if (!alreadyInTransaction
1403: && myConnection.supportsTransactions()) {
1404: myConnection.commit();
1405: }
1406: } else {
1407: throw new DataException(
1408: "Error SELECTing record for update.");
1409: }
1410: } catch (DBException ex) {
1411: throw new DataException(
1412: "Error SELECTing record for update.", ex);
1413: } catch (NoSuchMethodException ex) {
1414: throw new DataException(
1415: "Reflection error on oracle classes.", ex);
1416: } catch (IllegalAccessException ex) {
1417: throw new DataException(
1418: "Reflection error on oracle classes.", ex);
1419: } catch (InvocationTargetException ex) {
1420: throw new DataException(
1421: "Reflection error on oracle classes.", ex);
1422: } catch (ClassNotFoundException ex) {
1423: throw new DataException(
1424: "Reflection error on oracle classes.", ex);
1425: } catch (IOException ex) {
1426: throw new DataException("Error reading from InputStream.",
1427: ex);
1428: }
1429: }
1430:
1431: /**
1432: * Internal helper function to prepare a LOB update for Oracle.
1433: * Updates the record, setting the BLOB to to empty_blob() or CLOB to empty_clob() because inserts without
1434: * setting the LOB will result in a LOB Locator not being present, which will prevent
1435: * the LOB from being added to the record.
1436: *
1437: * @param baseObject The object that contains the metadata for this BLOB
1438: * @param fieldName the name of the field that is the BLOB field
1439: * @param theConnection an already allocated DBConnection object
1440: * @throws DataException Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1441: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
1442: */
1443: private void oraclePrepUpdateEmptyLob(JDBCDataObject baseObject,
1444: String fieldName, DBConnection theConnection)
1445: throws DataException {
1446: try {
1447: String whereClause = JDBCUtil.getInstance()
1448: .buildWhereClause(baseObject, false);
1449:
1450: FastStringBuffer prepStatement = FastStringBuffer
1451: .getInstance();
1452: String theSQL = null;
1453: try {
1454: prepStatement.append("UPDATE ");
1455: prepStatement
1456: .append(baseObject.getJDBCMetaData()
1457: .getTargetSQLTable(
1458: baseObject.getDataContext()));
1459: prepStatement.append(" SET ");
1460: prepStatement.append(fieldName);
1461: if (baseObject.getDataField(fieldName)
1462: .getFieldMetaData().isCharacterLongObjectType()) {
1463: prepStatement.append(" = empty_clob() ");
1464: } else {
1465: prepStatement.append(" = empty_blob() ");
1466: }
1467: prepStatement.append(whereClause);
1468: theSQL = prepStatement.toString();
1469: } finally {
1470: prepStatement.release();
1471: prepStatement = null;
1472: }
1473:
1474: theConnection.createPreparedStatement(theSQL);
1475: finalizeUpdate(theConnection);
1476: } catch (DBException ex) {
1477: throw new DataException("Error prepping LOB update", ex);
1478: }
1479: }
1480:
1481: /**
1482: * Internal helper function to prepare a BLOB update for Oracle.
1483: * Updates the record, setting the BLOB to to empty_blob() because inserts without
1484: * setting the BLOB will result in a LOB Locator not being present, which will prevent
1485: * the BLOB from being added to the record.
1486: *
1487: * @param baseObject The object that contains the metadata for this BLOB
1488: * @param fieldName the name of the field that is the BLOB field
1489: * @param theConnection an already allocated DBConnection object
1490: * @throws DataException Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1491: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
1492: */
1493: private void oraclePrepUpdateNullClob(JDBCDataObject baseObject,
1494: String fieldName, DBConnection theConnection)
1495: throws DataException {
1496: try {
1497: String whereClause = JDBCUtil.getInstance()
1498: .buildWhereClause(baseObject, false);
1499:
1500: FastStringBuffer prepStatement = FastStringBuffer
1501: .getInstance();
1502: String theSQL = null;
1503: try {
1504: prepStatement.append("UPDATE ");
1505: prepStatement
1506: .append(baseObject.getJDBCMetaData()
1507: .getTargetSQLTable(
1508: baseObject.getDataContext()));
1509: prepStatement.append(" SET ");
1510: prepStatement.append(fieldName);
1511: prepStatement.append(" = null ");
1512: prepStatement.append(whereClause);
1513: theSQL = prepStatement.toString();
1514: } finally {
1515: prepStatement.release();
1516: prepStatement = null;
1517: }
1518:
1519: theConnection.createPreparedStatement(theSQL);
1520: finalizeUpdate(theConnection);
1521: } catch (DBException ex) {
1522: throw new DataException("Error prepping CLOB update", ex);
1523: }
1524: }
1525:
1526: /**
1527: * Internal helper function to prepare a BLOB update for Oracle.
1528: * Updates the record, setting the BLOB to to empty_blob() because inserts without
1529: * setting the BLOB will result in a LOB Locator not being present, which will prevent
1530: * the BLOB from being added to the record.
1531: *
1532: * @param baseObject The object that contains the metadata for this BLOB
1533: * @param fieldName the name of the field that is the BLOB field
1534: * @param theConnection an already allocated DBConnection object
1535: * @throws DataException Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1536: * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
1537: */
1538: protected void oraclePrepSelectForUpdate(JDBCDataObject baseObject,
1539: String fieldName, DBConnection theConnection)
1540: throws DataException {
1541: try {
1542: FastStringBuffer prepStatement = FastStringBuffer
1543: .getInstance();
1544: prepStatement.append("SELECT ");
1545: prepStatement.append(fieldName);
1546: prepStatement.append(" from ");
1547: prepStatement.append(baseObject.getJDBCMetaData()
1548: .getTargetSQLTable(baseObject.getDataContext()));
1549: String whereClause = JDBCUtil.getInstance()
1550: .buildWhereClause(baseObject, false);
1551: prepStatement.append(whereClause);
1552: prepStatement.append(" FOR UPDATE");
1553: String thePrepString = prepStatement.toString();
1554: prepStatement.release();
1555:
1556: if (log.isDebugEnabled()) {
1557: log.debug("Preparing prepared statement: "
1558: + thePrepString);
1559: }
1560:
1561: PreparedStatement prep = theConnection
1562: .createPreparedStatement(thePrepString);
1563: if (prep == null) {
1564: throw new DataException(
1565: "Unable to create prepared statement for CLOB retrieval."
1566: + " Check DBConnection log for details");
1567: }
1568: if (!alreadyInTransaction
1569: && theConnection.supportsTransactions()) {
1570: theConnection.setAutoCommit(false);
1571: }
1572: theConnection.execute();
1573:
1574: if (log.isDebugEnabled()) {
1575: log.debug("Succesfully executed prepared statement");
1576: }
1577: } catch (DBException ex) {
1578: throw new DataException("Error prepping SELECT ResultSet",
1579: ex);
1580: }
1581: }
1582:
1583: }
|