From aee5ca3b85ee5145ba9ede4022d92517343fc6f9 Mon Sep 17 00:00:00 2001 From: dsyer Date: Fri, 2 Jan 2009 14:10:11 +0000 Subject: [PATCH] RESOLVED - issue BATCH-986: Provide factory bean for SqlPagingQueryProvider --- .../support/JobExplorerFactoryBean.java | 48 +---- .../repository/dao/JdbcJobExecutionDao.java | 4 +- .../support/JobExplorerFactoryBeanTests.java | 112 +---------- .../AbstractSqlPagingQueryProvider.java | 2 +- ...ltDataFieldMaxValueIncrementerFactory.java | 44 ++-- .../SqlPagingQueryProviderFactoryBean.java | 166 +++++++++++++++ .../batch/support/DatabaseType.java | 2 +- ...qlPagingQueryProviderFactoryBeanTests.java | 109 ++++++++++ .../batch/support/DatabaseTypeTestUtils.java | 61 ++++++ .../batch/support/DatabaseTypeTests.java | 190 ++++++------------ 10 files changed, 441 insertions(+), 297 deletions(-) create mode 100644 spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java create mode 100644 spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBeanTests.java create mode 100644 spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTestUtils.java diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/explore/support/JobExplorerFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/explore/support/JobExplorerFactoryBean.java index 17c035a86..86d4f8c00 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/explore/support/JobExplorerFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/explore/support/JobExplorerFactoryBean.java @@ -25,15 +25,13 @@ import org.springframework.batch.core.repository.dao.JdbcStepExecutionDao; import org.springframework.batch.core.repository.dao.JobExecutionDao; import org.springframework.batch.core.repository.dao.JobInstanceDao; import org.springframework.batch.core.repository.dao.StepExecutionDao; -import org.springframework.batch.item.database.support.DataFieldMaxValueIncrementerFactory; -import org.springframework.batch.item.database.support.DefaultDataFieldMaxValueIncrementerFactory; -import org.springframework.batch.support.DatabaseType; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.jdbc.core.simple.SimpleJdbcOperations; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; +import org.springframework.jdbc.support.incrementer.AbstractDataFieldMaxValueIncrementer; +import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; /** * A {@link FactoryBean} that automates the creation of a @@ -48,11 +46,14 @@ public class JobExplorerFactoryBean extends AbstractJobExplorerFactoryBean imple private SimpleJdbcOperations jdbcTemplate; - private String databaseType; - private String tablePrefix = AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX; - private DataFieldMaxValueIncrementerFactory incrementerFactory; + private DataFieldMaxValueIncrementer incrementer = new AbstractDataFieldMaxValueIncrementer() { + @Override + protected long getNextKey() { + throw new IllegalStateException("JobExplorer is read only."); + } + }; /** * Public setter for the {@link DataSource}. @@ -62,15 +63,6 @@ public class JobExplorerFactoryBean extends AbstractJobExplorerFactoryBean imple this.dataSource = dataSource; } - /** - * Sets the database type. - * @param dbType as specified by - * {@link DefaultDataFieldMaxValueIncrementerFactory} - */ - public void setDatabaseType(String dbType) { - this.databaseType = dbType; - } - /** * Sets the table prefix for all the batch meta-data tables. * @param tablePrefix @@ -79,28 +71,12 @@ public class JobExplorerFactoryBean extends AbstractJobExplorerFactoryBean imple this.tablePrefix = tablePrefix; } - public void setIncrementerFactory(DataFieldMaxValueIncrementerFactory incrementerFactory) { - this.incrementerFactory = incrementerFactory; - } - public void afterPropertiesSet() throws Exception { Assert.notNull(dataSource, "DataSource must not be null."); jdbcTemplate = new SimpleJdbcTemplate(dataSource); - if (incrementerFactory == null) { - incrementerFactory = new DefaultDataFieldMaxValueIncrementerFactory(dataSource); - } - - if (databaseType == null) { - databaseType = DatabaseType.fromMetaData(dataSource).name(); - } - - Assert.isTrue(incrementerFactory.isSupportedIncrementerType(databaseType), "'" + databaseType - + "' is an unsupported database type. The supported database types are " - + StringUtils.arrayToCommaDelimitedString(incrementerFactory.getSupportedIncrementerTypes())); - } private Object getTarget() throws Exception { @@ -111,7 +87,7 @@ public class JobExplorerFactoryBean extends AbstractJobExplorerFactoryBean imple protected JobInstanceDao createJobInstanceDao() throws Exception { JdbcJobInstanceDao dao = new JdbcJobInstanceDao(); dao.setJdbcTemplate(jdbcTemplate); - dao.setJobIncrementer(incrementerFactory.getIncrementer(databaseType, tablePrefix + "JOB_SEQ")); + dao.setJobIncrementer(incrementer); dao.setTablePrefix(tablePrefix); dao.afterPropertiesSet(); return dao; @@ -121,8 +97,7 @@ public class JobExplorerFactoryBean extends AbstractJobExplorerFactoryBean imple protected JobExecutionDao createJobExecutionDao() throws Exception { JdbcJobExecutionDao dao = new JdbcJobExecutionDao(); dao.setJdbcTemplate(jdbcTemplate); - dao.setJobExecutionIncrementer(incrementerFactory.getIncrementer(databaseType, tablePrefix - + "JOB_EXECUTION_SEQ")); + dao.setJobExecutionIncrementer(incrementer); dao.setTablePrefix(tablePrefix); dao.afterPropertiesSet(); return dao; @@ -131,8 +106,7 @@ public class JobExplorerFactoryBean extends AbstractJobExplorerFactoryBean imple protected StepExecutionDao createStepExecutionDao() throws Exception { JdbcStepExecutionDao dao = new JdbcStepExecutionDao(); dao.setJdbcTemplate(jdbcTemplate); - dao.setStepExecutionIncrementer(incrementerFactory.getIncrementer(databaseType, tablePrefix - + "STEP_EXECUTION_SEQ")); + dao.setStepExecutionIncrementer(incrementer); dao.setTablePrefix(tablePrefix); dao.afterPropertiesSet(); return dao; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java index e59c2bfa7..e6a76c6a8 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java @@ -87,7 +87,7 @@ public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); - Assert.notNull(jobExecutionIncrementer); + Assert.notNull(jobExecutionIncrementer, "The jobExecutionIncrementer must not be null."); } public List findJobExecutions(final JobInstance job) { @@ -271,7 +271,7 @@ public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements * @author Dave Syer * */ - private static class JobExecutionRowMapper implements ParameterizedRowMapper { + protected static class JobExecutionRowMapper implements ParameterizedRowMapper { private JobInstance jobInstance; diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/exlore/support/JobExplorerFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/exlore/support/JobExplorerFactoryBeanTests.java index 6d70302c9..5e17045ed 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/exlore/support/JobExplorerFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/exlore/support/JobExplorerFactoryBeanTests.java @@ -18,32 +18,23 @@ package org.springframework.batch.core.exlore.support; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; 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 java.sql.Connection; -import java.sql.DatabaseMetaData; +import static org.junit.Assert.assertNotNull; import javax.sql.DataSource; import org.junit.Before; import org.junit.Test; +import org.springframework.batch.core.explore.JobExplorer; import org.springframework.batch.core.explore.support.JobExplorerFactoryBean; -import org.springframework.batch.item.database.support.DataFieldMaxValueIncrementerFactory; -import org.springframework.dao.DataAccessException; -import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer; /** - * @author Lucas Ward + * @author Dave Syer * */ public class JobExplorerFactoryBeanTests { private JobExplorerFactoryBean factory; - private DataFieldMaxValueIncrementerFactory incrementerFactory; - private DataSource dataSource; private String tablePrefix = "TEST_BATCH_PREFIX_"; @@ -54,55 +45,10 @@ public class JobExplorerFactoryBeanTests { factory = new JobExplorerFactoryBean(); dataSource = createMock(DataSource.class); factory.setDataSource(dataSource); - incrementerFactory = createMock(DataFieldMaxValueIncrementerFactory.class); - factory.setIncrementerFactory(incrementerFactory); factory.setTablePrefix(tablePrefix); } - @Test - public void testDetectDatabaseType() throws Exception { - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - Connection con = createMock(Connection.class); - expect(dataSource.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("Oracle"); - expect(incrementerFactory.isSupportedIncrementerType("ORACLE")).andReturn(true); - expect(incrementerFactory.getSupportedIncrementerTypes()).andReturn(new String[0]); - expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_SEQ")).andReturn(new StubIncrementer()); - expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_EXECUTION_SEQ")).andReturn( - new StubIncrementer()); - expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "STEP_EXECUTION_SEQ")).andReturn( - new StubIncrementer()); - replay(dataSource, con, dmd, incrementerFactory); - factory.afterPropertiesSet(); - - } - - @Test - public void testNoDatabaseType() throws Exception { - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - Connection con = createMock(Connection.class); - expect(dataSource.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("foo"); - try { - expect(incrementerFactory.isSupportedIncrementerType(null)).andReturn(false); - expect(incrementerFactory.getSupportedIncrementerTypes()).andReturn(new String[0]); - replay(dataSource, con, dmd, incrementerFactory); - factory.afterPropertiesSet(); - fail(); - } - catch (IllegalArgumentException ex) { - // expected - String message = ex.getMessage(); - assertTrue("Wrong message: " + message, message.indexOf("DatabaseType") >= 0); - } - - } - @Test public void testMissingDataSource() throws Exception { @@ -119,60 +65,12 @@ public class JobExplorerFactoryBeanTests { } - @Test - public void testInvalidDatabaseType() throws Exception { - - factory.setDatabaseType("foo"); - try { - expect(incrementerFactory.isSupportedIncrementerType("foo")).andReturn(false); - expect(incrementerFactory.getSupportedIncrementerTypes()).andReturn(new String[0]); - replay(incrementerFactory); - factory.afterPropertiesSet(); - fail(); - } - catch (IllegalArgumentException ex) { - // expected - String message = ex.getMessage(); - assertTrue("Wrong message: " + message, message.indexOf("foo") >= 0); - } - - } - @Test public void testCreateExplorer() throws Exception { - String databaseType = "foo"; - factory.setDatabaseType(databaseType); - - expect(incrementerFactory.isSupportedIncrementerType("foo")).andReturn(true); - expect(incrementerFactory.getSupportedIncrementerTypes()).andReturn(new String[0]); - expect(incrementerFactory.getIncrementer(databaseType, tablePrefix + "JOB_SEQ")).andReturn( - new StubIncrementer()); - expect(incrementerFactory.getIncrementer(databaseType, tablePrefix + "JOB_EXECUTION_SEQ")).andReturn( - new StubIncrementer()); - expect(incrementerFactory.getIncrementer(databaseType, tablePrefix + "STEP_EXECUTION_SEQ")).andReturn( - new StubIncrementer()); - replay(incrementerFactory); factory.afterPropertiesSet(); - factory.getObject(); - - verify(incrementerFactory); - - } - - private static class StubIncrementer implements DataFieldMaxValueIncrementer { - - public int nextIntValue() throws DataAccessException { - return 0; - } - - public long nextLongValue() throws DataAccessException { - return 0; - } - - public String nextStringValue() throws DataAccessException { - return null; - } + JobExplorer explorer = (JobExplorer) factory.getObject(); + assertNotNull(explorer); } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java index dd9518e6b..50720cc41 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java @@ -130,7 +130,7 @@ public abstract class AbstractSqlPagingQueryProvider implements PagingQueryProvi } /** - * The sort key placegholder will vary depending on whether named parameters or traditional placeholders + * The sort key placeholder will vary depending on whether named parameters or traditional placeholders * are used in query strings. * * @return place holder for sortKey. diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DefaultDataFieldMaxValueIncrementerFactory.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DefaultDataFieldMaxValueIncrementerFactory.java index f1dd121d2..f05986ecb 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DefaultDataFieldMaxValueIncrementerFactory.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DefaultDataFieldMaxValueIncrementerFactory.java @@ -15,7 +15,15 @@ */ package org.springframework.batch.item.database.support; -import static org.springframework.batch.support.DatabaseType.*; +import static org.springframework.batch.support.DatabaseType.DB2; +import static org.springframework.batch.support.DatabaseType.DB2ZOS; +import static org.springframework.batch.support.DatabaseType.DERBY; +import static org.springframework.batch.support.DatabaseType.HSQL; +import static org.springframework.batch.support.DatabaseType.MYSQL; +import static org.springframework.batch.support.DatabaseType.ORACLE; +import static org.springframework.batch.support.DatabaseType.POSTGRES; +import static org.springframework.batch.support.DatabaseType.SQLSERVER; +import static org.springframework.batch.support.DatabaseType.SYBASE; import java.util.ArrayList; import java.util.List; @@ -23,6 +31,7 @@ import java.util.List; import javax.sql.DataSource; import org.springframework.batch.support.DatabaseType; +import org.springframework.jdbc.support.incrementer.DB2MainframeSequenceMaxValueIncrementer; import org.springframework.jdbc.support.incrementer.DB2SequenceMaxValueIncrementer; import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer; import org.springframework.jdbc.support.incrementer.DerbyMaxValueIncrementer; @@ -32,25 +41,10 @@ import org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrem import org.springframework.jdbc.support.incrementer.PostgreSQLSequenceMaxValueIncrementer; import org.springframework.jdbc.support.incrementer.SqlServerMaxValueIncrementer; import org.springframework.jdbc.support.incrementer.SybaseMaxValueIncrementer; -import org.springframework.jdbc.support.incrementer.DB2MainframeSequenceMaxValueIncrementer; /** * Default implementation of the {@link DataFieldMaxValueIncrementerFactory} - * interface. Valid types are: - * - * Valid values are: - * - *
    - *
  • db2
  • - *
  • db2zos
  • - *
  • derby
  • - *
  • hsql
  • - *
  • mysql
  • - *
  • oracle
  • - *
  • postgres
  • - *
  • sqlserver
  • - *
  • sybase
  • - *
+ * interface. Valid database types are given by the {@link DatabaseType} enum. * * @author Lucas Ward * @see DatabaseType @@ -64,8 +58,8 @@ public class DefaultDataFieldMaxValueIncrementerFactory implements DataFieldMaxV /** * Public setter for the column name (defaults to "ID") in the incrementer. * Only used by some platforms (Derby, HSQL, MySQL, SQL Server and Sybase), - * and should be fine for use with Spring Batch meta data as long as the default - * batch schema hasn't been changed. + * and should be fine for use with Spring Batch meta data as long as the + * default batch schema hasn't been changed. * * @param incrementerColumnName the primary key column name to set */ @@ -79,7 +73,7 @@ public class DefaultDataFieldMaxValueIncrementerFactory implements DataFieldMaxV public DataFieldMaxValueIncrementer getIncrementer(String incrementerType, String incrementerName) { DatabaseType databaseType = DatabaseType.valueOf(incrementerType.toUpperCase()); - + if (databaseType == DB2) { return new DB2SequenceMaxValueIncrementer(dataSource, incrementerName); } @@ -112,20 +106,20 @@ public class DefaultDataFieldMaxValueIncrementerFactory implements DataFieldMaxV } public boolean isSupportedIncrementerType(String incrementerType) { - for(DatabaseType type : DatabaseType.values()){ - if(type.name().equals(incrementerType.toUpperCase())){ + for (DatabaseType type : DatabaseType.values()) { + if (type.name().equals(incrementerType.toUpperCase())) { return true; } } - + return false; } public String[] getSupportedIncrementerTypes() { - + List types = new ArrayList(); - for(DatabaseType type : DatabaseType.values()){ + for (DatabaseType type : DatabaseType.values()) { types.add(type.name()); } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java new file mode 100644 index 000000000..46ead9fae --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java @@ -0,0 +1,166 @@ +/* + * 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.springframework.batch.support.DatabaseType.DB2; +import static org.springframework.batch.support.DatabaseType.DB2ZOS; +import static org.springframework.batch.support.DatabaseType.DERBY; +import static org.springframework.batch.support.DatabaseType.HSQL; +import static org.springframework.batch.support.DatabaseType.MYSQL; +import static org.springframework.batch.support.DatabaseType.ORACLE; +import static org.springframework.batch.support.DatabaseType.POSTGRES; +import static org.springframework.batch.support.DatabaseType.SQLSERVER; +import static org.springframework.batch.support.DatabaseType.SYBASE; + +import java.util.HashMap; +import java.util.Map; + +import javax.sql.DataSource; + +import org.springframework.batch.item.database.PagingQueryProvider; +import org.springframework.batch.support.DatabaseType; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.jdbc.support.MetaDataAccessException; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Factory bean for {@link PagingQueryProvider} interface. The database type + * will be determined from the data source if not provided explicitly. Valid + * types are given by the {@link DatabaseType} enum. + * + * @author Dave Syer + */ +public class SqlPagingQueryProviderFactoryBean implements FactoryBean { + + private DataSource dataSource; + + private String databaseType; + + private String fromClause; + + private String whereClause; + + private String selectClause; + + private String sortKey; + + private Map providers = new HashMap(); + { + providers.put(DB2, new Db2PagingQueryProvider()); + providers.put(DB2ZOS, new Db2PagingQueryProvider()); + providers.put(DERBY,new DerbyPagingQueryProvider()); + providers.put(HSQL,new HsqlPagingQueryProvider()); + providers.put(MYSQL,new MySqlPagingQueryProvider()); + providers.put(ORACLE,new OraclePagingQueryProvider()); + providers.put(POSTGRES,new PostgresPagingQueryProvider()); + providers.put(SQLSERVER,new SqlServerPagingQueryProvider()); + providers.put(SYBASE,new SybasePagingQueryProvider()); + } + + /** + * @param databaseType the databaseType to set + */ + public void setDatabaseType(String databaseType) { + this.databaseType = databaseType; + } + + /** + * @param dataSource the dataSource to set + */ + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + /** + * @param fromClause the fromClause to set + */ + public void setFromClause(String fromClause) { + this.fromClause = fromClause; + } + + /** + * @param whereClause the whereClause to set + */ + public void setWhereClause(String whereClause) { + this.whereClause = whereClause; + } + + /** + * @param selectClause the selectClause to set + */ + public void setSelectClause(String selectClause) { + this.selectClause = selectClause; + } + + /** + * @param sortKey the sortKey to set + */ + public void setSortKey(String sortKey) { + this.sortKey = sortKey; + } + + /** + * Get a {@link PagingQueryProvider} instance using the provided properties + * and appropriate for the given database type. + * + * @see FactoryBean#getObject() + */ + public Object getObject() throws Exception { + + DatabaseType type; + try { + type = databaseType != null ? DatabaseType.valueOf(databaseType.toUpperCase()) : DatabaseType + .fromMetaData(dataSource); + } + catch (MetaDataAccessException e) { + throw new IllegalArgumentException( + "Could not inspect meta data for database type. You have to supply it explicitly."); + } + + AbstractSqlPagingQueryProvider provider = providers.get(type); + Assert.state(provider!=null, "Should not happen: missing PagingQueryProvider for DatabaseType="+type); + + provider.setFromClause(fromClause); + provider.setWhereClause(whereClause); + provider.setSortKey(sortKey); + if (StringUtils.hasText(selectClause)) { + provider.setSelectClause(selectClause); + } + + provider.init(dataSource); + + return provider; + + } + + /** + * Always returns {@link PagingQueryProvider}. + * + * @see FactoryBean#getObjectType() + */ + public Class getObjectType() { + return PagingQueryProvider.class; + } + + /** + * Always returns true. + * @see FactoryBean#isSingleton() + */ + public boolean isSingleton() { + return true; + } +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/DatabaseType.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/DatabaseType.java index df55774bf..8b3f0f887 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/DatabaseType.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/DatabaseType.java @@ -76,7 +76,7 @@ public enum DatabaseType { * @return DatabaseType * @throws MetaDataAccessException */ - public static DatabaseType fromMetaData(DataSource dataSource) throws MetaDataAccessException{ + public static DatabaseType fromMetaData(DataSource dataSource) throws MetaDataAccessException { String databaseProductName = JdbcUtils.extractDatabaseMetaData(dataSource, "getDatabaseProductName").toString(); if ("DB2".equals(databaseProductName)) { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBeanTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBeanTests.java new file mode 100644 index 000000000..57a924250 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBeanTests.java @@ -0,0 +1,109 @@ +/* + * 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 static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import javax.sql.DataSource; + +import org.easymock.EasyMock; +import org.junit.Test; +import org.springframework.batch.item.database.PagingQueryProvider; +import org.springframework.batch.support.DatabaseType; +import org.springframework.batch.support.DatabaseTypeTestUtils; +import org.springframework.jdbc.support.MetaDataAccessException; + +/** + * @author Dave Syer + */ +public class SqlPagingQueryProviderFactoryBeanTests { + + private SqlPagingQueryProviderFactoryBean factory = new SqlPagingQueryProviderFactoryBean(); + + public SqlPagingQueryProviderFactoryBeanTests() throws Exception { + factory.setSelectClause("id, name, age"); + factory.setFromClause("foo"); + factory.setWhereClause("bar = 1"); + factory.setSortKey("id"); + DataSource dataSource = DatabaseTypeTestUtils.getMockDataSource(DatabaseType.HSQL.getProductName(), "100.0.0"); + factory.setDataSource(dataSource); + EasyMock.replay(dataSource); + } + + @Test + public void testFactory() throws Exception { + PagingQueryProvider provider = (PagingQueryProvider) factory.getObject(); + assertNotNull(provider); + } + + @Test + public void testType() throws Exception { + assertEquals(PagingQueryProvider.class, factory.getObjectType()); + } + + @Test + public void testSingleton() throws Exception { + assertEquals(true, factory.isSingleton()); + } + + @Test(expected=IllegalArgumentException.class) + public void testNoDataSource() throws Exception { + factory.setDataSource(null); + PagingQueryProvider provider = (PagingQueryProvider) factory.getObject(); + assertNotNull(provider); + } + + @Test(expected=IllegalArgumentException.class) + public void testNoSortKey() throws Exception { + factory.setSortKey(null); + PagingQueryProvider provider = (PagingQueryProvider) factory.getObject(); + assertNotNull(provider); + } + + @Test + public void testWhereClause() throws Exception { + factory.setWhereClause("x=y"); + PagingQueryProvider provider = (PagingQueryProvider) factory.getObject(); + String query = provider.generateFirstPageQuery(100); + assertTrue("Wrong query: "+query, query.contains("x=y")); + } + + @Test(expected=IllegalArgumentException.class) + public void testWrongDatabaseType() throws Exception { + factory.setDatabaseType("NoSuchDb"); + PagingQueryProvider provider = (PagingQueryProvider) factory.getObject(); + assertNotNull(provider); + } + + @Test(expected=IllegalArgumentException.class) + public void testMissingMetaData() throws Exception { + factory.setDataSource(DatabaseTypeTestUtils.getMockDataSource(new MetaDataAccessException("foo"))); + PagingQueryProvider provider = (PagingQueryProvider) factory.getObject(); + assertNotNull(provider); + } + + @Test + public void testAllDatabaseTypes() throws Exception { + for (DatabaseType type : DatabaseType.values()) { + factory.setDatabaseType(type.name()); + PagingQueryProvider provider = (PagingQueryProvider) factory.getObject(); + assertNotNull(provider); + } + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTestUtils.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTestUtils.java new file mode 100644 index 000000000..fe7258c79 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTestUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright 2006-2007 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.support; + +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; + +import javax.sql.DataSource; + +/** + * @author Dave Syer + * + */ +public class DatabaseTypeTestUtils { + + public static DataSource getMockDataSource() throws Exception { + return getMockDataSource(DatabaseType.HSQL.getProductName()); + } + + public static DataSource getMockDataSource(String databaseProductName) throws Exception { + return getMockDataSource(databaseProductName, null); + } + + public static DataSource getMockDataSource(String databaseProductName, String databaseVersion) throws Exception { + DatabaseMetaData dmd = createNiceMock(DatabaseMetaData.class); + DataSource ds = createNiceMock(DataSource.class); + Connection con = createNiceMock(Connection.class); + expect(ds.getConnection()).andReturn(con).anyTimes(); + expect(con.getMetaData()).andReturn(dmd).anyTimes(); + expect(dmd.getDatabaseProductName()).andReturn(databaseProductName).anyTimes(); + if (databaseVersion!=null) { + expect(dmd.getDatabaseProductVersion()).andReturn(databaseVersion).anyTimes(); + } + replay(dmd, con); + return ds; + } + + public static DataSource getMockDataSource(Exception e) throws Exception { + DataSource ds = createNiceMock(DataSource.class); + expect(ds.getConnection()).andReturn(null).anyTimes(); + return ds; + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTests.java index 1f5d937ea..15921e09c 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTests.java @@ -1,25 +1,33 @@ package org.springframework.batch.support; -import static org.springframework.batch.support.DatabaseType.*; -import static org.junit.Assert.*; -import static org.easymock.EasyMock.*; - -import java.sql.Connection; -import java.sql.DatabaseMetaData; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.springframework.batch.support.DatabaseType.DB2; +import static org.springframework.batch.support.DatabaseType.DB2ZOS; +import static org.springframework.batch.support.DatabaseType.DERBY; +import static org.springframework.batch.support.DatabaseType.HSQL; +import static org.springframework.batch.support.DatabaseType.MYSQL; +import static org.springframework.batch.support.DatabaseType.ORACLE; +import static org.springframework.batch.support.DatabaseType.POSTGRES; +import static org.springframework.batch.support.DatabaseType.SQLSERVER; +import static org.springframework.batch.support.DatabaseType.SYBASE; +import static org.springframework.batch.support.DatabaseType.fromProductName; import javax.sql.DataSource; import org.junit.Test; +import org.springframework.jdbc.support.MetaDataAccessException; /** * * @author Lucas Ward - * + * */ public class DatabaseTypeTests { @Test - public void testFromProductName(){ + public void testFromProductName() { assertEquals(DERBY, fromProductName("Apache Derby")); assertEquals(DB2, fromProductName("DB2")); assertEquals(DB2ZOS, fromProductName("DB2ZOS")); @@ -30,157 +38,91 @@ public class DatabaseTypeTests { assertEquals(POSTGRES, fromProductName("PostgreSQL")); assertEquals(SYBASE, fromProductName("Sybase")); } - - @Test(expected=IllegalArgumentException.class) - public void testInvalidProductName(){ - + + @Test(expected = IllegalArgumentException.class) + public void testInvalidProductName() { + fromProductName("bad product name"); } - + @Test - public void testFromMetaDataForDerby() throws Exception{ - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - DataSource ds = createMock(DataSource.class); - Connection con = createMock(Connection.class); - expect(ds.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("Apache Derby"); - replay(dmd,ds,con); - + public void testFromMetaDataForDerby() throws Exception { + DataSource ds = DatabaseTypeTestUtils.getMockDataSource("Apache Derby"); + replay(ds); assertEquals(DERBY, DatabaseType.fromMetaData(ds)); - - verify(dmd,ds,con); + verify(ds); } @Test - public void testFromMetaDataForDB2() throws Exception{ - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - DataSource ds = createMock(DataSource.class); - Connection con = createMock(Connection.class); - expect(ds.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("DB2/Linux"); - replay(dmd,ds,con); - + public void testFromMetaDataForDB2() throws Exception { + DataSource ds = DatabaseTypeTestUtils.getMockDataSource("DB2/Linux"); + replay(ds); assertEquals(DB2, DatabaseType.fromMetaData(ds)); - - verify(dmd,ds,con); + verify(ds); } @Test - public void testFromMetaDataForDB2ZOS() throws Exception{ - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - DataSource ds = createMock(DataSource.class); - Connection con = createMock(Connection.class); - expect(ds.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("DB2"); - expect(ds.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductVersion()).andReturn("DSN08015"); - replay(dmd,ds,con); - + public void testFromMetaDataForDB2ZOS() throws Exception { + DataSource ds = DatabaseTypeTestUtils.getMockDataSource("DB2", "DSN08015"); + replay(ds); assertEquals(DB2ZOS, DatabaseType.fromMetaData(ds)); - - verify(dmd,ds,con); + verify(ds); } @Test - public void testFromMetaDataForHsql() throws Exception{ - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - DataSource ds = createMock(DataSource.class); - Connection con = createMock(Connection.class); - expect(ds.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("HSQL Database Engine"); - replay(dmd,ds,con); - + public void testFromMetaDataForHsql() throws Exception { + DataSource ds = DatabaseTypeTestUtils.getMockDataSource("HSQL Database Engine"); + replay(ds); assertEquals(HSQL, DatabaseType.fromMetaData(ds)); - - verify(dmd,ds,con); + verify(ds); } @Test - public void testFromMetaDataForSqlServer() throws Exception{ - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - DataSource ds = createMock(DataSource.class); - Connection con = createMock(Connection.class); - expect(ds.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("Microsoft SQL Server"); - replay(dmd,ds,con); - + public void testFromMetaDataForSqlServer() throws Exception { + DataSource ds = DatabaseTypeTestUtils.getMockDataSource("Microsoft SQL Server"); + replay(ds); assertEquals(SQLSERVER, DatabaseType.fromMetaData(ds)); - - verify(dmd,ds,con); + verify(ds); } @Test - public void testFromMetaDataForMySql() throws Exception{ - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - DataSource ds = createMock(DataSource.class); - Connection con = createMock(Connection.class); - expect(ds.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("MySQL"); - replay(dmd,ds,con); - + public void testFromMetaDataForMySql() throws Exception { + DataSource ds = DatabaseTypeTestUtils.getMockDataSource("MySQL"); + replay(ds); assertEquals(MYSQL, DatabaseType.fromMetaData(ds)); - - verify(dmd,ds,con); + verify(ds); } @Test - public void testFromMetaDataForOracle() throws Exception{ - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - DataSource ds = createMock(DataSource.class); - Connection con = createMock(Connection.class); - expect(ds.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("Oracle"); - replay(dmd,ds,con); - + public void testFromMetaDataForOracle() throws Exception { + DataSource ds = DatabaseTypeTestUtils.getMockDataSource("Oracle"); + replay(ds); assertEquals(ORACLE, DatabaseType.fromMetaData(ds)); - - verify(dmd,ds,con); + verify(ds); } @Test - public void testFromMetaDataForPostgres() throws Exception{ - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - DataSource ds = createMock(DataSource.class); - Connection con = createMock(Connection.class); - expect(ds.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("PostgreSQL"); - replay(dmd,ds,con); - + public void testFromMetaDataForPostgres() throws Exception { + DataSource ds = DatabaseTypeTestUtils.getMockDataSource("PostgreSQL"); + replay(ds); assertEquals(POSTGRES, DatabaseType.fromMetaData(ds)); - - verify(dmd,ds,con); + verify(ds); } @Test - public void testFromMetaDataForSybase() throws Exception{ - - DatabaseMetaData dmd = createMock(DatabaseMetaData.class); - DataSource ds = createMock(DataSource.class); - Connection con = createMock(Connection.class); - expect(ds.getConnection()).andReturn(con); - expect(con.getMetaData()).andReturn(dmd); - expect(dmd.getDatabaseProductName()).andReturn("Adaptive Server Enterprise"); - replay(dmd,ds,con); - + public void testFromMetaDataForSybase() throws Exception { + DataSource ds = DatabaseTypeTestUtils.getMockDataSource("Adaptive Server Enterprise"); + replay(ds); assertEquals(SYBASE, DatabaseType.fromMetaData(ds)); - - verify(dmd,ds,con); + verify(ds); } + + @Test(expected=MetaDataAccessException.class) + public void testBadMetaData() throws Exception { + DataSource ds = DatabaseTypeTestUtils.getMockDataSource(new MetaDataAccessException("Bad!")); + replay(ds); + assertEquals(SYBASE, DatabaseType.fromMetaData(ds)); + verify(ds); + } + }