RESOLVED - issue BATCH-986: Provide factory bean for SqlPagingQueryProvider

This commit is contained in:
dsyer
2009-01-02 14:10:11 +00:00
parent 63023c0f87
commit aee5ca3b85
10 changed files with 441 additions and 297 deletions

View File

@@ -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.

View File

@@ -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:
*
* <ul>
* <li>db2</li>
* <li>db2zos</li>
* <li>derby</li>
* <li>hsql</li>
* <li>mysql</li>
* <li>oracle</li>
* <li>postgres</li>
* <li>sqlserver</li>
* <li>sybase</li>
* </ul>
* 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<String> types = new ArrayList<String>();
for(DatabaseType type : DatabaseType.values()){
for (DatabaseType type : DatabaseType.values()) {
types.add(type.name());
}

View File

@@ -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<DatabaseType, AbstractSqlPagingQueryProvider> providers = new HashMap<DatabaseType, AbstractSqlPagingQueryProvider>();
{
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<PagingQueryProvider> getObjectType() {
return PagingQueryProvider.class;
}
/**
* Always returns true.
* @see FactoryBean#isSingleton()
*/
public boolean isSingleton() {
return true;
}
}

View File

@@ -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)) {

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}