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}