View Javadoc

1   /*
2    * Copyright 2006-2009 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  
25  import org.springframework.jdbc.core.PreparedStatementSetter;
26  import org.springframework.jdbc.core.RowMapper;
27  import org.springframework.jdbc.support.JdbcUtils;
28  import org.springframework.util.Assert;
29  import org.springframework.util.ClassUtils;
30  
31  /**
32   * <p>
33   * Simple item reader implementation that opens a JDBC cursor and continually retrieves the
34   * next row in the ResultSet. 
35   * </p>
36   * 
37   * <p>
38   * The statement used to open the cursor is created with the 'READ_ONLY' option since a non read-only 
39   * cursor may unnecessarily lock tables or rows. It is also opened with 'TYPE_FORWARD_ONLY' option. 
40   * By default the cursor will be opened using a separate connection which means that it will not participate 
41   * in any transactions created as part of the step processing.
42   * </p>
43   *  
44   * <p>
45   * Each call to {@link #read()} will call the provided RowMapper, passing in the
46   * ResultSet. 
47   * </p>
48   * 
49   * @author Lucas Ward
50   * @author Peter Zozom
51   * @author Robert Kasanicky
52   * @author Thomas Risberg
53   */
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 	public void afterPropertiesSet() throws Exception {
106 		super.afterPropertiesSet();
107 		Assert.notNull(sql, "The SQL query must be provided");
108 		Assert.notNull(rowMapper, "RowMapper must be provided");
109 	}
110 
111 
112 	protected void openCursor(Connection con) {	
113 		try {
114 			if (isUseSharedExtendedConnection()) {
115 				preparedStatement = con.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
116 						ResultSet.HOLD_CURSORS_OVER_COMMIT);
117 			}
118 			else {
119 				preparedStatement = con.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
120 			}
121 			applyStatementSettings(preparedStatement);
122 			if (this.preparedStatementSetter != null) {
123 				preparedStatementSetter.setValues(preparedStatement);
124 			}
125 			this.rs = preparedStatement.executeQuery();
126 			handleWarnings(preparedStatement);
127 		}
128 		catch (SQLException se) {
129 			close();
130 			throw getExceptionTranslator().translate("Executing query", getSql(), se);
131 		}
132 	
133 	}
134 
135 
136 	@SuppressWarnings("unchecked")
137 	protected T readCursor(ResultSet rs, int currentRow) throws SQLException {
138 		return (T) rowMapper.mapRow(rs, currentRow);
139 	}
140 	
141 	/**
142 	 * Close the cursor and database connection.
143 	 */
144 	protected void cleanupOnClose() throws Exception {
145 		JdbcUtils.closeStatement(this.preparedStatement);
146 	}
147 
148 	@Override
149 	public String getSql() {
150 		return this.sql;
151 	}
152 }