001: /*
002: * $Header: /cvsroot/webman-cms/source/webman/com/teamkonzept/db/TKQuery.java,v 1.18 2002/02/16 15:56:51 alex Exp $
003: *
004: */
005: package com.teamkonzept.db;
006:
007: import java.io.*;
008: import java.sql.*;
009:
010: import com.teamkonzept.lib.*;
011: import com.teamkonzept.webman.mainint.DatabaseDefaults;
012: import org.apache.log4j.Category;
013:
014: /** Abstrakte Query Klasse.
015: * Stellt eine Basis zum Erstellen und Ausführen von SQL-Queries
016: * zur Verfügung.
017: *
018: * @author
019: * @version
020: */
021: public abstract class TKQuery implements QueryConstants {
022: /** Logging Category */
023: protected static final Category CAT = Category
024: .getInstance(TKQuery.class);
025:
026: public static final int DBFORM_IDENT = 69;
027: public static final int DBSTRUC_IDENT = 66;
028: public static final int DBFRAGMENT_IDENT = 67;
029:
030: /** Verbindung zu der DB
031: */
032: protected Connection conn;
033:
034: /** Objekt welches Datenstrukturen im Bezug auf die Connection enth‰lt
035: */
036: protected TKDBConnection aTKDBConnection;
037:
038: /** JDBC-Statement, das der Query zugrundeliegt.
039: */
040: protected Statement stmt;
041:
042: /** SQL-String, welches die Query definiert.
043: * Dieser String ist nicht unbedingt über die gesammte Lebenszeit
044: * des Objektes definiert, bzw. enthäalnt nicht zu jedem Zeitpunkt
045: * einen gültigen SQL-String.
046: */
047: protected String sqlString;
048:
049: /** Hashtable, welche Typinformation der Query-Parameter zur
050: * Verfügung stellt.
051: * key: String parameter_name
052: * value: java.sql.Types parameter_typ
053: *
054: * @see java.sql.Types
055: */
056: protected TKHashtable paramTypes;
057:
058: /** Flag welches festlegt ob die Query als senslibel betrachtet wird.
059: Als sensible sollten solche Queries markiert werde, die
060: Datenbankinhalte veraendern. Diese muessen dann innerhalb
061: einer expliziten Transaktionsumgebung ausgefuehrt werden.
062: */
063: protected boolean sensitiveQuery = false;
064:
065: /* Index mit dem nichtsensitive Queries deregistriert werden.
066: */
067: protected int deregisterIndex;
068:
069: /** Hashtable, welche zur jedem Query-Parametername dessen
070: * Wert verwaltet.
071: */
072: protected TKHashtable queryParams;
073: protected boolean[] setRelevants;
074:
075: protected int currPos;
076: protected boolean currIsResultSet;
077:
078: /** Abstrakte Methode zur Initialisierung der Query.
079: * Diese Methode wird üblicherweise von einer der beiden
080: * abgeleiteten Query-Klassen verwendet um eine <I>nicht abstrakte<I>
081: * Query zu initialisieren und muß deshalb definiert werden.
082: *
083: * @param Connection conn Verbindung zu der DB
084: *
085: * @see com.teamkonzept.db.TKUnprepQuery
086: * @see com.teamkonzept.db.TKPrepQuery
087: */
088: public abstract void initQuery(final Connection conn);
089:
090: /** Abstrakte Methode zur Initialisierung der Query.
091: * Diese Methode wird üblicherweise vom DB-Manager aufgerufen,
092: * um eine Query zu initialisieren. Sie wird
093: * u.a. implementiert von com.teamkonzept.db.TKUnprepQuery u.
094: * com.teamkonzept.db.TKPrepQuery .
095: *
096: * @param tkdbConn enth‰lt u.a. Verbindung zu der DB
097: * @param conv der der DB zugeordnete Typ-Konverter
098: * @param queryID Name der Query Klasse, erleichtert die Wiederverwendung des Objektes
099: *
100: * @see com.teamkonzept.db.TKDBManager
101: * @see com.teamkonzept.db.TKDBManager#newQuery
102: */
103: public abstract void initQuery(final TKSQLTypeConverter conv,
104: final TKDBConnection tkdbConn, Object queryID);
105:
106: /** Methode zur Initialisierung des Queryobjektes
107: *
108: * @param conn - Verbindung zur DB
109: * @param paramTypes - Liste von Parametername, Parametertyp Paaren
110: * @param setRelevants - geordnete Liste, welche angibt, welche
111: * SQl(Teil)Queries relevante ResultSets liefern.
112: */
113: public void initQuery(final Connection conn,
114: final Object[][] paramTypes, final boolean[] setRelevants) {
115: this .conn = conn;
116: this .setRelevants = setRelevants;
117: this .currPos = 0;
118:
119: if (this instanceof SensitiveQuery) {
120: sensitiveQuery = true;
121: }
122: if (paramTypes != null) {
123: this .paramTypes = new TKHashtable(paramTypes);
124: } else {
125: this .paramTypes = null;
126: }
127: queryParams = new TKHashtable();
128: }
129:
130: /** Abstrakte Methode, welche die Query ausführt.
131: *
132: * @return true, falls die Query ein java.sql.ResultSet geliefert hat,
133: * false, falls das Resultat der Query ein "Update Count" ist
134: * oder leer ist.
135: *
136: * @exception java.sql.SQLException
137: */
138: public abstract boolean execute() throws SQLException;
139:
140: /** Methode die die Query in einer eigenen Transaktion ausfuehrt.
141: Kann nur dann benutzt werden, wenn man kein Resultset braucht.
142: */
143: public void executeAsTran() throws SQLException {
144: aTKDBConnection.beginTransaction();
145: try {
146: this .execute();
147: this .close();
148: } catch (SQLException t) {
149: try {
150: aTKDBConnection.rollbackTransaction();
151: } catch (SQLException e) {
152: throw e;
153: }
154: throw new TKSQLError(t.getMessage(), t);
155: }
156: }
157:
158: /** Konstruktor zur Erstellung einer nicht initialisierten Query
159: */
160: public TKQuery() { /* NOP */
161: }
162:
163: /** Methode zur Registrierung der Query fuer spaeteres
164: * automatisches Schliessen, und zur Kontrolle ob
165: * sensible Queries in einer korrekten Transaktion ausgefuehrt
166: * werden. (Wird von execute() aufgrufen)
167: * @return i > -1 bei nichtsensibler Query ein Index mit
168: * welchem die Query wieder deregistriert werden kann
169: * @return -1 bei sensibler Query
170: */
171: protected int registerQuery() {
172: int anInt;
173: if (sensitiveQuery == false) {
174: anInt = aTKDBConnection.registerNonSensitiveQuery(this );
175:
176: } else {
177: aTKDBConnection.registerSensitiveQuery();
178: anInt = -1;
179: }
180: return anInt;
181: }
182:
183: /** Methode zum Abmelden. Wird von close() gerufen.
184: */
185: protected void deregisterQuery() {
186: TKQuery query;
187: if (sensitiveQuery) {
188: aTKDBConnection.deregisterSensitiveQuery();
189: } else {
190: query = aTKDBConnection
191: .deregisterNonSensitiveQuery(deregisterIndex);
192: if (query != this )
193: CAT.warn(" Wrong Query deregistered ");
194: }
195: }
196:
197: /** Methode zum Auslesen des Ergebnisses der Query.
198: *
199: * @return java.sql.ResultSet Ergebnis der Query, nach Aufruf der
200: * Methode execute().
201: *
202: * @exception com.teamkonzept.db.TKSQLError
203: */
204: public ResultSet fetchResultSet() {
205: if (TKDBManager.getDBVendor() == ORACLE
206: || TKDBManager.getDBVendor() == POSTGRESQL) {
207: try {
208: return stmt.getResultSet();
209: } catch (SQLException sqle) {
210: printSqlException(sqle, "fetchResultSet");
211: }
212: }
213:
214: if (setRelevants == null)
215: return null;
216:
217: ResultSet rs = null;
218:
219: FETCH: while (currPos < setRelevants.length) {
220: try {
221: if ((currPos != 0) || (!currIsResultSet)) {
222: while ((!(currIsResultSet = stmt.getMoreResults()))
223: && (stmt.getUpdateCount() != -1)) {
224: CAT.debug("POS " + currPos
225: + " NO RS OR UPDATECOUNT<BR>");
226: }
227: }
228:
229: if (currIsResultSet) {
230: rs = stmt.getResultSet();
231: CAT.debug("POS " + currPos + " RS FOUND <BR>");
232: } else {
233: CAT.debug("POS " + currPos + " END <BR>");
234: currPos = setRelevants.length;
235: break FETCH;
236: }
237:
238: if (setRelevants[currPos++]) {
239: CAT.debug("POS " + (currPos - 1)
240: + " RS RELEVANT <BR>");
241: break FETCH;
242: } else {
243: CAT.debug("POS " + (currPos - 1)
244: + " NOT RELEVANT, RS SET NULL<BR>");
245: rs = null;
246: }
247:
248: } catch (SQLException sqle) {
249: printSqlException(sqle, "fetchResultSet");
250: }
251: }
252:
253: return rs;
254: }
255:
256: /** Wie specClose, aber mit Fehlerbehandlung.
257: Innerhalb von Transaktionen wird der Verbindungsabbau
258: der Rollback - Methode ueberlassen.
259: */
260:
261: public void close() throws SQLException {
262: try {
263:
264: specClose();
265:
266: } catch (SQLException e) {
267: try {
268: if (!aTKDBConnection.isTransactionOpen()) {
269: TKDBManager.closeConnection();
270: }
271: } catch (SQLException s) {
272: CAT.debug(s.getMessage());
273: }
274: printSqlException(e, " close PrepQuery failed ");
275: }
276: }
277:
278: /** Methode um die Query abzuschliessen und bei
279: * TKDConnection Objekt abzumelden
280: *
281: */
282: public abstract void specClose() throws SQLException;
283:
284: /** Holt alle Results (das sind ResultSets und UpdateCounts)
285: * ab. Das ist z.B. erforderlich damit ein PreparedStatement als
286: * abgeschlossen gilt.
287: * ACHTUNG: mit getResultSet() anstelle von getMoreResults()
288: * h‰tte diese Methode nicht funktuiniert! Ein Bug?
289: *
290: * @exception java.sql.SQLException
291: */
292: public void throwawayResults() throws SQLException {
293: /* non jdbc conform behaviour */
294: if (TKDBManager.getDBVendor() == POSTGRESQL)
295: return;
296: if (TKDBManager.getDBVendor() == ORACLE)
297: return;
298: if (TKDBManager.getDBVendor() == DONTKNOW)
299: return;
300:
301: if (stmt != null) {
302: while (stmt.getMoreResults()
303: || (stmt.getUpdateCount() != -1)) {
304: }
305: }
306: }
307:
308: /** Methode, welche einem Query-Parameter einen Wert zuweist.
309: *
310: * @param java.lang.String param - Name des Parameters
311: * @param java.lang.Object val - Wert des Parameters
312: */
313: public void setQueryParams(String param, Object val) {
314: if (val == null) {
315: queryParams.put(param, TKNull.NULL);
316: } else {
317: queryParams.put(param, val);
318: }
319: }
320:
321: /** Methode, welche eine SQLException in einen TKSQLError
322: * konvertiert und diesen wirft.
323: *
324: * @param java.sql.SQLException sqle - die zu konvertierende SQLException
325: * @param java.lang.String s - String, welcher beschreibt, bei welcher Aktion
326: * die Ausnahme aufgetreten ist.
327: *
328: * @exception com.teamkonzept.db.TKSQLError
329: */
330: public void printSqlException(SQLException sqle, String s) {
331: throw new TKSQLError(" Query: " + this .getClass().getName()
332: + " STMT: " + sqlString + " Action: " + s
333: + " Message: " + sqle.getMessage(), sqle);
334: }
335: }
|