001/* 002 * Copyright 2002-2019 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.Nullable; 033 034/** 035 * The default implementation of Spring's {@link SqlRowSet} interface, wrapping a 036 * {@link java.sql.ResultSet}, catching any {@link SQLException SQLExceptions} and 037 * translating 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<>(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 @Nullable 165 public BigDecimal getBigDecimal(int columnIndex) throws InvalidResultSetAccessException { 166 try { 167 return this.resultSet.getBigDecimal(columnIndex); 168 } 169 catch (SQLException se) { 170 throw new InvalidResultSetAccessException(se); 171 } 172 } 173 174 /** 175 * @see java.sql.ResultSet#getBigDecimal(String) 176 */ 177 @Override 178 @Nullable 179 public BigDecimal getBigDecimal(String columnLabel) throws InvalidResultSetAccessException { 180 return getBigDecimal(findColumn(columnLabel)); 181 } 182 183 /** 184 * @see java.sql.ResultSet#getBoolean(int) 185 */ 186 @Override 187 public boolean getBoolean(int columnIndex) throws InvalidResultSetAccessException { 188 try { 189 return this.resultSet.getBoolean(columnIndex); 190 } 191 catch (SQLException se) { 192 throw new InvalidResultSetAccessException(se); 193 } 194 } 195 196 /** 197 * @see java.sql.ResultSet#getBoolean(String) 198 */ 199 @Override 200 public boolean getBoolean(String columnLabel) throws InvalidResultSetAccessException { 201 return getBoolean(findColumn(columnLabel)); 202 } 203 204 /** 205 * @see java.sql.ResultSet#getByte(int) 206 */ 207 @Override 208 public byte getByte(int columnIndex) throws InvalidResultSetAccessException { 209 try { 210 return this.resultSet.getByte(columnIndex); 211 } 212 catch (SQLException se) { 213 throw new InvalidResultSetAccessException(se); 214 } 215 } 216 217 /** 218 * @see java.sql.ResultSet#getByte(String) 219 */ 220 @Override 221 public byte getByte(String columnLabel) throws InvalidResultSetAccessException { 222 return getByte(findColumn(columnLabel)); 223 } 224 225 /** 226 * @see java.sql.ResultSet#getDate(int) 227 */ 228 @Override 229 @Nullable 230 public Date getDate(int columnIndex) throws InvalidResultSetAccessException { 231 try { 232 return this.resultSet.getDate(columnIndex); 233 } 234 catch (SQLException se) { 235 throw new InvalidResultSetAccessException(se); 236 } 237 } 238 239 /** 240 * @see java.sql.ResultSet#getDate(String) 241 */ 242 @Override 243 @Nullable 244 public Date getDate(String columnLabel) throws InvalidResultSetAccessException { 245 return getDate(findColumn(columnLabel)); 246 } 247 248 /** 249 * @see java.sql.ResultSet#getDate(int, Calendar) 250 */ 251 @Override 252 @Nullable 253 public Date getDate(int columnIndex, Calendar cal) throws InvalidResultSetAccessException { 254 try { 255 return this.resultSet.getDate(columnIndex, cal); 256 } 257 catch (SQLException se) { 258 throw new InvalidResultSetAccessException(se); 259 } 260 } 261 262 /** 263 * @see java.sql.ResultSet#getDate(String, Calendar) 264 */ 265 @Override 266 @Nullable 267 public Date getDate(String columnLabel, Calendar cal) throws InvalidResultSetAccessException { 268 return getDate(findColumn(columnLabel), cal); 269 } 270 271 /** 272 * @see java.sql.ResultSet#getDouble(int) 273 */ 274 @Override 275 public double getDouble(int columnIndex) throws InvalidResultSetAccessException { 276 try { 277 return this.resultSet.getDouble(columnIndex); 278 } 279 catch (SQLException se) { 280 throw new InvalidResultSetAccessException(se); 281 } 282 } 283 284 /** 285 * @see java.sql.ResultSet#getDouble(String) 286 */ 287 @Override 288 public double getDouble(String columnLabel) throws InvalidResultSetAccessException { 289 return getDouble(findColumn(columnLabel)); 290 } 291 292 /** 293 * @see java.sql.ResultSet#getFloat(int) 294 */ 295 @Override 296 public float getFloat(int columnIndex) throws InvalidResultSetAccessException { 297 try { 298 return this.resultSet.getFloat(columnIndex); 299 } 300 catch (SQLException se) { 301 throw new InvalidResultSetAccessException(se); 302 } 303 } 304 305 /** 306 * @see java.sql.ResultSet#getFloat(String) 307 */ 308 @Override 309 public float getFloat(String columnLabel) throws InvalidResultSetAccessException { 310 return getFloat(findColumn(columnLabel)); 311 } 312 313 /** 314 * @see java.sql.ResultSet#getInt(int) 315 */ 316 @Override 317 public int getInt(int columnIndex) throws InvalidResultSetAccessException { 318 try { 319 return this.resultSet.getInt(columnIndex); 320 } 321 catch (SQLException se) { 322 throw new InvalidResultSetAccessException(se); 323 } 324 } 325 326 /** 327 * @see java.sql.ResultSet#getInt(String) 328 */ 329 @Override 330 public int getInt(String columnLabel) throws InvalidResultSetAccessException { 331 return getInt(findColumn(columnLabel)); 332 } 333 334 /** 335 * @see java.sql.ResultSet#getLong(int) 336 */ 337 @Override 338 public long getLong(int columnIndex) throws InvalidResultSetAccessException { 339 try { 340 return this.resultSet.getLong(columnIndex); 341 } 342 catch (SQLException se) { 343 throw new InvalidResultSetAccessException(se); 344 } 345 } 346 347 /** 348 * @see java.sql.ResultSet#getLong(String) 349 */ 350 @Override 351 public long getLong(String columnLabel) throws InvalidResultSetAccessException { 352 return getLong(findColumn(columnLabel)); 353 } 354 355 /** 356 * @see java.sql.ResultSet#getNString(int) 357 */ 358 @Override 359 @Nullable 360 public String getNString(int columnIndex) throws InvalidResultSetAccessException { 361 try { 362 return this.resultSet.getNString(columnIndex); 363 } 364 catch (SQLException se) { 365 throw new InvalidResultSetAccessException(se); 366 } 367 } 368 369 /** 370 * @see java.sql.ResultSet#getNString(String) 371 */ 372 @Override 373 @Nullable 374 public String getNString(String columnLabel) throws InvalidResultSetAccessException { 375 return getNString(findColumn(columnLabel)); 376 } 377 378 /** 379 * @see java.sql.ResultSet#getObject(int) 380 */ 381 @Override 382 @Nullable 383 public Object getObject(int columnIndex) throws InvalidResultSetAccessException { 384 try { 385 return this.resultSet.getObject(columnIndex); 386 } 387 catch (SQLException se) { 388 throw new InvalidResultSetAccessException(se); 389 } 390 } 391 392 /** 393 * @see java.sql.ResultSet#getObject(String) 394 */ 395 @Override 396 @Nullable 397 public Object getObject(String columnLabel) throws InvalidResultSetAccessException { 398 return getObject(findColumn(columnLabel)); 399 } 400 401 /** 402 * @see java.sql.ResultSet#getObject(int, Map) 403 */ 404 @Override 405 @Nullable 406 public Object getObject(int columnIndex, Map<String, Class<?>> map) throws InvalidResultSetAccessException { 407 try { 408 return this.resultSet.getObject(columnIndex, map); 409 } 410 catch (SQLException se) { 411 throw new InvalidResultSetAccessException(se); 412 } 413 } 414 415 /** 416 * @see java.sql.ResultSet#getObject(String, Map) 417 */ 418 @Override 419 @Nullable 420 public Object getObject(String columnLabel, Map<String, Class<?>> map) throws InvalidResultSetAccessException { 421 return getObject(findColumn(columnLabel), map); 422 } 423 424 /** 425 * @see java.sql.ResultSet#getObject(int, Class) 426 */ 427 @Override 428 @Nullable 429 public <T> T getObject(int columnIndex, Class<T> type) throws InvalidResultSetAccessException { 430 try { 431 return this.resultSet.getObject(columnIndex, type); 432 } 433 catch (SQLException se) { 434 throw new InvalidResultSetAccessException(se); 435 } 436 } 437 438 /** 439 * @see java.sql.ResultSet#getObject(String, Class) 440 */ 441 @Override 442 @Nullable 443 public <T> T getObject(String columnLabel, Class<T> type) throws InvalidResultSetAccessException { 444 return getObject(findColumn(columnLabel), type); 445 } 446 447 /** 448 * @see java.sql.ResultSet#getShort(int) 449 */ 450 @Override 451 public short getShort(int columnIndex) throws InvalidResultSetAccessException { 452 try { 453 return this.resultSet.getShort(columnIndex); 454 } 455 catch (SQLException se) { 456 throw new InvalidResultSetAccessException(se); 457 } 458 } 459 460 /** 461 * @see java.sql.ResultSet#getShort(String) 462 */ 463 @Override 464 public short getShort(String columnLabel) throws InvalidResultSetAccessException { 465 return getShort(findColumn(columnLabel)); 466 } 467 468 /** 469 * @see java.sql.ResultSet#getString(int) 470 */ 471 @Override 472 @Nullable 473 public String getString(int columnIndex) throws InvalidResultSetAccessException { 474 try { 475 return this.resultSet.getString(columnIndex); 476 } 477 catch (SQLException se) { 478 throw new InvalidResultSetAccessException(se); 479 } 480 } 481 482 /** 483 * @see java.sql.ResultSet#getString(String) 484 */ 485 @Override 486 @Nullable 487 public String getString(String columnLabel) throws InvalidResultSetAccessException { 488 return getString(findColumn(columnLabel)); 489 } 490 491 /** 492 * @see java.sql.ResultSet#getTime(int) 493 */ 494 @Override 495 @Nullable 496 public Time getTime(int columnIndex) throws InvalidResultSetAccessException { 497 try { 498 return this.resultSet.getTime(columnIndex); 499 } 500 catch (SQLException se) { 501 throw new InvalidResultSetAccessException(se); 502 } 503 } 504 505 /** 506 * @see java.sql.ResultSet#getTime(String) 507 */ 508 @Override 509 @Nullable 510 public Time getTime(String columnLabel) throws InvalidResultSetAccessException { 511 return getTime(findColumn(columnLabel)); 512 } 513 514 /** 515 * @see java.sql.ResultSet#getTime(int, Calendar) 516 */ 517 @Override 518 @Nullable 519 public Time getTime(int columnIndex, Calendar cal) throws InvalidResultSetAccessException { 520 try { 521 return this.resultSet.getTime(columnIndex, cal); 522 } 523 catch (SQLException se) { 524 throw new InvalidResultSetAccessException(se); 525 } 526 } 527 528 /** 529 * @see java.sql.ResultSet#getTime(String, Calendar) 530 */ 531 @Override 532 @Nullable 533 public Time getTime(String columnLabel, Calendar cal) throws InvalidResultSetAccessException { 534 return getTime(findColumn(columnLabel), cal); 535 } 536 537 /** 538 * @see java.sql.ResultSet#getTimestamp(int) 539 */ 540 @Override 541 @Nullable 542 public Timestamp getTimestamp(int columnIndex) throws InvalidResultSetAccessException { 543 try { 544 return this.resultSet.getTimestamp(columnIndex); 545 } 546 catch (SQLException se) { 547 throw new InvalidResultSetAccessException(se); 548 } 549 } 550 551 /** 552 * @see java.sql.ResultSet#getTimestamp(String) 553 */ 554 @Override 555 @Nullable 556 public Timestamp getTimestamp(String columnLabel) throws InvalidResultSetAccessException { 557 return getTimestamp(findColumn(columnLabel)); 558 } 559 560 /** 561 * @see java.sql.ResultSet#getTimestamp(int, Calendar) 562 */ 563 @Override 564 @Nullable 565 public Timestamp getTimestamp(int columnIndex, Calendar cal) throws InvalidResultSetAccessException { 566 try { 567 return this.resultSet.getTimestamp(columnIndex, cal); 568 } 569 catch (SQLException se) { 570 throw new InvalidResultSetAccessException(se); 571 } 572 } 573 574 /** 575 * @see java.sql.ResultSet#getTimestamp(String, Calendar) 576 */ 577 @Override 578 @Nullable 579 public Timestamp getTimestamp(String columnLabel, Calendar cal) throws InvalidResultSetAccessException { 580 return getTimestamp(findColumn(columnLabel), cal); 581 } 582 583 584 // RowSet navigation methods 585 586 /** 587 * @see java.sql.ResultSet#absolute(int) 588 */ 589 @Override 590 public boolean absolute(int row) throws InvalidResultSetAccessException { 591 try { 592 return this.resultSet.absolute(row); 593 } 594 catch (SQLException se) { 595 throw new InvalidResultSetAccessException(se); 596 } 597 } 598 599 /** 600 * @see java.sql.ResultSet#afterLast() 601 */ 602 @Override 603 public void afterLast() throws InvalidResultSetAccessException { 604 try { 605 this.resultSet.afterLast(); 606 } 607 catch (SQLException se) { 608 throw new InvalidResultSetAccessException(se); 609 } 610 } 611 612 /** 613 * @see java.sql.ResultSet#beforeFirst() 614 */ 615 @Override 616 public void beforeFirst() throws InvalidResultSetAccessException { 617 try { 618 this.resultSet.beforeFirst(); 619 } 620 catch (SQLException se) { 621 throw new InvalidResultSetAccessException(se); 622 } 623 } 624 625 /** 626 * @see java.sql.ResultSet#first() 627 */ 628 @Override 629 public boolean first() throws InvalidResultSetAccessException { 630 try { 631 return this.resultSet.first(); 632 } 633 catch (SQLException se) { 634 throw new InvalidResultSetAccessException(se); 635 } 636 } 637 638 /** 639 * @see java.sql.ResultSet#getRow() 640 */ 641 @Override 642 public int getRow() throws InvalidResultSetAccessException { 643 try { 644 return this.resultSet.getRow(); 645 } 646 catch (SQLException se) { 647 throw new InvalidResultSetAccessException(se); 648 } 649 } 650 651 /** 652 * @see java.sql.ResultSet#isAfterLast() 653 */ 654 @Override 655 public boolean isAfterLast() throws InvalidResultSetAccessException { 656 try { 657 return this.resultSet.isAfterLast(); 658 } 659 catch (SQLException se) { 660 throw new InvalidResultSetAccessException(se); 661 } 662 } 663 664 /** 665 * @see java.sql.ResultSet#isBeforeFirst() 666 */ 667 @Override 668 public boolean isBeforeFirst() throws InvalidResultSetAccessException { 669 try { 670 return this.resultSet.isBeforeFirst(); 671 } 672 catch (SQLException se) { 673 throw new InvalidResultSetAccessException(se); 674 } 675 } 676 677 /** 678 * @see java.sql.ResultSet#isFirst() 679 */ 680 @Override 681 public boolean isFirst() throws InvalidResultSetAccessException { 682 try { 683 return this.resultSet.isFirst(); 684 } 685 catch (SQLException se) { 686 throw new InvalidResultSetAccessException(se); 687 } 688 } 689 690 /** 691 * @see java.sql.ResultSet#isLast() 692 */ 693 @Override 694 public boolean isLast() throws InvalidResultSetAccessException { 695 try { 696 return this.resultSet.isLast(); 697 } 698 catch (SQLException se) { 699 throw new InvalidResultSetAccessException(se); 700 } 701 } 702 703 /** 704 * @see java.sql.ResultSet#last() 705 */ 706 @Override 707 public boolean last() throws InvalidResultSetAccessException { 708 try { 709 return this.resultSet.last(); 710 } 711 catch (SQLException se) { 712 throw new InvalidResultSetAccessException(se); 713 } 714 } 715 716 /** 717 * @see java.sql.ResultSet#next() 718 */ 719 @Override 720 public boolean next() throws InvalidResultSetAccessException { 721 try { 722 return this.resultSet.next(); 723 } 724 catch (SQLException se) { 725 throw new InvalidResultSetAccessException(se); 726 } 727 } 728 729 /** 730 * @see java.sql.ResultSet#previous() 731 */ 732 @Override 733 public boolean previous() throws InvalidResultSetAccessException { 734 try { 735 return this.resultSet.previous(); 736 } 737 catch (SQLException se) { 738 throw new InvalidResultSetAccessException(se); 739 } 740 } 741 742 /** 743 * @see java.sql.ResultSet#relative(int) 744 */ 745 @Override 746 public boolean relative(int rows) throws InvalidResultSetAccessException { 747 try { 748 return this.resultSet.relative(rows); 749 } 750 catch (SQLException se) { 751 throw new InvalidResultSetAccessException(se); 752 } 753 } 754 755 /** 756 * @see java.sql.ResultSet#wasNull() 757 */ 758 @Override 759 public boolean wasNull() throws InvalidResultSetAccessException { 760 try { 761 return this.resultSet.wasNull(); 762 } 763 catch (SQLException se) { 764 throw new InvalidResultSetAccessException(se); 765 } 766 } 767 768}