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}