1 /***
2 * License Agreement.
3 *
4 * JSPA (POJO-SP)
5 *
6 * Copyright (C) 2009 HRX Pty Ltd
7 *
8 * This file is part of JSPA.
9 *
10 * JSPA is free software: you can redistribute it and/or modify it under the
11 * terms of the GNU Lesser General Public License version 3 as published by the
12 * Free Software Foundation.
13 *
14 * JSPA is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with JSPA. If not, see <http://www.gnu.org/licenses/>.
20 */
21 package com.hrx.rasp.util.dao.operation;
22
23 import java.io.InputStream;
24 import java.lang.reflect.Field;
25 import java.lang.reflect.InvocationTargetException;
26 import java.sql.Array;
27 import java.sql.Blob;
28 import java.sql.CallableStatement;
29 import java.sql.Clob;
30 import java.sql.Connection;
31 import java.sql.SQLException;
32 import java.sql.Timestamp;
33 import java.sql.Types;
34 import java.util.Collection;
35 import java.util.Set;
36
37 import org.apache.log4j.Logger;
38
39 import com.hrx.rasp.util.dao.ExpressionBuilder;
40 import com.hrx.rasp.util.dao.NullValue;
41 import com.hrx.rasp.util.dao.OracleHelper;
42 import com.hrx.rasp.util.dao.StoredProcUtility;
43 import com.hrx.rasp.util.dao.annotations.IN;
44 import com.hrx.rasp.util.dao.annotations.INOUT;
45 import com.hrx.rasp.util.dao.annotations.OUT;
46 import com.hrx.rasp.util.dao.annotations.Secret;
47 import com.hrx.rasp.util.dao.exception.StoredProcedureException;
48 import com.hrx.rasp.util.dao.exception.StoredProcedurePrepareException;
49 import com.hrx.rasp.util.dao.exception.StoredProcedureReservedIndexException;
50 import com.hrx.rasp.util.dao.metadata.MetadataHelper;
51 import com.hrx.rasp.util.dao.metadata.StoredProcField;
52 import com.hrx.rasp.util.dao.metadata.StoredProcMetaDataImpl;
53 import com.hrx.rasp.util.dao.metadata.StoredProcPkMetaData;
54
55 /***
56 * @author dan.stoica <dan.stoica@acslink.net.au>
57 *
58 * Description for StatementBuilder class
59 *
60 */
61 public final class StatementBuilder {
62 private final static Logger log = Logger.getLogger(StatementBuilder.class);
63
64 public static final String PK_NOT_FOUND = "Primary key not found";
65 public static final String IN_PARAMETERS_OR_PK_NOT_FOUND = "IN parameters or Primary key not found";
66 static final String PARAMETER_INDEX_IS_RESERVED = "Parameter index is reserved. Parameter name:";
67 public static final String IN_PARAM_NOT_FOUND_FOR_SELECT_SP = "Parameters IN not found for Select Stored Procedure";
68
69 /***
70 * Description for setObject method
71 *
72 * @param st
73 * CallableStatement
74 * @param index
75 * int
76 * @param arg
77 * Object
78 * @throws SQLException
79 */
80 protected static void setObject(final CallableStatement st, int index,
81 final Object arg) throws SQLException {
82 if (arg instanceof NullValue)
83 setNullValue(st, index, (NullValue) arg);
84 else
85 st.setObject(index, arg);
86 }
87
88 /***
89 * Register a given list of DAO properties as stored procedure OUT
90 * parameters
91 *
92 * @param st
93 * @param allFields
94 * @throws SQLException
95 */
96 protected static void registerOutParameters(final CallableStatement st,
97 final Collection<StoredProcField<OUT>> outFields)
98 throws SQLException {
99 for (StoredProcField<OUT> field : outFields) {
100 OUT parameter = field.getParameter();
101 registerOutParameters(st, parameter.index(), parameter.sqlType(),
102 field.getField(), parameter.sqlArrayName());
103 }
104 }
105
106 /***
107 * Register a given list of DAO properties as stored procedure OUT
108 * parameters
109 *
110 * @param st
111 * @param allFields
112 * @throws SQLException
113 */
114 protected static void registerInOutParameters(final CallableStatement st,
115 final Collection<StoredProcField<INOUT>> outFields)
116 throws SQLException {
117 for (StoredProcField<INOUT> field : outFields) {
118 INOUT parameter = field.getParameter();
119 registerOutParameters(st, parameter.index(), parameter.sqlType(),
120 field.getField(), parameter.sqlArrayName());
121 }
122 }
123
124 /***
125 * Register a DAO property as OUT stored procedure parameter
126 *
127 * @param st
128 * @param index
129 * @param sqlType
130 * @param field
131 * @param sqlArrayName
132 * @throws SQLException
133 */
134 private static void registerOutParameters(final CallableStatement st,
135 int index, int sqlType, Field field, String sqlArrayName)
136 throws SQLException {
137 Class<?> javaType = field.getType();
138
139 if (sqlType == Types.NULL) {
140
141 sqlType = StoredProcUtility.getSQLType(javaType);
142 }
143
144 if (sqlType == Types.ARRAY) {
145 if (log.isDebugEnabled()) {
146 log.debug("PREPARE: RegisterOutParameter parameter; index="
147 + index + ", type: 'ARRAY', arrayName=" + sqlArrayName);
148 }
149 st.registerOutParameter(index, Types.ARRAY, sqlArrayName);
150 } else {
151
152
153 if (sqlType == Types.BOOLEAN
154 && st.getClass().toString().toLowerCase()
155 .contains("oracle")) {
156 if (log.isDebugEnabled()) {
157 log
158 .debug("PREPARE: RegisterOutParameter BOOLEAN parameter for Oracle; index="
159 + index + ", as type:'Types.VARCHAR'");
160 }
161 st.registerOutParameter(index, Types.VARCHAR);
162 } else {
163 if (log.isDebugEnabled()) {
164 log.debug("RegisterOutParameter parameter; index=" + index
165 + ", type:'" + sqlType + "'");
166 }
167 st.registerOutParameter(index, sqlType);
168 }
169 }
170 }
171
172 /***
173 * Description for setObject method
174 *
175 * @param st
176 * CallableStatement
177 * @param inParameters
178 * @param inoutParameters
179 * @param reservedIndex
180 * - the first parameters can be reserved for error code , result
181 * set, etc...
182 * @throws SQLException
183 * @throws StoredProcedureReservedIndexException
184 * @throws StoredProcedurePrepareException
185 */
186 protected static void setInParameters(final CallableStatement st,
187 Object spBean, int reservedIndex,
188 final Collection<StoredProcField<IN>> inParameters)
189 throws SQLException, StoredProcedureReservedIndexException,
190 StoredProcedurePrepareException {
191 for (StoredProcField<IN> param : inParameters) {
192 try {
193 Object value = MetadataHelper.getPropertyValue(
194 param.getField(), spBean);
195 int index = param.getParameter().index();
196 String arrayName = param.getParameter().sqlArrayName();
197
198 if (log.isDebugEnabled()) {
199 StringBuilder msg = new StringBuilder(
200 "PREPARE: Set IN parameter. Field name="
201 + param.getName() + ", SP index=" + index
202 + ", value=");
203 msg = param.getField().isAnnotationPresent(Secret.class) ? msg
204 .append("*****")
205 : msg.append(value);
206 if (value instanceof Collection<?>)
207 msg.append(", arrayName=" + arrayName);
208 log.debug(msg);
209 }
210
211 setInParameter(st, param, value, arrayName, index,
212 reservedIndex);
213 } catch (StoredProcedureReservedIndexException e) {
214 throw e;
215 } catch (Throwable e) {
216 throw new StoredProcedurePrepareException(e);
217 }
218 }
219 }
220
221 protected static void setInOutParameters(final CallableStatement st,
222 Object spBean, int reservedIndex,
223 final Collection<StoredProcField<INOUT>> inParameters)
224 throws SQLException, StoredProcedureReservedIndexException,
225 StoredProcedurePrepareException {
226 for (StoredProcField<INOUT> param : inParameters) {
227 try {
228 Object value = MetadataHelper.getPropertyValue(
229 param.getField(), spBean);
230 int index = param.getParameter().index();
231 String arrayName = param.getParameter().sqlArrayName();
232
233 if (log.isDebugEnabled()) {
234 StringBuilder msg = new StringBuilder(
235 "PREPARE: Set INOUT parameter. Field name="
236 + param.getName() + ", SP index=" + index
237 + ", value=");
238 msg = param.getField().isAnnotationPresent(Secret.class) ? msg
239 .append("*****")
240 : msg.append(value);
241 if (value instanceof Collection<?>)
242 msg.append(", arrayName=" + arrayName);
243 log.debug(msg);
244 }
245
246 setInParameter(st, param, value, arrayName, index,
247 reservedIndex);
248 } catch (StoredProcedureReservedIndexException e) {
249 throw e;
250 } catch (Throwable e) {
251 throw new StoredProcedurePrepareException(e);
252 }
253 }
254 }
255
256 /***
257 * Description for setObject method
258 *
259 * @param st
260 * CallableStatement
261 * @param index
262 * int
263 * @param arg
264 * Object
265 * @throws SQLException
266 * @throws StoredProcedureReservedIndexException
267 * @throws StoredProcedurePrepareException
268 */
269 protected static void setInParameter(final CallableStatement st,
270 final StoredProcField<?> parameter, Object value, String arrayName,
271 int index, int minIndex) throws SQLException,
272 StoredProcedureReservedIndexException,
273 StoredProcedurePrepareException {
274 Class<?> objectType = value.getClass();
275
276
277 if (index <= minIndex) {
278 throw new StoredProcedureReservedIndexException(
279 StatementBuilder.PARAMETER_INDEX_IS_RESERVED
280 + parameter.getName());
281 }
282 if (value instanceof NullValue) {
283 setNullValue(st, index, (NullValue) value);
284 } else if (value instanceof String) {
285 st.setString(index, (String) value);
286 } else if (value instanceof Integer || objectType == Integer.TYPE) {
287 st.setInt(index, (Integer) value);
288 } else if (value instanceof Long || objectType == Long.TYPE) {
289 st.setLong(index, (Long) value);
290 } else if (value instanceof Double || objectType == Double.TYPE) {
291 st.setDouble(index, (Double) value);
292 } else if (value instanceof Float || objectType == Float.TYPE) {
293 st.setFloat(index, (Float) value);
294 } else if (value instanceof Short || objectType == Short.TYPE) {
295 st.setShort(index, (Short) value);
296 } else if (value instanceof Boolean || objectType == Boolean.TYPE) {
297
298
299 if (st.getClass().toString().toLowerCase().contains("oracle")) {
300 st.setString(index, Boolean.toString((Boolean) value));
301 } else {
302 st.setBoolean(index, (Boolean) value);
303 }
304 } else if (value instanceof Timestamp) {
305 st.setTimestamp(index, (Timestamp) value);
306 } else if (value instanceof java.util.Date) {
307 java.sql.Date date = new java.sql.Date((((java.util.Date) value))
308 .getTime());
309 st.setDate(index, date);
310 } else if (value instanceof java.sql.Date) {
311 st.setDate(index, (java.sql.Date) value);
312 } else if (value instanceof Clob) {
313 st.setClob(index, (Clob) value);
314 } else if (value instanceof Blob) {
315 st.setBlob(index, (Blob) value);
316 } else if (value instanceof Collection<?>) {
317 setOracleArray(st, index, (Collection<?>) value, arrayName);
318 } else if (value instanceof InputStream) {
319 st.setBinaryStream(index, (InputStream) value);
320 }
321
322 }
323
324 /***
325 * Description for setNullValue method
326 *
327 * @param st
328 * CallableStatement
329 * @param index
330 * int
331 * @param nullValue
332 * NullValue
333 * @throws SQLException
334 */
335 public static void setNullValue(final CallableStatement st,
336 final int index, final NullValue nullValue) throws SQLException {
337 st.setNull(index, nullValue.getSQLType());
338 }
339
340 private static void setOracleArray(final CallableStatement stmt, int index,
341 Collection<?> values, String arrayType)
342 throws StoredProcedurePrepareException, SQLException {
343 if ("N/A".equals(arrayType)) {
344 throw new StoredProcedurePrepareException(
345 "Invalid database ARRAY TYPE name, for IN PARAMETER at index: "
346 + index);
347 }
348 Connection conn = stmt.getConnection();
349 Array array = OracleHelper.createOracleARRAY(arrayType, values, conn);
350 stmt.setArray(index, array);
351 }
352
353
354 /***
355 * Build the CallableStatement object for the desired insert query
356 *
357 * @param conn
358 * The database connection
359 * @param storedProc
360 * The stored procedure
361 * @param pkClass
362 * the expected class of the primary key
363 * @param args
364 * The input values
365 * @return The callable statement that forms the insert query
366 * @throws StoredProcedureReservedIndexException
367 * @throws StoredProcedureException
368 */
369 protected static CallableStatement getInsertStatement(
370 final Connection conn, final String storedProc,
371 final Collection<StoredProcField<IN>> inParameters,
372 final Collection<StoredProcField<?>> pkParameters, Object spBean,
373 Object pkBean, boolean isEmbeddedId)
374 throws StoredProcedurePrepareException,
375 StoredProcedureReservedIndexException {
376 try {
377 inParameters.removeAll(pkParameters);
378 if (inParameters.size() == 0
379 || (isEmbeddedId && pkParameters.size() == 0)) {
380 throw new StoredProcedurePrepareException(
381 StatementBuilder.IN_PARAMETERS_OR_PK_NOT_FOUND);
382 }
383
384 int params = pkParameters.size() + inParameters.size();
385
386 String stmtExpression = ExpressionBuilder.getInsert(storedProc,
387 params);
388 CallableStatement st = conn.prepareCall(stmtExpression);
389
390
391 st.registerOutParameter(1, Types.INTEGER);
392 st.registerOutParameter(2, Types.VARCHAR);
393
394
395
396
397
398 setInParameters(st, spBean, 2, inParameters);
399
400
401
402 for (StoredProcField<?> param : pkParameters) {
403 try {
404 if (!isEmbeddedId) {
405 pkBean = spBean;
406 }
407 setAndRegistreParameter(pkBean, st, param);
408 } catch (StoredProcedureReservedIndexException e) {
409 throw e;
410 } catch (Throwable e) {
411 throw new StoredProcedurePrepareException(e);
412 }
413 }
414 return st;
415 } catch (SQLException se) {
416 throw new StoredProcedurePrepareException(se);
417 }
418 }
419
420 /***
421 * @param pkBean
422 * @param st
423 * @param param
424 * @throws NoSuchMethodException
425 * @throws IllegalAccessException
426 * @throws InvocationTargetException
427 * @throws SQLException
428 * @throws StoredProcedureReservedIndexException
429 */
430 private static void setAndRegistreParameter(Object pkBean,
431 CallableStatement st, StoredProcField<?> param)
432 throws NoSuchMethodException, IllegalAccessException,
433 InvocationTargetException, SQLException,
434 StoredProcedureReservedIndexException,
435 StoredProcedurePrepareException {
436 Object value = MetadataHelper
437 .getPropertyValue(param.getField(), pkBean);
438 if (param.getParameter() instanceof INOUT) {
439 INOUT p = (INOUT) param.getParameter();
440 int index = p.index();
441 setInParameter(st, param, value, p.sqlArrayName(), index, 2);
442 registerOutParameters(st, index, p.sqlType(), param.getField(), p
443 .sqlArrayName());
444 } else if (param.getParameter() instanceof IN) {
445 IN p = (IN) param.getParameter();
446 int index = p.index();
447 setInParameter(st, param, value, p.sqlArrayName(), index, 2);
448 } else if (param.getParameter() instanceof OUT) {
449 OUT p = (OUT) param.getParameter();
450 int index = p.index();
451 registerOutParameters(st, index, p.sqlType(), param.getField(), p
452 .sqlArrayName());
453 }
454 }
455
456 /***
457 * Build the CallableStatement object for the desired update query
458 *
459 * @param conn
460 * The database connection
461 * @param storedProc
462 * The stored procedure
463 * @param args
464 * The input values
465 * @return The callable statement that forms the update query
466 * @throws StoredProcedureReservedIndexException
467 * @throws StoredProcedureException
468 */
469 protected static CallableStatement getUpdateStatement(
470 final Connection conn, final String storedProc,
471 final Collection<StoredProcField<IN>> inParameters,
472 final Collection<StoredProcField<IN>> pkParameters,
473 final Object spBean, final Object pkBean, boolean isEmbeddedId)
474 throws StoredProcedurePrepareException,
475 StoredProcedureReservedIndexException {
476 try {
477 if (inParameters.size() == 0 || pkParameters.size() == 0) {
478 throw new StoredProcedurePrepareException(
479 StatementBuilder.IN_PARAMETERS_OR_PK_NOT_FOUND);
480 }
481
482 int totalParams = inParameters.size();
483
484 if (isEmbeddedId) {
485 totalParams = totalParams + pkParameters.size();
486 }
487
488 String stmtExpression = ExpressionBuilder.getUpdate(storedProc,
489 totalParams);
490 CallableStatement st = conn.prepareCall(stmtExpression);
491
492
493 st.registerOutParameter(1, Types.INTEGER);
494 st.registerOutParameter(2, Types.VARCHAR);
495
496
497
498 setInParameters(st, spBean, 2, inParameters);
499
500
501
502 if (isEmbeddedId) {
503 setInParameters(st, pkBean, 2, pkParameters);
504 }
505
506 return st;
507 } catch (SQLException se) {
508 throw new StoredProcedurePrepareException(se);
509 }
510 }
511
512 /***
513 * Build the CallableStatement object for the desired update query
514 *
515 * @param conn
516 * The database connection
517 * @param storedProc
518 * The stored procedure
519 * @param args
520 * The input values
521 * @return The callable statement that forms the update query
522 * @throws StoredProcedureReservedIndexException
523 * @throws StoredProcedureException
524 */
525 protected static CallableStatement getDeleteStatement(
526 final Connection conn, final String storedProc,
527 final Collection<StoredProcField<IN>> pkParameters,
528 final Object pkBean) throws StoredProcedurePrepareException,
529 StoredProcedureReservedIndexException {
530 try {
531 if (pkParameters.size() == 0) {
532 throw new StoredProcedurePrepareException(
533 StatementBuilder.PK_NOT_FOUND);
534 }
535 String stmtExpression = ExpressionBuilder.getUpdate(storedProc,
536 pkParameters.size());
537 CallableStatement st = conn.prepareCall(stmtExpression);
538
539
540 st.registerOutParameter(1, Types.INTEGER);
541 st.registerOutParameter(2, Types.VARCHAR);
542
543
544
545 setInParameters(st, pkBean, 2, pkParameters);
546 return st;
547 } catch (SQLException se) {
548 throw new StoredProcedurePrepareException(se);
549 }
550 }
551
552 /***
553 * Build the CallableStatement object for the desired select query
554 *
555 * @param conn
556 * The database connection
557 * @param storedProc
558 * The stored procedure
559 * @param inParameters
560 * The input values
561 * @return The CallableStatement that forms the select query
562 * @throws StoredProcedureReservedIndexException
563 * @throws StoredProcedureException
564 */
565 public static CallableStatement getSelectStatement(final Connection conn,
566 final String storedProc,
567 final Set<StoredProcField<IN>> inParameters,
568 final Set<StoredProcField<IN>> pkInParameters, Object spBean,
569 Object pkBean, boolean isEmbeddedId)
570 throws StoredProcedurePrepareException,
571 StoredProcedureReservedIndexException {
572 try {
573
574
575 int argc = inParameters.size();
576 if (isEmbeddedId) {
577 argc = argc + pkInParameters.size();
578 }
579
580 String stmtExpression = ExpressionBuilder.getSelect(storedProc,
581 argc);
582 CallableStatement st = conn.prepareCall(stmtExpression);
583
584
585 st.registerOutParameter(1, OracleHelper.ORACLE_CURSOR);
586 st.registerOutParameter(2, Types.INTEGER);
587 st.registerOutParameter(3, Types.VARCHAR);
588
589
590
591 setInParameters(st, spBean, 3, inParameters);
592 if (isEmbeddedId) {
593 setInParameters(st, pkBean, 3, pkInParameters);
594 }
595 return st;
596 } catch (SQLException se) {
597 throw new StoredProcedurePrepareException(se);
598 }
599 }
600
601 /***
602 * Build the CallableStatement object for the desired select query
603 *
604 * @param conn
605 * The database connection
606 * @param storedProc
607 * The stored procedure
608 * @param inParameters
609 * The input values
610 * @return The CallableStatement that forms the select query
611 * @throws StoredProcedureReservedIndexException
612 * @throws StoredProcedureException
613 */
614 public static CallableStatement getFindStatement(final Connection conn,
615 final String storedProc,
616 final Set<StoredProcField<IN>> pkParameters,
617 final Set<StoredProcField<OUT>> outParameters, Object spBean,
618 Object pkBean, boolean isEmbeddedId)
619 throws StoredProcedurePrepareException,
620 StoredProcedureReservedIndexException {
621 try {
622 if (pkParameters.size() == 0) {
623 throw new StoredProcedurePrepareException(
624 IN_PARAM_NOT_FOUND_FOR_SELECT_SP);
625 }
626 int argc = pkParameters.size() + outParameters.size();
627 String stmtExpression = ExpressionBuilder.getFind(storedProc, argc);
628 CallableStatement st = conn.prepareCall(stmtExpression);
629
630
631 st.registerOutParameter(1, Types.INTEGER);
632 st.registerOutParameter(2, Types.VARCHAR);
633
634
635
636 if (isEmbeddedId) {
637 setInParameters(st, pkBean, 2, pkParameters);
638 } else {
639 setInParameters(st, spBean, 2, pkParameters);
640 }
641
642 registerOutParameters(st, outParameters);
643
644 return st;
645 } catch (SQLException se) {
646 throw new StoredProcedurePrepareException(se);
647 }
648 }
649
650 /***
651 * Build the CallableStatement object for the desired function call
652 *
653 * @param conn
654 * @param spBean
655 * @param meta
656 * @return
657 * @throws StoredProcedurePrepareException
658 * @throws StoredProcedureReservedIndexException
659 */
660 public static CallableStatement getFunctionCall(final Connection conn,
661 Object spBean, Object pkBean, final StoredProcMetaDataImpl meta,
662 final StoredProcPkMetaData embeddedMeta, boolean embedded)
663 throws StoredProcedurePrepareException,
664 StoredProcedureReservedIndexException {
665 return getStoredProcedureCall(conn, spBean, pkBean, meta, embeddedMeta,
666 embedded, true);
667 }
668
669 public static CallableStatement getStoredProcedureCall(
670 final Connection conn, Object spBean, Object pkBean,
671 final StoredProcMetaDataImpl meta,
672 final StoredProcPkMetaData embeddedMeta, boolean embedded,
673 boolean isFunction) throws StoredProcedurePrepareException,
674 StoredProcedureReservedIndexException {
675 final String storedProc = meta.getStoredProcName();
676 final Set<StoredProcField<IN>> inParameters = meta.getInParameters();
677 final Set<StoredProcField<OUT>> ourParams = meta.getOutParameters();
678 final Set<StoredProcField<INOUT>> inoutParameters = meta
679 .getInOutParameters();
680 try {
681 int argc = inParameters.size() + ourParams.size()
682 + inoutParameters.size();
683 if (embedded) {
684 argc = argc + embeddedMeta.getInParameters().size()
685 + embeddedMeta.getOutParameters().size()
686 + embeddedMeta.getOutParameters().size();
687 }
688
689 CallableStatement st = null;
690 if (isFunction) {
691 st = conn.prepareCall(ExpressionBuilder.getFunctionCall(
692 storedProc, argc));
693 } else {
694 st = conn.prepareCall(ExpressionBuilder.getStoredProcedureCall(
695 storedProc, argc));
696 }
697
698 setInParameters(st, spBean, 0, inParameters);
699 setInOutParameters(st, spBean, 0, inoutParameters);
700 if (embedded) {
701 setInParameters(st, pkBean, 0, embeddedMeta.getInParameters());
702 setInOutParameters(st, pkBean, 0, embeddedMeta
703 .getInOutParameters());
704 }
705 registerOutParameters(st, ourParams);
706 registerInOutParameters(st, inoutParameters);
707
708 return st;
709
710 } catch (SQLException se) {
711 throw new StoredProcedurePrepareException(se);
712 }
713 }
714 }