001/* 002 * Copyright 2017 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.Map; 019 020import org.hibernate.SessionFactory; 021 022import org.springframework.batch.item.database.HibernateCursorItemReader; 023import org.springframework.batch.item.database.orm.HibernateNativeQueryProvider; 024import org.springframework.batch.item.database.orm.HibernateQueryProvider; 025import org.springframework.util.Assert; 026import org.springframework.util.StringUtils; 027 028/** 029 * This is a builder for the {@link HibernateCursorItemReader}. When configuring, one of 030 * the following should be provided (listed in order of precedence): 031 * <ul> 032 * <li>{@link #queryProvider(HibernateQueryProvider)}</li> 033 * <li>{@link #queryName(String)}</li> 034 * <li>{@link #queryString(String)}</li> 035 * <li>{@link #nativeQuery(String)} and {@link #entityClass(Class)}</li> 036 * </ul> 037 * 038 * @author Michael Minella 039 * @author Glenn Renfro 040 * @since 4.0 041 * @see HibernateCursorItemReader 042 */ 043public class HibernateCursorItemReaderBuilder<T> { 044 045 private Map<String, Object> parameterValues; 046 047 private String queryName; 048 049 private int fetchSize; 050 051 private HibernateQueryProvider<T> queryProvider; 052 053 private String queryString; 054 055 private SessionFactory sessionFactory; 056 057 private boolean useStatelessSession; 058 059 private String nativeQuery; 060 061 private Class nativeClass; 062 063 private boolean saveState = true; 064 065 private String name; 066 067 private int maxItemCount = Integer.MAX_VALUE; 068 069 private int currentItemCount; 070 071 /** 072 * Configure if the state of the {@link org.springframework.batch.item.ItemStreamSupport} 073 * should be persisted within the {@link org.springframework.batch.item.ExecutionContext} 074 * for restart purposes. 075 * 076 * @param saveState defaults to true 077 * @return The current instance of the builder. 078 */ 079 public HibernateCursorItemReaderBuilder<T> saveState(boolean saveState) { 080 this.saveState = saveState; 081 082 return this; 083 } 084 085 /** 086 * The name used to calculate the key within the 087 * {@link org.springframework.batch.item.ExecutionContext}. Required if 088 * {@link #saveState(boolean)} is set to true. 089 * 090 * @param name name of the reader instance 091 * @return The current instance of the builder. 092 * @see org.springframework.batch.item.ItemStreamSupport#setName(String) 093 */ 094 public HibernateCursorItemReaderBuilder<T> name(String name) { 095 this.name = name; 096 097 return this; 098 } 099 100 /** 101 * Configure the max number of items to be read. 102 * 103 * @param maxItemCount the max items to be read 104 * @return The current instance of the builder. 105 * @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setMaxItemCount(int) 106 */ 107 public HibernateCursorItemReaderBuilder<T> maxItemCount(int maxItemCount) { 108 this.maxItemCount = maxItemCount; 109 110 return this; 111 } 112 113 /** 114 * Index for the current item. Used on restarts to indicate where to start from. 115 * 116 * @param currentItemCount current index 117 * @return this instance for method chaining 118 * @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setCurrentItemCount(int) 119 */ 120 public HibernateCursorItemReaderBuilder<T> currentItemCount(int currentItemCount) { 121 this.currentItemCount = currentItemCount; 122 123 return this; 124 } 125 126 /** 127 * A map of parameter values to be set on the query. The key of the map is the name 128 * of the parameter to be set with the value being the value to be set. 129 * 130 * @param parameterValues map of values 131 * @return this instance for method chaining 132 * @see HibernateCursorItemReader#setParameterValues(Map) 133 */ 134 public HibernateCursorItemReaderBuilder<T> parameterValues(Map<String, Object> parameterValues) { 135 this.parameterValues = parameterValues; 136 137 return this; 138 } 139 140 /** 141 * The name of the Hibernate named query to be executed for this reader. 142 * 143 * @param queryName name of the query to execute 144 * @return this instance for method chaining 145 * @see HibernateCursorItemReader#setQueryName(String) 146 */ 147 public HibernateCursorItemReaderBuilder<T> queryName(String queryName) { 148 this.queryName = queryName; 149 150 return this; 151 } 152 153 /** 154 * The number of items to be returned with each round trip to the database. Used 155 * internally by Hibernate. 156 * 157 * @param fetchSize number of records to return per fetch 158 * @return this instance for method chaining 159 * @see HibernateCursorItemReader#setFetchSize(int) 160 */ 161 public HibernateCursorItemReaderBuilder<T> fetchSize(int fetchSize) { 162 this.fetchSize = fetchSize; 163 164 return this; 165 } 166 167 /** 168 * A query provider. This should be set only if {@link #queryString(String)} and 169 * {@link #queryName(String)} have not been set. 170 * 171 * @param queryProvider the query provider 172 * @return this instance for method chaining 173 * @see HibernateCursorItemReader#setQueryProvider(HibernateQueryProvider) 174 */ 175 public HibernateCursorItemReaderBuilder<T> queryProvider(HibernateQueryProvider<T> queryProvider) { 176 this.queryProvider = queryProvider; 177 178 return this; 179 } 180 181 /** 182 * The HQL query string to execute. This should only be set if 183 * {@link #queryProvider(HibernateQueryProvider)} and {@link #queryName(String)} have 184 * not been set. 185 * 186 * @param queryString the HQL query 187 * @return this instance for method chaining 188 * @see HibernateCursorItemReader#setQueryString(String) 189 */ 190 public HibernateCursorItemReaderBuilder<T> queryString(String queryString) { 191 this.queryString = queryString; 192 193 return this; 194 } 195 196 /** 197 * The Hibernate {@link SessionFactory} to execute the query against. 198 * 199 * @param sessionFactory the session factory 200 * @return this instance for method chaining 201 * @see HibernateCursorItemReader#setSessionFactory(SessionFactory) 202 */ 203 public HibernateCursorItemReaderBuilder<T> sessionFactory(SessionFactory sessionFactory) { 204 this.sessionFactory = sessionFactory; 205 206 return this; 207 } 208 209 /** 210 * Indicator for whether to use a {@link org.hibernate.StatelessSession} 211 * (<code>true</code>) or a {@link org.hibernate.Session} (<code>false</code>). 212 * 213 * @param useStatelessSession Defaults to false 214 * @return this instance for method chaining 215 * @see HibernateCursorItemReader#setUseStatelessSession(boolean) 216 */ 217 public HibernateCursorItemReaderBuilder<T> useStatelessSession(boolean useStatelessSession) { 218 this.useStatelessSession = useStatelessSession; 219 220 return this; 221 } 222 223 /** 224 * Used to configure a {@link HibernateNativeQueryProvider}. This is ignored if 225 * 226 * @param nativeQuery {@link String} containing the native query. 227 * @return this instance for method chaining 228 */ 229 public HibernateCursorItemReaderBuilder<T> nativeQuery(String nativeQuery) { 230 this.nativeQuery = nativeQuery; 231 232 return this; 233 } 234 235 public HibernateCursorItemReaderBuilder<T> entityClass(Class nativeClass) { 236 this.nativeClass = nativeClass; 237 238 return this; 239 } 240 241 /** 242 * Returns a fully constructed {@link HibernateCursorItemReader}. 243 * 244 * @return a new {@link HibernateCursorItemReader} 245 */ 246 public HibernateCursorItemReader<T> build() { 247 Assert.state(this.fetchSize >= 0, "fetchSize must not be negative"); 248 Assert.state(this.sessionFactory != null, "A SessionFactory must be provided"); 249 250 if(this.saveState) { 251 Assert.state(StringUtils.hasText(this.name), 252 "A name is required when saveState is set to true."); 253 } 254 255 HibernateCursorItemReader<T> reader = new HibernateCursorItemReader<>(); 256 257 reader.setFetchSize(this.fetchSize); 258 reader.setParameterValues(this.parameterValues); 259 260 if(this.queryProvider != null) { 261 reader.setQueryProvider(this.queryProvider); 262 } 263 else if(StringUtils.hasText(this.queryName)) { 264 reader.setQueryName(this.queryName); 265 } 266 else if(StringUtils.hasText(this.queryString)) { 267 reader.setQueryString(this.queryString); 268 } 269 else if(StringUtils.hasText(this.nativeQuery) && this.nativeClass != null) { 270 HibernateNativeQueryProvider provider = new HibernateNativeQueryProvider(); 271 provider.setSqlQuery(this.nativeQuery); 272 provider.setEntityClass(this.nativeClass); 273 274 try { 275 provider.afterPropertiesSet(); 276 } 277 catch (Exception e) { 278 throw new IllegalStateException("Unable to initialize the HibernateNativeQueryProvider", e); 279 } 280 281 reader.setQueryProvider(provider); 282 } 283 else { 284 throw new IllegalStateException("A HibernateQueryProvider, queryName, queryString, " + 285 "or both the nativeQuery and entityClass must be configured"); 286 } 287 288 reader.setSessionFactory(this.sessionFactory); 289 reader.setUseStatelessSession(this.useStatelessSession); 290 reader.setCurrentItemCount(this.currentItemCount); 291 reader.setMaxItemCount(this.maxItemCount); 292 reader.setName(this.name); 293 reader.setSaveState(this.saveState); 294 295 return reader; 296 } 297 298 }