001/* 002 * Copyright 2017-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 javax.sql.DataSource; 019 020import org.springframework.batch.item.database.AbstractCursorItemReader; 021 022import org.springframework.batch.item.database.StoredProcedureItemReader; 023import org.springframework.jdbc.core.PreparedStatementSetter; 024import org.springframework.jdbc.core.RowMapper; 025import org.springframework.jdbc.core.SqlParameter; 026import org.springframework.util.Assert; 027import org.springframework.util.StringUtils; 028 029/** 030 * A fluent builder API for the configuration of a {@link StoredProcedureItemReader}. 031 * 032 * @author Michael Minella 033 * @author Mahmoud Ben Hassine 034 * @since 4.0.0 035 * @see StoredProcedureItemReader 036 */ 037public class StoredProcedureItemReaderBuilder<T> { 038 039 public static final int VALUE_NOT_SET = -1; 040 041 private int currentItemCount = 0; 042 043 private int maxItemCount = Integer.MAX_VALUE; 044 045 private boolean saveState = true; 046 047 private DataSource dataSource; 048 049 private int fetchSize = VALUE_NOT_SET; 050 051 private int maxRows = VALUE_NOT_SET; 052 053 private int queryTimeout = VALUE_NOT_SET; 054 055 private boolean ignoreWarnings = true; 056 057 private boolean verifyCursorPosition = true; 058 059 private boolean driverSupportsAbsolute = false; 060 061 private boolean useSharedExtendedConnection = false; 062 063 private PreparedStatementSetter preparedStatementSetter; 064 065 private RowMapper<T> rowMapper; 066 067 private String procedureName; 068 069 private SqlParameter[] parameters = new SqlParameter[0]; 070 071 private boolean function = false; 072 073 private int refCursorPosition = 0; 074 075 private String name; 076 077 /** 078 * Configure if the state of the {@link org.springframework.batch.item.ItemStreamSupport} 079 * should be persisted within the {@link org.springframework.batch.item.ExecutionContext} 080 * for restart purposes. 081 * 082 * @param saveState defaults to true 083 * @return The current instance of the builder. 084 */ 085 public StoredProcedureItemReaderBuilder<T> saveState(boolean saveState) { 086 this.saveState = saveState; 087 088 return this; 089 } 090 091 /** 092 * The name used to calculate the key within the 093 * {@link org.springframework.batch.item.ExecutionContext}. Required if 094 * {@link #saveState(boolean)} is set to true. 095 * 096 * @param name name of the reader instance 097 * @return The current instance of the builder. 098 * @see org.springframework.batch.item.ItemStreamSupport#setName(String) 099 */ 100 public StoredProcedureItemReaderBuilder<T> name(String name) { 101 this.name = name; 102 103 return this; 104 } 105 106 /** 107 * Configure the max number of items to be read. 108 * 109 * @param maxItemCount the max items to be read 110 * @return The current instance of the builder. 111 * @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setMaxItemCount(int) 112 */ 113 public StoredProcedureItemReaderBuilder<T> maxItemCount(int maxItemCount) { 114 this.maxItemCount = maxItemCount; 115 116 return this; 117 } 118 119 /** 120 * Index for the current item. Used on restarts to indicate where to start from. 121 * 122 * @param currentItemCount current index 123 * @return this instance for method chaining 124 * @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setCurrentItemCount(int) 125 */ 126 public StoredProcedureItemReaderBuilder<T> currentItemCount(int currentItemCount) { 127 this.currentItemCount = currentItemCount; 128 129 return this; 130 } 131 132 /** 133 * The {@link DataSource} to read from 134 * 135 * @param dataSource a relational data base 136 * @return this instance for method chaining 137 * @see StoredProcedureItemReader#setDataSource(DataSource) 138 */ 139 public StoredProcedureItemReaderBuilder<T> dataSource(DataSource dataSource) { 140 this.dataSource = dataSource; 141 142 return this; 143 } 144 145 /** 146 * A hint to the driver as to how many rows to return with each fetch. 147 * 148 * @param fetchSize the hint 149 * @return this instance for method chaining 150 * @see StoredProcedureItemReader#setFetchSize(int) 151 */ 152 public StoredProcedureItemReaderBuilder<T> fetchSize(int fetchSize) { 153 this.fetchSize = fetchSize; 154 155 return this; 156 } 157 158 /** 159 * The max number of rows the {@link java.sql.ResultSet} can contain 160 * 161 * @param maxRows the max 162 * @return this instance for method chaining 163 * @see StoredProcedureItemReader#setMaxRows(int) 164 */ 165 public StoredProcedureItemReaderBuilder<T> maxRows(int maxRows) { 166 this.maxRows = maxRows; 167 168 return this; 169 } 170 171 /** 172 * The time in milliseconds for the query to timeout 173 * 174 * @param queryTimeout timeout 175 * @return this instance for method chaining 176 * @see StoredProcedureItemReader#setQueryTimeout(int) 177 */ 178 public StoredProcedureItemReaderBuilder<T> queryTimeout(int queryTimeout) { 179 this.queryTimeout = queryTimeout; 180 181 return this; 182 } 183 184 /** 185 * Indicates if SQL warnings should be ignored or if an exception should be thrown. 186 * 187 * @param ignoreWarnings indicator. Defaults to true 188 * @return this instance for method chaining 189 * @see AbstractCursorItemReader#setIgnoreWarnings(boolean) 190 */ 191 public StoredProcedureItemReaderBuilder<T> ignoreWarnings(boolean ignoreWarnings) { 192 this.ignoreWarnings = ignoreWarnings; 193 194 return this; 195 } 196 197 /** 198 * Indicates if the reader should verify the current position of the 199 * {@link java.sql.ResultSet} after being passed to the {@link RowMapper}. Defaults 200 * to true. 201 * 202 * @param verifyCursorPosition indicator 203 * @return this instance for method chaining 204 * @see StoredProcedureItemReader#setVerifyCursorPosition(boolean) 205 */ 206 public StoredProcedureItemReaderBuilder<T> verifyCursorPosition(boolean verifyCursorPosition) { 207 this.verifyCursorPosition = verifyCursorPosition; 208 209 return this; 210 } 211 212 /** 213 * Indicates if the JDBC driver supports setting the absolute row on the 214 * {@link java.sql.ResultSet}. 215 * 216 * @param driverSupportsAbsolute indicator 217 * @return this instance for method chaining 218 * @see StoredProcedureItemReader#setDriverSupportsAbsolute(boolean) 219 */ 220 public StoredProcedureItemReaderBuilder<T> driverSupportsAbsolute(boolean driverSupportsAbsolute) { 221 this.driverSupportsAbsolute = driverSupportsAbsolute; 222 223 return this; 224 } 225 226 /** 227 * Indicates that the connection used for the cursor is being used by all other 228 * processing, therefor part of the same transaction. 229 * 230 * @param useSharedExtendedConnection indicator 231 * @return this instance for method chaining 232 * @see StoredProcedureItemReader#setUseSharedExtendedConnection(boolean) 233 */ 234 public StoredProcedureItemReaderBuilder<T> useSharedExtendedConnection(boolean useSharedExtendedConnection) { 235 this.useSharedExtendedConnection = useSharedExtendedConnection; 236 237 return this; 238 } 239 240 /** 241 * Configures the provided {@link PreparedStatementSetter} to be used to populate any 242 * arguments in the SQL query to be executed for the reader. 243 * 244 * @param preparedStatementSetter setter 245 * @return this instance for method chaining 246 * @see StoredProcedureItemReader#setPreparedStatementSetter(PreparedStatementSetter) 247 */ 248 public StoredProcedureItemReaderBuilder<T> preparedStatementSetter(PreparedStatementSetter preparedStatementSetter) { 249 this.preparedStatementSetter = preparedStatementSetter; 250 251 return this; 252 } 253 254 /** 255 * The {@link RowMapper} used to map the results of the cursor to each item. 256 * 257 * @param rowMapper {@link RowMapper} 258 * @return this instance for method chaining 259 * @see StoredProcedureItemReader#setRowMapper(RowMapper) 260 */ 261 public StoredProcedureItemReaderBuilder<T> rowMapper(RowMapper<T> rowMapper) { 262 this.rowMapper = rowMapper; 263 264 return this; 265 } 266 267 /** 268 * The name of the stored procedure to execute 269 * 270 * @param procedureName name of the procedure 271 * @return this instance for method chaining 272 * @see StoredProcedureItemReader#setProcedureName(String) 273 */ 274 public StoredProcedureItemReaderBuilder<T> procedureName(String procedureName) { 275 this.procedureName = procedureName; 276 277 return this; 278 } 279 280 /** 281 * SQL parameters to be set when executing the stored procedure 282 * 283 * @param parameters parameters to be set 284 * @return this instance for method chaining 285 * @see StoredProcedureItemReader#setParameters(SqlParameter[]) 286 */ 287 public StoredProcedureItemReaderBuilder<T> parameters(SqlParameter[] parameters) { 288 this.parameters = parameters; 289 290 return this; 291 } 292 293 /** 294 * Indicates the stored procedure is a function 295 * 296 * @return this instance for method chaining 297 * @see StoredProcedureItemReader#setFunction(boolean) 298 */ 299 public StoredProcedureItemReaderBuilder<T> function() { 300 this.function = true; 301 302 return this; 303 } 304 305 /** 306 * The parameter position of the REF CURSOR. Only used for Oracle and PostgreSQL that 307 * use REF CURSORs. For any other database, this should remain as the default (0). 308 * 309 * @param refCursorPosition the parameter position 310 * @return this instance for method chaining 311 * @see StoredProcedureItemReader#setRefCursorPosition(int) 312 */ 313 public StoredProcedureItemReaderBuilder<T> refCursorPosition(int refCursorPosition) { 314 this.refCursorPosition = refCursorPosition; 315 316 return this; 317 } 318 319 /** 320 * Validates configuration and builds a new reader instance 321 * 322 * @return a fully constructed {@link StoredProcedureItemReader} 323 */ 324 public StoredProcedureItemReader<T> build() { 325 if(this.saveState) { 326 Assert.hasText(this.name, 327 "A name is required when saveSate is set to true"); 328 } 329 330 Assert.notNull(this.procedureName, "The name of the stored procedure must be provided"); 331 Assert.notNull(this.dataSource, "A datasource is required"); 332 Assert.notNull(this.rowMapper, "A rowmapper is required"); 333 334 StoredProcedureItemReader<T> itemReader = new StoredProcedureItemReader<>(); 335 336 if(StringUtils.hasText(this.name)) { 337 itemReader.setName(this.name); 338 } 339 340 itemReader.setProcedureName(this.procedureName); 341 itemReader.setRowMapper(this.rowMapper); 342 itemReader.setParameters(this.parameters); 343 itemReader.setPreparedStatementSetter(this.preparedStatementSetter); 344 itemReader.setFunction(this.function); 345 itemReader.setRefCursorPosition(this.refCursorPosition); 346 itemReader.setCurrentItemCount(this.currentItemCount); 347 itemReader.setDataSource(this.dataSource); 348 itemReader.setDriverSupportsAbsolute(this.driverSupportsAbsolute); 349 itemReader.setFetchSize(this.fetchSize); 350 itemReader.setIgnoreWarnings(this.ignoreWarnings); 351 itemReader.setMaxItemCount(this.maxItemCount); 352 itemReader.setMaxRows(this.maxRows); 353 itemReader.setQueryTimeout(this.queryTimeout); 354 itemReader.setSaveState(this.saveState); 355 itemReader.setUseSharedExtendedConnection(this.useSharedExtendedConnection); 356 itemReader.setVerifyCursorPosition(this.verifyCursorPosition); 357 358 return itemReader; 359 } 360 361}