001/* 002 * Copyright 2002-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jdbc.support.rowset; 018 019import java.math.BigDecimal; 020import java.sql.Date; 021import java.sql.ResultSet; 022import java.sql.ResultSetMetaData; 023import java.sql.SQLException; 024import java.sql.Time; 025import java.sql.Timestamp; 026import java.util.Calendar; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.Map; 030 031import org.springframework.jdbc.InvalidResultSetAccessException; 032import org.springframework.lang.UsesJava7; 033 034/** 035 * The default implementation of Spring's {@link SqlRowSet} interface, wrapping a 036 * {@link java.sql.ResultSet}, catching any {@link SQLException}s and translating 037 * them to a corresponding Spring {@link InvalidResultSetAccessException}. 038 * 039 * <p>The passed-in ResultSet should already be disconnected if the SqlRowSet is supposed 040 * to be usable in a disconnected fashion. This means that you will usually pass in a 041 * {@code javax.sql.rowset.CachedRowSet}, which implements the ResultSet interface. 042 * 043 * <p>Note: Since JDBC 4.0, it has been clarified that any methods using a String to identify 044 * the column should be using the column label. The column label is assigned using the ALIAS 045 * keyword in the SQL query string. When the query doesn't use an ALIAS, the default label is 046 * the column name. Most JDBC ResultSet implementations follow this new pattern but there are 047 * exceptions such as the {@code com.sun.rowset.CachedRowSetImpl} class which only uses 048 * the column name, ignoring any column labels. As of Spring 3.0.5, ResultSetWrappingSqlRowSet 049 * will translate column labels to the correct column index to provide better support for the 050 * {@code com.sun.rowset.CachedRowSetImpl} which is the default implementation used by 051 * {@link org.springframework.jdbc.core.JdbcTemplate} when working with RowSets. 052 * 053 * <p>Note: This class implements the {@code java.io.Serializable} marker interface 054 * through the SqlRowSet interface, but is only actually serializable if the disconnected 055 * ResultSet/RowSet contained in it is serializable. Most CachedRowSet implementations 056 * are actually serializable, so this should usually work out. 057 * 058 * @author Thomas Risberg 059 * @author Juergen Hoeller 060 * @since 1.2 061 * @see java.sql.ResultSet 062 * @see javax.sql.rowset.CachedRowSet 063 * @see org.springframework.jdbc.core.JdbcTemplate#queryForRowSet 064 */ 065public class ResultSetWrappingSqlRowSet implements SqlRowSet { 066 067 /** use serialVersionUID from Spring 1.2 for interoperability */ 068 private static final long serialVersionUID = -4688694393146734764L; 069 070 071 private final ResultSet resultSet; 072 073 private final SqlRowSetMetaData rowSetMetaData; 074 075 private final Map<String, Integer> columnLabelMap; 076 077 078 /** 079 * Create a new ResultSetWrappingSqlRowSet for the given ResultSet. 080 * @param resultSet a disconnected ResultSet to wrap 081 * (usually a {@code javax.sql.rowset.CachedRowSet}) 082 * @throws InvalidResultSetAccessException if extracting 083 * the ResultSetMetaData failed 084 * @see javax.sql.rowset.CachedRowSet 085 * @see java.sql.ResultSet#getMetaData 086 * @see ResultSetWrappingSqlRowSetMetaData 087 */ 088 public ResultSetWrappingSqlRowSet(ResultSet resultSet) throws InvalidResultSetAccessException { 089 this.resultSet = resultSet; 090 try { 091 this.rowSetMetaData = new ResultSetWrappingSqlRowSetMetaData(resultSet.getMetaData()); 092 } 093 catch (SQLException se) { 094 throw new InvalidResultSetAccessException(se); 095 } 096 try { 097 ResultSetMetaData rsmd = resultSet.getMetaData(); 098 if (rsmd != null) { 099 int columnCount = rsmd.getColumnCount(); 100 this.columnLabelMap = new HashMap<String, Integer>(columnCount); 101 for (int i = 1; i <= columnCount; i++) { 102 String key = rsmd.getColumnLabel(i); 103 // Make sure to preserve first matching column for any given name, 104 // as defined in ResultSet's type-level javadoc (lines 81 to 83). 105 if (!this.columnLabelMap.containsKey(key)) { 106 this.columnLabelMap.put(key, i); 107 } 108 } 109 } 110 else { 111 this.columnLabelMap = Collections.emptyMap(); 112 } 113 } 114 catch (SQLException se) { 115 throw new InvalidResultSetAccessException(se); 116 } 117 118 } 119 120 121 /** 122 * Return the underlying ResultSet 123 * (usually a {@code javax.sql.rowset.CachedRowSet}). 124 * @see javax.sql.rowset.CachedRowSet 125 */ 126 public final ResultSet getResultSet() { 127 return this.resultSet; 128 } 129 130 /** 131 * @see java.sql.ResultSetMetaData#getCatalogName(int) 132 */ 133 @Override 134 public final SqlRowSetMetaData getMetaData() { 135 return this.rowSetMetaData; 136 } 137 138 /** 139 * @see java.sql.ResultSet#findColumn(String) 140 */ 141 @Override 142 public int findColumn(String columnLabel) throws InvalidResultSetAccessException { 143 Integer columnIndex = this.columnLabelMap.get(columnLabel); 144 if (columnIndex != null) { 145 return columnIndex; 146 } 147 else { 148 try { 149 return this.resultSet.findColumn(columnLabel); 150 } 151 catch (SQLException se) { 152 throw new InvalidResultSetAccessException(se); 153 } 154 } 155 } 156 157 158 // RowSet methods for extracting data values 159 160 /** 161 * @see java.sql.ResultSet#getBigDecimal(int) 162 */ 163 @Override 164 public BigDecimal getBigDecimal(int columnIndex) throws InvalidResultSetAccessException { 165 try { 166 return this.resultSet.getBigDecimal(columnIndex); 167 } 168 catch (SQLException se) { 169 throw new InvalidResultSetAccessException(se); 170 } 171 } 172 173 /** 174 * @see java.sql.ResultSet#getBigDecimal(String) 175 */ 176 @Override 177 public BigDecimal getBigDecimal(String columnLabel) throws InvalidResultSetAccessException { 178 return getBigDecimal(findColumn(columnLabel)); 179 } 180 181 /** 182 * @see java.sql.ResultSet#getBoolean(int) 183 */ 184 @Override 185 public boolean getBoolean(int columnIndex) throws InvalidResultSetAccessException { 186 try { 187 return this.resultSet.getBoolean(columnIndex); 188 } 189 catch (SQLException se) { 190 throw new InvalidResultSetAccessException(se); 191 } 192 } 193 194 /** 195 * @see java.sql.ResultSet#getBoolean(String) 196 */ 197 @Override 198 public boolean getBoolean(String columnLabel) throws InvalidResultSetAccessException { 199 return getBoolean(findColumn(columnLabel)); 200 } 201 202 /** 203 * @see java.sql.ResultSet#getByte(int) 204 */ 205 @Override 206 public byte getByte(int columnIndex) throws InvalidResultSetAccessException { 207 try { 208 return this.resultSet.getByte(columnIndex); 209 } 210 catch (SQLException se) { 211 throw new InvalidResultSetAccessException(se); 212 } 213 } 214 215 /** 216 * @see java.sql.ResultSet#getByte(String) 217 */ 218 @Override 219 public byte getByte(String columnLabel) throws InvalidResultSetAccessException { 220 return getByte(findColumn(columnLabel)); 221 } 222 223 /** 224 * @see java.sql.ResultSet#getDate(int) 225 */ 226 @Override 227 public Date getDate(int columnIndex) throws InvalidResultSetAccessException { 228 try { 229 return this.resultSet.getDate(columnIndex); 230 } 231 catch (SQLException se) { 232 throw new InvalidResultSetAccessException(se); 233 } 234 } 235 236 /** 237 * @see java.sql.ResultSet#getDate(String) 238 */ 239 @Override 240 public Date getDate(String columnLabel) throws InvalidResultSetAccessException { 241 return getDate(findColumn(columnLabel)); 242 } 243 244 /** 245 * @see java.sql.ResultSet#getDate(int, Calendar) 246 */ 247 @Override 248 public Date getDate(int columnIndex, Calendar cal) throws InvalidResultSetAccessException { 249 try { 250 return this.resultSet.getDate(columnIndex, cal); 251 } 252 catch (SQLException se) { 253 throw new InvalidResultSetAccessException(se); 254 } 255 } 256 257 /** 258 * @see java.sql.ResultSet#getDate(String, Calendar) 259 */ 260 @Override 261 public Date getDate(String columnLabel, Calendar cal) throws InvalidResultSetAccessException { 262 return getDate(findColumn(columnLabel), cal); 263 } 264 265 /** 266 * @see java.sql.ResultSet#getDouble(int) 267 */ 268 @Override 269 public double getDouble(int columnIndex) throws InvalidResultSetAccessException { 270 try { 271 return this.resultSet.getDouble(columnIndex); 272 } 273 catch (SQLException se) { 274 throw new InvalidResultSetAccessException(se); 275 } 276 } 277 278 /** 279 * @see java.sql.ResultSet#getDouble(String) 280 */ 281 @Override 282 public double getDouble(String columnLabel) throws InvalidResultSetAccessException { 283 return getDouble(findColumn(columnLabel)); 284 } 285 286 /** 287 * @see java.sql.ResultSet#getFloat(int) 288 */ 289 @Override 290 public float getFloat(int columnIndex) throws InvalidResultSetAccessException { 291 try { 292 return this.resultSet.getFloat(columnIndex); 293 } 294 catch (SQLException se) { 295 throw new InvalidResultSetAccessException(se); 296 } 297 } 298 299 /** 300 * @see java.sql.ResultSet#getFloat(String) 301 */ 302 @Override 303 public float getFloat(String columnLabel) throws InvalidResultSetAccessException { 304 return getFloat(findColumn(columnLabel)); 305 } 306 307 /** 308 * @see java.sql.ResultSet#getInt(int) 309 */ 310 @Override 311 public int getInt(int columnIndex) throws InvalidResultSetAccessException { 312 try { 313 return this.resultSet.getInt(columnIndex); 314 } 315 catch (SQLException se) { 316 throw new InvalidResultSetAccessException(se); 317 } 318 } 319 320 /** 321 * @see java.sql.ResultSet#getInt(String) 322 */ 323 @Override 324 public int getInt(String columnLabel) throws InvalidResultSetAccessException { 325 return getInt(findColumn(columnLabel)); 326 } 327 328 /** 329 * @see java.sql.ResultSet#getLong(int) 330 */ 331 @Override 332 public long getLong(int columnIndex) throws InvalidResultSetAccessException { 333 try { 334 return this.resultSet.getLong(columnIndex); 335 } 336 catch (SQLException se) { 337 throw new InvalidResultSetAccessException(se); 338 } 339 } 340 341 /** 342 * @see java.sql.ResultSet#getLong(String) 343 */ 344 @Override 345 public long getLong(String columnLabel) throws InvalidResultSetAccessException { 346 return getLong(findColumn(columnLabel)); 347 } 348 349 /** 350 * @see java.sql.ResultSet#getNString(int) 351 */ 352 @Override 353 public String getNString(int columnIndex) throws InvalidResultSetAccessException { 354 try { 355 return this.resultSet.getNString(columnIndex); 356 } 357 catch (SQLException se) { 358 throw new InvalidResultSetAccessException(se); 359 } 360 } 361 362 /** 363 * @see java.sql.ResultSet#getNString(String) 364 */ 365 @Override 366 public String getNString(String columnLabel) throws InvalidResultSetAccessException { 367 return getNString(findColumn(columnLabel)); 368 } 369 370 /** 371 * @see java.sql.ResultSet#getObject(int) 372 */ 373 @Override 374 public Object getObject(int columnIndex) throws InvalidResultSetAccessException { 375 try { 376 return this.resultSet.getObject(columnIndex); 377 } 378 catch (SQLException se) { 379 throw new InvalidResultSetAccessException(se); 380 } 381 } 382 383 /** 384 * @see java.sql.ResultSet#getObject(String) 385 */ 386 @Override 387 public Object getObject(String columnLabel) throws InvalidResultSetAccessException { 388 return getObject(findColumn(columnLabel)); 389 } 390 391 /** 392 * @see java.sql.ResultSet#getObject(int, Map) 393 */ 394 @Override 395 public Object getObject(int columnIndex, Map<String, Class<?>> map) throws InvalidResultSetAccessException { 396 try { 397 return this.resultSet.getObject(columnIndex, map); 398 } 399 catch (SQLException se) { 400 throw new InvalidResultSetAccessException(se); 401 } 402 } 403 404 /** 405 * @see java.sql.ResultSet#getObject(String, Map) 406 */ 407 @Override 408 public Object getObject(String columnLabel, Map<String, Class<?>> map) throws InvalidResultSetAccessException { 409 return getObject(findColumn(columnLabel), map); 410 } 411 412 /** 413 * @see java.sql.ResultSet#getObject(int, Class) 414 */ 415 @UsesJava7 416 @Override 417 public <T> T getObject(int columnIndex, Class<T> type) throws InvalidResultSetAccessException { 418 try { 419 return this.resultSet.getObject(columnIndex, type); 420 } 421 catch (SQLException se) { 422 throw new InvalidResultSetAccessException(se); 423 } 424 } 425 426 /** 427 * @see java.sql.ResultSet#getObject(String, Class) 428 */ 429 @Override 430 public <T> T getObject(String columnLabel, Class<T> type) throws InvalidResultSetAccessException { 431 return getObject(findColumn(columnLabel), type); 432 } 433 434 /** 435 * @see java.sql.ResultSet#getShort(int) 436 */ 437 @Override 438 public short getShort(int columnIndex) throws InvalidResultSetAccessException { 439 try { 440 return this.resultSet.getShort(columnIndex); 441 } 442 catch (SQLException se) { 443 throw new InvalidResultSetAccessException(se); 444 } 445 } 446 447 /** 448 * @see java.sql.ResultSet#getShort(String) 449 */ 450 @Override 451 public short getShort(String columnLabel) throws InvalidResultSetAccessException { 452 return getShort(findColumn(columnLabel)); 453 } 454 455 /** 456 * @see java.sql.ResultSet#getString(int) 457 */ 458 @Override 459 public String getString(int columnIndex) throws InvalidResultSetAccessException { 460 try { 461 return this.resultSet.getString(columnIndex); 462 } 463 catch (SQLException se) { 464 throw new InvalidResultSetAccessException(se); 465 } 466 } 467 468 /** 469 * @see java.sql.ResultSet#getString(String) 470 */ 471 @Override 472 public String getString(String columnLabel) throws InvalidResultSetAccessException { 473 return getString(findColumn(columnLabel)); 474 } 475 476 /** 477 * @see java.sql.ResultSet#getTime(int) 478 */ 479 @Override 480 public Time getTime(int columnIndex) throws InvalidResultSetAccessException { 481 try { 482 return this.resultSet.getTime(columnIndex); 483 } 484 catch (SQLException se) { 485 throw new InvalidResultSetAccessException(se); 486 } 487 } 488 489 /** 490 * @see java.sql.ResultSet#getTime(String) 491 */ 492 @Override 493 public Time getTime(String columnLabel) throws InvalidResultSetAccessException { 494 return getTime(findColumn(columnLabel)); 495 } 496 497 /** 498 * @see java.sql.ResultSet#getTime(int, Calendar) 499 */ 500 @Override 501 public Time getTime(int columnIndex, Calendar cal) throws InvalidResultSetAccessException { 502 try { 503 return this.resultSet.getTime(columnIndex, cal); 504 } 505 catch (SQLException se) { 506 throw new InvalidResultSetAccessException(se); 507 } 508 } 509 510 /** 511 * @see java.sql.ResultSet#getTime(String, Calendar) 512 */ 513 @Override 514 public Time getTime(String columnLabel, Calendar cal) throws InvalidResultSetAccessException { 515 return getTime(findColumn(columnLabel), cal); 516 } 517 518 /** 519 * @see java.sql.ResultSet#getTimestamp(int) 520 */ 521 @Override 522 public Timestamp getTimestamp(int columnIndex) throws InvalidResultSetAccessException { 523 try { 524 return this.resultSet.getTimestamp(columnIndex); 525 } 526 catch (SQLException se) { 527 throw new InvalidResultSetAccessException(se); 528 } 529 } 530 531 /** 532 * @see java.sql.ResultSet#getTimestamp(String) 533 */ 534 @Override 535 public Timestamp getTimestamp(String columnLabel) throws InvalidResultSetAccessException { 536 return getTimestamp(findColumn(columnLabel)); 537 } 538 539 /** 540 * @see java.sql.ResultSet#getTimestamp(int, Calendar) 541 */ 542 @Override 543 public Timestamp getTimestamp(int columnIndex, Calendar cal) throws InvalidResultSetAccessException { 544 try { 545 return this.resultSet.getTimestamp(columnIndex, cal); 546 } 547 catch (SQLException se) { 548 throw new InvalidResultSetAccessException(se); 549 } 550 } 551 552 /** 553 * @see java.sql.ResultSet#getTimestamp(String, Calendar) 554 */ 555 @Override 556 public Timestamp getTimestamp(String columnLabel, Calendar cal) throws InvalidResultSetAccessException { 557 return getTimestamp(findColumn(columnLabel), cal); 558 } 559 560 561 // RowSet navigation methods 562 563 /** 564 * @see java.sql.ResultSet#absolute(int) 565 */ 566 @Override 567 public boolean absolute(int row) throws InvalidResultSetAccessException { 568 try { 569 return this.resultSet.absolute(row); 570 } 571 catch (SQLException se) { 572 throw new InvalidResultSetAccessException(se); 573 } 574 } 575 576 /** 577 * @see java.sql.ResultSet#afterLast() 578 */ 579 @Override 580 public void afterLast() throws InvalidResultSetAccessException { 581 try { 582 this.resultSet.afterLast(); 583 } 584 catch (SQLException se) { 585 throw new InvalidResultSetAccessException(se); 586 } 587 } 588 589 /** 590 * @see java.sql.ResultSet#beforeFirst() 591 */ 592 @Override 593 public void beforeFirst() throws InvalidResultSetAccessException { 594 try { 595 this.resultSet.beforeFirst(); 596 } 597 catch (SQLException se) { 598 throw new InvalidResultSetAccessException(se); 599 } 600 } 601 602 /** 603 * @see java.sql.ResultSet#first() 604 */ 605 @Override 606 public boolean first() throws InvalidResultSetAccessException { 607 try { 608 return this.resultSet.first(); 609 } 610 catch (SQLException se) { 611 throw new InvalidResultSetAccessException(se); 612 } 613 } 614 615 /** 616 * @see java.sql.ResultSet#getRow() 617 */ 618 @Override 619 public int getRow() throws InvalidResultSetAccessException { 620 try { 621 return this.resultSet.getRow(); 622 } 623 catch (SQLException se) { 624 throw new InvalidResultSetAccessException(se); 625 } 626 } 627 628 /** 629 * @see java.sql.ResultSet#isAfterLast() 630 */ 631 @Override 632 public boolean isAfterLast() throws InvalidResultSetAccessException { 633 try { 634 return this.resultSet.isAfterLast(); 635 } 636 catch (SQLException se) { 637 throw new InvalidResultSetAccessException(se); 638 } 639 } 640 641 /** 642 * @see java.sql.ResultSet#isBeforeFirst() 643 */ 644 @Override 645 public boolean isBeforeFirst() throws InvalidResultSetAccessException { 646 try { 647 return this.resultSet.isBeforeFirst(); 648 } 649 catch (SQLException se) { 650 throw new InvalidResultSetAccessException(se); 651 } 652 } 653 654 /** 655 * @see java.sql.ResultSet#isFirst() 656 */ 657 @Override 658 public boolean isFirst() throws InvalidResultSetAccessException { 659 try { 660 return this.resultSet.isFirst(); 661 } 662 catch (SQLException se) { 663 throw new InvalidResultSetAccessException(se); 664 } 665 } 666 667 /** 668 * @see java.sql.ResultSet#isLast() 669 */ 670 @Override 671 public boolean isLast() throws InvalidResultSetAccessException { 672 try { 673 return this.resultSet.isLast(); 674 } 675 catch (SQLException se) { 676 throw new InvalidResultSetAccessException(se); 677 } 678 } 679 680 /** 681 * @see java.sql.ResultSet#last() 682 */ 683 @Override 684 public boolean last() throws InvalidResultSetAccessException { 685 try { 686 return this.resultSet.last(); 687 } 688 catch (SQLException se) { 689 throw new InvalidResultSetAccessException(se); 690 } 691 } 692 693 /** 694 * @see java.sql.ResultSet#next() 695 */ 696 @Override 697 public boolean next() throws InvalidResultSetAccessException { 698 try { 699 return this.resultSet.next(); 700 } 701 catch (SQLException se) { 702 throw new InvalidResultSetAccessException(se); 703 } 704 } 705 706 /** 707 * @see java.sql.ResultSet#previous() 708 */ 709 @Override 710 public boolean previous() throws InvalidResultSetAccessException { 711 try { 712 return this.resultSet.previous(); 713 } 714 catch (SQLException se) { 715 throw new InvalidResultSetAccessException(se); 716 } 717 } 718 719 /** 720 * @see java.sql.ResultSet#relative(int) 721 */ 722 @Override 723 public boolean relative(int rows) throws InvalidResultSetAccessException { 724 try { 725 return this.resultSet.relative(rows); 726 } 727 catch (SQLException se) { 728 throw new InvalidResultSetAccessException(se); 729 } 730 } 731 732 /** 733 * @see java.sql.ResultSet#wasNull() 734 */ 735 @Override 736 public boolean wasNull() throws InvalidResultSetAccessException { 737 try { 738 return this.resultSet.wasNull(); 739 } 740 catch (SQLException se) { 741 throw new InvalidResultSetAccessException(se); 742 } 743 } 744 745}