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