001/* 002 * Copyright 2016-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.springframework.batch.item.database.builder; 017 018import java.util.List; 019import javax.sql.DataSource; 020 021import org.springframework.batch.item.database.AbstractCursorItemReader; 022import org.springframework.batch.item.database.JdbcCursorItemReader; 023import org.springframework.batch.item.database.support.ListPreparedStatementSetter; 024import org.springframework.beans.factory.InitializingBean; 025import org.springframework.jdbc.core.ArgumentPreparedStatementSetter; 026import org.springframework.jdbc.core.ArgumentTypePreparedStatementSetter; 027import org.springframework.jdbc.core.BeanPropertyRowMapper; 028import org.springframework.jdbc.core.PreparedStatementSetter; 029import org.springframework.jdbc.core.RowMapper; 030import org.springframework.util.Assert; 031import org.springframework.util.StringUtils; 032 033/** 034 * Builder for the {@link JdbcCursorItemReader} 035 * 036 * @author Michael Minella 037 * @author Glenn Renfro 038 * @since 4.0 039 */ 040public class JdbcCursorItemReaderBuilder<T> { 041 042 private DataSource dataSource; 043 044 private int fetchSize = AbstractCursorItemReader.VALUE_NOT_SET; 045 046 private int maxRows = AbstractCursorItemReader.VALUE_NOT_SET; 047 048 private int queryTimeout = AbstractCursorItemReader.VALUE_NOT_SET; 049 050 private boolean ignoreWarnings; 051 052 private boolean verifyCursorPosition; 053 054 private boolean driverSupportsAbsolute; 055 056 private boolean useSharedExtendedConnection; 057 058 private PreparedStatementSetter preparedStatementSetter; 059 060 private String sql; 061 062 private RowMapper<T> rowMapper; 063 064 private boolean saveState = true; 065 066 private String name; 067 068 private int maxItemCount = Integer.MAX_VALUE; 069 070 private int currentItemCount; 071 072 /** 073 * Configure if the state of the {@link org.springframework.batch.item.ItemStreamSupport} 074 * should be persisted within the {@link org.springframework.batch.item.ExecutionContext} 075 * for restart purposes. 076 * 077 * @param saveState defaults to true 078 * @return The current instance of the builder. 079 */ 080 public JdbcCursorItemReaderBuilder<T> saveState(boolean saveState) { 081 this.saveState = saveState; 082 083 return this; 084 } 085 086 /** 087 * The name used to calculate the key within the 088 * {@link org.springframework.batch.item.ExecutionContext}. Required if 089 * {@link #saveState(boolean)} is set to true. 090 * 091 * @param name name of the reader instance 092 * @return The current instance of the builder. 093 * @see org.springframework.batch.item.ItemStreamSupport#setName(String) 094 */ 095 public JdbcCursorItemReaderBuilder<T> name(String name) { 096 this.name = name; 097 098 return this; 099 } 100 101 /** 102 * Configure the max number of items to be read. 103 * 104 * @param maxItemCount the max items to be read 105 * @return The current instance of the builder. 106 * @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setMaxItemCount(int) 107 */ 108 public JdbcCursorItemReaderBuilder<T> maxItemCount(int maxItemCount) { 109 this.maxItemCount = maxItemCount; 110 111 return this; 112 } 113 114 /** 115 * Index for the current item. Used on restarts to indicate where to start from. 116 * 117 * @param currentItemCount current index 118 * @return this instance for method chaining 119 * @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setCurrentItemCount(int) 120 */ 121 public JdbcCursorItemReaderBuilder<T> currentItemCount(int currentItemCount) { 122 this.currentItemCount = currentItemCount; 123 124 return this; 125 } 126 127 /** 128 * The {@link DataSource} to read from 129 * 130 * @param dataSource a relational data base 131 * @return this instance for method chaining 132 * @see JdbcCursorItemReader#setDataSource(DataSource) 133 */ 134 public JdbcCursorItemReaderBuilder<T> dataSource(DataSource dataSource) { 135 this.dataSource = dataSource; 136 137 return this; 138 } 139 140 /** 141 * A hint to the driver as to how many rows to return with each fetch. 142 * 143 * @param fetchSize the hint 144 * @return this instance for method chaining 145 * @see JdbcCursorItemReader#setFetchSize(int) 146 */ 147 public JdbcCursorItemReaderBuilder<T> fetchSize(int fetchSize) { 148 this.fetchSize = fetchSize; 149 150 return this; 151 } 152 153 /** 154 * The max number of rows the {@link java.sql.ResultSet} can contain 155 * 156 * @param maxRows the max 157 * @return this instance for method chaining 158 * @see JdbcCursorItemReader#setMaxRows(int) 159 */ 160 public JdbcCursorItemReaderBuilder<T> maxRows(int maxRows) { 161 this.maxRows = maxRows; 162 163 return this; 164 } 165 166 /** 167 * The time in milliseconds for the query to timeout 168 * 169 * @param queryTimeout timeout 170 * @return this instance for method chaining 171 * @see JdbcCursorItemReader#setQueryTimeout(int) 172 */ 173 public JdbcCursorItemReaderBuilder<T> queryTimeout(int queryTimeout) { 174 this.queryTimeout = queryTimeout; 175 176 return this; 177 } 178 179 public JdbcCursorItemReaderBuilder<T> ignoreWarnings(boolean ignoreWarnings) { 180 this.ignoreWarnings = ignoreWarnings; 181 182 return this; 183 } 184 185 /** 186 * Indicates if the reader should verify the current position of the 187 * {@link java.sql.ResultSet} after being passed to the {@link RowMapper}. Defaults 188 * to true. 189 * 190 * @param verifyCursorPosition indicator 191 * @return this instance for method chaining 192 * @see JdbcCursorItemReader#setVerifyCursorPosition(boolean) 193 */ 194 public JdbcCursorItemReaderBuilder<T> verifyCursorPosition(boolean verifyCursorPosition) { 195 this.verifyCursorPosition = verifyCursorPosition; 196 197 return this; 198 } 199 200 /** 201 * Indicates if the JDBC driver supports setting the absolute row on the 202 * {@link java.sql.ResultSet}. 203 * 204 * @param driverSupportsAbsolute indicator 205 * @return this instance for method chaining 206 * @see JdbcCursorItemReader#setDriverSupportsAbsolute(boolean) 207 */ 208 public JdbcCursorItemReaderBuilder<T> driverSupportsAbsolute(boolean driverSupportsAbsolute) { 209 this.driverSupportsAbsolute = driverSupportsAbsolute; 210 211 return this; 212 } 213 214 /** 215 * Indicates that the connection used for the cursor is being used by all other 216 * processing, therefor part of the same transaction. 217 * 218 * @param useSharedExtendedConnection indicator 219 * @return this instance for method chaining 220 * @see JdbcCursorItemReader#setUseSharedExtendedConnection(boolean) 221 */ 222 public JdbcCursorItemReaderBuilder<T> useSharedExtendedConnection(boolean useSharedExtendedConnection) { 223 this.useSharedExtendedConnection = useSharedExtendedConnection; 224 225 return this; 226 } 227 228 /** 229 * Configures the provided {@link PreparedStatementSetter} to be used to populate any 230 * arguments in the SQL query to be executed for the reader. 231 * 232 * @param preparedStatementSetter setter 233 * @return this instance for method chaining 234 * @see JdbcCursorItemReader#setPreparedStatementSetter(PreparedStatementSetter) 235 */ 236 public JdbcCursorItemReaderBuilder<T> preparedStatementSetter(PreparedStatementSetter preparedStatementSetter) { 237 this.preparedStatementSetter = preparedStatementSetter; 238 239 return this; 240 } 241 242 /** 243 * Configures a {@link PreparedStatementSetter} that will use the array as the values 244 * to be set on the query to be executed for this reader. 245 * 246 * @param args values to set on the reader query 247 * @return this instance for method chaining 248 */ 249 public JdbcCursorItemReaderBuilder<T> queryArguments(Object[] args) { 250 this.preparedStatementSetter = new ArgumentPreparedStatementSetter(args); 251 252 return this; 253 } 254 255 /** 256 * Configures a {@link PreparedStatementSetter} that will use the Object [] as the 257 * values to be set on the query to be executed for this reader. The int[] will 258 * provide the types ({@link java.sql.Types}) for each of the values provided. 259 * 260 * @param args values to set on the query 261 * @param types the type for each value in the args array 262 * @return this instance for method chaining 263 */ 264 public JdbcCursorItemReaderBuilder<T> queryArguments(Object[] args, int[] types) { 265 this.preparedStatementSetter = new ArgumentTypePreparedStatementSetter(args, types); 266 267 return this; 268 } 269 270 /** 271 * Configures a {@link PreparedStatementSetter} that will use the List as the values 272 * to be set on the query to be executed for this reader. 273 * 274 * @param args values to set on the query 275 * @return this instance for method chaining 276 * @throws Exception from {@link InitializingBean#afterPropertiesSet()} 277 */ 278 public JdbcCursorItemReaderBuilder<T> queryArguments(List<?> args) throws Exception { 279 ListPreparedStatementSetter listPreparedStatementSetter = new ListPreparedStatementSetter(args); 280 281 listPreparedStatementSetter.afterPropertiesSet(); 282 283 this.preparedStatementSetter = listPreparedStatementSetter; 284 285 return this; 286 } 287 288 /** 289 * The query to be executed for this reader 290 * 291 * @param sql query 292 * @return this instance for method chaining 293 * @see JdbcCursorItemReader#setSql(String) 294 */ 295 public JdbcCursorItemReaderBuilder<T> sql(String sql) { 296 this.sql = sql; 297 298 return this; 299 } 300 301 /** 302 * The {@link RowMapper} used to map the results of the cursor to each item. 303 * 304 * @param rowMapper {@link RowMapper} 305 * @return this instance for method chaining 306 * @see JdbcCursorItemReader#setRowMapper(RowMapper) 307 */ 308 public JdbcCursorItemReaderBuilder<T> rowMapper(RowMapper<T> rowMapper) { 309 this.rowMapper = rowMapper; 310 311 return this; 312 } 313 314 /** 315 * Creates a {@link BeanPropertyRowMapper} to be used as your 316 * {@link RowMapper}. 317 * 318 * @param mappedClass the class for the row mapper 319 * @return this instance for method chaining 320 * @see BeanPropertyRowMapper 321 */ 322 public JdbcCursorItemReaderBuilder<T> beanRowMapper(Class<T> mappedClass) { 323 this.rowMapper = new BeanPropertyRowMapper<>(mappedClass); 324 325 return this; 326 } 327 328 /** 329 * Validates configuration and builds a new reader instance. 330 * 331 * @return a fully constructed {@link JdbcCursorItemReader} 332 */ 333 public JdbcCursorItemReader<T> build() { 334 if(this.saveState) { 335 Assert.hasText(this.name, 336 "A name is required when saveSate is set to true"); 337 } 338 339 Assert.hasText(this.sql, "A query is required"); 340 Assert.notNull(this.dataSource, "A datasource is required"); 341 Assert.notNull(this.rowMapper, "A rowmapper is required"); 342 343 JdbcCursorItemReader<T> reader = new JdbcCursorItemReader<>(); 344 345 if(StringUtils.hasText(this.name)) { 346 reader.setName(this.name); 347 } 348 349 reader.setSaveState(this.saveState); 350 reader.setPreparedStatementSetter(this.preparedStatementSetter); 351 reader.setRowMapper(this.rowMapper); 352 reader.setSql(this.sql); 353 reader.setCurrentItemCount(this.currentItemCount); 354 reader.setDataSource(this.dataSource); 355 reader.setDriverSupportsAbsolute(this.driverSupportsAbsolute); 356 reader.setFetchSize(this.fetchSize); 357 reader.setIgnoreWarnings(this.ignoreWarnings); 358 reader.setMaxItemCount(this.maxItemCount); 359 reader.setMaxRows(this.maxRows); 360 reader.setQueryTimeout(this.queryTimeout); 361 reader.setUseSharedExtendedConnection(this.useSharedExtendedConnection); 362 reader.setVerifyCursorPosition(this.verifyCursorPosition); 363 364 return reader; 365 } 366 }