diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateCursorItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateCursorItemReader.java index 18e6f2fea..55d1263e5 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateCursorItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateCursorItemReader.java @@ -15,6 +15,8 @@ */ package org.springframework.batch.item.database; +import java.util.Map; + import org.hibernate.Query; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; @@ -25,6 +27,7 @@ import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemStream; import org.springframework.batch.item.ItemStreamException; +import org.springframework.batch.item.database.support.AbstractHibernateQueryProvider; import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; @@ -68,6 +71,10 @@ public class HibernateCursorItemReader extends AbstractItemCountingItemStream private String queryName = ""; + private Map parameterValues; + + private HibernateQueryProvider queryProvider; + private boolean useStatelessSession = true; private boolean initialized = false; @@ -77,29 +84,53 @@ public class HibernateCursorItemReader extends AbstractItemCountingItemStream public HibernateCursorItemReader() { setName(ClassUtils.getShortName(HibernateCursorItemReader.class)); } + + /** + * The parameter values to apply to a query (map of name:value). + * + * @param parameterValues the parameter values to set + */ + public void setParameterValues(Map parameterValues) { + this.parameterValues = parameterValues; + } /** * Open appropriate type of hibernate session and create the query. */ private Query createQuery() { + if (useStatelessSession) { statelessSession = sessionFactory.openStatelessSession(); - if (StringUtils.hasText(queryName)) { - return statelessSession.getNamedQuery(queryName); + if (queryProvider != null) { + queryProvider.setStatelessSession(statelessSession); } else { - return statelessSession.createQuery(queryString); + if (StringUtils.hasText(queryName)) { + return statelessSession.getNamedQuery(queryName); + } + else { + return statelessSession.createQuery(queryString); + } } } else { statefulSession = sessionFactory.openSession(); - if (StringUtils.hasText(queryName)) { - return statefulSession.getNamedQuery(queryName); + if (queryProvider != null) { + queryProvider.setSession(statefulSession); } else { - return statefulSession.createQuery(queryString); + if (StringUtils.hasText(queryName)) { + return statefulSession.getNamedQuery(queryName); + } + else { + return statefulSession.createQuery(queryString); + } } } + + // if queryProvider is set use it to create a query + return queryProvider.createQuery(); + } /** @@ -110,10 +141,21 @@ public class HibernateCursorItemReader extends AbstractItemCountingItemStream } public void afterPropertiesSet() throws Exception { - Assert.notNull(sessionFactory, "session factory must be set"); - Assert.isTrue(fetchSize >= 0, "fetchSize must not be negative"); - Assert.isTrue(StringUtils.hasText(queryString) ^ StringUtils.hasText(queryName), - "exactly one of queryString or queryName must be set"); + + Assert.state(sessionFactory!=null, "A SessionFactory must be provided"); + Assert.state(fetchSize >= 0, "fetchSize must not be negative"); + + if (queryProvider == null) { + Assert.notNull(sessionFactory, "session factory must be set"); + Assert.state(StringUtils.hasText(queryString) ^ StringUtils.hasText(queryName), + "queryString or queryName must be set"); + } + // making sure that the appropriate (Hibernate) query provider is set + else { + Assert.state(queryProvider instanceof AbstractHibernateQueryProvider, + "Hibernate query provider must be set"); + } + } /** @@ -130,6 +172,13 @@ public class HibernateCursorItemReader extends AbstractItemCountingItemStream this.queryString = queryString; } + /** + * @param queryProvider Hibernate query provider + */ + public void setQueryProvider(HibernateQueryProvider queryProvider) { + this.queryProvider = queryProvider; + } + /** * Can be set only in uninitialized state. * @@ -193,24 +242,27 @@ public class HibernateCursorItemReader extends AbstractItemCountingItemStream /** * Open hibernate session and create a forward-only cursor for the - * {@link #setQueryString(String)}. + * query. */ protected void doOpen() throws Exception { Assert.state(!initialized, "Cannot open an already opened ItemReader, call close first"); - cursor = createQuery().setFetchSize(fetchSize).scroll(ScrollMode.FORWARD_ONLY); + Query query = createQuery(); + if (parameterValues!=null) { + query.setProperties(parameterValues); + } + cursor = query.setFetchSize(fetchSize).scroll(ScrollMode.FORWARD_ONLY); initialized = true; - } - + @Override protected void jumpToItem(int itemIndex) throws Exception { - for(int i = 0; i < itemIndex; i++){ + for (int i = 0; i < itemIndex; i++) { cursor.next(); } } - + /** * Close the cursor and hibernate session. */ @@ -220,6 +272,7 @@ public class HibernateCursorItemReader extends AbstractItemCountingItemStream if (cursor != null) { cursor.close(); } + if (useStatelessSession) { if (statelessSession != null) { statelessSession.close(); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateQueryProvider.java new file mode 100644 index 000000000..1039deb95 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateQueryProvider.java @@ -0,0 +1,78 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.database; + +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.StatelessSession; +import org.springframework.batch.item.ItemReader; + +/** + *

+ * Interface defining the functionality to be provided for generating queries + * for use with Hibernate {@link ItemReader}s or other custom built artifacts. + *

+ * + * @author Anatoly Polinsky + * @author Dave Syer + * + * @since 2.1 + * + */ +public interface HibernateQueryProvider { + + /** + *

+ * Create the query object which type will be determined by the underline + * implementation (e.g. Hibernate, JPA, etc.) + *

+ * + * @return created query + */ + Query createQuery(); + + /** + *

+ * Inject a {@link Session} that can be used as a factory for queries. The + * state of the session is controlled by the caller (i.e. it should be + * closed if necessary). + *

+ * + *

+ * Use either this method or {@link #setStatelessSession(StatelessSession)} + *

+ * + * @param session the {@link Session} to set + */ + void setSession(Session session); + + /** + *

+ * Inject a {@link StatelessSession} that can be used as a factory for + * queries. The state of the session is controlled by the caller (i.e. it + * should be closed if necessary). + *

+ * + *

+ * Use either this method or {@link #setSession(Session)} + *

+ * + * @param session the {@link StatelessSession} to set + */ + void setStatelessSession(StatelessSession session); + +} 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 8a332b4f2..4ee0e99de 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 @@ -26,6 +26,7 @@ import javax.persistence.EntityTransaction; import javax.persistence.Query; import org.springframework.batch.item.ExecutionContext; +import org.springframework.batch.item.database.support.AbstractJpaQueryProvider; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -90,12 +91,27 @@ public class JpaPagingItemReader extends AbstractPagingItemReader { private String queryString; + private JpaQueryProvider queryProvider; + private Map parameterValues; public JpaPagingItemReader() { setName(ClassUtils.getShortName(JpaPagingItemReader.class)); } + /** + * Create a query using an appropriate query provider (entityManager OR + * queryProvider). + */ + private Query createQuery() { + if (queryProvider == null) { + return entityManager.createQuery(queryString); + } + else { + return queryProvider.createQuery(); + } + } + public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) { this.entityManagerFactory = entityManagerFactory; } @@ -112,8 +128,15 @@ public class JpaPagingItemReader extends AbstractPagingItemReader { public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); - Assert.notNull(entityManagerFactory); - Assert.hasLength(queryString); + + if (queryProvider == null) { + Assert.notNull(entityManagerFactory); + Assert.hasLength(queryString); + } + // making sure that the appropriate (JPA) query provider is set + else { + Assert.isTrue(queryProvider instanceof AbstractJpaQueryProvider, "JPA query provider must be set"); + } } /** @@ -123,13 +146,27 @@ public class JpaPagingItemReader extends AbstractPagingItemReader { this.queryString = queryString; } + /** + * @param queryProvider JPA query provider + */ + public void setQueryProvider(JpaQueryProvider queryProvider) { + this.queryProvider = queryProvider; + } + @Override protected void doOpen() throws Exception { super.doOpen(); + entityManager = entityManagerFactory.createEntityManager(jpaPropertyMap); if (entityManager == null) { throw new DataAccessResourceFailureException("Unable to obtain an EntityManager"); } + // set entityManager to queryProvider, so it participates + // in JpaPagingItemReader's managed transaction + if (queryProvider != null) { + queryProvider.setEntityManager(entityManager); + } + } @Override @@ -142,8 +179,7 @@ public class JpaPagingItemReader extends AbstractPagingItemReader { entityManager.flush(); entityManager.clear(); - Query query = entityManager.createQuery(queryString).setFirstResult(getPage() * getPageSize()).setMaxResults( - getPageSize()); + Query query = createQuery().setFirstResult(getPage() * getPageSize()).setMaxResults(getPageSize()); if (parameterValues != null) { for (Map.Entry me : parameterValues.entrySet()) { diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaQueryProvider.java new file mode 100644 index 000000000..d833ec7f3 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaQueryProvider.java @@ -0,0 +1,49 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.database; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.springframework.batch.item.ItemReader; + +/** + *

Interface defining the functionality to be provided for generating queries + * for use with JPA {@link ItemReader}s or other custom built artifacts.

+ * + * @author Anatoly Polinsky + * @author Dave Syer + * @since 2.1 + * + */ +public interface JpaQueryProvider { + + /** + *

Create the query object.

+ * + * @return created query + */ + public Query createQuery(); + + /** + * Provide an {@link EntityManager} for the query to be built. + * + * @param entityManager + */ + void setEntityManager(EntityManager entityManager); + +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractHibernateQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractHibernateQueryProvider.java new file mode 100644 index 000000000..2518bc01d --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractHibernateQueryProvider.java @@ -0,0 +1,63 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.database.support; + +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.StatelessSession; +import org.springframework.batch.item.database.HibernateQueryProvider; + +/** + *

Abstract Hibernate Query Provider to serve as a base class for all + * Hibernate {@link Query} providers.

+ * + *

The implementing provider can be configured to use either + * {@link StatelessSession} sufficient for simple mappings without the need + * to cascade to associated objects or standard Hibernate {@link Session} + * for more advanced mappings or when caching is desired.

+ * + * @author Anatoly Polinsky + * @author Dave Syer + * + * @since 2.1 + * + */ +public abstract class AbstractHibernateQueryProvider implements HibernateQueryProvider { + + private StatelessSession statelessSession; + private Session statefulSession; + + public void setStatelessSession(StatelessSession statelessSession) { + this.statelessSession = statelessSession; + } + + public void setSession(Session statefulSession) { + this.statefulSession = statefulSession; + } + + public boolean isStatelessSession() { + return this.statefulSession==null && this.statelessSession!=null; + } + + protected StatelessSession getStatelessSession() { + return statelessSession; + } + + protected Session getStatefulSession() { + return statefulSession; + } +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractJpaQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractJpaQueryProvider.java new file mode 100644 index 000000000..3e1b46e0a --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractJpaQueryProvider.java @@ -0,0 +1,64 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.database.support; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.springframework.batch.item.database.JpaQueryProvider; +import org.springframework.batch.item.database.HibernateQueryProvider; +import org.springframework.beans.factory.InitializingBean; + +/** + *

+ * Abstract JPA Query Provider to serve as a base class for all JPA + * {@link Query} providers. + *

+ * + * @author Anatoly Polinsky + * @author Dave Syer + * + * @since 2.1 + */ +public abstract class AbstractJpaQueryProvider implements JpaQueryProvider, InitializingBean { + + private EntityManager entityManager; + + /** + *

+ * Public setter to override the entityManager that was created by this + * {@link HibernateQueryProvider}. This is currently needed to allow + * {@link HibernateQueryProvider} to participate in a user's managed transaction. + *

+ * + * @param entityManager + */ + public void setEntityManager(EntityManager entityManager) { + this.entityManager = entityManager; + } + + /** + *

+ * Getter for {@link EntityManager} + *

+ * + * @return entityManager the injected {@link EntityManager} + */ + protected EntityManager getEntityManager() { + return entityManager; + } +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/HibernateNativeQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/HibernateNativeQueryProvider.java new file mode 100644 index 000000000..75f90e7c5 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/HibernateNativeQueryProvider.java @@ -0,0 +1,69 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.database.support; + +import org.hibernate.Query; +import org.hibernate.SQLQuery; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + *

+ * This query provider creates Hibernate {@link Query}s from injected native SQL + * queries. This is useful if there is a need to utilize database-specific + * features such as query hints, the CONNECT keyword in Oracle, etc. + *

+ * + * @author Anatoly Polinsky + * + * @param entity returned by executing the query + */ +public class HibernateNativeQueryProvider extends AbstractHibernateQueryProvider { + + private String sqlQuery; + + private Class entityClass; + + /** + *

+ * Create an {@link SQLQuery} from the session provided (preferring + * stateless if both are available). + *

+ */ + public SQLQuery createQuery() { + + if (isStatelessSession()) { + return getStatelessSession().createSQLQuery(sqlQuery).addEntity(entityClass); + } + else { + return getStatefulSession().createSQLQuery(sqlQuery).addEntity(entityClass); + } + } + + public void setSqlQuery(String sqlQuery) { + this.sqlQuery = sqlQuery; + } + + public void setEntityClass(Class entityClazz) { + this.entityClass = entityClazz; + } + + public void afterPropertiesSet() throws Exception { + Assert.isTrue(StringUtils.hasText(sqlQuery), "Native SQL query cannot be empty"); + Assert.notNull(entityClass, "Entity class cannot be NULL"); + } +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/JpaNativeQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/JpaNativeQueryProvider.java new file mode 100644 index 000000000..308de29cf --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/JpaNativeQueryProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.database.support; + +import javax.persistence.Query; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + *

+ * This query provider creates JPA {@link Query}s from injected native SQL + * queries. This is useful if there is a need to utilize database-specific + * features such as query hints, the CONNECT keyword in Oracle, etc. + *

+ * + * @author Anatoly Polinsky + * + * @param entity returned by executing the query + */ +public class JpaNativeQueryProvider extends AbstractJpaQueryProvider { + + private Class entityClass; + + private String sqlQuery; + + public Query createQuery() { + return getEntityManager().createNativeQuery(sqlQuery, entityClass); + } + + public void setSqlQuery(String sqlQuery) { + this.sqlQuery = sqlQuery; + } + + public void setEntityClass(Class entityClazz) { + this.entityClass = entityClazz; + } + + public void afterPropertiesSet() throws Exception { + Assert.isTrue(StringUtils.hasText(sqlQuery), "Native SQL query cannot be empty"); + Assert.notNull(entityClass, "Entity class cannot be NULL"); + } +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/HibernateCursorItemReaderNativeQueryIntegrationTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/HibernateCursorItemReaderNativeQueryIntegrationTests.java new file mode 100644 index 000000000..e5cbe30d2 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/HibernateCursorItemReaderNativeQueryIntegrationTests.java @@ -0,0 +1,47 @@ +package org.springframework.batch.item.database; + +import org.hibernate.SessionFactory; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.database.support.HibernateNativeQueryProvider; +import org.springframework.batch.item.sample.Foo; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.orm.hibernate3.LocalSessionFactoryBean; + +/** + * @author Anatoly Polinsky + * @author Dave Syer + */ +public class HibernateCursorItemReaderNativeQueryIntegrationTests extends HibernateCursorItemReaderIntegrationTests { + + public ItemReader createItemReader() throws Exception { + LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean(); + factoryBean.setDataSource(dataSource); + factoryBean.setMappingLocations(new Resource[] { new ClassPathResource("Foo.hbm.xml", getClass()) }); + factoryBean.afterPropertiesSet(); + + SessionFactory sessionFactory = (SessionFactory) factoryBean.getObject(); + + + HibernateCursorItemReader hibernateReader = new HibernateCursorItemReader(); + + String nativeQuery = "select * from T_FOOS"; + + //creating a native query provider as it would be created in configuration + HibernateNativeQueryProvider queryProvider = + new HibernateNativeQueryProvider(); + + queryProvider.setSqlQuery(nativeQuery); + queryProvider.setEntityClass(Foo.class); + queryProvider.afterPropertiesSet(); + + hibernateReader.setQueryProvider(queryProvider); + + //hibernateReader.setUseStatelessSession(isUseStatelessSession()); + hibernateReader.setSessionFactory(sessionFactory); + hibernateReader.setSaveState(true); + hibernateReader.afterPropertiesSet(); + + return hibernateReader; + } +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/HibernateCursorItemReaderParametersIntegrationTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/HibernateCursorItemReaderParametersIntegrationTests.java new file mode 100644 index 000000000..a634f2da4 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/HibernateCursorItemReaderParametersIntegrationTests.java @@ -0,0 +1,54 @@ +package org.springframework.batch.item.database; + +import java.util.Collections; + +import org.hibernate.SessionFactory; +import org.hibernate.StatelessSession; +import org.junit.runner.RunWith; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.sample.Foo; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.orm.hibernate3.LocalSessionFactoryBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Tests for {@link HibernateCursorItemReader} using {@link StatelessSession}. + * + * @author Robert Kasanicky + * @author Dave Syer + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "data-source-context.xml") +public class HibernateCursorItemReaderParametersIntegrationTests extends AbstractDataSourceItemReaderIntegrationTests { + + protected ItemReader createItemReader() throws Exception { + LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean(); + factoryBean.setDataSource(dataSource); + factoryBean.setMappingLocations(new Resource[] { new ClassPathResource("Foo.hbm.xml", getClass()) }); + factoryBean.afterPropertiesSet(); + + SessionFactory sessionFactory = (SessionFactory) factoryBean.getObject(); + + + HibernateCursorItemReader hibernateReader = new HibernateCursorItemReader(); + setQuery(hibernateReader); + hibernateReader.setSessionFactory(sessionFactory); + hibernateReader.setUseStatelessSession(isUseStatelessSession()); + hibernateReader.afterPropertiesSet(); + hibernateReader.setSaveState(true); + + return hibernateReader; + } + + protected void setQuery(HibernateCursorItemReader reader) { + reader.setQueryString("from Foo where name like :name"); + reader.setParameterValues(Collections.singletonMap("name", (Object)"bar%")); + } + + protected boolean isUseStatelessSession() { + return true; + } + +} 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 index 2c9d87a5e..ff44a7e82 100644 --- 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 @@ -1,5 +1,7 @@ package org.springframework.batch.item.database; +import java.util.Collections; + import org.junit.runner.RunWith; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.ContextConfiguration; @@ -28,10 +30,11 @@ public class JpaPagingItemReaderIntegrationTests extends AbstractDataSourceItemR EntityManagerFactory entityManagerFactory = factoryBean.getObject(); - String jpqlQuery = "select f from Foo f"; + String jpqlQuery = "select f from Foo f where name like :name"; JpaPagingItemReader inputSource = new JpaPagingItemReader(); inputSource.setQueryString(jpqlQuery); + inputSource.setParameterValues(Collections.singletonMap("name", (Object)"bar%")); inputSource.setEntityManagerFactory(entityManagerFactory); inputSource.setPageSize(3); inputSource.afterPropertiesSet(); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderNativeQueryIntegrationTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderNativeQueryIntegrationTests.java new file mode 100644 index 000000000..761ee9266 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderNativeQueryIntegrationTests.java @@ -0,0 +1,47 @@ +package org.springframework.batch.item.database; + +import java.util.Collections; + +import javax.persistence.EntityManagerFactory; + +import org.junit.runner.RunWith; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.database.support.JpaNativeQueryProvider; +import org.springframework.batch.item.sample.Foo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Anatoly Polinsky + * @author Dave Syer + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations={"JpaPagingItemReaderParameterTests-context.xml"}) +public class JpaPagingItemReaderNativeQueryIntegrationTests extends AbstractPagingItemReaderParameterTests { + + @Autowired + private EntityManagerFactory entityManagerFactory; + + protected ItemReader getItemReader() throws Exception { + + String sqlQuery = "select * from T_FOOS where value >= :limit"; + + JpaPagingItemReader reader = new JpaPagingItemReader(); + + //creating a native query provider as it would be created in configuration + JpaNativeQueryProvider queryProvider= new JpaNativeQueryProvider(); + queryProvider.setSqlQuery(sqlQuery); + queryProvider.setEntityClass(Foo.class); + queryProvider.afterPropertiesSet(); + + reader.setParameterValues(Collections.singletonMap("limit", 3)); + reader.setEntityManagerFactory(entityManagerFactory); + reader.setPageSize(3); + reader.setQueryProvider(queryProvider); + reader.afterPropertiesSet(); + reader.setSaveState(true); + + return reader; + } +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/HibernateNativeQueryProviderIntegrationTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/HibernateNativeQueryProviderIntegrationTests.java new file mode 100644 index 000000000..0d30c7227 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/HibernateNativeQueryProviderIntegrationTests.java @@ -0,0 +1,103 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.database.support; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import javax.sql.DataSource; + +import org.hibernate.Query; +import org.hibernate.SessionFactory; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.item.sample.Foo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.orm.hibernate3.LocalSessionFactoryBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Anatoly Polinsky + * @author Dave Syer + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "../data-source-context.xml") +public class HibernateNativeQueryProviderIntegrationTests { + + protected DataSource dataSource; + + protected HibernateNativeQueryProvider hibernateQueryProvider; + + private SessionFactory sessionFactory; + + @Autowired + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + public HibernateNativeQueryProviderIntegrationTests() { + hibernateQueryProvider = new HibernateNativeQueryProvider(); + hibernateQueryProvider.setEntityClass(Foo.class); + } + + @Before + public void setUp() throws Exception { + + LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean(); + factoryBean.setDataSource(dataSource); + factoryBean.setMappingLocations(new Resource[] { new ClassPathResource("../Foo.hbm.xml", getClass()) }); + factoryBean.afterPropertiesSet(); + + sessionFactory = (SessionFactory) factoryBean.getObject(); + + } + + @Test + @Transactional + public void shouldRetrieveAndMapAllFoos() throws Exception { + + String nativeQuery = "select * from T_FOOS"; + + hibernateQueryProvider.setSqlQuery(nativeQuery); + hibernateQueryProvider.afterPropertiesSet(); + hibernateQueryProvider.setSession(sessionFactory.getCurrentSession()); + + Query query = hibernateQueryProvider.createQuery(); + + List expectedFoos = new ArrayList(); + + expectedFoos.add(new Foo(1, "bar1", 1)); + expectedFoos.add(new Foo(2, "bar2", 2)); + expectedFoos.add(new Foo(3, "bar3", 3)); + expectedFoos.add(new Foo(4, "bar4", 4)); + expectedFoos.add(new Foo(5, "bar5", 5)); + + @SuppressWarnings("unchecked") + List actualFoos = query.list(); + + assertEquals(actualFoos, expectedFoos); + + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/HibernateNativeQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/HibernateNativeQueryProviderTests.java new file mode 100644 index 000000000..f96989643 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/HibernateNativeQueryProviderTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.database.support; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; + +import org.hibernate.SQLQuery; +import org.hibernate.StatelessSession; +import org.hibernate.classic.Session; +import org.junit.Test; +import org.springframework.util.Assert; + +/** + * @author Anatoly Polinsky + * @author Dave Syer + */ +public class HibernateNativeQueryProviderTests { + + protected HibernateNativeQueryProvider hibernateQueryProvider; + + public HibernateNativeQueryProviderTests() { + hibernateQueryProvider = new HibernateNativeQueryProvider(); + hibernateQueryProvider.setEntityClass(Foo.class); + } + + @Test + public void testCreateQueryWithStatelessSession() { + String sqlQuery = "select * from T_FOOS"; + hibernateQueryProvider.setSqlQuery(sqlQuery); + + StatelessSession session = createMock(StatelessSession.class); + SQLQuery query = createMock(SQLQuery.class); + + expect(session.createSQLQuery(sqlQuery)).andReturn(query); + expect(query.addEntity(Foo.class)).andReturn(query); + + replay(session, query); + + hibernateQueryProvider.setStatelessSession(session); + Assert.notNull(hibernateQueryProvider.createQuery()); + + verify(session, query); + } + + @Test + public void shouldCreateQueryWithStatefulSession() { + String sqlQuery = "select * from T_FOOS"; + hibernateQueryProvider.setSqlQuery(sqlQuery); + + Session session = createMock(Session.class); + SQLQuery query = createMock(SQLQuery.class); + + expect(session.createSQLQuery(sqlQuery)).andReturn(query); + expect(query.addEntity(Foo.class)).andReturn(query); + + replay(session, query); + + hibernateQueryProvider.setSession(session); + Assert.notNull(hibernateQueryProvider.createQuery()); + + verify(session, query); + } + + private static class Foo { + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/JpaNativeQueryProviderIntegrationTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/JpaNativeQueryProviderIntegrationTests.java new file mode 100644 index 000000000..76da811bf --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/JpaNativeQueryProviderIntegrationTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.database.support; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.EntityManagerFactory; +import javax.persistence.Query; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.item.sample.Foo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Anatoly Polinsky + * @author Dave Syer + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "../JpaPagingItemReaderCommonTests-context.xml" }) +public class JpaNativeQueryProviderIntegrationTests { + + @Autowired + private EntityManagerFactory entityManagerFactory; + + private JpaNativeQueryProvider jpaQueryProvider; + + public JpaNativeQueryProviderIntegrationTests() { + jpaQueryProvider = new JpaNativeQueryProvider(); + jpaQueryProvider.setEntityClass(Foo.class); + } + + @Test + @Transactional + public void shouldRetrieveAndMapAllFoos() throws Exception { + + String sqlQuery = "select * from T_FOOS"; + jpaQueryProvider.setSqlQuery(sqlQuery); + jpaQueryProvider.afterPropertiesSet(); + jpaQueryProvider.setEntityManager(entityManagerFactory.createEntityManager()); + + Query query = jpaQueryProvider.createQuery(); + + List expectedFoos = new ArrayList(); + + expectedFoos.add(new Foo(1, "bar1", 1)); + expectedFoos.add(new Foo(2, "bar2", 2)); + expectedFoos.add(new Foo(3, "bar3", 3)); + expectedFoos.add(new Foo(4, "bar4", 4)); + expectedFoos.add(new Foo(5, "bar5", 5)); + + @SuppressWarnings("unchecked") + List actualFoos = query.getResultList(); + + assertEquals(actualFoos, expectedFoos); + } + + @Test + @Transactional + public void shouldExecuteParameterizedQuery() throws Exception { + + String sqlQuery = "select * from T_FOOS where value >= :limit"; + + jpaQueryProvider.setSqlQuery(sqlQuery); + jpaQueryProvider.afterPropertiesSet(); + jpaQueryProvider.setEntityManager(entityManagerFactory.createEntityManager()); + + Query query = jpaQueryProvider.createQuery(); + query.setParameter("limit", 3); + + List expectedFoos = new ArrayList(); + + expectedFoos.add(new Foo(3, "bar3", 3)); + expectedFoos.add(new Foo(4, "bar4", 4)); + expectedFoos.add(new Foo(5, "bar5", 5)); + + @SuppressWarnings("unchecked") + List actualFoos = query.getResultList(); + + assertEquals(actualFoos, expectedFoos); + } +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/JpaNativeQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/JpaNativeQueryProviderTests.java new file mode 100644 index 000000000..a4f69577a --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/JpaNativeQueryProviderTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.database.support; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.junit.Test; +import org.springframework.batch.item.sample.Foo; +import org.springframework.util.Assert; + +/** + * @author Anatoly Polinsky + * @author Dave Syer + */ +public class JpaNativeQueryProviderTests { + + private JpaNativeQueryProvider jpaQueryProvider; + + public JpaNativeQueryProviderTests() { + jpaQueryProvider = new JpaNativeQueryProvider(); + jpaQueryProvider.setEntityClass(Foo.class); + } + + @Test + public void testCreateQuery() { + + String sqlQuery = "select * from T_FOOS where value >= :limit"; + jpaQueryProvider.setSqlQuery(sqlQuery); + + EntityManager entityManager = createMock(EntityManager.class); + Query query = createMock(Query.class); + + expect(entityManager.createNativeQuery(sqlQuery, Foo.class)).andReturn(query); + + replay(entityManager); + + jpaQueryProvider.setEntityManager(entityManager); + Assert.notNull(jpaQueryProvider.createQuery()); + + verify(entityManager); + } +}