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 */
016
017package org.springframework.batch.item.database.support;
018
019import javax.sql.DataSource;
020
021import org.springframework.batch.item.database.PagingQueryProvider;
022import org.springframework.dao.InvalidDataAccessResourceUsageException;
023import org.springframework.jdbc.support.JdbcUtils;
024
025/**
026 * Derby implementation of a  {@link PagingQueryProvider} using standard SQL:2003 windowing functions.
027 * These features are supported starting with Apache Derby version 10.4.1.3.
028 *
029 * As the OVER() function does not support the ORDER BY clause a sub query is instead used to order the results
030 * before the ROW_NUM restriction is applied
031 *
032 * @author Thomas Risberg
033 * @author David Thexton
034 * @author Michael Minella
035 * @since 2.0
036 */
037public class DerbyPagingQueryProvider extends SqlWindowingPagingQueryProvider {
038        
039        private static final String MINIMAL_DERBY_VERSION = "10.4.1.3";
040
041        @Override
042        public void init(DataSource dataSource) throws Exception {
043                super.init(dataSource);
044                String version = JdbcUtils.extractDatabaseMetaData(dataSource, "getDatabaseProductVersion").toString();
045                if (!isDerbyVersionSupported(version)) {
046                        throw new InvalidDataAccessResourceUsageException("Apache Derby version " + version + " is not supported by this class,  Only version " + MINIMAL_DERBY_VERSION + " or later is supported");
047                }
048        }
049        
050        // derby version numbering is M.m.f.p [ {alpha|beta} ] see https://db.apache.org/derby/papers/versionupgrade.html#Basic+Numbering+Scheme
051        private boolean isDerbyVersionSupported(String version) {
052                String[] minimalVersionParts = MINIMAL_DERBY_VERSION.split("\\.");
053                String[] versionParts = version.split("[\\. ]");
054                for (int i = 0; i < minimalVersionParts.length; i++) {
055                        int minimalVersionPart = Integer.valueOf(minimalVersionParts[i]);
056                        int versionPart = Integer.valueOf(versionParts[i]);
057                        if (versionPart < minimalVersionPart) {
058                                return false;
059                        } else if (versionPart > minimalVersionPart) {
060                                return true;
061                        }
062                }
063                return true; 
064        }
065        
066        @Override
067        protected String getOrderedQueryAlias() {
068                return "TMP_ORDERED";
069        }
070
071        @Override
072        protected String getOverClause() {
073                return "";
074        }
075
076    @Override
077        protected String getOverSubstituteClauseStart() {
078                return " FROM (SELECT " + getSelectClause();
079        }
080
081    @Override
082        protected String getOverSubstituteClauseEnd() {
083                return " ) AS " + getOrderedQueryAlias();
084        }
085
086}