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.util.Map; 019import java.util.concurrent.CopyOnWriteArrayList; 020 021import org.hibernate.Session; 022import org.hibernate.SessionFactory; 023import org.hibernate.StatelessSession; 024import org.springframework.batch.item.ExecutionContext; 025import org.springframework.batch.item.ItemReader; 026import org.springframework.batch.item.database.orm.HibernateQueryProvider; 027import org.springframework.beans.factory.InitializingBean; 028import org.springframework.util.Assert; 029import org.springframework.util.ClassUtils; 030 031/** 032 * {@link ItemReader} for reading database records built on top of Hibernate and 033 * reading only up to a fixed number of items at a time. It executes an HQL 034 * query when initialized is paged as the {@link #read()} method is called. The 035 * query can be set directly using {@link #setQueryString(String)}, a named 036 * query can be used by {@link #setQueryName(String)}, or a query provider 037 * strategy can be supplied via 038 * {@link #setQueryProvider(HibernateQueryProvider)}. 039 * 040 * <p> 041 * The reader can be configured to use either {@link StatelessSession} 042 * sufficient for simple mappings without the need to cascade to associated 043 * objects or standard hibernate {@link Session} for more advanced mappings or 044 * when caching is desired. When stateful session is used it will be cleared in 045 * the {@link #update(ExecutionContext)} method without being flushed (no data 046 * modifications are expected). 047 * </p> 048 * 049 * <p> 050 * The implementation is thread-safe in between calls to 051 * {@link #open(ExecutionContext)}, but remember to use 052 * <code>saveState=false</code> if used in a multi-threaded client (no restart 053 * available). 054 * </p> 055 * 056 * @author Dave Syer 057 * 058 * @since 2.1 059 */ 060public class HibernatePagingItemReader<T> extends AbstractPagingItemReader<T> 061 implements InitializingBean { 062 063 private HibernateItemReaderHelper<T> helper = new HibernateItemReaderHelper<>(); 064 065 private Map<String, Object> parameterValues; 066 067 private int fetchSize; 068 069 public HibernatePagingItemReader() { 070 setName(ClassUtils.getShortName(HibernatePagingItemReader.class)); 071 } 072 073 /** 074 * The parameter values to apply to a query (map of name:value). 075 * 076 * @param parameterValues the parameter values to set 077 */ 078 public void setParameterValues(Map<String, Object> parameterValues) { 079 this.parameterValues = parameterValues; 080 } 081 082 /** 083 * A query name for an externalized query. Either this or the { 084 * {@link #setQueryString(String) query string} or the { 085 * {@link #setQueryProvider(HibernateQueryProvider) query provider} should 086 * be set. 087 * 088 * @param queryName name of a hibernate named query 089 */ 090 public void setQueryName(String queryName) { 091 helper.setQueryName(queryName); 092 } 093 094 /** 095 * Fetch size used internally by Hibernate to limit amount of data fetched 096 * from database per round trip. 097 * 098 * @param fetchSize the fetch size to pass down to Hibernate 099 */ 100 public void setFetchSize(int fetchSize) { 101 this.fetchSize = fetchSize; 102 } 103 104 /** 105 * A query provider. Either this or the {{@link #setQueryString(String) 106 * query string} or the {{@link #setQueryName(String) query name} should be 107 * set. 108 * 109 * @param queryProvider Hibernate query provider 110 */ 111 public void setQueryProvider(HibernateQueryProvider<? extends T> queryProvider) { 112 helper.setQueryProvider(queryProvider); 113 } 114 115 /** 116 * A query string in HQL. Either this or the { 117 * {@link #setQueryProvider(HibernateQueryProvider) query provider} or the { 118 * {@link #setQueryName(String) query name} should be set. 119 * 120 * @param queryString HQL query string 121 */ 122 public void setQueryString(String queryString) { 123 helper.setQueryString(queryString); 124 } 125 126 /** 127 * The Hibernate SessionFactory to use the create a session. 128 * 129 * @param sessionFactory the {@link SessionFactory} to set 130 */ 131 public void setSessionFactory(SessionFactory sessionFactory) { 132 helper.setSessionFactory(sessionFactory); 133 } 134 135 /** 136 * Can be set only in uninitialized state. 137 * 138 * @param useStatelessSession <code>true</code> to use 139 * {@link StatelessSession} <code>false</code> to use standard hibernate 140 * {@link Session} 141 */ 142 public void setUseStatelessSession(boolean useStatelessSession) { 143 helper.setUseStatelessSession(useStatelessSession); 144 } 145 146 @Override 147 public void afterPropertiesSet() throws Exception { 148 super.afterPropertiesSet(); 149 Assert.state(fetchSize >= 0, "fetchSize must not be negative"); 150 helper.afterPropertiesSet(); 151 } 152 153 @Override 154 protected void doOpen() throws Exception { 155 super.doOpen(); 156 } 157 158 @Override 159 protected void doReadPage() { 160 161 if (results == null) { 162 results = new CopyOnWriteArrayList<T>(); 163 } 164 else { 165 results.clear(); 166 } 167 168 results.addAll(helper.readPage(getPage(), getPageSize(), fetchSize, parameterValues)); 169 170 } 171 172 @Override 173 protected void doJumpToPage(int itemIndex) { 174 } 175 176 @Override 177 protected void doClose() throws Exception { 178 helper.close(); 179 super.doClose(); 180 } 181 182}