View Javadoc

1   /*
2    * Copyright 2006-2013 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.batch.item.database;
18  
19  import java.sql.Connection;
20  import java.sql.PreparedStatement;
21  import java.sql.ResultSet;
22  import java.sql.SQLException;
23  
24  import org.springframework.jdbc.core.PreparedStatementSetter;
25  import org.springframework.jdbc.core.RowMapper;
26  import org.springframework.jdbc.support.JdbcUtils;
27  import org.springframework.util.Assert;
28  import org.springframework.util.ClassUtils;
29  
30  /**
31   * <p>
32   * Simple item reader implementation that opens a JDBC cursor and continually retrieves the
33   * next row in the ResultSet.
34   * </p>
35   *
36   * <p>
37   * The statement used to open the cursor is created with the 'READ_ONLY' option since a non read-only
38   * cursor may unnecessarily lock tables or rows. It is also opened with 'TYPE_FORWARD_ONLY' option.
39   * By default the cursor will be opened using a separate connection which means that it will not participate
40   * in any transactions created as part of the step processing.
41   * </p>
42   *
43   * <p>
44   * Each call to {@link #read()} will call the provided RowMapper, passing in the
45   * ResultSet.
46   * </p>
47   *
48   * @author Lucas Ward
49   * @author Peter Zozom
50   * @author Robert Kasanicky
51   * @author Thomas Risberg
52   */
53  @SuppressWarnings("rawtypes")
54  public class JdbcCursorItemReader<T> extends AbstractCursorItemReader<T> {
55  
56  	PreparedStatement preparedStatement;
57  
58  	PreparedStatementSetter preparedStatementSetter;
59  
60  	String sql;
61  
62  	RowMapper rowMapper;
63  
64  	public JdbcCursorItemReader() {
65  		super();
66  		setName(ClassUtils.getShortName(JdbcCursorItemReader.class));
67  	}
68  
69  	/**
70  	 * Set the RowMapper to be used for all calls to read().
71  	 *
72  	 * @param rowMapper
73  	 */
74  	public void setRowMapper(RowMapper rowMapper) {
75  		this.rowMapper = rowMapper;
76  	}
77  
78  	/**
79  	 * Set the SQL statement to be used when creating the cursor. This statement
80  	 * should be a complete and valid SQL statement, as it will be run directly
81  	 * without any modification.
82  	 *
83  	 * @param sql
84  	 */
85  	public void setSql(String sql) {
86  		this.sql = sql;
87  	}
88  
89  	/**
90  	 * Set the PreparedStatementSetter to use if any parameter values that need
91  	 * to be set in the supplied query.
92  	 *
93  	 * @param preparedStatementSetter
94  	 */
95  	public void setPreparedStatementSetter(PreparedStatementSetter preparedStatementSetter) {
96  		this.preparedStatementSetter = preparedStatementSetter;
97  	}
98  
99  	/**
100 	 * Assert that mandatory properties are set.
101 	 *
102 	 * @throws IllegalArgumentException if either data source or sql properties
103 	 * not set.
104 	 */
105 	@Override
106 	public void afterPropertiesSet() throws Exception {
107 		super.afterPropertiesSet();
108 		Assert.notNull(sql, "The SQL query must be provided");
109 		Assert.notNull(rowMapper, "RowMapper must be provided");
110 	}
111 
112 
113 	@Override
114 	protected void openCursor(Connection con) {
115 		try {
116 			if (isUseSharedExtendedConnection()) {
117 				preparedStatement = con.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
118 						ResultSet.HOLD_CURSORS_OVER_COMMIT);
119 			}
120 			else {
121 				preparedStatement = con.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
122 			}
123 			applyStatementSettings(preparedStatement);
124 			if (this.preparedStatementSetter != null) {
125 				preparedStatementSetter.setValues(preparedStatement);
126 			}
127 			this.rs = preparedStatement.executeQuery();
128 			handleWarnings(preparedStatement);
129 		}
130 		catch (SQLException se) {
131 			close();
132 			throw getExceptionTranslator().translate("Executing query", getSql(), se);
133 		}
134 
135 	}
136 
137 
138 	@Override
139 	@SuppressWarnings("unchecked")
140 	protected T readCursor(ResultSet rs, int currentRow) throws SQLException {
141 		return (T) rowMapper.mapRow(rs, currentRow);
142 	}
143 
144 	/**
145 	 * Close the cursor and database connection.
146 	 */
147 	@Override
148 	protected void cleanupOnClose() throws Exception {
149 		JdbcUtils.closeStatement(this.preparedStatement);
150 	}
151 
152 	@Override
153 	public String getSql() {
154 		return this.sql;
155 	}
156 }