View Javadoc

1   /*
2    * Copyright 2006-2013 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.springframework.batch.item.database;
17  
18  import java.util.Collection;
19  import java.util.List;
20  import java.util.Map;
21  
22  import org.hibernate.Query;
23  import org.hibernate.ScrollMode;
24  import org.hibernate.ScrollableResults;
25  import org.hibernate.Session;
26  import org.hibernate.SessionFactory;
27  import org.hibernate.StatelessSession;
28  import org.springframework.batch.item.database.orm.HibernateQueryProvider;
29  import org.springframework.beans.factory.InitializingBean;
30  import org.springframework.util.Assert;
31  import org.springframework.util.StringUtils;
32  
33  /**
34   * Internal shared state helper for hibernate readers managing sessions and
35   * queries.
36   *
37   * @author Dave Syer
38   *
39   */
40  public class HibernateItemReaderHelper<T> implements InitializingBean {
41  
42  	private SessionFactory sessionFactory;
43  
44  	private String queryString = "";
45  
46  	private String queryName = "";
47  
48  	private HibernateQueryProvider queryProvider;
49  
50  	private boolean useStatelessSession = true;
51  
52  	private StatelessSession statelessSession;
53  
54  	private Session statefulSession;
55  
56  	/**
57  	 * @param queryName name of a hibernate named query
58  	 */
59  	public void setQueryName(String queryName) {
60  		this.queryName = queryName;
61  	}
62  
63  	/**
64  	 * @param queryString HQL query string
65  	 */
66  	public void setQueryString(String queryString) {
67  		this.queryString = queryString;
68  	}
69  
70  	/**
71  	 * @param queryProvider Hibernate query provider
72  	 */
73  	public void setQueryProvider(HibernateQueryProvider queryProvider) {
74  		this.queryProvider = queryProvider;
75  	}
76  
77  	/**
78  	 * Can be set only in uninitialized state.
79  	 *
80  	 * @param useStatelessSession <code>true</code> to use
81  	 * {@link StatelessSession} <code>false</code> to use standard hibernate
82  	 * {@link Session}
83  	 */
84  	public void setUseStatelessSession(boolean useStatelessSession) {
85  		Assert.state(statefulSession == null && statelessSession == null,
86  				"The useStatelessSession flag can only be set before a session is initialized.");
87  		this.useStatelessSession = useStatelessSession;
88  	}
89  
90  	/**
91  	 * @param sessionFactory hibernate session factory
92  	 */
93  	public void setSessionFactory(SessionFactory sessionFactory) {
94  		this.sessionFactory = sessionFactory;
95  	}
96  
97  	@Override
98  	public void afterPropertiesSet() throws Exception {
99  
100 		Assert.state(sessionFactory != null, "A SessionFactory must be provided");
101 
102 		if (queryProvider == null) {
103 			Assert.notNull(sessionFactory, "session factory must be set");
104 			Assert.state(StringUtils.hasText(queryString) ^ StringUtils.hasText(queryName),
105 					"queryString or queryName must be set");
106 		}
107 		// making sure that the appropriate (Hibernate) query provider is set
108 		else {
109 			Assert.state(queryProvider != null, "Hibernate query provider must be set");
110 		}
111 
112 	}
113 
114 	/**
115 	 * Get a cursor over all of the results, with the forward-only flag set.
116 	 *
117 	 * @param fetchSize the fetch size to use retrieving the results
118 	 * @param parameterValues the parameter values to use (or null if none).
119 	 *
120 	 * @return a forward-only {@link ScrollableResults}
121 	 */
122 	public ScrollableResults getForwardOnlyCursor(int fetchSize, Map<String, Object> parameterValues) {
123 		Query query = createQuery();
124 		if (parameterValues != null) {
125 			query.setProperties(parameterValues);
126 		}
127 		return query.setFetchSize(fetchSize).scroll(ScrollMode.FORWARD_ONLY);
128 	}
129 
130 	/**
131 	 * Open appropriate type of hibernate session and create the query.
132 	 */
133 	public Query createQuery() {
134 
135 		if (useStatelessSession) {
136 			if (statelessSession == null) {
137 				statelessSession = sessionFactory.openStatelessSession();
138 			}
139 			if (queryProvider != null) {
140 				queryProvider.setStatelessSession(statelessSession);
141 			}
142 			else {
143 				if (StringUtils.hasText(queryName)) {
144 					return statelessSession.getNamedQuery(queryName);
145 				}
146 				else {
147 					return statelessSession.createQuery(queryString);
148 				}
149 			}
150 		}
151 		else {
152 			if (statefulSession == null) {
153 				statefulSession = sessionFactory.openSession();
154 			}
155 			if (queryProvider != null) {
156 				queryProvider.setSession(statefulSession);
157 			}
158 			else {
159 				if (StringUtils.hasText(queryName)) {
160 					return statefulSession.getNamedQuery(queryName);
161 				}
162 				else {
163 					return statefulSession.createQuery(queryString);
164 				}
165 			}
166 		}
167 
168 		// If queryProvider is set use it to create a query
169 		return queryProvider.createQuery();
170 
171 	}
172 
173 	/**
174 	 * Scroll through the results up to the item specified.
175 	 *
176 	 * @param cursor the results to scroll over
177 	 */
178 	public void jumpToItem(ScrollableResults cursor, int itemIndex, int flushInterval) {
179 		for (int i = 0; i < itemIndex; i++) {
180 			cursor.next();
181 			if (i % flushInterval == 0 && !useStatelessSession) {
182 				statefulSession.clear(); // Clears in-memory cache
183 			}
184 		}
185 	}
186 
187 	/**
188 	 * Close the open session (stateful or otherwise).
189 	 */
190 	public void close() {
191 		if (statelessSession != null) {
192 			statelessSession.close();
193 			statelessSession = null;
194 		}
195 		if (statefulSession != null) {
196 			statefulSession.close();
197 			statefulSession = null;
198 		}
199 	}
200 
201 	/**
202 	 * Read a page of data, clearing the existing session (if necessary) first,
203 	 * and creating a new session before executing the query.
204 	 *
205 	 * @param page the page to read (starting at 0)
206 	 * @param pageSize the size of the page or maximum number of items to read
207 	 * @param fetchSize the fetch size to use
208 	 * @param parameterValues the parameter values to use (if any, otherwise
209 	 * null)
210 	 * @return a collection of items
211 	 */
212 	public Collection<? extends T> readPage(int page, int pageSize, int fetchSize, Map<String, Object> parameterValues) {
213 
214 		clear();
215 
216 		Query query = createQuery();
217 		if (parameterValues != null) {
218 			query.setProperties(parameterValues);
219 		}
220 		@SuppressWarnings("unchecked")
221 		List<T> result = query.setFetchSize(fetchSize).setFirstResult(page * pageSize).setMaxResults(pageSize).list();
222 		return result;
223 
224 	}
225 
226 	/**
227 	 * Clear the session if stateful.
228 	 */
229 	public void clear() {
230 		if (statefulSession != null) {
231 			statefulSession.clear();
232 		}
233 	}
234 
235 }