001/* 002 * Copyright 2006-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; 017 018import java.util.List; 019 020import org.apache.commons.logging.Log; 021import org.apache.commons.logging.LogFactory; 022import org.hibernate.Session; 023import org.hibernate.SessionFactory; 024import org.hibernate.context.spi.CurrentSessionContext; 025 026import org.springframework.batch.item.ItemWriter; 027import org.springframework.beans.factory.InitializingBean; 028import org.springframework.orm.hibernate5.HibernateOperations; 029import org.springframework.util.Assert; 030 031/** 032 * {@link ItemWriter} that uses a Hibernate session to save or update entities 033 * that are not part of the current Hibernate session. It will also flush the 034 * session after writing (i.e. at chunk boundaries if used in a Spring Batch 035 * TaskletStep). It will also clear the session on write 036 * default (see {@link #setClearSession(boolean) clearSession} property).<br> 037 * <br> 038 * 039 * The writer is thread-safe once properties are set (normal singleton behavior) 040 * if a {@link CurrentSessionContext} that uses only one session per thread is 041 * used. 042 * 043 * @author Dave Syer 044 * @author Thomas Risberg 045 * @author Michael Minella 046 * 047 */ 048public class HibernateItemWriter<T> implements ItemWriter<T>, InitializingBean { 049 050 protected static final Log logger = LogFactory 051 .getLog(HibernateItemWriter.class); 052 053 private SessionFactory sessionFactory; 054 055 private boolean clearSession = true; 056 057 /** 058 * Flag to indicate that the session should be cleared and flushed at the 059 * end of the write (default true). 060 * 061 * @param clearSession 062 * the flag value to set 063 */ 064 public void setClearSession(boolean clearSession) { 065 this.clearSession = clearSession; 066 } 067 068 /** 069 * Set the Hibernate SessionFactory to be used internally. 070 * 071 * @param sessionFactory session factory to be used by the writer 072 */ 073 public void setSessionFactory(SessionFactory sessionFactory) { 074 this.sessionFactory = sessionFactory; 075 } 076 077 /** 078 * Check mandatory properties - there must be a sessionFactory. 079 */ 080 @Override 081 public void afterPropertiesSet() { 082 Assert.state(sessionFactory != null, 083 "SessionFactory must be provided"); 084 } 085 086 /** 087 * Save or update any entities not in the current hibernate session and then 088 * flush the hibernate session. 089 * 090 * @see org.springframework.batch.item.ItemWriter#write(java.util.List) 091 */ 092 @Override 093 public void write(List<? extends T> items) { 094 doWrite(sessionFactory, items); 095 sessionFactory.getCurrentSession().flush(); 096 if(clearSession) { 097 sessionFactory.getCurrentSession().clear(); 098 } 099 } 100 101 /** 102 * Do perform the actual write operation using Hibernate's API. 103 * This can be overridden in a subclass if necessary. 104 * 105 * @param sessionFactory Hibernate SessionFactory to be used 106 * @param items the list of items to use for the write 107 */ 108 protected void doWrite(SessionFactory sessionFactory, List<? extends T> items) { 109 if (logger.isDebugEnabled()) { 110 logger.debug("Writing to Hibernate with " + items.size() 111 + " items."); 112 } 113 114 Session currentSession = sessionFactory.getCurrentSession(); 115 116 if (!items.isEmpty()) { 117 long saveOrUpdateCount = 0; 118 for (T item : items) { 119 if (!currentSession.contains(item)) { 120 currentSession.saveOrUpdate(item); 121 saveOrUpdateCount++; 122 } 123 } 124 if (logger.isDebugEnabled()) { 125 logger.debug(saveOrUpdateCount + " entities saved/updated."); 126 logger.debug((items.size() - saveOrUpdateCount) 127 + " entities found in session."); 128 } 129 } 130 } 131 132 /** 133 * Do perform the actual write operation using {@link HibernateOperations}. 134 * This can be overridden in a subclass if necessary. 135 * 136 * @param hibernateTemplate 137 * the HibernateTemplate to use for the operation 138 * @param items 139 * the list of items to use for the write 140 * @deprecated As of 2.2 in favor of using Hibernate's session management APIs directly 141 */ 142 @Deprecated 143 protected void doWrite(HibernateOperations hibernateTemplate, 144 List<? extends T> items) { 145 146 if (logger.isDebugEnabled()) { 147 logger.debug("Writing to Hibernate with " + items.size() 148 + " items."); 149 } 150 151 if (!items.isEmpty()) { 152 long saveOrUpdateCount = 0; 153 for (T item : items) { 154 if (!hibernateTemplate.contains(item)) { 155 hibernateTemplate.saveOrUpdate(item); 156 saveOrUpdateCount++; 157 } 158 } 159 if (logger.isDebugEnabled()) { 160 logger.debug(saveOrUpdateCount + " entities saved/updated."); 161 logger.debug((items.size() - saveOrUpdateCount) 162 + " entities found in session."); 163 } 164 } 165 166 } 167 168}