001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ------------------------ 028 * JDBCCategoryDataset.java 029 * ------------------------ 030 * (C) Copyright 2002-2008, by Bryan Scott and Contributors. 031 * 032 * Original Author: Bryan Scott; Andy; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Thomas Morgner; 035 * 036 * Changes 037 * ------- 038 * 26-Apr-2002 : Creation based on JdbcXYDataSet, using code contributed from 039 * Andy; 040 * 13-Aug-2002 : Updated Javadocs, import statements and formatting (DG); 041 * 03-Sep-2002 : Added fix for bug 591385 (DG); 042 * 18-Sep-2002 : Updated to support BIGINT (BS); 043 * 16-Oct-2002 : Added fix for bug 586667 (DG); 044 * 03-Feb-2003 : Added Types.DECIMAL (see bug report 677814) (DG); 045 * 13-Jun-2003 : Added Types.TIME as suggest by Bryan Scott in the forum (DG); 046 * 30-Jun-2003 : CVS Write test (BS); 047 * 30-Jul-2003 : Added empty contructor and executeQuery(connection,string) 048 * method (BS); 049 * 29-Aug-2003 : Added a 'transpose' flag, so that data can be easily 050 * transposed if required (DG); 051 * 10-Sep-2003 : Added support for additional JDBC types (DG); 052 * 24-Sep-2003 : Added clearing results from previous queries to executeQuery 053 * following being highlighted on online forum (BS); 054 * 02-Dec-2003 : Throwing exceptions allows to handle errors, removed default 055 * constructor, as without a connection, a query can never be 056 * executed (TM); 057 * 04-Dec-2003 : Added missing Javadocs (DG); 058 * ------------- JFREECHART 1.0.0 --------------------------------------------- 059 * 08-Mar-2006 : Fixed bug 1445748 where an exception is thrown if 060 * executeQuery() is called more than once (DG); 061 * 062 */ 063 064 package org.jfree.data.jdbc; 065 066 import java.sql.Connection; 067 import java.sql.Date; 068 import java.sql.DriverManager; 069 import java.sql.ResultSet; 070 import java.sql.ResultSetMetaData; 071 import java.sql.SQLException; 072 import java.sql.Statement; 073 import java.sql.Types; 074 075 import org.jfree.data.category.CategoryDataset; 076 import org.jfree.data.category.DefaultCategoryDataset; 077 078 /** 079 * A {@link CategoryDataset} implementation over a database JDBC result set. 080 * The dataset is populated via a call to {@link #executeQuery(String)} with 081 * the string SQL query. The SQL query must return at least two columns. The 082 * first column will be the category name and remaining columns values (each 083 * column represents a series). Subsequent calls to 084 * {@link #executeQuery(String)} will refresh the dataset. 085 * <p> 086 * The database connection is read-only and no write back facility exists. 087 * <p> 088 * NOTE: Many people have found this class too restrictive in general use. 089 * For the greatest flexibility, please consider writing your own code to read 090 * data from a <code>ResultSet</code> and populate a 091 * {@link DefaultCategoryDataset} directly. 092 */ 093 public class JDBCCategoryDataset extends DefaultCategoryDataset { 094 095 /** For serialization. */ 096 static final long serialVersionUID = -3080395327918844965L; 097 098 /** The database connection. */ 099 private transient Connection connection; 100 101 /** 102 * A flag the controls whether or not the table is transposed. The default 103 * is 'true' because this provides the behaviour described in the 104 * documentation. 105 */ 106 private boolean transpose = true; 107 108 109 /** 110 * Creates a new dataset with a database connection. 111 * 112 * @param url the URL of the database connection. 113 * @param driverName the database driver class name. 114 * @param user the database user. 115 * @param passwd the database user's password. 116 * 117 * @throws ClassNotFoundException if the driver cannot be found. 118 * @throws SQLException if there is an error obtaining a connection to the 119 * database. 120 */ 121 public JDBCCategoryDataset(String url, 122 String driverName, 123 String user, 124 String passwd) 125 throws ClassNotFoundException, SQLException { 126 127 Class.forName(driverName); 128 this.connection = DriverManager.getConnection(url, user, passwd); 129 } 130 131 /** 132 * Create a new dataset with the given database connection. 133 * 134 * @param connection the database connection. 135 */ 136 public JDBCCategoryDataset(Connection connection) { 137 if (connection == null) { 138 throw new NullPointerException("A connection must be supplied."); 139 } 140 this.connection = connection; 141 } 142 143 /** 144 * Creates a new dataset with the given database connection, and executes 145 * the supplied query to populate the dataset. 146 * 147 * @param connection the connection. 148 * @param query the query. 149 * 150 * @throws SQLException if there is a problem executing the query. 151 */ 152 public JDBCCategoryDataset(Connection connection, String query) 153 throws SQLException { 154 this(connection); 155 executeQuery(query); 156 } 157 158 /** 159 * Returns a flag that controls whether or not the table values are 160 * transposed when added to the dataset. 161 * 162 * @return A boolean. 163 */ 164 public boolean getTranspose() { 165 return this.transpose; 166 } 167 168 /** 169 * Sets a flag that controls whether or not the table values are transposed 170 * when added to the dataset. 171 * 172 * @param transpose the flag. 173 */ 174 public void setTranspose(boolean transpose) { 175 this.transpose = transpose; 176 } 177 178 /** 179 * Populates the dataset by executing the supplied query against the 180 * existing database connection. If no connection exists then no action 181 * is taken. 182 * <p> 183 * The results from the query are extracted and cached locally, thus 184 * applying an upper limit on how many rows can be retrieved successfully. 185 * 186 * @param query the query. 187 * 188 * @throws SQLException if there is a problem executing the query. 189 */ 190 public void executeQuery(String query) throws SQLException { 191 executeQuery(this.connection, query); 192 } 193 194 /** 195 * Populates the dataset by executing the supplied query against the 196 * existing database connection. If no connection exists then no action 197 * is taken. 198 * <p> 199 * The results from the query are extracted and cached locally, thus 200 * applying an upper limit on how many rows can be retrieved successfully. 201 * 202 * @param con the connection. 203 * @param query the query. 204 * 205 * @throws SQLException if there is a problem executing the query. 206 */ 207 public void executeQuery(Connection con, String query) throws SQLException { 208 209 Statement statement = null; 210 ResultSet resultSet = null; 211 try { 212 statement = con.createStatement(); 213 resultSet = statement.executeQuery(query); 214 ResultSetMetaData metaData = resultSet.getMetaData(); 215 216 int columnCount = metaData.getColumnCount(); 217 218 if (columnCount < 2) { 219 throw new SQLException( 220 "JDBCCategoryDataset.executeQuery() : insufficient columns " 221 + "returned from the database."); 222 } 223 224 // Remove any previous old data 225 int i = getRowCount(); 226 while (--i >= 0) { 227 removeRow(i); 228 } 229 230 while (resultSet.next()) { 231 // first column contains the row key... 232 Comparable rowKey = resultSet.getString(1); 233 for (int column = 2; column <= columnCount; column++) { 234 235 Comparable columnKey = metaData.getColumnName(column); 236 int columnType = metaData.getColumnType(column); 237 238 switch (columnType) { 239 case Types.TINYINT: 240 case Types.SMALLINT: 241 case Types.INTEGER: 242 case Types.BIGINT: 243 case Types.FLOAT: 244 case Types.DOUBLE: 245 case Types.DECIMAL: 246 case Types.NUMERIC: 247 case Types.REAL: { 248 Number value = (Number) resultSet.getObject(column); 249 if (this.transpose) { 250 setValue(value, columnKey, rowKey); 251 } 252 else { 253 setValue(value, rowKey, columnKey); 254 } 255 break; 256 } 257 case Types.DATE: 258 case Types.TIME: 259 case Types.TIMESTAMP: { 260 Date date = (Date) resultSet.getObject(column); 261 Number value = new Long(date.getTime()); 262 if (this.transpose) { 263 setValue(value, columnKey, rowKey); 264 } 265 else { 266 setValue(value, rowKey, columnKey); 267 } 268 break; 269 } 270 case Types.CHAR: 271 case Types.VARCHAR: 272 case Types.LONGVARCHAR: { 273 String string 274 = (String) resultSet.getObject(column); 275 try { 276 Number value = Double.valueOf(string); 277 if (this.transpose) { 278 setValue(value, columnKey, rowKey); 279 } 280 else { 281 setValue(value, rowKey, columnKey); 282 } 283 } 284 catch (NumberFormatException e) { 285 // suppress (value defaults to null) 286 } 287 break; 288 } 289 default: 290 // not a value, can't use it (defaults to null) 291 break; 292 } 293 } 294 } 295 296 fireDatasetChanged(); 297 } 298 finally { 299 if (resultSet != null) { 300 try { 301 resultSet.close(); 302 } 303 catch (Exception e) { 304 // report this? 305 } 306 } 307 if (statement != null) { 308 try { 309 statement.close(); 310 } 311 catch (Exception e) { 312 // report this? 313 } 314 } 315 } 316 } 317 318 }