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  
17  package org.springframework.batch.item.database;
18  
19  import java.util.List;
20  
21  import javax.persistence.EntityManager;
22  import javax.persistence.EntityManagerFactory;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.springframework.batch.item.ItemWriter;
27  import org.springframework.beans.factory.InitializingBean;
28  import org.springframework.dao.DataAccessResourceFailureException;
29  import org.springframework.orm.jpa.EntityManagerFactoryUtils;
30  import org.springframework.util.Assert;
31  
32  /**
33   * {@link org.springframework.batch.item.ItemWriter} that is using a JPA
34   * EntityManagerFactory to merge any Entities that aren't part of the
35   * persistence context.
36   *
37   * It is required that {@link #write(List)} is called inside a transaction.<br/>
38   *
39   * The reader must be configured with an
40   * {@link javax.persistence.EntityManagerFactory} that is capable of
41   * participating in Spring managed transactions.
42   *
43   * The writer is thread safe after its properties are set (normal singleton
44   * behaviour), so it can be used to write in multiple concurrent transactions.
45   *
46   * @author Thomas Risberg
47   *
48   */
49  public class JpaItemWriter<T> implements ItemWriter<T>, InitializingBean {
50  
51  	protected static final Log logger = LogFactory.getLog(JpaItemWriter.class);
52  
53  	private EntityManagerFactory entityManagerFactory;
54  
55  	/**
56  	 * Set the EntityManager to be used internally.
57  	 *
58  	 * @param entityManagerFactory the entityManagerFactory to set
59  	 */
60  	public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
61  		this.entityManagerFactory = entityManagerFactory;
62  	}
63  
64  	/**
65  	 * Check mandatory properties - there must be an entityManagerFactory.
66  	 */
67  	@Override
68  	public void afterPropertiesSet() throws Exception {
69  		Assert.notNull(entityManagerFactory, "An EntityManagerFactory is required");
70  	}
71  
72  	/**
73  	 * Merge all provided items that aren't already in the persistence context
74  	 * and then flush the entity manager.
75  	 *
76  	 * @see org.springframework.batch.item.ItemWriter#write(java.util.List)
77  	 */
78  	@Override
79  	public final void write(List<? extends T> items) {
80  		EntityManager entityManager = EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory);
81  		if (entityManager == null) {
82  			throw new DataAccessResourceFailureException("Unable to obtain a transactional EntityManager");
83  		}
84  		doWrite(entityManager, items);
85  		entityManager.flush();
86  	}
87  
88  	/**
89  	 * Do perform the actual write operation. This can be overridden in a
90  	 * subclass if necessary.
91  	 *
92  	 * @param entityManager the EntityManager to use for the operation
93  	 * @param items the list of items to use for the write
94  	 */
95  	protected void doWrite(EntityManager entityManager, List<? extends T> items) {
96  
97  		if (logger.isDebugEnabled()) {
98  			logger.debug("Writing to JPA with " + items.size() + " items.");
99  		}
100 
101 		if (!items.isEmpty()) {
102 			long mergeCount = 0;
103 			for (T item : items) {
104 				if (!entityManager.contains(item)) {
105 					entityManager.merge(item);
106 					mergeCount++;
107 				}
108 			}
109 			if (logger.isDebugEnabled()) {
110 				logger.debug(mergeCount + " entities merged.");
111 				logger.debug((items.size() - mergeCount) + " entities found in persistence context.");
112 			}
113 		}
114 
115 	}
116 
117 }