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.List; 020import java.util.Map; 021import javax.sql.DataSource; 022 023import org.springframework.jdbc.core.CallableStatementCreator; 024import org.springframework.jdbc.core.CallableStatementCreatorFactory; 025import org.springframework.jdbc.core.ParameterMapper; 026import org.springframework.jdbc.core.SqlParameter; 027 028/** 029 * RdbmsOperation using a JdbcTemplate and representing a SQL-based 030 * call such as a stored procedure or a stored function. 031 * 032 * <p>Configures a CallableStatementCreatorFactory based on the declared 033 * parameters. 034 * 035 * @author Rod Johnson 036 * @author Thomas Risberg 037 * @see CallableStatementCreatorFactory 038 */ 039public abstract class SqlCall extends RdbmsOperation { 040 041 /** 042 * Flag used to indicate that this call is for a function and to 043 * use the {? = call get_invoice_count(?)} syntax. 044 */ 045 private boolean function = false; 046 047 /** 048 * Flag used to indicate that the sql for this call should be used exactly as 049 * it is defined. No need to add the escape syntax and parameter place holders. 050 */ 051 private boolean sqlReadyForUse = false; 052 053 /** 054 * Call string as defined in java.sql.CallableStatement. 055 * String of form {call add_invoice(?, ?, ?)} or {? = call get_invoice_count(?)} 056 * if isFunction is set to true. Updated after each parameter is added. 057 */ 058 private String callString; 059 060 /** 061 * Object enabling us to create CallableStatementCreators 062 * efficiently, based on this class's declared parameters. 063 */ 064 private CallableStatementCreatorFactory callableStatementFactory; 065 066 067 /** 068 * Constructor to allow use as a JavaBean. 069 * A DataSource, SQL and any parameters must be supplied before 070 * invoking the {@code compile} method and using this object. 071 * @see #setDataSource 072 * @see #setSql 073 * @see #compile 074 */ 075 public SqlCall() { 076 } 077 078 /** 079 * Create a new SqlCall object with SQL, but without parameters. 080 * Must add parameters or settle with none. 081 * @param ds the DataSource to obtain connections from 082 * @param sql the SQL to execute 083 */ 084 public SqlCall(DataSource ds, String sql) { 085 setDataSource(ds); 086 setSql(sql); 087 } 088 089 090 /** 091 * Set whether this call is for a function. 092 */ 093 public void setFunction(boolean function) { 094 this.function = function; 095 } 096 097 /** 098 * Return whether this call is for a function. 099 */ 100 public boolean isFunction() { 101 return this.function; 102 } 103 104 /** 105 * Set whether the SQL can be used as is. 106 */ 107 public void setSqlReadyForUse(boolean sqlReadyForUse) { 108 this.sqlReadyForUse = sqlReadyForUse; 109 } 110 111 /** 112 * Return whether the SQL can be used as is. 113 */ 114 public boolean isSqlReadyForUse() { 115 return this.sqlReadyForUse; 116 } 117 118 119 /** 120 * Overridden method to configure the CallableStatementCreatorFactory 121 * based on our declared parameters. 122 * @see RdbmsOperation#compileInternal() 123 */ 124 @Override 125 protected final void compileInternal() { 126 if (isSqlReadyForUse()) { 127 this.callString = getSql(); 128 } 129 else { 130 List<SqlParameter> parameters = getDeclaredParameters(); 131 int parameterCount = 0; 132 if (isFunction()) { 133 this.callString = "{? = call " + getSql() + "("; 134 parameterCount = -1; 135 } 136 else { 137 this.callString = "{call " + getSql() + "("; 138 } 139 for (SqlParameter parameter : parameters) { 140 if (!(parameter.isResultsParameter())) { 141 if (parameterCount > 0) { 142 this.callString += ", "; 143 } 144 if (parameterCount >= 0) { 145 this.callString += "?"; 146 } 147 parameterCount++; 148 } 149 } 150 this.callString += ")}"; 151 } 152 if (logger.isDebugEnabled()) { 153 logger.debug("Compiled stored procedure. Call string is [" + getCallString() + "]"); 154 } 155 156 this.callableStatementFactory = new CallableStatementCreatorFactory(getCallString(), getDeclaredParameters()); 157 this.callableStatementFactory.setResultSetType(getResultSetType()); 158 this.callableStatementFactory.setUpdatableResults(isUpdatableResults()); 159 this.callableStatementFactory.setNativeJdbcExtractor(getJdbcTemplate().getNativeJdbcExtractor()); 160 161 onCompileInternal(); 162 } 163 164 /** 165 * Hook method that subclasses may override to react to compilation. 166 * This implementation does nothing. 167 */ 168 protected void onCompileInternal() { 169 } 170 171 /** 172 * Get the call string. 173 */ 174 public String getCallString() { 175 return this.callString; 176 } 177 178 /** 179 * Return a CallableStatementCreator to perform an operation 180 * with this parameters. 181 * @param inParams parameters. May be {@code null}. 182 */ 183 protected CallableStatementCreator newCallableStatementCreator(Map<String, ?> inParams) { 184 return this.callableStatementFactory.newCallableStatementCreator(inParams); 185 } 186 187 /** 188 * Return a CallableStatementCreator to perform an operation 189 * with the parameters returned from this ParameterMapper. 190 * @param inParamMapper parametermapper. May not be {@code null}. 191 */ 192 protected CallableStatementCreator newCallableStatementCreator(ParameterMapper inParamMapper) { 193 return this.callableStatementFactory.newCallableStatementCreator(inParamMapper); 194 } 195 196}