diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/builder/HibernateCursorItemReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/builder/HibernateCursorItemReaderBuilder.java index 239eb373d..91f0f8600 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/builder/HibernateCursorItemReaderBuilder.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/builder/HibernateCursorItemReaderBuilder.java @@ -20,17 +20,19 @@ import java.util.Map; import org.hibernate.SessionFactory; import org.springframework.batch.item.database.HibernateCursorItemReader; +import org.springframework.batch.item.database.orm.HibernateNativeQueryProvider; import org.springframework.batch.item.database.orm.HibernateQueryProvider; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * This is a builder for the {@link HibernateCursorItemReader}. When configuring, one of - * the following should be provided: + * the following should be provided (listed in order of precedence): * * * @author Michael Minella @@ -61,6 +63,10 @@ public class HibernateCursorItemReaderBuilder { private String name; + private String nativeQuery; + + private Class nativeClass; + /** * A name used to prevent key collisions while saving the state in the * {@link org.springframework.batch.item.ExecutionContext} @@ -212,6 +218,23 @@ public class HibernateCursorItemReaderBuilder { return this; } + /** + * Used to configure a {@link HibernateNativeQueryProvider}. This is ignored if + * @param nativeQuery + * @return + */ + public HibernateCursorItemReaderBuilder nativeQuery(String nativeQuery) { + this.nativeQuery = nativeQuery; + + return this; + } + + public HibernateCursorItemReaderBuilder entityClass(Class nativeClass) { + this.nativeClass = nativeClass; + + return this; + } + /** * Returns a fully constructed {@link HibernateCursorItemReader}. * @@ -226,18 +249,39 @@ public class HibernateCursorItemReaderBuilder { "A name is required when saveState is set to true."); } - if (queryProvider == null) { - Assert.state(StringUtils.hasText(queryString) ^ StringUtils.hasText(queryName), - "queryString or queryName must be set"); - } - HibernateCursorItemReader reader = new HibernateCursorItemReader<>(); reader.setFetchSize(this.fetchSize); reader.setParameterValues(this.parameterValues); - reader.setQueryName(this.queryName); - reader.setQueryProvider(this.queryProvider); - reader.setQueryString(this.queryString); + + if(this.queryProvider != null) { + reader.setQueryProvider(this.queryProvider); + } + else if(StringUtils.hasText(this.queryName)) { + reader.setQueryName(this.queryName); + } + else if(StringUtils.hasText(this.queryString)) { + reader.setQueryString(this.queryString); + } + else if(StringUtils.hasText(this.nativeQuery) && this.nativeClass != null) { + HibernateNativeQueryProvider provider = new HibernateNativeQueryProvider(); + provider.setSqlQuery(this.nativeQuery); + provider.setEntityClass(this.nativeClass); + + try { + provider.afterPropertiesSet(); + } + catch (Exception e) { + throw new IllegalStateException("Unable to initialize the HibernateNativeQueryProvider", e); + } + + reader.setQueryProvider(provider); + } + else { + throw new IllegalStateException("A HibernateQueryProvider, queryName, queryString, " + + "or both the nativeQuery and entityClass must be configured"); + } + reader.setSessionFactory(this.sessionFactory); reader.setUseStatelessSession(this.useStatelessSession); reader.setCurrentItemCount(this.currentItem); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/builder/HibernateCursorItemReaderBuilderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/builder/HibernateCursorItemReaderBuilderTests.java index f7d44a5f1..ffa812fb2 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/builder/HibernateCursorItemReaderBuilderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/builder/HibernateCursorItemReaderBuilderTests.java @@ -137,7 +137,6 @@ public class HibernateCursorItemReaderBuilderTests { provider.setSqlQuery("select * from T_FOOS"); provider.afterPropertiesSet(); - HibernateCursorItemReader reader = new HibernateCursorItemReaderBuilder() .name("fooReader") .sessionFactory(this.sessionFactory) @@ -161,6 +160,32 @@ public class HibernateCursorItemReaderBuilderTests { assertEquals(5, i); } + @Test + public void testConfigurationNativeQuery() throws Exception { + HibernateCursorItemReader reader = new HibernateCursorItemReaderBuilder() + .name("fooReader") + .sessionFactory(this.sessionFactory) + .nativeQuery("select * from T_FOOS") + .entityClass(Foo.class) + .build(); + + reader.afterPropertiesSet(); + + ExecutionContext executionContext = new ExecutionContext(); + + reader.open(executionContext); + + int i = 0; + while(reader.read() != null) { + i++; + } + + reader.update(executionContext); + reader.close(); + + assertEquals(5, i); + } + @Test public void testValidation() { try { @@ -195,10 +220,12 @@ public class HibernateCursorItemReaderBuilderTests { .sessionFactory(this.sessionFactory) .saveState(false) .build(); - fail("queryString or queryName must be set"); + fail("A HibernateQueryProvider, queryName, queryString, " + + "or both the nativeQuery and entityClass must be configured"); } catch (IllegalStateException ise) { - assertEquals("queryString or queryName must be set", ise.getMessage()); + assertEquals("A HibernateQueryProvider, queryName, queryString, " + + "or both the nativeQuery and entityClass must be configured", ise.getMessage()); } }