/* * Copyright 2010, Andrew M Gibson * * www.andygibson.net * * This file is part of DataValve. * * DataValve 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, either version 3 of the License, or * (at your option) any later version. * * DataValve 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. * * * You should have received a copy of the GNU Lesser General Public License * along with DataValve. If not, see <http://www.gnu.org/licenses/>. * */ package org.fluttercode.datavalve.provider.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.fluttercode.datavalve.params.Parameter; import org.fluttercode.datavalve.provider.AbstractQueryDataProvider; import org.fluttercode.datavalve.provider.QueryDataProvider; import org.fluttercode.datavalve.provider.util.DataQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Provides the base implementation for a SQL based {@link QueryDataProvider} * which uses restrictions, parameters and ordering to define the final results. * <p/> * Because this is a SQL based dataset, we need some mechanism to turn the raw * data into an object. We do this using the template pattern by defining the * method * {@link AbstractJdbcDataProvider#createListFromResultSet(ResultSet, Integer, Integer) * as abstract. This will be implemented in sub classes to convert the result * set data into a strongly typed object. * * * @author Andy Gibson * * @param <T> * The type of object that will be returned in the dataset. */ public abstract class AbstractJdbcDataProvider<T> extends AbstractQueryDataProvider<T> { private static final long serialVersionUID = 1L; private static Logger log = LoggerFactory .getLogger(AbstractJdbcDataProvider.class); private transient Connection connection; public AbstractJdbcDataProvider() { this(null); } public AbstractJdbcDataProvider(Connection connection) { this.connection = connection; } /** * Creates a new {@link PreparedStatement} for use in fetching the count or * the results. * * @param query * {@link DataQuery} instance that contains the query sql and * parameters * @return the JDBC prepared statement reflecting the {@link DataQuery} * @throws SQLException */ private PreparedStatement buildPreparedStatement(DataQuery query) throws SQLException { PreparedStatement statement = connection.prepareStatement(query .getStatement()); List<Parameter> params = query.getParameters(); for (int i = 0; i < params.size(); i++) { Parameter param = params.get(i); log.debug("Setting parameter {} to '{}'", i, param.getValue()); statement.setObject(i + 1, param.getValue()); } return statement; } @Override protected Integer queryForCount(DataQuery query) { PreparedStatement statement = null; ResultSet resultSet = null; try { try { statement = buildPreparedStatement(query); resultSet = statement.executeQuery(); if (resultSet.next()) { int value = resultSet.getInt(1); log.debug("Fetch Result Count SQL returned {}", value); return value; } else { return 0; } } finally { if (resultSet != null) { resultSet.close(); } if (statement != null) { statement.close(); } } } catch (SQLException ex) { ex.printStackTrace(); } return 0; } @Override protected List<T> queryForResults(DataQuery query, Integer firstResult, Integer count) { PreparedStatement statement = null; ResultSet resultSet = null; try { try { statement = buildPreparedStatement(query); resultSet = statement.executeQuery(); List<T> results = createListFromResultSet(resultSet, firstResult, count); log.debug("Results processor returned {} results", results .size()); return results; } finally { if (resultSet != null) { resultSet.close(); } if (statement != null) { statement.close(); } } } catch (SQLException ex) { ex.printStackTrace(); } return Collections.emptyList(); } /** * Iterate over the given result set calling the * {@link AbstractJdbcQueryDataProvider#resultSetObjectMapper} for each row * in the result set. Each created object is added to the result list which * is then returned to the user. * * @param resultSet * list of records we are converting to objects. * @param firstRow * number of the row we start processing from * @param maxRows * used to limit the number of rows we process (null indicates * all rows) * @return List of objects derived from the result set from firstRow to * firstRow + maxRows or to the end of the resultset if maxRows == 0 * @throws SQLException */ protected List<T> createListFromResultSet(ResultSet resultSet, Integer firstRow, Integer maxRows) throws SQLException { List<T> results = new ArrayList<T>(); if (!skipRows(resultSet, firstRow)) { return results; } while (true) { results.add(createObjectFromResultSet(resultSet)); // if this is paged, limit the number of rows loaded if (maxRows != null && results.size() == maxRows) { return results; } // if there are no more results, return the list. if (!resultSet.next()) { return results; } } } /** * Moves the result set to the row indicated by first row. * * @param resultSet * Result set to skip rows in * @param firstRow * number of rows we need to skip, null or zero for the first row * @return returns true if there are more rows left to process, or false if * the end of the result set was reached * @throws SQLException */ private final boolean skipRows(ResultSet resultSet, Integer firstRow) throws SQLException { // if firstRow is null, then just return if (firstRow == null) { firstRow = 0; } int index = 0; while (index < firstRow && resultSet.next()) { index++; } return resultSet.next(); } public abstract T createObjectFromResultSet(ResultSet resultSet) throws SQLException; }