KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > jdbc > MPreparedStatement


1 /**
2  * com.mckoi.database.jdbc.MPreparedStatement 22 Jul 2000
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database.jdbc;
26
27 import java.sql.*;
28 import java.io.*;
29 import java.util.Calendar JavaDoc;
30 import java.math.BigDecimal JavaDoc;
31 import com.mckoi.database.global.ByteLongObject;
32 import com.mckoi.database.global.ObjectTranslator;
33 import com.mckoi.database.global.CastHelper;
34 import com.mckoi.database.global.StreamableObject;
35 import com.mckoi.database.global.StringObject;
36 import com.mckoi.util.BigNumber;
37
38 /**
39  * An implementation of a JDBC prepared statement.
40  *
41  * Multi-threaded issue: This class is not designed to be multi-thread
42  * safe. A PreparedStatement should not be accessed by concurrent threads.
43  *
44  * @author Tobias Downer
45  */

46
47 class MPreparedStatement extends MStatement implements PreparedStatement {
48
49   /**
50    * The SQLQuery object constructed for this statement.
51    */

52   private SQLQuery statement;
53
54
55   /**
56    * Constructs the PreparedStatement.
57    */

58   MPreparedStatement(MConnection connection, String JavaDoc sql) {
59     super(connection);
60     statement = new SQLQuery(sql);
61   }
62
63   // ---------- Utility ----------
64

65   /**
66    * Converts the given Object to the given SQL type object.
67    */

68   Object JavaDoc convertToType(Object JavaDoc ob, int sqlType) throws SQLException {
69     // We use CastHelper to convert to the given SQL type.
70
return CastHelper.castObjectToSQLType(ob, sqlType, -1, -1,
71                                           "requested type");
72   }
73
74   /**
75    * Converts a Java Object using the JDBC type conversion rules. For example,
76    * java.lang.Double is converted to a NUMERIC type
77    * (com.mckoi.util.BigNumber).
78    */

79   Object JavaDoc castToMckoiObject(Object JavaDoc ob) throws SQLException {
80     if (ob == null) {
81       return ob;
82     }
83     if (ob instanceof String JavaDoc) {
84       return StringObject.fromString((String JavaDoc) ob);
85     }
86     if (ob instanceof Boolean JavaDoc) {
87       return ob;
88     }
89     if (ob instanceof Number JavaDoc) {
90       Number JavaDoc n = (Number JavaDoc) ob;
91       if (ob instanceof Byte JavaDoc ||
92           ob instanceof Short JavaDoc ||
93           ob instanceof Integer JavaDoc) {
94         return BigNumber.fromInt(n.intValue());
95       }
96       else if (ob instanceof Long JavaDoc) {
97         return BigNumber.fromLong(n.longValue());
98       }
99       else if (ob instanceof Float JavaDoc) {
100         return BigNumber.fromFloat(n.floatValue());
101       }
102       else if (ob instanceof Double JavaDoc) {
103         return BigNumber.fromDouble(n.doubleValue());
104       }
105       else {
106         return BigNumber.fromString(n.toString());
107       }
108     }
109     if (ob instanceof byte[]) {
110       return new ByteLongObject((byte[]) ob);
111     }
112     try {
113       return ObjectTranslator.translate(ob);
114     }
115     catch (Throwable JavaDoc e) {
116       // Hacky - we need for ObjectTranslator to throw a better exception
117
throw new SQLException(e.getMessage());
118     }
119   }
120
121   /**
122    * Given an InputStream and a length of bytes to read from the stream, this
123    * method will insert a correct type of parameter into the statement to handle
124    * this size of object. If the object is too large it will mark it as a
125    * streamable object.
126    *
127    * @param parameterIndex 1 for first parameter, 2 for second, etc.
128    * @param x the input stream containing the binary data.
129    * @param length the number of bytes to read.
130    * @param type 2 = binary, 3 = 1 byte char, 4 = 2 byte unicode.
131    */

132   private void setVariableLengthStream(int parameterIndex,
133                     InputStream x, int length, byte type) throws IOException {
134     // If we are going to transfer more than 8K bytes then the object becomes
135
// a streamable object
136
if (length > 8 * 1024) {
137       int p_ind = parameterIndex - 1;
138       // Generate a new StreamableObject and for this InputStream and store it
139
// in the hold.
140
StreamableObject s_object = createStreamableObject(x, length, type);
141       // Put the streamable object in the statement variable list.
142
statement.setVar(p_ind, s_object);
143     }
144     else {
145       // If binary stream,
146
if (type == 2) {
147         // Less than 8K bytes so we transfer the object as a standard
148
// ByteLongObject.
149
ByteLongObject b = new ByteLongObject(x, length);
150         statement.setVar(parameterIndex - 1, b);
151       }
152       // If ascii stream
153
else if (type == 3) {
154         // Convert to a String
155
StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
156         for (int i = 0; i < length; ++i) {
157           int v = x.read();
158           if (v == -1) {
159             throw new IOException("Premature EOF reached.");
160           }
161           buf.append((char) v);
162         }
163         statement.setVar(parameterIndex - 1,
164                          StringObject.fromString(new String JavaDoc(buf)));
165       }
166       // If unicode stream
167
else if (type == 4) {
168         // Convert to a String
169
StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
170         int half_len = length / 2;
171         for (int i = 0; i < half_len; ++i) {
172           int v1 = x.read();
173           int v2 = x.read();
174           if (v1 == -1 || v2 == -1) {
175             throw new IOException("Premature EOF reached.");
176           }
177           buf.append((char) ((v1 << 8) + v2));
178         }
179         statement.setVar(parameterIndex - 1,
180                          StringObject.fromString(new String JavaDoc(buf)));
181       }
182       else {
183         throw new RuntimeException JavaDoc("Do not understand type.");
184       }
185     }
186   }
187   
188   // ---------- Overridden from MStatement ----------
189

190   public void close() throws SQLException {
191     super.close();
192     statement = null;
193   }
194   
195   
196   // ---------- Implemented from PreparedStatement ----------
197

198   public ResultSet executeQuery() throws SQLException {
199     return executeQuery(statement);
200   }
201
202   public int executeUpdate() throws SQLException {
203     MResultSet result_set = executeQuery(statement);
204     return result_set.intValue();
205   }
206
207   public void setNull(int parameterIndex, int sqlType) throws SQLException {
208     statement.setVar(parameterIndex - 1, null);
209   }
210
211   public void setBoolean(int parameterIndex, boolean x) throws SQLException {
212     statement.setVar(parameterIndex - 1, new Boolean JavaDoc(x));
213   }
214
215   public void setByte(int parameterIndex, byte x) throws SQLException {
216     setLong(parameterIndex, x);
217   }
218
219   public void setShort(int parameterIndex, short x) throws SQLException {
220     setLong(parameterIndex, x);
221   }
222
223   public void setInt(int parameterIndex, int x) throws SQLException {
224     setLong(parameterIndex, x);
225   }
226
227   public void setLong(int parameterIndex, long x) throws SQLException {
228     statement.setVar(parameterIndex - 1, BigNumber.fromLong(x));
229   }
230
231   public void setFloat(int parameterIndex, float x) throws SQLException {
232     statement.setVar(parameterIndex - 1, BigNumber.fromFloat(x));
233   }
234
235   public void setDouble(int parameterIndex, double x) throws SQLException {
236     statement.setVar(parameterIndex - 1, BigNumber.fromDouble(x));
237   }
238
239   public void setBigDecimal(int parameterIndex, BigDecimal JavaDoc x)
240                                                          throws SQLException {
241     if (x == null) {
242       setNull(parameterIndex, Types.NUMERIC);
243     }
244     else {
245       statement.setVar(parameterIndex - 1, BigNumber.fromBigDecimal(x));
246     }
247   }
248
249   public void setString(int parameterIndex, String JavaDoc x) throws SQLException {
250     if (x == null) {
251       setNull(parameterIndex, Types.VARCHAR);
252     }
253     else {
254       // If the string is less than 4K characters then transfer as a regular
255
// string, otherwise treat the string as a large object.
256
if (x.length() <= 4 * 1024) {
257         statement.setVar(parameterIndex - 1, StringObject.fromString(x));
258       }
259       else {
260         setCharacterStream(parameterIndex, new StringReader(x), x.length());
261       }
262     }
263   }
264
265   public void setBytes(int parameterIndex, byte[] x) throws SQLException {
266     if (x == null) {
267       setNull(parameterIndex, Types.BINARY);
268     }
269     else {
270       // If the byte array is small then transfer as a regular ByteLongObject
271
if (x.length <= 8 * 1024) {
272         ByteLongObject b = new ByteLongObject(x);
273         statement.setVar(parameterIndex - 1, b);
274       }
275       else {
276         // Otherwise wrap around a ByteArrayInputStream and treat as a large
277
// object.
278
setBinaryStream(parameterIndex, new ByteArrayInputStream(x), x.length);
279       }
280     }
281   }
282
283   // JDBC Extension ... Use java.util.Date as parameter
284
public void extSetDate(int parameterIndex, java.util.Date JavaDoc x)
285                                                          throws SQLException {
286     statement.setVar(parameterIndex - 1, x);
287   }
288
289
290   public void setDate(int parameterIndex, java.sql.Date JavaDoc x)
291                                                          throws SQLException {
292     if (x == null) {
293       setNull(parameterIndex, Types.DATE);
294     }
295     else {
296       extSetDate(parameterIndex, new java.util.Date JavaDoc(x.getTime()));
297     }
298   }
299
300   public void setTime(int parameterIndex, java.sql.Time JavaDoc x)
301                                                          throws SQLException {
302     if (x == null) {
303       setNull(parameterIndex, Types.TIME);
304     }
305     else {
306       extSetDate(parameterIndex, new java.util.Date JavaDoc(x.getTime()));
307     }
308   }
309
310   /**
311    * True if the timestamp value includes nanoseconds, which is the case
312    * starting with Java 1.4.0
313    */

314   private static final boolean TIMESTAMP_VALUE_INCLUDES_NANOS =
315                                    (new java.sql.Timestamp JavaDoc(5).getTime() == 5);
316
317   public void setTimestamp(int parameterIndex, java.sql.Timestamp JavaDoc x)
318                                                          throws SQLException {
319     if (x == null) {
320       setNull(parameterIndex, Types.TIMESTAMP);
321     }
322     else {
323       long time = x.getTime();
324       if (!TIMESTAMP_VALUE_INCLUDES_NANOS) {
325         time += (x.getNanos() / 1000000);
326       }
327       extSetDate(parameterIndex, new java.util.Date JavaDoc(time));
328     }
329   }
330
331   public void setAsciiStream(int parameterIndex, java.io.InputStream JavaDoc x,
332                              int length) throws SQLException {
333     if (x == null) {
334       setNull(parameterIndex, Types.LONGVARCHAR);
335     }
336     else {
337       try {
338         // Process a potentially large object.
339
setVariableLengthStream(parameterIndex, x, length, (byte) 3);
340       }
341       catch (IOException e) {
342         throw new SQLException("IOException reading input stream: " +
343                                e.getMessage());
344       }
345 // // Fudge implementation since we fudged the result set version of this.
346
// // In an ideal world we'd open up a stream with the server and download
347
// // the information without having to collect all the data to transfer it.
348
// try {
349
// StringBuffer buf = new StringBuffer();
350
// int i = 0;
351
// while (i < length) {
352
// int c = x.read();
353
// if (c == -1) {
354
// throw new IOException(
355
// "End of stream reached before length reached.");
356
// }
357
// buf.append((char) c);
358
// ++i;
359
// }
360
// setString(parameterIndex, new String(buf));
361
// }
362
// catch (IOException e) {
363
// e.printStackTrace();
364
// throw new SQLException("IO Error: " + e.getMessage());
365
// }
366
}
367   }
368
369   /**
370    * @deprecated
371    */

372   public void setUnicodeStream(int parameterIndex, java.io.InputStream JavaDoc x,
373                                int length) throws SQLException {
374     throw new SQLException("Deprecated method not supported");
375   }
376
377   public void setBinaryStream(int parameterIndex, java.io.InputStream JavaDoc x,
378                               int length) throws SQLException {
379     if (x == null) {
380       setNull(parameterIndex, Types.BINARY);
381     }
382     else {
383       try {
384         // Process a potentially large object.
385
setVariableLengthStream(parameterIndex, x, length, (byte) 2);
386       }
387       catch (IOException e) {
388         throw new SQLException("IOException reading input stream: " +
389                                e.getMessage());
390       }
391     }
392   }
393
394   public void clearParameters() throws SQLException {
395     statement.clear();
396   }
397
398   //----------------------------------------------------------------------
399
// Advanced features:
400

401   public void setObject(int parameterIndex, Object JavaDoc x, int targetSqlType,
402                         int scale) throws SQLException {
403     if (x == null) {
404       setNull(parameterIndex, targetSqlType);
405     }
406     else {
407       x = convertToType(x, targetSqlType);
408       if (x instanceof BigDecimal JavaDoc) {
409         x = ((BigDecimal JavaDoc) x).setScale(scale, BigDecimal.ROUND_HALF_UP);
410       }
411       statement.setVar(parameterIndex - 1, x);
412     }
413   }
414
415   public void setObject(int parameterIndex, Object JavaDoc x, int targetSqlType)
416                                                          throws SQLException {
417     if (x == null) {
418       setNull(parameterIndex, targetSqlType);
419     }
420     else {
421       statement.setVar(parameterIndex - 1, convertToType(x, targetSqlType));
422     }
423   }
424
425   public void setObject(int parameterIndex, Object JavaDoc x) throws SQLException {
426     statement.setVar(parameterIndex - 1, castToMckoiObject(x));
427   }
428
429   public boolean execute() throws SQLException {
430     MResultSet result_set = executeQuery(statement);
431     return !result_set.isUpdate();
432   }
433
434 //#IFDEF(JDBC2.0)
435

436   //--------------------------JDBC 2.0-----------------------------
437

438   public void addBatch() throws SQLException {
439     addBatch(statement.copy());
440   }
441
442   public void setCharacterStream(int parameterIndex,
443                                  java.io.Reader JavaDoc reader,
444                                  int length) throws SQLException {
445     if (reader == null) {
446       setNull(parameterIndex, Types.LONGVARCHAR);
447     }
448     else {
449       try {
450         // Process as a potentially large object.
451
setVariableLengthStream(parameterIndex,
452                      new UnicodeToBinaryStream(reader), length * 2, (byte) 4);
453       }
454       catch (IOException e) {
455         throw new SQLException("IOException reading input stream: " +
456                                e.getMessage());
457       }
458 // // NOTE: The whole stream is read into a String and the 'setString' method
459
// // is called. This is inappropriate for long streams but probably
460
// // won't be an issue any time in the future.
461
// StringBuffer buf = new StringBuffer();
462
// final int BUF_LENGTH = 1024;
463
// char[] char_buf = new char[BUF_LENGTH];
464
// try {
465
// while (length > 0) {
466
// int read = reader.read(char_buf, 0, Math.min(BUF_LENGTH, length));
467
// if (read > 0) {
468
// buf.append(char_buf, 0, read);
469
// length = length - read;
470
// }
471
// else {
472
// throw new SQLException("Premature end of Reader reached.");
473
// }
474
// }
475
// }
476
// catch (IOException e) {
477
// throw new SQLException("IOError: " + e.getMessage());
478
// }
479
// setString(parameterIndex, new String(buf));
480
}
481   }
482
483   public void setRef (int i, Ref x) throws SQLException {
484     throw MSQLException.unsupported();
485   }
486
487   public void setBlob (int i, Blob x) throws SQLException {
488     if (x == null) {
489       setNull(i, Types.BLOB);
490     }
491     else {
492       // BLOBs are handled the same as a binary stream. If the length of the
493
// blob exceeds a certain threshold the object is treated as a large
494
// object and transferred to the server in separate chunks.
495
long len = x.length();
496       if (len >= 32768L * 65536L) {
497         throw new SQLException("BLOB > 2 gigabytes is too large.");
498       }
499       setBinaryStream(i, x.getBinaryStream(), (int) len);
500     }
501   }
502
503   public void setClob (int i, Clob x) throws SQLException {
504     if (x == null) {
505       setNull(i, Types.CLOB);
506     }
507     else {
508       // CLOBs are handled the same as a character stream. If the length of the
509
// clob exceeds a certain threshold the object is treated as a large
510
// object and transferred to the server in separate chunks.
511
long len = x.length();
512       if (len >= 16384L * 65536L) {
513         throw new SQLException("CLOB > 1 billion characters is too large.");
514       }
515       setCharacterStream(i, x.getCharacterStream(), (int) len);
516     }
517   }
518
519   public void setArray (int i, Array x) throws SQLException {
520     throw MSQLException.unsupported();
521   }
522
523   public ResultSetMetaData getMetaData() throws SQLException {
524     // TODO....
525
throw MSQLException.unsupported();
526   }
527
528   public void setDate(int parameterIndex, java.sql.Date JavaDoc x, Calendar JavaDoc cal)
529                                                         throws SQLException {
530     // Kludge...
531
setDate(parameterIndex, x);
532   }
533
534   public void setTime(int parameterIndex, java.sql.Time JavaDoc x, Calendar JavaDoc cal)
535                                                         throws SQLException {
536     // Kludge...
537
setTime(parameterIndex, x);
538   }
539
540   public void setTimestamp(int parameterIndex, java.sql.Timestamp JavaDoc x,
541                            Calendar JavaDoc cal) throws SQLException {
542     // Kludge...
543
setTimestamp(parameterIndex, x);
544   }
545
546   public void setNull (int paramIndex, int sqlType, String JavaDoc typeName)
547                                                         throws SQLException {
548     // Kludge again...
549
setNull(paramIndex, sqlType);
550   }
551
552 //#ENDIF
553

554 //#IFDEF(JDBC3.0)
555

556   // ---------- JDBC 3.0 ----------
557

558   public void setURL(int parameterIndex, java.net.URL JavaDoc x) throws SQLException {
559     throw MSQLException.unsupported();
560   }
561
562   public ParameterMetaData getParameterMetaData() throws SQLException {
563     throw MSQLException.unsupported();
564   }
565
566 //#ENDIF
567

568   /**
569    * For diagnostics.
570    */

571   public String JavaDoc toString() {
572     return statement.toString();
573   }
574  
575 }
576
Popular Tags