KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > dbobj > LOBSupport


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64
65 package com.jcorporate.expresso.core.dbobj;
66
67 import com.jcorporate.expresso.core.db.DBConnection;
68 import com.jcorporate.expresso.core.db.DBConnectionPool;
69 import com.jcorporate.expresso.core.db.DBException;
70 import com.jcorporate.expresso.core.misc.StringUtil;
71 import com.jcorporate.expresso.kernel.util.FastStringBuffer;
72 import org.apache.log4j.Logger;
73
74 import java.io.InputStream JavaDoc;
75 import java.sql.PreparedStatement JavaDoc;
76 import java.sql.SQLException JavaDoc;
77
78 /**
79  * Many databases support arbitrarily large objects. The problem with large objects
80  * is that their symantics are significantly different compared to other standard
81  * database fields. Specifically some highlighted differences are:
82  * <ul>
83  * <li><b>Size -</b> The arbitrary size of LOB objects can significantly consume system
84  * resources if you automatically load all LOBS along with the rest of the
85  * data object</li>
86  * <li><b>JDBC Interface -</b> Many JDBC Drivers require you to use streams to retrieve LOBs from a database
87  * as opposed to standard objects loaded from a connection.</li>
88  * <li><b>Transaction Requirements -</b>Many JDBC Drivers require to update BLOBS within
89  * an explicitly committed transaction</li>
90  * <li><b>Object Oriented Wrapping -</b> LOBs by definition are of unknown objects
91  * Therefore, it is difficult for an Object Oriented System to deal with the
92  * arbitrariness of the object. Some sort of data type mapping must be supplied
93  * </li>
94  * </ul>
95  * <p/>
96  * <p>To support this, LOB Support is designed to provide several facilities to
97  * assist with this:<p>
98  * <p/>
99  * <p><b>Delayed Loading:</b> The LOBSupport class is activated by DBObjects only
100  * when a LOB data field's data is requested.</p>
101  * <p/>
102  * <p><b>Separate command execution</b> The LOBSupport executes a second statement
103  * to the database in the form of a PreparedStatement to retrieve all the Large
104  * Objects</p>
105  * <p/>
106  * <p><b>Transaction usage IF supported</b> If the JDBC driver indicates it supports
107  * transactions, LOBSupport will automatically attempt to setAutoCommit(false) and
108  * call explitic commit()s. If you are already within a transaction, LOBSupport
109  * will not attempt to get another transaction</p>
110  * <p/>
111  * <h4>Usage</h4>
112  * <p>Most often you would not use this class directly, but instead call the
113  * similar <code>DBObject</code> method calls. However, sometimes access to
114  * the lower level capabilities of these functions are useful for performance
115  * sake and therefore...</p>
116  *
117  * @author Michael Rimov
118  * @see com.jcorporate.expresso.core.dataobjcets.jdbc.LOBField
119  * @see com.jcorporate.expresso.services.dbobj.MediaDBObject
120  * <p/>
121  * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
122  * @since $DatabaseSchema $Date: 2004/11/17 20:48:11 $
123  * @deprecated since Expresso 5.3 the ways it worked on LOBs wasn't remotely
124  * efficient and has been replaced by the implementation in com.jcorporate.expresso.
125  * core.dataobjects.jdbc.LOBField
126  */

