001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049:
050: package org.jaffa.persistence.engines.jdbcengine.querygenerator;
051:
052: import org.apache.log4j.Logger;
053: import java.util.*;
054: import java.lang.reflect.*;
055: import java.sql.CallableStatement;
056: import java.sql.PreparedStatement;
057: import java.sql.ResultSet;
058:
059: import java.io.IOException;
060: import java.sql.SQLException;
061: import java.lang.reflect.InvocationTargetException;
062: import java.sql.Statement;
063:
064: import org.jaffa.persistence.UOW;
065: import org.jaffa.persistence.IPersistent;
066: import org.jaffa.persistence.Criteria;
067: import org.jaffa.persistence.engines.jdbcengine.datasource.DataSource;
068: import org.jaffa.persistence.engines.jdbcengine.configservice.ConfigurationService;
069: import org.jaffa.persistence.engines.jdbcengine.configservice.ClassMetaData;
070: import org.jaffa.persistence.engines.jdbcengine.datasource.exceptions.DataSourceCursorRuntimeException;
071: import org.jaffa.persistence.engines.jdbcengine.IStoredProcedure;
072: import org.jaffa.persistence.engines.jdbcengine.util.Introspection;
073: import org.jaffa.persistence.engines.jdbcengine.querygenerator.DataTranslator;
074: import org.jaffa.persistence.engines.jdbcengine.util.MoldingService;
075: import org.jaffa.persistence.engines.jdbcengine.proxy.PersistentInstanceFactory;
076:
077: import org.jaffa.persistence.exceptions.PostLoadFailedException;
078:
079: /** Use the methods of this class to execute various operations on the database.
080: */
081: public class JdbcBridge {
082: private static final Logger log = Logger
083: .getLogger(JdbcBridge.class);
084: private static final int QUERY_TIMEOUT_FOR_LOCKING = 5;
085:
086: /** No need to instantiate this class.*/
087: private JdbcBridge() {
088: }
089:
090: /** Adds the Persistent object to the database.
091: * @param object The object to be added.
092: * @param dataSource The DataSource to which the object will be added.
093: * @throws SQLException if any database error occurs.
094: * @throws IllegalAccessException if the Persistent class is not accessible for introspection.
095: * @throws InvocationTargetException if the accessor method for the Persistent class throws an exception.
096: * @throws IOException if any error occurs while extracting the value for an attribute.
097: */
098: public static void executeAdd(IPersistent object,
099: DataSource dataSource) throws SQLException,
100: IllegalAccessException, InvocationTargetException,
101: IOException {
102: if (usePreparedStatement(dataSource))
103: executeAddWithPreparedStatement(object, dataSource);
104: else
105: executeAddWithStatement(object, dataSource);
106: updatePersistentFlagsOnAdd(object);
107: }
108:
109: private static void executeAddWithPreparedStatement(
110: IPersistent object, DataSource dataSource)
111: throws SQLException, IllegalAccessException,
112: InvocationTargetException {
113: ClassMetaData classMetaData = ConfigurationService
114: .getInstance().getMetaData(
115: PersistentInstanceFactory
116: .getActualPersistentClass(object)
117: .getName());
118: String sql = PreparedStatementHelper
119: .getInsertPreparedStatementString(classMetaData);
120: PreparedStatement pstmt = dataSource.getPreparedStatement(sql);
121:
122: int i = 0;
123: for (Iterator itr = classMetaData.getNonAutoKeyFieldNames()
124: .keySet().iterator(); itr.hasNext();) {
125: ++i;
126: String fieldName = (String) itr.next();
127: Object value = MoldingService.getInstanceValue(object,
128: classMetaData, fieldName);
129: DataTranslator.setAppObject(pstmt, i, value, classMetaData
130: .getSqlType(fieldName), dataSource.getEngineType());
131: }
132: for (Iterator itr = classMetaData.getAttributes().keySet()
133: .iterator(); itr.hasNext();) {
134: ++i;
135: String fieldName = (String) itr.next();
136: Object value = MoldingService.getInstanceValue(object,
137: classMetaData, fieldName);
138: DataTranslator.setAppObject(pstmt, i, value, classMetaData
139: .getSqlType(fieldName), dataSource.getEngineType());
140: }
141:
142: if (log.isInfoEnabled())
143: log.info("Executing the Prepared Statement\n" + sql);
144: dataSource.executeUpdate(pstmt);
145: }
146:
147: private static void executeAddWithStatement(IPersistent object,
148: DataSource dataSource) throws SQLException,
149: IllegalAccessException, InvocationTargetException,
150: IOException {
151: String sql = StatementHelper.getInsertStatementString(object,
152: dataSource.getEngineType());
153: if (log.isInfoEnabled())
154: log.info("Executing the SQL\n" + sql);
155: dataSource.executeUpdate(sql);
156: }
157:
158: /** Updates the Persistent object in the database.
159: * @param object The object to be updated.
160: * @param dataSource The DataSource in which the object will be updated.
161: * @throws SQLException if any database error occurs.
162: * @throws IllegalAccessException if the Persistent class is not accessible for introspection.
163: * @throws InvocationTargetException if the accessor method for the Persistent class throws an exception.
164: * @throws IOException if any error occurs while extracting the value for an attribute.
165: */
166: public static void executeUpdate(IPersistent object,
167: DataSource dataSource) throws SQLException,
168: IllegalAccessException, InvocationTargetException,
169: IOException {
170: if (usePreparedStatement(dataSource))
171: executeUpdateWithPreparedStatement(object, dataSource);
172: else
173: executeUpdateWithStatement(object, dataSource);
174: updatePersistentFlagsOnUpdate(object);
175: }
176:
177: private static void executeUpdateWithPreparedStatement(
178: IPersistent object, DataSource dataSource)
179: throws SQLException, IllegalAccessException,
180: InvocationTargetException {
181: ClassMetaData classMetaData = ConfigurationService
182: .getInstance().getMetaData(
183: PersistentInstanceFactory
184: .getActualPersistentClass(object)
185: .getName());
186: String sql = PreparedStatementHelper
187: .getUpdatePreparedStatementString(classMetaData);
188: PreparedStatement pstmt = dataSource.getPreparedStatement(sql);
189:
190: int i = 0;
191: for (Iterator itr = classMetaData.getAttributes().keySet()
192: .iterator(); itr.hasNext();) {
193: ++i;
194: String fieldName = (String) itr.next();
195: Object value = MoldingService.getInstanceValue(object,
196: classMetaData, fieldName);
197: DataTranslator.setAppObject(pstmt, i, value, classMetaData
198: .getSqlType(fieldName), dataSource.getEngineType());
199: }
200: for (Iterator itr = classMetaData.getAllKeyFieldNames()
201: .keySet().iterator(); itr.hasNext();) {
202: ++i;
203: String fieldName = (String) itr.next();
204: Object value = MoldingService.getInstanceValue(object,
205: classMetaData, fieldName);
206: DataTranslator.setAppObject(pstmt, i, value, classMetaData
207: .getSqlType(fieldName), dataSource.getEngineType());
208: }
209:
210: if (log.isInfoEnabled())
211: log.info("Executing the Prepared Statement\n" + sql);
212: dataSource.executeUpdate(pstmt);
213: }
214:
215: private static void executeUpdateWithStatement(IPersistent object,
216: DataSource dataSource) throws SQLException,
217: IllegalAccessException, InvocationTargetException,
218: IOException {
219: String sql = StatementHelper.getUpdateStatementString(object,
220: dataSource.getEngineType());
221: if (log.isInfoEnabled())
222: log.info("Executing the SQL\n" + sql);
223: dataSource.executeUpdate(sql);
224: }
225:
226: /** Deletes the Persistent object from the database.
227: * @param object The object to be deleted.
228: * @param dataSource The DataSource from which the object will be deleted.
229: * @throws SQLException if any database error occurs.
230: * @throws IllegalAccessException if the Persistent class is not accessible for introspection.
231: * @throws InvocationTargetException if the accessor method for the Persistent class throws an exception.
232: * @throws IOException if any error occurs while extracting the value for an attribute.
233: */
234: public static void executeDelete(IPersistent object,
235: DataSource dataSource) throws SQLException,
236: IllegalAccessException, InvocationTargetException,
237: IOException {
238: if (usePreparedStatement(dataSource))
239: executeDeleteWithPreparedStatement(object, dataSource);
240: else
241: executeDeleteWithStatement(object, dataSource);
242: updatePersistentFlagsOnDelete(object);
243: }
244:
245: private static void executeDeleteWithPreparedStatement(
246: IPersistent object, DataSource dataSource)
247: throws SQLException, IllegalAccessException,
248: InvocationTargetException {
249: ClassMetaData classMetaData = ConfigurationService
250: .getInstance().getMetaData(
251: PersistentInstanceFactory
252: .getActualPersistentClass(object)
253: .getName());
254: String sql = PreparedStatementHelper
255: .getDeletePreparedStatementString(classMetaData);
256: PreparedStatement pstmt = dataSource.getPreparedStatement(sql);
257:
258: int i = 0;
259: for (Iterator itr = classMetaData.getAllKeyFieldNames()
260: .keySet().iterator(); itr.hasNext();) {
261: ++i;
262: String fieldName = (String) itr.next();
263: Object value = MoldingService.getInstanceValue(object,
264: classMetaData, fieldName);
265: DataTranslator.setAppObject(pstmt, i, value, classMetaData
266: .getSqlType(fieldName), dataSource.getEngineType());
267: }
268:
269: if (log.isInfoEnabled())
270: log.info("Executing the Prepared Statement\n" + sql);
271: dataSource.executeUpdate(pstmt);
272: }
273:
274: private static void executeDeleteWithStatement(IPersistent object,
275: DataSource dataSource) throws SQLException,
276: IllegalAccessException, InvocationTargetException,
277: IOException {
278: String sql = StatementHelper.getDeleteStatementString(object,
279: dataSource.getEngineType());
280: if (log.isInfoEnabled())
281: log.info("Executing the SQL\n" + sql);
282: dataSource.executeUpdate(sql);
283: }
284:
285: /** Executes the query based on the Criteria object.
286: * @param criteria The input Criteria.
287: * @param dataSource The DataSource against which the query is to be executed.
288: * @throws IOException if any error occurs while extracting the String from the criteria.
289: * @throws SQLException if any database error occurs.
290: * @throws PostLoadFailedException if any error is thrown in the PostLoad trigger of the persistent object.
291: * @throws DataSourceCursorRuntimeException if any error occurs while molding the row into the Persistent object.
292: * @return a Collection of Persistent objects as a result of the query.
293: */
294: public static Collection executeQuery(Criteria criteria,
295: DataSource dataSource) throws IOException, SQLException,
296: PostLoadFailedException, DataSourceCursorRuntimeException {
297: ClassMetaData classMetaData = ConfigurationService
298: .getInstance().getMetaData(criteria.getTable());
299: String sql = QueryStatementHelper.getStatement(criteria,
300: dataSource.getEngineType());
301: if (log.isInfoEnabled())
302: log.info("Executing the SQL:\n" + sql);
303:
304: return dataSource
305: .executeQuery(
306: sql,
307: classMetaData,
308: criteria,
309: (criteria.getLocking() == Criteria.LOCKING_PARANOID ? QUERY_TIMEOUT_FOR_LOCKING
310: : 0));
311: }
312:
313: /** Executes the Stored Procedure.
314: * @param sp The Stored Procedure to execute.
315: * @param dataSource The DataSource against which the Stored Procedure is to be executed.
316: * @throws SQLException if any database error occurs.
317: * @throws IllegalAccessException if the class is not accessible.
318: * @throws InvocationTargetException if the accessor/mutator method for the Stored Procedure throws an exception.
319: * @throws IOException if any error occurs in reading the data from the database.
320: */
321: public static void executeStoredProcedure(IStoredProcedure sp,
322: DataSource dataSource) throws SQLException,
323: IllegalAccessException, InvocationTargetException,
324: IOException {
325: // get a CallableStatement
326: CallableStatement stmt = dataSource.getCallableStatement(sp
327: .prepareCall());
328:
329: String[] parameters = sp.getParameters();
330: String[] paramSqlTypes = sp.getParamSqlTypes();
331: int[] paramDirections = sp.getParamDirections();
332: Class clazz = PersistentInstanceFactory
333: .getActualPersistentClass(sp);
334:
335: for (int i = 0; i < parameters.length; i++) {
336: // set the input parameters
337: if (paramDirections[i] == IStoredProcedure.IN
338: || paramDirections[i] == IStoredProcedure.BOTH) {
339: Object value = Introspection.getAccessorMethod(clazz,
340: parameters[i], null).invoke(sp, new Object[0]);
341: DataTranslator.setAppObject(stmt, i + 1, value,
342: paramSqlTypes[i], dataSource.getEngineType());
343: }
344:
345: // register the out parameters
346: if (paramDirections[i] == IStoredProcedure.OUT
347: || paramDirections[i] == IStoredProcedure.BOTH) {
348: int sqlType = TypeDefs.getSqlType(paramSqlTypes[i],
349: dataSource.getEngineType());
350: stmt.registerOutParameter(i + 1, sqlType);
351: }
352: }
353:
354: // execute the stored procedure
355: if (log.isInfoEnabled())
356: log.info("Executing the Stored Procedure "
357: + clazz.getName() + " with initial state:\n"
358: + ((Object) sp).toString());
359: stmt.execute();
360:
361: // set the output parameters
362: for (int i = 0; i < parameters.length; i++) {
363: if (paramDirections[i] == IStoredProcedure.OUT
364: || paramDirections[i] == IStoredProcedure.BOTH) {
365: Object value = DataTranslator.getAppObject(stmt, i + 1,
366: paramSqlTypes[i], dataSource.getEngineType());
367: Introspection.getMutatorMethod(clazz, parameters[i],
368: null).invoke(sp, new Object[] { value });
369: }
370: }
371:
372: if (log.isInfoEnabled())
373: log.info("Contents of the Stored Procedure "
374: + clazz.getName() + " after execution:\n"
375: + ((Object) sp).toString());
376: }
377:
378: /** Locks the underlying database row of the Persistent object.
379: * @param object The object to be locked.
380: * @param dataSource The DataSource in which the object will be locked.
381: * @throws SQLException if any database error occurs.
382: * @throws IllegalAccessException if the Persistent class is not accessible for introspection.
383: * @throws InvocationTargetException if the accessor method for the Persistent class throws an exception.
384: * @throws IOException if any error occurs while extracting the value for an attribute.
385: */
386: public static void executeLock(IPersistent object,
387: DataSource dataSource) throws SQLException,
388: IllegalAccessException, InvocationTargetException,
389: IOException {
390: if (usePreparedStatement(dataSource))
391: executeLockWithPreparedStatement(object, dataSource);
392: else
393: executeLockWithStatement(object, dataSource);
394: updatePersistentFlagsOnLock(object);
395: }
396:
397: private static void executeLockWithPreparedStatement(
398: IPersistent object, DataSource dataSource)
399: throws SQLException, IllegalAccessException,
400: InvocationTargetException {
401: ClassMetaData classMetaData = ConfigurationService
402: .getInstance().getMetaData(
403: PersistentInstanceFactory
404: .getActualPersistentClass(object)
405: .getName());
406: String sql = PreparedStatementHelper
407: .getLockPreparedStatementString(classMetaData,
408: dataSource.getEngineType());
409: PreparedStatement pstmt = dataSource.getPreparedStatement(sql);
410:
411: int i = 0;
412: for (Iterator itr = classMetaData.getAllKeyFieldNames()
413: .keySet().iterator(); itr.hasNext();) {
414: ++i;
415: String fieldName = (String) itr.next();
416: Object value = MoldingService.getInstanceValue(object,
417: classMetaData, fieldName);
418: DataTranslator.setAppObject(pstmt, i, value, classMetaData
419: .getSqlType(fieldName), dataSource.getEngineType());
420: }
421:
422: if (log.isInfoEnabled())
423: log.info("Executing the Prepared Statement\n" + sql);
424: // Added for MS-Sql-Server as 'NO-WAIT" is not implemented like in Oracle
425: pstmt.setQueryTimeout(QUERY_TIMEOUT_FOR_LOCKING);
426: ResultSet rs = pstmt.executeQuery();
427: rs.next();
428: rs.close();
429: }
430:
431: private static void executeLockWithStatement(IPersistent object,
432: DataSource dataSource) throws SQLException,
433: IllegalAccessException, InvocationTargetException,
434: IOException {
435: String sql = StatementHelper.getLockStatementString(object,
436: dataSource.getEngineType());
437: if (log.isInfoEnabled())
438: log.info("Executing the SQL\n" + sql);
439: Statement statement = dataSource.getStatement();
440: // Added for MS-Sql-Server as 'NO-WAIT" is not implemented like in Oracle
441: statement.setQueryTimeout(QUERY_TIMEOUT_FOR_LOCKING);
442: ResultSet rs = statement.executeQuery(sql);
443: rs.next();
444: rs.close();
445: dataSource.closeStatement(statement);
446: }
447:
448: /** This method sets the appropriate flags on a Persistent object after being added to the database.
449: * @param object the Persistent object.
450: */
451: public static void updatePersistentFlagsOnAdd(IPersistent object) {
452: object.setDatabaseOccurence(true);
453: object.setLocked(false);
454: object.setModified(false);
455: object.setQueued(false);
456: }
457:
458: /** This method sets the appropriate flags on a Persistent object after being updated to the database.
459: * @param object the Persistent object.
460: */
461: public static void updatePersistentFlagsOnUpdate(IPersistent object) {
462: object.setLocked(false);
463: object.setModified(false);
464: object.setQueued(false);
465: }
466:
467: /** This method sets the appropriate flags on a Persistent object after being deleted from the database.
468: * @param object the Persistent object.
469: */
470: public static void updatePersistentFlagsOnDelete(IPersistent object) {
471: object.setDatabaseOccurence(false);
472: object.setLocked(false);
473: object.setModified(false);
474: object.setQueued(false);
475: }
476:
477: /** This method sets the appropriate flags on a Persistent object after being retrieved from the database.
478: * @param object the Persistent object.
479: * @param criteria the Criteria used for retrieving the object.
480: */
481: public static void updatePersistentFlagsOnQuery(IPersistent object,
482: Criteria criteria) {
483: object.setUOW(criteria.getUow());
484: object.setDatabaseOccurence(true);
485: object.setLocking(criteria.getLocking());
486: object
487: .setLocked(criteria.getLocking() == Criteria.LOCKING_PARANOID ? true
488: : false);
489: }
490:
491: /** This method sets the appropriate flags on a Persistent object after the underlying row is locked in the database.
492: * @param object the Persistent object.
493: */
494: public static void updatePersistentFlagsOnLock(IPersistent object) {
495: object.setLocked(true);
496: }
497:
498: private static boolean usePreparedStatement(DataSource dataSource) {
499: Boolean bool = dataSource.getUsePreparedStatement();
500: return bool != null ? bool.booleanValue() : false;
501: }
502: }
|