KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > platforms > PlatformOracleImpl


1 package org.apache.ojb.broker.platforms;
2
3 /* Copyright 2002-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 org.apache.ojb.broker.util.logging.Logger;
19 import org.apache.ojb.broker.util.logging.LoggerFactory;
20 import org.apache.ojb.broker.util.ClassHelper;
21 import org.apache.ojb.broker.util.sequence.SequenceManagerHelper;
22
23 import java.io.ByteArrayInputStream JavaDoc;
24 import java.io.InputStreamReader JavaDoc;
25 import java.io.Reader JavaDoc;
26 import java.io.StringReader JavaDoc;
27 import java.lang.reflect.Field JavaDoc;
28 import java.security.AccessController JavaDoc;
29 import java.security.PrivilegedAction JavaDoc;
30 import java.sql.CallableStatement JavaDoc;
31 import java.sql.Connection JavaDoc;
32 import java.sql.DatabaseMetaData JavaDoc;
33 import java.sql.PreparedStatement JavaDoc;
34 import java.sql.SQLException JavaDoc;
35 import java.sql.Statement JavaDoc;
36 import java.sql.Types JavaDoc;
37 import java.util.Properties JavaDoc;
38
39 /**
40  * This class is a concrete implementation of <code>Platform</code>. Provides an implementation
41  * that works around some issues with Oracle in general and Oracle's Thin driver in particular.
42  *
43  * <p/>
44  * Many of the database sequence specific properties can be specified using
45  * <em>custom attributes</em> within the <em>sequence-manager</em> element.
46  * <br/>
47  * The database sequence specific properties are generally speaking, see database user guide
48  * for detailed description.
49  *
50  * <p>
51  * Implementation configuration properties:
52  * </p>
53  *
54  * <table cellspacing="2" cellpadding="2" border="3" frame="box">
55  * <tr>
56  * <td><strong>Property Key</strong></td>
57  * <td><strong>Property Values</strong></td>
58  * </tr>
59  * <tr>
60  * <td>sequenceStart</td>
61  * <td>
62  * DEPRECATED. Database sequence specific property.<br/>
63  * Specifies the first sequence number to be
64  * generated. Allowed: <em>1</em> or greater.
65  * </td>
66  * </tr>
67  * <tr>
68  * <td>seq.start</td>
69  * <td>
70  * Database sequence specific property.<br/>
71  * Specifies the first sequence number to be
72  * generated. Allowed: <em>1</em> or greater.
73  * </td>
74  * </tr>
75  * <tr>
76  * <td>seq.incrementBy</td>
77  * <td>
78  * Database sequence specific property.<br/>
79  * Specifies the interval between sequence numbers.
80  * This value can be any positive or negative
81  * integer, but it cannot be 0.
82  * </td>
83  * </tr>
84  * <tr>
85  * <td>seq.maxValue</td>
86  * <td>
87  * Database sequence specific property.<br/>
88  * Set max value for sequence numbers.
89  * </td>
90  * </tr>
91  * <tr>
92  * <td>seq.minValue</td>
93  * <td>
94  * Database sequence specific property.<br/>
95  * Set min value for sequence numbers.
96  * </td>
97  * </tr>
98  * <tr>
99  * <td>seq.cycle</td>
100  * <td>
101  * Database sequence specific property.<br/>
102  * If <em>true</em>, specifies that the sequence continues to generate
103  * values after reaching either its maximum or minimum value.
104  * <br/>
105  * If <em>false</em>, specifies that the sequence cannot generate more values after
106  * reaching its maximum or minimum value.
107  * </td>
108  * </tr>
109  * <tr>
110  * <td>seq.cache</td>
111  * <td>
112  * Database sequence specific property.<br/>
113  * Specifies how many values of the sequence Oracle
114  * preallocates and keeps in memory for faster access.
115  * Allowed values: <em>2</em> or greater. If set <em>0</em>,
116  * an explicite <em>nocache</em> expression will be set.
117  * </td>
118  * </tr>
119  * <tr>
120  * <td>seq.order</td>
121  * <td>
122  * Database sequence specific property.<br/>
123  * If set <em>true</em>, guarantees that sequence numbers
124  * are generated in order of request.
125  * <br/>
126  * If <em>false</em>, a <em>no order</em> expression will be set.
127  * </td>
128  * </tr>
129  * </table>
130  *
131  * @author <a HREF="mailto:thma@apache.org">Thomas Mahler <a>
132  * @version $Id: PlatformOracleImpl.java,v 1.20.2.4 2005/08/16 20:11:19 arminw Exp $
133  */

