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}