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