001/*
002 * Copyright 2002-2018 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.jdbc.core.metadata;
018
019import java.sql.DatabaseMetaData;
020import java.sql.SQLException;
021import java.util.Arrays;
022import java.util.List;
023import javax.sql.DataSource;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027
028import org.springframework.dao.DataAccessResourceFailureException;
029import org.springframework.jdbc.support.DatabaseMetaDataCallback;
030import org.springframework.jdbc.support.JdbcUtils;
031import org.springframework.jdbc.support.MetaDataAccessException;
032
033/**
034 * Factory used to create a {@link CallMetaDataProvider} implementation
035 * based on the type of database being used.
036 *
037 * @author Thomas Risberg
038 * @author Juergen Hoeller
039 * @since 2.5
040 */
041public class CallMetaDataProviderFactory {
042
043        /** List of supported database products for procedure calls */
044        public static final List<String> supportedDatabaseProductsForProcedures = Arrays.asList(
045                        "Apache Derby",
046                        "DB2",
047                        "MySQL",
048                        "Microsoft SQL Server",
049                        "Oracle",
050                        "PostgreSQL",
051                        "Sybase"
052                );
053
054        /** List of supported database products for function calls */
055        public static final List<String> supportedDatabaseProductsForFunctions = Arrays.asList(
056                        "MySQL",
057                        "Microsoft SQL Server",
058                        "Oracle",
059                        "PostgreSQL"
060                );
061
062        private static final Log logger = LogFactory.getLog(CallMetaDataProviderFactory.class);
063
064
065        /**
066         * Create a {@link CallMetaDataProvider} based on the database meta-data.
067         * @param dataSource the JDBC DataSource to use for retrieving meta-data
068         * @param context the class that holds configuration and meta-data
069         * @return instance of the CallMetaDataProvider implementation to be used
070         */
071        public static CallMetaDataProvider createMetaDataProvider(DataSource dataSource, final CallMetaDataContext context) {
072                try {
073                        return (CallMetaDataProvider) JdbcUtils.extractDatabaseMetaData(dataSource, new DatabaseMetaDataCallback() {
074                                @Override
075                                public Object processMetaData(DatabaseMetaData databaseMetaData) throws SQLException, MetaDataAccessException {
076                                        String databaseProductName = JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName());
077                                        boolean accessProcedureColumnMetaData = context.isAccessCallParameterMetaData();
078                                        if (context.isFunction()) {
079                                                if (!supportedDatabaseProductsForFunctions.contains(databaseProductName)) {
080                                                        if (logger.isWarnEnabled()) {
081                                                                logger.warn(databaseProductName + " is not one of the databases fully supported for function calls " +
082                                                                                "-- supported are: " + supportedDatabaseProductsForFunctions);
083                                                        }
084                                                        if (accessProcedureColumnMetaData) {
085                                                                logger.warn("Metadata processing disabled - you must specify all parameters explicitly");
086                                                                accessProcedureColumnMetaData = false;
087                                                        }
088                                                }
089                                        }
090                                        else {
091                                                if (!supportedDatabaseProductsForProcedures.contains(databaseProductName)) {
092                                                        if (logger.isWarnEnabled()) {
093                                                                logger.warn(databaseProductName + " is not one of the databases fully supported for procedure calls " +
094                                                                                "-- supported are: " + supportedDatabaseProductsForProcedures);
095                                                        }
096                                                        if (accessProcedureColumnMetaData) {
097                                                                logger.warn("Metadata processing disabled - you must specify all parameters explicitly");
098                                                                accessProcedureColumnMetaData = false;
099                                                        }
100                                                }
101                                        }
102
103                                        CallMetaDataProvider provider;
104                                        if ("Oracle".equals(databaseProductName)) {
105                                                provider = new OracleCallMetaDataProvider(databaseMetaData);
106                                        }
107                                        else if ("PostgreSQL".equals(databaseProductName)) {
108                                                provider = new PostgresCallMetaDataProvider((databaseMetaData));
109                                        }
110                                        else if ("Apache Derby".equals(databaseProductName)) {
111                                                provider = new DerbyCallMetaDataProvider((databaseMetaData));
112                                        }
113                                        else if ("DB2".equals(databaseProductName)) {
114                                                provider = new Db2CallMetaDataProvider((databaseMetaData));
115                                        }
116                                        else if ("HDB".equals(databaseProductName)) {
117                                                provider = new HanaCallMetaDataProvider((databaseMetaData));
118                                        }
119                                        else if ("Microsoft SQL Server".equals(databaseProductName)) {
120                                                provider = new SqlServerCallMetaDataProvider((databaseMetaData));
121                                        }
122                                        else if ("Sybase".equals(databaseProductName)) {
123                                                provider = new SybaseCallMetaDataProvider((databaseMetaData));
124                                        }
125                                        else {
126                                                provider = new GenericCallMetaDataProvider(databaseMetaData);
127                                        }
128
129                                        if (logger.isDebugEnabled()) {
130                                                logger.debug("Using " + provider.getClass().getName());
131                                        }
132                                        provider.initializeWithMetaData(databaseMetaData);
133                                        if (accessProcedureColumnMetaData) {
134                                                provider.initializeWithProcedureColumnMetaData(databaseMetaData,
135                                                                context.getCatalogName(), context.getSchemaName(), context.getProcedureName());
136                                        }
137                                        return provider;
138                                }
139                        });
140                }
141                catch (MetaDataAccessException ex) {
142                        throw new DataAccessResourceFailureException("Error retrieving database meta-data", ex);
143                }
144        }
145
146}