/*
* Copyright (c) 2008, SQL Power Group Inc.
*
* This file is part of SQL Power Library.
*
* SQL Power Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* SQL Power 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ca.sqlpower.util.reservoir;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
/**
* A reservoir data source implementation that gets its data from the results of
* an SQL query. This class attempts to configure the connection and statement
* so that the rows that are skipped will not be transferred into the JVM,
* however that is not possible on all database platforms. If it is possible
* to skip rows on your platform, but this class isn't achieving that behaviour,
* please send us a patch that makes it work!
*/
public class JDBCReserviorDataSource implements ReservoirDataSource<Object[]> {
private final Statement stmt;
private final ResultSet rs;
private final ResultSetMetaData rsmd;
private final int colCount;
/**
* Returns the number of rows read or skipped so far. Once
* {@link #hasNext()} has returned false, this will be the total number of
* rows that were returned by the query.
*/
private int rowCount;
/**
*
* @param con The connection to use. WARNING: auto-commit will be turned off
* for this connection! If you want auto-commit on, turn it back on when you're
* finished with this reservoir data source.
* @param query The query to execute
* @throws SQLException if there is a problem reading the data from the database.
* This will most likely be caused by an invalid select statement, but anything
* is possible!
*/
public JDBCReserviorDataSource(Connection con, String query) throws SQLException {
con.setAutoCommit(false);
stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(1);
rs = stmt.executeQuery(query);
rsmd = rs.getMetaData();
colCount = rsmd.getColumnCount();
}
/**
* The element type is an array: the column values of a row returned by the query.
*/
public Class<Object[]> getElementType() {
return Object[].class;
}
public boolean hasNext() throws ReservoirDataException {
try {
return !rs.isLast() && !(rs.getRow() == 0 && !rs.isBeforeFirst());
} catch (SQLException e) {
throw new ReservoirDataException(e);
}
}
public Object[] readNextRecord() throws ReservoirDataException {
try {
boolean hasNext = rs.next();
if (!hasNext) throw new ReservoirDataException("Attempted to read past last record");
rowCount++;
Object[] rowValues = new Object[colCount];
for (int i = 0; i < colCount; i++) {
rowValues[i] = rs.getObject(i + 1);
}
return rowValues;
} catch (SQLException e) {
throw new ReservoirDataException(e);
}
}
public void skipRecords(int count) throws ReservoirDataException {
try {
boolean onValidRow = rs.relative(count);
if (onValidRow) {
rowCount = rs.getRow();
} else {
rowCount = rs.getRow() - 1;
}
} catch (SQLException e) {
throw new ReservoirDataException(e);
}
}
/**
* This is exposed as package-private so that the tests can examine
* the statement settings.
*/
Statement getStatement() {
return stmt;
}
/**
* Returns the number of rows read or skipped so far. Once
* {@link #hasNext()} has returned false, this will be the total number of
* rows that were returned by the query.
*/
public int getRowCount() {
return rowCount;
}
}