001 /** 002 * Copyright (c) 2010 Yahoo! Inc. All rights reserved. 003 * Licensed under the Apache License, Version 2.0 (the "License"); 004 * you may not use this file except in compliance with the License. 005 * You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software 010 * distributed under the License is distributed on an "AS IS" BASIS, 011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 * See the License for the specific language governing permissions and 013 * limitations under the License. See accompanying LICENSE file. 014 */ 015 package org.apache.oozie.util.db; 016 017 import java.io.BufferedInputStream; 018 import java.io.BufferedOutputStream; 019 import java.io.ByteArrayOutputStream; 020 import java.io.IOException; 021 import java.sql.Blob; 022 import java.sql.Connection; 023 import java.sql.PreparedStatement; 024 import java.sql.ResultSet; 025 import java.sql.SQLException; 026 import java.sql.Timestamp; 027 import java.util.ArrayList; 028 import java.util.HashSet; 029 import java.util.List; 030 import java.util.Map; 031 import java.util.Set; 032 033 import org.apache.commons.logging.LogFactory; 034 import org.apache.oozie.util.XLog; 035 import org.apache.oozie.util.db.Schema.Column; 036 import org.apache.oozie.util.db.Schema.Table; 037 038 /** 039 * The <code>SqlStatement</code> is used to generate SQL Statements. Prepare the generated Statements and also to parse 040 * the resultSets 041 */ 042 public abstract class SqlStatement { 043 044 private static XLog log = new XLog(LogFactory.getLog(SqlStatement.class)); 045 protected boolean forUpdate = false; 046 047 /** 048 * <code>ResultSetReader</code> is used to parse the result set and gives methods for getting appropriate type of 049 * data given the column name 050 */ 051 public static class ResultSetReader { 052 final ResultSet rSet; 053 054 private ResultSetReader(ResultSet rSet) { 055 this.rSet = rSet; 056 } 057 058 /** 059 * Move the Result Set to next record 060 * 061 * @return true if there is a next record 062 * @throws SQLException 063 */ 064 public boolean next() throws SQLException { 065 return rSet.next(); 066 } 067 068 /** 069 * Close the Result Set 070 * 071 * @throws SQLException 072 */ 073 public void close() throws SQLException { 074 rSet.close(); 075 } 076 077 /** 078 * Get the Column data given its type and name 079 * 080 * @param <T> Type of the column 081 * @param clazz Class of the Type 082 * @param col Column name 083 * @return Column data 084 * @throws SQLException 085 */ 086 @SuppressWarnings("unchecked") 087 public <T> T get(Class<T> clazz, Column col) throws SQLException { 088 if (clazz.isAssignableFrom(col.getType())) { 089 return (T) rSet.getObject(col.asLabel()); 090 } 091 else { 092 if (String.class.equals(clazz)) { 093 return (T) ("" + rSet.getObject(col.asLabel())); 094 } 095 else { 096 throw new RuntimeException("Column Error : Actual Type [" + col.getType() + "]," + " Requested Type [" 097 + clazz + "] !!"); 098 } 099 } 100 } 101 102 /** 103 * Get the data for columns with blob type 104 * 105 * @param col Column name 106 * @return Column data 107 * @throws SQLException 108 */ 109 public byte[] getByteArray(Column col) throws SQLException { 110 byte[] bArray = null; 111 if (Blob.class.equals(col.getType())) { 112 BufferedInputStream bStream = new BufferedInputStream(rSet.getBinaryStream(col.asLabel())); 113 byte[] temp = new byte[1024]; 114 int num = 0; 115 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 116 BufferedOutputStream bOut = new BufferedOutputStream(baos); 117 try { 118 while ((num = bStream.read(temp)) != -1) { 119 bOut.write(temp, 0, num); 120 } 121 bOut.flush(); 122 bOut.close(); 123 } 124 catch (IOException e) { 125 throw new SQLException(e); 126 } 127 bArray = baos.toByteArray(); 128 } 129 else { 130 throw new RuntimeException("Column Error : Actual Type [" + col.getType() + "]," + " Requested Type [" 131 + Blob.class + "] !!"); 132 } 133 return bArray; 134 } 135 136 /** 137 * Get a String Column 138 * 139 * @param col Column Name 140 * @return Column data 141 * @throws SQLException 142 */ 143 public String getString(Column col) throws SQLException { 144 return get(String.class, col); 145 } 146 147 /** 148 * Get the Timestamp Column 149 * 150 * @param col Column name 151 * @return Column data 152 * @throws SQLException 153 */ 154 public Timestamp getTimestamp(Column col) throws SQLException { 155 return get(Timestamp.class, col); 156 } 157 158 /** 159 * Get the Boolean Column 160 * 161 * @param col Column name 162 * @return Column data 163 * @throws SQLException 164 */ 165 public Boolean getBoolean(Column col) throws SQLException { 166 return get(Boolean.class, col); 167 } 168 169 /** 170 * Get the Numeric data 171 * 172 * @param col Column name 173 * @return Column data 174 * @throws SQLException 175 */ 176 public Long getLong(Column col) throws SQLException { 177 return get(Long.class, col); 178 } 179 180 } 181 182 /** 183 * Construct the condition statement that will be used in the where clause 184 */ 185 public static class Condition { 186 187 final Column column; 188 protected StringBuilder sb = new StringBuilder("( "); 189 protected List<Object> values = new ArrayList<Object>(); 190 191 private Condition(Column column) { 192 this.column = column; 193 if (column != null) { 194 sb.append(column); 195 } 196 } 197 198 /** 199 * Return the Condition string 200 */ 201 public String toString() { 202 return sb.toString(); 203 } 204 } 205 206 /** 207 * NULL/NOT NULL Condition Generator 208 */ 209 static class Null extends Condition { 210 Null(boolean isInvert, Column column) { 211 super(column); 212 sb.append(" IS"); 213 sb.append(isInvert ? " NOT" : ""); 214 sb.append(" NULL "); 215 sb.append(" )"); 216 } 217 } 218 219 /** 220 * Generate condition statement for IS NULL 221 * 222 * @param column column name 223 * @return IS NULL condition statement 224 */ 225 public static Condition isNull(Column column) { 226 return new Null(false, column); 227 } 228 229 /** 230 * Generate condition statement for IS NOT NULL 231 * 232 * @param column column name 233 * @return IS NOT NULL condition statement 234 */ 235 public static Condition isNotNull(Column column) { 236 return new Null(true, column); 237 } 238 239 /** 240 * LIKE/NOT LIKE Condition Generator 241 */ 242 static class Like extends Condition { 243 Like(boolean isInvert, Column column, String value) { 244 super(column); 245 sb.append(isInvert ? " NOT" : "").append(" LIKE ").append("?").append(" )"); 246 values.add(value); 247 } 248 } 249 250 /** 251 * Generate condition statement for IS LIKE 252 * 253 * @param column column name 254 * @param value value to be checked 255 * @return IS LIKE condition statement 256 */ 257 public static Condition isLike(Column column, String value) { 258 return new Like(false, column, value); 259 } 260 261 /** 262 * Generates condition statement for IS NOT LIKE 263 * 264 * @param column column name 265 * @param value value to be checked 266 * @return IS NOT LIKE condition statement 267 */ 268 public static Condition isNotLike(Column column, String value) { 269 return new Like(true, column, value); 270 } 271 272 /** 273 * Comparison condition statement generator 274 */ 275 static class Compare extends Condition { 276 Compare(String oper, Column column, Object value) { 277 super(column); 278 if (value instanceof Column) { 279 sb.append(oper).append(value).append(" )"); 280 } 281 else { 282 sb.append(oper).append("?").append(" )"); 283 values.add(value); 284 } 285 } 286 } 287 288 /** 289 * Generate Condition statement for equality check 290 * 291 * @param column 292 * @param value 293 * @return Equality Condition statement 294 */ 295 public static Condition isEqual(Column column, Object value) { 296 return new Compare(" = ", column, value); 297 } 298 299 /** 300 * Generate InEquality Condition statement 301 * 302 * @param column 303 * @param value 304 * @return Inequality Condition statement 305 */ 306 public static Condition isNotEqual(Column column, Object value) { 307 return new Compare(" <> ", column, value); 308 } 309 310 /** 311 * Generate Condition statement for LESS THAN condition checking 312 * 313 * @param column 314 * @param value 315 * @return less than condition statement 316 */ 317 public static Condition lessThan(Column column, Object value) { 318 return new Compare(" < ", column, value); 319 } 320 321 /** 322 * Generate Condition statement for GREATER THAN condition checking 323 * 324 * @param column 325 * @param value 326 * @return greater than condition statement 327 */ 328 public static Condition greaterThan(Column column, Object value) { 329 return new Compare(" > ", column, value); 330 } 331 332 /** 333 * Generate Condition statement for LESS THAN OR EQUAL condition checking 334 * 335 * @param column 336 * @param value 337 * @return less than or equal condition statement 338 */ 339 public static Condition lessThanOrEqual(Column column, Object value) { 340 return new Compare(" <= ", column, value); 341 } 342 343 /** 344 * Generate Condition statement for GREATER THAN OR EQUAL condition checking 345 * 346 * @param column 347 * @param value 348 * @return greater than or equal condition statement 349 */ 350 public static Condition greaterThanOrEqual(Column column, Object value) { 351 return new Compare(" >= ", column, value); 352 } 353 354 /** 355 * IN/NOT IN condition statement generator for checking multiple values and for sub queries 356 */ 357 static class In extends Condition { 358 In(boolean isInvert, Column column, Object... values) { 359 super(column); 360 sb.append(isInvert ? " NOT" : "").append(" IN ("); 361 for (Object value : values) { 362 sb.append(" ? ").append(","); 363 this.values.add(value); 364 } 365 // remove last comma 366 sb.setLength(sb.length() - 1); 367 sb.append(") )"); 368 } 369 370 In(boolean isInvert, Column column, Select select) { 371 super(column); 372 sb.append(isInvert ? " NOT" : "").append(" IN ("); 373 for (Object value : select.values) { 374 this.values.add(value); 375 } 376 sb.append(select.toString()); 377 sb.append(") )"); 378 } 379 } 380 381 /** 382 * IN Condition for checking multiple values 383 * 384 * @param column 385 * @param values 386 * @return In condition statement 387 */ 388 public static Condition in(Column column, Object... values) { 389 return new In(false, column, values); 390 } 391 392 /** 393 * NOT IN Condition for checking multiple values 394 * 395 * @param column 396 * @param values 397 * @return not in condition statement 398 */ 399 public static Condition notIn(Column column, Object... values) { 400 return new In(true, column, values); 401 } 402 403 /** 404 * Sub query with IN condition 405 * 406 * @param column 407 * @param select 408 * @return Sub query using in 409 */ 410 public static Condition in(Column column, Select select) { 411 return new In(false, column, select); 412 } 413 414 /** 415 * Sub query with NOT IN condition 416 * 417 * @param column 418 * @param select 419 * @return sub query using not in 420 */ 421 public static Condition notIn(Column column, Select select) { 422 return new In(true, column, select); 423 } 424 425 /** 426 * Generate the Range checking statements 427 */ 428 static class Between extends Condition { 429 Between(boolean isInvert, Column column, Object lVal, Object rVal) { 430 super(column); 431 sb.append(isInvert ? " NOT" : "").append(" BETWEEN "); 432 sb.append(" ? "); 433 values.add(lVal); 434 sb.append(" AND "); 435 sb.append(" ? ").append(" )"); 436 values.add(rVal); 437 } 438 } 439 440 /** 441 * BETWEEN range checking statement 442 * 443 * @param column 444 * @param lVal min value for range checking 445 * @param rVal max value for range checking 446 * @return between condition statement 447 */ 448 public static Condition between(Column column, Object lVal, Object rVal) { 449 return new Between(false, column, lVal, rVal); 450 } 451 452 /** 453 * NOT BETWEEN range checking statement 454 * 455 * @param column 456 * @param lVal min value for range checking 457 * @param rVal max value for range checking 458 * @return not between condition statement 459 */ 460 public static Condition notBetween(Column column, Object lVal, Object rVal) { 461 return new Between(true, column, lVal, rVal); 462 } 463 464 /** 465 * Logical AND condition Generator 466 * 467 * @param conds list of conditions for AND 468 * @return AND statement 469 */ 470 public static Condition and(Condition... conds) { 471 Condition retVal = new Condition(null); 472 if (conds.length >= 2) { 473 for (int i = 0; i < conds.length; i++) { 474 if (i > 0) { 475 retVal.sb.append(" AND "); 476 } 477 retVal.sb.append(conds[i].sb); 478 retVal.values.addAll(conds[i].values); 479 } 480 } 481 else { 482 if (conds.length == 1) { 483 return conds[0]; 484 } 485 } 486 retVal.sb.append(" )"); 487 return retVal; 488 } 489 490 /** 491 * Logical OR condition generator 492 * 493 * @param conds list of conditions for OR 494 * @return OR statement 495 */ 496 public static Condition or(Condition... conds) { 497 Condition retVal = new Condition(null); 498 if (conds.length >= 2) { 499 for (int i = 0; i < conds.length; i++) { 500 if (i > 0) { 501 retVal.sb.append(" OR "); 502 } 503 retVal.sb.append(conds[i].sb); 504 retVal.values.addAll(conds[i].values); 505 } 506 } 507 else { 508 if (conds.length == 1) { 509 return conds[0]; 510 } 511 } 512 retVal.sb.append(" )"); 513 return retVal; 514 } 515 516 protected StringBuilder sb = new StringBuilder(""); 517 protected List<Object> values = new ArrayList<Object>(); 518 519 /** 520 * Select Statement generator. Generate the SQL Statement for select statements. Provide methods to add WHERE 521 * clause, ORDER BY clause, FOR UPDATE clause. 522 */ 523 public static class Select extends SqlStatement { 524 private Condition condition; 525 private boolean isOdered = false; 526 private Column[] orderby = null; 527 private boolean[] isAscending = null; 528 private boolean isLimitSet = false; 529 530 private Select(Select other) { 531 this.condition = other.condition; 532 this.sb.append(other.sb); 533 this.values.addAll(other.values); 534 this.isOdered = other.isOdered; 535 this.isLimitSet = other.isLimitSet; 536 this.orderby = other.orderby; 537 this.isAscending = other.isAscending; 538 this.forUpdate = other.forUpdate; 539 } 540 541 Select(boolean count, Table... tables) { 542 String temp = count ? "COUNT(*)" : "*"; 543 this.sb.append("SELECT " + temp + " FROM"); 544 if ((tables != null) && (tables.length > 0)) { 545 for (Table table : tables) { 546 this.sb.append(" " + table + " ,"); 547 } 548 // remove comma 549 this.sb.setLength(sb.length() - 1); 550 } 551 else { 552 throw new RuntimeException("Need atleast 1 Table !!"); 553 } 554 } 555 556 Select(Column... columns) { 557 this.sb.append("SELECT"); 558 if ((columns != null) && (columns.length > 0)) { 559 Set<Table> tables = new HashSet<Table>(); 560 for (Column column : columns) { 561 tables.add(column.table()); 562 this.sb.append(" " + column + " AS " + column.asLabel() + " ,"); 563 } 564 // remove comma 565 this.sb.setLength(sb.length() - 1); 566 this.sb.append(" FROM"); 567 for (Table table : tables) { 568 this.sb.append(" " + table + " ,"); 569 } 570 // remove comma 571 this.sb.setLength(sb.length() - 1); 572 } 573 else { 574 throw new RuntimeException("Need atleast 1 Column !!"); 575 } 576 } 577 578 /** 579 * Set the condition for where clause 580 * 581 * @param condition condition for where clause 582 * @return <code>Select</code> for cascading 583 */ 584 public Select where(Condition condition) { 585 Select retVal = new Select(this); 586 retVal.condition = condition; 587 retVal.values.addAll(condition.values); 588 return retVal; 589 } 590 591 /** 592 * Sets the column to sort and the order of sort 593 * 594 * @param column column to sort 595 * @param order true = ascending 596 * @return <code>Select</code> for cascading 597 */ 598 public Select orderBy(Column column, boolean order) { 599 if (!isOdered) { 600 Select retVal = new Select(this); 601 retVal.orderby = new Column[]{column}; 602 retVal.isAscending = new boolean[]{order}; 603 retVal.isOdered = true; 604 return retVal; 605 } 606 return this; 607 } 608 609 /** 610 * To sort 2 columns 611 * 612 * @param column0 First column to be sorted 613 * @param order0 true = ascending 614 * @param column1 Second column to be sorted 615 * @param order1 true = ascending 616 * @return <code>Select</code> for cascading 617 */ 618 public Select orderBy(Column column0, boolean order0, Column column1, boolean order1) { 619 if (!isOdered) { 620 Select retVal = new Select(this); 621 retVal.orderby = new Column[]{column0, column1}; 622 retVal.isAscending = new boolean[]{order0, order1}; 623 retVal.isOdered = true; 624 return retVal; 625 } 626 return this; 627 } 628 629 /** 630 * Setting the offset and limit for LIMIT clause 631 * 632 * @param offset 633 * @param limit 634 * @return <code>Select</code> for cascading 635 */ 636 public Select limit(int offset, int limit) { 637 if (isOdered) { 638 Select retVal = new Select(this); 639 retVal.values.add(limit); 640 retVal.values.add(offset); 641 retVal.isLimitSet = true; 642 return retVal; 643 } 644 return this; 645 } 646 647 /** 648 * Set the "for update" flag to lock the rows for updating 649 * 650 * @return <code>Select</code> for cascading 651 */ 652 // TODO Not working for hsql 653 public Select forUpdate() { 654 Select retVal = new Select(this); 655 retVal.forUpdate = true; 656 return retVal; 657 } 658 659 /** 660 * Generate the SQL Select Statement with conditions and other clauses that were set 661 */ 662 public String toString() { 663 String oBy = ""; 664 if ((orderby != null) && (isAscending != null) && (orderby.length == isAscending.length)) { 665 StringBuffer osb = new StringBuffer(" ORDER BY "); 666 int i = 0; 667 for (Column column : orderby) { 668 osb.append(column.asLabel()).append(isAscending[i] ? " ASC ," : " DESC ,"); 669 } 670 osb.setLength(osb.length() - 1); 671 if (isLimitSet) { 672 osb.append("LIMIT ").append("?").append(" OFFSET ").append("?").append(" "); 673 } 674 oBy = osb.toString(); 675 } 676 return sb.toString() + ((condition != null) ? "WHERE " + condition.toString() : "") + oBy; 677 } 678 } 679 680 /** 681 * SQL Statement generator for DELETE Statements 682 */ 683 public static class Delete extends SqlStatement { 684 Condition condition; 685 final Table table; 686 687 Delete(Table table) { 688 this.table = table; 689 this.sb.append("DELETE FROM " + table + " "); 690 } 691 692 private Delete(Delete other) { 693 this.table = other.table; 694 this.condition = other.condition; 695 this.sb.append(other.sb); 696 this.values.addAll(other.values); 697 } 698 699 /** 700 * Set the where clause for DELETE 701 * 702 * @param condition condition for where clause 703 * @return <code>Delete</code> for cascading 704 */ 705 public Delete where(Condition condition) { 706 Delete retVal = new Delete(this); 707 retVal.condition = condition; 708 retVal.values.addAll(condition.values); 709 return retVal; 710 } 711 712 /** 713 * Return the DELETE Statement 714 */ 715 public String toString() { 716 return sb.toString() + ((condition != null) ? "WHERE " + condition.toString() : ""); 717 } 718 } 719 720 /** 721 * UPDATE SQL Statement generator 722 */ 723 public static class Update extends SqlStatement { 724 Condition condition; 725 final Table table; 726 727 Update(Table table) { 728 this.table = table; 729 this.sb.append("UPDATE " + table + " SET "); 730 } 731 732 private Update(Update other) { 733 this.table = other.table; 734 this.condition = other.condition; 735 this.sb.append(other.sb); 736 this.values.addAll(other.values); 737 } 738 739 /** 740 * SET clause for update statement 741 * 742 * @param column column name 743 * @param value A temporary place holder which can be replaced while preparing 744 * @return <code>Update</code> for cascading 745 */ 746 public Update set(Column column, Object value) { 747 Update retVal = new Update(this); 748 retVal.sb.append((values.isEmpty() ? "" : ", ") + column + " = ? "); 749 retVal.values.add(value); 750 return retVal; 751 } 752 753 /** 754 * Set condition for updating 755 * 756 * @param condition condition for where clause 757 * @return <code>Update</code> for cascading 758 */ 759 public Update where(Condition condition) { 760 Update retVal = new Update(this); 761 retVal.condition = condition; 762 retVal.values.addAll(condition.values); 763 return retVal; 764 } 765 766 /** 767 * Return the UPDATE statement 768 */ 769 public String toString() { 770 return sb.toString() + ((condition != null) ? " WHERE " + condition.toString() : ""); 771 } 772 } 773 774 /** 775 * INSERT Statement generator 776 */ 777 public static class Insert extends SqlStatement { 778 StringBuilder sbCol = new StringBuilder(""); 779 StringBuilder sbVal = new StringBuilder(""); 780 boolean isFirst = true; 781 final Table table; 782 783 Insert(Table table) { 784 this.table = table; 785 this.sbCol.append("INSERT INTO " + table + " ( )"); 786 this.sbVal.append("VALUES ( )"); 787 } 788 789 private Insert(Insert other) { 790 this.table = other.table; 791 this.sbCol.append(other.sbCol); 792 this.sbVal.append(other.sbVal); 793 this.values.addAll(other.values); 794 if (other.isFirst) { 795 this.isFirst = false; 796 } 797 } 798 799 /** 800 * Set the VALUES that are to be inserted 801 * 802 * @param column 803 * @param value A temporary place holder which will be replaced while preparing 804 * @return 805 */ 806 public Insert value(Column column, Object value) { 807 Insert retVal = new Insert(this); 808 retVal.sbCol.setLength(retVal.sbCol.length() - 1); 809 retVal.sbVal.setLength(retVal.sbVal.length() - 1); 810 retVal.values.add(value); 811 retVal.sbCol.append(((isFirst) ? "" : ", ") + column + " )"); 812 retVal.sbVal.append(((isFirst) ? "" : ", ") + "? )"); 813 retVal.isFirst = false; 814 return retVal; 815 } 816 817 /** 818 * Return the INSERT Statement 819 */ 820 public String toString() { 821 return sbCol.toString() + " " + sbVal.toString(); 822 } 823 } 824 825 /** 826 * Prepare the SQL Statement that is generated and assign the values to prepared statement. setValues should be 827 * called to set the Real Values for place holders 828 * 829 * @param conn Connection 830 * @return Prepared SQL Statement 831 * @throws SQLException 832 */ 833 public PreparedStatement prepareAndSetValues(Connection conn) throws SQLException { 834 String stmt = toString(); 835 if (forUpdate && !Schema.isHsqlConnection(conn)) { 836 stmt += " FOR UPDATE"; 837 } 838 PreparedStatement pStmt = conn.prepareStatement(stmt); 839 int i = 1; 840 for (Object value : this.values) { 841 pStmt.setObject(i, value); 842 i++; 843 } 844 log.trace(XLog.Info.get().createPrefix() + " Preparing : " + stmt); 845 log.trace(XLog.Info.get().createPrefix() + " Values : " + values); 846 return pStmt; 847 } 848 849 /** 850 * Assign the values to Prepared Statement. setValues should be called to set the Real Values for place holders 851 * 852 * @param pStmt Prepared Statement 853 * @return PreparedStatement with values set 854 * @throws SQLException 855 */ 856 public PreparedStatement prepare(PreparedStatement pStmt) throws SQLException { 857 int i = 1; 858 pStmt.clearParameters(); 859 for (Object value : this.values) { 860 pStmt.setObject(i, value); 861 i++; 862 } 863 return pStmt; 864 } 865 866 /** 867 * Prepare the SQL Statement. Doesn't set the values. 868 * 869 * @param conn Connection 870 * @return PreparedStatement 871 * @throws SQLException 872 */ 873 public PreparedStatement prepare(Connection conn) throws SQLException { 874 String stmt = toString(); 875 if (forUpdate && !Schema.isHsqlConnection(conn)) { 876 stmt += " FOR UPDATE"; 877 } 878 return conn.prepareStatement(stmt); 879 } 880 881 /** 882 * Preparing Multiple statements for batch execution. 883 * 884 * @param conn Connection 885 * @param values A list of maps that contains the actual values 886 * @return Prepared Statement 887 * @throws SQLException 888 */ 889 public PreparedStatement prepareForBatch(Connection conn, List<? extends Map<Object, Object>> values, 890 PreparedStatement pStmt) throws SQLException { 891 String stmt = toString(); 892 if (forUpdate && !Schema.isHsqlConnection(conn)) { 893 stmt += " FOR UPDATE"; 894 } 895 // PreparedStatement pStmt = conn.prepareStatement(stmt); 896 for (Map<Object, Object> map : values) { 897 getNewStatementWithValues(map).prepare(pStmt); 898 pStmt.addBatch(); 899 } 900 return pStmt; 901 } 902 903 /** 904 * Replace the place holders with actual values in the sql statement 905 * 906 * @param oldVal Place holder 907 * @param newVal Actual Value 908 * @return SQL Statement with oldVal(place holder) replaced with newVal(actual value) 909 */ 910 public SqlStatement setValue(Object oldVal, Object newVal) { 911 ArrayList<Object> temp = new ArrayList<Object>(values); 912 if (values.contains(oldVal)) { 913 int i = temp.indexOf(oldVal); 914 temp.set(i, newVal); 915 } 916 SqlStatement retVal = create(temp); 917 return retVal; 918 } 919 920 /** 921 * Replace the keys(newValues) which are place holders in the sql statements with the corresponding new values. And 922 * Gives back a new SQL Statement so that the actual statement can be re-used 923 * 924 * @param newValues 925 * @return A New SQL Statement object with actual values set in its member 926 */ 927 public SqlStatement getNewStatementWithValues(Map<Object, Object> newValues) { 928 ArrayList<Object> temp = new ArrayList<Object>(); 929 for (Object value : values) { 930 if (newValues.containsKey(value)) { 931 temp.add(newValues.get(value)); 932 } 933 else { 934 temp.add(value); 935 } 936 } 937 SqlStatement retVal = create(temp); 938 return retVal; 939 } 940 941 /** 942 * Create the Appropriate SQL Statement with the given values 943 * 944 * @param temp 945 * @return 946 */ 947 private SqlStatement create(ArrayList<Object> temp) { 948 SqlStatement retVal = null; 949 if (this instanceof Select) { 950 retVal = new Select((Select) this); 951 } 952 else { 953 if (this instanceof Insert) { 954 retVal = new Insert((Insert) this); 955 } 956 else { 957 if (this instanceof Update) { 958 retVal = new Update((Update) this); 959 } 960 else { 961 if (this instanceof Delete) { 962 retVal = new Delete((Delete) this); 963 } 964 } 965 } 966 } 967 retVal.values.clear(); 968 retVal.values.addAll(temp); 969 return retVal; 970 } 971 972 /** 973 * Create the <code>ResultSetReader</code> object that has the methods to access the data from the result set 974 * 975 * @param rSet Result Set 976 * @return ResultSet Reader 977 */ 978 public static ResultSetReader parse(ResultSet rSet) { 979 return new ResultSetReader(rSet); 980 } 981 982 /** 983 * Return a new Insert Statement 984 * 985 * @param table 986 * @return Insert statement 987 */ 988 public static Insert insertInto(Table table) { 989 return new Insert(table); 990 } 991 992 /** 993 * Return a new Update Statement 994 * 995 * @param table 996 * @return Update statement 997 */ 998 public static Update update(Table table) { 999 return new Update(table); 1000 } 1001 1002 /** 1003 * Return a new Delete Statement 1004 * 1005 * @param table 1006 * @return Delete Statement 1007 */ 1008 public static Delete deleteFrom(Table table) { 1009 return new Delete(table); 1010 } 1011 1012 /** 1013 * Return a Select All Statement 1014 * 1015 * @param tables 1016 * @return Select * statement 1017 */ 1018 public static Select selectAllFrom(Table... tables) { 1019 return new Select(false, tables); 1020 } 1021 1022 /** 1023 * Return a Select Statement 1024 * 1025 * @param columns columns to select 1026 * @return select statement 1027 */ 1028 public static Select selectColumns(Column... columns) { 1029 return new Select(columns); 1030 } 1031 1032 /** 1033 * Select count(*) Statement generator. 1034 * 1035 * @param tables 1036 * @return "select count(*) from tables" statement 1037 */ 1038 public static Select getCount(Table... tables) { 1039 return new Select(true, tables); 1040 } 1041 }