Coverage Report - org.apache.ojb.broker.platforms.PlatformWLOracle9iImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
PlatformWLOracle9iImpl
N/A
N/A
8.4
 
 1  
 package org.apache.ojb.broker.platforms;
 2  
 
 3  
 /* Copyright 2004-2005 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 import java.io.ByteArrayInputStream;
 19  
 import java.lang.reflect.Method;
 20  
 import java.sql.Connection;
 21  
 import java.sql.PreparedStatement;
 22  
 import java.sql.SQLException;
 23  
 import java.sql.Types;
 24  
 import java.util.Collections;
 25  
 import java.util.Map;
 26  
 import java.util.WeakHashMap;
 27  
 
 28  
 import org.apache.ojb.broker.metadata.JdbcType;
 29  
 import org.apache.ojb.broker.util.ClassHelper;
 30  
 import org.apache.ojb.broker.metadata.JdbcTypesHelper;
 31  
 
 32  
 /**
 33  
  * This class is a concrete implementation of <code>Platform</code>. Provides
 34  
  * an implementation that works around some issues with Oracle running within WebLogic. As
 35  
  * WebLogic wraps the Oracle physical connection with its own logical connection it is necessary to
 36  
  * retrieve the underlying physical connection before creating a CLOB or BLOB.
 37  
  *
 38  
  * NOTE : When you use the physical connection WebLogic by default marks it as "infected" and discards it when
 39  
  * the logicical connection is closed.  You can change this behavior by setting the
 40  
  * RemoveInfectedConnectionsEnabled attribute on a connection pool.
 41  
  * see http://e-docs.bea.com/wls/docs81/jdbc/thirdparty.html#1043646
 42  
  *
 43  
  * Optimization: Oracle Batching (not standard JDBC batching)
 44  
  * see http://technet.oracle.com/products/oracle9i/daily/jun07.html
 45  
  *
 46  
  * Optimization: Oracle Prefetching
 47  
  * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html
 48  
  *
 49  
  * TODO: Optimization: use ROWNUM to minimize the effects of not having server side cursors
 50  
  * see http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
 51  
  *
 52  
  * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
 53  
  * @author <a href="mailto:erik@cj.com">Erik Forkalsrud</a>
 54  
  * @author <a href="mailto:martin.kalen@curalia.se">Martin Kal&eacute;n</a>
 55  
  * @author <a href="mailto:d441-6iq2@spamex.com">Dougall Squair</a>
 56  
  * @version CVS $Id: PlatformWLOracle9iImpl.java,v 1.1 2007-08-24 22:17:35 ewestfal Exp $
 57  
  * @see Platform
 58  
  * @see PlatformDefaultImpl
 59  
  * @see PlatformOracleImpl
 60  
  * @see PlatformOracle9iImpl
 61  
  *
 62  
  * @deprecated since OJB 1.0.2 the default {@link PlatformOracle9iImpl} should be usable in WebLogic
 63  
  */
 64  
 public class PlatformWLOracle9iImpl extends PlatformOracleImpl
 65  
 {
 66  
     protected static final int ROW_PREFETCH_SIZE = 100;
 67  
 
 68  
     // From Oracle9i JDBC Developer's Guide and Reference:
 69  
     // "Batch values between 5 and 30 tend to be the most effective."
 70  
     protected static final int STATEMENTS_PER_BATCH = 20;
 71  
     protected static Map m_batchStatementsInProgress = Collections.synchronizedMap(new WeakHashMap(STATEMENTS_PER_BATCH));
 72  
 
 73  
     protected static final Class[] PARAM_TYPE_INTEGER = {Integer.TYPE};
 74  
     protected static final Class[] PARAM_TYPE_BOOLEAN = {Boolean.TYPE};
 75  
     protected static final Class[] PARAM_TYPE_STRING = {String.class};
 76  
 
 77  
     protected static final Object[] PARAM_ROW_PREFETCH_SIZE = new Object[]{new Integer(ROW_PREFETCH_SIZE)};
 78  
     protected static final Object[] PARAM_STATEMENT_BATCH_SIZE = new Object[]{new Integer(STATEMENTS_PER_BATCH)};
 79  
     protected static final Object[] PARAM_BOOLEAN_TRUE = new Object[]{Boolean.TRUE};
 80  
 
 81  
     protected static final JdbcType BASE_CLOB = JdbcTypesHelper.getJdbcTypeByName("clob");
 82  
     protected static final JdbcType BASE_BLOB = JdbcTypesHelper.getJdbcTypeByName("blob");
 83  
 
 84  
 
 85  
 
 86  
     /**
 87  
      * Enables Oracle row prefetching if supported.
 88  
      * See http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html.
 89  
      * This is RDBMS server-to-client prefetching and thus one layer below
 90  
      * the OJB-internal prefetching-to-cache introduced in version 1.0rc5.
 91  
      * @param stmt the statement just created
 92  
      * @throws PlatformException upon JDBC failure
 93  
      */
 94  
     public void afterStatementCreate(java.sql.Statement stmt) throws PlatformException
 95  
     {
 96  
         super.afterStatementCreate(stmt);
 97  
 
 98  
         // Check for OracleStatement-specific row prefetching support
 99  
         final Method methodSetRowPrefetch;
 100  
         methodSetRowPrefetch = ClassHelper.getMethod(stmt, "setRowPrefetch", PARAM_TYPE_INTEGER);
 101  
 
 102  
         final boolean rowPrefetchingSupported = methodSetRowPrefetch != null;
 103  
         if (rowPrefetchingSupported)
 104  
         {
 105  
             try
 106  
             {
 107  
                 // Set number of prefetched rows
 108  
                 methodSetRowPrefetch.invoke(stmt, PARAM_ROW_PREFETCH_SIZE);
 109  
             }
 110  
             catch (Exception e)
 111  
             {
 112  
                 throw new PlatformException(e.getLocalizedMessage(), e);
 113  
             }
 114  
         }
 115  
     }
 116  
 
 117  
     /**
 118  
      * Try Oracle update batching and call setExecuteBatch or revert to
 119  
      * JDBC update batching. See 12-2 Update Batching in the Oracle9i
 120  
      * JDBC Developer's Guide and Reference.
 121  
      * @param stmt the prepared statement to be used for batching
 122  
      * @throws PlatformException upon JDBC failure
 123  
      */
 124  
     public void beforeBatch(PreparedStatement stmt) throws PlatformException
 125  
     {
 126  
         // Check for Oracle batching support
 127  
         final Method methodSetExecuteBatch;
 128  
         final Method methodSendBatch;
 129  
         methodSetExecuteBatch = ClassHelper.getMethod(stmt, "setExecuteBatch", PARAM_TYPE_INTEGER);
 130  
         methodSendBatch = ClassHelper.getMethod(stmt, "sendBatch", null);
 131  
 
 132  
         final boolean statementBatchingSupported = methodSetExecuteBatch != null && methodSendBatch != null;
 133  
         if (statementBatchingSupported)
 134  
         {
 135  
             try
 136  
             {
 137  
                 // Set number of statements per batch
 138  
                 methodSetExecuteBatch.invoke(stmt, PARAM_STATEMENT_BATCH_SIZE);
 139  
                 m_batchStatementsInProgress.put(stmt, methodSendBatch);
 140  
             }
 141  
             catch (Exception e)
 142  
             {
 143  
                 throw new PlatformException(e.getLocalizedMessage(), e);
 144  
             }
 145  
         }
 146  
         else
 147  
         {
 148  
             super.beforeBatch(stmt);
 149  
         }
 150  
     }
 151  
 
 152  
     /**
 153  
      * Try Oracle update batching and call executeUpdate or revert to
 154  
      * JDBC update batching.
 155  
      * @param stmt the statement beeing added to the batch
 156  
      * @throws PlatformException upon JDBC failure
 157  
      */
 158  
     public void addBatch(PreparedStatement stmt) throws PlatformException
 159  
     {
 160  
         // Check for Oracle batching support
 161  
         final boolean statementBatchingSupported = m_batchStatementsInProgress.containsKey(stmt);
 162  
         if (statementBatchingSupported)
 163  
         {
 164  
             try
 165  
             {
 166  
                 stmt.executeUpdate();
 167  
             }
 168  
             catch (SQLException e)
 169  
             {
 170  
                 throw new PlatformException(e.getLocalizedMessage(), e);
 171  
             }
 172  
         }
 173  
         else
 174  
         {
 175  
             super.addBatch(stmt);
 176  
         }
 177  
     }
 178  
 
 179  
     /**
 180  
      * Try Oracle update batching and call sendBatch or revert to
 181  
      * JDBC update batching.
 182  
      * @param stmt the batched prepared statement about to be executed
 183  
      * @return always <code>null</code> if Oracle update batching is used,
 184  
      * since it is impossible to dissolve total row count into distinct
 185  
      * statement counts. If JDBC update batching is used, an int array is
 186  
      * returned containing number of updated rows for each batched statement.
 187  
      * @throws PlatformException upon JDBC failure
 188  
      */
 189  
     public int[] executeBatch(PreparedStatement stmt) throws PlatformException
 190  
     {
 191  
         // Check for Oracle batching support
 192  
         final Method methodSendBatch = (Method) m_batchStatementsInProgress.remove(stmt);
 193  
         final boolean statementBatchingSupported = methodSendBatch != null;
 194  
 
 195  
         int[] retval = null;
 196  
         if (statementBatchingSupported)
 197  
         {
 198  
             try
 199  
             {
 200  
                 // sendBatch() returns total row count as an Integer
 201  
                 methodSendBatch.invoke(stmt, null);
 202  
             }
 203  
             catch (Exception e)
 204  
             {
 205  
                 throw new PlatformException(e.getLocalizedMessage(), e);
 206  
             }
 207  
         }
 208  
         else
 209  
         {
 210  
             retval = super.executeBatch(stmt);
 211  
         }
 212  
         return retval;
 213  
     }
 214  
 
 215  
     /** @see Platform#setObjectForStatement */
 216  
     public void setObjectForStatement(PreparedStatement ps, int index, Object value, int sqlType) throws SQLException
 217  
     {
 218  
         boolean blobHandlingSupported = false;
 219  
         boolean clobHandlingSupported = false;
 220  
         Method methodSetBlob = null;
 221  
         Method methodSetClob = null;
 222  
         Method methodGetVendorConnection = null;
 223  
 
 224  
         // Check for Oracle JDBC-driver LOB-support
 225  
         if (sqlType == Types.CLOB)
 226  
         {
 227  
             try
 228  
             {
 229  
                 Class clobClass = ClassHelper.getClass("oracle.sql.CLOB", false);
 230  
                 methodSetClob = ClassHelper.getMethod(ps, "setCLOB", new Class[]{Integer.TYPE, clobClass});
 231  
                 methodGetVendorConnection = ClassHelper.getMethod(ps.getConnection(), "getVendorConnection",
 232  
                         new Class[]{});
 233  
                 clobHandlingSupported = methodSetClob != null && methodGetVendorConnection != null;
 234  
             }
 235  
             catch (Exception ignore)
 236  
             {
 237  
                 // ignore it
 238  
             }
 239  
         }
 240  
         else if (sqlType == Types.BLOB)
 241  
         {
 242  
             try
 243  
             {
 244  
                 Class blobClass = ClassHelper.getClass("oracle.sql.BLOB", false);
 245  
                 methodSetBlob = ClassHelper.getMethod(ps, "setBLOB", new Class[]{Integer.TYPE, blobClass});
 246  
                 methodGetVendorConnection = ClassHelper.getMethod(ps.getConnection(), "getVendorConnection",
 247  
                         new Class[]{});
 248  
                 blobHandlingSupported = methodSetBlob != null && methodGetVendorConnection != null;
 249  
             }
 250  
             catch (Exception ignore)
 251  
             {
 252  
                 // ignore it
 253  
             }
 254  
         }
 255  
 
 256  
         // Type-specific Oracle conversions
 257  
         if (((sqlType == Types.VARBINARY) || (sqlType == Types.LONGVARBINARY)) && (value instanceof byte[]))
 258  
         {
 259  
             byte buf[] = (byte[]) value;
 260  
             ByteArrayInputStream inputStream = new ByteArrayInputStream(buf);
 261  
             super.changePreparedStatementResultSetType(ps);
 262  
             ps.setBinaryStream(index, inputStream, buf.length);
 263  
         }
 264  
         else if (value instanceof Double)
 265  
         {
 266  
             // workaround for the bug in Oracle thin driver
 267  
             ps.setDouble(index, ((Double) value).doubleValue());
 268  
         }
 269  
         else if (sqlType == Types.BIGINT && value instanceof Integer)
 270  
         {
 271  
             // workaround: Oracle thin driver problem when expecting long
 272  
             ps.setLong(index, ((Integer) value).intValue());
 273  
         }
 274  
         else if (sqlType == Types.INTEGER && value instanceof Long)
 275  
         {
 276  
             ps.setLong(index, ((Long) value).longValue());
 277  
         }
 278  
         else if (sqlType == Types.CLOB && clobHandlingSupported && value instanceof String)
 279  
         {
 280  
             // TODO: If using Oracle update batching with the thin driver, throw exception on 4k limit
 281  
             try
 282  
             {
 283  
                 Connection vendorConnection = (Connection) methodGetVendorConnection.invoke(ps.getConnection(),
 284  
                         new Object[]{});
 285  
                 Object clob = Oracle9iLobHandler.createCLOBFromString(vendorConnection, (String) value);
 286  
                 methodSetClob.invoke(ps, new Object[]{new Integer(index), clob});
 287  
             }
 288  
             catch (Exception e)
 289  
             {
 290  
                 throw new SQLException(e.getLocalizedMessage());
 291  
             }
 292  
         }
 293  
         else if (sqlType == Types.BLOB && blobHandlingSupported && value instanceof byte[])
 294  
         {
 295  
             // TODO: If using Oracle update batching with the thin driver, throw exception on 2k limit
 296  
             try
 297  
             {
 298  
                 Connection vendorConnection = (Connection) methodGetVendorConnection.invoke(ps.getConnection(),
 299  
                         new Object[]{});
 300  
                 Object blob = Oracle9iLobHandler.createBLOBFromByteArray(vendorConnection, (byte[]) value);
 301  
                 methodSetBlob.invoke(ps, new Object[]{new Integer(index), blob});
 302  
             }
 303  
             catch (Exception e)
 304  
             {
 305  
                 throw new SQLException(e.getLocalizedMessage());
 306  
             }
 307  
         }
 308  
         else
 309  
         {
 310  
             // Fall-through to superclass
 311  
             super.setObjectForStatement(ps, index, value, sqlType);
 312  
         }
 313  
     }
 314  
 
 315  
 }