001/*
002 * Copyright 2002-2018 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.core;
018
019import java.sql.ResultSet;
020import java.sql.ResultSetMetaData;
021import java.sql.SQLException;
022
023import org.springframework.jdbc.support.JdbcUtils;
024import org.springframework.lang.Nullable;
025
026/**
027 * Implementation of RowCallbackHandler. Convenient superclass for callback handlers.
028 * An instance can only be used once.
029 *
030 * <p>We can either use this on its own (for example, in a test case, to ensure
031 * that our result sets have valid dimensions), or use it as a superclass
032 * for callback handlers that actually do something, and will benefit
033 * from the dimension information it provides.
034 *
035 * <p>A usage example with JdbcTemplate:
036 *
037 * <pre class="code">JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);  // reusable object
038 *
039 * RowCountCallbackHandler countCallback = new RowCountCallbackHandler();  // not reusable
040 * jdbcTemplate.query("select * from user", countCallback);
041 * int rowCount = countCallback.getRowCount();</pre>
042 *
043 * @author Rod Johnson
044 * @since May 3, 2001
045 */
046public class RowCountCallbackHandler implements RowCallbackHandler {
047
048        /** Rows we've seen so far. */
049        private int rowCount;
050
051        /** Columns we've seen so far. */
052        private int columnCount;
053
054        /**
055         * Indexed from 0. Type (as in java.sql.Types) for the columns
056         * as returned by ResultSetMetaData object.
057         */
058        @Nullable
059        private int[] columnTypes;
060
061        /**
062         * Indexed from 0. Column name as returned by ResultSetMetaData object.
063         */
064        @Nullable
065        private String[] columnNames;
066
067
068        /**
069         * Implementation of ResultSetCallbackHandler.
070         * Work out column size if this is the first row, otherwise just count rows.
071         * <p>Subclasses can perform custom extraction or processing
072         * by overriding the {@code processRow(ResultSet, int)} method.
073         * @see #processRow(java.sql.ResultSet, int)
074         */
075        @Override
076        public final void processRow(ResultSet rs) throws SQLException {
077                if (this.rowCount == 0) {
078                        ResultSetMetaData rsmd = rs.getMetaData();
079                        this.columnCount = rsmd.getColumnCount();
080                        this.columnTypes = new int[this.columnCount];
081                        this.columnNames = new String[this.columnCount];
082                        for (int i = 0; i < this.columnCount; i++) {
083                                this.columnTypes[i] = rsmd.getColumnType(i + 1);
084                                this.columnNames[i] = JdbcUtils.lookupColumnName(rsmd, i + 1);
085                        }
086                        // could also get column names
087                }
088                processRow(rs, this.rowCount++);
089        }
090
091        /**
092         * Subclasses may override this to perform custom extraction
093         * or processing. This class's implementation does nothing.
094         * @param rs the ResultSet to extract data from. This method is
095         * invoked for each row
096         * @param rowNum number of the current row (starting from 0)
097         */
098        protected void processRow(ResultSet rs, int rowNum) throws SQLException {
099        }
100
101
102        /**
103         * Return the types of the columns as java.sql.Types constants
104         * Valid after processRow is invoked the first time.
105         * @return the types of the columns as java.sql.Types constants.
106         * <b>Indexed from 0 to n-1.</b>
107         */
108        @Nullable
109        public final int[] getColumnTypes() {
110                return this.columnTypes;
111        }
112
113        /**
114         * Return the names of the columns.
115         * Valid after processRow is invoked the first time.
116         * @return the names of the columns.
117         * <b>Indexed from 0 to n-1.</b>
118         */
119        @Nullable
120        public final String[] getColumnNames() {
121                return this.columnNames;
122        }
123
124        /**
125         * Return the row count of this ResultSet.
126         * Only valid after processing is complete
127         * @return the number of rows in this ResultSet
128         */
129        public final int getRowCount() {
130                return this.rowCount;
131        }
132
133        /**
134         * Return the number of columns in this result set.
135         * Valid once we've seen the first row,
136         * so subclasses can use it during processing
137         * @return the number of columns in this result set
138         */
139        public final int getColumnCount() {
140                return this.columnCount;
141        }
142
143}