001/*
002 * Copyright 2002-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.jdbc.object;
018
019import java.sql.ResultSet;
020import java.sql.SQLException;
021import javax.sql.DataSource;
022
023import org.springframework.dao.TypeMismatchDataAccessException;
024import org.springframework.jdbc.core.SingleColumnRowMapper;
025
026/**
027 * SQL "function" wrapper for a query that returns a single row of results.
028 * The default behavior is to return an int, but that can be overridden by
029 * using the constructor with an extra return type parameter.
030 *
031 * <p>Intended to use to call SQL functions that return a single result using a
032 * query like "select user()" or "select sysdate from dual". It is not intended
033 * for calling more complex stored functions or for using a CallableStatement to
034 * invoke a stored procedure or stored function. Use StoredProcedure or SqlCall
035 * for this type of processing.
036 *
037 * <p>This is a concrete class, which there is often no need to subclass.
038 * Code using this package can create an object of this type, declaring SQL
039 * and parameters, and then invoke the appropriate {@code run} method
040 * repeatedly to execute the function. Subclasses are only supposed to add
041 * specialized {@code run} methods for specific parameter and return types.
042 *
043 * <p>Like all RdbmsOperation objects, SqlFunction objects are thread-safe.
044 *
045 * @author Rod Johnson
046 * @author Juergen Hoeller
047 * @author Jean-Pierre Pawlak
048 * @see StoredProcedure
049 */
050public class SqlFunction<T> extends MappingSqlQuery<T> {
051
052        private final SingleColumnRowMapper<T> rowMapper = new SingleColumnRowMapper<T>();
053
054
055        /**
056         * Constructor to allow use as a JavaBean.
057         * A DataSource, SQL and any parameters must be supplied before
058         * invoking the {@code compile} method and using this object.
059         * @see #setDataSource
060         * @see #setSql
061         * @see #compile
062         */
063        public SqlFunction() {
064                setRowsExpected(1);
065        }
066
067        /**
068         * Create a new SqlFunction object with SQL, but without parameters.
069         * Must add parameters or settle with none.
070         * @param ds DataSource to obtain connections from
071         * @param sql SQL to execute
072         */
073        public SqlFunction(DataSource ds, String sql) {
074                setRowsExpected(1);
075                setDataSource(ds);
076                setSql(sql);
077        }
078
079        /**
080         * Create a new SqlFunction object with SQL and parameters.
081         * @param ds DataSource to obtain connections from
082         * @param sql SQL to execute
083         * @param types SQL types of the parameters, as defined in the
084         * {@code java.sql.Types} class
085         * @see java.sql.Types
086         */
087        public SqlFunction(DataSource ds, String sql, int[] types) {
088                setRowsExpected(1);
089                setDataSource(ds);
090                setSql(sql);
091                setTypes(types);
092        }
093
094        /**
095         * Create a new SqlFunction object with SQL, parameters and a result type.
096         * @param ds DataSource to obtain connections from
097         * @param sql SQL to execute
098         * @param types SQL types of the parameters, as defined in the
099         * {@code java.sql.Types} class
100         * @param resultType the type that the result object is required to match
101         * @see #setResultType(Class)
102         * @see java.sql.Types
103         */
104        public SqlFunction(DataSource ds, String sql, int[] types, Class<T> resultType) {
105                setRowsExpected(1);
106                setDataSource(ds);
107                setSql(sql);
108                setTypes(types);
109                setResultType(resultType);
110        }
111
112
113        /**
114         * Specify the type that the result object is required to match.
115         * <p>If not specified, the result value will be exposed as
116         * returned by the JDBC driver.
117         */
118        public void setResultType(Class<T> resultType) {
119                this.rowMapper.setRequiredType(resultType);
120        }
121
122
123        /**
124         * This implementation of this method extracts a single value from the
125         * single row returned by the function. If there are a different number
126         * of rows returned, this is treated as an error.
127         */
128        @Override
129        protected T mapRow(ResultSet rs, int rowNum) throws SQLException {
130                return this.rowMapper.mapRow(rs, rowNum);
131        }
132
133
134        /**
135         * Convenient method to run the function without arguments.
136         * @return the value of the function
137         */
138        public int run() {
139                return run(new Object[0]);
140        }
141
142        /**
143         * Convenient method to run the function with a single int argument.
144         * @param parameter single int parameter
145         * @return the value of the function
146         */
147        public int run(int parameter) {
148                return run(new Object[] {parameter});
149        }
150
151        /**
152         * Analogous to the SqlQuery.execute([]) method. This is a
153         * generic method to execute a query, taken a number of arguments.
154         * @param parameters array of parameters. These will be objects or
155         * object wrapper types for primitives.
156         * @return the value of the function
157         */
158        public int run(Object... parameters) {
159                Object obj = super.findObject(parameters);
160                if (!(obj instanceof Number)) {
161                        throw new TypeMismatchDataAccessException("Couldn't convert result object [" + obj + "] to int");
162                }
163                return ((Number) obj).intValue();
164        }
165
166        /**
167         * Convenient method to run the function without arguments,
168         * returning the value as an object.
169         * @return the value of the function
170         */
171        public Object runGeneric() {
172                return findObject((Object[]) null);
173        }
174
175        /**
176         * Convenient method to run the function with a single int argument.
177         * @param parameter single int parameter
178         * @return the value of the function as an Object
179         */
180        public Object runGeneric(int parameter) {
181                return findObject(parameter);
182        }
183
184        /**
185         * Analogous to the {@code SqlQuery.findObject(Object[])} method.
186         * This is a generic method to execute a query, taken a number of arguments.
187         * @param parameters array of parameters. These will be objects or
188         * object wrapper types for primitives.
189         * @return the value of the function, as an Object
190         * @see #execute(Object[])
191         */
192        public Object runGeneric(Object[] parameters) {
193                return findObject(parameters);
194        }
195
196}