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 */ 016 017package org.springframework.batch.item.database; 018 019import org.apache.commons.logging.Log; 020import org.apache.commons.logging.LogFactory; 021import org.springframework.batch.item.ItemWriter; 022import org.springframework.beans.factory.InitializingBean; 023import org.springframework.dao.DataAccessResourceFailureException; 024import org.springframework.orm.jpa.EntityManagerFactoryUtils; 025import org.springframework.util.Assert; 026 027import javax.persistence.EntityManager; 028import javax.persistence.EntityManagerFactory; 029import java.util.List; 030 031/** 032 * {@link org.springframework.batch.item.ItemWriter} that is using a JPA 033 * EntityManagerFactory to merge any Entities that aren't part of the 034 * persistence context. 035 * 036 * It is required that {@link #write(List)} is called inside a transaction.<br> 037 * 038 * The reader must be configured with an 039 * {@link javax.persistence.EntityManagerFactory} that is capable of 040 * participating in Spring managed transactions. 041 * 042 * The writer is thread-safe after its properties are set (normal singleton 043 * behaviour), so it can be used to write in multiple concurrent transactions. 044 * 045 * @author Thomas Risberg 046 * 047 */ 048public class JpaItemWriter<T> implements ItemWriter<T>, InitializingBean { 049 050 protected static final Log logger = LogFactory.getLog(JpaItemWriter.class); 051 052 private EntityManagerFactory entityManagerFactory; 053 054 /** 055 * Set the EntityManager to be used internally. 056 * 057 * @param entityManagerFactory the entityManagerFactory to set 058 */ 059 public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) { 060 this.entityManagerFactory = entityManagerFactory; 061 } 062 063 /** 064 * Check mandatory properties - there must be an entityManagerFactory. 065 */ 066 @Override 067 public void afterPropertiesSet() throws Exception { 068 Assert.notNull(entityManagerFactory, "An EntityManagerFactory is required"); 069 } 070 071 /** 072 * Merge all provided items that aren't already in the persistence context 073 * and then flush the entity manager. 074 * 075 * @see org.springframework.batch.item.ItemWriter#write(java.util.List) 076 */ 077 @Override 078 public void write(List<? extends T> items) { 079 EntityManager entityManager = EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory); 080 if (entityManager == null) { 081 throw new DataAccessResourceFailureException("Unable to obtain a transactional EntityManager"); 082 } 083 doWrite(entityManager, items); 084 entityManager.flush(); 085 } 086 087 /** 088 * Do perform the actual write operation. This can be overridden in a 089 * subclass if necessary. 090 * 091 * @param entityManager the EntityManager to use for the operation 092 * @param items the list of items to use for the write 093 */ 094 protected void doWrite(EntityManager entityManager, List<? extends T> items) { 095 096 if (logger.isDebugEnabled()) { 097 logger.debug("Writing to JPA with " + items.size() + " items."); 098 } 099 100 if (!items.isEmpty()) { 101 long mergeCount = 0; 102 for (T item : items) { 103 if (!entityManager.contains(item)) { 104 entityManager.merge(item); 105 mergeCount++; 106 } 107 } 108 if (logger.isDebugEnabled()) { 109 logger.debug(mergeCount + " entities merged."); 110 logger.debug((items.size() - mergeCount) + " entities found in persistence context."); 111 } 112 } 113 114 } 115 116}