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;
019import javax.persistence.EntityManagerFactory;
020
021import org.springframework.batch.item.database.JpaPagingItemReader;
022import org.springframework.batch.item.database.orm.JpaQueryProvider;
023import org.springframework.util.Assert;
024
025/**
026 * Creates a fully qualified JpaPagingItemReader.
027 *
028 * @author Michael Minella
029 * @author Glenn Renfro
030 *
031 * @since 4.0
032 */
033
034public class JpaPagingItemReaderBuilder<T> {
035
036        private int pageSize = 10;
037
038        private EntityManagerFactory entityManagerFactory;
039
040        private Map<String, Object> parameterValues;
041
042        private boolean transacted = true;
043
044        private String queryString;
045
046        private JpaQueryProvider queryProvider;
047
048        private boolean saveState = true;
049
050        private String name;
051
052        private int maxItemCount = Integer.MAX_VALUE;
053
054        private int currentItemCount;
055
056        /**
057         * Configure if the state of the {@link org.springframework.batch.item.ItemStreamSupport}
058         * should be persisted within the {@link org.springframework.batch.item.ExecutionContext}
059         * for restart purposes.
060         *
061         * @param saveState defaults to true
062         * @return The current instance of the builder.
063         */
064        public JpaPagingItemReaderBuilder<T> saveState(boolean saveState) {
065                this.saveState = saveState;
066
067                return this;
068        }
069
070        /**
071         * The name used to calculate the key within the
072         * {@link org.springframework.batch.item.ExecutionContext}. Required if
073         * {@link #saveState(boolean)} is set to true.
074         *
075         * @param name name of the reader instance
076         * @return The current instance of the builder.
077         * @see org.springframework.batch.item.ItemStreamSupport#setName(String)
078         */
079        public JpaPagingItemReaderBuilder<T> name(String name) {
080                this.name = name;
081
082                return this;
083        }
084
085        /**
086         * Configure the max number of items to be read.
087         *
088         * @param maxItemCount the max items to be read
089         * @return The current instance of the builder.
090         * @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setMaxItemCount(int)
091         */
092        public JpaPagingItemReaderBuilder<T> maxItemCount(int maxItemCount) {
093                this.maxItemCount = maxItemCount;
094
095                return this;
096        }
097
098        /**
099         * Index for the current item. Used on restarts to indicate where to start from.
100         *
101         * @param currentItemCount current index
102         * @return this instance for method chaining
103         * @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setCurrentItemCount(int)
104         */
105        public JpaPagingItemReaderBuilder<T> currentItemCount(int currentItemCount) {
106                this.currentItemCount = currentItemCount;
107
108                return this;
109        }
110
111        /**
112         * The number of records to request per page/query.  Defaults to 10.  Must be greater
113         * than zero.
114         *
115         * @param pageSize number of items
116         * @return this instance for method chaining
117         * @see JpaPagingItemReader#setPageSize(int)
118         */
119        public JpaPagingItemReaderBuilder<T> pageSize(int pageSize) {
120                this.pageSize = pageSize;
121
122                return this;
123        }
124
125        /**
126         * A map of parameter values to be set on the query.   The key of the map is the name
127         * of the parameter to be set with the value being the value to be set.
128         *
129         * @param parameterValues map of values
130         * @return this instance for method chaining
131         * @see JpaPagingItemReader#setParameterValues(Map)
132         */
133        public JpaPagingItemReaderBuilder<T> parameterValues(Map<String, Object> parameterValues) {
134                this.parameterValues = parameterValues;
135
136                return this;
137        }
138
139        /**
140         * A query provider.  This should be set only if {@link #queryString(String)} have not
141         * been set.
142         *
143         * @param queryProvider the query provider
144         * @return this instance for method chaining
145         * @see JpaPagingItemReader#setQueryProvider(JpaQueryProvider)
146         */
147        public JpaPagingItemReaderBuilder<T> queryProvider(JpaQueryProvider queryProvider) {
148                this.queryProvider = queryProvider;
149
150                return this;
151        }
152
153        /**
154         * The HQL query string to execute.  This should only be set if
155         * {@link #queryProvider(JpaQueryProvider)} has not been set.
156         *
157         * @param queryString the HQL query
158         * @return this instance for method chaining
159         * @see JpaPagingItemReader#setQueryString(String)
160         */
161        public JpaPagingItemReaderBuilder<T> queryString(String queryString) {
162                this.queryString = queryString;
163
164                return this;
165        }
166
167        /**
168         * Indicates if a transaction should be created around the read (true by default).
169         * Can be set to false in cases where JPA implementation doesn't support a particular
170         * transaction, however this may cause object inconsistency in the EntityManagerFactory.
171         *
172         * @param transacted defaults to true
173         * @return this instance for method chaining
174         * @see JpaPagingItemReader#setTransacted(boolean)
175         */
176        public JpaPagingItemReaderBuilder<T> transacted(boolean transacted) {
177                this.transacted = transacted;
178
179                return this;
180        }
181
182        /**
183         * The {@link EntityManagerFactory} to be used for executing the configured
184         * {@link #queryString}.
185         *
186         * @param entityManagerFactory {@link EntityManagerFactory} used to create
187         * {@link javax.persistence.EntityManager}
188         * @return this instance for method chaining
189         */
190        public JpaPagingItemReaderBuilder<T> entityManagerFactory(EntityManagerFactory entityManagerFactory) {
191                this.entityManagerFactory = entityManagerFactory;
192
193                return this;
194        }
195
196        /**
197         * Returns a fully constructed {@link JpaPagingItemReader}.
198         *
199         * @return a new {@link JpaPagingItemReader}
200         */
201        public JpaPagingItemReader<T> build() {
202                Assert.isTrue(this.pageSize > 0, "pageSize must be greater than zero");
203                Assert.notNull(this.entityManagerFactory, "An EntityManagerFactory is required");
204
205                if(this.saveState) {
206                        Assert.hasText(this.name,
207                                        "A name is required when saveState is set to true");
208                }
209
210                if(this.queryProvider == null) {
211                        Assert.hasLength(this.queryString, "Query string is required when queryProvider is null");
212                }
213
214                JpaPagingItemReader<T> reader = new JpaPagingItemReader<>();
215
216                reader.setQueryString(this.queryString);
217                reader.setPageSize(this.pageSize);
218                reader.setParameterValues(this.parameterValues);
219                reader.setEntityManagerFactory(this.entityManagerFactory);
220                reader.setQueryProvider(this.queryProvider);
221                reader.setTransacted(this.transacted);
222                reader.setCurrentItemCount(this.currentItemCount);
223                reader.setMaxItemCount(this.maxItemCount);
224                reader.setSaveState(this.saveState);
225                reader.setName(this.name);
226
227                return reader;
228        }
229}