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 }