127
128 public class LOBSupport {
129     static protected LOBSupport theInstance = null;
130     static private Logger log = Logger.getLogger("com.jcorporate" +
131             ".expresso.core.dbobj.LOBSupport");
132
133     /**
134      * Default Constructor
135      */

136     protected LOBSupport() {
137     }
138
139     /**
140      * Singleton implementation. Use this to get to the LOB helper functions
141      *
142      * @return an instantiated LOBSupport class
143      */

144     public static synchronized LOBSupport getInstance() {
145         if (theInstance == null) {
146             theInstance = new LOBSupport();
147         }
148
149         return theInstance;
150     }
151
152
153     public String JavaDoc getCLOB(DBObject baseObject,
154                           String JavaDoc fieldName) throws DBException {
155         DBConnectionPool aPool = DBConnectionPool.getInstance(baseObject.getDataContext());
156         DBConnection localConnection = aPool.getConnection();
157         String JavaDoc returnValue = null;
158         try {
159             returnValue = this.getCLOB(baseObject, fieldName, localConnection);
160         } finally {
161             aPool.release(localConnection);
162         }
163         return returnValue;
164     }
165
166     /**
167      * Retrieves the CLOB data for the specified DBObject and the specified
168      * fieldName
169      *
170      * @param baseObject The dbobject containing all the table information, and
171      * key fields set.
172      * @param fieldName The name of the field to retrieve as a LOB.
173      * @param theConnection a <code>DBConnection</code> object that has already
174      * been retrieved from the <code>DBConnectionPool</code> May be null although
175      * it's not recommended for performance.
176      * @return an array of bytes that will contain the BLOB
177      * @throws DBException if there's an error executing the statement
178      */

179     public String JavaDoc getCLOB(DBObject baseObject,
180                           String JavaDoc fieldName,
181                           DBConnection theConnection) throws DBException {
182         prepSelectResultSet(baseObject, fieldName, theConnection);
183
184         if (theConnection.next()) {
185             return StringUtil.notNull(theConnection.getString(1));
186         }
187
188         return "";
189     }
190
191     /**
192      * Writes a LONG Character string to the database.
193      *
194      * @param baseObject The object that contains the metadata for this BLOB
195      * @param fieldName the name of the field that is the BLOB field
196      * @param theData the data in a <code>String</code>
197      * @param theConnection an already allocated DBConnection object Currently
198      * may <b>not</b> be null
199      * @throws DBException upon error
200      */

201     public void setCLOB(DBObject baseObject,
202                         String JavaDoc fieldName,
203                         String JavaDoc theData,
204                         DBConnection theConnection) throws DBException {
205
206         PreparedStatement JavaDoc preparedStatement = prepUpdate(baseObject, fieldName, theConnection);
207
208         try {
209             if ("interbase.interclient.Driver".equals(theConnection.getDBDriver())) {
210                 //
211
//Workaround, interclient 2 doesn't support setCharacterStream()
212
//
213
byte[] data = theData.getBytes();
214                 java.io.ByteArrayInputStream JavaDoc bis = new java.io.ByteArrayInputStream JavaDoc(data);
215                 preparedStatement.setAsciiStream(1, bis, data.length);
216             } else {
217                 java.io.Reader JavaDoc r = new java.io.StringReader JavaDoc(theData);
218                 preparedStatement.setCharacterStream(1, r, theData.length());
219             }
220         } catch (SQLException JavaDoc ex) {
221             throw new DBException("Unable to set CharacterStream to CLOB object.", ex);
222         }
223
224         finalizeUpdate(theConnection);
225     }
226
227
228     /**
229      * Returns a single stream for a LOB object. This single LOB object is
230      * not wrapped by any javax.activation code and therefore is strictly up to
231      * the code creator on how to use the Input Stream.
232      *
233      * @param baseObject The dbobject containing all the table information, and
234      * key fields set.
235      * @param fieldName The name of the field to retrieve as a LOB.
236      * @param theConnection a <code>DBConnection</code> object that has already
237      * been retrieved from the <code>DBConnectionPool</code>
238      * @return an array of bytes that will contain the BLOB
239      * @throws DBException if there's an error executing the statement
240      */

241     public byte[] getBLOB(DBObject baseObject,
242                           String JavaDoc fieldName,
243                           DBConnection theConnection) throws DBException {
244
245         prepSelectResultSet(baseObject, fieldName, theConnection);
246         if (theConnection.next()) {
247             byte retVal[] = theConnection.getBytes(1);
248             if (retVal == null) {
249                 return new byte[0];
250             } else {
251                 return retVal;
252             }
253         }
254
255         return new byte[0];
256     }
257
258     /**
259      * Updates the underlying table with a BLOB object.
260      *
261      * @param baseObject The object that contains the metadata for this BLOB
262      * @param fieldName the name of the field that is the BLOB field
263      * @param theData the data in a byte array
264      * @param theConnection an already allocated DBConnection object
265      * @throws DBException upon error
266      */

267     public void setBLOB(DBObject baseObject,
268                         String JavaDoc fieldName,
269                         byte theData[],
270                         DBConnection theConnection) throws DBException {
271
272         PreparedStatement JavaDoc preparedStatement = prepUpdate(baseObject, fieldName, theConnection);
273
274
275         try {
276             preparedStatement.setBytes(1, theData);
277         } catch (SQLException JavaDoc ex) {
278             throw new DBException("Error setting BLOB object", ex);
279         }
280
281         finalizeUpdate(theConnection);
282     }
283
284     /**
285      * Updates the underlying table with a BLOB object. Same as the byte[] method
286      * but uses InputStreams intead
287      *
288      * @param baseObject The object that contains the metadata for this BLOB
289      * @param fieldName the name of the field that is the BLOB field
290      * @param theData the data as an InputStream
291      * @param dataLength the total length to be read from the InputStream
292      * @param theConnection an already allocated DBConnection object
293      * @throws DBException upon error
294      */

295     public void setBLOB(DBObject baseObject,
296                         String JavaDoc fieldName,
297                         InputStream JavaDoc theData,
298                         int dataLength,
299                         DBConnection theConnection) throws DBException {
300         PreparedStatement JavaDoc preparedStatement = prepUpdate(baseObject, fieldName, theConnection);
301
302
303         try {
304             preparedStatement.setBinaryStream(1, theData, dataLength);
305         } catch (SQLException JavaDoc ex) {
306             throw new DBException("Error setting BLOB object", ex);
307         }
308
309
310         finalizeUpdate(theConnection);
311     }
312
313
314     /**
315      * Internal helper function that does the guts of the work
316      *
317      * @param baseObject The object that contains the metadata for this BLOB
318      * @param fieldName the name of the field that is the BLOB field
319      * @param theConnection an already allocated DBConnection object. <b>This
320      * function modifies the state of theConnection by allocating a prepared
321      * statement</b>
322      * @throws DBException upon error
323      * <p/>
324      * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
325      * @since $DatabaseSchema $Date: 2004/11/17 20:48:11 $
326      */

327     protected void prepSelectResultSet(DBObject baseObject,
328                                        String JavaDoc fieldName,
329                                        DBConnection theConnection) throws DBException {
330         FastStringBuffer prepStatement = FastStringBuffer.getInstance();
331         try {
332             prepStatement.append("SELECT ");
333             prepStatement.append(fieldName);
334             prepStatement.append(" from ");
335             prepStatement.append(baseObject.getJDBCMetaData().getTargetSQLTable(baseObject.getDataContext()));
336             String JavaDoc whereClause = baseObject.buildWhereClause(false);
337             prepStatement.append(whereClause);
338             String JavaDoc thePrepString = prepStatement.toString();
339             if (log.isDebugEnabled()) {
340                 log.debug("Preparing prepared statement: " + thePrepString);
341             }
342
343             PreparedStatement JavaDoc prep = theConnection.createPreparedStatement(thePrepString);
344             if (prep == null) {
345                 throw new DBException("Unable to create prepared statement for CLOB retrieval." +
346                         " Check DBConnection log for details");
347             }
348             theConnection.execute();
349
350             if (log.isDebugEnabled()) {
351                 log.debug("Succesfully executed prepared statement");
352             }
353         } finally {
354             prepStatement.release();
355             prepStatement = null;
356         }
357     }
358
359     /**
360      * Internal helper function to prepare a LOB update
361      *
362      * @param baseObject The object that contains the metadata for this BLOB
363      * @param fieldName the name of the field that is the BLOB field
364      * @param theConnection an already allocated DBConnection object
365      * @return a created PreparedStatement object
366      * @throws DBException Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
367      * @since $DatabaseSchema $Date: 2004/11/17 20:48:11 $
368      */

369     protected PreparedStatement JavaDoc prepUpdate(DBObject baseObject,
370                                            String JavaDoc fieldName,
371                                            DBConnection theConnection) throws DBException {
372         String JavaDoc whereClause = baseObject.buildWhereClause(false);
373
374         FastStringBuffer prepStatement = FastStringBuffer.getInstance();
375         String JavaDoc theSQL = null;
376         try {
377             prepStatement.append("UPDATE ");
378             prepStatement.append(baseObject.getJDBCMetaData().getTargetSQLTable(baseObject.getDataContext()));
379             prepStatement.append(" SET ");
380             prepStatement.append(fieldName);
381             prepStatement.append(" = ? ");
382             prepStatement.append(whereClause);
383             theSQL = prepStatement.toString();
384         } finally {
385             prepStatement.release();
386             prepStatement = null;
387         }
388
389         return theConnection.createPreparedStatement(theSQL);
390     }
391
392     protected void finalizeUpdate(DBConnection theConnection) throws DBException {
393
394         boolean alreadyInTransaction = !(theConnection.getAutoCommit());
395         boolean success = false;
396
397         //
398
//SAPDB and others REQUIRE that BLOB updates be done within a transaction. So
399
//If we aren't already within one, we enter one now. Then commit
400
//if a transaction wasn't already in process
401
//
402
if (!alreadyInTransaction && theConnection.supportsTransactions()) {
403             if (log.isDebugEnabled()) {
404                 log.debug("Turning off auto-commit");
405             }
406             theConnection.setAutoCommit(false);
407         }
408
409         try {
410             theConnection.executeUpdate(null);
411             success = true;
412         } finally {
413             if (success == false) {
414                 if (!alreadyInTransaction && theConnection.supportsTransactions()) {
415                     if (log.isDebugEnabled()) {
416                         log.debug("rolling back");
417                     }
418                     theConnection.rollback();
419                 }
420             }
421             if (!alreadyInTransaction && theConnection.supportsTransactions()) {
422                 if (log.isDebugEnabled()) {
423                     log.debug("Finishing commit and turning auto-commit back to true");
424                 }
425                 theConnection.commit();
426                 theConnection.setAutoCommit(true);
427             }
428         }
429
430     }
431
432 }
433
Popular Tags