001/*
002 * Copyright 2006-2012 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.springframework.batch.item.database.support;
017
018import static org.springframework.batch.support.DatabaseType.DB2;
019import static org.springframework.batch.support.DatabaseType.DB2VSE;
020import static org.springframework.batch.support.DatabaseType.DB2ZOS;
021import static org.springframework.batch.support.DatabaseType.DB2AS400;
022import static org.springframework.batch.support.DatabaseType.DERBY;
023import static org.springframework.batch.support.DatabaseType.H2;
024import static org.springframework.batch.support.DatabaseType.HSQL;
025import static org.springframework.batch.support.DatabaseType.MYSQL;
026import static org.springframework.batch.support.DatabaseType.ORACLE;
027import static org.springframework.batch.support.DatabaseType.POSTGRES;
028import static org.springframework.batch.support.DatabaseType.SQLITE;
029import static org.springframework.batch.support.DatabaseType.SQLSERVER;
030import static org.springframework.batch.support.DatabaseType.SYBASE;
031
032import java.util.HashMap;
033import java.util.LinkedHashMap;
034import java.util.Map;
035
036import javax.sql.DataSource;
037
038import org.springframework.batch.item.database.Order;
039import org.springframework.batch.item.database.PagingQueryProvider;
040import org.springframework.batch.support.DatabaseType;
041import org.springframework.beans.factory.FactoryBean;
042import org.springframework.jdbc.support.MetaDataAccessException;
043import org.springframework.util.Assert;
044import org.springframework.util.StringUtils;
045
046/**
047 * Factory bean for {@link PagingQueryProvider} interface. The database type
048 * will be determined from the data source if not provided explicitly. Valid
049 * types are given by the {@link DatabaseType} enum.
050 * 
051 * @author Dave Syer
052 * @author Michael Minella
053 */
054public class SqlPagingQueryProviderFactoryBean implements FactoryBean<PagingQueryProvider> {
055
056        private DataSource dataSource;
057
058        private String databaseType;
059
060        private String fromClause;
061
062        private String whereClause;
063
064        private String selectClause;
065        
066        private String groupClause;
067
068        private Map<String, Order> sortKeys;
069
070        private Map<DatabaseType, AbstractSqlPagingQueryProvider> providers = new HashMap<DatabaseType, AbstractSqlPagingQueryProvider>();
071
072
073        {
074                providers.put(DB2, new Db2PagingQueryProvider());
075                providers.put(DB2VSE, new Db2PagingQueryProvider());
076                providers.put(DB2ZOS, new Db2PagingQueryProvider());
077                providers.put(DB2AS400, new Db2PagingQueryProvider());
078                providers.put(DERBY,new DerbyPagingQueryProvider());
079                providers.put(HSQL,new HsqlPagingQueryProvider());
080                providers.put(H2,new H2PagingQueryProvider());
081                providers.put(MYSQL,new MySqlPagingQueryProvider());
082                providers.put(ORACLE,new OraclePagingQueryProvider());
083                providers.put(POSTGRES,new PostgresPagingQueryProvider());
084                providers.put(SQLITE, new SqlitePagingQueryProvider());
085                providers.put(SQLSERVER,new SqlServerPagingQueryProvider());
086                providers.put(SYBASE,new SybasePagingQueryProvider());
087        }
088        
089        /**
090         * @param groupClause SQL GROUP BY clause part of the SQL query string
091         */
092        public void setGroupClause(String groupClause) {
093                this.groupClause = groupClause;
094        }
095
096        /**
097         * @param databaseType the databaseType to set
098         */
099        public void setDatabaseType(String databaseType) {
100                this.databaseType = databaseType;
101        }
102
103        /**
104         * @param dataSource the dataSource to set
105         */
106        public void setDataSource(DataSource dataSource) {
107                this.dataSource = dataSource;
108        }
109
110        /**
111         * @param fromClause the fromClause to set
112         */
113        public void setFromClause(String fromClause) {
114                this.fromClause = fromClause;
115        }
116
117        /**
118         * @param whereClause the whereClause to set
119         */
120        public void setWhereClause(String whereClause) {
121                this.whereClause = whereClause;
122        }
123
124        /**
125         * @param selectClause the selectClause to set
126         */
127        public void setSelectClause(String selectClause) {
128                this.selectClause = selectClause;
129        }
130
131        /**
132         * @param sortKeys the sortKeys to set
133         */
134        public void setSortKeys(Map<String, Order> sortKeys) {
135                this.sortKeys = sortKeys;
136        }
137        
138        public void setSortKey(String key) {
139                Assert.doesNotContain(key, ",", "String setter is valid for a single ASC key only");
140                
141                Map<String, Order> keys = new LinkedHashMap<String, Order>();
142                keys.put(key, Order.ASCENDING);
143                
144                this.sortKeys = keys;
145        }
146
147        /**
148         * Get a {@link PagingQueryProvider} instance using the provided properties
149         * and appropriate for the given database type.
150         * 
151         * @see FactoryBean#getObject()
152         */
153    @Override
154        public PagingQueryProvider getObject() throws Exception {
155
156                DatabaseType type;
157                try {
158                        type = databaseType != null ? DatabaseType.valueOf(databaseType.toUpperCase()) : DatabaseType
159                                        .fromMetaData(dataSource);
160                }
161                catch (MetaDataAccessException e) {
162                        throw new IllegalArgumentException(
163                                        "Could not inspect meta data for database type.  You have to supply it explicitly.", e);
164                }
165
166                AbstractSqlPagingQueryProvider provider = providers.get(type);
167                Assert.state(provider!=null, "Should not happen: missing PagingQueryProvider for DatabaseType="+type);
168
169                provider.setFromClause(fromClause);
170                provider.setWhereClause(whereClause);
171                provider.setSortKeys(sortKeys);
172                if (StringUtils.hasText(selectClause)) {
173                        provider.setSelectClause(selectClause);
174                }
175                if(StringUtils.hasText(groupClause)) {
176                        provider.setGroupClause(groupClause);
177                }
178
179                provider.init(dataSource);
180
181                return provider;
182
183        }
184
185        /**
186         * Always returns {@link PagingQueryProvider}.
187         * 
188         * @see FactoryBean#getObjectType()
189         */
190    @Override
191        public Class<PagingQueryProvider> getObjectType() {
192                return PagingQueryProvider.class;
193        }
194
195        /**
196         * Always returns true.
197         * @see FactoryBean#isSingleton()
198         */
199    @Override
200        public boolean isSingleton() {
201                return true;
202        }
203
204}