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