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