134
135 public class PlatformOracleImpl extends PlatformDefaultImpl
136 {
137     protected static final String JavaDoc THIN_URL_PREFIX = "jdbc:oracle:thin";
138     // Oracle:thin handles direct BLOB insert <= 4000 and update <= 2000
139
protected static final int THIN_BLOB_MAX_SIZE = 2000;
140     // Oracle:thin handles direct CLOB insert and update <= 4000
141
protected static final int THIN_CLOB_MAX_SIZE = 4000;
142
143     /**
144      * Field value of <code>oracle.jdbc.OracleTypes.CURSOR</code>.
145      * @see #initOracleReflectedVars
146      */

147     protected static int ORACLE_JDBC_TYPE_CURSOR = -10;
148
149     private Logger logger = LoggerFactory.getLogger(PlatformOracleImpl.class);
150
151     /**
152      * Default constructor.
153      */

154     public PlatformOracleImpl()
155     {
156         initOracleReflectedVars();
157     }
158
159     /**
160      * Method prepareNextValProcedureStatement implementation
161      * is simply copied over from PlatformMsSQLServerImpl class.
162      * @see org.apache.ojb.broker.platforms.Platform#prepareNextValProcedureStatement(java.sql.Connection, java.lang.String, java.lang.String)
163      */

164     public CallableStatement JavaDoc prepareNextValProcedureStatement(Connection JavaDoc con, String JavaDoc procedureName, String JavaDoc sequenceName)
165             throws PlatformException
166     {
167         try
168         {
169             String JavaDoc sp = "{?= call " + procedureName + " (?)}";
170             CallableStatement JavaDoc cs = con.prepareCall(sp);
171             cs.registerOutParameter(1, Types.INTEGER);
172             cs.setString(2, sequenceName);
173             return cs;
174         }
175         catch (SQLException JavaDoc e)
176         {
177             throw new PlatformException(e);
178         }
179     }
180
181     /**
182      * In Oracle we set escape processing explizit 'true' after a statement was created.
183      */

184     public void afterStatementCreate(Statement JavaDoc stmt) throws PlatformException
185     {
186         try
187         {
188             stmt.setEscapeProcessing(true);
189         }
190         catch (SQLException JavaDoc e)
191         {
192             throw new PlatformException("Could not set escape processing", e);
193         }
194     }
195
196     /**
197      * For objects beyond 4k, weird things happen in Oracle if you try to use "setBytes", so for
198      * all cases it's better to use setBinaryStream. Oracle also requires a change in the resultset
199      * type of the prepared statement. MBAIRD NOTE: BLOBS may not work with Oracle database/thin
200      * driver versions prior to 8.1.6.
201      *
202      * @see Platform#setObjectForStatement
203      */

204     public void setObjectForStatement(PreparedStatement JavaDoc ps, int index, Object JavaDoc value, int sqlType)
205             throws SQLException JavaDoc
206     {
207         if (((sqlType == Types.VARBINARY) || (sqlType == Types.LONGVARBINARY) || (sqlType == Types.BLOB))
208                 && (value instanceof byte[]))
209         {
210             byte buf[] = (byte[]) value;
211             int length = buf.length;
212             if (isUsingOracleThinDriver(ps.getConnection()) && length > THIN_BLOB_MAX_SIZE)
213             {
214                 throw new SQLException JavaDoc(
215                         "Oracle thin driver cannot update BLOB values with length>2000. (Consider using Oracle9i as OJB platform.)");
216             }
217             ByteArrayInputStream JavaDoc inputStream = new ByteArrayInputStream JavaDoc(buf);
218             changePreparedStatementResultSetType(ps);
219             ps.setBinaryStream(index, inputStream, length);
220         }
221         else if (value instanceof Double JavaDoc)
222         {
223             // workaround for the bug in Oracle thin driver
224
ps.setDouble(index, ((Double JavaDoc) value).doubleValue());
225         }
226         else if (sqlType == Types.BIGINT && value instanceof Integer JavaDoc)
227         {
228             // workaround: Oracle thin driver problem when expecting long
229
ps.setLong(index, ((Integer JavaDoc) value).intValue());
230         }
231         else if (sqlType == Types.INTEGER && value instanceof Long JavaDoc)
232         {
233             ps.setLong(index, ((Long JavaDoc) value).longValue());
234         }
235         else if (sqlType == Types.DATE && value instanceof String JavaDoc)
236         {
237             // special handling of like for dates (birthDate like '2000-01%')
238
ps.setString(index, (String JavaDoc) value);
239         }
240         else if (sqlType == Types.CLOB && (value instanceof String JavaDoc || value instanceof byte[]))
241         {
242             Reader JavaDoc reader;
243             int length;
244             if (value instanceof String JavaDoc)
245             {
246                 String JavaDoc stringValue = (String JavaDoc) value;
247                 length = stringValue.length();
248                 reader = new StringReader JavaDoc(stringValue);
249             }
250             else
251             {
252                 byte buf[] = (byte[]) value;
253                 ByteArrayInputStream JavaDoc inputStream = new ByteArrayInputStream JavaDoc(buf);
254                 reader = new InputStreamReader JavaDoc(inputStream);
255                 length = buf.length;
256             }
257             if (isUsingOracleThinDriver(ps.getConnection()) && length > THIN_CLOB_MAX_SIZE)
258             {
259                 throw new SQLException JavaDoc(
260                         "Oracle thin driver cannot insert CLOB values with length>4000. (Consider using Oracle9i as OJB platform.)");
261             }
262             ps.setCharacterStream(index, reader, length);
263         }
264         else if ((sqlType == Types.CHAR || sqlType == Types.VARCHAR)
265                  &&
266                  (value instanceof String JavaDoc || value instanceof Character JavaDoc))
267         {
268             if (value instanceof String JavaDoc)
269             {
270                 ps.setString(index, (String JavaDoc) value);
271             }
272             else // assert: value instanceof Character
273
{
274                 ps.setString(index, value.toString());
275             }
276         }
277         else
278         {
279             super.setObjectForStatement(ps, index, value, sqlType);
280         }
281     }
282
283     /**
284      * Attempts to modify a private member in the Oracle thin driver's resultset to allow proper
285      * setting of large binary streams.
286      */

287     protected void changePreparedStatementResultSetType(PreparedStatement JavaDoc ps)
288     {
289         try
290         {
291             final Field JavaDoc f = ps.getClass().getSuperclass().getDeclaredField("m_userRsetType");
292             AccessController.doPrivileged(new PrivilegedAction JavaDoc()
293             {
294                 public Object JavaDoc run()
295                 {
296                     f.setAccessible(true);
297                     return null;
298                 }
299             });
300             f.setInt(ps, 1);
301             f.setAccessible(false);
302         }
303         catch (Exception JavaDoc e)
304         {
305             logger.info("Not using classes12.zip.");
306         }
307     }
308
309     /**
310      * Get join syntax type for this RDBMS - one on of the constants from JoinSyntaxType interface
311      */

312     public byte getJoinSyntaxType()
313     {
314         return ORACLE_JOIN_SYNTAX;
315     }
316
317     public String JavaDoc createSequenceQuery(String JavaDoc sequenceName)
318     {
319         return "CREATE SEQUENCE " + sequenceName;
320     }
321
322     public String JavaDoc createSequenceQuery(String JavaDoc sequenceName, Properties JavaDoc prop)
323     {
324         /*
325         CREATE SEQUENCE [schema.]sequence
326             [INCREMENT BY integer]
327             [START WITH integer]
328             [MAXVALUE integer | NOMAXVALUE]
329             [MINVALUE integer | NOMINVALUE]
330             [CYCLE | NOCYCLE]
331             [CACHE integer | NOCACHE]
332             [ORDER | NOORDER]
333         */

334         StringBuffer JavaDoc query = new StringBuffer JavaDoc(createSequenceQuery(sequenceName));
335         if(prop != null)
336         {
337             Boolean JavaDoc b;
338             Long JavaDoc value;
339
340             value = SequenceManagerHelper.getSeqIncrementBy(prop);
341             if(value != null)
342             {
343                 query.append(" INCREMENT BY ").append(value.longValue());
344             }
345
346             value = SequenceManagerHelper.getSeqStart(prop);
347             if(value != null)
348             {
349                 query.append(" START WITH ").append(value.longValue());
350             }
351
352             value = SequenceManagerHelper.getSeqMaxValue(prop);
353             if(value != null)
354             {
355                 query.append(" MAXVALUE ").append(value.longValue());
356             }
357
358             value = SequenceManagerHelper.getSeqMinValue(prop);
359             if(value != null)
360             {
361                 query.append(" MINVALUE ").append(value.longValue());
362             }
363
364             b = SequenceManagerHelper.getSeqCycleValue(prop);
365             if(b != null)
366             {
367                 if(b.booleanValue()) query.append(" CYCLE");
368                 else query.append(" NOCYCLE");
369             }
370
371             value = SequenceManagerHelper.getSeqCacheValue(prop);
372             if(value != null)
373             {
374                 query.append(" CACHE ").append(value.longValue());
375             }
376
377             b = SequenceManagerHelper.getSeqOrderValue(prop);
378             if(b != null)
379             {
380                 if(b.booleanValue()) query.append(" ORDER");
381                 else query.append(" NOORDER");
382             }
383         }
384         return query.toString();
385     }
386
387     public String JavaDoc nextSequenceQuery(String JavaDoc sequenceName)
388     {
389         return "select " + sequenceName + ".nextval from dual";
390     }
391
392     public String JavaDoc dropSequenceQuery(String JavaDoc sequenceName)
393     {
394         return "drop sequence " + sequenceName;
395     }
396
397     /**
398      * @see org.apache.ojb.broker.platforms.Platform#registerOutResultSet(java.sql.CallableStatement, int)
399      */

400     public void registerOutResultSet(CallableStatement JavaDoc stmt, int position)
401             throws SQLException JavaDoc
402     {
403         stmt.registerOutParameter(position, ORACLE_JDBC_TYPE_CURSOR);
404     }
405
406     /**
407      * Checks if the supplied connection is using the Oracle thin driver.
408      *
409      * @param conn database connection for which to check JDBC-driver
410      * @return <code>true</code> if the connection is using Oracle thin driver, <code>false</code>
411      * otherwise.
412      */

413     protected static boolean isUsingOracleThinDriver(Connection JavaDoc conn)
414     {
415         if (conn == null)
416         {
417             return false;
418         }
419         final DatabaseMetaData JavaDoc dbMetaData;
420         final String JavaDoc dbUrl;
421         try
422         {
423             dbMetaData = conn.getMetaData();
424             dbUrl = dbMetaData.getURL();
425             if (dbUrl != null && dbUrl.startsWith(THIN_URL_PREFIX))
426             {
427                 return true;
428             }
429         }
430         catch (Exception JavaDoc e)
431         {
432             // ignore it
433
}
434         return false;
435     }
436
437     /**
438      * Initializes static variables needed for getting Oracle-specific JDBC types.
439      */

440     protected void initOracleReflectedVars()
441     {
442         try
443         {
444             // Check for Oracle-specific Types class
445
final Class JavaDoc oracleTypes = ClassHelper.getClass("oracle.jdbc.OracleTypes", false);
446             final Field JavaDoc cursorField = oracleTypes.getField("CURSOR");
447             ORACLE_JDBC_TYPE_CURSOR = cursorField.getInt(null);
448         }
449         catch (ClassNotFoundException JavaDoc e)
450         {
451             log.warn("PlatformOracleImpl could not find Oracle JDBC classes");
452         }
453         catch (NoSuchFieldException JavaDoc e)
454         {
455             log.warn("PlatformOracleImpl could not find Oracle JDBC type fields");
456         }
457         catch (IllegalAccessException JavaDoc e)
458         {
459             log.warn("PlatformOracleImpl could not get Oracle JDBC type values");
460         }
461     }
462
463 }
464
Popular Tags