001: package org.apache.ojb.broker.platforms;
002:
003: /* Copyright 2004-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.io.ByteArrayInputStream;
019: import java.lang.reflect.Method;
020: import java.sql.Connection;
021: import java.sql.PreparedStatement;
022: import java.sql.SQLException;
023: import java.sql.Types;
024: import java.util.Collections;
025: import java.util.Map;
026: import java.util.WeakHashMap;
027:
028: import org.apache.ojb.broker.metadata.JdbcType;
029: import org.apache.ojb.broker.util.ClassHelper;
030: import org.apache.ojb.broker.metadata.JdbcTypesHelper;
031:
032: /**
033: * This class is a concrete implementation of <code>Platform</code>. Provides
034: * an implementation that works around some issues with Oracle running within WebLogic. As
035: * WebLogic wraps the Oracle physical connection with its own logical connection it is necessary to
036: * retrieve the underlying physical connection before creating a CLOB or BLOB.
037: *
038: * NOTE : When you use the physical connection WebLogic by default marks it as "infected" and discards it when
039: * the logicical connection is closed. You can change this behavior by setting the
040: * RemoveInfectedConnectionsEnabled attribute on a connection pool.
041: * see http://e-docs.bea.com/wls/docs81/jdbc/thirdparty.html#1043646
042: *
043: * Optimization: Oracle Batching (not standard JDBC batching)
044: * see http://technet.oracle.com/products/oracle9i/daily/jun07.html
045: *
046: * Optimization: Oracle Prefetching
047: * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html
048: *
049: * TODO: Optimization: use ROWNUM to minimize the effects of not having server side cursors
050: * see http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
051: *
052: * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
053: * @author <a href="mailto:erik@cj.com">Erik Forkalsrud</a>
054: * @author <a href="mailto:martin.kalen@curalia.se">Martin Kalén</a>
055: * @author <a href="mailto:d441-6iq2@spamex.com">Dougall Squair</a>
056: * @version CVS $Id: PlatformWLOracle9iImpl.java,v 1.2.2.3 2005/12/21 22:26:40 tomdz Exp $
057: * @see Platform
058: * @see PlatformDefaultImpl
059: * @see PlatformOracleImpl
060: * @see PlatformOracle9iImpl
061: *
062: * @deprecated since OJB 1.0.2 the default {@link PlatformOracle9iImpl} should be usable in WebLogic
063: */
064: public class PlatformWLOracle9iImpl extends PlatformOracleImpl {
065: protected static final int ROW_PREFETCH_SIZE = 100;
066:
067: // From Oracle9i JDBC Developer's Guide and Reference:
068: // "Batch values between 5 and 30 tend to be the most effective."
069: protected static final int STATEMENTS_PER_BATCH = 20;
070: protected static Map m_batchStatementsInProgress = Collections
071: .synchronizedMap(new WeakHashMap(STATEMENTS_PER_BATCH));
072:
073: protected static final Class[] PARAM_TYPE_INTEGER = { Integer.TYPE };
074: protected static final Class[] PARAM_TYPE_BOOLEAN = { Boolean.TYPE };
075: protected static final Class[] PARAM_TYPE_STRING = { String.class };
076:
077: protected static final Object[] PARAM_ROW_PREFETCH_SIZE = new Object[] { new Integer(
078: ROW_PREFETCH_SIZE) };
079: protected static final Object[] PARAM_STATEMENT_BATCH_SIZE = new Object[] { new Integer(
080: STATEMENTS_PER_BATCH) };
081: protected static final Object[] PARAM_BOOLEAN_TRUE = new Object[] { Boolean.TRUE };
082:
083: protected static final JdbcType BASE_CLOB = JdbcTypesHelper
084: .getJdbcTypeByName("clob");
085: protected static final JdbcType BASE_BLOB = JdbcTypesHelper
086: .getJdbcTypeByName("blob");
087:
088: /**
089: * Enables Oracle row prefetching if supported.
090: * See http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html.
091: * This is RDBMS server-to-client prefetching and thus one layer below
092: * the OJB-internal prefetching-to-cache introduced in version 1.0rc5.
093: * @param stmt the statement just created
094: * @throws PlatformException upon JDBC failure
095: */
096: public void afterStatementCreate(java.sql.Statement stmt)
097: throws PlatformException {
098: super .afterStatementCreate(stmt);
099:
100: // Check for OracleStatement-specific row prefetching support
101: final Method methodSetRowPrefetch;
102: methodSetRowPrefetch = ClassHelper.getMethod(stmt,
103: "setRowPrefetch", PARAM_TYPE_INTEGER);
104:
105: final boolean rowPrefetchingSupported = methodSetRowPrefetch != null;
106: if (rowPrefetchingSupported) {
107: try {
108: // Set number of prefetched rows
109: methodSetRowPrefetch.invoke(stmt,
110: PARAM_ROW_PREFETCH_SIZE);
111: } catch (Exception e) {
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)
125: throws PlatformException {
126: // Check for Oracle batching support
127: final Method methodSetExecuteBatch;
128: final Method methodSendBatch;
129: methodSetExecuteBatch = ClassHelper.getMethod(stmt,
130: "setExecuteBatch", PARAM_TYPE_INTEGER);
131: methodSendBatch = ClassHelper
132: .getMethod(stmt, "sendBatch", null);
133:
134: final boolean statementBatchingSupported = methodSetExecuteBatch != null
135: && methodSendBatch != null;
136: if (statementBatchingSupported) {
137: try {
138: // Set number of statements per batch
139: methodSetExecuteBatch.invoke(stmt,
140: PARAM_STATEMENT_BATCH_SIZE);
141: m_batchStatementsInProgress.put(stmt, methodSendBatch);
142: } catch (Exception e) {
143: throw new PlatformException(e.getLocalizedMessage(), e);
144: }
145: } else {
146: super .beforeBatch(stmt);
147: }
148: }
149:
150: /**
151: * Try Oracle update batching and call executeUpdate or revert to
152: * JDBC update batching.
153: * @param stmt the statement beeing added to the batch
154: * @throws PlatformException upon JDBC failure
155: */
156: public void addBatch(PreparedStatement stmt)
157: throws PlatformException {
158: // Check for Oracle batching support
159: final boolean statementBatchingSupported = m_batchStatementsInProgress
160: .containsKey(stmt);
161: if (statementBatchingSupported) {
162: try {
163: stmt.executeUpdate();
164: } catch (SQLException e) {
165: throw new PlatformException(e.getLocalizedMessage(), e);
166: }
167: } else {
168: super .addBatch(stmt);
169: }
170: }
171:
172: /**
173: * Try Oracle update batching and call sendBatch or revert to
174: * JDBC update batching.
175: * @param stmt the batched prepared statement about to be executed
176: * @return always <code>null</code> if Oracle update batching is used,
177: * since it is impossible to dissolve total row count into distinct
178: * statement counts. If JDBC update batching is used, an int array is
179: * returned containing number of updated rows for each batched statement.
180: * @throws PlatformException upon JDBC failure
181: */
182: public int[] executeBatch(PreparedStatement stmt)
183: throws PlatformException {
184: // Check for Oracle batching support
185: final Method methodSendBatch = (Method) m_batchStatementsInProgress
186: .remove(stmt);
187: final boolean statementBatchingSupported = methodSendBatch != null;
188:
189: int[] retval = null;
190: if (statementBatchingSupported) {
191: try {
192: // sendBatch() returns total row count as an Integer
193: methodSendBatch.invoke(stmt, null);
194: } catch (Exception e) {
195: throw new PlatformException(e.getLocalizedMessage(), e);
196: }
197: } else {
198: retval = super .executeBatch(stmt);
199: }
200: return retval;
201: }
202:
203: /** @see Platform#setObjectForStatement */
204: public void setObjectForStatement(PreparedStatement ps, int index,
205: Object value, int sqlType) throws SQLException {
206: boolean blobHandlingSupported = false;
207: boolean clobHandlingSupported = false;
208: Method methodSetBlob = null;
209: Method methodSetClob = null;
210: Method methodGetVendorConnection = null;
211:
212: // Check for Oracle JDBC-driver LOB-support
213: if (sqlType == Types.CLOB) {
214: try {
215: Class clobClass = ClassHelper.getClass(
216: "oracle.sql.CLOB", false);
217: methodSetClob = ClassHelper.getMethod(ps, "setCLOB",
218: new Class[] { Integer.TYPE, clobClass });
219: methodGetVendorConnection = ClassHelper.getMethod(ps
220: .getConnection(), "getVendorConnection",
221: new Class[] {});
222: clobHandlingSupported = methodSetClob != null
223: && methodGetVendorConnection != null;
224: } catch (Exception ignore) {
225: // ignore it
226: }
227: } else if (sqlType == Types.BLOB) {
228: try {
229: Class blobClass = ClassHelper.getClass(
230: "oracle.sql.BLOB", false);
231: methodSetBlob = ClassHelper.getMethod(ps, "setBLOB",
232: new Class[] { Integer.TYPE, blobClass });
233: methodGetVendorConnection = ClassHelper.getMethod(ps
234: .getConnection(), "getVendorConnection",
235: new Class[] {});
236: blobHandlingSupported = methodSetBlob != null
237: && methodGetVendorConnection != null;
238: } catch (Exception ignore) {
239: // ignore it
240: }
241: }
242:
243: // Type-specific Oracle conversions
244: if (((sqlType == Types.VARBINARY) || (sqlType == Types.LONGVARBINARY))
245: && (value instanceof byte[])) {
246: byte buf[] = (byte[]) value;
247: ByteArrayInputStream inputStream = new ByteArrayInputStream(
248: buf);
249: super .changePreparedStatementResultSetType(ps);
250: ps.setBinaryStream(index, inputStream, buf.length);
251: } else if (value instanceof Double) {
252: // workaround for the bug in Oracle thin driver
253: ps.setDouble(index, ((Double) value).doubleValue());
254: } else if (sqlType == Types.BIGINT && value instanceof Integer) {
255: // workaround: Oracle thin driver problem when expecting long
256: ps.setLong(index, ((Integer) value).intValue());
257: } else if (sqlType == Types.INTEGER && value instanceof Long) {
258: ps.setLong(index, ((Long) value).longValue());
259: } else if (sqlType == Types.CLOB && clobHandlingSupported
260: && value instanceof String) {
261: // TODO: If using Oracle update batching with the thin driver, throw exception on 4k limit
262: try {
263: Connection vendorConnection = (Connection) methodGetVendorConnection
264: .invoke(ps.getConnection(), new Object[] {});
265: Object clob = Oracle9iLobHandler.createCLOBFromString(
266: vendorConnection, (String) value);
267: methodSetClob.invoke(ps, new Object[] {
268: new Integer(index), clob });
269: } catch (Exception e) {
270: throw new SQLException(e.getLocalizedMessage());
271: }
272: } else if (sqlType == Types.BLOB && blobHandlingSupported
273: && value instanceof byte[]) {
274: // TODO: If using Oracle update batching with the thin driver, throw exception on 2k limit
275: try {
276: Connection vendorConnection = (Connection) methodGetVendorConnection
277: .invoke(ps.getConnection(), new Object[] {});
278: Object blob = Oracle9iLobHandler
279: .createBLOBFromByteArray(vendorConnection,
280: (byte[]) value);
281: methodSetBlob.invoke(ps, new Object[] {
282: new Integer(index), blob });
283: } catch (Exception e) {
284: throw new SQLException(e.getLocalizedMessage());
285: }
286: } else {
287: // Fall-through to superclass
288: super.setObjectForStatement(ps, index, value, sqlType);
289: }
290: }
291:
292: }
|