/* * Copyright 2006-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.batch.item.database; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.jdbc.core.PreparedStatementSetter; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.support.JdbcUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** * <p> * Simple item reader implementation that opens a JDBC cursor and continually retrieves the * next row in the ResultSet. * </p> * * <p> * The statement used to open the cursor is created with the 'READ_ONLY' option since a non read-only * cursor may unnecessarily lock tables or rows. It is also opened with 'TYPE_FORWARD_ONLY' option. * By default the cursor will be opened using a separate connection which means that it will not participate * in any transactions created as part of the step processing. * </p> * * <p> * Each call to {@link #read()} will call the provided RowMapper, passing in the * ResultSet. * </p> * * @author Lucas Ward * @author Peter Zozom * @author Robert Kasanicky * @author Thomas Risberg */ public class JdbcCursorItemReader<T> extends AbstractCursorItemReader<T> { PreparedStatement preparedStatement; PreparedStatementSetter preparedStatementSetter; String sql; RowMapper<T> rowMapper; public JdbcCursorItemReader() { super(); setName(ClassUtils.getShortName(JdbcCursorItemReader.class)); } /** * Set the RowMapper to be used for all calls to read(). * * @param rowMapper the mapper used to map each item */ public void setRowMapper(RowMapper<T> rowMapper) { this.rowMapper = rowMapper; } /** * Set the SQL statement to be used when creating the cursor. This statement * should be a complete and valid SQL statement, as it will be run directly * without any modification. * * @param sql SQL statement */ public void setSql(String sql) { this.sql = sql; } /** * Set the PreparedStatementSetter to use if any parameter values that need * to be set in the supplied query. * * @param preparedStatementSetter PreparedStatementSetter responsible for filling out the statement */ public void setPreparedStatementSetter(PreparedStatementSetter preparedStatementSetter) { this.preparedStatementSetter = preparedStatementSetter; } /** * Assert that mandatory properties are set. * * @throws IllegalArgumentException if either data source or SQL properties * not set. */ @Override public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); Assert.notNull(sql, "The SQL query must be provided"); Assert.notNull(rowMapper, "RowMapper must be provided"); } @Override protected void openCursor(Connection con) { try { if (isUseSharedExtendedConnection()) { preparedStatement = con.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); } else { preparedStatement = con.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); } applyStatementSettings(preparedStatement); if (this.preparedStatementSetter != null) { preparedStatementSetter.setValues(preparedStatement); } this.rs = preparedStatement.executeQuery(); handleWarnings(preparedStatement); } catch (SQLException se) { close(); throw getExceptionTranslator().translate("Executing query", getSql(), se); } } @Override protected T readCursor(ResultSet rs, int currentRow) throws SQLException { return rowMapper.mapRow(rs, currentRow); } /** * Close the cursor and database connection. */ @Override protected void cleanupOnClose() throws Exception { JdbcUtils.closeStatement(this.preparedStatement); } @Override public String getSql() { return this.sql; } }