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}