/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2004-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.metadata.sql; import java.sql.Array; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.geotools.util.logging.Logging; /** * The result of a query for metadata attributes. This object {@linkplain PreparedStatement * prepares a statement} once for ever for a given table. When a particular record in this * table is fetched, the {@link ResultSet} is automatically constructed. If many attributes * are fetched consecutivly for the same record, then the same {@link ResultSet} is reused. * * @since 2.1 * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) * * @todo Automatically close the ResultSet after some delay (e.g. 2 minutes). */ final class MetadataResult { /** * The table name, used for formatting error message. */ private final String tableName; /** * The statements for a specific table. */ private final PreparedStatement statement; /** * The results, or {@code null} if not yet determined. */ private ResultSet results; /** * The identifier (usually the primary key) for current results. * If the record to fetch doesn't have the same identifier, then * the {@link #results} will need to be closed and reconstructed. */ private String identifier; /** * Constructs a metadata result from the specified connection. * * @param connection The connection to the database. * @param query The SQL query. The first question mark will be replaced * by the table name. * @param tableName The table name. * @throws SQLException if the statement can't be created. */ public MetadataResult(final Connection connection, final String query, final String tableName) throws SQLException { this.tableName = tableName; final int index = query.indexOf('?'); if (index < 0) { // TODO: localize throw new SQLException("Invalid query"); } final StringBuilder buffer = new StringBuilder(query); buffer.replace(index, index+1, tableName); statement = connection.prepareStatement(buffer.toString()); } /** * Returns the result set for the given record. * * @param identifier The object identifier, usually the primary key value. * @return The result set. * @throws SQLException if an SQL operation failed. */ private ResultSet getResultSet(final String identifier) throws SQLException { if (results != null) { if (this.identifier.equals(identifier)) { return results; } if (results.next()) { // TODO: Localize Logging.getLogger(MetadataResult.class).warning("Duplicate identifier: "+identifier); } results.close(); results = null; // In case the 'results = ...' below will fails. } this.identifier = identifier; statement.setString(1, identifier); results = statement.executeQuery(); if (!results.next()) { results.close(); results = null; throw new SQLException("Metadata not found: \""+identifier+"\" in table \""+tableName+'"'); // TODO: localize } return results; } /** * Returns the attribute value in the given column for the given record. * * @param identifier The object identifier, usually the primary key value. * @param columnName The column name of the attribute to search. * @return The attribute value. * @throws SQLException if an SQL operation failed. */ public Object getObject(final String identifier, final String columnName) throws SQLException { return getResultSet(identifier).getObject(columnName); } /** * Returns the attribute value in the given column for the given record. * * @param identifier The object identifier, usually the primary key value. * @param columnName The column name of the attribute to search. * @return The attribute value. * @throws SQLException if an SQL operation failed. */ public Object getArray(final String identifier, final String columnName) throws SQLException { final Array array = getResultSet(identifier).getArray(columnName); return (array!=null) ? array.getArray() : null; } /** * Returns the attribute value in the given column for the given record. * * @param identifier The object identifier, usually the primary key value. * @param columnName The column name of the attribute to search. * @return The attribute value. * @throws SQLException if an SQL operation failed. */ public int getInt(final String identifier, final String columnName) throws SQLException { return getResultSet(identifier).getInt(columnName); } /** * Returns the attribute value in the given column for the given record. * * @param identifier The object identifier, usually the primary key value. * @param columnName The column name of the attribute to search. * @return The attribute value. * @throws SQLException if an SQL operation failed. */ public String getString(final String identifier, final String columnName) throws SQLException { return getResultSet(identifier).getString(columnName); } /** * Returns the string value in the first column of the given record. * This is used for fetching the name of a code list element. * * @param code The object identifier, usually the primary key value. * @return The string value found in the first column. * @throws SQLException if an SQL operation failed. */ public String getString(final String code) throws SQLException { return getResultSet(code).getString(1); } /** * Returns {@code true} if the last value returned by a {@code getFoo} method was null. */ public boolean wasNull() throws SQLException { return results.wasNull(); } /** * Close this statement and free all resources. * After this method has been invoked, this object can't be used anymore. * * @throws SQLException if an SQL operation failed. */ public void close() throws SQLException { if (results != null) { results.close(); results = null; } statement.close(); } }