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