001/* 002 * Copyright 2006-2013 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; 017 018import java.lang.reflect.Method; 019import java.util.Collection; 020import java.util.List; 021import java.util.Map; 022 023import org.hibernate.Query; 024import org.hibernate.ScrollMode; 025import org.hibernate.ScrollableResults; 026import org.hibernate.Session; 027import org.hibernate.SessionFactory; 028import org.hibernate.StatelessSession; 029 030import org.springframework.batch.item.database.orm.HibernateQueryProvider; 031import org.springframework.beans.factory.InitializingBean; 032import org.springframework.util.Assert; 033import org.springframework.util.ReflectionUtils; 034import org.springframework.util.StringUtils; 035 036/** 037 * Internal shared state helper for hibernate readers managing sessions and 038 * queries. 039 * 040 * @author Dave Syer 041 * 042 */ 043@SuppressWarnings("rawtype") 044public class HibernateItemReaderHelper<T> implements InitializingBean { 045 046 private SessionFactory sessionFactory; 047 048 private String queryString = ""; 049 050 private String queryName = ""; 051 052 private HibernateQueryProvider<? extends T> queryProvider; 053 054 private boolean useStatelessSession = true; 055 056 private StatelessSession statelessSession; 057 058 private Session statefulSession; 059 060 /** 061 * @param queryName name of a hibernate named query 062 */ 063 public void setQueryName(String queryName) { 064 this.queryName = queryName; 065 } 066 067 /** 068 * @param queryString HQL query string 069 */ 070 public void setQueryString(String queryString) { 071 this.queryString = queryString; 072 } 073 074 /** 075 * @param queryProvider Hibernate query provider 076 */ 077 public void setQueryProvider(HibernateQueryProvider<? extends T> queryProvider) { 078 this.queryProvider = queryProvider; 079 } 080 081 /** 082 * Can be set only in uninitialized state. 083 * 084 * @param useStatelessSession <code>true</code> to use 085 * {@link StatelessSession} <code>false</code> to use standard hibernate 086 * {@link Session} 087 */ 088 public void setUseStatelessSession(boolean useStatelessSession) { 089 Assert.state(statefulSession == null && statelessSession == null, 090 "The useStatelessSession flag can only be set before a session is initialized."); 091 this.useStatelessSession = useStatelessSession; 092 } 093 094 /** 095 * @param sessionFactory hibernate session factory 096 */ 097 public void setSessionFactory(SessionFactory sessionFactory) { 098 this.sessionFactory = sessionFactory; 099 } 100 101 @Override 102 public void afterPropertiesSet() throws Exception { 103 104 Assert.state(sessionFactory != null, "A SessionFactory must be provided"); 105 106 if (queryProvider == null) { 107 Assert.notNull(sessionFactory, "session factory must be set"); 108 Assert.state(StringUtils.hasText(queryString) ^ StringUtils.hasText(queryName), 109 "queryString or queryName must be set"); 110 } 111 } 112 113 /** 114 * Get a cursor over all of the results, with the forward-only flag set. 115 * 116 * @param fetchSize the fetch size to use retrieving the results 117 * @param parameterValues the parameter values to use (or null if none). 118 * 119 * @return a forward-only {@link ScrollableResults} 120 */ 121 public ScrollableResults getForwardOnlyCursor(int fetchSize, Map<String, Object> parameterValues) { 122 Query query = createQuery(); 123 if (parameterValues != null) { 124 query.setProperties(parameterValues); 125 } 126 return query.setFetchSize(fetchSize).scroll(ScrollMode.FORWARD_ONLY); 127 } 128 129 /** 130 * Open appropriate type of hibernate session and create the query. 131 * 132 * @return a Hibernate Query 133 */ 134 public Query createQuery() { 135 136 if (useStatelessSession) { 137 if (statelessSession == null) { 138 statelessSession = sessionFactory.openStatelessSession(); 139 } 140 if (queryProvider != null) { 141 queryProvider.setStatelessSession(statelessSession); 142 } 143 else { 144 if (StringUtils.hasText(queryName)) { 145 return statelessSession.getNamedQuery(queryName); 146 } 147 else { 148 return statelessSession.createQuery(queryString); 149 } 150 } 151 } 152 else { 153 if (statefulSession == null) { 154 statefulSession = sessionFactory.openSession(); 155 } 156 if (queryProvider != null) { 157 queryProvider.setSession(statefulSession); 158 } 159 else { 160 if (StringUtils.hasText(queryName)) { 161 return statefulSession.getNamedQuery(queryName); 162 } 163 else { 164 return statefulSession.createQuery(queryString); 165 } 166 } 167 } 168 169 // If queryProvider is set use it to create a query 170 return queryProvider.createQuery(); 171 172 } 173 174 /** 175 * Scroll through the results up to the item specified. 176 * 177 * @param cursor the results to scroll over 178 * @param itemIndex index to scroll to 179 * @param flushInterval the number of items to scroll past before flushing 180 */ 181 public void jumpToItem(ScrollableResults cursor, int itemIndex, int flushInterval) { 182 for (int i = 0; i < itemIndex; i++) { 183 cursor.next(); 184 if (i % flushInterval == 0 && !useStatelessSession) { 185 statefulSession.clear(); // Clears in-memory cache 186 } 187 } 188 } 189 190 /** 191 * Close the open session (stateful or otherwise). 192 */ 193 public void close() { 194 if (statelessSession != null) { 195 statelessSession.close(); 196 statelessSession = null; 197 } 198 if (statefulSession != null) { 199 200 Method close = ReflectionUtils.findMethod(Session.class, "close"); 201 ReflectionUtils.invokeMethod(close, statefulSession); 202 statefulSession = null; 203 } 204 } 205 206 /** 207 * Read a page of data, clearing the existing session (if necessary) first, 208 * and creating a new session before executing the query. 209 * 210 * @param page the page to read (starting at 0) 211 * @param pageSize the size of the page or maximum number of items to read 212 * @param fetchSize the fetch size to use 213 * @param parameterValues the parameter values to use (if any, otherwise 214 * null) 215 * @return a collection of items 216 */ 217 public Collection<? extends T> readPage(int page, int pageSize, int fetchSize, Map<String, Object> parameterValues) { 218 219 clear(); 220 221 Query query = createQuery(); 222 if (parameterValues != null) { 223 query.setProperties(parameterValues); 224 } 225 @SuppressWarnings("unchecked") 226 List<T> result = query.setFetchSize(fetchSize).setFirstResult(page * pageSize).setMaxResults(pageSize).list(); 227 return result; 228 229 } 230 231 /** 232 * Clear the session if stateful. 233 */ 234 public void clear() { 235 if (statefulSession != null) { 236 statefulSession.clear(); 237 } 238 } 239 240}