001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * -------------------
028 * JDBCPieDataset.java
029 * -------------------
030 * (C) Copyright 2002-present, by Bryan Scott and Contributors.
031 *
032 * Original Author:  Bryan Scott; Andy
033 * Contributor(s):   David Gilbert;
034 *                   Thomas Morgner;
035 *
036 */
037
038package org.jfree.data.jdbc;
039
040import java.sql.Connection;
041import java.sql.DriverManager;
042import java.sql.ResultSet;
043import java.sql.ResultSetMetaData;
044import java.sql.SQLException;
045import java.sql.Statement;
046import java.sql.Timestamp;
047import java.sql.Types;
048
049import org.jfree.data.general.DefaultPieDataset;
050import org.jfree.data.general.PieDataset;
051
052/**
053 * A {@link PieDataset} that reads data from a database via JDBC.
054 * <P>
055 * A query should be supplied that returns data in two columns, the first
056 * containing VARCHAR data, and the second containing numerical data.  The
057 * data is cached in-memory and can be refreshed at any time.
058 */
059public class JDBCPieDataset extends DefaultPieDataset {
060
061    /** For serialization. */
062    static final long serialVersionUID = -8753216855496746108L;
063
064    /** The database connection. */
065    private transient Connection connection;
066
067    /**
068     * Creates a new JDBCPieDataset and establishes a new database connection.
069     *
070     * @param url  the URL of the database connection.
071     * @param driverName  the database driver class name.
072     * @param user  the database user.
073     * @param password  the database users password.
074     *
075     * @throws ClassNotFoundException if the driver cannot be found.
076     * @throws SQLException if there is a problem obtaining a database
077     *                      connection.
078     */
079    public JDBCPieDataset(String url,
080                          String driverName,
081                          String user,
082                          String password)
083        throws SQLException, ClassNotFoundException {
084
085        Class.forName(driverName);
086        this.connection = DriverManager.getConnection(url, user, password);
087    }
088
089    /**
090     * Creates a new JDBCPieDataset using a pre-existing database connection.
091     * <P>
092     * The dataset is initially empty, since no query has been supplied yet.
093     *
094     * @param con  the database connection.
095     */
096    public JDBCPieDataset(Connection con) {
097        if (con == null) {
098            throw new NullPointerException("A connection must be supplied.");
099        }
100        this.connection = con;
101    }
102
103
104    /**
105     * Creates a new JDBCPieDataset using a pre-existing database connection.
106     * <P>
107     * The dataset is initialised with the supplied query.
108     *
109     * @param con  the database connection.
110     * @param query  the database connection.
111     *
112     * @throws SQLException if there is a problem executing the query.
113     */
114    public JDBCPieDataset(Connection con, String query) throws SQLException {
115        this(con);
116        executeQuery(query);
117    }
118
119    /**
120     *  ExecuteQuery will attempt execute the query passed to it against the
121     *  existing database connection.  If no connection exists then no action
122     *  is taken.
123     *  The results from the query are extracted and cached locally, thus
124     *  applying an upper limit on how many rows can be retrieved successfully.
125     *
126     * @param  query  the query to be executed.
127     *
128     * @throws SQLException if there is a problem executing the query.
129     */
130    public void executeQuery(String query) throws SQLException {
131      executeQuery(this.connection, query);
132    }
133
134    /**
135     *  ExecuteQuery will attempt execute the query passed to it against the
136     *  existing database connection.  If no connection exists then no action
137     *  is taken.
138     *  The results from the query are extracted and cached locally, thus
139     *  applying an upper limit on how many rows can be retrieved successfully.
140     *
141     * @param  query  the query to be executed
142     * @param  con  the connection the query is to be executed against
143     *
144     * @throws SQLException if there is a problem executing the query.
145     */
146    public void executeQuery(Connection con, String query) throws SQLException {
147
148        Statement statement = null;
149        ResultSet resultSet = null;
150
151        try {
152            statement = con.createStatement();
153            resultSet = statement.executeQuery(query);
154            ResultSetMetaData metaData = resultSet.getMetaData();
155
156            int columnCount = metaData.getColumnCount();
157            if (columnCount != 2) {
158                throw new SQLException(
159                    "Invalid sql generated.  PieDataSet requires 2 columns only"
160                );
161            }
162
163            int columnType = metaData.getColumnType(2);
164            double value;
165            while (resultSet.next()) {
166                Comparable key = resultSet.getString(1);
167                switch (columnType) {
168                    case Types.NUMERIC:
169                    case Types.REAL:
170                    case Types.INTEGER:
171                    case Types.DOUBLE:
172                    case Types.FLOAT:
173                    case Types.DECIMAL:
174                    case Types.BIGINT:
175                        value = resultSet.getDouble(2);
176                        setValue(key, value);
177                        break;
178
179                    case Types.DATE:
180                    case Types.TIME:
181                    case Types.TIMESTAMP:
182                        Timestamp date = resultSet.getTimestamp(2);
183                        value = date.getTime();
184                        setValue(key, value);
185                        break;
186
187                    default:
188                        System.err.println(
189                                "JDBCPieDataset - unknown data type");
190                        break;
191                }
192            }
193
194            fireDatasetChanged();
195
196        }
197        finally {
198            if (resultSet != null) {
199                try {
200                    resultSet.close();
201                }
202                catch (Exception e) {
203                    System.err.println("JDBCPieDataset: swallowing exception.");
204                }
205            }
206            if (statement != null) {
207                try {
208                    statement.close();
209                }
210                catch (Exception e) {
211                    System.err.println("JDBCPieDataset: swallowing exception.");
212                }
213            }
214        }
215    }
216
217
218    /**
219     * Close the database connection
220     */
221    public void close() {
222        try {
223            this.connection.close();
224        }
225        catch (Exception e) {
226            System.err.println("JdbcXYDataset: swallowing exception.");
227        }
228    }
229}