KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > SqlStatement


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/rolap/SqlStatement.java#1 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2007-2007 Julian Hyde and others
7 // All Rights Reserved.
8 // You must accept the terms of that agreement to use this software.
9 */

10 package mondrian.rolap;
11
12 import mondrian.olap.Util;
13
14 import javax.sql.DataSource JavaDoc;
15 import java.sql.Connection JavaDoc;
16 import java.sql.ResultSet JavaDoc;
17 import java.sql.SQLException JavaDoc;
18 import java.sql.Statement JavaDoc;
19 import java.io.PrintWriter JavaDoc;
20
21 /**
22  * SqlStatement contains a SQL statement and associated resources throughout
23  * its lifetime.
24  *
25  * <p>The goal of SqlStatement is to make tracing, error-handling and
26  * resource-management easier. None of the methods throws a SQLException;
27  * if an error occurs in one of the methods, the method wraps the exception
28  * in a {@link RuntimeException} describing the high-level operation, logs
29  * that the operation failed, and throws that RuntimeException.
30  *
31  * <p>If methods succeed, the method generates lifecycle logging such as
32  * the elapsed time and number of rows fetched.
33  *
34  * <p>There are a few obligations on the caller. The caller must:<ul>
35  * <li>call the {@link #handle(Exception)} method if one of the contained
36  * objects (say the {@link java.sql.ResultSet}) gives an error;
37  * <li>call the {@link #close()} method if all operations complete
38  * successfully.
39  * <li>increment the {@link #rowCount} field each time a row is fetched.
40  * </ul>
41  *
42  * <p>The {@link #close()} method is idempotent. You are welcome to call it
43  * more than once.
44  *
45  * <p>SqlStatement is not thread-safe.
46  *
47  * @version $Id: //open/mondrian/src/main/mondrian/rolap/SqlStatement.java#1 $
48  * @author jhyde
49  * @since 2.3
50  */

51 public class SqlStatement {
52     private final DataSource JavaDoc dataSource;
53     private Connection JavaDoc jdbcConnection;
54     private ResultSet JavaDoc resultSet;
55     private final String JavaDoc sql;
56     private final int maxRows;
57     private final String JavaDoc component;
58     private final int resultSetType;
59     private final int resultSetConcurrency;
60     private final RolapUtil.Semaphore querySemaphore = RolapUtil
61         .getQuerySemaphore();
62     private final String JavaDoc message;
63     private boolean haveSemaphore;
64     public int rowCount;
65     private long startTime;
66     private final PrintWriter JavaDoc trace;
67
68     SqlStatement(
69         DataSource JavaDoc dataSource,
70         String JavaDoc sql,
71         int maxRows,
72         String JavaDoc component,
73         String JavaDoc message,
74         int resultSetType,
75         int resultSetConcurrency)
76     {
77         this.dataSource = dataSource;
78         this.sql = sql;
79         this.maxRows = maxRows;
80         this.component = component;
81         this.message = message;
82         this.resultSetType = resultSetType;
83         this.resultSetConcurrency = resultSetConcurrency;
84         this.trace = RolapUtil.checkTracing();
85     }
86
87     public void execute() throws SQLException JavaDoc {
88         this.jdbcConnection = dataSource.getConnection();
89         querySemaphore.enter();
90         haveSemaphore = true;
91         Statement JavaDoc statement = null;
92         String JavaDoc status = "failed";
93
94         // Trace start of execution.
95
if (trace != null) {
96             trace.print(component + ": executing sql [");
97             if (sql.indexOf('\n') >= 0) {
98                 // SQL appears to be formatted as multiple lines. Make it
99
// start on its own line.
100
trace.println();
101             }
102             trace.print(sql);
103             trace.print(']');
104             trace.flush();
105         }
106
107         // Execute hook.
108
RolapUtil.ExecuteQueryHook hook = RolapUtil.threadHooks.get();
109         if (hook != null) {
110             hook.onExecuteQuery(sql);
111         }
112         try {
113             startTime = System.currentTimeMillis();
114             if (resultSetType < 0 || resultSetConcurrency < 0) {
115                 statement = jdbcConnection.createStatement();
116             } else {
117                 statement = jdbcConnection.createStatement(
118                     resultSetType,
119                     resultSetConcurrency);
120             }
121             if (maxRows > 0) {
122                 statement.setMaxRows(maxRows);
123             }
124             this.resultSet = statement.executeQuery(sql);
125             long time = System.currentTimeMillis();
126             final long execMs = time - startTime;
127             Util.addDatabaseTime(execMs);
128             status = ", exec " + execMs + " ms";
129         } catch (SQLException JavaDoc e) {
130             status = ", failed (" + e + ")";
131             try {
132                 if (statement != null) {
133                     statement.close();
134                 }
135             } catch (SQLException JavaDoc e2) {
136                 // ignore
137
}
138             throw handle(e);
139         } finally {
140             if (trace != null) {
141                 trace.print(status);
142             }
143             if (RolapUtil.LOGGER.isDebugEnabled()) {
144                 RolapUtil.LOGGER.debug(component + ": executing sql [" +
145                     sql + "]" + status);
146             }
147         }
148     }
149
150     /**
151      * Closes all resources (statement, result set) held by this
152      * SqlStatement.
153      *
154      * <p>If any of them fails, wraps them in a
155      * {@link RuntimeException} describing the high-level operation which
156      * this statement was performing. No further error-handling is required
157      * to produce a descriptive stack trace, unless you want to absorb the
158      * error.
159      */

160     public void close() {
161         if (haveSemaphore) {
162             haveSemaphore = false;
163             querySemaphore.leave();
164         }
165         if (resultSet != null) {
166             try {
167                 resultSet.close();
168             } catch (SQLException JavaDoc e) {
169                 throw Util.newError(message + "; sql=[" + sql + "]");
170             } finally {
171                 resultSet = null;
172             }
173         }
174         if (jdbcConnection != null) {
175             try {
176                 jdbcConnection.close();
177             } catch (SQLException JavaDoc e) {
178                 throw Util.newError(message + "; sql=[" + sql + "]");
179             } finally {
180                 jdbcConnection = null;
181             }
182         }
183         long time = System.currentTimeMillis();
184         long totalMs = time - startTime;
185         if (trace != null) {
186             trace.println(
187                 ", exec+fetch " + totalMs + " ms, " + rowCount + " rows");
188         }
189     }
190
191     public ResultSet JavaDoc getResultSet() {
192         return resultSet;
193     }
194
195     /**
196      * Handles an exception thrown from the ResultSet, implicitly calls
197      * {@link #close}, and returns an exception which includes the full
198      * stack, including a description of the high-level operation.
199      *
200      * @param e Exception
201      * @return Runtime exception
202      */

203     public RuntimeException JavaDoc handle(Exception JavaDoc e) {
204         RuntimeException JavaDoc runtimeException =
205             Util.newError(e, message + "; sql=[" + sql + "]");
206         try {
207             close();
208         } catch (RuntimeException JavaDoc re) {
209             // ignore
210
}
211         return runtimeException;
212     }
213 }
214
215 // End SqlStatement.java
216
Popular Tags