diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaPagingItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaPagingItemReader.java index 012145c85..32c457a58 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaPagingItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaPagingItemReader.java @@ -23,6 +23,7 @@ import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Query; +import javax.persistence.EntityTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -51,8 +52,8 @@ import org.springframework.util.ClassUtils; * after each page is read. This cuases any entities read to be detached. If you make changes to the * entities and want the changes persisted then you must explicitly merge the entities. * - * The reader must be configured with an {@link javax.persistence.EntityManagerFactory} that is capable - * of participating in Spring managed transactions. + * The reader must be configured with an {@link javax.persistence.EntityManagerFactory}. All entity access + * is performed within a new transaction, independent of any existing Spring managed transactions. * * The implementation is *not* thread-safe. * @@ -122,11 +123,14 @@ public class JpaPagingItemReader extends AbstractBufferedItemReaderItemStream if (entities == null || current >= pageSize) { EntityManager entityManager = - EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory, jpaPropertyMap); + entityManagerFactory.createEntityManager(jpaPropertyMap); if (entityManager == null) { - throw new DataAccessResourceFailureException("Unable to obtain a transactional EntityManager"); + throw new DataAccessResourceFailureException("Unable to obtain an EntityManager"); } + EntityTransaction tx = entityManager.getTransaction(); + tx.begin(); + Query query = entityManager.createQuery(queryString) .setFirstResult(page * pageSize) .setMaxResults(pageSize); @@ -136,6 +140,8 @@ public class JpaPagingItemReader extends AbstractBufferedItemReaderItemStream entityManager.flush(); entityManager.clear(); + tx.commit(); + if (current >= pageSize) { current = 0; } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderCommonTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderCommonTests.java index 865316cf3..63350624e 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderCommonTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderCommonTests.java @@ -7,7 +7,6 @@ import org.springframework.batch.item.CommonItemStreamItemReaderTests; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.ContextConfiguration; -import org.springframework.transaction.annotation.Transactional; import org.junit.runner.RunWith; import org.junit.Test; @@ -43,32 +42,32 @@ public class JpaPagingItemReaderCommonTests extends CommonItemStreamItemReaderTe } - @Transactional @Test + @Test public void testRestart() throws Exception { super.testRestart(); } - @Transactional @Test + @Test public void testResetAndRestart() throws Exception { super.testResetAndRestart(); } - @Transactional @Test + @Test public void testReopen() throws Exception { super.testReopen(); } - @Transactional @Test + @Test public void testRead() throws Exception { super.testRead(); } - @Transactional @Test + @Test public void testReset() throws Exception { super.testReset(); } - @Transactional @Test + @Test public void testEmptyInput() throws Exception { super.testEmptyInput(); } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderIntegrationTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderIntegrationTests.java new file mode 100644 index 000000000..2d4740848 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderIntegrationTests.java @@ -0,0 +1,42 @@ +package org.springframework.batch.item.database; + +import org.junit.runner.RunWith; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.batch.item.sample.Foo; +import org.springframework.batch.item.ItemReader; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; + +import javax.persistence.EntityManagerFactory; + +/** + * Tests for {@link org.springframework.batch.item.database.JpaPagingItemReader}. + * + * @author Thomas Risberg + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "data-source-context.xml") +public class JpaPagingItemReaderIntegrationTests extends AbstractDataSourceItemReaderIntegrationTests { + + protected ItemReader createItemReader() throws Exception { + LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); + factoryBean.setDataSource(dataSource); + factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + factoryBean.setPersistenceUnitName("bar"); + factoryBean.afterPropertiesSet(); + + EntityManagerFactory entityManagerFactory = factoryBean.getObject(); + + String jpqlQuery = "select f from Foo f"; + + JpaPagingItemReader inputSource = new JpaPagingItemReader(); + inputSource.setQueryString(jpqlQuery); + inputSource.setEntityManagerFactory(entityManagerFactory); + inputSource.afterPropertiesSet(); + inputSource.setSaveState(true); + + return inputSource; + } + +}