001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.module.database;
011:
012: import java.sql.*;
013: import org.mmbase.util.logging.Logger;
014: import org.mmbase.util.logging.Logging;
015:
016: /**
017: * MultiStatement is a wrapper class for a callable Statement
018: * obtained by a MultiConnection object.
019: * The sole function of this class is to log the sql statement passed to it
020: * using the MultiConnection that called it - all calls are then passed to
021: * the Statement object passed to the constructor.
022: *
023: * @sql It would possibly be better to pass the logging of the sql query
024: * to the code that calls the statement, rather than place it in
025: * the statement itself, as it's implementation leads to conflicts
026: * between various JDBC versions.
027: *
028: * @author vpro
029: * @author Pierre van Rooden
030: * @version $Id: MultiStatement.java,v 1.20 2007/06/11 12:29:03 michiel Exp $
031: */
032: public class MultiStatement implements Statement {
033: private static final Logger log = Logging
034: .getLoggerInstance(MultiStatement.class);
035:
036: /**
037: * The connection that created this statement.
038: */
039: MultiConnection parent;
040: /**
041: * The actual statement (created by the database driver)
042: */
043: private Statement s;
044:
045: /**
046: * @javadoc
047: */
048: MultiStatement(MultiConnection parent, Statement s) {
049: this .parent = parent;
050: this .s = s;
051: }
052:
053: /**
054: * @javadoc
055: */
056: public int executeUpdate(String sql) throws SQLException {
057: parent.setLastSQL(sql);
058: return s.executeUpdate(sql);
059: }
060:
061: /**
062: * @javadoc
063: */
064: public void close() throws SQLException {
065: s.close();
066: s = null; // lets asign it to null to be sure
067: }
068:
069: /**
070: * @javadoc
071: */
072: public int getMaxFieldSize() throws SQLException {
073: return s.getMaxFieldSize();
074: }
075:
076: /**
077: * @javadoc
078: */
079: public void setMaxFieldSize(int max) throws SQLException {
080: s.setMaxFieldSize(max);
081: }
082:
083: /**
084: * @javadoc
085: */
086: public int getMaxRows() throws SQLException {
087: return s.getMaxRows();
088: }
089:
090: /**
091: * @javadoc
092: */
093: public void setMaxRows(int max) throws SQLException {
094: s.setMaxRows(max);
095: }
096:
097: /**
098: * @javadoc
099: */
100: public void setEscapeProcessing(boolean enable) throws SQLException {
101: s.setEscapeProcessing(enable);
102: }
103:
104: /**
105: * @javadoc
106: */
107: public int getQueryTimeout() throws SQLException {
108: return s.getQueryTimeout();
109: }
110:
111: /**
112: * @javadoc
113: */
114: public void setQueryTimeout(int seconds) throws SQLException {
115: s.setQueryTimeout(seconds);
116: }
117:
118: /**
119: * @javadoc
120: */
121: public void cancel() throws SQLException {
122: s.cancel();
123: }
124:
125: /**
126: * @javadoc
127: */
128: public SQLWarning getWarnings() throws SQLException {
129: return s.getWarnings();
130: }
131:
132: /**
133: * @javadoc
134: */
135: public void clearWarnings() throws SQLException {
136: s.clearWarnings();
137: }
138:
139: /**
140: * @javadoc
141: */
142: public boolean execute(String sql) throws SQLException {
143: parent.setLastSQL(sql);
144: return s.execute(sql);
145: }
146:
147: /**
148: * @javadoc
149: */
150: public ResultSet getResultSet() throws SQLException {
151: return s.getResultSet();
152: }
153:
154: /**
155: * @javadoc
156: */
157: public int getUpdateCount() throws SQLException {
158: return s.getUpdateCount();
159: }
160:
161: /**
162: * @javadoc
163: */
164: public boolean getMoreResults() throws SQLException {
165: return s.getMoreResults();
166: }
167:
168: /**
169: * @javadoc
170: */
171: public void setCursorName(String name) throws SQLException {
172: s.setCursorName(name);
173: }
174:
175: /**
176: * Tries to fix the parent connection, if it proves to be broken. It is supposed to be broken if
177: * the query "SELECT 1 FROM <OBJECT TABLE> WHERE 1 = 0" does yield an exception.
178: * This method is meant to be called in the catch after trying to exceute a statement.
179: *
180: * @return A new Statement object if a new Connection was successfully obtained. Or null, if 'SELECT 1' did succeed.
181: * @throws SQLException if SELECT 1 did fail an no new Connection could be obtained.
182: * @since MMBase-1.7.1
183: */
184: protected Statement checkAfterException() throws SQLException {
185: if (!org.mmbase.module.core.MMBase.getMMBase().getBuilder(
186: "object").created()) {
187: // if this table does not exist, this is impossible
188: return s;
189: }
190: ResultSet rs = null;
191: // check wether connection is still functional
192: try {
193: rs = s.executeQuery("SELECT 1 FROM "
194: + org.mmbase.module.core.MMBase.getMMBase()
195: .getBuilder("object").getFullTableName()
196: + " WHERE 1 = 0"); // if this goes wrong too it can't be the query);
197: } catch (SQLException isqe) {
198: // so, connection must be broken.
199: log.service("Found broken connection, will try to fix it.");
200: s.close();
201: parent.getParent().replaceConnection(parent);
202: s = parent.createStatement();
203: // this would be more correct:
204: //s = parent.createStatement(s.getResultSetType(), s.getResultSetConcurrency(), s.getResultSetHoldability());
205: // but I have a psql driver:
206: // org.postgresql.util.PSQLException: This method is not yet implemented.
207: // at org.postgresql.Driver.notImplemented(Driver.java:388)
208: // at org.postgresql.jdbc3.AbstractJdbc3Statement.getResultSetHoldability(AbstractJdbc3Statement.java:278)
209: // It does not matter much because in all of MMBase only 'createStatement()' is used.
210: return s;
211: } finally {
212: if (rs != null) {
213: rs.close();
214: }
215: }
216:
217: return null;
218: }
219:
220: /**
221: * @javadoc
222: */
223: public ResultSet executeQuery(String sql) throws SQLException {
224: try {
225: parent.setLastSQL(sql);
226: return s.executeQuery(sql);
227: } catch (SQLException sqe) {
228: Statement ts = checkAfterException();
229: if (ts != null) { // there was something wrong with the connection, try it once again
230: return ts.executeQuery(sql);
231: } else {
232: throw sqe;
233: }
234:
235: }
236: }
237:
238: /**
239: * @javadoc
240: */
241: public int[] executeBatch() throws SQLException {
242: return s.executeBatch();
243: }
244:
245: /**
246: * @javadoc
247: */
248: public void setFetchDirection(int dir) throws SQLException {
249: s.setFetchDirection(dir);
250: }
251:
252: /**
253: * @javadoc
254: */
255: public int getFetchDirection() throws SQLException {
256: return s.getFetchDirection();
257: }
258:
259: /**
260: * @javadoc
261: */
262: public int getResultSetConcurrency() throws SQLException {
263: return s.getResultSetConcurrency();
264: }
265:
266: /**
267: * @javadoc
268: */
269: public int getResultSetType() throws SQLException {
270: return s.getResultSetType();
271: }
272:
273: /**
274: * @javadoc
275: */
276: public void addBatch(String sql) throws SQLException {
277: s.addBatch(sql);
278: }
279:
280: /**
281: * @javadoc
282: */
283: public void clearBatch() throws SQLException {
284: s.clearBatch();
285: }
286:
287: /**
288: * @javadoc
289: */
290: public Connection getConnection() throws SQLException {
291: return s.getConnection();
292: }
293:
294: /**
295: * @javadoc
296: */
297: public int getFetchSize() throws SQLException {
298: return s.getFetchSize();
299: }
300:
301: /**
302: * @javadoc
303: */
304: public void setFetchSize(int i) throws SQLException {
305: s.setFetchSize(i);
306: }
307:
308: /**
309: * Moves to this Statement object's next result, deals with any current ResultSet object(s) according to
310: * the instructions specified by the given flag, and returns true if the next result is a ResultSet object.
311: * @param current one of CLOSE_CURRENT_RESULT, KEEP_CURRENT_RESULT, or CLOSE_ALL_RESULTS
312: * @return true if the next result is a ResultSet object; false if it is an update count or there are no more results
313: * @since MMBase 1.5, JDBC 1.4
314: */
315: public boolean getMoreResults(int current) throws SQLException {
316: return s.getMoreResults(current);
317: }
318:
319: /**
320: * Retrieves any auto-generated keys created as a result of executing this Statement object.
321: * @return a ResultSet object containing the auto-generated key(s) generated by the execution of this Statement object
322: * @since MMBase 1.5, JDBC 1.4
323: */
324: public ResultSet getGeneratedKeys() throws SQLException {
325: return s.getGeneratedKeys();
326: }
327:
328: /**
329: * Executes the given SQL statement and signals the driver with the given flag about whether the
330: * auto-generated keys produced by this Statement object should be made available for retrieval.
331: * @param sql must be an SQL INSERT, UPDATE or DELETE statement or an SQL statement that returns nothing
332: * @param autoGeneratedKeys a flag indicating whether auto-generated keys should be made available for retrieval
333: * @return either the row count for INSERT, UPDATE or DELETE statements, or 0 for SQL statements that return nothing
334: * @since MMBase 1.5, JDBC 1.4
335: */
336: public int executeUpdate(String sql, int autoGeneratedKeys)
337: throws SQLException {
338: try {
339: parent.setLastSQL(sql);
340: return s.executeUpdate(sql, autoGeneratedKeys);
341: } catch (SQLException sqe) {
342: Statement ts = checkAfterException();
343: if (ts != null) { // there was something wrong with the connection, try it once again
344: return ts.executeUpdate(sql, autoGeneratedKeys);
345: } else {
346: throw sqe;
347: }
348: }
349: }
350:
351: /**
352: * Executes the given SQL statement and signals the driver that the auto-generated keys indicated in
353: * the given array should be made available for retrieval.
354: * @param sql must be an SQL INSERT, UPDATE or DELETE statement or an SQL statement that returns nothing
355: * @param columnIndexes an array of column indexes indicating the columns that should be returned from the inserted row
356: * @return either the row count for INSERT, UPDATE or DELETE statements, or 0 for SQL statements that return nothing
357: * @since MMBase 1.5, JDBC 1.4
358: */
359: public int executeUpdate(String sql, int[] columnIndexes)
360: throws SQLException {
361: try {
362: parent.setLastSQL(sql);
363: return s.executeUpdate(sql, columnIndexes);
364: } catch (SQLException sqe) {
365: Statement ts = checkAfterException();
366: if (ts != null) { // there was something wrong with the connection, try it once again
367: return ts.executeUpdate(sql, columnIndexes);
368: } else {
369: throw sqe;
370: }
371:
372: }
373: }
374:
375: /**
376: * Executes the given SQL statement and signals the driver that the auto-generated keys indicated in the given array
377: * should be made available for retrieval.
378: * @param sql must be an SQL INSERT, UPDATE or DELETE statement or an SQL statement that returns nothing
379: * @param columnNames - an array of the names of the columns that should be returned from the inserted row
380: * @return either the row count for INSERT, UPDATE or DELETE statements, or 0 for SQL statements that return nothing
381: * @since MMBase 1.5, JDBC 1.4
382: */
383: public int executeUpdate(String sql, String[] columnNames)
384: throws SQLException {
385: try {
386: parent.setLastSQL(sql);
387: return s.executeUpdate(sql, columnNames);
388: } catch (SQLException sqe) {
389: Statement ts = checkAfterException();
390: if (ts != null) { // there was something wrong with the connection, try it once again
391: return ts.executeUpdate(sql, columnNames);
392: } else {
393: throw sqe;
394: }
395:
396: }
397: }
398:
399: /**
400: * Executes the given SQL statement, which may return multiple results, and signals the driver that
401: * any auto-generated keys should be made available for retrieval.
402: * @param sql any SQL statement
403: * @param autoGeneratedKeys a flag indicating whether auto-generated keys should be made available for retrieval
404: * @return true if the first result is a ResultSet object; false if it is an update count or there are no results
405: * @since MMBase 1.5, JDBC 1.4
406: */
407: public boolean execute(String sql, int autoGeneratedKeys)
408: throws SQLException {
409: try {
410: parent.setLastSQL(sql);
411: return s.execute(sql, autoGeneratedKeys);
412: } catch (SQLException sqe) {
413: Statement ts = checkAfterException();
414: if (ts != null) { // there was something wrong with the connection, try it once again
415: return ts.execute(sql, autoGeneratedKeys);
416: } else {
417: throw sqe;
418: }
419: }
420: }
421:
422: /**
423: * Executes the given SQL statement, which may return multiple results, and signals the driver that
424: * the auto-generated keys indicated in the given array should be made available for retrieval.
425: * @param sql any SQL statement
426: * @param columnIndexes an array of column indexes indicating the columns that should be returned from the inserted row
427: * @return true if the first result is a ResultSet object; false if it is an update count or there are no results
428: * @since MMBase 1.5, JDBC 1.4
429: */
430: public boolean execute(String sql, int[] columnIndexes)
431: throws SQLException {
432: try {
433: parent.setLastSQL(sql);
434: return s.execute(sql, columnIndexes);
435: } catch (SQLException sqe) {
436: Statement ts = checkAfterException();
437: if (ts != null) { // there was something wrong with the connection, try it once again
438: return ts.execute(sql, columnIndexes);
439: } else {
440: throw sqe;
441: }
442: }
443: }
444:
445: /**
446: * Executes the given SQL statement, which may return multiple results, and signals the driver that
447: * the auto-generated keys indicated in the given array should be made available for retrieval.
448: * @param sql any SQL statement
449: * @param columnNames - an array of the names of the columns that should be returned from the inserted row
450: * @return true if the first result is a ResultSet object; false if it is an update count or there are no results
451: * @since MMBase 1.5, JDBC 1.4
452: */
453: public boolean execute(String sql, String[] columnNames)
454: throws SQLException {
455: try {
456: parent.setLastSQL(sql);
457: return s.execute(sql, columnNames);
458: } catch (SQLException sqe) {
459: Statement ts = checkAfterException();
460: if (ts != null) { // there was something wrong with the connection, try it once again
461: return ts.execute(sql, columnNames);
462: } else {
463: throw sqe;
464: }
465: }
466: }
467:
468: /**
469: * Retrieves the result set holdability for ResultSet objects generated by this Statement object.
470: * @return either ResultSet.HOLD_CURSORS_OVER_COMMIT or ResultSet.CLOSE_CURSORS_AT_COMMIT
471: * @since MMBase 1.5, JDBC 1.4
472: */
473: public int getResultSetHoldability() throws SQLException {
474: return s.getResultSetHoldability();
475: }
476:
477: public boolean isClosed() throws SQLException {
478: return s == null;// || s.isClosed(); // java 6
479: }
480:
481: public void setPoolable(boolean p) throws SQLException {
482: if (s == null)
483: throw new SQLException("Statement is closed");
484: //s.setPoolable(p);
485: }
486:
487: public boolean isPoolable() throws SQLException {
488: if (s == null)
489: throw new SQLException("Statement is closed");
490: //return s.isPoolable(); // java 6
491: return false;
492: }
493:
494: public <T> T unwrap(Class<T> iface) {
495: return (T) s;
496: }
497:
498: public boolean isWrapperFor(Class<?> iface) {
499: return iface.isAssignableFrom(s.getClass());
500: }
501: }
|