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.object;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import javax.sql.DataSource;
023
024import org.springframework.dao.DataAccessException;
025import org.springframework.dao.InvalidDataAccessApiUsageException;
026import org.springframework.jdbc.core.JdbcTemplate;
027import org.springframework.jdbc.core.ParameterMapper;
028import org.springframework.jdbc.core.SqlParameter;
029
030/**
031 * Superclass for object abstractions of RDBMS stored procedures.
032 * This class is abstract and it is intended that subclasses will provide a typed
033 * method for invocation that delegates to the supplied {@link #execute} method.
034 *
035 * <p>The inherited {@link #setSql sql} property is the name of the stored procedure
036 * in the RDBMS.
037 *
038 * @author Rod Johnson
039 * @author Thomas Risberg
040 */
041public abstract class StoredProcedure extends SqlCall {
042
043        /**
044         * Allow use as a bean.
045         */
046        protected StoredProcedure() {
047        }
048
049        /**
050         * Create a new object wrapper for a stored procedure.
051         * @param ds the DataSource to use throughout the lifetime
052         * of this object to obtain connections
053         * @param name the name of the stored procedure in the database
054         */
055        protected StoredProcedure(DataSource ds, String name) {
056                setDataSource(ds);
057                setSql(name);
058        }
059
060        /**
061         * Create a new object wrapper for a stored procedure.
062         * @param jdbcTemplate the JdbcTemplate which wraps DataSource
063         * @param name the name of the stored procedure in the database
064         */
065        protected StoredProcedure(JdbcTemplate jdbcTemplate, String name) {
066                setJdbcTemplate(jdbcTemplate);
067                setSql(name);
068        }
069
070
071        /**
072         * StoredProcedure parameter Maps are by default allowed to contain
073         * additional entries that are not actually used as parameters.
074         */
075        @Override
076        protected boolean allowsUnusedParameters() {
077                return true;
078        }
079
080        /**
081         * Declare a parameter.
082         * <p>Parameters declared as {@code SqlParameter} and {@code SqlInOutParameter}
083         * will always be used to provide input values. In addition to this, any parameter declared
084         * as {@code SqlOutParameter} where a non-null input value is provided will also be used
085         * as an input parameter.
086         * <b>Note: Calls to declareParameter must be made in the same order as
087         * they appear in the database's stored procedure parameter list.</b>
088         * <p>Names are purely used to help mapping.
089         * @param param the parameter object
090         */
091        @Override
092        public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {
093                if (param.getName() == null) {
094                        throw new InvalidDataAccessApiUsageException("Parameters to stored procedures must have names as well as types");
095                }
096                super.declareParameter(param);
097        }
098
099        /**
100         * Execute the stored procedure with the provided parameter values. This is
101         * a convenience method where the order of the passed in parameter values
102         * must match the order that the parameters where declared in.
103         * @param inParams variable number of input parameters. Output parameters should
104         * not be included in this map. It is legal for values to be {@code null}, and this
105         * will produce the correct behavior using a NULL argument to the stored procedure.
106         * @return map of output params, keyed by name as in parameter declarations.
107         * Output parameters will appear here, with their values after the stored procedure
108         * has been called.
109         */
110        public Map<String, Object> execute(Object... inParams) {
111                Map<String, Object> paramsToUse = new HashMap<>();
112                validateParameters(inParams);
113                int i = 0;
114                for (SqlParameter sqlParameter : getDeclaredParameters()) {
115                        if (sqlParameter.isInputValueProvided() && i < inParams.length) {
116                                paramsToUse.put(sqlParameter.getName(), inParams[i++]);
117                        }
118                }
119                return getJdbcTemplate().call(newCallableStatementCreator(paramsToUse), getDeclaredParameters());
120        }
121
122        /**
123         * Execute the stored procedure. Subclasses should define a strongly typed
124         * execute method (with a meaningful name) that invokes this method, populating
125         * the input map and extracting typed values from the output map. Subclass
126         * execute methods will often take domain objects as arguments and return values.
127         * Alternatively, they can return void.
128         * @param inParams map of input parameters, keyed by name as in parameter
129         * declarations. Output parameters need not (but can) be included in this map.
130         * It is legal for map entries to be {@code null}, and this will produce the
131         * correct behavior using a NULL argument to the stored procedure.
132         * @return map of output params, keyed by name as in parameter declarations.
133         * Output parameters will appear here, with their values after the
134         * stored procedure has been called.
135         */
136        public Map<String, Object> execute(Map<String, ?> inParams) throws DataAccessException {
137                validateParameters(inParams.values().toArray());
138                return getJdbcTemplate().call(newCallableStatementCreator(inParams), getDeclaredParameters());
139        }
140
141        /**
142         * Execute the stored procedure. Subclasses should define a strongly typed
143         * execute method (with a meaningful name) that invokes this method, passing in
144         * a ParameterMapper that will populate the input map.  This allows mapping database
145         * specific features since the ParameterMapper has access to the Connection object.
146         * The execute method is also responsible for extracting typed values from the output map.
147         * Subclass execute methods will often take domain objects as arguments and return values.
148         * Alternatively, they can return void.
149         * @param inParamMapper map of input parameters, keyed by name as in parameter
150         * declarations. Output parameters need not (but can) be included in this map.
151         * It is legal for map entries to be {@code null}, and this will produce the correct
152         * behavior using a NULL argument to the stored procedure.
153         * @return map of output params, keyed by name as in parameter declarations.
154         * Output parameters will appear here, with their values after the
155         * stored procedure has been called.
156         */
157        public Map<String, Object> execute(ParameterMapper inParamMapper) throws DataAccessException {
158                checkCompiled();
159                return getJdbcTemplate().call(newCallableStatementCreator(inParamMapper), getDeclaredParameters());
160        }
161
162}