Initial import of JDBC module

This commit is contained in:
Arjen Poutsma 2008-10-23 14:23:13 +00:00
parent 8c87d84728
commit c236f9fac7
216 changed files with 30681 additions and 0 deletions

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="org.springframework.jdbc">
<property file="${basedir}/../build.properties"/>
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
<import file="${basedir}/../spring-build/standard/default.xml"/>
</project>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
<ivy-module
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
version="1.3">
<info organisation="org.springframework" module="${ant.project.name}">
<license name="Apache 2.0" url="http://www.apache.org/licenses/LICENSE-2.0"/>
</info>
<configurations>
<include file="${spring.build.dir}/common/default-ivy-configurations.xml"/>
</configurations>
<publications>
<artifact name="${ant.project.name}"/>
<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
</publications>
<dependencies>
<!-- compile dependencies -->
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1" conf="compile->runtime" />
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration" conf="compile->compile" />
<dependency org="org.springframework" name="org.springframework.beans" rev="latest.integration" conf="compile->compile" />
<dependency org="org.springframework" name="org.springframework.aop" rev="latest.integration" conf="compile->compile" />
<dependency org="org.springframework" name="org.springframework.transaction" rev="latest.integration" conf="compile->compile" />
<!-- optional dependencies -->
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration" conf="optional->compile" />
<dependency org="javax.transaction" name="com.springsource.javax.transaction" rev="1.1.0" conf="optional->compile" />
<dependency org="com.mchange.c3p0" name="com.springsource.com.mchange.v2.c3p0" rev="0.9.1.2" conf="optional->compile" />
<dependency org="com.experlog.xapool" name="com.springsource.org.enhydra.jdbc" rev="1.5.0" conf="optional->compile" />
<!-- test dependencies -->
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.4.0" conf="test->runtime" />
</dependencies>
</ivy-module>

View File

@ -0,0 +1,59 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.core</artifactId>
<packaging>jar</packaging>
<name>Spring Core Abstractions and Utilities</name>
<version>3.0.0.M1</version>
<repositories>
<repository>
<id>com.springsource.repository.bundles.external</id>
<name>SpringSource Enterprise Bundle Repository - External Bundle Releases</name>
<url>http://repository.springsource.com/maven/bundles/external</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>com.springsource.org.apache.commons.logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.log4j</groupId>
<artifactId>com.springsource.org.apache.log4j</artifactId>
<version>1.2.15</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>com.springsource.org.apache.commons.collections</artifactId>
<version>3.2.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.2.RELEASE</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.objectweb.asm</groupId>
<artifactId>com.springsource.org.objectweb.asm.commons</artifactId>
<version>2.2.3</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,65 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc;
import java.sql.SQLException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
/**
* Exception thrown when SQL specified is invalid. Such exceptions always have
* a <code>java.sql.SQLException</code> root cause.
*
* <p>It would be possible to have subclasses for no such table, no such column etc.
* A custom SQLExceptionTranslator could create such more specific exceptions,
* without affecting code using this class.
*
* @author Rod Johnson
* @see InvalidResultSetAccessException
*/
public class BadSqlGrammarException extends InvalidDataAccessResourceUsageException {
private String sql;
/**
* Constructor for BadSqlGrammarException.
* @param task name of current task
* @param sql the offending SQL statement
* @param ex the root cause
*/
public BadSqlGrammarException(String task, String sql, SQLException ex) {
super(task + "; bad SQL grammar [" + sql + "]", ex);
this.sql = sql;
}
/**
* Return the wrapped SQLException.
*/
public SQLException getSQLException() {
return (SQLException) getCause();
}
/**
* Return the SQL that caused the problem.
*/
public String getSql() {
return this.sql;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc;
import java.sql.SQLException;
import org.springframework.dao.DataAccessResourceFailureException;
/**
* Fatal exception thrown when we can't connect to an RDBMS using JDBC.
*
* @author Rod Johnson
*/
public class CannotGetJdbcConnectionException extends DataAccessResourceFailureException {
/**
* Constructor for CannotGetJdbcConnectionException.
* @param msg the detail message
* @param ex SQLException root cause
*/
public CannotGetJdbcConnectionException(String msg, SQLException ex) {
super(msg, ex);
}
/**
* Constructor for CannotGetJdbcConnectionException.
* @param msg the detail message
* @param ex ClassNotFoundException root cause
* @deprecated since Spring 2.5, in favor of throwing an
* IllegalStateException in case of the driver not being found
*/
public CannotGetJdbcConnectionException(String msg, ClassNotFoundException ex) {
super(msg, ex);
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc;
import org.springframework.dao.DataRetrievalFailureException;
/**
* Data access exception thrown when a result set did not have the correct column count,
* for example when expecting a single column but getting 0 or more than 1 columns.
*
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.dao.IncorrectResultSizeDataAccessException
*/
public class IncorrectResultSetColumnCountException extends DataRetrievalFailureException {
private int expectedCount;
private int actualCount;
/**
* Constructor for IncorrectResultSetColumnCountException.
* @param expectedCount the expected column count
* @param actualCount the actual column count
*/
public IncorrectResultSetColumnCountException(int expectedCount, int actualCount) {
super("Incorrect column count: expected " + expectedCount + ", actual " + actualCount);
this.expectedCount = expectedCount;
this.actualCount = actualCount;
}
/**
* Constructor for IncorrectResultCountDataAccessException.
* @param msg the detail message
* @param expectedCount the expected column count
* @param actualCount the actual column count
*/
public IncorrectResultSetColumnCountException(String msg, int expectedCount, int actualCount) {
super(msg);
this.expectedCount = expectedCount;
this.actualCount = actualCount;
}
/**
* Return the expected column count.
*/
public int getExpectedCount() {
return this.expectedCount;
}
/**
* Return the actual column count.
*/
public int getActualCount() {
return this.actualCount;
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc;
import java.sql.SQLException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
/**
* Exception thrown when a ResultSet has been accessed in an invalid fashion.
* Such exceptions always have a <code>java.sql.SQLException</code> root cause.
*
* <p>This typically happens when an invalid ResultSet column index or name
* has been specified. Also thrown by disconnected SqlRowSets.
*
* @author Juergen Hoeller
* @since 1.2
* @see BadSqlGrammarException
* @see org.springframework.jdbc.support.rowset.SqlRowSet
*/
public class InvalidResultSetAccessException extends InvalidDataAccessResourceUsageException {
private String sql;
/**
* Constructor for InvalidResultSetAccessException.
* @param task name of current task
* @param sql the offending SQL statement
* @param ex the root cause
*/
public InvalidResultSetAccessException(String task, String sql, SQLException ex) {
super(task + "; invalid ResultSet access for SQL [" + sql + "]", ex);
this.sql = sql;
}
/**
* Constructor for InvalidResultSetAccessException.
* @param ex the root cause
*/
public InvalidResultSetAccessException(SQLException ex) {
super(ex.getMessage(), ex);
}
/**
* Return the wrapped SQLException.
*/
public SQLException getSQLException() {
return (SQLException) getCause();
}
/**
* Return the SQL that caused the problem.
* @return the offending SQL, if known
*/
public String getSql() {
return this.sql;
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc;
import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException;
/**
* Exception thrown when a JDBC update affects an unexpected number of rows.
* Typically we expect an update to affect a single row, meaning it's an
* error if it affects multiple rows.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public class JdbcUpdateAffectedIncorrectNumberOfRowsException extends IncorrectUpdateSemanticsDataAccessException {
/** Number of rows that should have been affected */
private int expected;
/** Number of rows that actually were affected */
private int actual;
/**
* Constructor for JdbcUpdateAffectedIncorrectNumberOfRowsException.
* @param sql SQL we were tring to execute
* @param expected the expected number of rows affected
* @param actual the actual number of rows affected
*/
public JdbcUpdateAffectedIncorrectNumberOfRowsException(String sql, int expected, int actual) {
super("SQL update '" + sql + "' affected " + actual + " rows, not " + expected + " as expected");
this.expected = expected;
this.actual = actual;
}
/**
* Return the number of rows that should have been affected.
*/
public int getExpectedRowsAffected() {
return this.expected;
}
/**
* Return the number of rows that have actually been affected.
*/
public int getActualRowsAffected() {
return this.actual;
}
public boolean wasDataUpdated() {
return (getActualRowsAffected() > 0);
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc;
import java.io.IOException;
import org.springframework.dao.DataRetrievalFailureException;
/**
* Exception to be thrown when a LOB could not be retrieved.
*
* @author Juergen Hoeller
* @since 1.0.2
*/
public class LobRetrievalFailureException extends DataRetrievalFailureException {
/**
* Constructor for LobRetrievalFailureException.
* @param msg the detail message
*/
public LobRetrievalFailureException(String msg) {
super(msg);
}
/**
* Constructor for LobRetrievalFailureException.
* @param msg the detail message
* @param ex IOException root cause
*/
public LobRetrievalFailureException(String msg, IOException ex) {
super(msg, ex);
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc;
import java.sql.SQLWarning;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Exception thrown when we're not ignoring {@link java.sql.SQLWarning SQLWarnings}.
*
* <p>If a SQLWarning is reported, the operation completed, so we will need
* to explicitly roll it back if we're not happy when looking at the warning.
* We might choose to ignore (and log) the warning, or to wrap and throw it
* in the shape of this SQLWarningException instead.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.jdbc.core.JdbcTemplate#setIgnoreWarnings
*/
public class SQLWarningException extends UncategorizedDataAccessException {
/**
* Constructor for SQLWarningException.
* @param msg the detail message
* @param ex the JDBC warning
*/
public SQLWarningException(String msg, SQLWarning ex) {
super(msg, ex);
}
/**
* Return the underlying SQLWarning.
*/
public SQLWarning SQLWarning() {
return (SQLWarning) getCause();
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc;
import java.sql.SQLException;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Exception thrown when we can't classify a SQLException into
* one of our generic data access exceptions.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public class UncategorizedSQLException extends UncategorizedDataAccessException {
/** SQL that led to the problem */
private final String sql;
/**
* Constructor for UncategorizedSQLException.
* @param task name of current task
* @param sql the offending SQL statement
* @param ex the root cause
*/
public UncategorizedSQLException(String task, String sql, SQLException ex) {
super(task + "; uncategorized SQLException for SQL [" + sql + "]; SQL state [" +
ex.getSQLState() + "]; error code [" + ex.getErrorCode() + "]; " + ex.getMessage(), ex);
this.sql = sql;
}
/**
* Return the underlying SQLException.
*/
public SQLException getSQLException() {
return (SQLException) getCause();
}
/**
* Return the SQL that led to the problem.
*/
public String getSql() {
return this.sql;
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* Simple adapter for PreparedStatementSetter that applies
* a given array of arguments.
*
* @author Juergen Hoeller
*/
class ArgPreparedStatementSetter implements PreparedStatementSetter, ParameterDisposer {
private final Object[] args;
/**
* Create a new ArgPreparedStatementSetter for the given arguments.
* @param args the arguments to set
*/
public ArgPreparedStatementSetter(Object[] args) {
this.args = args;
}
public void setValues(PreparedStatement ps) throws SQLException {
if (this.args != null) {
for (int i = 0; i < this.args.length; i++) {
Object arg = this.args[i];
if (arg instanceof SqlParameterValue) {
SqlParameterValue paramValue = (SqlParameterValue) arg;
StatementCreatorUtils.setParameterValue(ps, i + 1, paramValue, paramValue.getValue());
}
else {
StatementCreatorUtils.setParameterValue(ps, i + 1, SqlTypeValue.TYPE_UNKNOWN, arg);
}
}
}
}
public void cleanupParameters() {
StatementCreatorUtils.cleanupParameters(this.args);
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.dao.InvalidDataAccessApiUsageException;
/**
* Simple adapter for PreparedStatementSetter that applies
* given arrays of arguments and JDBC argument types.
*
* @author Juergen Hoeller
*/
class ArgTypePreparedStatementSetter implements PreparedStatementSetter, ParameterDisposer {
private final Object[] args;
private final int[] argTypes;
/**
* Create a new ArgTypePreparedStatementSetter for the given arguments.
* @param args the arguments to set
* @param argTypes the corresponding SQL types of the arguments
*/
public ArgTypePreparedStatementSetter(Object[] args, int[] argTypes) {
if ((args != null && argTypes == null) || (args == null && argTypes != null) ||
(args != null && args.length != argTypes.length)) {
throw new InvalidDataAccessApiUsageException("args and argTypes parameters must match");
}
this.args = args;
this.argTypes = argTypes;
}
public void setValues(PreparedStatement ps) throws SQLException {
int argIndx = 1;
if (this.args != null) {
for (int i = 0; i < this.args.length; i++) {
Object arg = this.args[i];
if (arg instanceof Collection && this.argTypes[i] != Types.ARRAY) {
Collection entries = (Collection) arg;
for (Iterator it = entries.iterator(); it.hasNext();) {
Object entry = it.next();
if (entry instanceof Object[]) {
Object[] valueArray = ((Object[])entry);
for (int k = 0; k < valueArray.length; k++) {
Object argValue = valueArray[k];
StatementCreatorUtils.setParameterValue(ps, argIndx++, this.argTypes[i], argValue);
}
}
else {
StatementCreatorUtils.setParameterValue(ps, argIndx++, this.argTypes[i], entry);
}
}
}
else {
StatementCreatorUtils.setParameterValue(ps, argIndx++, this.argTypes[i], arg);
}
}
}
}
public void cleanupParameters() {
StatementCreatorUtils.cleanupParameters(this.args);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* Batch update callback interface used by the {@link JdbcTemplate} class.
*
* <p>This interface sets values on a {@link java.sql.PreparedStatement} provided
* by the JdbcTemplate class, for each of a number of updates in a batch using the
* same SQL. Implementations are responsible for setting any necessary parameters.
* SQL with placeholders will already have been supplied.
*
* <p>Implementations <i>do not</i> need to concern themselves with SQLExceptions
* that may be thrown from operations they attempt. The JdbcTemplate class will
* catch and handle SQLExceptions appropriately.
*
* @author Rod Johnson
* @since March 2, 2003
* @see JdbcTemplate#batchUpdate(String, BatchPreparedStatementSetter)
* @see InterruptibleBatchPreparedStatementSetter
*/
public interface BatchPreparedStatementSetter {
/**
* Set parameter values on the given PreparedStatement.
* @param ps the PreparedStatement to invoke setter methods on
* @param i index of the statement we're issuing in the batch, starting from 0
* @throws SQLException if a SQLException is encountered
* (i.e. there is no need to catch SQLException)
*/
void setValues(PreparedStatement ps, int i) throws SQLException;
/**
* Return the size of the batch.
* @return the number of statements in the batch
*/
int getBatchSize();
}

View File

@ -0,0 +1,270 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.beans.PropertyDescriptor;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.util.Assert;
/**
* {@link RowMapper} implementation that converts a row into a new instance
* of the specified mapped target class. The mapped target class must be a
* top-level class and it must have a default or no-arg constructor.
*
* <p>Column values are mapped based on matching the column name as obtained from result set
* metadata to public setters for the corresponding properties. The names are matched either
* directly or by transforming a name separating the parts with underscores to the same name
* using "camel" case.
*
* <p>Mapping is provided for fields in the target class for many common types, e.g.:
* String, boolean, Boolean, byte, Byte, short, Short, int, Integer, long, Long,
* float, Float, double, Double, BigDecimal, <code>java.util.Date</code>, etc.
*
* <p>To facilitate mapping between columns and fields that don't have matching names,
* try using column aliases in the SQL statement like "select fname as first_name from customer".
*
* <p>Please note that this class is designed to provide convenience rather than high performance.
* For best performance consider using a custom RowMapper.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.5
*/
public class BeanPropertyRowMapper implements RowMapper {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
/** The class we are mapping to */
private Class mappedClass;
/** Whether we're strictly validating */
private boolean checkFullyPopulated = false;
/** Map of the fields we provide mapping for */
private Map mappedFields;
/** Set of bean properties we provide mapping for */
private Set mappedProperties;
/**
* Create a new BeanPropertyRowMapper for bean-style configuration.
* @see #setMappedClass
* @see #setCheckFullyPopulated
*/
public BeanPropertyRowMapper() {
}
/**
* Create a new BeanPropertyRowMapper, accepting unpopulated properties
* in the target bean.
* @param mappedClass the class that each row should be mapped to
*/
public BeanPropertyRowMapper(Class mappedClass) {
initialize(mappedClass);
}
/**
* Create a new BeanPropertyRowMapper.
* @param mappedClass the class that each row should be mapped to
* @param checkFullyPopulated whether we're strictly validating that
* all bean properties have been mapped from corresponding database fields
*/
public BeanPropertyRowMapper(Class mappedClass, boolean checkFullyPopulated) {
initialize(mappedClass);
this.checkFullyPopulated = checkFullyPopulated;
}
/**
* Set the class that each row should be mapped to.
*/
public void setMappedClass(Class mappedClass) {
if (this.mappedClass == null) {
initialize(mappedClass);
}
else {
if (!this.mappedClass.equals(mappedClass)) {
throw new InvalidDataAccessApiUsageException("The mapped class can not be reassigned to map to " +
mappedClass + " since it is already providing mapping for " + this.mappedClass);
}
}
}
/**
* Initialize the mapping metadata for the given class.
* @param mappedClass the mapped class.
*/
protected void initialize(Class mappedClass) {
this.mappedClass = mappedClass;
this.mappedFields = new HashMap();
this.mappedProperties = new HashSet();
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
for (int i = 0; i < pds.length; i++) {
PropertyDescriptor pd = pds[i];
if (pd.getWriteMethod() != null) {
this.mappedFields.put(pd.getName().toLowerCase(), pd);
String underscoredName = underscoreName(pd.getName());
if (!pd.getName().toLowerCase().equals(underscoredName)) {
this.mappedFields.put(underscoredName, pd);
}
this.mappedProperties.add(pd.getName());
}
}
}
/**
* Convert a name in camelCase to an underscored name in lower case.
* Any upper case letters are converted to lower case with a preceding underscore.
* @param name the string containing original name
* @return the converted name
*/
private String underscoreName(String name) {
StringBuffer result = new StringBuffer();
if (name != null && name.length() > 0) {
result.append(name.substring(0, 1).toLowerCase());
for (int i = 1; i < name.length(); i++) {
String s = name.substring(i, i + 1);
if (s.equals(s.toUpperCase())) {
result.append("_");
result.append(s.toLowerCase());
}
else {
result.append(s);
}
}
}
return result.toString();
}
/**
* Get the class that we are mapping to.
*/
public final Class getMappedClass() {
return this.mappedClass;
}
/**
* Set whether we're strictly validating that all bean properties have been
* mapped from corresponding database fields.
* <p>Default is <code>false</code>, accepting unpopulated properties in the
* target bean.
*/
public void setCheckFullyPopulated(boolean checkFullyPopulated) {
this.checkFullyPopulated = checkFullyPopulated;
}
/**
* Return whether we're strictly validating that all bean properties have been
* mapped from corresponding database fields.
*/
public boolean isCheckFullyPopulated() {
return this.checkFullyPopulated;
}
/**
* Extract the values for all columns in the current row.
* <p>Utilizes public setters and result set metadata.
* @see java.sql.ResultSetMetaData
*/
public Object mapRow(ResultSet rs, int rowNumber) throws SQLException {
Assert.state(this.mappedClass != null, "Mapped class was not specified");
Object mappedObject = BeanUtils.instantiateClass(this.mappedClass);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
initBeanWrapper(bw);
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
Set populatedProperties = (isCheckFullyPopulated() ? new HashSet() : null);
for (int index = 1; index <= columnCount; index++) {
String column = JdbcUtils.lookupColumnName(rsmd, index).toLowerCase();
PropertyDescriptor pd = (PropertyDescriptor) this.mappedFields.get(column);
if (pd != null) {
try {
Object value = getColumnValue(rs, index, pd);
if (logger.isDebugEnabled() && rowNumber == 0) {
logger.debug("Mapping column '" + column + "' to property '" +
pd.getName() + "' of type " + pd.getPropertyType());
}
bw.setPropertyValue(pd.getName(), value);
if (populatedProperties != null) {
populatedProperties.add(pd.getName());
}
}
catch (NotWritablePropertyException ex) {
throw new DataRetrievalFailureException(
"Unable to map column " + column + " to property " + pd.getName(), ex);
}
}
}
if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) {
throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields " +
"necessary to populate object of class [" + this.mappedClass + "]: " + this.mappedProperties);
}
return mappedObject;
}
/**
* Initialize the given BeanWrapper to be used for row mapping.
* To be called for each row.
* <p>The default implementation is empty. Can be overridden in subclasses.
* @param bw the BeanWrapper to initialize
*/
protected void initBeanWrapper(BeanWrapper bw) {
}
/**
* Retrieve a JDBC object value for the specified column.
* <p>The default implementation calls
* {@link JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)}.
* Subclasses may override this to check specific value types upfront,
* or to post-process values return from <code>getResultSetValue</code>.
* @param rs is the ResultSet holding the data
* @param index is the column index
* @param pd the bean property that each result object is expected to match
* (or <code>null</code> if none specified)
* @return the Object value
* @throws SQLException in case of extraction failure
* @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)
*/
protected Object getColumnValue(ResultSet rs, int index, PropertyDescriptor pd) throws SQLException {
return JdbcUtils.getResultSetValue(rs, index, pd.getPropertyType());
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.CallableStatement;
import java.sql.SQLException;
import org.springframework.dao.DataAccessException;
/**
* Generic callback interface for code that operates on a CallableStatement.
* Allows to execute any number of operations on a single CallableStatement,
* for example a single execute call or repeated execute calls with varying
* parameters.
*
* <p>Used internally by JdbcTemplate, but also useful for application code.
* Note that the passed-in CallableStatement can have been created by the
* framework or by a custom CallableStatementCreator. However, the latter is
* hardly ever necessary, as most custom callback actions will perform updates
* in which case a standard CallableStatement is fine. Custom actions will
* always set parameter values themselves, so that CallableStatementCreator
* capability is not needed either.
*
* @author Juergen Hoeller
* @since 16.03.2004
* @see JdbcTemplate#execute(String, CallableStatementCallback)
* @see JdbcTemplate#execute(CallableStatementCreator, CallableStatementCallback)
*/
public interface CallableStatementCallback {
/**
* Gets called by <code>JdbcTemplate.execute</code> with an active JDBC
* CallableStatement. Does not need to care about closing the Statement
* or the Connection, or about handling transactions: this will all be
* handled by Spring's JdbcTemplate.
*
* <p><b>NOTE:</b> Any ResultSets opened should be closed in finally blocks
* within the callback implementation. Spring will close the Statement
* object after the callback returned, but this does not necessarily imply
* that the ResultSet resources will be closed: the Statement objects might
* get pooled by the connection pool, with <code>close</code> calls only
* returning the object to the pool but not physically closing the resources.
*
* <p>If called without a thread-bound JDBC transaction (initiated by
* DataSourceTransactionManager), the code will simply get executed on the
* JDBC connection with its transactional semantics. If JdbcTemplate is
* configured to use a JTA-aware DataSource, the JDBC connection and thus
* the callback code will be transactional if a JTA transaction is active.
*
* <p>Allows for returning a result object created within the callback, i.e.
* a domain object or a collection of domain objects. A thrown RuntimeException
* is treated as application exception: it gets propagated to the caller of
* the template.
*
* @param cs active JDBC CallableStatement
* @return a result object, or <code>null</code> if none
* @throws SQLException if thrown by a JDBC method, to be auto-converted
* into a DataAccessException by a SQLExceptionTranslator
* @throws DataAccessException in case of custom exceptions
*/
Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
/**
* One of the three central callback interfaces used by the JdbcTemplate class.
* This interface creates a CallableStatement given a connection, provided
* by the JdbcTemplate class. Implementations are responsible for providing
* SQL and any necessary parameters.
*
* <p>Implementations <i>do not</i> need to concern themselves with
* SQLExceptions that may be thrown from operations they attempt.
* The JdbcTemplate class will catch and handle SQLExceptions appropriately.
*
* <p>A PreparedStatementCreator should also implement the SqlProvider interface
* if it is able to provide the SQL it uses for PreparedStatement creation.
* This allows for better contextual information in case of exceptions.
*
* @author Rod Johnson
* @author Thomas Risberg
* @see JdbcTemplate#execute(CallableStatementCreator, CallableStatementCallback)
* @see JdbcTemplate#call
* @see SqlProvider
*/
public interface CallableStatementCreator {
/**
* Create a callable statement in this connection. Allows implementations to use
* CallableStatements.
* @param con Connection to use to create statement
* @return a callable statement
* @throws SQLException there is no need to catch SQLExceptions
* that may be thrown in the implementation of this method.
* The JdbcTemplate class will handle them.
*/
CallableStatement createCallableStatement(Connection con) throws SQLException;
}

View File

@ -0,0 +1,239 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
/**
* Helper class that efficiently creates multiple {@link CallableStatementCreator}
* objects with different parameters based on a SQL statement and a single
* set of parameter declarations.
*
* @author Rod Johnson
* @author Thomas Risberg
* @author Juergen Hoeller
*/
public class CallableStatementCreatorFactory {
/** The SQL call string, which won't change when the parameters change. */
private final String callString;
/** List of SqlParameter objects. May not be <code>null</code>. */
private final List declaredParameters;
private int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
private boolean updatableResults = false;
private NativeJdbcExtractor nativeJdbcExtractor;
/**
* Create a new factory. Will need to add parameters via the
* {@link #addParameter} method or have no parameters.
*/
public CallableStatementCreatorFactory(String callString) {
this.callString = callString;
this.declaredParameters = new LinkedList();
}
/**
* Create a new factory with the given SQL and the given parameters.
* @param callString the SQL call string
* @param declaredParameters list of {@link SqlParameter} objects
*/
public CallableStatementCreatorFactory(String callString, List declaredParameters) {
this.callString = callString;
this.declaredParameters = declaredParameters;
}
/**
* Add a new declared parameter.
* <p>Order of parameter addition is significant.
* @param param the parameter to add to the list of declared parameters
*/
public void addParameter(SqlParameter param) {
this.declaredParameters.add(param);
}
/**
* Set whether to use prepared statements that return a specific type of ResultSet.
* specific type of ResultSet.
* @param resultSetType the ResultSet type
* @see java.sql.ResultSet#TYPE_FORWARD_ONLY
* @see java.sql.ResultSet#TYPE_SCROLL_INSENSITIVE
* @see java.sql.ResultSet#TYPE_SCROLL_SENSITIVE
*/
public void setResultSetType(int resultSetType) {
this.resultSetType = resultSetType;
}
/**
* Set whether to use prepared statements capable of returning updatable ResultSets.
*/
public void setUpdatableResults(boolean updatableResults) {
this.updatableResults = updatableResults;
}
/**
* Specify the NativeJdbcExtractor to use for unwrapping CallableStatements, if any.
*/
public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) {
this.nativeJdbcExtractor = nativeJdbcExtractor;
}
/**
* Return a new CallableStatementCreator instance given this parameters.
* @param params list of parameters (may be <code>null</code>)
*/
public CallableStatementCreator newCallableStatementCreator(Map params) {
return new CallableStatementCreatorImpl(params != null ? params : new HashMap());
}
/**
* Return a new CallableStatementCreator instance given this parameter mapper.
* @param inParamMapper ParameterMapper implementation that will return a Map of parameters
*/
public CallableStatementCreator newCallableStatementCreator(ParameterMapper inParamMapper) {
return new CallableStatementCreatorImpl(inParamMapper);
}
/**
* CallableStatementCreator implementation returned by this class.
*/
private class CallableStatementCreatorImpl implements CallableStatementCreator, SqlProvider, ParameterDisposer {
private ParameterMapper inParameterMapper;
private Map inParameters;
/**
* Create a new CallableStatementCreatorImpl.
* @param inParamMapper ParameterMapper implementation for mapping input parameters
*/
public CallableStatementCreatorImpl(ParameterMapper inParamMapper) {
this.inParameterMapper = inParamMapper;
}
/**
* Create a new CallableStatementCreatorImpl.
* @param inParams list of SqlParameter objects
*/
public CallableStatementCreatorImpl(Map inParams) {
this.inParameters = inParams;
}
public CallableStatement createCallableStatement(Connection con) throws SQLException {
// If we were given a ParameterMapper, we must let the mapper do its thing to create the Map.
if (this.inParameterMapper != null) {
this.inParameters = this.inParameterMapper.createMap(con);
}
else {
if (this.inParameters == null) {
throw new InvalidDataAccessApiUsageException(
"A ParameterMapper or a Map of parameters must be provided");
}
}
CallableStatement cs = null;
if (resultSetType == ResultSet.TYPE_FORWARD_ONLY && !updatableResults) {
cs = con.prepareCall(callString);
}
else {
cs = con.prepareCall(callString, resultSetType,
updatableResults ? ResultSet.CONCUR_UPDATABLE : ResultSet.CONCUR_READ_ONLY);
}
// Determine CallabeStatement to pass to custom types.
CallableStatement csToUse = cs;
if (nativeJdbcExtractor != null) {
csToUse = nativeJdbcExtractor.getNativeCallableStatement(cs);
}
int sqlColIndx = 1;
for (int i = 0; i < declaredParameters.size(); i++) {
SqlParameter declaredParam = (SqlParameter) declaredParameters.get(i);
if (!declaredParam.isResultsParameter()) {
// So, it's a call parameter - part of the call string.
// Get the value - it may still be null.
Object inValue = this.inParameters.get(declaredParam.getName());
if (declaredParam instanceof ResultSetSupportingSqlParameter) {
// It's an output parameter: SqlReturnResultSet parameters already excluded.
// It need not (but may be) supplied by the caller.
if (declaredParam instanceof SqlOutParameter) {
if (declaredParam.getTypeName() != null) {
cs.registerOutParameter(sqlColIndx, declaredParam.getSqlType(), declaredParam.getTypeName());
}
else {
if (declaredParam.getScale() != null) {
cs.registerOutParameter(sqlColIndx, declaredParam.getSqlType(), declaredParam.getScale().intValue());
}
else {
cs.registerOutParameter(sqlColIndx, declaredParam.getSqlType());
}
}
if (declaredParam.isInputValueProvided()) {
StatementCreatorUtils.setParameterValue(csToUse, sqlColIndx, declaredParam, inValue);
}
}
}
else {
// It's an input parameter; must be supplied by the caller.
if (!this.inParameters.containsKey(declaredParam.getName())) {
throw new InvalidDataAccessApiUsageException(
"Required input parameter '" + declaredParam.getName() + "' is missing");
}
StatementCreatorUtils.setParameterValue(csToUse, sqlColIndx, declaredParam, inValue);
}
sqlColIndx++;
}
}
return cs;
}
public String getSql() {
return callString;
}
public void cleanupParameters() {
if (this.inParameters != null) {
StatementCreatorUtils.cleanupParameters(this.inParameters.values());
}
}
public String toString() {
StringBuffer buf = new StringBuffer("CallableStatementCreatorFactory.CallableStatementCreatorImpl: sql=[");
buf.append(callString).append("]; parameters=").append(this.inParameters);
return buf.toString();
}
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Map;
import org.springframework.core.CollectionFactory;
import org.springframework.jdbc.support.JdbcUtils;
/**
* {@link RowMapper} implementation that creates a <code>java.util.Map</code>
* for each row, representing all columns as key-value pairs: one
* entry for each column, with the column name as key.
*
* <p>The Map implementation to use and the key to use for each column
* in the column Map can be customized through overriding
* {@link #createColumnMap} and {@link #getColumnKey}, respectively.
*
* <p><b>Note:</b> By default, ColumnMapRowMapper will try to build a linked Map
* with case-insensitive keys, to preserve column order as well as allow any
* casing to be used for column names. This requires Commons Collections on the
* classpath (which will be autodetected). Else, the fallback is a standard linked
* HashMap, which will still preserve column order but requires the application
* to specify the column names in the same casing as exposed by the driver.
*
* @author Juergen Hoeller
* @since 1.2
* @see JdbcTemplate#queryForList(String)
* @see JdbcTemplate#queryForMap(String)
*/
public class ColumnMapRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
Map mapOfColValues = createColumnMap(columnCount);
for (int i = 1; i <= columnCount; i++) {
String key = getColumnKey(JdbcUtils.lookupColumnName(rsmd, i));
Object obj = getColumnValue(rs, i);
mapOfColValues.put(key, obj);
}
return mapOfColValues;
}
/**
* Create a Map instance to be used as column map.
* <p>By default, a linked case-insensitive Map will be created if possible,
* else a plain HashMap (see Spring's CollectionFactory).
* @param columnCount the column count, to be used as initial
* capacity for the Map
* @return the new Map instance
* @see org.springframework.core.CollectionFactory#createLinkedCaseInsensitiveMapIfPossible
*/
protected Map createColumnMap(int columnCount) {
return CollectionFactory.createLinkedCaseInsensitiveMapIfPossible(columnCount);
}
/**
* Determine the key to use for the given column in the column Map.
* @param columnName the column name as returned by the ResultSet
* @return the column key to use
* @see java.sql.ResultSetMetaData#getColumnName
*/
protected String getColumnKey(String columnName) {
return columnName;
}
/**
* Retrieve a JDBC object value for the specified column.
* <p>The default implementation uses the <code>getObject</code> method.
* Additionally, this implementation includes a "hack" to get around Oracle
* returning a non standard object for their TIMESTAMP datatype.
* @param rs is the ResultSet holding the data
* @param index is the column index
* @return the Object returned
* @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue
*/
protected Object getColumnValue(ResultSet rs, int index) throws SQLException {
return JdbcUtils.getResultSetValue(rs, index);
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.Connection;
import java.sql.SQLException;
import org.springframework.dao.DataAccessException;
/**
* Generic callback interface for code that operates on a JDBC Connection.
* Allows to execute any number of operations on a single Connection,
* using any type and number of Statements.
*
* <p>This is particularly useful for delegating to existing data access code
* that expects a Connection to work on and throws SQLException. For newly
* written code, it is strongly recommended to use JdbcTemplate's more specific
* operations, for example a <code>query</code> or <code>update</code> variant.
*
* @author Juergen Hoeller
* @since 1.1.3
* @see JdbcTemplate#execute(ConnectionCallback)
* @see JdbcTemplate#query
* @see JdbcTemplate#update
*/
public interface ConnectionCallback {
/**
* Gets called by <code>JdbcTemplate.execute</code> with an active JDBC
* Connection. Does not need to care about activating or closing the
* Connection, or handling transactions.
*
* <p>If called without a thread-bound JDBC transaction (initiated by
* DataSourceTransactionManager), the code will simply get executed on the
* JDBC connection with its transactional semantics. If JdbcTemplate is
* configured to use a JTA-aware DataSource, the JDBC Connection and thus
* the callback code will be transactional if a JTA transaction is active.
*
* <p>Allows for returning a result object created within the callback, i.e.
* a domain object or a collection of domain objects. Note that there's special
* support for single step actions: see <code>JdbcTemplate.queryForObject</code>
* etc. A thrown RuntimeException is treated as application exception:
* it gets propagated to the caller of the template.
*
* @param con active JDBC Connection
* @return a result object, or <code>null</code> if none
* @throws SQLException if thrown by a JDBC method, to be auto-converted
* to a DataAccessException by a SQLExceptionTranslator
* @throws DataAccessException in case of custom exceptions
* @see JdbcTemplate#queryForObject(String, Class)
* @see JdbcTemplate#queryForRowSet(String)
*/
Object doInConnection(Connection con) throws SQLException, DataAccessException;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
/**
* Subinterface of {@link SqlTypeValue} that adds a cleanup callback,
* to be invoked after the value has been set and the corresponding
* statement has been executed.
*
* @author Juergen Hoeller
* @since 1.1
* @see org.springframework.jdbc.core.support.SqlLobValue
*/
public interface DisposableSqlTypeValue extends SqlTypeValue {
/**
* Clean up resources held by this type value,
* for example the LobCreator in case of a SqlLobValue.
* @see org.springframework.jdbc.core.support.SqlLobValue#cleanup()
* @see org.springframework.jdbc.support.SqlValue#cleanup()
*/
void cleanup();
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
/**
* Extension of the {@link BatchPreparedStatementSetter} interface,
* adding a batch exhaustion check.
*
* <p>This interface allows you to signal the end of a batch rather than
* having to determine the exact batch size upfront. Batch size is still
* being honored but it is now the maximum size of the batch.
*
* <p>The {@link #isBatchExhausted} method is called after each call to
* {@link #setValues} to determine whether there were some values added,
* or if the batch was determined to be complete and no additional values
* were provided during the last call to <code>setValues</code>.
*
* <p>Consider extending the
* {@link org.springframework.jdbc.core.support.AbstractInterruptibleBatchPreparedStatementSetter}
* base class instead of implementing this interface directly, using a single
* <code>setValuesIfAvailable</code> callback method that checks for available
* values and sets them, returning whether values have actually been provided.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.0
* @see JdbcTemplate#batchUpdate(String, BatchPreparedStatementSetter)
* @see org.springframework.jdbc.core.support.AbstractInterruptibleBatchPreparedStatementSetter
*/
public interface InterruptibleBatchPreparedStatementSetter extends BatchPreparedStatementSetter {
/**
* Return whether the batch is complete, that is, whether there were no
* additional values added during the last <code>setValues</code> call.
* <p><b>NOTE:</b> If this method returns <code>true</code>, any parameters
* that might have been set during the last <code>setValues</code> call will
* be ignored! Make sure that you set a corresponding internal flag if you
* detect exhaustion <i>at the beginning</i> of your <code>setValues</code>
* implementation, letting this method return <code>true</code> based on the flag.
* @param i index of the statement we're issuing in the batch, starting from 0
* @return whether the batch is already exhausted
* @see #setValues
* @see org.springframework.jdbc.core.support.AbstractInterruptibleBatchPreparedStatementSetter#setValuesIfAvailable
*/
boolean isBatchExhausted(int i);
}

View File

@ -0,0 +1,936 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.util.List;
import java.util.Map;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.jdbc.support.rowset.SqlRowSet;
/**
* Interface specifying a basic set of JDBC operations.
* Implemented by {@link JdbcTemplate}. Not often used directly, but a useful
* option to enhance testability, as it can easily be mocked or stubbed.
*
* <p>Alternatively, the standard JDBC infrastructure can be mocked.
* However, mocking this interface constitutes significantly less work.
* As an alternative to a mock objects approach to testing data access code,
* consider the powerful integration testing support provided in the
* <code>org.springframework.test</code> package, shipped in
* <code>spring-mock.jar</code>.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see JdbcTemplate
*/
public interface JdbcOperations {
//-------------------------------------------------------------------------
// Methods dealing with a plain java.sql.Connection
//-------------------------------------------------------------------------
/**
* Execute a JDBC data access operation, implemented as callback action
* working on a JDBC Connection. This allows for implementing arbitrary
* data access operations, within Spring's managed JDBC environment:
* that is, participating in Spring-managed transactions and converting
* JDBC SQLExceptions into Spring's DataAccessException hierarchy.
* <p>The callback action can return a result object, for example a
* domain object or a collection of domain objects.
* @param action the callback object that specifies the action
* @return a result object returned by the action, or <code>null</code>
* @throws DataAccessException if there is any problem
*/
Object execute(ConnectionCallback action) throws DataAccessException;
//-------------------------------------------------------------------------
// Methods dealing with static SQL (java.sql.Statement)
//-------------------------------------------------------------------------
/**
* Execute a JDBC data access operation, implemented as callback action
* working on a JDBC Statement. This allows for implementing arbitrary data
* access operations on a single Statement, within Spring's managed JDBC
* environment: that is, participating in Spring-managed transactions and
* converting JDBC SQLExceptions into Spring's DataAccessException hierarchy.
* <p>The callback action can return a result object, for example a
* domain object or a collection of domain objects.
* @param action callback object that specifies the action
* @return a result object returned by the action, or <code>null</code>
* @throws DataAccessException if there is any problem
*/
Object execute(StatementCallback action) throws DataAccessException;
/**
* Issue a single SQL execute, typically a DDL statement.
* @param sql static SQL to execute
* @throws DataAccessException if there is any problem
*/
void execute(String sql) throws DataAccessException;
/**
* Execute a query given static SQL, reading the ResultSet with a
* ResultSetExtractor.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>query</code> method with <code>null</code> as argument array.
* @param sql SQL query to execute
* @param rse object that will extract all rows of results
* @return an arbitrary result object, as returned by the ResultSetExtractor
* @throws DataAccessException if there is any problem executing the query
* @see #query(String, Object[], ResultSetExtractor)
*/
Object query(String sql, ResultSetExtractor rse) throws DataAccessException;
/**
* Execute a query given static SQL, reading the ResultSet on a per-row
* basis with a RowCallbackHandler.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>query</code> method with <code>null</code> as argument array.
* @param sql SQL query to execute
* @param rch object that will extract results, one row at a time
* @throws DataAccessException if there is any problem executing the query
* @see #query(String, Object[], RowCallbackHandler)
*/
void query(String sql, RowCallbackHandler rch) throws DataAccessException;
/**
* Execute a query given static SQL, mapping each row to a Java object
* via a RowMapper.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>query</code> method with <code>null</code> as argument array.
* @param sql SQL query to execute
* @param rowMapper object that will map one object per row
* @return the result List, containing mapped objects
* @throws DataAccessException if there is any problem executing the query
* @see #query(String, Object[], RowMapper)
*/
List query(String sql, RowMapper rowMapper) throws DataAccessException;
/**
* Execute a query given static SQL, mapping a single result row to a Java
* object via a RowMapper.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>queryForObject</code> method with <code>null</code> as argument array.
* @param sql SQL query to execute
* @param rowMapper object that will map one object per row
* @return the single mapped object
* @throws IncorrectResultSizeDataAccessException if the query does not
* return exactly one row
* @throws DataAccessException if there is any problem executing the query
* @see #queryForObject(String, Object[], RowMapper)
*/
Object queryForObject(String sql, RowMapper rowMapper) throws DataAccessException;
/**
* Execute a query for a result object, given static SQL.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>queryForObject</code> method with <code>null</code> as argument array.
* <p>This method is useful for running static SQL with a known outcome.
* The query is expected to be a single row/single column query; the returned
* result will be directly mapped to the corresponding object type.
* @param sql SQL query to execute
* @param requiredType the type that the result object is expected to match
* @return the result object of the required type, or <code>null</code> in case of SQL NULL
* @throws IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws DataAccessException if there is any problem executing the query
* @see #queryForObject(String, Object[], Class)
*/
Object queryForObject(String sql, Class requiredType) throws DataAccessException;
/**
* Execute a query for a result Map, given static SQL.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>queryForMap</code> method with <code>null</code> as argument array.
* <p>The query is expected to be a single row query; the result row will be
* mapped to a Map (one entry for each column, using the column name as the key).
* @param sql SQL query to execute
* @return the result Map (one entry for each column, using the
* column name as the key)
* @throws IncorrectResultSizeDataAccessException if the query does not
* return exactly one row
* @throws DataAccessException if there is any problem executing the query
* @see #queryForMap(String, Object[])
* @see ColumnMapRowMapper
*/
Map queryForMap(String sql) throws DataAccessException;
/**
* Execute a query that results in a long value, given static SQL.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>queryForLong</code> method with <code>null</code> as argument array.
* <p>This method is useful for running static SQL with a known outcome.
* The query is expected to be a single row/single column query that results
* in a long value.
* @param sql SQL query to execute
* @return the long value, or 0 in case of SQL NULL
* @throws IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws DataAccessException if there is any problem executing the query
* @see #queryForLong(String, Object[])
*/
long queryForLong(String sql) throws DataAccessException;
/**
* Execute a query that results in an int value, given static SQL.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>queryForInt</code> method with <code>null</code> as argument array.
* <p>This method is useful for running static SQL with a known outcome.
* The query is expected to be a single row/single column query that results
* in an int value.
* @param sql SQL query to execute
* @return the int value, or 0 in case of SQL NULL
* @throws IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws DataAccessException if there is any problem executing the query
* @see #queryForInt(String, Object[])
*/
int queryForInt(String sql) throws DataAccessException;
/**
* Execute a query for a result list, given static SQL.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>queryForList</code> method with <code>null</code> as argument array.
* <p>The results will be mapped to a List (one entry for each row) of
* result objects, each of them matching the specified element type.
* @param sql SQL query to execute
* @param elementType the required type of element in the result list
* (for example, <code>Integer.class</code>)
* @return a List of objects that match the specified element type
* @throws DataAccessException if there is any problem executing the query
* @see #queryForList(String, Object[], Class)
* @see SingleColumnRowMapper
*/
List queryForList(String sql, Class elementType) throws DataAccessException;
/**
* Execute a query for a result list, given static SQL.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>queryForList</code> method with <code>null</code> as argument array.
* <p>The results will be mapped to a List (one entry for each row) of
* Maps (one entry for each column using the column name as the key).
* Each element in the list will be of the form returned by this interface's
* queryForMap() methods.
* @param sql SQL query to execute
* @return an List that contains a Map per row
* @throws DataAccessException if there is any problem executing the query
* @see #queryForList(String, Object[])
*/
List queryForList(String sql) throws DataAccessException;
/**
* Execute a query for a SqlRowSet, given static SQL.
* <p>Uses a JDBC Statement, not a PreparedStatement. If you want to
* execute a static query with a PreparedStatement, use the overloaded
* <code>queryForRowSet</code> method with <code>null</code> as argument array.
* <p>The results will be mapped to an SqlRowSet which holds the data in a
* disconnected fashion. This wrapper will translate any SQLExceptions thrown.
* <p>Note that that, for the default implementation, JDBC RowSet support needs to
* be available at runtime: by default, Sun's <code>com.sun.rowset.CachedRowSetImpl</code>
* class is used, which is part of JDK 1.5+ and also available separately as part of
* Sun's JDBC RowSet Implementations download (rowset.jar).
* @param sql SQL query to execute
* @return a SqlRowSet representation (possibly a wrapper around a
* <code>javax.sql.rowset.CachedRowSet</code>)
* @throws DataAccessException if there is any problem executing the query
* @see #queryForRowSet(String, Object[])
* @see SqlRowSetResultSetExtractor
* @see javax.sql.rowset.CachedRowSet
*/
SqlRowSet queryForRowSet(String sql) throws DataAccessException;
/**
* Issue a single SQL update operation (such as an insert, update or delete statement).
* @param sql static SQL to execute
* @return the number of rows affected
* @throws DataAccessException if there is any problem.
*/
int update(String sql) throws DataAccessException;
/**
* Issue multiple SQL updates on a single JDBC Statement using batching.
* <p>Will fall back to separate updates on a single Statement if the JDBC
* driver does not support batch updates.
* @param sql defining an array of SQL statements that will be executed.
* @return an array of the number of rows affected by each statement
* @throws DataAccessException if there is any problem executing the batch
*/
int[] batchUpdate(String[] sql) throws DataAccessException;
//-------------------------------------------------------------------------
// Methods dealing with prepared statements
//-------------------------------------------------------------------------
/**
* Execute a JDBC data access operation, implemented as callback action
* working on a JDBC PreparedStatement. This allows for implementing arbitrary
* data access operations on a single Statement, within Spring's managed
* JDBC environment: that is, participating in Spring-managed transactions
* and converting JDBC SQLExceptions into Spring's DataAccessException hierarchy.
* <p>The callback action can return a result object, for example a
* domain object or a collection of domain objects.
* @param psc object that can create a PreparedStatement given a Connection
* @param action callback object that specifies the action
* @return a result object returned by the action, or <code>null</code>
* @throws DataAccessException if there is any problem
*/
Object execute(PreparedStatementCreator psc, PreparedStatementCallback action)
throws DataAccessException;
/**
* Execute a JDBC data access operation, implemented as callback action
* working on a JDBC PreparedStatement. This allows for implementing arbitrary
* data access operations on a single Statement, within Spring's managed
* JDBC environment: that is, participating in Spring-managed transactions
* and converting JDBC SQLExceptions into Spring's DataAccessException hierarchy.
* <p>The callback action can return a result object, for example a
* domain object or a collection of domain objects.
* @param sql SQL to execute
* @param action callback object that specifies the action
* @return a result object returned by the action, or <code>null</code>
* @throws DataAccessException if there is any problem
*/
Object execute(String sql, PreparedStatementCallback action) throws DataAccessException;
/**
* Query using a prepared statement, reading the ResultSet with a
* ResultSetExtractor.
* <p>A PreparedStatementCreator can either be implemented directly or
* configured through a PreparedStatementCreatorFactory.
* @param psc object that can create a PreparedStatement given a Connection
* @param rse object that will extract results
* @return an arbitrary result object, as returned by the ResultSetExtractor
* @throws DataAccessException if there is any problem
* @see PreparedStatementCreatorFactory
*/
Object query(PreparedStatementCreator psc, ResultSetExtractor rse) throws DataAccessException;
/**
* Query using a prepared statement, reading the ResultSet with a
* ResultSetExtractor.
* @param sql SQL query to execute
* @param pss object that knows how to set values on the prepared statement.
* If this is <code>null</code>, the SQL will be assumed to contain no bind parameters.
* Even if there are no bind parameters, this object may be used to
* set fetch size and other performance options.
* @param rse object that will extract results
* @return an arbitrary result object, as returned by the ResultSetExtractor
* @throws DataAccessException if there is any problem
*/
Object query(String sql, PreparedStatementSetter pss, ResultSetExtractor rse)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, reading the ResultSet with a
* ResultSetExtractor.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @param rse object that will extract results
* @return an arbitrary result object, as returned by the ResultSetExtractor
* @throws DataAccessException if the query fails
* @see java.sql.Types
*/
Object query(String sql, Object[] args, int[] argTypes, ResultSetExtractor rse)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, reading the ResultSet with a
* ResultSetExtractor.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @param rse object that will extract results
* @return an arbitrary result object, as returned by the ResultSetExtractor
* @throws DataAccessException if the query fails
*/
Object query(String sql, Object[] args, ResultSetExtractor rse) throws DataAccessException;
/**
* Query using a prepared statement, reading the ResultSet on a per-row
* basis with a RowCallbackHandler.
* <p>A PreparedStatementCreator can either be implemented directly or
* configured through a PreparedStatementCreatorFactory.
* @param psc object that can create a PreparedStatement given a Connection
* @param rch object that will extract results, one row at a time
* @throws DataAccessException if there is any problem
* @see PreparedStatementCreatorFactory
*/
void query(PreparedStatementCreator psc, RowCallbackHandler rch) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* PreparedStatementSetter implementation that knows how to bind values
* to the query, reading the ResultSet on a per-row basis with a
* RowCallbackHandler.
* @param sql SQL query to execute
* @param pss object that knows how to set values on the prepared statement.
* If this is <code>null</code>, the SQL will be assumed to contain no bind parameters.
* Even if there are no bind parameters, this object may be used to
* set fetch size and other performance options.
* @param rch object that will extract results, one row at a time
* @throws DataAccessException if the query fails
*/
void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list of
* arguments to bind to the query, reading the ResultSet on a per-row basis
* with a RowCallbackHandler.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @param rch object that will extract results, one row at a time
* @throws DataAccessException if the query fails
* @see java.sql.Types
*/
void query(String sql, Object[] args, int[] argTypes, RowCallbackHandler rch)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list of
* arguments to bind to the query, reading the ResultSet on a per-row basis
* with a RowCallbackHandler.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @param rch object that will extract results, one row at a time
* @throws DataAccessException if the query fails
*/
void query(String sql, Object[] args, RowCallbackHandler rch) throws DataAccessException;
/**
* Query using a prepared statement, mapping each row to a Java object
* via a RowMapper.
* <p>A PreparedStatementCreator can either be implemented directly or
* configured through a PreparedStatementCreatorFactory.
* @param psc object that can create a PreparedStatement given a Connection
* @param rowMapper object that will map one object per row
* @return the result List, containing mapped objects
* @throws DataAccessException if there is any problem
* @see PreparedStatementCreatorFactory
*/
List query(PreparedStatementCreator psc, RowMapper rowMapper) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* PreparedStatementSetter implementation that knows how to bind values
* to the query, mapping each row to a Java object via a RowMapper.
* @param sql SQL query to execute
* @param pss object that knows how to set values on the prepared statement.
* If this is <code>null</code>, the SQL will be assumed to contain no bind parameters.
* Even if there are no bind parameters, this object may be used to
* set fetch size and other performance options.
* @param rowMapper object that will map one object per row
* @return the result List, containing mapped objects
* @throws DataAccessException if the query fails
*/
List query(String sql, PreparedStatementSetter pss, RowMapper rowMapper)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, mapping each row to a Java object
* via a RowMapper.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @param rowMapper object that will map one object per row
* @return the result List, containing mapped objects
* @throws DataAccessException if the query fails
* @see java.sql.Types
*/
List query(String sql, Object[] args, int[] argTypes, RowMapper rowMapper)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, mapping each row to a Java object
* via a RowMapper.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @param rowMapper object that will map one object per row
* @return the result List, containing mapped objects
* @throws DataAccessException if the query fails
*/
List query(String sql, Object[] args, RowMapper rowMapper) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, mapping a single result row to a
* Java object via a RowMapper.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @param rowMapper object that will map one object per row
* @return the single mapped object
* @throws IncorrectResultSizeDataAccessException if the query does not
* return exactly one row
* @throws DataAccessException if the query fails
*/
Object queryForObject(String sql, Object[] args, int[] argTypes, RowMapper rowMapper)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, mapping a single result row to a
* Java object via a RowMapper.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @param rowMapper object that will map one object per row
* @return the single mapped object
* @throws IncorrectResultSizeDataAccessException if the query does not
* return exactly one row
* @throws DataAccessException if the query fails
*/
Object queryForObject(String sql, Object[] args, RowMapper rowMapper)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result object.
* <p>The query is expected to be a single row/single column query; the returned
* result will be directly mapped to the corresponding object type.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @param requiredType the type that the result object is expected to match
* @return the result object of the required type, or <code>null</code> in case of SQL NULL
* @throws IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws DataAccessException if the query fails
* @see #queryForObject(String, Class)
* @see java.sql.Types
*/
Object queryForObject(String sql, Object[] args, int[] argTypes, Class requiredType)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result object.
* <p>The query is expected to be a single row/single column query; the returned
* result will be directly mapped to the corresponding object type.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @param requiredType the type that the result object is expected to match
* @return the result object of the required type, or <code>null</code> in case of SQL NULL
* @throws IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws DataAccessException if the query fails
* @see #queryForObject(String, Class)
*/
Object queryForObject(String sql, Object[] args, Class requiredType) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result Map.
* <p>The query is expected to be a single row query; the result row will be
* mapped to a Map (one entry for each column, using the column name as the key).
* @param sql SQL query to execute
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @return the result Map (one entry for each column, using the
* column name as the key)
* @throws IncorrectResultSizeDataAccessException if the query does not
* return exactly one row
* @throws DataAccessException if the query fails
* @see #queryForMap(String)
* @see ColumnMapRowMapper
* @see java.sql.Types
*/
Map queryForMap(String sql, Object[] args, int[] argTypes) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result Map.
* The queryForMap() methods defined by this interface are appropriate
* when you don't have a domain model. Otherwise, consider using
* one of the queryForObject() methods.
* <p>The query is expected to be a single row query; the result row will be
* mapped to a Map (one entry for each column, using the column name as the key).
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @return the result Map (one entry for each column, using the
* column name as the key)
* @throws IncorrectResultSizeDataAccessException if the query does not
* return exactly one row
* @throws DataAccessException if the query fails
* @see #queryForMap(String)
* @see ColumnMapRowMapper
*/
Map queryForMap(String sql, Object[] args) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, resulting in a long value.
* <p>The query is expected to be a single row/single column query that
* results in a long value.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @return the long value, or 0 in case of SQL NULL
* @throws IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws DataAccessException if the query fails
* @see #queryForLong(String)
* @see java.sql.Types
*/
long queryForLong(String sql, Object[] args, int[] argTypes) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, resulting in a long value.
* <p>The query is expected to be a single row/single column query that
* results in a long value.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @return the long value, or 0 in case of SQL NULL
* @throws IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws DataAccessException if the query fails
* @see #queryForLong(String)
*/
long queryForLong(String sql, Object[] args) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, resulting in an int value.
* <p>The query is expected to be a single row/single column query that
* results in an int value.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @return the int value, or 0 in case of SQL NULL
* @throws IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws DataAccessException if the query fails
* @see #queryForInt(String)
* @see java.sql.Types
*/
int queryForInt(String sql, Object[] args, int[] argTypes) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, resulting in an int value.
* <p>The query is expected to be a single row/single column query that
* results in an int value.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @return the int value, or 0 in case of SQL NULL
* @throws IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws DataAccessException if the query fails
* @see #queryForInt(String)
*/
int queryForInt(String sql, Object[] args) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result list.
* <p>The results will be mapped to a List (one entry for each row) of
* result objects, each of them matching the specified element type.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @param elementType the required type of element in the result list
* (for example, <code>Integer.class</code>)
* @return a List of objects that match the specified element type
* @throws DataAccessException if the query fails
* @see #queryForList(String, Class)
* @see SingleColumnRowMapper
*/
List queryForList(String sql, Object[] args, int[] argTypes, Class elementType)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result list.
* <p>The results will be mapped to a List (one entry for each row) of
* result objects, each of them matching the specified element type.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @param elementType the required type of element in the result list
* (for example, <code>Integer.class</code>)
* @return a List of objects that match the specified element type
* @throws DataAccessException if the query fails
* @see #queryForList(String, Class)
* @see SingleColumnRowMapper
*/
List queryForList(String sql, Object[] args, Class elementType) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result list.
* <p>The results will be mapped to a List (one entry for each row) of
* Maps (one entry for each column, using the column name as the key).
* Thus Each element in the list will be of the form returned by this interface's
* queryForMap() methods.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @return a List that contains a Map per row
* @throws DataAccessException if the query fails
* @see #queryForList(String)
* @see java.sql.Types
*/
List queryForList(String sql, Object[] args, int[] argTypes) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result list.
* <p>The results will be mapped to a List (one entry for each row) of
* Maps (one entry for each column, using the column name as the key).
* Each element in the list will be of the form returned by this interface's
* queryForMap() methods.
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @return a List that contains a Map per row
* @throws DataAccessException if the query fails
* @see #queryForList(String)
*/
List queryForList(String sql, Object[] args) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a SqlRowSet.
* <p>The results will be mapped to an SqlRowSet which holds the data in a
* disconnected fashion. This wrapper will translate any SQLExceptions thrown.
* <p>Note that that, for the default implementation, JDBC RowSet support needs to
* be available at runtime: by default, Sun's <code>com.sun.rowset.CachedRowSetImpl</code>
* class is used, which is part of JDK 1.5+ and also available separately as part of
* Sun's JDBC RowSet Implementations download (rowset.jar).
* @param sql SQL query to execute
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @return a SqlRowSet representation (possibly a wrapper around a
* <code>javax.sql.rowset.CachedRowSet</code>)
* @throws DataAccessException if there is any problem executing the query
* @see #queryForRowSet(String)
* @see SqlRowSetResultSetExtractor
* @see javax.sql.rowset.CachedRowSet
* @see java.sql.Types
*/
SqlRowSet queryForRowSet(String sql, Object[] args, int[] argTypes) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a SqlRowSet.
* <p>The results will be mapped to an SqlRowSet which holds the data in a
* disconnected fashion. This wrapper will translate any SQLExceptions thrown.
* <p>Note that that, for the default implementation, JDBC RowSet support needs to
* be available at runtime: by default, Sun's <code>com.sun.rowset.CachedRowSetImpl</code>
* class is used, which is part of JDK 1.5+ and also available separately as part of
* Sun's JDBC RowSet Implementations download (rowset.jar).
* @param sql SQL query to execute
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @return a SqlRowSet representation (possibly a wrapper around a
* <code>javax.sql.rowset.CachedRowSet</code>)
* @throws DataAccessException if there is any problem executing the query
* @see #queryForRowSet(String)
* @see SqlRowSetResultSetExtractor
* @see javax.sql.rowset.CachedRowSet
*/
SqlRowSet queryForRowSet(String sql, Object[] args) throws DataAccessException;
/**
* Issue a single SQL update operation (such as an insert, update or delete statement)
* using a PreparedStatementCreator to provide SQL and any required parameters.
* <p>A PreparedStatementCreator can either be implemented directly or
* configured through a PreparedStatementCreatorFactory.
* @param psc object that provides SQL and any necessary parameters
* @return the number of rows affected
* @throws DataAccessException if there is any problem issuing the update
* @see PreparedStatementCreatorFactory
*/
int update(PreparedStatementCreator psc) throws DataAccessException;
/**
* Issue an update statement using a PreparedStatementCreator to provide SQL and
* any required parameters. Generated keys will be put into the given KeyHolder.
* <p>Note that the given PreparedStatementCreator has to create a statement
* with activated extraction of generated keys (a JDBC 3.0 feature). This can
* either be done directly or through using a PreparedStatementCreatorFactory.
* @param psc object that provides SQL and any necessary parameters
* @param generatedKeyHolder KeyHolder that will hold the generated keys
* @return the number of rows affected
* @throws DataAccessException if there is any problem issuing the update
* @see PreparedStatementCreatorFactory
* @see org.springframework.jdbc.support.GeneratedKeyHolder
*/
int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder) throws DataAccessException;
/**
* Issue an update statement using a PreparedStatementSetter to set bind parameters,
* with given SQL. Simpler than using a PreparedStatementCreator as this method
* will create the PreparedStatement: The PreparedStatementSetter just needs to
* set parameters.
* @param sql SQL containing bind parameters
* @param pss helper that sets bind parameters. If this is <code>null</code>
* we run an update with static SQL.
* @return the number of rows affected
* @throws DataAccessException if there is any problem issuing the update
*/
int update(String sql, PreparedStatementSetter pss) throws DataAccessException;
/**
* Issue a single SQL update operation (such as an insert, update or delete statement)
* via a prepared statement, binding the given arguments.
* @param sql SQL containing bind parameters
* @param args arguments to bind to the query
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @return the number of rows affected
* @throws DataAccessException if there is any problem issuing the update
* @see java.sql.Types
*/
int update(String sql, Object[] args, int[] argTypes) throws DataAccessException;
/**
* Issue a single SQL update operation (such as an insert, update or delete statement)
* via a prepared statement, binding the given arguments.
* @param sql SQL containing bind parameters
* @param args arguments to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type);
* may also contain {@link SqlParameterValue} objects which indicate not
* only the argument value but also the SQL type and optionally the scale
* @return the number of rows affected
* @throws DataAccessException if there is any problem issuing the update
*/
int update(String sql, Object[] args) throws DataAccessException;
/**
* Issue multiple update statements on a single PreparedStatement,
* using batch updates and a BatchPreparedStatementSetter to set values.
* <p>Will fall back to separate updates on a single PreparedStatement
* if the JDBC driver does not support batch updates.
* @param sql defining PreparedStatement that will be reused.
* All statements in the batch will use the same SQL.
* @param pss object to set parameters on the PreparedStatement
* created by this method
* @return an array of the number of rows affected by each statement
* @throws DataAccessException if there is any problem issuing the update
*/
int[] batchUpdate(String sql, BatchPreparedStatementSetter pss) throws DataAccessException;
//-------------------------------------------------------------------------
// Methods dealing with callable statements
//-------------------------------------------------------------------------
/**
* Execute a JDBC data access operation, implemented as callback action
* working on a JDBC CallableStatement. This allows for implementing arbitrary
* data access operations on a single Statement, within Spring's managed
* JDBC environment: that is, participating in Spring-managed transactions
* and converting JDBC SQLExceptions into Spring's DataAccessException hierarchy.
* <p>The callback action can return a result object, for example a
* domain object or a collection of domain objects.
* @param csc object that can create a CallableStatement given a Connection
* @param action callback object that specifies the action
* @return a result object returned by the action, or <code>null</code>
* @throws DataAccessException if there is any problem
*/
Object execute(CallableStatementCreator csc, CallableStatementCallback action)
throws DataAccessException;
/**
* Execute a JDBC data access operation, implemented as callback action
* working on a JDBC CallableStatement. This allows for implementing arbitrary
* data access operations on a single Statement, within Spring's managed
* JDBC environment: that is, participating in Spring-managed transactions
* and converting JDBC SQLExceptions into Spring's DataAccessException hierarchy.
* <p>The callback action can return a result object, for example a
* domain object or a collection of domain objects.
* @param callString the SQL call string to execute
* @param action callback object that specifies the action
* @return a result object returned by the action, or <code>null</code>
* @throws DataAccessException if there is any problem
*/
Object execute(String callString, CallableStatementCallback action) throws DataAccessException;
/**
* Execute a SQL call using a CallableStatementCreator to provide SQL and any
* required parameters.
* @param csc object that provides SQL and any necessary parameters
* @param declaredParameters list of declared SqlParameter objects
* @return Map of extracted out parameters
* @throws DataAccessException if there is any problem issuing the update
*/
Map call(CallableStatementCreator csc, List declaredParameters) throws DataAccessException;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
/**
* Interface to be implemented by objects that can close resources
* allocated by parameters like SqlLobValues.
*
* <p>Typically implemented by PreparedStatementCreators and
* PreparedStatementSetters that support DisposableSqlTypeValue
* objects (e.g. SqlLobValue) as parameters.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 1.1
* @see PreparedStatementCreator
* @see PreparedStatementSetter
* @see DisposableSqlTypeValue
* @see org.springframework.jdbc.core.support.SqlLobValue
*/
public interface ParameterDisposer {
/**
* Close the resources allocated by parameters that the implementing
* object holds, for example in case of a DisposableSqlTypeValue
* (like a SqlLobValue).
* @see DisposableSqlTypeValue#cleanup
* @see org.springframework.jdbc.core.support.SqlLobValue#cleanup
*/
public void cleanupParameters();
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
/**
* Implement this interface when parameters need to be customized based
* on the connection. We might need to do this to make use of proprietary
* features, available only with a specific Connection type.
*
* @author Rod Johnson
* @author Thomas Risberg
* @see CallableStatementCreatorFactory#newCallableStatementCreator(ParameterMapper)
* @see org.springframework.jdbc.object.StoredProcedure#execute(ParameterMapper)
*/
public interface ParameterMapper {
/**
* Create a Map of input parameters, keyed by name.
* @param con JDBC connection. This is useful (and the purpose of this interface)
* if we need to do something RDBMS-specific with a proprietary Connection
* implementation class. This class conceals such proprietary details. However,
* it is best to avoid using such proprietary RDBMS features if possible.
* @throws SQLException if a SQLException is encountered setting
* parameter values (that is, there's no need to catch SQLException)
* @return Map of input parameters, keyed by name (never <code>null</code>)
*/
Map createMap(Connection con) throws SQLException;
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.dao.DataAccessException;
/**
* Generic callback interface for code that operates on a PreparedStatement.
* Allows to execute any number of operations on a single PreparedStatement,
* for example a single <code>executeUpdate</code> call or repeated
* <code>executeUpdate</code> calls with varying parameters.
*
* <p>Used internally by JdbcTemplate, but also useful for application code.
* Note that the passed-in PreparedStatement can have been created by the
* framework or by a custom PreparedStatementCreator. However, the latter is
* hardly ever necessary, as most custom callback actions will perform updates
* in which case a standard PreparedStatement is fine. Custom actions will
* always set parameter values themselves, so that PreparedStatementCreator
* capability is not needed either.
*
* @author Juergen Hoeller
* @since 16.03.2004
* @see JdbcTemplate#execute(String, PreparedStatementCallback)
* @see JdbcTemplate#execute(PreparedStatementCreator, PreparedStatementCallback)
*/
public interface PreparedStatementCallback {
/**
* Gets called by <code>JdbcTemplate.execute</code> with an active JDBC
* PreparedStatement. Does not need to care about closing the Statement
* or the Connection, or about handling transactions: this will all be
* handled by Spring's JdbcTemplate.
*
* <p><b>NOTE:</b> Any ResultSets opened should be closed in finally blocks
* within the callback implementation. Spring will close the Statement
* object after the callback returned, but this does not necessarily imply
* that the ResultSet resources will be closed: the Statement objects might
* get pooled by the connection pool, with <code>close</code> calls only
* returning the object to the pool but not physically closing the resources.
*
* <p>If called without a thread-bound JDBC transaction (initiated by
* DataSourceTransactionManager), the code will simply get executed on the
* JDBC connection with its transactional semantics. If JdbcTemplate is
* configured to use a JTA-aware DataSource, the JDBC connection and thus
* the callback code will be transactional if a JTA transaction is active.
*
* <p>Allows for returning a result object created within the callback, i.e.
* a domain object or a collection of domain objects. Note that there's
* special support for single step actions: see JdbcTemplate.queryForObject etc.
* A thrown RuntimeException is treated as application exception, it gets
* propagated to the caller of the template.
*
* @param ps active JDBC PreparedStatement
* @return a result object, or <code>null</code> if none
* @throws SQLException if thrown by a JDBC method, to be auto-converted
* to a DataAccessException by a SQLExceptionTranslator
* @throws DataAccessException in case of custom exceptions
* @see JdbcTemplate#queryForObject(String, Object[], Class)
* @see JdbcTemplate#queryForList(String, Object[])
*/
Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* One of the two central callback interfaces used by the JdbcTemplate class.
* This interface creates a PreparedStatement given a connection, provided
* by the JdbcTemplate class. Implementations are responsible for providing
* SQL and any necessary parameters.
*
* <p>Implementations <i>do not</i> need to concern themselves with
* SQLExceptions that may be thrown from operations they attempt.
* The JdbcTemplate class will catch and handle SQLExceptions appropriately.
*
* <p>A PreparedStatementCreator should also implement the SqlProvider interface
* if it is able to provide the SQL it uses for PreparedStatement creation.
* This allows for better contextual information in case of exceptions.
*
* @author Rod Johnson
* @see JdbcTemplate#execute(PreparedStatementCreator, PreparedStatementCallback)
* @see JdbcTemplate#query(PreparedStatementCreator, RowCallbackHandler)
* @see JdbcTemplate#update(PreparedStatementCreator)
* @see SqlProvider
*/
public interface PreparedStatementCreator {
/**
* Create a statement in this connection. Allows implementations to use
* PreparedStatements. The JdbcTemplate will close the created statement.
* @param con Connection to use to create statement
* @return a prepared statement
* @throws SQLException there is no need to catch SQLExceptions
* that may be thrown in the implementation of this method.
* The JdbcTemplate class will handle them.
*/
PreparedStatement createPreparedStatement(Connection con) throws SQLException;
}

View File

@ -0,0 +1,321 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
import org.springframework.util.Assert;
/**
* Helper class that efficiently creates multiple {@link PreparedStatementCreator}
* objects with different parameters based on a SQL statement and a single
* set of parameter declarations.
*
* @author Rod Johnson
* @author Thomas Risberg
* @author Juergen Hoeller
*/
public class PreparedStatementCreatorFactory {
/** The SQL, which won't change when the parameters change */
private final String sql;
/** List of SqlParameter objects. May not be <code>null</code>. */
private final List declaredParameters;
private int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
private boolean updatableResults = false;
private boolean returnGeneratedKeys = false;
private String[] generatedKeysColumnNames = null;
private NativeJdbcExtractor nativeJdbcExtractor;
/**
* Create a new factory. Will need to add parameters via the
* {@link #addParameter} method or have no parameters.
*/
public PreparedStatementCreatorFactory(String sql) {
this.sql = sql;
this.declaredParameters = new LinkedList();
}
/**
* Create a new factory with the given SQL and JDBC types.
* @param sql SQL to execute
* @param types int array of JDBC types
*/
public PreparedStatementCreatorFactory(String sql, int[] types) {
this.sql = sql;
this.declaredParameters = SqlParameter.sqlTypesToAnonymousParameterList(types);
}
/**
* Create a new factory with the given SQL and parameters.
* @param sql SQL
* @param declaredParameters list of {@link SqlParameter} objects
* @see SqlParameter
*/
public PreparedStatementCreatorFactory(String sql, List declaredParameters) {
this.sql = sql;
this.declaredParameters = declaredParameters;
}
/**
* Add a new declared parameter.
* <p>Order of parameter addition is significant.
* @param param the parameter to add to the list of declared parameters
*/
public void addParameter(SqlParameter param) {
this.declaredParameters.add(param);
}
/**
* Set whether to use prepared statements that return a specific type of ResultSet.
* @param resultSetType the ResultSet type
* @see java.sql.ResultSet#TYPE_FORWARD_ONLY
* @see java.sql.ResultSet#TYPE_SCROLL_INSENSITIVE
* @see java.sql.ResultSet#TYPE_SCROLL_SENSITIVE
*/
public void setResultSetType(int resultSetType) {
this.resultSetType = resultSetType;
}
/**
* Set whether to use prepared statements capable of returning updatable ResultSets.
*/
public void setUpdatableResults(boolean updatableResults) {
this.updatableResults = updatableResults;
}
/**
* Set whether prepared statements should be capable of returning auto-generated keys.
*/
public void setReturnGeneratedKeys(boolean returnGeneratedKeys) {
this.returnGeneratedKeys = returnGeneratedKeys;
}
/**
* Set the column names of the auto-generated keys.
*/
public void setGeneratedKeysColumnNames(String[] names) {
this.generatedKeysColumnNames = names;
}
/**
* Specify the NativeJdbcExtractor to use for unwrapping PreparedStatements, if any.
*/
public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) {
this.nativeJdbcExtractor = nativeJdbcExtractor;
}
/**
* Return a new PreparedStatementSetter for the given parameters.
* @param params list of parameters (may be <code>null</code>)
*/
public PreparedStatementSetter newPreparedStatementSetter(List params) {
return new PreparedStatementCreatorImpl(params != null ? params : Collections.EMPTY_LIST);
}
/**
* Return a new PreparedStatementSetter for the given parameters.
* @param params the parameter array (may be <code>null</code>)
*/
public PreparedStatementSetter newPreparedStatementSetter(Object[] params) {
return new PreparedStatementCreatorImpl(params != null ? Arrays.asList(params) : Collections.EMPTY_LIST);
}
/**
* Return a new PreparedStatementCreator for the given parameters.
* @param params list of parameters (may be <code>null</code>)
*/
public PreparedStatementCreator newPreparedStatementCreator(List params) {
return new PreparedStatementCreatorImpl(params != null ? params : Collections.EMPTY_LIST);
}
/**
* Return a new PreparedStatementCreator for the given parameters.
* @param params the parameter array (may be <code>null</code>)
*/
public PreparedStatementCreator newPreparedStatementCreator(Object[] params) {
return new PreparedStatementCreatorImpl(params != null ? Arrays.asList(params) : Collections.EMPTY_LIST);
}
/**
* Return a new PreparedStatementCreator for the given parameters.
* @param sqlToUse the actual SQL statement to use (if different from
* the factory's, for example because of named parameter expanding)
* @param params the parameter array (may be <code>null</code>)
*/
public PreparedStatementCreator newPreparedStatementCreator(String sqlToUse, Object[] params) {
return new PreparedStatementCreatorImpl(
sqlToUse, params != null ? Arrays.asList(params) : Collections.EMPTY_LIST);
}
/**
* PreparedStatementCreator implementation returned by this class.
*/
private class PreparedStatementCreatorImpl
implements PreparedStatementCreator, PreparedStatementSetter, SqlProvider, ParameterDisposer {
private final String actualSql;
private final List parameters;
public PreparedStatementCreatorImpl(List parameters) {
this(sql, parameters);
}
public PreparedStatementCreatorImpl(String actualSql, List parameters) {
this.actualSql = actualSql;
Assert.notNull(parameters, "Parameters List must not be null");
this.parameters = parameters;
if (this.parameters.size() != declaredParameters.size()) {
// account for named parameters being used multiple times
Set names = new HashSet();
for (int i = 0; i < parameters.size(); i++) {
Object o = parameters.get(i);
if (o instanceof SqlParameterValue) {
names.add(((SqlParameterValue)o).getName());
}
else {
names.add("Parameter #" + i);
}
}
if (names.size() != declaredParameters.size()) {
throw new InvalidDataAccessApiUsageException(
"SQL [" + sql + "]: given " + names.size() +
" parameters but expected " + declaredParameters.size());
}
}
}
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = null;
if (generatedKeysColumnNames != null || returnGeneratedKeys) {
try {
if (generatedKeysColumnNames != null) {
ps = con.prepareStatement(this.actualSql, generatedKeysColumnNames);
}
else {
ps = con.prepareStatement(this.actualSql, PreparedStatement.RETURN_GENERATED_KEYS);
}
}
catch (AbstractMethodError ex) {
throw new InvalidDataAccessResourceUsageException(
"The JDBC driver is not compliant to JDBC 3.0 and thus " +
"does not support retrieval of auto-generated keys", ex);
}
}
else if (resultSetType == ResultSet.TYPE_FORWARD_ONLY && !updatableResults) {
ps = con.prepareStatement(this.actualSql);
}
else {
ps = con.prepareStatement(this.actualSql, resultSetType,
updatableResults ? ResultSet.CONCUR_UPDATABLE : ResultSet.CONCUR_READ_ONLY);
}
setValues(ps);
return ps;
}
public void setValues(PreparedStatement ps) throws SQLException {
// Determine PreparedStatement to pass to custom types.
PreparedStatement psToUse = ps;
if (nativeJdbcExtractor != null) {
psToUse = nativeJdbcExtractor.getNativePreparedStatement(ps);
}
// Set arguments: Does nothing if there are no parameters.
int sqlColIndx = 1;
for (int i = 0; i < this.parameters.size(); i++) {
Object in = this.parameters.get(i);
SqlParameter declaredParameter = null;
// SqlParameterValue overrides declared parameter metadata, in particular for
// independence from the declared parameter position in case of named parameters.
if (in instanceof SqlParameterValue) {
SqlParameterValue paramValue = (SqlParameterValue) in;
in = paramValue.getValue();
declaredParameter = paramValue;
}
else {
if (declaredParameters.size() <= i) {
throw new InvalidDataAccessApiUsageException(
"SQL [" + sql + "]: unable to access parameter number " + (i + 1) +
" given only " + declaredParameters.size() + " parameters");
}
declaredParameter = (SqlParameter) declaredParameters.get(i);
}
if (in instanceof Collection && declaredParameter.getSqlType() != Types.ARRAY) {
Collection entries = (Collection) in;
for (Iterator it = entries.iterator(); it.hasNext();) {
Object entry = it.next();
if (entry instanceof Object[]) {
Object[] valueArray = ((Object[])entry);
for (int k = 0; k < valueArray.length; k++) {
Object argValue = valueArray[k];
StatementCreatorUtils.setParameterValue(psToUse, sqlColIndx++, declaredParameter, argValue);
}
}
else {
StatementCreatorUtils.setParameterValue(psToUse, sqlColIndx++, declaredParameter, entry);
}
}
}
else {
StatementCreatorUtils.setParameterValue(psToUse, sqlColIndx++, declaredParameter, in);
}
}
}
public String getSql() {
return sql;
}
public void cleanupParameters() {
StatementCreatorUtils.cleanupParameters(this.parameters);
}
public String toString() {
StringBuffer buf = new StringBuffer("PreparedStatementCreatorFactory.PreparedStatementCreatorImpl: sql=[");
buf.append(sql).append("]; parameters=").append(this.parameters);
return buf.toString();
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* General callback interface used by the {@link JdbcTemplate} class.
*
* <p>This interface sets values on a {@link java.sql.PreparedStatement} provided
* by the JdbcTemplate class, for each of a number of updates in a batch using the
* same SQL. Implementations are responsible for setting any necessary parameters.
* SQL with placeholders will already have been supplied.
*
* <p>It's easier to use this interface than {@link PreparedStatementCreator}:
* The JdbcTemplate will create the PreparedStatement, with the callback
* only being responsible for setting parameter values.
*
* <p>Implementations <i>do not</i> need to concern themselves with
* SQLExceptions that may be thrown from operations they attempt.
* The JdbcTemplate class will catch and handle SQLExceptions appropriately.
*
* @author Rod Johnson
* @since March 2, 2003
* @see JdbcTemplate#update(String, PreparedStatementSetter)
* @see JdbcTemplate#query(String, PreparedStatementSetter, ResultSetExtractor)
*/
public interface PreparedStatementSetter {
/**
* Set parameter values on the given PreparedStatement.
* @param ps the PreparedStatement to invoke setter methods on
* @throws SQLException if a SQLException is encountered
* (i.e. there is no need to catch SQLException)
*/
void setValues(PreparedStatement ps) throws SQLException;
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.dao.DataAccessException;
/**
* Callback interface used by {@link JdbcTemplate}'s query methods.
* Implementations of this interface perform the actual work of extracting
* results from a {@link java.sql.ResultSet}, but don't need to worry
* about exception handling. {@link java.sql.SQLException SQLExceptions}
* will be caught and handled by the calling JdbcTemplate.
*
* <p>This interface is mainly used within the JDBC framework itself.
* A {@link RowMapper} is usually a simpler choice for ResultSet processing,
* mapping one result object per row instead of one result object for
* the entire ResultSet.
*
* <p>Note: In contrast to a {@link RowCallbackHandler}, a ResultSetExtractor
* object is typically stateless and thus reusable, as long as it doesn't
* access stateful resources (such as output streams when streaming LOB
* contents) or keep result state within the object.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since April 24, 2003
* @see JdbcTemplate
* @see RowCallbackHandler
* @see RowMapper
* @see org.springframework.jdbc.core.support.AbstractLobStreamingResultSetExtractor
*/
public interface ResultSetExtractor {
/**
* Implementations must implement this method to process the entire ResultSet.
* @param rs ResultSet to extract data from. Implementations should
* not close this: it will be closed by the calling JdbcTemplate.
* @return an arbitrary result object, or <code>null</code> if none
* (the extractor will typically be stateful in the latter case).
* @throws SQLException if a SQLException is encountered getting column
* values or navigating (that is, there's no need to catch SQLException)
* @throws DataAccessException in case of custom exceptions
*/
Object extractData(ResultSet rs) throws SQLException, DataAccessException;
}

View File

@ -0,0 +1,136 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
/**
* Common base class for ResultSet-supporting SqlParameters like
* {@link SqlOutParameter} and {@link SqlReturnResultSet}.
*
* @author Juergen Hoeller
* @since 1.0.2
*/
public class ResultSetSupportingSqlParameter extends SqlParameter {
private ResultSetExtractor resultSetExtractor;
private RowCallbackHandler rowCallbackHandler;
private RowMapper rowMapper;
/**
* Create a new ResultSetSupportingSqlParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
*/
public ResultSetSupportingSqlParameter(String name, int sqlType) {
super(name, sqlType);
}
/**
* Create a new ResultSetSupportingSqlParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param scale the number of digits after the decimal point
* (for DECIMAL and NUMERIC types)
*/
public ResultSetSupportingSqlParameter(String name, int sqlType, int scale) {
super(name, sqlType, scale);
}
/**
* Create a new ResultSetSupportingSqlParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param typeName the type name of the parameter (optional)
*/
public ResultSetSupportingSqlParameter(String name, int sqlType, String typeName) {
super(name, sqlType, typeName);
}
/**
* Create a new ResultSetSupportingSqlParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param rse ResultSetExtractor to use for parsing the ResultSet
*/
public ResultSetSupportingSqlParameter(String name, int sqlType, ResultSetExtractor rse) {
super(name, sqlType);
this.resultSetExtractor = rse;
}
/**
* Create a new ResultSetSupportingSqlParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param rch RowCallbackHandler to use for parsing the ResultSet
*/
public ResultSetSupportingSqlParameter(String name, int sqlType, RowCallbackHandler rch) {
super(name, sqlType);
this.rowCallbackHandler = rch;
}
/**
* Create a new ResultSetSupportingSqlParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param rm RowMapper to use for parsing the ResultSet
*/
public ResultSetSupportingSqlParameter(String name, int sqlType, RowMapper rm) {
super(name, sqlType);
this.rowMapper = rm;
}
/**
* Does this parameter support a ResultSet, i.e. does it hold a
* ResultSetExtractor, RowCallbackHandler or RowMapper?
*/
public boolean isResultSetSupported() {
return (this.resultSetExtractor != null || this.rowCallbackHandler != null || this.rowMapper != null);
}
/**
* Return the ResultSetExtractor held by this parameter, if any.
*/
public ResultSetExtractor getResultSetExtractor() {
return resultSetExtractor;
}
/**
* Return the RowCallbackHandler held by this parameter, if any.
*/
public RowCallbackHandler getRowCallbackHandler() {
return this.rowCallbackHandler;
}
/**
* Return the RowMapper held by this parameter, if any.
*/
public RowMapper getRowMapper() {
return this.rowMapper;
}
/**
* <p>This implementation always returns <code>false</code>.
*/
public boolean isInputValueProvided() {
return false;
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* An interface used by {@link JdbcTemplate} for processing rows of a
* {@link java.sql.ResultSet} on a per-row basis. Implementations of
* this interface perform the actual work of processing each row
* but don't need to worry about exception handling.
* {@link java.sql.SQLException SQLExceptions} will be caught and handled
* by the calling JdbcTemplate.
*
* <p>In contrast to a {@link ResultSetExtractor}, a RowCallbackHandler
* object is typically stateful: It keeps the result state within the
* object, to be available for later inspection. See
* {@link RowCountCallbackHandler} for a usage example.
*
* <p>Consider using a {@link RowMapper} instead if you need to map
* exactly one result object per row, assembling them into a List.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see JdbcTemplate
* @see RowMapper
* @see ResultSetExtractor
* @see RowCountCallbackHandler
*/
public interface RowCallbackHandler {
/**
* Implementations must implement this method to process each row of data
* in the ResultSet. This method should not call <code>next()</code> on
* the ResultSet; it is only supposed to extract values of the current row.
* <p>Exactly what the implementation chooses to do is up to it:
* A trivial implementation might simply count rows, while another
* implementation might build an XML document.
* @param rs the ResultSet to process (pre-initialized for the current row)
* @throws SQLException if a SQLException is encountered getting
* column values (that is, there's no need to catch SQLException)
*/
void processRow(ResultSet rs) throws SQLException;
}

View File

@ -0,0 +1,137 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import org.springframework.jdbc.support.JdbcUtils;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
/**
* Implementation of RowCallbackHandler. Convenient superclass for callback handlers.
* An instance can only be used once.
*
* <p>We can either use this on its own (for example, in a test case, to ensure
* that our result sets have valid dimensions), or use it as a superclass
* for callback handlers that actually do something, and will benefit
* from the dimension information it provides.
*
* <p>A usage example with JdbcTemplate:
*
* <pre class="code">JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); // reusable object
*
* RowCountCallbackHandler countCallback = new RowCountCallbackHandler(); // not reusable
* jdbcTemplate.query("select * from user", countCallback);
* int rowCount = countCallback.getRowCount();</pre>
*
* @author Rod Johnson
* @since May 3, 2001
*/
public class RowCountCallbackHandler implements RowCallbackHandler {
/** Rows we've seen so far */
private int rowCount;
/** Columns we've seen so far */
private int columnCount;
/**
* Indexed from 0. Type (as in java.sql.Types) for the columns
* as returned by ResultSetMetaData object.
*/
private int[] columnTypes;
/**
* Indexed from 0. Column name as returned by ResultSetMetaData object.
*/
private String[] columnNames;
/**
* Implementation of ResultSetCallbackHandler.
* Work out column size if this is the first row, otherwise just count rows.
* <p>Subclasses can perform custom extraction or processing
* by overriding the <code>processRow(ResultSet, int)</code> method.
* @see #processRow(java.sql.ResultSet, int)
*/
public final void processRow(ResultSet rs) throws SQLException {
if (this.rowCount == 0) {
ResultSetMetaData rsmd = rs.getMetaData();
this.columnCount = rsmd.getColumnCount();
this.columnTypes = new int[this.columnCount];
this.columnNames = new String[this.columnCount];
for (int i = 0; i < this.columnCount; i++) {
this.columnTypes[i] = rsmd.getColumnType(i + 1);
this.columnNames[i] = JdbcUtils.lookupColumnName(rsmd, i + 1);
}
// could also get column names
}
processRow(rs, this.rowCount++);
}
/**
* Subclasses may override this to perform custom extraction
* or processing. This class's implementation does nothing.
* @param rs ResultSet to extract data from. This method is
* invoked for each row
* @param rowNum number of the current row (starting from 0)
*/
protected void processRow(ResultSet rs, int rowNum) throws SQLException {
}
/**
* Return the types of the columns as java.sql.Types constants
* Valid after processRow is invoked the first time.
* @return the types of the columns as java.sql.Types constants.
* <b>Indexed from 0 to n-1.</b>
*/
public final int[] getColumnTypes() {
return columnTypes;
}
/**
* Return the names of the columns.
* Valid after processRow is invoked the first time.
* @return the names of the columns.
* <b>Indexed from 0 to n-1.</b>
*/
public final String[] getColumnNames() {
return columnNames;
}
/**
* Return the row count of this ResultSet
* Only valid after processing is complete
* @return the number of rows in this ResultSet
*/
public final int getRowCount() {
return rowCount;
}
/**
* Return the number of columns in this result set.
* Valid once we've seen the first row,
* so subclasses can use it during processing
* @return the number of columns in this result set
*/
public final int getColumnCount() {
return columnCount;
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* An interface used by {@link JdbcTemplate} for mapping rows of a
* {@link java.sql.ResultSet} on a per-row basis. Implementations of this
* interface perform the actual work of mapping each row to a result object,
* but don't need to worry about exception handling.
* {@link java.sql.SQLException SQLExceptions} will be caught and handled
* by the calling JdbcTemplate.
*
* <p>Typically used either for {@link JdbcTemplate}'s query methods
* or for out parameters of stored procedures. RowMapper objects are
* typically stateless and thus reusable; they are an ideal choice for
* implementing row-mapping logic in a single place.
*
* <p>Alternatively, consider subclassing
* {@link org.springframework.jdbc.object.MappingSqlQuery} from the
* <code>jdbc.object</code> package: Instead of working with separate
* JdbcTemplate and RowMapper objects, you can build executable query
* objects (containing row-mapping logic) in that style.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @see JdbcTemplate
* @see RowCallbackHandler
* @see ResultSetExtractor
* @see org.springframework.jdbc.object.MappingSqlQuery
*/
public interface RowMapper {
/**
* Implementations must implement this method to map each row of data
* in the ResultSet. This method should not call <code>next()</code> on
* the ResultSet; it is only supposed to map values of the current row.
* @param rs the ResultSet to map (pre-initialized for the current row)
* @param rowNum the number of the current row
* @return the result object for the current row
* @throws SQLException if a SQLException is encountered getting
* column values (that is, there's no need to catch SQLException)
*/
Object mapRow(ResultSet rs, int rowNum) throws SQLException;
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.util.Assert;
/**
* Adapter implementation of the ResultSetExtractor interface that delegates
* to a RowMapper which is supposed to create an object for each row.
* Each object is added to the results List of this ResultSetExtractor.
*
* <p>Useful for the typical case of one object per row in the database table.
* The number of entries in the results list will match the number of rows.
*
* <p>Note that a RowMapper object is typically stateless and thus reusable;
* just the RowMapperResultSetExtractor adapter is stateful.
*
* <p>A usage example with JdbcTemplate:
*
* <pre class="code">JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); // reusable object
* RowMapper rowMapper = new UserRowMapper(); // reusable object
*
* List allUsers = (List) jdbcTemplate.query(
* "select * from user",
* new RowMapperResultSetExtractor(rowMapper, 10));
*
* User user = (User) jdbcTemplate.queryForObject(
* "select * from user where id=?", new Object[] {id},
* new RowMapperResultSetExtractor(rowMapper, 1));</pre>
*
* <p>Alternatively, consider subclassing MappingSqlQuery from the <code>jdbc.object</code>
* package: Instead of working with separate JdbcTemplate and RowMapper objects,
* you can have executable query objects (containing row-mapping logic) there.
*
* @author Juergen Hoeller
* @since 1.0.2
* @see RowMapper
* @see JdbcTemplate
* @see org.springframework.jdbc.object.MappingSqlQuery
*/
public class RowMapperResultSetExtractor implements ResultSetExtractor {
private final RowMapper rowMapper;
private final int rowsExpected;
/**
* Create a new RowMapperResultSetExtractor.
* @param rowMapper the RowMapper which creates an object for each row
*/
public RowMapperResultSetExtractor(RowMapper rowMapper) {
this(rowMapper, 0);
}
/**
* Create a new RowMapperResultSetExtractor.
* @param rowMapper the RowMapper which creates an object for each row
* @param rowsExpected the number of expected rows
* (just used for optimized collection handling)
*/
public RowMapperResultSetExtractor(RowMapper rowMapper, int rowsExpected) {
Assert.notNull(rowMapper, "RowMapper is required");
this.rowMapper = rowMapper;
this.rowsExpected = rowsExpected;
}
public Object extractData(ResultSet rs) throws SQLException {
List results = (this.rowsExpected > 0 ? new ArrayList(this.rowsExpected) : new ArrayList());
int rowNum = 0;
while (rs.next()) {
results.add(this.rowMapper.mapRow(rs, rowNum++));
}
return results;
}
}

View File

@ -0,0 +1,185 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.springframework.dao.TypeMismatchDataAccessException;
import org.springframework.jdbc.IncorrectResultSetColumnCountException;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.util.NumberUtils;
/**
* {@link RowMapper} implementation that converts a single column into a single
* result value per row. Expects to operate on a <code>java.sql.ResultSet</code>
* that just contains a single column.
*
* <p>The type of the result value for each row can be specified. The value
* for the single column will be extracted from the <code>ResultSet</code>
* and converted into the specified target type.
*
* @author Juergen Hoeller
* @since 1.2
* @see JdbcTemplate#queryForList(String, Class)
* @see JdbcTemplate#queryForObject(String, Class)
*/
public class SingleColumnRowMapper implements RowMapper {
private Class requiredType;
/**
* Create a new SingleColumnRowMapper.
* @see #setRequiredType
*/
public SingleColumnRowMapper() {
}
/**
* Create a new SingleColumnRowMapper.
* @param requiredType the type that each result object is expected to match
*/
public SingleColumnRowMapper(Class requiredType) {
this.requiredType = requiredType;
}
/**
* Set the type that each result object is expected to match.
* <p>If not specified, the column value will be exposed as
* returned by the JDBC driver.
*/
public void setRequiredType(Class requiredType) {
this.requiredType = requiredType;
}
/**
* Extract a value for the single column in the current row.
* <p>Validates that there is only one column selected,
* then delegates to <code>getColumnValue()</code> and also
* <code>convertValueToRequiredType</code>, if necessary.
* @see java.sql.ResultSetMetaData#getColumnCount()
* @see #getColumnValue(java.sql.ResultSet, int, Class)
* @see #convertValueToRequiredType(Object, Class)
*/
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
// Validate column count.
ResultSetMetaData rsmd = rs.getMetaData();
int nrOfColumns = rsmd.getColumnCount();
if (nrOfColumns != 1) {
throw new IncorrectResultSetColumnCountException(1, nrOfColumns);
}
// Extract column value from JDBC ResultSet.
Object result = getColumnValue(rs, 1, this.requiredType);
if (result != null && this.requiredType != null && !this.requiredType.isInstance(result)) {
// Extracted value does not match already: try to convert it.
try {
return convertValueToRequiredType(result, this.requiredType);
}
catch (IllegalArgumentException ex) {
throw new TypeMismatchDataAccessException(
"Type mismatch affecting row number " + rowNum + " and column type '" +
rsmd.getColumnTypeName(1) + "': " + ex.getMessage());
}
}
return result;
}
/**
* Retrieve a JDBC object value for the specified column.
* <p>The default implementation calls
* {@link JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)}.
* If no required type has been specified, this method delegates to
* <code>getColumnValue(rs, index)</code>, which basically calls
* <code>ResultSet.getObject(index)</code> but applies some additional
* default conversion to appropriate value types.
* @param rs is the ResultSet holding the data
* @param index is the column index
* @param requiredType the type that each result object is expected to match
* (or <code>null</code> if none specified)
* @return the Object value
* @throws SQLException in case of extraction failure
* @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)
* @see #getColumnValue(java.sql.ResultSet, int)
*/
protected Object getColumnValue(ResultSet rs, int index, Class requiredType) throws SQLException {
if (requiredType != null) {
return JdbcUtils.getResultSetValue(rs, index, requiredType);
}
else {
// No required type specified -> perform default extraction.
return getColumnValue(rs, index);
}
}
/**
* Retrieve a JDBC object value for the specified column, using the most
* appropriate value type. Called if no required type has been specified.
* <p>The default implementation delegates to <code>JdbcUtils.getResultSetValue()</code>,
* which uses the <code>ResultSet.getObject(index)</code> method. Additionally,
* it includes a "hack" to get around Oracle returning a non-standard object for
* their TIMESTAMP datatype. See the <code>JdbcUtils#getResultSetValue()</code>
* javadoc for details.
* @param rs is the ResultSet holding the data
* @param index is the column index
* @return the Object value
* @throws SQLException in case of extraction failure
* @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue(java.sql.ResultSet, int)
*/
protected Object getColumnValue(ResultSet rs, int index) throws SQLException {
return JdbcUtils.getResultSetValue(rs, index);
}
/**
* Convert the given column value to the specified required type.
* Only called if the extracted column value does not match already.
* <p>If the required type is String, the value will simply get stringified
* via <code>toString()</code>. In case of a Number, the value will be
* converted into a Number, either through number conversion or through
* String parsing (depending on the value type).
* @param value the column value as extracted from <code>getColumnValue()</code>
* (never <code>null</code>)
* @param requiredType the type that each result object is expected to match
* (never <code>null</code>)
* @return the converted value
* @see #getColumnValue(java.sql.ResultSet, int, Class)
*/
protected Object convertValueToRequiredType(Object value, Class requiredType) {
if (String.class.equals(requiredType)) {
return value.toString();
}
else if (Number.class.isAssignableFrom(requiredType)) {
if (value instanceof Number) {
// Convert original Number to target Number class.
return NumberUtils.convertNumberToTargetClass(((Number) value), requiredType);
}
else {
// Convert stringified value to target Number class.
return NumberUtils.parseNumber(value.toString(), requiredType);
}
}
else {
throw new IllegalArgumentException(
"Value [" + value + "] is of type [" + value.getClass().getName() +
"] and cannot be converted to required type [" + requiredType.getName() + "]");
}
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
/**
* Subclass of {@link SqlOutParameter} to represent an INOUT parameter.
* Will return <code>true</code> for SqlParameter's {@link #isInputValueProvided}
* test, in contrast to a standard SqlOutParameter.
*
* <p>Output parameters - like all stored procedure parameters -
* must have names.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.0
*/
public class SqlInOutParameter extends SqlOutParameter {
/**
* Create a new SqlInOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
*/
public SqlInOutParameter(String name, int sqlType) {
super(name, sqlType);
}
/**
* Create a new SqlInOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param scale the number of digits after the decimal point
* (for DECIMAL and NUMERIC types)
*/
public SqlInOutParameter(String name, int sqlType, int scale) {
super(name, sqlType, scale);
}
/**
* Create a new SqlInOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param typeName the type name of the parameter (optional)
*/
public SqlInOutParameter(String name, int sqlType, String typeName) {
super(name, sqlType, typeName);
}
/**
* Create a new SqlInOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param typeName the type name of the parameter (optional)
* @param sqlReturnType custom value handler for complex type (optional)
*/
public SqlInOutParameter(String name, int sqlType, String typeName, SqlReturnType sqlReturnType) {
super(name, sqlType, typeName, sqlReturnType);
}
/**
* Create a new SqlInOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param rse ResultSetExtractor to use for parsing the ResultSet
*/
public SqlInOutParameter(String name, int sqlType, ResultSetExtractor rse) {
super(name, sqlType, rse);
}
/**
* Create a new SqlInOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param rch RowCallbackHandler to use for parsing the ResultSet
*/
public SqlInOutParameter(String name, int sqlType, RowCallbackHandler rch) {
super(name, sqlType, rch);
}
/**
* Create a new SqlInOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param rm RowMapper to use for parsing the ResultSet
*/
public SqlInOutParameter(String name, int sqlType, RowMapper rm) {
super(name, sqlType, rm);
}
/**
* This implementation always returns <code>true</code>.
*/
public boolean isInputValueProvided() {
return true;
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
/**
* Subclass of SqlParameter to represent an output parameter.
* No additional properties: instanceof will be used to check
* for such types.
*
* <p>Output parameters - like all stored procedure parameters -
* must have names.
*
* @author Rod Johnson
* @author Thomas Risberg
* @author Juergen Hoeller
* @see SqlReturnResultSet
* @see SqlInOutParameter
*/
public class SqlOutParameter extends ResultSetSupportingSqlParameter {
private SqlReturnType sqlReturnType;
/**
* Create a new SqlOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
*/
public SqlOutParameter(String name, int sqlType) {
super(name, sqlType);
}
/**
* Create a new SqlOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param scale the number of digits after the decimal point
* (for DECIMAL and NUMERIC types)
*/
public SqlOutParameter(String name, int sqlType, int scale) {
super(name, sqlType, scale);
}
/**
* Create a new SqlOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param typeName the type name of the parameter (optional)
*/
public SqlOutParameter(String name, int sqlType, String typeName) {
super(name, sqlType, typeName);
}
/**
* Create a new SqlOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param typeName the type name of the parameter (optional)
* @param sqlReturnType custom value handler for complex type (optional)
*/
public SqlOutParameter(String name, int sqlType, String typeName, SqlReturnType sqlReturnType) {
super(name, sqlType, typeName);
this.sqlReturnType = sqlReturnType;
}
/**
* Create a new SqlOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param rse ResultSetExtractor to use for parsing the ResultSet
*/
public SqlOutParameter(String name, int sqlType, ResultSetExtractor rse) {
super(name, sqlType, rse);
}
/**
* Create a new SqlOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param rch RowCallbackHandler to use for parsing the ResultSet
*/
public SqlOutParameter(String name, int sqlType, RowCallbackHandler rch) {
super(name, sqlType, rch);
}
/**
* Create a new SqlOutParameter.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to java.sql.Types
* @param rm RowMapper to use for parsing the ResultSet
*/
public SqlOutParameter(String name, int sqlType, RowMapper rm) {
super(name, sqlType, rm);
}
/**
* Return the custom return type, if any.
*/
public SqlReturnType getSqlReturnType() {
return this.sqlReturnType;
}
/**
* Return whether this parameter holds a custom return type.
*/
public boolean isReturnTypeSupported() {
return (this.sqlReturnType != null);
}
}

View File

@ -0,0 +1,190 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.util.LinkedList;
import java.util.List;
import org.springframework.util.Assert;
/**
* Object to represent a SQL parameter definition.
*
* <p>Parameters may be anonymous, in which case "name" is <code>null</code>.
* However, all parameters must define a SQL type according to {@link java.sql.Types}.
*
* @author Rod Johnson
* @author Thomas Risberg
* @author Juergen Hoeller
* @see java.sql.Types
*/
public class SqlParameter {
/** The name of the parameter, if any */
private String name;
/** SQL type constant from <code>java.sql.Types</code> */
private final int sqlType;
/** Used for types that are user-named like: STRUCT, DISTINCT, JAVA_OBJECT, named array types */
private String typeName;
/** The scale to apply in case of a NUMERIC or DECIMAL type, if any */
private Integer scale;
/**
* Create a new anonymous SqlParameter, supplying the SQL type.
* @param sqlType SQL type of the parameter according to <code>java.sql.Types</code>
*/
public SqlParameter(int sqlType) {
this.sqlType = sqlType;
}
/**
* Create a new anonymous SqlParameter, supplying the SQL type.
* @param sqlType SQL type of the parameter according to <code>java.sql.Types</code>
* @param typeName the type name of the parameter (optional)
*/
public SqlParameter(int sqlType, String typeName) {
this.sqlType = sqlType;
this.typeName = typeName;
}
/**
* Create a new anonymous SqlParameter, supplying the SQL type.
* @param sqlType SQL type of the parameter according to <code>java.sql.Types</code>
* @param scale the number of digits after the decimal point
* (for DECIMAL and NUMERIC types)
*/
public SqlParameter(int sqlType, int scale) {
this.sqlType = sqlType;
this.scale = new Integer(scale);
}
/**
* Create a new SqlParameter, supplying name and SQL type.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to <code>java.sql.Types</code>
*/
public SqlParameter(String name, int sqlType) {
this.name = name;
this.sqlType = sqlType;
}
/**
* Create a new SqlParameter, supplying name and SQL type.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to <code>java.sql.Types</code>
* @param typeName the type name of the parameter (optional)
*/
public SqlParameter(String name, int sqlType, String typeName) {
this.name = name;
this.sqlType = sqlType;
this.typeName = typeName;
}
/**
* Create a new SqlParameter, supplying name and SQL type.
* @param name name of the parameter, as used in input and output maps
* @param sqlType SQL type of the parameter according to <code>java.sql.Types</code>
* @param scale the number of digits after the decimal point
* (for DECIMAL and NUMERIC types)
*/
public SqlParameter(String name, int sqlType, int scale) {
this.name = name;
this.sqlType = sqlType;
this.scale = new Integer(scale);
}
/**
* Copy constructor.
* @param otherParam the SqlParameter object to copy from
*/
public SqlParameter(SqlParameter otherParam) {
Assert.notNull(otherParam, "SqlParameter object must not be null");
this.name = otherParam.name;
this.sqlType = otherParam.sqlType;
this.typeName = otherParam.typeName;
this.scale = otherParam.scale;
}
/**
* Return the name of the parameter.
*/
public String getName() {
return this.name;
}
/**
* Return the SQL type of the parameter.
*/
public int getSqlType() {
return this.sqlType;
}
/**
* Return the type name of the parameter, if any.
*/
public String getTypeName() {
return this.typeName;
}
/**
* Return the scale of the parameter, if any.
*/
public Integer getScale() {
return this.scale;
}
/**
* Return whether this parameter holds input values that should be set
* before execution even if they are <code>null</code>.
* <p>This implementation always returns <code>true</code>.
*/
public boolean isInputValueProvided() {
return true;
}
/**
* Return whether this parameter is an implicit return parameter used during the
* results preocessing of the CallableStatement.getMoreResults/getUpdateCount.
* <p>This implementation always returns <code>false</code>.
*/
public boolean isResultsParameter() {
return false;
}
/**
* Convert a list of JDBC types, as defined in <code>java.sql.Types</code>,
* to a List of SqlParameter objects as used in this package.
*/
public static List sqlTypesToAnonymousParameterList(int[] types) {
List result = new LinkedList();
if (types != null) {
for (int i = 0; i < types.length; i++) {
result.add(new SqlParameter(types[i]));
}
}
return result;
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
/**
* Object to represent a SQL parameter value, including parameter metadata
* such as the SQL type and the scale for numeric values.
*
* <p>Designed for use with {@link JdbcTemplate}'s operations that take an array of
* argument values: Each such argument value may be a <code>SqlParameterValue</code>,
* indicating the SQL type (and optionally the scale) instead of letting the
* template guess a default type. Note that this only applies to the operations with
* a 'plain' argument array, not to the overloaded variants with an explicit type array.
*
* @author Juergen Hoeller
* @since 2.0.5
* @see java.sql.Types
* @see JdbcTemplate#query(String, Object[], ResultSetExtractor)
* @see JdbcTemplate#query(String, Object[], RowCallbackHandler)
* @see JdbcTemplate#query(String, Object[], RowMapper)
* @see JdbcTemplate#update(String, Object[])
*/
public class SqlParameterValue extends SqlParameter {
private final Object value;
/**
* Create a new SqlParameterValue, supplying the SQL type.
* @param sqlType SQL type of the parameter according to <code>java.sql.Types</code>
* @param value the value object
*/
public SqlParameterValue(int sqlType, Object value) {
super(sqlType);
this.value = value;
}
/**
* Create a new SqlParameterValue, supplying the SQL type.
* @param sqlType SQL type of the parameter according to <code>java.sql.Types</code>
* @param typeName the type name of the parameter (optional)
* @param value the value object
*/
public SqlParameterValue(int sqlType, String typeName, Object value) {
super(sqlType, typeName);
this.value = value;
}
/**
* Create a new SqlParameterValue, supplying the SQL type.
* @param sqlType SQL type of the parameter according to <code>java.sql.Types</code>
* @param scale the number of digits after the decimal point
* (for DECIMAL and NUMERIC types)
* @param value the value object
*/
public SqlParameterValue(int sqlType, int scale, Object value) {
super(sqlType, scale);
this.value = value;
}
/**
* Create a new SqlParameterValue based on the given SqlParameter declaration.
* @param declaredParam the declared SqlParameter to define a value for
* @param value the value object
*/
public SqlParameterValue(SqlParameter declaredParam, Object value) {
super(declaredParam);
this.value = value;
}
/**
* Return the value object that this parameter value holds.
*/
public Object getValue() {
return this.value;
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
/**
* Interface to be implemented by objects that can provide SQL strings.
*
* <p>Typically implemented by PreparedStatementCreators, CallableStatementCreators
* and StatementCallbacks that want to expose the SQL they use to create their
* statements, to allow for better contextual information in case of exceptions.
*
* @author Juergen Hoeller
* @since 16.03.2004
* @see PreparedStatementCreator
* @see CallableStatementCreator
* @see StatementCallback
*/
public interface SqlProvider {
/**
* Return the SQL string for this object, i.e.
* typically the SQL used for creating statements.
* @return the SQL string, or <code>null</code>
*/
String getSql();
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
/**
* Represents a returned {@link java.sql.ResultSet} from a stored procedure call.
*
* <p>A {@link ResultSetExtractor}, {@link RowCallbackHandler} or {@link RowMapper}
* must be provided to handle any returned rows.
*
* <p>Returned {@link java.sql.ResultSet ResultSets} - like all stored procedure
* parameters - <b>must</b> have names.
*
* @author Thomas Risberg
* @author Juergen Hoeller
*/
public class SqlReturnResultSet extends ResultSetSupportingSqlParameter {
/**
* Create a new instance of the {@link SqlReturnResultSet} class.
* @param name name of the parameter, as used in input and output maps
* @param extractor ResultSetExtractor to use for parsing the {@link java.sql.ResultSet}
*/
public SqlReturnResultSet(String name, ResultSetExtractor extractor) {
super(name, 0, extractor);
}
/**
* Create a new instance of the {@link SqlReturnResultSet} class.
* @param name name of the parameter, as used in input and output maps
* @param handler RowCallbackHandler to use for parsing the {@link java.sql.ResultSet}
*/
public SqlReturnResultSet(String name, RowCallbackHandler handler) {
super(name, 0, handler);
}
/**
* Create a new instance of the {@link SqlReturnResultSet} class.
* @param name name of the parameter, as used in input and output maps
* @param mapper RowMapper to use for parsing the {@link java.sql.ResultSet}
*/
public SqlReturnResultSet(String name, RowMapper mapper) {
super(name, 0, mapper);
}
/**
* Return whether this parameter is an implicit return parameter used during the
* results preocessing of the CallableStatement.getMoreResults/getUpdateCount.
* <p>This implementation always returns <code>true</code>.
*/
public boolean isResultsParameter() {
return true;
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.CallableStatement;
import java.sql.SQLException;
/**
* Interface to be implemented for retrieving values for more complex database-specific
* types not supported by the standard <code>CallableStatement.getObject</code> method.
*
* <p>Implementations perform the actual work of getting the actual values. They must
* implement the callback method <code>getTypeValue</code> which can throw SQLExceptions
* that will be caught and translated by the calling code. This callback method has
* access to the underlying Connection via the given CallableStatement object, if that
* should be needed to create any database-specific objects.
*
* @author Thomas Risberg
* @since 1.1
* @see java.sql.Types
* @see java.sql.CallableStatement#getObject
* @see org.springframework.jdbc.object.StoredProcedure#execute(java.util.Map)
*/
public interface SqlReturnType {
/**
* Constant that indicates an unknown (or unspecified) SQL type.
* Passed into setTypeValue if the original operation method does
* not specify a SQL type.
* @see java.sql.Types
* @see JdbcOperations#update(String, Object[])
*/
int TYPE_UNKNOWN = Integer.MIN_VALUE;
/**
* Get the type value from the specific object.
* @param cs the CallableStatement to operate on
* @param paramIndex the index of the parameter for which we need to set the value
* @param sqlType SQL type of the parameter we are setting
* @param typeName the type name of the parameter
* @return the target value
* @throws SQLException if a SQLException is encountered setting parameter values
* (that is, there's no need to catch SQLException)
* @see java.sql.Types
* @see java.sql.CallableStatement#getObject
*/
Object getTypeValue(CallableStatement cs, int paramIndex, int sqlType, String typeName)
throws SQLException;
}

View File

@ -0,0 +1,41 @@
package org.springframework.jdbc.core;
import java.sql.Types;
/**
* Represents a returned update count from a stored procedure call.
*
* <p>Returned update counts - like all stored procedure
* parameters - <b>must</b> have names.
*
* @author Thomas Risberg
*/
public class SqlReturnUpdateCount extends SqlParameter {
/**
* Create a new instance of the {@link SqlReturnUpdateCount} class.
* @param name name of the parameter, as used in input and output maps
*/
public SqlReturnUpdateCount(String name) {
super(name, Types.INTEGER);
}
/**
* Return whether this parameter holds input values that should be set
* before execution even if they are <code>null</code>.
* <p>This implementation always returns <code>false</code>.
*/
public boolean isInputValueProvided() {
return false;
}
/**
* Return whether this parameter is an implicit return parameter used during the
* results preocessing of the CallableStatement.getMoreResults/getUpdateCount.
* <p>This implementation always returns <code>true</code>.
*/
public boolean isResultsParameter() {
return true;
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.rowset.CachedRowSet;
import com.sun.rowset.CachedRowSetImpl;
import org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSet;
/**
* ResultSetExtractor implementation that returns a Spring SqlRowSet
* representation for each given ResultSet.
*
* <p>The default implementation uses a standard JDBC CachedRowSet underneath.
* This means that JDBC RowSet support needs to be available at runtime:
* by default, Sun's <code>com.sun.rowset.CachedRowSetImpl</code> class is
* used, which is part of JDK 1.5+ and also available separately as part of
* Sun's JDBC RowSet Implementations download (rowset.jar).
*
* @author Juergen Hoeller
* @since 1.2
* @see #newCachedRowSet
* @see org.springframework.jdbc.support.rowset.SqlRowSet
* @see JdbcTemplate#queryForRowSet(String)
* @see javax.sql.rowset.CachedRowSet
*/
public class SqlRowSetResultSetExtractor implements ResultSetExtractor {
public Object extractData(ResultSet rs) throws SQLException {
return createSqlRowSet(rs);
}
/**
* Create a SqlRowSet that wraps the given ResultSet,
* representing its data in a disconnected fashion.
* <p>This implementation creates a Spring ResultSetWrappingSqlRowSet
* instance that wraps a standard JDBC CachedRowSet instance.
* Can be overridden to use a different implementation.
* @param rs the original ResultSet (connected)
* @return the disconnected SqlRowSet
* @throws SQLException if thrown by JDBC methods
* @see #newCachedRowSet
* @see org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet
*/
protected SqlRowSet createSqlRowSet(ResultSet rs) throws SQLException {
CachedRowSet rowSet = newCachedRowSet();
rowSet.populate(rs);
return new ResultSetWrappingSqlRowSet(rowSet);
}
/**
* Create a new CachedRowSet instance, to be populated by
* the <code>createSqlRowSet</code> implementation.
* <p>The default implementation creates a new instance of
* Sun's <code>com.sun.rowset.CachedRowSetImpl</code> class,
* which is part of JDK 1.5+ and also available separately
* as part of Sun's JDBC RowSet Implementations download.
* @return a new CachedRowSet instance
* @throws SQLException if thrown by JDBC methods
* @see #createSqlRowSet
* @see com.sun.rowset.CachedRowSetImpl
*/
protected CachedRowSet newCachedRowSet() throws SQLException {
return new CachedRowSetImpl();
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.jdbc.support.JdbcUtils;
/**
* Interface to be implemented for setting values for more complex database-specific
* types not supported by the standard <code>setObject</code> method. This is
* effectively an extended variant of {@link org.springframework.jdbc.support.SqlValue}.
*
* <p>Implementations perform the actual work of setting the actual values. They must
* implement the callback method <code>setTypeValue</code> which can throw SQLExceptions
* that will be caught and translated by the calling code. This callback method has
* access to the underlying Connection via the given PreparedStatement object, if that
* should be needed to create any database-specific objects.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 1.1
* @see java.sql.Types
* @see java.sql.PreparedStatement#setObject
* @see JdbcOperations#update(String, Object[], int[])
* @see org.springframework.jdbc.support.SqlValue
*/
public interface SqlTypeValue {
/**
* Constant that indicates an unknown (or unspecified) SQL type.
* Passed into <code>setTypeValue</code> if the original operation method
* does not specify a SQL type.
* @see java.sql.Types
* @see JdbcOperations#update(String, Object[])
*/
int TYPE_UNKNOWN = JdbcUtils.TYPE_UNKNOWN;
/**
* Set the type value on the given PreparedStatement.
* @param ps the PreparedStatement to work on
* @param paramIndex the index of the parameter for which we need to set the value
* @param sqlType SQL type of the parameter we are setting
* @param typeName the type name of the parameter (optional)
* @throws SQLException if a SQLException is encountered while setting parameter values
* @see java.sql.Types
* @see java.sql.PreparedStatement#setObject
*/
void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) throws SQLException;
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.sql.SQLException;
import java.sql.Statement;
import org.springframework.dao.DataAccessException;
/**
* Generic callback interface for code that operates on a JDBC Statement.
* Allows to execute any number of operations on a single Statement,
* for example a single <code>executeUpdate</code> call or repeated
* <code>executeUpdate</code> calls with varying SQL.
*
* <p>Used internally by JdbcTemplate, but also useful for application code.
*
* @author Juergen Hoeller
* @since 16.03.2004
* @see JdbcTemplate#execute(StatementCallback)
*/
public interface StatementCallback {
/**
* Gets called by <code>JdbcTemplate.execute</code> with an active JDBC
* Statement. Does not need to care about closing the Statement or the
* Connection, or about handling transactions: this will all be handled
* by Spring's JdbcTemplate.
*
* <p><b>NOTE:</b> Any ResultSets opened should be closed in finally blocks
* within the callback implementation. Spring will close the Statement
* object after the callback returned, but this does not necessarily imply
* that the ResultSet resources will be closed: the Statement objects might
* get pooled by the connection pool, with <code>close</code> calls only
* returning the object to the pool but not physically closing the resources.
*
* <p>If called without a thread-bound JDBC transaction (initiated by
* DataSourceTransactionManager), the code will simply get executed on the
* JDBC connection with its transactional semantics. If JdbcTemplate is
* configured to use a JTA-aware DataSource, the JDBC connection and thus
* the callback code will be transactional if a JTA transaction is active.
*
* <p>Allows for returning a result object created within the callback, i.e.
* a domain object or a collection of domain objects. Note that there's
* special support for single step actions: see JdbcTemplate.queryForObject etc.
* A thrown RuntimeException is treated as application exception, it gets
* propagated to the caller of the template.
*
* @param stmt active JDBC Statement
* @return a result object, or <code>null</code> if none
* @throws SQLException if thrown by a JDBC method, to be auto-converted
* to a DataAccessException by a SQLExceptionTranslator
* @throws DataAccessException in case of custom exceptions
* @see JdbcTemplate#queryForObject(String, Class)
* @see JdbcTemplate#queryForRowSet(String)
*/
Object doInStatement(Statement stmt) throws SQLException, DataAccessException;
}

View File

@ -0,0 +1,409 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.support.SqlValue;
/**
* Utility methods for PreparedStatementSetter/Creator and CallableStatementCreator
* implementations, providing sophisticated parameter management (including support
* for LOB values).
*
* <p>Used by PreparedStatementCreatorFactory and CallableStatementCreatorFactory,
* but also available for direct use in custom setter/creator implementations.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 1.1
* @see PreparedStatementSetter
* @see PreparedStatementCreator
* @see CallableStatementCreator
* @see PreparedStatementCreatorFactory
* @see CallableStatementCreatorFactory
* @see SqlParameter
* @see SqlTypeValue
* @see org.springframework.jdbc.core.support.SqlLobValue
*/
public abstract class StatementCreatorUtils {
private static final Log logger = LogFactory.getLog(StatementCreatorUtils.class);
private static Map javaTypeToSqlTypeMap = new HashMap(32);
static {
/* JDBC 3.0 only - not compatible with e.g. MySQL at present
javaTypeToSqlTypeMap.put(boolean.class, new Integer(Types.BOOLEAN));
javaTypeToSqlTypeMap.put(Boolean.class, new Integer(Types.BOOLEAN));
*/
javaTypeToSqlTypeMap.put(byte.class, new Integer(Types.TINYINT));
javaTypeToSqlTypeMap.put(Byte.class, new Integer(Types.TINYINT));
javaTypeToSqlTypeMap.put(short.class, new Integer(Types.SMALLINT));
javaTypeToSqlTypeMap.put(Short.class, new Integer(Types.SMALLINT));
javaTypeToSqlTypeMap.put(int.class, new Integer(Types.INTEGER));
javaTypeToSqlTypeMap.put(Integer.class, new Integer(Types.INTEGER));
javaTypeToSqlTypeMap.put(long.class, new Integer(Types.BIGINT));
javaTypeToSqlTypeMap.put(Long.class, new Integer(Types.BIGINT));
javaTypeToSqlTypeMap.put(BigInteger.class, new Integer(Types.BIGINT));
javaTypeToSqlTypeMap.put(float.class, new Integer(Types.FLOAT));
javaTypeToSqlTypeMap.put(Float.class, new Integer(Types.FLOAT));
javaTypeToSqlTypeMap.put(double.class, new Integer(Types.DOUBLE));
javaTypeToSqlTypeMap.put(Double.class, new Integer(Types.DOUBLE));
javaTypeToSqlTypeMap.put(BigDecimal.class, new Integer(Types.DECIMAL));
javaTypeToSqlTypeMap.put(java.sql.Date.class, new Integer(Types.DATE));
javaTypeToSqlTypeMap.put(java.sql.Time.class, new Integer(Types.TIME));
javaTypeToSqlTypeMap.put(java.sql.Timestamp.class, new Integer(Types.TIMESTAMP));
javaTypeToSqlTypeMap.put(Blob.class, new Integer(Types.BLOB));
javaTypeToSqlTypeMap.put(Clob.class, new Integer(Types.CLOB));
}
/**
* Derive a default SQL type from the given Java type.
* @param javaType the Java type to translate
* @return the corresponding SQL type, or <code>null</code> if none found
*/
public static int javaTypeToSqlParameterType(Class javaType) {
Integer sqlType = (Integer) javaTypeToSqlTypeMap.get(javaType);
if (sqlType != null) {
return sqlType.intValue();
}
if (Number.class.isAssignableFrom(javaType)) {
return Types.NUMERIC;
}
if (isStringValue(javaType)) {
return Types.VARCHAR;
}
if (isDateValue(javaType) || Calendar.class.isAssignableFrom(javaType)) {
return Types.TIMESTAMP;
}
return SqlTypeValue.TYPE_UNKNOWN;
}
/**
* Set the value for a parameter. The method used is based on the SQL type
* of the parameter and we can handle complex types like arrays and LOBs.
* @param ps the prepared statement or callable statement
* @param paramIndex index of the parameter we are setting
* @param param the parameter as it is declared including type
* @param inValue the value to set
* @throws SQLException if thrown by PreparedStatement methods
*/
public static void setParameterValue(
PreparedStatement ps, int paramIndex, SqlParameter param, Object inValue)
throws SQLException {
setParameterValueInternal(ps, paramIndex, param.getSqlType(), param.getTypeName(), param.getScale(), inValue);
}
/**
* Set the value for a parameter. The method used is based on the SQL type
* of the parameter and we can handle complex types like arrays and LOBs.
* @param ps the prepared statement or callable statement
* @param paramIndex index of the parameter we are setting
* @param sqlType the SQL type of the parameter
* @param inValue the value to set (plain value or a SqlTypeValue)
* @throws SQLException if thrown by PreparedStatement methods
* @see SqlTypeValue
*/
public static void setParameterValue(
PreparedStatement ps, int paramIndex, int sqlType, Object inValue)
throws SQLException {
setParameterValueInternal(ps, paramIndex, sqlType, null, null, inValue);
}
/**
* Set the value for a parameter. The method used is based on the SQL type
* of the parameter and we can handle complex types like arrays and LOBs.
* @param ps the prepared statement or callable statement
* @param paramIndex index of the parameter we are setting
* @param sqlType the SQL type of the parameter
* @param typeName the type name of the parameter
* (optional, only used for SQL NULL and SqlTypeValue)
* @param inValue the value to set (plain value or a SqlTypeValue)
* @throws SQLException if thrown by PreparedStatement methods
* @see SqlTypeValue
*/
public static void setParameterValue(
PreparedStatement ps, int paramIndex, int sqlType, String typeName, Object inValue)
throws SQLException {
setParameterValueInternal(ps, paramIndex, sqlType, typeName, null, inValue);
}
/**
* Set the value for a parameter. The method used is based on the SQL type
* of the parameter and we can handle complex types like arrays and LOBs.
* @param ps the prepared statement or callable statement
* @param paramIndex index of the parameter we are setting
* @param sqlType the SQL type of the parameter
* @param typeName the type name of the parameter
* (optional, only used for SQL NULL and SqlTypeValue)
* @param scale the number of digits after the decimal point
* (for DECIMAL and NUMERIC types)
* @param inValue the value to set (plain value or a SqlTypeValue)
* @throws SQLException if thrown by PreparedStatement methods
* @see SqlTypeValue
*/
private static void setParameterValueInternal(
PreparedStatement ps, int paramIndex, int sqlType, String typeName, Integer scale, Object inValue)
throws SQLException {
String typeNameToUse = typeName;
int sqlTypeToUse = sqlType;
Object inValueToUse = inValue;
// override type info?
if (inValue instanceof SqlParameterValue) {
SqlParameterValue parameterValue = (SqlParameterValue) inValue;
if (logger.isDebugEnabled()) {
logger.debug("Overriding typeinfo with runtime info from SqlParameterValue: column index " + paramIndex +
", SQL type " + parameterValue.getSqlType() +
", Type name " + parameterValue.getTypeName());
}
if (parameterValue.getSqlType() != SqlTypeValue.TYPE_UNKNOWN) {
sqlTypeToUse = parameterValue.getSqlType();
}
if (parameterValue.getTypeName() != null) {
typeNameToUse = parameterValue.getTypeName();
}
inValueToUse = parameterValue.getValue();
}
if (logger.isTraceEnabled()) {
logger.trace("Setting SQL statement parameter value: column index " + paramIndex +
", parameter value [" + inValueToUse +
"], value class [" + (inValueToUse != null ? inValueToUse.getClass().getName() : "null") +
"], SQL type " + (sqlTypeToUse == SqlTypeValue.TYPE_UNKNOWN ? "unknown" : Integer.toString(sqlTypeToUse)));
}
if (inValueToUse == null) {
setNull(ps, paramIndex, sqlTypeToUse, typeNameToUse);
}
else {
setValue(ps, paramIndex, sqlTypeToUse, typeNameToUse, scale, inValueToUse);
}
}
private static void setNull(PreparedStatement ps, int paramIndex, int sqlType, String typeName) throws SQLException {
if (sqlType == SqlTypeValue.TYPE_UNKNOWN) {
boolean useSetObject = false;
sqlType = Types.NULL;
try {
DatabaseMetaData dbmd = ps.getConnection().getMetaData();
String databaseProductName = dbmd.getDatabaseProductName();
String jdbcDriverName = dbmd.getDriverName();
if (databaseProductName.startsWith("Informix") ||
jdbcDriverName.startsWith("Microsoft SQL Server")) {
useSetObject = true;
}
else if (databaseProductName.startsWith("DB2") ||
jdbcDriverName.startsWith("jConnect") ||
jdbcDriverName.startsWith("SQLServer")||
jdbcDriverName.startsWith("Apache Derby Embedded")) {
sqlType = Types.VARCHAR;
}
}
catch (Throwable ex) {
logger.debug("Could not check database or driver name", ex);
}
if (useSetObject) {
ps.setObject(paramIndex, null);
}
else {
ps.setNull(paramIndex, sqlType);
}
}
else if (typeName != null) {
ps.setNull(paramIndex, sqlType, typeName);
}
else {
ps.setNull(paramIndex, sqlType);
}
}
private static void setValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName,
Integer scale, Object inValue) throws SQLException {
if (inValue instanceof SqlTypeValue) {
((SqlTypeValue) inValue).setTypeValue(ps, paramIndex, sqlType, typeName);
}
else if (inValue instanceof SqlValue) {
((SqlValue) inValue).setValue(ps, paramIndex);
}
else if (sqlType == Types.VARCHAR || sqlType == Types.LONGVARCHAR ||
(sqlType == Types.CLOB && isStringValue(inValue.getClass()))) {
ps.setString(paramIndex, inValue.toString());
}
else if (sqlType == Types.DECIMAL || sqlType == Types.NUMERIC) {
if (inValue instanceof BigDecimal) {
ps.setBigDecimal(paramIndex, (BigDecimal) inValue);
}
else if (scale != null) {
ps.setObject(paramIndex, inValue, sqlType, scale.intValue());
}
else {
ps.setObject(paramIndex, inValue, sqlType);
}
}
else if (sqlType == Types.DATE) {
if (inValue instanceof java.util.Date) {
if (inValue instanceof java.sql.Date) {
ps.setDate(paramIndex, (java.sql.Date) inValue);
}
else {
ps.setDate(paramIndex, new java.sql.Date(((java.util.Date) inValue).getTime()));
}
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setDate(paramIndex, new java.sql.Date(cal.getTime().getTime()), cal);
}
else {
ps.setObject(paramIndex, inValue, Types.DATE);
}
}
else if (sqlType == Types.TIME) {
if (inValue instanceof java.util.Date) {
if (inValue instanceof java.sql.Time) {
ps.setTime(paramIndex, (java.sql.Time) inValue);
}
else {
ps.setTime(paramIndex, new java.sql.Time(((java.util.Date) inValue).getTime()));
}
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setTime(paramIndex, new java.sql.Time(cal.getTime().getTime()), cal);
}
else {
ps.setObject(paramIndex, inValue, Types.TIME);
}
}
else if (sqlType == Types.TIMESTAMP) {
if (inValue instanceof java.util.Date) {
if (inValue instanceof java.sql.Timestamp) {
ps.setTimestamp(paramIndex, (java.sql.Timestamp) inValue);
}
else {
ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime()));
}
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal);
}
else {
ps.setObject(paramIndex, inValue, Types.TIMESTAMP);
}
}
else if (sqlType == SqlTypeValue.TYPE_UNKNOWN) {
if (isStringValue(inValue.getClass())) {
ps.setString(paramIndex, inValue.toString());
}
else if (isDateValue(inValue.getClass())) {
ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime()));
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal);
}
else {
// Fall back to generic setObject call without SQL type specified.
ps.setObject(paramIndex, inValue);
}
}
else {
// Fall back to generic setObject call with SQL type specified.
ps.setObject(paramIndex, inValue, sqlType);
}
}
/**
* Check whether the given value can be treated as a String value.
*/
private static boolean isStringValue(Class inValueType) {
// Consider any CharSequence (including JDK 1.5's StringBuilder) as String.
return (CharSequence.class.isAssignableFrom(inValueType) ||
StringWriter.class.isAssignableFrom(inValueType));
}
/**
* Check whether the given value is a <code>java.util.Date</code>
* (but not one of the JDBC-specific subclasses).
*/
private static boolean isDateValue(Class inValueType) {
return (java.util.Date.class.isAssignableFrom(inValueType) &&
!(java.sql.Date.class.isAssignableFrom(inValueType) ||
java.sql.Time.class.isAssignableFrom(inValueType) ||
java.sql.Timestamp.class.isAssignableFrom(inValueType)));
}
/**
* Clean up all resources held by parameter values which were passed to an
* execute method. This is for example important for closing LOB values.
* @param paramValues parameter values supplied. May be <code>null</code>.
* @see DisposableSqlTypeValue#cleanup()
* @see org.springframework.jdbc.core.support.SqlLobValue#cleanup()
*/
public static void cleanupParameters(Object[] paramValues) {
if (paramValues != null) {
cleanupParameters(Arrays.asList(paramValues));
}
}
/**
* Clean up all resources held by parameter values which were passed to an
* execute method. This is for example important for closing LOB values.
* @param paramValues parameter values supplied. May be <code>null</code>.
* @see DisposableSqlTypeValue#cleanup()
* @see org.springframework.jdbc.core.support.SqlLobValue#cleanup()
*/
public static void cleanupParameters(Collection paramValues) {
if (paramValues != null) {
for (Iterator it = paramValues.iterator(); it.hasNext();) {
Object inValue = it.next();
if (inValue instanceof DisposableSqlTypeValue) {
((DisposableSqlTypeValue) inValue).cleanup();
}
else if (inValue instanceof SqlValue) {
((SqlValue) inValue).cleanup();
}
}
}
}
}

View File

@ -0,0 +1,589 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlReturnResultSet;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.jdbc.support.JdbcUtils;
/**
* Class to manage context metadata used for the configuration and execution of the call.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.5
*/
public class CallMetaDataContext {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
/** name of procedure to call **/
private String procedureName;
/** name of catalog for call **/
private String catalogName;
/** name of schema for call **/
private String schemaName;
/** List of SqlParameter objects to be used in call execution */
private List<SqlParameter> callParameters = new ArrayList<SqlParameter>();
/** name to use for the return value in the output map */
private String functionReturnName = "return";
/** Set of in parameter names to exclude use for any not listed */
private Set<String> limitedInParameterNames = new HashSet<String>();
/** List of SqlParameter names for out parameters */
private List<String> outParameterNames = new ArrayList<String>();
/** should we access call parameter meta data info or not */
private boolean accessCallParameterMetaData = true;
/** indicates whether this is a procedure or a function **/
private boolean function;
/** indicates whether this procedure's return value should be included **/
private boolean returnValueRequired;
/** the provider of call meta data */
private CallMetaDataProvider metaDataProvider;
/**
* Specify the name used for the return value of the function.
*/
public void setFunctionReturnName(String functionReturnName) {
this.functionReturnName = functionReturnName;
}
/**
* Get the name used for the return value of the function.
*/
public String getFunctionReturnName() {
return this.functionReturnName;
}
/**
* Specify a limited set of in parameters to be used.
*/
public void setLimitedInParameterNames(Set<String> limitedInParameterNames) {
this.limitedInParameterNames = limitedInParameterNames;
}
/**
* Get a limited set of in parameters to be used.
*/
public Set<String> getLimitedInParameterNames() {
return this.limitedInParameterNames;
}
/**
* Specify the names of the out parameters.
*/
public void setOutParameterNames(List<String> outParameterNames) {
this.outParameterNames = outParameterNames;
}
/**
* Get a list of the out parameter names.
*/
public List<String> getOutParameterNames() {
return this.outParameterNames;
}
/**
* Specify the name of the procedure.
*/
public void setProcedureName(String procedureName) {
this.procedureName = procedureName;
}
/**
* Get the name of the procedure.
*/
public String getProcedureName() {
return this.procedureName;
}
/**
* Specify the name of the catalog.
*/
public void setCatalogName(String catalogName) {
this.catalogName = catalogName;
}
/**
* Get the name of the catalog.
*/
public String getCatalogName() {
return this.catalogName;
}
/**
* Secify the name of the schema.
*/
public void setSchemaName(String schemaName) {
this.schemaName = schemaName;
}
/**
* Get the name of the schema.
*/
public String getSchemaName() {
return this.schemaName;
}
/**
* Specify whether this call is a function call.
*/
public void setFunction(boolean function) {
this.function = function;
}
/**
* Check whether this call is a function call.
*/
public boolean isFunction() {
return this.function;
}
/**
* Specify whether a return value is required.
*/
public void setReturnValueRequired(boolean returnValueRequired) {
this.returnValueRequired = returnValueRequired;
}
/**
* Check whether a return value is required.
*/
public boolean isReturnValueRequired() {
return this.returnValueRequired;
}
/**
* Specify whether call parameter metadata should be accessed.
*/
public void setAccessCallParameterMetaData(boolean accessCallParameterMetaData) {
this.accessCallParameterMetaData = accessCallParameterMetaData;
}
/**
* Check whether call parameter metadata should be accessed.
*/
public boolean isAccessCallParameterMetaData() {
return this.accessCallParameterMetaData;
}
/**
* Create a ReturnResultSetParameter/SqlOutParameter depending on the support provided
* by the JDBC driver used for the database in use.
* @param parameterName the name of the parameter (also used as the name of the List returned in the output)
* @param rowMapper a RowMapper implementation used to map the data retuned in the result set
* @return the appropriate SqlParameter
*/
public SqlParameter createReturnResultSetParameter(String parameterName, RowMapper rowMapper) {
if (this.metaDataProvider.isReturnResultSetSupported()) {
return new SqlReturnResultSet(parameterName, rowMapper);
}
else {
if (this.metaDataProvider.isRefCursorSupported()) {
return new SqlOutParameter(parameterName, this.metaDataProvider.getRefCursorSqlType(), rowMapper);
}
else {
throw new InvalidDataAccessApiUsageException("Return of a ResultSet from a stored procedure is not supported.");
}
}
}
/**
* Get the name of the single out parameter for this call. If there are multiple parameters then the name of
* the first one is returned.
*/
public String getScalarOutParameterName() {
if (isFunction()) {
return this.functionReturnName;
}
else {
if (this.outParameterNames.size() > 1) {
logger.warn("Accessing single output value when procedure has more than one output parameter");
}
return (this.outParameterNames.size() > 0 ? this.outParameterNames.get(0) : null);
}
}
/**
* Get the List of SqlParameter objects to be used in call execution
*/
public List<SqlParameter> getCallParameters() {
return this.callParameters;
}
/**
* Initialize this class with metadata from the database
* @param dataSource the DataSource used to retrieve metadata
*/
public void initializeMetaData(DataSource dataSource) {
this.metaDataProvider = CallMetaDataProviderFactory.createMetaDataProvider(dataSource, this);
}
/**
* Process the list of parameters provided and if procedure column metedata is used the
* parameters will be matched against the metadata information and any missing ones will
* be automatically included
* @param parameters the list of parameters to use as a base
*/
public void processParameters(List<SqlParameter> parameters) {
this.callParameters = reconcileParameters(parameters);
}
/**
* Reconcile the provided parameters with available metadata and add new ones where appropriate
*/
private List<SqlParameter> reconcileParameters(List<SqlParameter> parameters) {
final List<SqlParameter> declaredReturnParameters = new ArrayList<SqlParameter>();
final Map<String, SqlParameter> declaredParameters = new LinkedHashMap<String, SqlParameter>();
boolean returnDeclared = false;
List<String> outParameterNames = new ArrayList<String>();
List<String> metaDataParameterNames = new ArrayList<String>();
// get the names of the meta data parameters
for (CallParameterMetaData meta : metaDataProvider.getCallParameterMetaData()) {
if (meta.getParameterType() != DatabaseMetaData.procedureColumnReturn) {
metaDataParameterNames.add(meta.getParameterName().toLowerCase());
}
}
// Separate implicit return parameters from explicit parameters...
for (SqlParameter parameter : parameters) {
if (parameter.isResultsParameter()) {
declaredReturnParameters.add(parameter);
}
else {
String parameterNameToMatch = this.metaDataProvider.parameterNameToUse(parameter.getName()).toLowerCase();
declaredParameters.put(parameterNameToMatch, parameter);
if (parameter instanceof SqlOutParameter) {
outParameterNames.add(parameter.getName());
if (this.isFunction() && !metaDataParameterNames.contains(parameterNameToMatch)) {
if (!returnDeclared) {
if (logger.isDebugEnabled()) {
logger.debug("Using declared out parameter '" + parameter.getName() + "' for function return value");
}
this.setFunctionReturnName(parameter.getName());
returnDeclared = true;
}
}
}
}
}
this.setOutParameterNames(outParameterNames);
final List<SqlParameter> workParameters = new ArrayList<SqlParameter>();
workParameters.addAll(declaredReturnParameters);
if (!this.metaDataProvider.isProcedureColumnMetaDataUsed()) {
workParameters.addAll(declaredParameters.values());
return workParameters;
}
Map<String, String> limitedInParamNamesMap = new HashMap<String, String>(this.limitedInParameterNames.size());
for (String limitedParameterName : this.limitedInParameterNames) {
limitedInParamNamesMap.put(
this.metaDataProvider.parameterNameToUse(limitedParameterName).toLowerCase(), limitedParameterName);
}
for (CallParameterMetaData meta : metaDataProvider.getCallParameterMetaData()) {
String parNameToCheck = null;
if (meta.getParameterName() != null) {
parNameToCheck = this.metaDataProvider.parameterNameToUse(meta.getParameterName()).toLowerCase();
}
String parNameToUse = this.metaDataProvider.parameterNameToUse(meta.getParameterName());
if (declaredParameters.containsKey(parNameToCheck) ||
(meta.getParameterType() == DatabaseMetaData.procedureColumnReturn && returnDeclared)) {
SqlParameter parameter;
if (meta.getParameterType() == DatabaseMetaData.procedureColumnReturn) {
parameter = declaredParameters.get(this.getFunctionReturnName());
if (parameter == null && this.getOutParameterNames().size() > 0) {
parameter = declaredParameters.get(this.getOutParameterNames().get(0).toLowerCase());
}
if (parameter == null) {
throw new InvalidDataAccessApiUsageException(
"Unable to locate declared parameter for function return value - " +
" add an SqlOutParameter with name \"" + getFunctionReturnName() +"\"");
}
else {
this.setFunctionReturnName(parameter.getName());
}
}
else {
parameter = declaredParameters.get(parNameToCheck);
}
if (parameter != null) {
workParameters.add(parameter);
if (logger.isDebugEnabled()) {
logger.debug("Using declared parameter for: " +
(parNameToUse == null ? getFunctionReturnName() : parNameToUse));
}
}
}
else {
if (meta.getParameterType() == DatabaseMetaData.procedureColumnReturn) {
if (!isFunction() && !isReturnValueRequired() &&
this.metaDataProvider.byPassReturnParameter(meta.getParameterName())) {
if (logger.isDebugEnabled()) {
logger.debug("Bypassing metadata return parameter for: " + meta.getParameterName());
}
}
else {
String returnNameToUse =
( meta.getParameterName() == null || meta.getParameterName().length() < 1 ) ?
this.getFunctionReturnName() : parNameToUse;
workParameters.add(new SqlOutParameter(returnNameToUse, meta.getSqlType()));
if (this.isFunction())
outParameterNames.add(returnNameToUse);
if (logger.isDebugEnabled()) {
logger.debug("Added metadata return parameter for: " + returnNameToUse);
}
}
}
else {
if (meta.getParameterType() == DatabaseMetaData.procedureColumnOut) {
workParameters.add(this.metaDataProvider.createDefaultOutParameter(parNameToUse, meta));
outParameterNames.add(parNameToUse);
if (logger.isDebugEnabled()) {
logger.debug("Added metadata out parameter for: " + parNameToUse);
}
}
else if (meta.getParameterType() == DatabaseMetaData.procedureColumnInOut) {
workParameters.add(this.metaDataProvider.createDefaultInOutParameter(parNameToUse, meta));
outParameterNames.add(parNameToUse);
if (logger.isDebugEnabled()) {
logger.debug("Added metadata in out parameter for: " + parNameToUse);
}
}
else {
if (this.limitedInParameterNames.size() == 0 ||
limitedInParamNamesMap.containsKey(parNameToUse.toLowerCase())) {
workParameters.add(this.metaDataProvider.createDefaultInParameter(parNameToUse, meta));
if (logger.isDebugEnabled()) {
logger.debug("Added metadata in parameter for: " + parNameToUse);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Limited set of parameters " + limitedInParamNamesMap.keySet() +
" skipped parameter for: " + parNameToUse);
}
}
}
}
}
}
return workParameters;
}
/**
* Match input parameter values with the parameters declared to be used in the call.
* @param parameterSource the input values
* @return a Map containing the matched parameter names with the value taken from the input
*/
public Map<String, Object> matchInParameterValuesWithCallParameters(SqlParameterSource parameterSource) {
// For parameter source lookups we need to provide case-insensitive lookup support
// since the database metadata is not necessarily providing case sensitive parameter names.
Map caseInsensitiveParameterNames =
SqlParameterSourceUtils.extractCaseInsensitiveParameterNames(parameterSource);
Map<String, String> callParameterNames = new HashMap<String, String>(this.callParameters.size());
Map<String, Object> matchedParameters = new HashMap<String, Object>(this.callParameters.size());
for (SqlParameter parameter : this.callParameters) {
if (parameter.isInputValueProvided()) {
String parameterName = parameter.getName();
String parameterNameToMatch = this.metaDataProvider.parameterNameToUse(parameterName);
if (parameterNameToMatch != null) {
callParameterNames.put(parameterNameToMatch.toLowerCase(), parameterName);
}
if (parameterName != null) {
if (parameterSource.hasValue(parameterName)) {
matchedParameters.put(parameterName, SqlParameterSourceUtils.getTypedValue(parameterSource, parameterName));
}
else {
String lowerCaseName = parameterName.toLowerCase();
if (parameterSource.hasValue(lowerCaseName)) {
matchedParameters.put(parameterName, SqlParameterSourceUtils.getTypedValue(parameterSource, lowerCaseName));
}
else {
String propertyName = JdbcUtils.convertUnderscoreNameToPropertyName(parameterName);
if (parameterSource.hasValue(propertyName)) {
matchedParameters.put(parameterName, SqlParameterSourceUtils.getTypedValue(parameterSource, propertyName));
}
else {
if (caseInsensitiveParameterNames.containsKey(lowerCaseName)) {
String sourceName = (String) caseInsensitiveParameterNames.get(lowerCaseName);
matchedParameters.put(parameterName, SqlParameterSourceUtils.getTypedValue(parameterSource, sourceName));
}
else {
logger.warn("Unable to locate the corresponding parameter value for '" + parameterName +
"' within the parameter values provided: " + caseInsensitiveParameterNames.values());
}
}
}
}
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Matching " + caseInsensitiveParameterNames.values() + " with " + callParameterNames.values());
logger.debug("Found match for " + matchedParameters.keySet());
}
return matchedParameters;
}
/**
* Match input parameter values with the parameters declared to be used in the call.
* @param inParameters the input values
* @return a Map containing the matched parameter names with the value taken from the input
*/
public Map<String, Object> matchInParameterValuesWithCallParameters(Map<String, Object> inParameters) {
if (!this.metaDataProvider.isProcedureColumnMetaDataUsed()) {
return inParameters;
}
Map<String, String> callParameterNames = new HashMap<String, String>(this.callParameters.size());
for (SqlParameter parameter : this.callParameters) {
if (parameter.isInputValueProvided()) {
String parameterName = parameter.getName();
String parameterNameToMatch = this.metaDataProvider.parameterNameToUse(parameterName);
if (parameterNameToMatch != null) {
callParameterNames.put(parameterNameToMatch.toLowerCase(), parameterName);
}
}
}
Map<String, Object> matchedParameters = new HashMap<String, Object>(inParameters.size());
for (String parameterName : inParameters.keySet()) {
String parameterNameToMatch = this.metaDataProvider.parameterNameToUse(parameterName);
String callParameterName = callParameterNames.get(parameterNameToMatch.toLowerCase());
if (callParameterName == null) {
if (logger.isDebugEnabled()) {
Object value = inParameters.get(parameterName);
if (value instanceof SqlParameterValue) {
value = ((SqlParameterValue)value).getValue();
}
if (value != null) {
logger.debug("Unable to locate the corresponding IN or IN-OUT parameter for \"" + parameterName +
"\" in the parameters used: " + callParameterNames.keySet());
}
}
}
else {
matchedParameters.put(callParameterName, inParameters.get(parameterName));
}
}
if (matchedParameters.size() < callParameterNames.size()) {
for (String parameterName : callParameterNames.keySet()) {
String parameterNameToMatch = this.metaDataProvider.parameterNameToUse(parameterName);
String callParameterName = callParameterNames.get(parameterNameToMatch.toLowerCase());
if (!matchedParameters.containsKey(callParameterName)) {
logger.warn("Unable to locate the corresponding parameter value for '" + parameterName +
"' within the parameter values provided: " + inParameters.keySet());
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Matching " + inParameters.keySet() + " with " + callParameterNames.values());
logger.debug("Found match for " + matchedParameters.keySet());
}
return matchedParameters;
}
/**
* Build the call string based on configuration and metadata information.
* @return the call string to be used
*/
public String createCallString() {
String callString;
int parameterCount = 0;
String catalogNameToUse = null;
String schemaNameToUse = null;
// For Oracle where catalogs are not supported we need to reverse the schema name
// and the catalog name since the cataog is used for the package name
if (this.metaDataProvider.isSupportsSchemasInProcedureCalls() &&
!this.metaDataProvider.isSupportsCatalogsInProcedureCalls()) {
schemaNameToUse = this.metaDataProvider.catalogNameToUse(this.getCatalogName());
catalogNameToUse = this.metaDataProvider.schemaNameToUse(this.getSchemaName());
}
else {
catalogNameToUse = this.metaDataProvider.catalogNameToUse(this.getCatalogName());
schemaNameToUse = this.metaDataProvider.schemaNameToUse(this.getSchemaName());
}
String procedureNameToUse = this.metaDataProvider.procedureNameToUse(this.getProcedureName());
if (this.isFunction() || this.isReturnValueRequired()) {
callString = "{? = call " +
(catalogNameToUse != null && catalogNameToUse.length() > 0 ? catalogNameToUse + "." : "") +
(schemaNameToUse != null && schemaNameToUse.length() > 0 ? schemaNameToUse + "." : "") +
procedureNameToUse + "(";
parameterCount = -1;
}
else {
callString = "{call " +
(catalogNameToUse != null && catalogNameToUse.length() > 0 ? catalogNameToUse + "." : "") +
(schemaNameToUse != null && schemaNameToUse.length() > 0 ? schemaNameToUse + "." : "") +
procedureNameToUse + "(";
}
for (SqlParameter parameter : this.callParameters) {
if (!(parameter.isResultsParameter())) {
if (parameterCount > 0) {
callString += ", ";
}
if (parameterCount >= 0) {
callString += "?";
}
parameterCount++;
}
}
callString += ")}";
return callString;
}
}

View File

@ -0,0 +1,175 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.SqlParameter;
/**
* Interface specifying the API to be implemented by a class providing call metadata.
* This is intended for internal use by Spring's
* {@link org.springframework.jdbc.core.simple.SimpleJdbcTemplate}.
*
* @author Thomas Risberg
* @since 2.5
*/
public interface CallMetaDataProvider {
/**
* Initialize using the provided DatabaseMetData.
* @param databaseMetaData used to retrieve database specific information
* @throws SQLException in case of initialization failure
*/
void initializeWithMetaData(DatabaseMetaData databaseMetaData) throws SQLException;
/**
* Initialize the database specific management of procedure column meta data.
* This is only called for databases that are supported. This initalization
* can be turned off by specifying that column meta data should not be used.
* @param databaseMetaData used to retreive database specific information
* @param catalogName name of catalog to use or null
* @param schemaName name of schema name to use or null
* @param procedureName name of the stored procedure
* @throws SQLException in case of initialization failure
* @see org.springframework.jdbc.core.simple.SimpleJdbcCall#withoutProcedureColumnMetaDataAccess()
*/
void initializeWithProcedureColumnMetaData(
DatabaseMetaData databaseMetaData, String catalogName, String schemaName, String procedureName)
throws SQLException;
/**
* Provide any modification of the procedure name passed in to match the meta data currently used.
* This could include alterig the case.
*/
String procedureNameToUse(String procedureName);
/**
* Provide any modification of the catalog name passed in to match the meta data currently used.
* This could include alterig the case.
*/
String catalogNameToUse(String catalogName);
/**
* Provide any modification of the schema name passed in to match the meta data currently used.
* This could include alterig the case.
*/
String schemaNameToUse(String schemaName);
/**
* Provide any modification of the catalog name passed in to match the meta data currently used.
* The reyurned value will be used for meta data lookups. This could include alterig the case used or
* providing a base catalog if mone provided.
*/
String metaDataCatalogNameToUse(String catalogName) ;
/**
* Provide any modification of the schema name passed in to match the meta data currently used.
* The reyurned value will be used for meta data lookups. This could include alterig the case used or
* providing a base schema if mone provided.
*/
String metaDataSchemaNameToUse(String schemaName) ;
/**
* Provide any modification of the column name passed in to match the meta data currently used.
* This could include alterig the case.
* @param parameterName name of the parameter of column
*/
String parameterNameToUse(String parameterName);
/**
* Create a default out parameter based on the provided meta data. This is used when no expicit
* parameter declaration has been made.
* @param parameterName the name of the parameter
* @param meta meta data used for this call
* @return the configured SqlOutParameter
*/
SqlParameter createDefaultOutParameter(String parameterName, CallParameterMetaData meta);
/**
* Create a default inout parameter based on the provided meta data. This is used when no expicit
* parameter declaration has been made.
* @param parameterName the name of the parameter
* @param meta meta data used for this call
* @return the configured SqlInOutParameter
*/
SqlParameter createDefaultInOutParameter(String parameterName, CallParameterMetaData meta);
/**
* Create a default in parameter based on the provided meta data. This is used when no expicit
* parameter declaration has been made.
* @param parameterName the name of the parameter
* @param meta meta data used for this call
* @return the configured SqlParameter
*/
SqlParameter createDefaultInParameter(String parameterName, CallParameterMetaData meta);
/**
* Get the name of the current user. Useful for meta data lookups etc.
* @return current user name from database connection
*/
String getUserName();
/**
* Does this database support returning resultsets that should be retreived with the JDBC call
* {@link java.sql.Statement#getResultSet()}
*/
boolean isReturnResultSetSupported();
/**
* Does this database support returning resultsets as ref cursors to be retreived with
* {@link java.sql.CallableStatement#getObject(int)} for the specified column.
*/
boolean isRefCursorSupported();
/**
* Get the {@link java.sql.Types} type for columns that return resultsets as ref cursors if this feature
* is supported.
*/
int getRefCursorSqlType();
/**
* Are we using the meta data for the procedure columns?
*/
boolean isProcedureColumnMetaDataUsed();
/**
* Should we bypass the return parameter with the specified name.
* This allows the database specific implementation to skip the processing
* for specific results returned by the database call.
*/
boolean byPassReturnParameter(String parameterName);
/**
* Get the call parameter metadata that is currently used.
* @return List of {@link CallParameterMetaData}
*/
List<CallParameterMetaData> getCallParameterMetaData();
/**
* Does the database support the use of catalog name in procedure calls
*/
boolean isSupportsCatalogsInProcedureCalls();
/**
* Does the database support the use of schema name in procedure calls
*/
boolean isSupportsSchemasInProcedureCalls();
}

View File

@ -0,0 +1,140 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.support.DatabaseMetaDataCallback;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;
/**
* Factory used to create a {@link CallMetaDataProvider} implementation based on the type of databse being used.
*
* @author Thomas Risberg
* @since 2.5
*/
public class CallMetaDataProviderFactory {
/** Logger */
private static final Log logger = LogFactory.getLog(CallMetaDataProviderFactory.class);
/** List of supported database products for procedure calls */
public static final List<String> supportedDatabaseProductsForProcedures = Arrays.asList(
"Apache Derby",
"DB2",
"MySQL",
"Microsoft SQL Server",
"Oracle",
"PostgreSQL",
"Sybase"
);
/** List of supported database products for function calls */
public static final List<String> supportedDatabaseProductsForFunctions = Arrays.asList(
"MySQL",
"Microsoft SQL Server",
"Oracle",
"PostgreSQL"
);
/**
* Create a CallMetaDataProvider based on the database metedata
* @param dataSource used to retrieve metedata
* @param context the class that holds configuration and metedata
* @return instance of the CallMetaDataProvider implementation to be used
*/
static public CallMetaDataProvider createMetaDataProvider(DataSource dataSource, final CallMetaDataContext context) {
try {
return (CallMetaDataProvider) JdbcUtils.extractDatabaseMetaData(dataSource, new DatabaseMetaDataCallback() {
public Object processMetaData(DatabaseMetaData databaseMetaData) throws SQLException, MetaDataAccessException {
String databaseProductName = JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName());
boolean accessProcedureColumnMetaData = context.isAccessCallParameterMetaData();
if (context.isFunction()) {
if (!supportedDatabaseProductsForFunctions.contains(databaseProductName)) {
if (logger.isWarnEnabled()) {
logger.warn(databaseProductName + " is not one of the databases fully supported for function calls " +
"-- supported are: " + supportedDatabaseProductsForFunctions);
}
if (accessProcedureColumnMetaData) {
logger.warn("Metadata processing disabled - you must specify all parameters explicitly");
accessProcedureColumnMetaData = false;
}
}
}
else {
if (!supportedDatabaseProductsForProcedures.contains(databaseProductName)) {
if (logger.isWarnEnabled()) {
logger.warn(databaseProductName + " is not one of the databases fully supported for procedure calls " +
"-- supported are: " + supportedDatabaseProductsForProcedures);
}
if (accessProcedureColumnMetaData) {
logger.warn("Metadata processing disabled - you must specify all parameters explicitly");
accessProcedureColumnMetaData = false;
}
}
}
CallMetaDataProvider provider;
if ("Oracle".equals(databaseProductName)) {
provider = new OracleCallMetaDataProvider(databaseMetaData);
}
else if ("DB2".equals(databaseProductName)) {
provider = new Db2CallMetaDataProvider((databaseMetaData));
}
else if ("Apache Derby".equals(databaseProductName)) {
provider = new DerbyCallMetaDataProvider((databaseMetaData));
}
else if ("PostgreSQL".equals(databaseProductName)) {
provider = new PostgresCallMetaDataProvider((databaseMetaData));
}
else if ("Sybase".equals(databaseProductName)) {
provider = new SybaseCallMetaDataProvider((databaseMetaData));
}
else if ("Microsoft SQL Server".equals(databaseProductName)) {
provider = new SqlServerCallMetaDataProvider((databaseMetaData));
}
else {
provider = new GenericCallMetaDataProvider(databaseMetaData);
}
if (logger.isDebugEnabled()) {
logger.debug("Using " + provider.getClass().getName());
}
provider.initializeWithMetaData(databaseMetaData);
if (accessProcedureColumnMetaData) {
provider.initializeWithProcedureColumnMetaData(
databaseMetaData, context.getCatalogName(), context.getSchemaName(), context.getProcedureName());
}
return provider;
}
});
}
catch (MetaDataAccessException ex) {
throw new DataAccessResourceFailureException("Error retreiving database metadata", ex);
}
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
/**
* Holder of metadata for a specific parameter that is used for call processing.
*
* @author Thomas Risberg
* @since 2.5
*/
public class CallParameterMetaData {
private String parameterName;
private int parameterType;
private int sqlType;
private String typeName;
private boolean nullable;
/**
* Constructor taking all the properties
*/
public CallParameterMetaData(String columnName, int columnType, int sqlType, String typeName, boolean nullable) {
this.parameterName = columnName;
this.parameterType = columnType;
this.sqlType = sqlType;
this.typeName = typeName;
this.nullable = nullable;
}
/**
* Get the parameter name.
*/
public String getParameterName() {
return parameterName;
}
/**
* Get the parameter type.
*/
public int getParameterType() {
return parameterType;
}
/**
* Get the parameter SQL type.
*/
public int getSqlType() {
return sqlType;
}
/**
* Get the parameter type name.
*/
public String getTypeName() {
return typeName;
}
/**
* Get whether the parameter is nullable.
*/
public boolean isNullable() {
return nullable;
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
/**
* DB2 specific implementation for the {@link CallMetaDataProvider} interface.
* This class is intended for internal use by the Simple JDBC classes.
*
* @author Thomas Risberg
* @since 2.5
*/
public class Db2CallMetaDataProvider extends GenericCallMetaDataProvider {
public Db2CallMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
super(databaseMetaData);
}
@Override
public void initializeWithMetaData(DatabaseMetaData databaseMetaData) throws SQLException {
try {
setSupportsCatalogsInProcedureCalls(databaseMetaData.supportsCatalogsInProcedureCalls());
}
catch (SQLException se) {
logger.debug("Error retrieving 'DatabaseMetaData.supportsCatalogsInProcedureCalls' - " + se.getMessage());
}
try {
setSupportsSchemasInProcedureCalls(databaseMetaData.supportsSchemasInProcedureCalls());
}
catch (SQLException se) {
logger.debug("Error retrieving 'DatabaseMetaData.supportsSchemasInProcedureCalls' - " + se.getMessage());
}
try {
setStoresUpperCaseIdentifiers(databaseMetaData.storesUpperCaseIdentifiers());
}
catch (SQLException se) {
logger.debug("Error retrieving 'DatabaseMetaData.storesUpperCaseIdentifiers' - " + se.getMessage());
}
try {
setStoresLowerCaseIdentifiers(databaseMetaData.storesLowerCaseIdentifiers());
}
catch (SQLException se) {
logger.debug("Error retrieving 'DatabaseMetaData.storesLowerCaseIdentifiers' - " + se.getMessage());
}
}
@Override
public String metaDataSchemaNameToUse(String schemaName) {
// Use current user schema if no schema specified
return schemaName == null ? getUserName().toUpperCase() : super.metaDataSchemaNameToUse(schemaName);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
/**
* Derby specific implementation for the {@link CallMetaDataProvider} interface.
* This class is intended for internal use by the Simple JDBC classes.
*
* @author Thomas Risberg
* @since 2.5
*/
public class DerbyCallMetaDataProvider extends GenericCallMetaDataProvider {
public DerbyCallMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
super(databaseMetaData);
}
@Override
public String metaDataSchemaNameToUse(String schemaName) {
// Use current user schema if no schema specified
return schemaName == null ? getUserName().toUpperCase() : super.metaDataSchemaNameToUse(schemaName);
}
}

View File

@ -0,0 +1,364 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlInOutParameter;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.util.StringUtils;
/**
* Generic implementation for the {@link CallMetaDataProvider} interface.
* This class can be extended to provide database specific behavior.
*
* @author Thomas Risberg
* @since 2.5
*/
public class GenericCallMetaDataProvider implements CallMetaDataProvider {
/** Logger available to subclasses */
protected static final Log logger = LogFactory.getLog(CallMetaDataProvider.class);
private boolean procedureColumnMetaDataUsed = false;
private String userName;
private boolean supportsCatalogsInProcedureCalls = true;
private boolean supportsSchemasInProcedureCalls = true;
private boolean storesUpperCaseIdentifiers = true;
private boolean storesLowerCaseIdentifiers = false;
private List<CallParameterMetaData> callParameterMetaData = new ArrayList<CallParameterMetaData>();
/**
* Constructor used to initialize with provided database meta data.
* @param databaseMetaData meta data to be used
*/
protected GenericCallMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
this.userName = databaseMetaData.getUserName();
}
public void initializeWithMetaData(DatabaseMetaData databaseMetaData) throws SQLException {
try {
setSupportsCatalogsInProcedureCalls(databaseMetaData.supportsCatalogsInProcedureCalls());
}
catch (SQLException se) {
logger.warn("Error retrieving 'DatabaseMetaData.supportsCatalogsInProcedureCalls' - " + se.getMessage());
}
try {
setSupportsSchemasInProcedureCalls(databaseMetaData.supportsSchemasInProcedureCalls());
}
catch (SQLException se) {
logger.warn("Error retrieving 'DatabaseMetaData.supportsSchemasInProcedureCalls' - " + se.getMessage());
}
try {
setStoresUpperCaseIdentifiers(databaseMetaData.storesUpperCaseIdentifiers());
}
catch (SQLException se) {
logger.warn("Error retrieving 'DatabaseMetaData.storesUpperCaseIdentifiers' - " + se.getMessage());
}
try {
setStoresLowerCaseIdentifiers(databaseMetaData.storesLowerCaseIdentifiers());
}
catch (SQLException se) {
logger.warn("Error retrieving 'DatabaseMetaData.storesLowerCaseIdentifiers' - " + se.getMessage());
}
}
public void initializeWithProcedureColumnMetaData(DatabaseMetaData databaseMetaData, String catalogName, String schemaName, String procedureName)
throws SQLException {
this.procedureColumnMetaDataUsed = true;
processProcedureColumns(databaseMetaData, catalogName, schemaName, procedureName);
}
public List<CallParameterMetaData> getCallParameterMetaData() {
return callParameterMetaData;
}
public String procedureNameToUse(String procedureName) {
if (procedureName == null)
return null;
else if (isStoresUpperCaseIdentifiers())
return procedureName.toUpperCase();
else if(isStoresLowerCaseIdentifiers())
return procedureName.toLowerCase();
else
return procedureName;
}
public String catalogNameToUse(String catalogName) {
if (catalogName == null)
return null;
else if (isStoresUpperCaseIdentifiers())
return catalogName.toUpperCase();
else if(isStoresLowerCaseIdentifiers())
return catalogName.toLowerCase();
else
return catalogName;
}
public String schemaNameToUse(String schemaName) {
if (schemaName == null)
return null;
else if (isStoresUpperCaseIdentifiers())
return schemaName.toUpperCase();
else if(isStoresLowerCaseIdentifiers())
return schemaName.toLowerCase();
else
return schemaName;
}
public String metaDataCatalogNameToUse(String catalogName) {
if (isSupportsCatalogsInProcedureCalls()) {
return catalogNameToUse(catalogName);
}
else {
return null;
}
}
public String metaDataSchemaNameToUse(String schemaName) {
if (isSupportsSchemasInProcedureCalls()) {
return schemaNameToUse(schemaName);
}
else {
return null;
}
}
public String parameterNameToUse(String parameterName) {
if (parameterName == null) {
return null;
}
else if (isStoresUpperCaseIdentifiers()) {
return parameterName.toUpperCase();
}
else if(isStoresLowerCaseIdentifiers()) {
return parameterName.toLowerCase();
}
else {
return parameterName;
}
}
public boolean byPassReturnParameter(String parameterName) {
return false;
}
public SqlParameter createDefaultOutParameter(String parameterName, CallParameterMetaData meta) {
return new SqlOutParameter(parameterName, meta.getSqlType());
}
public SqlParameter createDefaultInOutParameter(String parameterName, CallParameterMetaData meta) {
return new SqlInOutParameter(parameterName, meta.getSqlType());
}
public SqlParameter createDefaultInParameter(String parameterName, CallParameterMetaData meta) {
return new SqlParameter(parameterName, meta.getSqlType());
}
public String getUserName() {
return this.userName;
}
public boolean isReturnResultSetSupported() {
return true;
}
public boolean isRefCursorSupported() {
return false;
}
public int getRefCursorSqlType() {
return Types.OTHER;
}
public boolean isProcedureColumnMetaDataUsed() {
return this.procedureColumnMetaDataUsed;
}
/**
* Specify whether the database supports the use of catalog name in procedure calls
*/
protected void setSupportsCatalogsInProcedureCalls(boolean supportsCatalogsInProcedureCalls) {
this.supportsCatalogsInProcedureCalls = supportsCatalogsInProcedureCalls;
}
/**
* Does the database support the use of catalog name in procedure calls
*/
public boolean isSupportsCatalogsInProcedureCalls() {
return this.supportsCatalogsInProcedureCalls;
}
/**
* Specify whether the database supports the use of schema name in procedure calls
*/
protected void setSupportsSchemasInProcedureCalls(boolean supportsSchemasInProcedureCalls) {
this.supportsSchemasInProcedureCalls = supportsSchemasInProcedureCalls;
}
/**
* Does the database support the use of schema name in procedure calls
*/
public boolean isSupportsSchemasInProcedureCalls() {
return this.supportsSchemasInProcedureCalls;
}
/**
* Specify whether the database uses upper case for identifiers
*/
protected void setStoresUpperCaseIdentifiers(boolean storesUpperCaseIdentifiers) {
this.storesUpperCaseIdentifiers = storesUpperCaseIdentifiers;
}
/**
* Does the database use upper case for identifiers
*/
protected boolean isStoresUpperCaseIdentifiers() {
return this.storesUpperCaseIdentifiers;
}
/**
* Specify whether the database uses lower case for identifiers
*/
protected void setStoresLowerCaseIdentifiers(boolean storesLowerCaseIdentifiers) {
this.storesLowerCaseIdentifiers = storesLowerCaseIdentifiers;
}
/**
* Does the database use lower case for identifiers
*/
protected boolean isStoresLowerCaseIdentifiers() {
return this.storesLowerCaseIdentifiers;
}
/**
* Process the procedure column metadata
*/
private void processProcedureColumns(DatabaseMetaData databaseMetaData, String catalogName, String schemaName, String procedureName) {
ResultSet procs = null;
String metaDataCatalogName = metaDataCatalogNameToUse(catalogName);
String metaDataSchemaName = metaDataSchemaNameToUse(schemaName);
String metaDataProcedureName = procedureNameToUse(procedureName);
if (logger.isDebugEnabled()) {
logger.debug("Retrieving metadata for " + metaDataCatalogName + "/" +
metaDataSchemaName + "/" + metaDataProcedureName);
}
try {
procs = databaseMetaData.getProcedures(
metaDataCatalogName,
metaDataSchemaName,
metaDataProcedureName);
List found = new ArrayList();
while (procs.next()) {
found.add(procs.getString("PROCEDURE_CAT") +
"."+procs.getString("PROCEDURE_SCHEM") +
"."+procs.getString("PROCEDURE_NAME"));
}
procs.close();
if (found.size() > 1) {
throw new InvalidDataAccessApiUsageException("Unable to determine the correct call signature - " +
"multiple procedures/functions/signatures for " + metaDataProcedureName + " found " + found);
}
if (found.size() < 1) {
if (metaDataProcedureName.contains(".") && !StringUtils.hasText(metaDataCatalogName)) {
String packageName = metaDataProcedureName.substring(0, metaDataProcedureName.indexOf("."));
throw new InvalidDataAccessApiUsageException("Unable to determine the correct call signature for " +
metaDataProcedureName + " - package name should be specified separately using " +
"'.withCatalogName(\"" + packageName + "\")'");
}
}
procs = databaseMetaData.getProcedureColumns(
metaDataCatalogName,
metaDataSchemaName,
metaDataProcedureName,
null);
while (procs.next()) {
String columnName = procs.getString("COLUMN_NAME");
int columnType = procs.getInt("COLUMN_TYPE");
if (columnName == null && (
columnType == DatabaseMetaData.procedureColumnIn ||
columnType == DatabaseMetaData.procedureColumnInOut ||
columnType == DatabaseMetaData.procedureColumnOut)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping metadata for: "
+ columnName +
" " + columnType +
" " + procs.getInt("DATA_TYPE") +
" " + procs.getString("TYPE_NAME") +
" " + procs.getBoolean("NULLABLE") +
" (probably a member of a collection)"
);
}
}
else {
CallParameterMetaData meta = new CallParameterMetaData(
columnName,
columnType,
procs.getInt("DATA_TYPE"),
procs.getString("TYPE_NAME"),
procs.getBoolean("NULLABLE")
);
callParameterMetaData.add(meta);
if (logger.isDebugEnabled()) {
logger.debug("Retrieved metadata: "
+ meta.getParameterName() +
" " + meta.getParameterType() +
" " + meta.getSqlType() +
" " + meta.getTypeName() +
" " + meta.isNullable()
);
}
}
}
}
catch (SQLException se) {
logger.warn("Error while retreiving metadata for procedure columns: " + se.getMessage());
}
finally {
try {
if (procs != null)
procs.close();
}
catch (SQLException se) {
logger.warn("Problem closing resultset for procedure column metadata " + se.getMessage());
}
}
}
}

View File

@ -0,0 +1,421 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessResourceFailureException;
/**
* A generic implementation of the {@link TableMetaDataProvider} that should provide enough features for all supported
* databases.
*
* @author Thomas Risberg
* @since 2.5
*/
public class GenericTableMetaDataProvider implements TableMetaDataProvider {
/** Logger available to subclasses */
protected static final Log logger = LogFactory.getLog(TableMetaDataProvider.class);
/** indicator whether column metadata should be used */
private boolean tableColumnMetaDataUsed = false;
/** the version of the database */
private String databaseVersion;
/** the name of the user currently connected */
private String userName;
/** indicates whether the identifiers are uppercased */
private boolean storesUpperCaseIdentifiers = true;
/** indicates whether the identifiers are lowercased */
private boolean storesLowerCaseIdentifiers = false;
/** indicates whether generated keys retrieval is supported */
private boolean getGeneratedKeysSupported = true;
/** indicates whether the use of a String[] for generated keys is supported */
private boolean generatedKeysColumnNameArraySupported = true;
/** database product we know that don't support the use of a String[] for generated keys */
private List productsNotSupportingGeneratedKeysColumnNameArray = Arrays.asList(new String[] {"Apache Derby"});
/** Collection of TableParameterMetaData objects */
private List<TableParameterMetaData> insertParameterMetaData = new ArrayList<TableParameterMetaData>();
/**
* Constructor used to initialize with provided database meta data.
* @param databaseMetaData meta data to be used
* @throws SQLException
*/
protected GenericTableMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
userName = databaseMetaData.getUserName();
}
/**
* Get whether identifiers use upper case
*/
public boolean isStoresUpperCaseIdentifiers() {
return storesUpperCaseIdentifiers;
}
/**
* Specify whether identifiers use upper case
*/
public void setStoresUpperCaseIdentifiers(boolean storesUpperCaseIdentifiers) {
this.storesUpperCaseIdentifiers = storesUpperCaseIdentifiers;
}
/**
* Get whether identifiers use lower case
*/
public boolean isStoresLowerCaseIdentifiers() {
return storesLowerCaseIdentifiers;
}
/**
* Specify whether identifiers use lower case
*/
public void setStoresLowerCaseIdentifiers(boolean storesLowerCaseIdentifiers) {
this.storesLowerCaseIdentifiers = storesLowerCaseIdentifiers;
}
public boolean isTableColumnMetaDataUsed() {
return tableColumnMetaDataUsed;
}
public List<TableParameterMetaData> getTableParameterMetaData() {
return insertParameterMetaData;
}
public boolean isGetGeneratedKeysSupported() {
return getGeneratedKeysSupported;
}
public boolean isGetGeneratedKeysSimulated(){
return false;
}
public String getSimpleQueryForGetGeneratedKey(String tableName, String keyColumnName) {
return null;
}
/**
* Specify whether a column name array is supported for generated keys
*/
public void setGetGeneratedKeysSupported(boolean getGeneratedKeysSupported) {
this.getGeneratedKeysSupported = getGeneratedKeysSupported;
}
public boolean isGeneratedKeysColumnNameArraySupported() {
return generatedKeysColumnNameArraySupported;
}
/**
* Specify whether a column name array is supported for generated keys
*/
public void setGeneratedKeysColumnNameArraySupported(boolean generatedKeysColumnNameArraySupported) {
this.generatedKeysColumnNameArraySupported = generatedKeysColumnNameArraySupported;
}
public void initializeWithMetaData(DatabaseMetaData databaseMetaData) throws SQLException {
try {
if (databaseMetaData.supportsGetGeneratedKeys()) {
logger.debug("GetGeneratedKeys is supported");
setGetGeneratedKeysSupported(true);
}
else {
logger.debug("GetGeneratedKeys is not supported");
setGetGeneratedKeysSupported(false);
}
}
catch (SQLException se) {
logger.warn("Error retrieving 'DatabaseMetaData.getGeneratedKeys' - " + se.getMessage());
}
try {
String databaseProductName = databaseMetaData.getDatabaseProductName();
if (productsNotSupportingGeneratedKeysColumnNameArray.contains(databaseProductName)) {
logger.debug("GeneratedKeysColumnNameArray is not supported for " + databaseProductName);
setGeneratedKeysColumnNameArraySupported(false);
}
else {
logger.debug("GeneratedKeysColumnNameArray is supported for " + databaseProductName);
setGeneratedKeysColumnNameArraySupported(true);
}
}
catch (SQLException se) {
logger.warn("Error retrieving 'DatabaseMetaData.getDatabaseProductName' - " + se.getMessage());
}
try {
databaseVersion = databaseMetaData.getDatabaseProductVersion();
}
catch (SQLException se) {
logger.warn("Error retrieving 'DatabaseMetaData.getDatabaseProductVersion' - " + se.getMessage());
}
try {
setStoresUpperCaseIdentifiers(databaseMetaData.storesUpperCaseIdentifiers());
}
catch (SQLException se) {
logger.warn("Error retrieving 'DatabaseMetaData.storesUpperCaseIdentifiers' - " + se.getMessage());
}
try {
setStoresLowerCaseIdentifiers(databaseMetaData.storesLowerCaseIdentifiers());
}
catch (SQLException se) {
logger.warn("Error retrieving 'DatabaseMetaData.storesLowerCaseIdentifiers' - " + se.getMessage());
}
}
public void initializeWithTableColumnMetaData(DatabaseMetaData databaseMetaData, String catalogName, String schemaName, String tableName)
throws SQLException {
tableColumnMetaDataUsed = true;
locateTableAndProcessMetaData(databaseMetaData, catalogName, schemaName, tableName);
}
public String tableNameToUse(String tableName) {
if (tableName == null)
return null;
else if (isStoresUpperCaseIdentifiers())
return tableName.toUpperCase();
else if(isStoresLowerCaseIdentifiers())
return tableName.toLowerCase();
else
return tableName;
}
public String catalogNameToUse(String catalogName) {
if (catalogName == null)
return null;
else if (isStoresUpperCaseIdentifiers())
return catalogName.toUpperCase();
else if(isStoresLowerCaseIdentifiers())
return catalogName.toLowerCase();
else
return catalogName;
}
public String schemaNameToUse(String schemaName) {
if (schemaName == null)
return null;
else if (isStoresUpperCaseIdentifiers())
return schemaName.toUpperCase();
else if(isStoresLowerCaseIdentifiers())
return schemaName.toLowerCase();
else
return schemaName;
}
public String metaDataCatalogNameToUse(String catalogName) {
return catalogNameToUse(catalogName);
}
public String metaDataSchemaNameToUse(String schemaName) {
if (schemaName == null) {
return schemaNameToUse(userName);
}
return schemaNameToUse(schemaName);
}
/**
* Provide access to version info for subclasses
*/
protected String getDatabaseVersion() {
return databaseVersion;
}
/**
* Method supporting the metedata processing for a table
*/
private void locateTableAndProcessMetaData(DatabaseMetaData databaseMetaData, String catalogName, String schemaName, String tableName) {
Map<String, TableMetaData> tableMeta = new HashMap<String, TableMetaData>();
ResultSet tables = null;
try {
tables = databaseMetaData.getTables(
catalogNameToUse(catalogName),
schemaNameToUse(schemaName),
tableNameToUse(tableName),
null);
while (tables != null && tables.next()) {
TableMetaData tmd = new TableMetaData();
tmd.setCatalogName(tables.getString("TABLE_CAT"));
tmd.setSchemaName(tables.getString("TABLE_SCHEM"));
tmd.setTableName(tables.getString("TABLE_NAME"));
tmd.setType(tables.getString("TABLE_TYPE"));
if (tmd.getSchemaName() == null) {
tableMeta.put(userName.toUpperCase(), tmd);
}
else {
tableMeta.put(tmd.getSchemaName().toUpperCase(), tmd);
}
}
}
catch (SQLException se) {
logger.warn("Error while accessing table meta data results" + se.getMessage());
}
finally {
if (tables != null) {
try {
tables.close();
} catch (SQLException e) {
logger.warn("Error while closing table meta data reults" + e.getMessage());
}
}
}
if (tableMeta.size() < 1) {
logger.warn("Unable to locate table meta data for '" + tableName +"' -- column names must be provided");
}
else {
TableMetaData tmd = null;
if (schemaName == null) {
tmd = tableMeta.get(userName.toUpperCase());
if (tmd == null) {
tmd = tableMeta.get("PUBLIC");
if (tmd == null) {
tmd = tableMeta.get("DBO");
}
if (tmd == null) {
throw new DataAccessResourceFailureException("Unable to locate table meta data for '" + tableName + "' in the default schema");
}
}
}
else {
tmd = tableMeta.get(schemaName.toUpperCase());
if (tmd == null) {
throw new DataAccessResourceFailureException("Unable to locate table meta data for '" + tableName + "' in the '" + schemaName + "' schema");
}
}
processTableColumns(databaseMetaData, tmd);
}
}
/**
* Method supporting the metedata processing for a table's columns
*/
private void processTableColumns(DatabaseMetaData databaseMetaData, TableMetaData tmd) {
ResultSet tableColumns = null;
String metaDataCatalogName = metaDataCatalogNameToUse(tmd.getCatalogName());
String metaDataSchemaName = metaDataSchemaNameToUse(tmd.getSchemaName());
String metaDataTableName = tableNameToUse(tmd.getTableName());
if (logger.isDebugEnabled()) {
logger.debug("Retrieving metadata for " + metaDataCatalogName + "/" +
metaDataSchemaName + "/" + metaDataTableName);
}
try {
tableColumns = databaseMetaData.getColumns(
metaDataCatalogName,
metaDataSchemaName,
metaDataTableName,
null);
while (tableColumns.next()) {
TableParameterMetaData meta = new TableParameterMetaData(
tableColumns.getString("COLUMN_NAME"),
tableColumns.getInt("DATA_TYPE"),
tableColumns.getBoolean("NULLABLE")
);
insertParameterMetaData.add(meta);
if (logger.isDebugEnabled()) {
logger.debug("Retrieved metadata: "
+ meta.getParameterName() +
" " + meta.getSqlType() +
" " + meta.isNullable()
);
}
}
}
catch (SQLException se) {
logger.warn("Error while retreiving metadata for procedure columns: " + se.getMessage());
}
finally {
try {
if (tableColumns != null)
tableColumns.close();
}
catch (SQLException se) {
logger.warn("Problem closing resultset for procedure column metadata " + se.getMessage());
}
}
}
/**
* Class representing table meta data
*/
private class TableMetaData {
private String catalogName;
private String schemaName;
private String tableName;
private String type;
public String getCatalogName() {
return catalogName;
}
public void setCatalogName(String catalogName) {
this.catalogName = catalogName;
}
public String getSchemaName() {
return schemaName;
}
public void setSchemaName(String schemaName) {
this.schemaName = schemaName;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
/**
* The HSQL specific implementation of the {@link TableMetaDataProvider}. Suports a feature for
* retreiving generated keys without the JDBC 3.0 getGeneratedKeys support.
*
* @author Thomas Risberg
* @since 2.5
*/
public class HsqlTableMetaDataProvider extends GenericTableMetaDataProvider {
public HsqlTableMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
super(databaseMetaData);
}
public boolean isGetGeneratedKeysSimulated() {
return true;
}
public String getSimpleQueryForGetGeneratedKey(String tableName, String keyColumnName) {
return "select max(identity()) from " + tableName;
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Types;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlInOutParameter;
/**
* Oracle specific implementation for the {@link CallMetaDataProvider} interface.
* This class is intended for internal use by the Simple JDBC classes.
*
* @author Thomas Risberg
* @since 2.5
*/
public class OracleCallMetaDataProvider extends GenericCallMetaDataProvider {
private static final String REF_CURSOR_NAME = "REF CURSOR";
public OracleCallMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
super(databaseMetaData);
}
@Override
public boolean isReturnResultSetSupported() {
return false;
}
@Override
public boolean isRefCursorSupported() {
return true;
}
@Override
public int getRefCursorSqlType() {
return -10;
}
@Override
public String metaDataCatalogNameToUse(String catalogName) {
// Oracle uses catalog name for package name or an empty string if no package
return catalogName == null ? "" : catalogNameToUse(catalogName);
}
@Override
public String metaDataSchemaNameToUse(String schemaName) {
// Use current user schema if no schema specified
return schemaName == null ? getUserName() : super.metaDataSchemaNameToUse(schemaName);
}
@Override
public SqlParameter createDefaultOutParameter(String parameterName, CallParameterMetaData meta) {
if (meta.getSqlType() == Types.OTHER && REF_CURSOR_NAME.equals(meta.getTypeName())) {
return new SqlOutParameter(parameterName, getRefCursorSqlType(), new ColumnMapRowMapper());
}
else {
return super.createDefaultOutParameter(parameterName, meta);
}
}
}

View File

@ -0,0 +1,64 @@
package org.springframework.jdbc.core.metadata;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Types;
/**
* Oracle specific implementation for the {@link org.springframework.jdbc.core.metadata.CallMetaDataProvider} interface.
* This class is intended for internal use by the Simple JDBC classes.
*
* @author Thomas Risberg
* @since 2.5
*/
public class PostgresCallMetaDataProvider extends GenericCallMetaDataProvider {
private static final String RETURN_VALUE_NAME = "returnValue";
public PostgresCallMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
super(databaseMetaData);
}
@Override
public boolean isReturnResultSetSupported() {
return false;
}
@Override
public boolean isRefCursorSupported() {
return true;
}
@Override
public int getRefCursorSqlType() {
return Types.OTHER;
}
@Override
public String metaDataSchemaNameToUse(String schemaName) {
// Use public schema if no schema specified
return schemaName == null ? "public" : super.metaDataSchemaNameToUse(schemaName);
}
@Override
public SqlParameter createDefaultOutParameter(String parameterName, CallParameterMetaData meta) {
if (meta.getSqlType() == Types.OTHER && "refcursor".equals(meta.getTypeName())) {
return new SqlOutParameter(parameterName, getRefCursorSqlType(), new ColumnMapRowMapper());
}
else {
return super.createDefaultOutParameter(parameterName, meta);
}
}
@Override
public boolean byPassReturnParameter(String parameterName) {
return (RETURN_VALUE_NAME.equals(parameterName) ||
super.byPassReturnParameter(parameterName));
}
}

View File

@ -0,0 +1,34 @@
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
/**
* The HSQL specific implementation of the {@link org.springframework.jdbc.core.metadata.TableMetaDataProvider}. Suports a feature for
* retreiving generated keys without the JDBC 3.0 getGeneratedKeys support.
*
* @author Thomas Risberg
* @since 2.5
*/
public class PostgresTableMetaDataProvider extends GenericTableMetaDataProvider {
public PostgresTableMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
super(databaseMetaData);
}
public boolean isGetGeneratedKeysSimulated() {
if (getDatabaseVersion().compareTo("8.2.0") >= 0) {
return true;
}
else {
logger.warn("PostgreSQL does not support getGeneratedKeys or INSERT ... RETURNING in version " + getDatabaseVersion());
return false;
}
}
public String getSimpleQueryForGetGeneratedKey(String tableName, String keyColumnName) {
return "RETURNING " + keyColumnName;
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
/**
* SQL Server specific implementation for the {@link CallMetaDataProvider} interface.
* This class is intended for internal use by the Simple JDBC classes.
*
* @author Thomas Risberg
* @since 2.5
*/
public class SqlServerCallMetaDataProvider extends GenericCallMetaDataProvider {
private static final String REMOVABLE_COLUMN_PREFIX = "@";
private static final String RETURN_VALUE_NAME = "@RETURN_VALUE";
public SqlServerCallMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
super(databaseMetaData);
}
@Override
public String parameterNameToUse(String parameterName) {
if (parameterName == null) {
return null;
}
else if (parameterName.length() > 1 && parameterName.startsWith(REMOVABLE_COLUMN_PREFIX)) {
return super.parameterNameToUse(parameterName.substring(1));
}
else {
return super.parameterNameToUse(parameterName);
}
}
@Override
public boolean byPassReturnParameter(String parameterName) {
return (RETURN_VALUE_NAME.equals(parameterName) || super.byPassReturnParameter(parameterName));
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
/**
* Sybase specific implementation for the {@link CallMetaDataProvider} interface.
* This class is intended for internal use by the Simple JDBC classes.
*
* @author Thomas Risberg
* @since 2.5
*/
public class SybaseCallMetaDataProvider extends GenericCallMetaDataProvider {
private static final String REMOVABLE_COLUMN_PREFIX = "@";
private static final String RETURN_VALUE_NAME = "RETURN_VALUE";
public SybaseCallMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
super(databaseMetaData);
}
@Override
public String parameterNameToUse(String parameterName) {
if (parameterName == null) {
return null;
}
else if (parameterName.length() > 1 && parameterName.startsWith(REMOVABLE_COLUMN_PREFIX)) {
return super.parameterNameToUse(parameterName.substring(1));
}
else {
return super.parameterNameToUse(parameterName);
}
}
@Override
public boolean byPassReturnParameter(String parameterName) {
return (RETURN_VALUE_NAME.equals(parameterName) ||
RETURN_VALUE_NAME.equals(parameterNameToUse(parameterName)) ||
super.byPassReturnParameter(parameterName));
}
}

View File

@ -0,0 +1,347 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.jdbc.support.JdbcUtils;
/**
* Class to manage context metadata used for the configuration
* and execution of operations on a database table.
*
* @author Thomas Risberg
* @since 2.5
*/
public class TableMetaDataContext {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
/** name of procedure to call **/
private String tableName;
/** name of catalog for call **/
private String catalogName;
/** name of schema for call **/
private String schemaName;
/** List of columns objects to be used in this context */
private List<String> tableColumns = new ArrayList<String>();
/** should we access insert parameter meta data info or not */
private boolean accessTableParameterMetaData = true;
/** the provider of call meta data */
private TableMetaDataProvider metaDataProvider;
/** are we using generated key columns */
private boolean generatedKeyColumnsUsed = false;
/**
* Set the name of the table for this context.
*/
public void setTableName(String tableName) {
this.tableName = tableName;
}
/**
* Get the name of the table for this context.
*/
public String getTableName() {
return this.tableName;
}
/**
* Set the name of the catalog for this context.
*/
public void setCatalogName(String catalogName) {
this.catalogName = catalogName;
}
/**
* Get the name of the catalog for this context.
*/
public String getCatalogName() {
return this.catalogName;
}
/**
* Set the name of the schema for this context.
*/
public void setSchemaName(String schemaName) {
this.schemaName = schemaName;
}
/**
* Get the name of the schema for this context.
*/
public String getSchemaName() {
return this.schemaName;
}
/**
* Specify whether we should access table column meta data.
*/
public void setAccessTableParameterMetaData(boolean accessTableParameterMetaData) {
this.accessTableParameterMetaData = accessTableParameterMetaData;
}
/**
* Are we accessing table meta data?
*/
public boolean isAccessTableParameterMetaData() {
return this.accessTableParameterMetaData;
}
/**
* Get a List of the table column names.
*/
public List<String> getTableColumns() {
return this.tableColumns;
}
/**
* Does this database support the JDBC 3.0 feature of retreiving generated keys
* {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}?
*/
public boolean isGetGeneratedKeysSupported() {
return this.metaDataProvider.isGetGeneratedKeysSupported();
}
/**
* Does this database support simple query to retrieve generated keys
* when the JDBC 3.0 feature is not supported
* {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}?
*/
public boolean isGetGeneratedKeysSimulated() {
return this.metaDataProvider.isGetGeneratedKeysSimulated();
}
/**
* Does this database support simple query to retrieve generated keys
* when the JDBC 3.0 feature is not supported
* {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}?
*/
public String getSimulationQueryForGetGeneratedKey(String tableName, String keyColumnName) {
return this.metaDataProvider.getSimpleQueryForGetGeneratedKey(tableName, keyColumnName);
}
/**
* Is a column name String array for retreiving generated keys supported
* {@link java.sql.Connection#createStruct(String, Object[])}?
*/
public boolean isGeneratedKeysColumnNameArraySupported() {
return this.metaDataProvider.isGeneratedKeysColumnNameArraySupported();
}
/**
* Process the current meta data with the provided configuration options
* @param dataSource the DataSource being used
* @param declaredColumns any coluns that are declared
* @param generatedKeyNames name of generated keys
*/
public void processMetaData(DataSource dataSource, List<String> declaredColumns, String[] generatedKeyNames) {
this.metaDataProvider = TableMetaDataProviderFactory.createMetaDataProvider(dataSource, this);
this.tableColumns = reconcileColumnsToUse(declaredColumns, generatedKeyNames);
}
/**
* Compare columns created from metadata with declared columns and return a reconciled list.
* @param declaredColumns declared column names
* @param generatedKeyNames names of generated key columns
*/
private List<String> reconcileColumnsToUse(List<String> declaredColumns, String[] generatedKeyNames) {
if (generatedKeyNames.length > 0) {
generatedKeyColumnsUsed = true;
}
if (declaredColumns.size() > 0) {
return new ArrayList<String>(declaredColumns);
}
Set keys = new HashSet(generatedKeyNames.length);
for (String key : generatedKeyNames) {
keys.add(key.toUpperCase());
}
List<String> columns = new ArrayList<String>();
for (TableParameterMetaData meta : metaDataProvider.getTableParameterMetaData()) {
if (!keys.contains(meta.getParameterName().toUpperCase())) {
columns.add(meta.getParameterName());
}
}
return columns;
}
/**
* Match the provided column names and values with the list of columns used.
* @param parameterSource the parameter names and values
*/
public List<Object> matchInParameterValuesWithInsertColumns(SqlParameterSource parameterSource) {
List<Object> values = new ArrayList<Object>();
// for parameter source lookups we need to provide caseinsensitive lookup support since the
// database metadata is not necessarily providing case sensitive column names
Map caseInsensitiveParameterNames =
SqlParameterSourceUtils.extractCaseInsensitiveParameterNames(parameterSource);
for (String column : tableColumns) {
if (parameterSource.hasValue(column)) {
values.add(SqlParameterSourceUtils.getTypedValue(parameterSource, column));
}
else {
String lowerCaseName = column.toLowerCase();
if (parameterSource.hasValue(lowerCaseName)) {
values.add(SqlParameterSourceUtils.getTypedValue(parameterSource, lowerCaseName));
}
else {
String propertyName = JdbcUtils.convertUnderscoreNameToPropertyName(column);
if (parameterSource.hasValue(propertyName)) {
values.add(SqlParameterSourceUtils.getTypedValue(parameterSource, propertyName));
}
else {
if (caseInsensitiveParameterNames.containsKey(lowerCaseName)) {
values.add(
SqlParameterSourceUtils.getTypedValue(parameterSource,
(String) caseInsensitiveParameterNames.get(lowerCaseName)));
}
else {
values.add(null);
}
}
}
}
}
return values;
}
/**
* Match the provided column names and values with the list of columns used.
* @param inParameters the parameter names and values
*/
public List<Object> matchInParameterValuesWithInsertColumns(Map<String, Object> inParameters) {
List<Object> values = new ArrayList<Object>();
Map<String, Object> source = new HashMap<String, Object>();
for (String key : inParameters.keySet()) {
source.put(key.toLowerCase(), inParameters.get(key));
}
for (String column : tableColumns) {
values.add(source.get(column.toLowerCase()));
}
return values;
}
/**
* Build the insert string based on configuration and metadata information
* @return the insert string to be used
*/
public String createInsertString(String[] generatedKeyNames) {
HashSet<String> keys = new HashSet<String>(generatedKeyNames.length);
for (String key : generatedKeyNames) {
keys.add(key.toUpperCase());
}
StringBuilder insertStatement = new StringBuilder();
insertStatement.append("INSERT INTO ");
if (this.getSchemaName() != null) {
insertStatement.append(this.getSchemaName());
insertStatement.append(".");
}
insertStatement.append(this.getTableName());
insertStatement.append(" (");
int columnCount = 0;
for (String columnName : this.getTableColumns()) {
if (!keys.contains(columnName.toUpperCase())) {
columnCount++;
if (columnCount > 1) {
insertStatement.append(", ");
}
insertStatement.append(columnName);
}
}
insertStatement.append(") VALUES(");
if (columnCount < 1) {
if (generatedKeyColumnsUsed) {
logger.info("Unable to locate non-key columns for table '" +
this.getTableName() + "' so an empty insert statement is generated");
}
else {
throw new InvalidDataAccessApiUsageException("Unable to locate columns for table '" +
this.getTableName() + "' so an insert statement can't be generated");
}
}
for (int i = 0; i < columnCount; i++) {
if (i > 0) {
insertStatement.append(", ");
}
insertStatement.append("?");
}
insertStatement.append(")");
return insertStatement.toString();
}
/**
* Build the array of {@link java.sql.Types} based on configuration and metadata information
* @return the array of types to be used
*/
public int[] createInsertTypes() {
int[] types = new int[this.getTableColumns().size()];
List<TableParameterMetaData> parameters = this.metaDataProvider.getTableParameterMetaData();
Map<String, TableParameterMetaData> parameterMap = new HashMap<String, TableParameterMetaData>(parameters.size());
for (TableParameterMetaData tpmd : parameters) {
parameterMap.put(tpmd.getParameterName().toUpperCase(), tpmd);
}
int typeIndx = 0;
for (String column : this.getTableColumns()) {
if (column == null) {
types[typeIndx] = SqlTypeValue.TYPE_UNKNOWN;
}
else {
TableParameterMetaData tpmd = parameterMap.get(column.toUpperCase());
if (tpmd != null) {
types[typeIndx] = tpmd.getSqlType();
}
else {
types[typeIndx] = SqlTypeValue.TYPE_UNKNOWN;
}
}
typeIndx++;
}
return types;
}
}

View File

@ -0,0 +1,130 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.List;
/**
* Interface specifying the API to be implemented by a class providing table metedata. This is intended for internal use
* by the Simple JDBC classes.
*
* @author Thomas Risberg
* @since 2.5
*/
public interface TableMetaDataProvider {
/**
* Initialize using the database metedata provided
* @param databaseMetaData
* @throws SQLException
*/
void initializeWithMetaData(DatabaseMetaData databaseMetaData) throws SQLException;
/**
* Initialize using provided database metadata, table and column information. This initalization can be
* turned off by specifying that column meta data should not be used.
* @param databaseMetaData used to retreive database specific information
* @param catalogName name of catalog to use or null
* @param schemaName name of schema name to use or null
* @param tableName name of the table
* @throws SQLException
*/
void initializeWithTableColumnMetaData(DatabaseMetaData databaseMetaData, String catalogName, String schemaName, String tableName)
throws SQLException;
/**
* Get the table name formatted based on metadata information. This could include altering the case.
*
* @param tableName
* @return table name formatted
*/
String tableNameToUse(String tableName);
/**
* Get the catalog name formatted based on metadata information. This could include altering the case.
*
* @param catalogName
* @return catalog name formatted
*/
String catalogNameToUse(String catalogName);
/**
* Get the schema name formatted based on metadata information. This could include altering the case.
*
* @param schemaName
* @return schema name formatted
*/
String schemaNameToUse(String schemaName);
/**
* Provide any modification of the catalog name passed in to match the meta data currently used.
* The reyurned value will be used for meta data lookups. This could include alterig the case used or
* providing a base catalog if mone provided.
*
* @param catalogName
* @return catalog name to use
*/
String metaDataCatalogNameToUse(String catalogName) ;
/**
* Provide any modification of the schema name passed in to match the meta data currently used.
* The reyurned value will be used for meta data lookups. This could include alterig the case used or
* providing a base schema if mone provided.
*
* @param schemaName
* @return schema name to use
*/
String metaDataSchemaNameToUse(String schemaName) ;
/**
* Are we using the meta data for the table columns?
*/
boolean isTableColumnMetaDataUsed();
/**
* Does this database support the JDBC 3.0 feature of retreiving generated keys
* {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}
*/
boolean isGetGeneratedKeysSupported();
/**
* Does this database support a simple quey to retreive the generated key whe the JDBC 3.0 feature
* of retreiving generated keys is not supported
* {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}
*/
boolean isGetGeneratedKeysSimulated();
/**
* Get the simple query to retreive a generated key
*/
String getSimpleQueryForGetGeneratedKey(String tableName, String keyColumnName);
/**
* Does this database support a column name String array for retreiving generated keys
* {@link java.sql.Connection#createStruct(String, Object[])}
*/
boolean isGeneratedKeysColumnNameArraySupported();
/**
* Get the table parameter metadata that is currently used.
* @return List of {@link TableParameterMetaData}
*/
List<TableParameterMetaData> getTableParameterMetaData();
}

View File

@ -0,0 +1,85 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.support.DatabaseMetaDataCallback;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;
/**
* Factory used to create a {@link TableMetaDataProvider} implementation based on the type of databse being used.
*
* @author Thomas Risberg
* @since 2.5
*/
public class TableMetaDataProviderFactory {
/** Logger */
private static final Log logger = LogFactory.getLog(TableMetaDataProviderFactory.class);
/**
* Create a TableMetaDataProvider based on the database metedata
* @param dataSource used to retrieve metedata
* @param context the class that holds configuration and metedata
* @return instance of the TableMetaDataProvider implementation to be used
*/
static public TableMetaDataProvider createMetaDataProvider(DataSource dataSource,
final TableMetaDataContext context) {
try {
return (TableMetaDataProvider) JdbcUtils.extractDatabaseMetaData(
dataSource, new DatabaseMetaDataCallback() {
public Object processMetaData(DatabaseMetaData databaseMetaData)
throws SQLException, MetaDataAccessException {
String databaseProductName = JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName());
boolean accessTableColumnMetaData = context.isAccessTableParameterMetaData();
TableMetaDataProvider provider;
if ("HSQL Database Engine".equals(databaseProductName)) {
provider = new HsqlTableMetaDataProvider(databaseMetaData);
}
else if ("PostgreSQL".equals(databaseProductName)) {
provider = new PostgresTableMetaDataProvider(databaseMetaData);
}
else {
provider = new GenericTableMetaDataProvider(databaseMetaData);
}
if (logger.isDebugEnabled()) {
logger.debug("Using " + provider.getClass().getName());
}
provider.initializeWithMetaData(databaseMetaData);
if (accessTableColumnMetaData) {
provider.initializeWithTableColumnMetaData(databaseMetaData, context.getCatalogName(), context.getSchemaName(), context.getTableName());
}
return provider;
}
});
} catch (MetaDataAccessException e) {
throw new DataAccessResourceFailureException("Error retreiving database metadata", e);
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.metadata;
/**
* Holder of metadata for a specific parameter that is used for table processing.
*
* @author Thomas Risberg
* @since 2.5
*/
public class TableParameterMetaData {
private final String parameterName;
private final int sqlType;
private final boolean nullable;
/**
* Constructor taking all the properties.
*/
public TableParameterMetaData(String columnName, int sqlType, boolean nullable) {
this.parameterName = columnName;
this.sqlType = sqlType;
this.nullable = nullable;
}
/**
* Get the parameter name.
*/
public String getParameterName() {
return this.parameterName;
}
/**
* Get the parameter SQL type.
*/
public int getSqlType() {
return this.sqlType;
}
/**
* Get whether the parameter/column is nullable.
*/
public boolean isNullable() {
return this.nullable;
}
}

View File

@ -0,0 +1,7 @@
<html>
<body>
Context metadata abstraction for the configuration and execution of a stored procedure call.
</body>
</html>

View File

@ -0,0 +1,84 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.namedparam;
import java.util.HashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* Abstract base class for {@link SqlParameterSource} implementations.
* Provides registration of SQL types per parameter.
*
* @author Juergen Hoeller
* @since 2.0
*/
public abstract class AbstractSqlParameterSource implements SqlParameterSource {
private final Map sqlTypes = new HashMap();
private final Map typeNames = new HashMap();
/**
* Register a SQL type for the given parameter.
* @param paramName the name of the parameter
* @param sqlType the SQL type of the parameter
*/
public void registerSqlType(String paramName, int sqlType) {
Assert.notNull(paramName, "Parameter name must not be null");
this.sqlTypes.put(paramName, new Integer(sqlType));
}
/**
* Register a SQL type for the given parameter.
* @param paramName the name of the parameter
* @param typeName the type name of the parameter
*/
public void registerTypeName(String paramName, String typeName) {
Assert.notNull(paramName, "Parameter name must not be null");
this.typeNames.put(paramName, typeName);
}
/**
* Return the SQL type for the given parameter, if registered.
* @param paramName the name of the parameter
* @return the SQL type of the parameter,
* or <code>TYPE_UNKNOWN</code> if not registered
*/
public int getSqlType(String paramName) {
Assert.notNull(paramName, "Parameter name must not be null");
Integer sqlType = (Integer) this.sqlTypes.get(paramName);
if (sqlType != null) {
return sqlType.intValue();
}
return TYPE_UNKNOWN;
}
/**
* Return the type name for the given parameter, if registered.
* @param paramName the name of the parameter
* @return the type name of the parameter,
* or <code>null</code> if not registered
*/
public String getTypeName(String paramName) {
Assert.notNull(paramName, "Parameter name must not be null");
return (String) this.typeNames.get(paramName);
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.namedparam;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.NotReadablePropertyException;
import org.springframework.beans.PropertyAccessor;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.jdbc.core.StatementCreatorUtils;
/**
* {@link SqlParameterSource} implementation that obtains parameter values
* from bean properties of a given JavaBean object. The names of the bean
* properties have to match the parameter names.
*
* <p>Uses a Spring BeanWrapper for bean property access underneath.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.0
* @see NamedParameterJdbcTemplate
* @see org.springframework.beans.BeanWrapper
*/
public class BeanPropertySqlParameterSource extends AbstractSqlParameterSource {
private final BeanWrapper beanWrapper;
private String[] propertyNames;
/**
* Create a new BeanPropertySqlParameterSource for the given bean.
* @param object the bean instance to wrap
*/
public BeanPropertySqlParameterSource(Object object) {
this.beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(object);
}
public boolean hasValue(String paramName) {
return this.beanWrapper.isReadableProperty(paramName);
}
public Object getValue(String paramName) throws IllegalArgumentException {
try {
return this.beanWrapper.getPropertyValue(paramName);
}
catch (NotReadablePropertyException ex) {
throw new IllegalArgumentException(ex.getMessage());
}
}
/**
* Provide access to the property names of the wrapped bean.
* Uses support provided in the {@link PropertyAccessor} interface.
* @return an array containing all the known property names
*/
public String[] getReadablePropertyNames() {
if (this.propertyNames == null) {
List names = new ArrayList();
PropertyDescriptor[] props = this.beanWrapper.getPropertyDescriptors();
for (int i = 0; i < props.length; i++) {
if (this.beanWrapper.isReadableProperty(props[i].getName())) {
names.add(props[i].getName());
}
}
this.propertyNames = (String[]) names.toArray(new String[names.size()]);
}
return this.propertyNames;
}
/**
* Derives a default SQL type from the corresponding property type.
* @see org.springframework.jdbc.core.StatementCreatorUtils#javaTypeToSqlParameterType
*/
public int getSqlType(String paramName) {
int sqlType = super.getSqlType(paramName);
if (sqlType != TYPE_UNKNOWN) {
return sqlType;
}
Class propType = this.beanWrapper.getPropertyType(paramName);
return StatementCreatorUtils.javaTypeToSqlParameterType(propType);
}
}

View File

@ -0,0 +1,165 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.namedparam;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import org.springframework.util.Assert;
import org.springframework.jdbc.core.SqlParameterValue;
/**
* {@link SqlParameterSource} implementation that holds a given Map of parameters.
*
* <p>This class is intended for passing in a simple Map of parameter values
* to the methods of the {@link NamedParameterJdbcTemplate} class.
*
* <p>The <code>addValue</code> methods on this class will make adding several
* values easier. The methods return a reference to the {@link MapSqlParameterSource}
* itself, so you can chain several method calls together within a single statement.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.0
* @see #addValue(String, Object)
* @see #addValue(String, Object, int)
* @see #registerSqlType
* @see NamedParameterJdbcTemplate
*/
public class MapSqlParameterSource extends AbstractSqlParameterSource {
private final Map values = new HashMap();
/**
* Create an empty MapSqlParameterSource,
* with values to be added via <code>addValue</code>.
* @see #addValue(String, Object)
*/
public MapSqlParameterSource() {
}
/**
* Create a new MapSqlParameterSource, with one value
* comprised of the supplied arguments.
* @param paramName the name of the parameter
* @param value the value of the parameter
* @see #addValue(String, Object)
*/
public MapSqlParameterSource(String paramName, Object value) {
addValue(paramName, value);
}
/**
* Create a new MapSqlParameterSource based on a Map.
* @param values a Map holding existing parameter values (can be <code>null</code>)
*/
public MapSqlParameterSource(Map values) {
addValues(values);
}
/**
* Add a parameter to this parameter source.
* @param paramName the name of the parameter
* @param value the value of the parameter
* @return a reference to this parameter source,
* so it's possible to chain several calls together
*/
public MapSqlParameterSource addValue(String paramName, Object value) {
Assert.notNull(paramName, "Parameter name must not be null");
this.values.put(paramName, value);
if (value != null && value instanceof SqlParameterValue) {
registerSqlType(paramName, ((SqlParameterValue)value).getSqlType());
}
return this;
}
/**
* Add a parameter to this parameter source.
* @param paramName the name of the parameter
* @param value the value of the parameter
* @param sqlType the SQL type of the parameter
* @return a reference to this parameter source,
* so it's possible to chain several calls together
*/
public MapSqlParameterSource addValue(String paramName, Object value, int sqlType) {
Assert.notNull(paramName, "Parameter name must not be null");
this.values.put(paramName, value);
registerSqlType(paramName, sqlType);
return this;
}
/**
* Add a parameter to this parameter source.
* @param paramName the name of the parameter
* @param value the value of the parameter
* @param sqlType the SQL type of the parameter
* @param typeName the type name of the parameter
* @return a reference to this parameter source,
* so it's possible to chain several calls together
*/
public MapSqlParameterSource addValue(String paramName, Object value, int sqlType, String typeName) {
Assert.notNull(paramName, "Parameter name must not be null");
this.values.put(paramName, value);
registerSqlType(paramName, sqlType);
registerTypeName(paramName, typeName);
return this;
}
/**
* Add a Map of parameters to this parameter source.
* @param values a Map holding existing parameter values (can be <code>null</code>)
* @return a reference to this parameter source,
* so it's possible to chain several calls together
*/
public MapSqlParameterSource addValues(Map values) {
if (values != null) {
this.values.putAll(values);
for (Iterator iter = values.keySet().iterator(); iter.hasNext();) {
Object k = iter.next();
Object o = values.get(k);
if (o != null && k instanceof String && o instanceof SqlParameterValue) {
registerSqlType((String)k, ((SqlParameterValue)o).getSqlType());
}
}
}
return this;
}
/**
* Expose the current parameter values as read-only Map.
*/
public Map getValues() {
return Collections.unmodifiableMap(this.values);
}
public boolean hasValue(String paramName) {
return this.values.containsKey(paramName);
}
public Object getValue(String paramName) {
if (!hasValue(paramName)) {
throw new IllegalArgumentException("No value registered for key '" + paramName + "'");
}
return this.values.get(paramName);
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.namedparam;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* Extension of JdbcDaoSupport that exposes a NamedParameterJdbcTemplate as well.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.0
* @see NamedParameterJdbcTemplate
*/
public class NamedParameterJdbcDaoSupport extends JdbcDaoSupport {
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
/**
* Create a NamedParameterJdbcTemplate based on the configured JdbcTemplate.
*/
protected void initTemplateConfig() {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(getJdbcTemplate());
}
/**
* Return a NamedParameterJdbcTemplate wrapping the configured JdbcTemplate.
*/
public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
return namedParameterJdbcTemplate;
}
}

View File

@ -0,0 +1,492 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.namedparam;
import java.util.List;
import java.util.Map;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.jdbc.support.rowset.SqlRowSet;
/**
* Interface specifying a basic set of JDBC operations allowing the use
* of named parameters rather than the traditional '?' placeholders.
*
* <p>This is an alternative to the classic
* {@link org.springframework.jdbc.core.JdbcOperations} interface,
* implemented by {@link NamedParameterJdbcTemplate}. This interface is not
* often used directly, but provides a useful option to enhance testability,
* as it can easily be mocked or stubbed.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.0
* @see NamedParameterJdbcTemplate
* @see org.springframework.jdbc.core.JdbcOperations
*/
public interface NamedParameterJdbcOperations {
/**
* Expose the classic Spring JdbcTemplate to allow invocation of
* classic JDBC operations.
*/
JdbcOperations getJdbcOperations();
/**
* Execute a JDBC data access operation, implemented as callback action
* working on a JDBC PreparedStatement. This allows for implementing arbitrary
* data access operations on a single Statement, within Spring's managed
* JDBC environment: that is, participating in Spring-managed transactions
* and converting JDBC SQLExceptions into Spring's DataAccessException hierarchy.
* <p>The callback action can return a result object, for example a
* domain object or a collection of domain objects.
* @param sql SQL to execute
* @param paramSource container of arguments to bind to the query
* @param action callback object that specifies the action
* @return a result object returned by the action, or <code>null</code>
* @throws DataAccessException if there is any problem
*/
Object execute(String sql, SqlParameterSource paramSource, PreparedStatementCallback action)
throws DataAccessException;
/**
* Execute a JDBC data access operation, implemented as callback action
* working on a JDBC PreparedStatement. This allows for implementing arbitrary
* data access operations on a single Statement, within Spring's managed
* JDBC environment: that is, participating in Spring-managed transactions
* and converting JDBC SQLExceptions into Spring's DataAccessException hierarchy.
* <p>The callback action can return a result object, for example a
* domain object or a collection of domain objects.
* @param sql SQL to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @param action callback object that specifies the action
* @return a result object returned by the action, or <code>null</code>
* @throws DataAccessException if there is any problem
*/
Object execute(String sql, Map paramMap, PreparedStatementCallback action)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, reading the ResultSet with a
* ResultSetExtractor.
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @param rse object that will extract results
* @return an arbitrary result object, as returned by the ResultSetExtractor
* @throws DataAccessException if the query fails
*/
Object query(String sql, SqlParameterSource paramSource, ResultSetExtractor rse)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, reading the ResultSet with a
* ResultSetExtractor.
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @param rse object that will extract results
* @return an arbitrary result object, as returned by the ResultSetExtractor
* @throws org.springframework.dao.DataAccessException if the query fails
*/
Object query(String sql, Map paramMap, ResultSetExtractor rse) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list of
* arguments to bind to the query, reading the ResultSet on a per-row basis
* with a RowCallbackHandler.
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @param rch object that will extract results, one row at a time
* @throws DataAccessException if the query fails
*/
void query(String sql, SqlParameterSource paramSource, RowCallbackHandler rch)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list of
* arguments to bind to the query, reading the ResultSet on a per-row basis
* with a RowCallbackHandler.
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @param rch object that will extract results, one row at a time
* @throws org.springframework.dao.DataAccessException if the query fails
*/
void query(String sql, Map paramMap, RowCallbackHandler rch) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, mapping each row to a Java object
* via a RowMapper.
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @param rowMapper object that will map one object per row
* @return the result List, containing mapped objects
* @throws org.springframework.dao.DataAccessException if the query fails
*/
List query(String sql, SqlParameterSource paramSource, RowMapper rowMapper)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, mapping each row to a Java object
* via a RowMapper.
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @param rowMapper object that will map one object per row
* @return the result List, containing mapped objects
* @throws org.springframework.dao.DataAccessException if the query fails
*/
List query(String sql, Map paramMap, RowMapper rowMapper) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, mapping a single result row to a
* Java object via a RowMapper.
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @param rowMapper object that will map one object per row
* @return the single mapped object
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
* if the query does not return exactly one row, or does not return exactly
* one column in that row
* @throws org.springframework.dao.DataAccessException if the query fails
*/
Object queryForObject(String sql, SqlParameterSource paramSource, RowMapper rowMapper)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a list
* of arguments to bind to the query, mapping a single result row to a
* Java object via a RowMapper.
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @param rowMapper object that will map one object per row
* @return the single mapped object
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
* if the query does not return exactly one row, or does not return exactly
* one column in that row
* @throws org.springframework.dao.DataAccessException if the query fails
*/
Object queryForObject(String sql, Map paramMap, RowMapper rowMapper) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result object.
* <p>The query is expected to be a single row/single column query; the returned
* result will be directly mapped to the corresponding object type.
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @param requiredType the type that the result object is expected to match
* @return the result object of the required type, or <code>null</code> in case of SQL NULL
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
* if the query does not return exactly one row, or does not return exactly
* one column in that row
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForObject(String, Class)
*/
Object queryForObject(String sql, SqlParameterSource paramSource, Class requiredType)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result object.
* <p>The query is expected to be a single row/single column query; the returned
* result will be directly mapped to the corresponding object type.
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @param requiredType the type that the result object is expected to match
* @return the result object of the required type, or <code>null</code> in case of SQL NULL
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
* if the query does not return exactly one row, or does not return exactly
* one column in that row
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForObject(String, Class)
*/
Object queryForObject(String sql, Map paramMap, Class requiredType) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result Map.
* <p>The query is expected to be a single row query; the result row will be
* mapped to a Map (one entry for each column, using the column name as the key).
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @return the result Map (one entry for each column, using the column name as the key)
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
* if the query does not return exactly one row, or does not return exactly
* one column in that row
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForMap(String)
* @see org.springframework.jdbc.core.ColumnMapRowMapper
*/
Map queryForMap(String sql, SqlParameterSource paramSource) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result Map.
* The queryForMap() methods defined by this interface are appropriate
* when you don't have a domain model. Otherwise, consider using
* one of the queryForObject() methods.
* <p>The query is expected to be a single row query; the result row will be
* mapped to a Map (one entry for each column, using the column name as the key).
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @return the result Map (one entry for each column, using the column name as the key)
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
* if the query does not return exactly one row, or does not return exactly
* one column in that row
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForMap(String)
* @see org.springframework.jdbc.core.ColumnMapRowMapper
*/
Map queryForMap(String sql, Map paramMap) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, resulting in a long value.
* <p>The query is expected to be a single row/single column query that
* results in a long value.
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @return the long value, or 0 in case of SQL NULL
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
* if the query does not return exactly one row, or does not return exactly
* one column in that row
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForLong(String)
*/
long queryForLong(String sql, SqlParameterSource paramSource) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, resulting in a long value.
* <p>The query is expected to be a single row/single column query that
* results in a long value.
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @return the long value, or 0 in case of SQL NULL
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
* if the query does not return exactly one row, or does not return exactly
* one column in that row
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForLong(String)
*/
long queryForLong(String sql, Map paramMap) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, resulting in an int value.
* <p>The query is expected to be a single row/single column query that
* results in an int value.
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @return the int value, or 0 in case of SQL NULL
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForInt(String)
*/
int queryForInt(String sql, SqlParameterSource paramSource) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, resulting in an int value.
* <p>The query is expected to be a single row/single column query that
* results in an int value.
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @return the int value, or 0 in case of SQL NULL
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if the query does not return
* exactly one row, or does not return exactly one column in that row
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForInt(String)
*/
int queryForInt(String sql, Map paramMap) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result list.
* <p>The results will be mapped to a List (one entry for each row) of
* result objects, each of them matching the specified element type.
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @param elementType the required type of element in the result list
* (for example, <code>Integer.class</code>)
* @return a List of objects that match the specified element type
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForList(String, Class)
* @see org.springframework.jdbc.core.SingleColumnRowMapper
*/
List queryForList(String sql, SqlParameterSource paramSource, Class elementType)
throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result list.
* <p>The results will be mapped to a List (one entry for each row) of
* result objects, each of them matching the specified element type.
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @param elementType the required type of element in the result list
* (for example, <code>Integer.class</code>)
* @return a List of objects that match the specified element type
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForList(String, Class)
* @see org.springframework.jdbc.core.SingleColumnRowMapper
*/
List queryForList(String sql, Map paramMap, Class elementType) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result list.
* <p>The results will be mapped to a List (one entry for each row) of
* Maps (one entry for each column, using the column name as the key).
* Thus Each element in the list will be of the form returned by this interface's
* queryForMap() methods.
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @return a List that contains a Map per row
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForList(String)
*/
List queryForList(String sql, SqlParameterSource paramSource) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a result list.
* <p>The results will be mapped to a List (one entry for each row) of
* Maps (one entry for each column, using the column name as the key).
* Each element in the list will be of the form returned by this interface's
* queryForMap() methods.
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @return a List that contains a Map per row
* @throws org.springframework.dao.DataAccessException if the query fails
* @see org.springframework.jdbc.core.JdbcTemplate#queryForList(String)
*/
List queryForList(String sql, Map paramMap) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a SqlRowSet.
* <p>The results will be mapped to an SqlRowSet which holds the data in a
* disconnected fashion. This wrapper will translate any SQLExceptions thrown.
* <p>Note that that, for the default implementation, JDBC RowSet support needs to
* be available at runtime: by default, Sun's <code>com.sun.rowset.CachedRowSetImpl</code>
* class is used, which is part of JDK 1.5+ and also available separately as part of
* Sun's JDBC RowSet Implementations download (rowset.jar).
* @param sql SQL query to execute
* @param paramSource container of arguments to bind to the query
* @return a SqlRowSet representation (possibly a wrapper around a
* <code>javax.sql.rowset.CachedRowSet</code>)
* @throws org.springframework.dao.DataAccessException if there is any problem executing the query
* @see org.springframework.jdbc.core.JdbcTemplate#queryForRowSet(String)
* @see org.springframework.jdbc.core.SqlRowSetResultSetExtractor
* @see javax.sql.rowset.CachedRowSet
*/
SqlRowSet queryForRowSet(String sql, SqlParameterSource paramSource) throws DataAccessException;
/**
* Query given SQL to create a prepared statement from SQL and a
* list of arguments to bind to the query, expecting a SqlRowSet.
* <p>The results will be mapped to an SqlRowSet which holds the data in a
* disconnected fashion. This wrapper will translate any SQLExceptions thrown.
* <p>Note that that, for the default implementation, JDBC RowSet support needs to
* be available at runtime: by default, Sun's <code>com.sun.rowset.CachedRowSetImpl</code>
* class is used, which is part of JDK 1.5+ and also available separately as part of
* Sun's JDBC RowSet Implementations download (rowset.jar).
* @param sql SQL query to execute
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @return a SqlRowSet representation (possibly a wrapper around a
* <code>javax.sql.rowset.CachedRowSet</code>)
* @throws org.springframework.dao.DataAccessException if there is any problem executing the query
* @see org.springframework.jdbc.core.JdbcTemplate#queryForRowSet(String)
* @see org.springframework.jdbc.core.SqlRowSetResultSetExtractor
* @see javax.sql.rowset.CachedRowSet
*/
SqlRowSet queryForRowSet(String sql, Map paramMap) throws DataAccessException;
/**
* Issue an update via a prepared statement, binding the given arguments.
* @param sql SQL containing named parameters
* @param paramSource container of arguments and SQL types to bind to the query
* @return the number of rows affected
* @throws org.springframework.dao.DataAccessException if there is any problem issuing the update
*/
int update(String sql, SqlParameterSource paramSource) throws DataAccessException;
/**
* Issue an update via a prepared statement, binding the given arguments.
* @param sql SQL containing named parameters
* @param paramMap map of parameters to bind to the query
* (leaving it to the PreparedStatement to guess the corresponding SQL type)
* @return the number of rows affected
* @throws org.springframework.dao.DataAccessException if there is any problem issuing the update
*/
int update(String sql, Map paramMap) throws DataAccessException;
/**
* Issue an update via a prepared statement, binding the given arguments,
* returning generated keys.
* @param sql SQL containing named parameters
* @param paramSource container of arguments and SQL types to bind to the query
* @param generatedKeyHolder KeyHolder that will hold the generated keys
* @return the number of rows affected
* @throws org.springframework.dao.DataAccessException if there is any problem issuing the update
* @see MapSqlParameterSource
* @see org.springframework.jdbc.support.GeneratedKeyHolder
*/
int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder)
throws DataAccessException;
/**
* Issue an update via a prepared statement, binding the given arguments,
* returning generated keys.
* @param sql SQL containing named parameters
* @param paramSource container of arguments and SQL types to bind to the query
* @param generatedKeyHolder KeyHolder that will hold the generated keys
* @param keyColumnNames names of the columns that will have keys generated for them
* @return the number of rows affected
* @throws org.springframework.dao.DataAccessException if there is any problem issuing the update
* @see MapSqlParameterSource
* @see org.springframework.jdbc.support.GeneratedKeyHolder
*/
int update(
String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder, String[] keyColumnNames)
throws DataAccessException;
}

View File

@ -0,0 +1,278 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.namedparam;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.jdbc.core.SqlRowSetResultSetExtractor;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.util.Assert;
/**
* Template class with a basic set of JDBC operations, allowing the use
* of named parameters rather than traditional '?' placeholders.
*
* <p>This class delegates to a wrapped {@link #getJdbcOperations() JdbcTemplate}
* once the substitution from named parameters to JDBC style '?' placeholders is
* done at execution time. It also allows for expanding a {@link java.util.List}
* of values to the appropriate number of placeholders.
*
* <p>The underlying {@link org.springframework.jdbc.core.JdbcTemplate} is
* exposed to allow for convenient access to the traditional
* {@link org.springframework.jdbc.core.JdbcTemplate} methods.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.0
* @see NamedParameterJdbcOperations
* @see org.springframework.jdbc.core.JdbcTemplate
*/
public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations {
/** The JdbcTemplate we are wrapping */
private final JdbcOperations classicJdbcTemplate;
/** Map of original SQL String to ParsedSql representation */
private final Map parsedSqlCache = new HashMap();
/**
* Create a new NamedParameterJdbcTemplate for the given {@link DataSource}.
* <p>Creates a classic Spring {@link org.springframework.jdbc.core.JdbcTemplate} and wraps it.
* @param dataSource the JDBC DataSource to access
*/
public NamedParameterJdbcTemplate(DataSource dataSource) {
Assert.notNull(dataSource, "The [dataSource] argument cannot be null.");
this.classicJdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* Create a new NamedParameterJdbcTemplate for the given classic
* Spring {@link org.springframework.jdbc.core.JdbcTemplate}.
* @param classicJdbcTemplate the classic Spring JdbcTemplate to wrap
*/
public NamedParameterJdbcTemplate(JdbcOperations classicJdbcTemplate) {
Assert.notNull(classicJdbcTemplate, "JdbcTemplate must not be null");
this.classicJdbcTemplate = classicJdbcTemplate;
}
/**
* Expose the classic Spring JdbcTemplate to allow invocation of
* less commonly used methods.
*/
public JdbcOperations getJdbcOperations() {
return this.classicJdbcTemplate;
}
public Object execute(String sql, SqlParameterSource paramSource, PreparedStatementCallback action)
throws DataAccessException {
return getJdbcOperations().execute(getPreparedStatementCreator(sql, paramSource), action);
}
public Object execute(String sql, Map paramMap, PreparedStatementCallback action) throws DataAccessException {
return execute(sql, new MapSqlParameterSource(paramMap), action);
}
public Object query(String sql, SqlParameterSource paramSource, ResultSetExtractor rse)
throws DataAccessException {
return getJdbcOperations().query(getPreparedStatementCreator(sql, paramSource), rse);
}
public Object query(String sql, Map paramMap, ResultSetExtractor rse) throws DataAccessException {
return query(sql, new MapSqlParameterSource(paramMap), rse);
}
public void query(String sql, SqlParameterSource paramSource, RowCallbackHandler rch)
throws DataAccessException {
getJdbcOperations().query(getPreparedStatementCreator(sql, paramSource), rch);
}
public void query(String sql, Map paramMap, RowCallbackHandler rch) throws DataAccessException {
query(sql, new MapSqlParameterSource(paramMap), rch);
}
public List query(String sql, SqlParameterSource paramSource, RowMapper rowMapper)
throws DataAccessException {
return getJdbcOperations().query(getPreparedStatementCreator(sql, paramSource), rowMapper);
}
public List query(String sql, Map paramMap, RowMapper rowMapper) throws DataAccessException {
return query(sql, new MapSqlParameterSource(paramMap), rowMapper);
}
public Object queryForObject(String sql, SqlParameterSource paramSource, RowMapper rowMapper)
throws DataAccessException {
List results = getJdbcOperations().query(getPreparedStatementCreator(sql, paramSource), rowMapper);
return DataAccessUtils.requiredSingleResult(results);
}
public Object queryForObject(String sql, Map paramMap, RowMapper rowMapper) throws DataAccessException {
return queryForObject(sql, new MapSqlParameterSource(paramMap), rowMapper);
}
public Object queryForObject(String sql, SqlParameterSource paramSource, Class requiredType)
throws DataAccessException {
return queryForObject(sql, paramSource, new SingleColumnRowMapper(requiredType));
}
public Object queryForObject(String sql, Map paramMap, Class requiredType) throws DataAccessException {
return queryForObject(sql, paramMap, new SingleColumnRowMapper(requiredType));
}
public Map queryForMap(String sql, SqlParameterSource paramSource) throws DataAccessException {
return (Map) queryForObject(sql, paramSource, new ColumnMapRowMapper());
}
public Map queryForMap(String sql, Map paramMap) throws DataAccessException {
return (Map) queryForObject(sql, paramMap, new ColumnMapRowMapper());
}
public long queryForLong(String sql, SqlParameterSource paramSource) throws DataAccessException {
Number number = (Number) queryForObject(sql, paramSource, Number.class);
return (number != null ? number.longValue() : 0);
}
public long queryForLong(String sql, Map paramMap) throws DataAccessException {
return queryForLong(sql, new MapSqlParameterSource(paramMap));
}
public int queryForInt(String sql, SqlParameterSource paramSource) throws DataAccessException {
Number number = (Number) queryForObject(sql, paramSource, Number.class);
return (number != null ? number.intValue() : 0);
}
public int queryForInt(String sql, Map paramMap) throws DataAccessException {
return queryForInt(sql, new MapSqlParameterSource(paramMap));
}
public List queryForList(String sql, SqlParameterSource paramSource, Class elementType)
throws DataAccessException {
return query(sql, paramSource, new SingleColumnRowMapper(elementType));
}
public List queryForList(String sql, Map paramMap, Class elementType) throws DataAccessException {
return queryForList(sql, new MapSqlParameterSource(paramMap), elementType);
}
public List queryForList(String sql, SqlParameterSource paramSource) throws DataAccessException {
return query(sql, paramSource, new ColumnMapRowMapper());
}
public List queryForList(String sql, Map paramMap) throws DataAccessException {
return queryForList(sql, new MapSqlParameterSource(paramMap));
}
public SqlRowSet queryForRowSet(String sql, SqlParameterSource paramSource) throws DataAccessException {
return (SqlRowSet) getJdbcOperations().query(
getPreparedStatementCreator(sql, paramSource), new SqlRowSetResultSetExtractor());
}
public SqlRowSet queryForRowSet(String sql, Map paramMap) throws DataAccessException {
return queryForRowSet(sql, new MapSqlParameterSource(paramMap));
}
public int update(String sql, SqlParameterSource paramSource) throws DataAccessException {
return getJdbcOperations().update(getPreparedStatementCreator(sql, paramSource));
}
public int update(String sql, Map paramMap) throws DataAccessException {
return update(sql, new MapSqlParameterSource(paramMap));
}
public int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder)
throws DataAccessException {
return update(sql, paramSource, generatedKeyHolder, null);
}
public int update(
String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder, String[] keyColumnNames)
throws DataAccessException {
ParsedSql parsedSql = getParsedSql(sql);
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null);
int[] paramTypes = NamedParameterUtils.buildSqlTypeArray(parsedSql, paramSource);
PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(sqlToUse, paramTypes);
if (keyColumnNames != null) {
pscf.setGeneratedKeysColumnNames(keyColumnNames);
}
else {
pscf.setReturnGeneratedKeys(true);
}
return getJdbcOperations().update(pscf.newPreparedStatementCreator(params), generatedKeyHolder);
}
/**
* Build a PreparedStatementCreator based on the given SQL and named parameters.
* <p>Note: Not used for the <code>update</code> variant with generated key handling.
* @param sql SQL to execute
* @param paramSource container of arguments to bind
* @return the corresponding PreparedStatementCreator
*/
protected PreparedStatementCreator getPreparedStatementCreator(String sql, SqlParameterSource paramSource) {
ParsedSql parsedSql = getParsedSql(sql);
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null);
int[] paramTypes = NamedParameterUtils.buildSqlTypeArray(parsedSql, paramSource);
PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(sqlToUse, paramTypes);
return pscf.newPreparedStatementCreator(params);
}
/**
* Obtain a parsed representation of the given SQL statement.
* @param sql the original SQL
* @return a representation of the parsed SQL statement
*/
protected ParsedSql getParsedSql(String sql) {
synchronized (this.parsedSqlCache) {
ParsedSql parsedSql = (ParsedSql) this.parsedSqlCache.get(sql);
if (parsedSql == null) {
parsedSql = NamedParameterUtils.parseSqlStatement(sql);
this.parsedSqlCache.put(sql, parsedSql);
}
return parsedSql;
}
}
}

View File

@ -0,0 +1,386 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.namedparam;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.util.Assert;
/**
* Helper methods for named parameter parsing.
* Only intended for internal use within Spring's JDBC framework.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.0
*/
public abstract class NamedParameterUtils {
/**
* Set of characters that qualify as parameter separators,
* indicating that a parameter name in a SQL String has ended.
*/
private static final char[] PARAMETER_SEPARATORS =
new char[] {'"', '\'', ':', '&', ',', ';', '(', ')', '|', '=', '+', '-', '*', '%', '/', '\\', '<', '>', '^'};
/**
* Set of characters that qualify as comment or quotes starting characters.
*/
private static final String[] START_SKIP =
new String[] {"'", "\"", "--", "/*"};
/**
* Set of characters that at are the corresponding comment or quotes ending characters.
*/
private static final String[] STOP_SKIP =
new String[] {"'", "\"", "\n", "*/"};
//-------------------------------------------------------------------------
// Core methods used by NamedParameterJdbcTemplate and SqlQuery/SqlUpdate
//-------------------------------------------------------------------------
/**
* Parse the SQL statement and locate any placeholders or named parameters.
* Named parameters are substituted for a JDBC placeholder.
* @param sql the SQL statement
* @return the parsed statement, represented as ParsedSql instance
*/
public static ParsedSql parseSqlStatement(String sql) {
Assert.notNull(sql, "SQL must not be null");
Set namedParameters = new HashSet();
ParsedSql parsedSql = new ParsedSql(sql);
char[] statement = sql.toCharArray();
int namedParameterCount = 0;
int unnamedParameterCount = 0;
int totalParameterCount = 0;
int i = 0;
while (i < statement.length) {
int skipToPosition = skipCommentsAndQuotes(statement, i);
if (i != skipToPosition) {
if (skipToPosition >= statement.length) {
break;
}
i = skipToPosition;
}
char c = statement[i];
if (c == ':' || c == '&') {
int j = i + 1;
if (j < statement.length && statement[j] == ':' && c == ':') {
// Postgres-style "::" casting operator - to be skipped.
i = i + 2;
continue;
}
while (j < statement.length && !isParameterSeparator(statement[j])) {
j++;
}
if (j - i > 1) {
String parameter = sql.substring(i + 1, j);
if (!namedParameters.contains(parameter)) {
namedParameters.add(parameter);
namedParameterCount++;
}
parsedSql.addNamedParameter(parameter, i, j);
totalParameterCount++;
}
i = j - 1;
}
else {
if (c == '?') {
unnamedParameterCount++;
totalParameterCount++;
}
}
i++;
}
parsedSql.setNamedParameterCount(namedParameterCount);
parsedSql.setUnnamedParameterCount(unnamedParameterCount);
parsedSql.setTotalParameterCount(totalParameterCount);
return parsedSql;
}
/**
* Skip over comments and quoted names present in an SQL statement
* @param statement character array containing SQL statement
* @param position current position of statement
* @return next position to process after any comments or quotes are skipped
*/
private static int skipCommentsAndQuotes(char[] statement, int position) {
for (int i = 0; i < START_SKIP.length; i++) {
if (statement[position] == START_SKIP[i].charAt(0)) {
boolean match = true;
for (int j = 1; j < START_SKIP[i].length(); j++) {
if (!(statement[position + j] == START_SKIP[i].charAt(j))) {
match = false;
break;
}
}
if (match) {
int offset = START_SKIP[i].length();
for (int m = position + offset; m < statement.length; m++) {
if (statement[m] == STOP_SKIP[i].charAt(0)) {
boolean endMatch = true;
int endPos = m;
for (int n = 1; n < STOP_SKIP[i].length(); n++) {
if (m + n >= statement.length) {
// last comment not closed properly
return statement.length;
}
if (!(statement[m + n] == STOP_SKIP[i].charAt(n))) {
endMatch = false;
break;
}
endPos = m + n;
}
if (endMatch) {
// found character sequence ending comment or quote
return endPos + 1;
}
}
}
// character sequence ending comment or quote not found
return statement.length;
}
}
}
return position;
}
/**
* Parse the SQL statement and locate any placeholders or named parameters.
* Named parameters are substituted for a JDBC placeholder and any select list
* is expanded to the required number of placeholders. Select lists may contain
* an array of objects and in that case the placeholders will be grouped and
* enclosed with parantheses. This allows for the use of "expression lists" in
* the SQL statement like:<br/>
* select id, name, state from table where (name, age) in (('John', 35), ('Ann', 50))
* <p>The parameter values passed in are used to determine the number of
* placeholder to be used for a select list. Select lists should be limited
* to 100 or fewer elements. A larger number of elements is not guaramteed to
* be supported by the database and is strictly vendor-dependent.
* @param parsedSql the parsed represenation of the SQL statement
* @param paramSource the source for named parameters
* @return the SQL statement with substituted parameters
* @see #parseSqlStatement
*/
public static String substituteNamedParameters(ParsedSql parsedSql, SqlParameterSource paramSource) {
String originalSql = parsedSql.getOriginalSql();
StringBuffer actualSql = new StringBuffer();
List paramNames = parsedSql.getParameterNames();
int lastIndex = 0;
for (int i = 0; i < paramNames.size(); i++) {
String paramName = (String) paramNames.get(i);
int[] indexes = parsedSql.getParameterIndexes(i);
int startIndex = indexes[0];
int endIndex = indexes[1];
actualSql.append(originalSql.substring(lastIndex, startIndex));
if (paramSource != null && paramSource.hasValue(paramName)) {
Object value = paramSource.getValue(paramName);
if (value instanceof Collection) {
Iterator entryIter = ((Collection) value).iterator();
int k = 0;
while (entryIter.hasNext()) {
if (k > 0) {
actualSql.append(", ");
}
k++;
Object entryItem = entryIter.next();
if (entryItem instanceof Object[]) {
Object[] expressionList = (Object[]) entryItem;
actualSql.append("(");
for (int m = 0; m < expressionList.length; m++) {
if (m > 0) {
actualSql.append(", ");
}
actualSql.append("?");
}
actualSql.append(")");
}
else {
actualSql.append("?");
}
}
}
else {
actualSql.append("?");
}
}
else {
actualSql.append("?");
}
lastIndex = endIndex;
}
actualSql.append(originalSql.substring(lastIndex, originalSql.length()));
return actualSql.toString();
}
/**
* Convert a Map of named parameter values to a corresponding array.
* @param parsedSql the parsed SQL statement
* @param paramSource the source for named parameters
* @param declaredParams the List of declared SqlParameter objects
* (may be <code>null</code>). If specified, the parameter metadata will
* be built into the value array in the form of SqlParameterValue objects.
* @return the array of values
*/
public static Object[] buildValueArray(ParsedSql parsedSql, SqlParameterSource paramSource, List declaredParams) {
Object[] paramArray = new Object[parsedSql.getTotalParameterCount()];
if (parsedSql.getNamedParameterCount() > 0 && parsedSql.getUnnamedParameterCount() > 0) {
throw new InvalidDataAccessApiUsageException(
"You can't mix named and traditional ? placeholders. You have " +
parsedSql.getNamedParameterCount() + " named parameter(s) and " +
parsedSql.getUnnamedParameterCount() + " traditonal placeholder(s) in [" +
parsedSql.getOriginalSql() + "]");
}
List paramNames = parsedSql.getParameterNames();
for (int i = 0; i < paramNames.size(); i++) {
String paramName = (String) paramNames.get(i);
try {
Object value = paramSource.getValue(paramName);
SqlParameter param = findParameter(declaredParams, paramName, i);
paramArray[i] = (param != null ? new SqlParameterValue(param, value) : value);
}
catch (IllegalArgumentException ex) {
throw new InvalidDataAccessApiUsageException(
"No value supplied for the SQL parameter '" + paramName + "': " + ex.getMessage());
}
}
return paramArray;
}
/**
* Find a matching parameter in the given list of declared parameters.
* @param declaredParams the declared SqlParameter objects
* @param paramName the name of the desired parameter
* @param paramIndex the index of the desired parameter
* @return the declared SqlParameter, or <code>null</code> if none found
*/
private static SqlParameter findParameter(List declaredParams, String paramName, int paramIndex) {
if (declaredParams != null) {
// First pass: Look for named parameter match.
for (Iterator it = declaredParams.iterator(); it.hasNext();) {
SqlParameter declaredParam = (SqlParameter) it.next();
if (paramName.equals(declaredParam.getName())) {
return declaredParam;
}
}
// Second pass: Look for parameter index match.
if (paramIndex < declaredParams.size()) {
SqlParameter declaredParam = (SqlParameter) declaredParams.get(paramIndex);
// Only accept unnamed parameters for index matches.
if (declaredParam.getName() == null) {
return declaredParam;
}
}
}
return null;
}
/**
* Determine whether a parameter name ends at the current position,
* that is, whether the given character qualifies as a separator.
*/
private static boolean isParameterSeparator(char c) {
if (Character.isWhitespace(c)) {
return true;
}
for (int i = 0; i < PARAMETER_SEPARATORS.length; i++) {
if (c == PARAMETER_SEPARATORS[i]) {
return true;
}
}
return false;
}
/**
* Convert a Map of parameter types to a corresponding int array.
* This is necessary in order to reuse existing methods on JdbcTemplate.
* Any named parameter types are placed in the correct position in the
* Object array based on the parsed SQL statement info.
* @param parsedSql the parsed SQL statement
* @param paramSource the source for named parameters
*/
public static int[] buildSqlTypeArray(ParsedSql parsedSql, SqlParameterSource paramSource) {
int[] sqlTypes = new int[parsedSql.getTotalParameterCount()];
List paramNames = parsedSql.getParameterNames();
for (int i = 0; i < paramNames.size(); i++) {
String paramName = (String) paramNames.get(i);
sqlTypes[i] = paramSource.getSqlType(paramName);
}
return sqlTypes;
}
//-------------------------------------------------------------------------
// Convenience methods operating on a plain SQL String
//-------------------------------------------------------------------------
/**
* Parse the SQL statement and locate any placeholders or named parameters.
* Named parameters are substituted for a JDBC placeholder.
* <p>This is a shortcut version of
* {@link #parseSqlStatement(String)} in combination with
* {@link #substituteNamedParameters(ParsedSql, SqlParameterSource)}.
* @param sql the SQL statement
* @return the actual (parsed) SQL statement
*/
public static String parseSqlStatementIntoString(String sql) {
ParsedSql parsedSql = parseSqlStatement(sql);
return substituteNamedParameters(parsedSql, null);
}
/**
* Parse the SQL statement and locate any placeholders or named parameters.
* Named parameters are substituted for a JDBC placeholder and any select list
* is expanded to the required number of placeholders.
* <p>This is a shortcut version of
* {@link #substituteNamedParameters(ParsedSql, SqlParameterSource)}.
* @param sql the SQL statement
* @param paramSource the source for named parameters
* @return the SQL statement with substituted parameters
*/
public static String substituteNamedParameters(String sql, SqlParameterSource paramSource) {
ParsedSql parsedSql = parseSqlStatement(sql);
return substituteNamedParameters(parsedSql, paramSource);
}
/**
* Convert a Map of named parameter values to a corresponding array.
* <p>This is a shortcut version of
* {@link #buildValueArray(ParsedSql, SqlParameterSource, java.util.List)}.
* @param sql the SQL statement
* @param paramMap the Map of parameters
* @return the array of values
*/
public static Object[] buildValueArray(String sql, Map paramMap) {
ParsedSql parsedSql = parseSqlStatement(sql);
return buildValueArray(parsedSql, new MapSqlParameterSource(paramMap), null);
}
}

View File

@ -0,0 +1,144 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.namedparam;
import java.util.ArrayList;
import java.util.List;
/**
* Holds information about a parsed SQL statement.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.0
*/
public class ParsedSql {
private String originalSql;
private List parameterNames = new ArrayList();
private List parameterIndexes = new ArrayList();
private int namedParameterCount;
private int unnamedParameterCount;
private int totalParameterCount;
/**
* Create a new instance of the {@link ParsedSql} class.
* @param originalSql the SQL statement that is being (or is to be) parsed
*/
ParsedSql(String originalSql) {
this.originalSql = originalSql;
}
/**
* Return the SQL statement that is being parsed.
*/
String getOriginalSql() {
return this.originalSql;
}
/**
* Add a named parameter parsed from this SQL statement.
* @param parameterName the name of the parameter
* @param startIndex the start index in the original SQL String
* @param endIndex the end index in the original SQL String
*/
void addNamedParameter(String parameterName, int startIndex, int endIndex) {
this.parameterNames.add(parameterName);
this.parameterIndexes.add(new int[] {startIndex, endIndex});
}
/**
* Return all of the parameters (bind variables) in the parsed SQL statement.
* Repeated occurences of the same parameter name are included here.
*/
List getParameterNames() {
return this.parameterNames;
}
/**
* Return the parameter indexes for the specified parameter.
* @param parameterPosition the position of the parameter
* (as index in the parameter names List)
* @return the start index and end index, combined into
* a int array of length 2
*/
int[] getParameterIndexes(int parameterPosition) {
return (int[]) this.parameterIndexes.get(parameterPosition);
}
/**
* Set the count of named parameters in the SQL statement.
* Each parameter name counts once; repeated occurences do not count here.
*/
void setNamedParameterCount(int namedParameterCount) {
this.namedParameterCount = namedParameterCount;
}
/**
* Return the count of named parameters in the SQL statement.
* Each parameter name counts once; repeated occurences do not count here.
*/
int getNamedParameterCount() {
return this.namedParameterCount;
}
/**
* Set the count of all of the unnamed parameters in the SQL statement.
*/
void setUnnamedParameterCount(int unnamedParameterCount) {
this.unnamedParameterCount = unnamedParameterCount;
}
/**
* Return the count of all of the unnamed parameters in the SQL statement.
*/
int getUnnamedParameterCount() {
return this.unnamedParameterCount;
}
/**
* Set the total count of all of the parameters in the SQL statement.
* Repeated occurences of the same parameter name do count here.
*/
void setTotalParameterCount(int totalParameterCount) {
this.totalParameterCount = totalParameterCount;
}
/**
* Return the total count of all of the parameters in the SQL statement.
* Repeated occurences of the same parameter name do count here.
*/
int getTotalParameterCount() {
return this.totalParameterCount;
}
/**
* Exposes the original SQL String.
*/
public String toString() {
return this.originalSql;
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.namedparam;
import org.springframework.jdbc.support.JdbcUtils;
/**
* Interface that defines common functionality for objects that can
* offer parameter values for named SQL parameters, serving as argument
* for {@link NamedParameterJdbcTemplate} operations.
*
* <p>This interface allows for the specification of SQL type in addition
* to parameter values. All parameter values and types are identified by
* specifying the name of the parameter.
*
* <p>Intended to wrap various implementations like a Map or a JavaBean
* with a consistent interface.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.0
* @see NamedParameterJdbcOperations
* @see NamedParameterJdbcTemplate
* @see MapSqlParameterSource
* @see BeanPropertySqlParameterSource
*/
public interface SqlParameterSource {
/**
* Constant that indicates an unknown (or unspecified) SQL type.
* To be returned from <code>getType</code> when no specific SQL type known.
* @see #getSqlType
* @see java.sql.Types
*/
int TYPE_UNKNOWN = JdbcUtils.TYPE_UNKNOWN;
/**
* Determine whether there is a value for the specified named parameter.
* @param paramName the name of the parameter
* @return whether there is a value defined
*/
boolean hasValue(String paramName);
/**
* Return the parameter value for the requested named parameter.
* @param paramName the name of the parameter
* @return the value of the specified parameter
* @throws IllegalArgumentException if there is no value for the requested parameter
*/
Object getValue(String paramName) throws IllegalArgumentException;
/**
* Determine the SQL type for the specified named parameter.
* @param paramName the name of the parameter
* @return the SQL type of the specified parameter,
* or <code>TYPE_UNKNOWN</code> if not known
* @see #TYPE_UNKNOWN
*/
int getSqlType(String paramName);
/**
* Determine the type ane for the specified named parameter.
* @param paramName the name of the parameter
* @return the type name of the specified parameter,
* or <code>null</code> if not known
*/
String getTypeName(String paramName);
}

View File

@ -0,0 +1,110 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.namedparam;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import org.springframework.jdbc.core.SqlParameterValue;
/**
* Class that provides helper methods for the use of {@link SqlParameterSource}
* with <code>SimpleJdbc</code> classes.
*
* @author Thomas Risberg
* @since 2.5
* @see org.springframework.jdbc.core.simple.SimpleJdbcTemplate
*/
public class SqlParameterSourceUtils {
/**
* Create an array of MapSqlParameterSource objects populated with data from the
* values passed in. This will define what is included in a batch operation.
* @param valueMaps array of Maps containing the values to be used
* @return an array of SqlParameterSource
*/
public static SqlParameterSource[] createBatch(Map[] valueMaps) {
MapSqlParameterSource[] batch = new MapSqlParameterSource[valueMaps.length];
for (int i = 0; i < valueMaps.length; i++) {
Map valueMap = valueMaps[i];
batch[i] = new MapSqlParameterSource(valueMap);
}
return batch;
}
/**
* Create an array of BeanPropertySqlParameterSource objects populated with data
* from the values passed in. This will define what is included in a batch operation.
* @param beans object array of beans containing the values to be used
* @return an array of SqlParameterSource
*/
public static SqlParameterSource[] createBatch(Object[] beans) {
BeanPropertySqlParameterSource[] batch = new BeanPropertySqlParameterSource[beans.length];
for (int i = 0; i < beans.length; i++) {
Object bean = beans[i];
batch[i] = new BeanPropertySqlParameterSource(bean);
}
return batch;
}
/**
* Create a wrapped value if parameter has type information, plain object if not.
* @param source the source of paramer values and type information
* @param parameterName the name of the parameter
* @return the value object
*/
public static Object getTypedValue(SqlParameterSource source, String parameterName) {
int sqlType = source.getSqlType(parameterName);
if (sqlType != SqlParameterSource.TYPE_UNKNOWN) {
if (source.getTypeName(parameterName) != null) {
return new SqlParameterValue(sqlType, source.getTypeName(parameterName), source.getValue(parameterName));
}
else {
return new SqlParameterValue(sqlType, source.getValue(parameterName));
}
}
else {
return source.getValue(parameterName);
}
}
/**
* Create a Map of case insensitive parameter names together with the original name.
* @param parameterSource the source of paramer names
* @return the Map that can be used for case insensitive matching of parameter names
*/
public static Map extractCaseInsensitiveParameterNames(SqlParameterSource parameterSource) {
Map caseInsensitiveParameterNames = new HashMap();
if (parameterSource instanceof BeanPropertySqlParameterSource) {
String[] propertyNames = ((BeanPropertySqlParameterSource)parameterSource).getReadablePropertyNames();
for (int i = 0; i < propertyNames.length; i++) {
String name = propertyNames[i];
caseInsensitiveParameterNames.put(name.toLowerCase(), name);
}
}
else if (parameterSource instanceof MapSqlParameterSource) {
for (Iterator it = ((MapSqlParameterSource) parameterSource).getValues().keySet().iterator(); it.hasNext();)
{
String name = (String) it.next();
caseInsensitiveParameterNames.put(name.toLowerCase(), name);
}
}
return caseInsensitiveParameterNames;
}
}

View File

@ -0,0 +1,16 @@
<html>
<body>
JdbcTemplate variant with named parameter support.
<p>NamedParameterJdbcTemplate is a wrapper around JdbcTemplate that adds
support for named parameter parsing. It does not implement the JdbcOperations
interface or extend JdbcTemplate, but implements the dedicated
NamedParameterJdbcOperations interface.
<P>If you need the full power of Spring JDBC for less common operations, use
the <code>getJdbcOperations()</code> method of NamedParameterJdbcTemplate and
work with the returned classic template, or use a JdbcTemplate instance directly.
</body>
</html>

View File

@ -0,0 +1,8 @@
<html>
<body>
Provides the core JDBC framework, based on JdbcTemplate
and its associated callback interfaces and helper objects.
</body>
</html>

View File

@ -0,0 +1,407 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.CallableStatementCreatorFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.metadata.CallMetaDataContext;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.StringUtils;
/**
* Abstract class to provide base functionality for easy stored procedure calls
* based on configuration options and database metadata.
* This class provides the base SPI for {@link SimpleJdbcCall}.
*
* @author Thomas Risberg
* @since 2.5
*/
public abstract class AbstractJdbcCall {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
/** Lower-level class used to execute SQL */
private JdbcTemplate jdbcTemplate = new JdbcTemplate();
/** List of SqlParameter objects */
private final List<SqlParameter> declaredParameters = new ArrayList<SqlParameter>();
/** List of RefCursor/ResultSet RowMapper objects */
private final Map<String, ParameterizedRowMapper> declaredRowMappers = new LinkedHashMap<String, ParameterizedRowMapper>();
/**
* Has this operation been compiled? Compilation means at
* least checking that a DataSource and sql have been provided,
* but subclasses may also implement their own custom validation.
*/
private boolean compiled = false;
/** the generated string used for call statement */
private String callString;
/** context used to retrieve and manage database metadata */
private CallMetaDataContext callMetaDataContext = new CallMetaDataContext();
/**
* Object enabling us to create CallableStatementCreators
* efficiently, based on this class's declared parameters.
*/
private CallableStatementCreatorFactory callableStatementFactory;
/**
* Constructor to be used when initializing using a {@link DataSource}.
* @param dataSource the DataSource to be used
*/
protected AbstractJdbcCall(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* Constructor to be used when initializing using a {@link JdbcTemplate}.
* @param jdbcTemplate the JdbcTemplate to use
*/
protected AbstractJdbcCall(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* Get the configured {@link JdbcTemplate}
*/
public JdbcTemplate getJdbcTemplate() {
return this.jdbcTemplate;
}
/**
* Get the {@link CallableStatementCreatorFactory} being used
*/
protected CallableStatementCreatorFactory getCallableStatementFactory() {
return this.callableStatementFactory;
}
/**
* Set the name of the stored procedure.
*/
public void setProcedureName(String procedureName) {
this.callMetaDataContext.setProcedureName(procedureName);
}
/**
* Get the name of the stored procedure.
*/
public String getProcedureName() {
return this.callMetaDataContext.getProcedureName();
}
/**
* Set the names of in parameters to be used.
*/
public void setInParameterNames(Set<String> inParameterNames) {
this.callMetaDataContext.setLimitedInParameterNames(inParameterNames);
}
/**
* Get the names of in parameters to be used.
*/
public Set<String> getInParameterNames() {
return this.callMetaDataContext.getLimitedInParameterNames();
}
/**
* Set the catalog name to use.
*/
public void setCatalogName(String catalogName) {
this.callMetaDataContext.setCatalogName(catalogName);
}
/**
* Get the catalog name used.
*/
public String getCatalogName() {
return this.callMetaDataContext.getCatalogName();
}
/**
* Set the schema name to use,
*/
public void setSchemaName(String schemaName) {
this.callMetaDataContext.setSchemaName(schemaName);
}
/**
* Get the schema name used.
*/
public String getSchemaName() {
return this.callMetaDataContext.getSchemaName();
}
/**
* Specify whether this call is a function call.
*/
public void setFunction(boolean function) {
this.callMetaDataContext.setFunction(function);
}
/**
* Is this call a function call?
*/
public boolean isFunction() {
return this.callMetaDataContext.isFunction();
}
/**
* Specify whether the call requires a rerurn value.
*/
public void setReturnValueRequired(boolean b) {
this.callMetaDataContext.setReturnValueRequired(b);
}
/**
* Does the call require a return value?
*/
public boolean isReturnValueRequired() {
return this.callMetaDataContext.isReturnValueRequired();
}
/**
* Add a declared parameter to the list of parameters for the call.
* Only parameters declared as <code>SqlParameter</code> and <code>SqlInOutParameter</code>
* will be used to provide input values. This is different from the <code>StoredProcedure</code> class
* which for backwards compatibility reasons allows input values to be provided for parameters declared
* as <code>SqlOutParameter</code>.
* @param parameter the {@link SqlParameter} to add
*/
public void addDeclaredParameter(SqlParameter parameter) {
if (!StringUtils.hasText(parameter.getName())) {
throw new InvalidDataAccessApiUsageException("You must specify a parameter name when declaring parameters for \"" + getProcedureName() + "\"");
}
this.declaredParameters.add(parameter);
if (logger.isDebugEnabled()) {
logger.debug("Added declared parameter for [" + getProcedureName() + "]: " + parameter.getName());
}
}
/**
* Add a {@link org.springframework.jdbc.core.RowMapper} for the specified parameter or column
* @param parameterName name of parameter or column
* @param rowMapper the RowMapper implementation to use
*/
public void addDeclaredRowMapper(String parameterName, ParameterizedRowMapper rowMapper) {
this.declaredRowMappers.put(parameterName, rowMapper);
if (logger.isDebugEnabled()) {
logger.debug("Added row mapper for [" + getProcedureName() + "]: " + parameterName);
}
}
/**
* Get the call string that should be used based on parameters and meta data
*/
public String getCallString() {
return this.callString;
}
/**
* Specify whether the parameter metadata for the call should be used. The default is true.
*/
public void setAccessCallParameterMetaData(boolean accessCallParameterMetaData) {
this.callMetaDataContext.setAccessCallParameterMetaData(accessCallParameterMetaData);
}
//-------------------------------------------------------------------------
// Methods handling compilation issues
//-------------------------------------------------------------------------
/**
* Compile this JdbcCall using provided parameters and meta data plus other settings. This
* finalizes the configuration for this object and subsequent attempts to compile are ignored.
* This will be implicitly called the first time an un-compiled call is executed.
* @throws org.springframework.dao.InvalidDataAccessApiUsageException if the object hasn't
* been correctly initialized, for example if no DataSource has been provided
*/
public final void compile() throws InvalidDataAccessApiUsageException {
if (!isCompiled()) {
if (getProcedureName() == null) {
throw new InvalidDataAccessApiUsageException("Procedure or Function name is required");
}
try {
this.jdbcTemplate.afterPropertiesSet();
}
catch (IllegalArgumentException ex) {
throw new InvalidDataAccessApiUsageException(ex.getMessage());
}
compileInternal();
this.compiled = true;
if (logger.isDebugEnabled()) {
logger.debug("SqlCall for " + (isFunction() ? "function" : "procedure") + " [" + getProcedureName() + "] compiled");
}
}
}
/**
* Method to perform the actual compilation. Subclasses can override this template method to perform
* their own compilation. Invoked after this base class's compilation is complete.
*/
protected void compileInternal() {
this.callMetaDataContext.initializeMetaData(getJdbcTemplate().getDataSource());
// iterate over the declared RowMappers and register the corresponding SqlParameter
for (Map.Entry<String, ParameterizedRowMapper> entry : this.declaredRowMappers.entrySet()) {
SqlParameter resultSetParameter =
this.callMetaDataContext.createReturnResultSetParameter(entry.getKey(), entry.getValue());
this.declaredParameters.add(resultSetParameter);
}
callMetaDataContext.processParameters(this.declaredParameters);
this.callString = this.callMetaDataContext.createCallString();
if (logger.isDebugEnabled()) {
logger.debug("Compiled stored procedure. Call string is [" + this.callString + "]");
}
this.callableStatementFactory =
new CallableStatementCreatorFactory(getCallString(), this.callMetaDataContext.getCallParameters());
this.callableStatementFactory.setNativeJdbcExtractor(getJdbcTemplate().getNativeJdbcExtractor());
onCompileInternal();
}
/**
* Hook method that subclasses may override to react to compilation.
* This implementation does nothing.
*/
protected void onCompileInternal() {
}
/**
* Is this operation "compiled"?
* @return whether this operation is compiled, and ready to use.
*/
public boolean isCompiled() {
return this.compiled;
}
/**
* Check whether this operation has been compiled already;
* lazily compile it if not already compiled.
* <p>Automatically called by <code>doExecute</code>.
*/
protected void checkCompiled() {
if (!isCompiled()) {
logger.debug("JdbcCall call not compiled before execution - invoking compile");
compile();
}
}
//-------------------------------------------------------------------------
// Methods handling execution
//-------------------------------------------------------------------------
/**
* Method that provides execution of the call using the passed in {@link SqlParameterSource}
* @param parameterSource parameter names and values to be used in call
* @return Map of out parameters
*/
protected Map<String, Object> doExecute(SqlParameterSource parameterSource) {
checkCompiled();
Map params = matchInParameterValuesWithCallParameters(parameterSource);
return executeCallInternal(params);
}
/**
* Method that provides execution of the call using the passed in Map of parameters
* @param args Map of parameter name and values
* @return Map of out parameters
*/
protected Map<String, Object> doExecute(Map<String, Object> args) {
checkCompiled();
Map params = matchInParameterValuesWithCallParameters(args);
return executeCallInternal(params);
}
/**
* Method to perform the actual call processing
*/
private Map<String, Object> executeCallInternal(Map params) {
CallableStatementCreator csc = getCallableStatementFactory().newCallableStatementCreator(params);
if (logger.isDebugEnabled()) {
logger.debug("The following parameters are used for call " + getCallString() + " with: " + params);
int i = 1;
for (SqlParameter p : getCallParameters()) {
logger.debug(i++ + ": " + p.getName() + " SQL Type "+ p.getSqlType() + " Type Name " + p.getTypeName() + " " + p.getClass().getName());
}
}
return getJdbcTemplate().call(csc, getCallParameters());
}
/**
* Get the name of a single out parameter or return value.
* Used for functions or procedures with one out parameter.
*/
protected String getScalarOutParameterName() {
return this.callMetaDataContext.getScalarOutParameterName();
}
/**
* Match the provided in parameter values with registered parameters and
* parameters defined via metadata processing.
* @param parameterSource the parameter vakues provided as a {@link SqlParameterSource}
* @return Map with parameter names and values
*/
protected Map<String, Object> matchInParameterValuesWithCallParameters(SqlParameterSource parameterSource) {
return this.callMetaDataContext.matchInParameterValuesWithCallParameters(parameterSource);
}
/**
* Match the provided in parameter values with registered parameters and
* parameters defined via metadata processing.
* @param args the parameter values provided in a Map
* @return Map with parameter names and values
*/
protected Map<String, Object> matchInParameterValuesWithCallParameters(Map<String, Object> args) {
return this.callMetaDataContext.matchInParameterValuesWithCallParameters(args);
}
/**
* Get a List of all the call parameters to be used for call. This includes any parameters added
* based on meta data processing.
*/
protected List<SqlParameter> getCallParameters() {
return this.callMetaDataContext.getCallParameters();
}
}

View File

@ -0,0 +1,625 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.jdbc.core.metadata.TableMetaDataContext;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.util.Assert;
/**
* Abstract class to provide base functionality for easy inserts
* based on configuration options and database metadata.
* This class provides the base SPI for {@link SimpleJdbcInsert}.
*
* @author Thomas Risberg
* @since 2.5
*/
public abstract class AbstractJdbcInsert {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
/** Lower-level class used to execute SQL */
private JdbcTemplate jdbcTemplate = new JdbcTemplate();
/** List of columns objects to be used in insert statement */
private List<String> declaredColumns = new ArrayList<String>();
/**
* Has this operation been compiled? Compilation means at
* least checking that a DataSource or JdbcTemplate has been provided,
* but subclasses may also implement their own custom validation.
*/
private boolean compiled = false;
/** the generated string used for insert statement */
private String insertString;
/** the SQL Type information for the insert columns */
private int[] insertTypes;
/** the names of the columns holding the generated key */
private String[] generatedKeyNames = new String[] {};
/** context used to retrieve and manage database metadata */
private TableMetaDataContext tableMetaDataContext = new TableMetaDataContext();
/**
* Constructor for sublasses to delegate to for setting the DataSource.
*/
protected AbstractJdbcInsert(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* Constructor for sublasses to delegate to for setting the JdbcTemplate.
*/
protected AbstractJdbcInsert(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//-------------------------------------------------------------------------
// Methods dealing with configuaration properties
//-------------------------------------------------------------------------
/**
* Get the name of the table for this insert
*/
public String getTableName() {
return tableMetaDataContext.getTableName();
}
/**
* Set the name of the table for this insert
*/
public void setTableName(String tableName) {
checkIfConfigurationModificationIsAllowed();
tableMetaDataContext.setTableName(tableName);
}
/**
* Get the name of the schema for this insert
*/
public String getSchemaName() {
return tableMetaDataContext.getSchemaName();
}
/**
* Set the name of the schema for this insert
*/
public void setSchemaName(String schemaName) {
checkIfConfigurationModificationIsAllowed();
tableMetaDataContext.setSchemaName(schemaName);
}
/**
* Get the name of the catalog for this insert
*/
public String getCatalogName() {
return tableMetaDataContext.getCatalogName();
}
/**
* Set the name of the catalog for this insert
*/
public void setCatalogName(String catalogName) {
checkIfConfigurationModificationIsAllowed();
tableMetaDataContext.setCatalogName(catalogName);
}
/**
* Set the names of the columns to be used
*/
public void setColumnNames(List<String> columnNames) {
checkIfConfigurationModificationIsAllowed();
declaredColumns.clear();
declaredColumns.addAll(columnNames);
}
/**
* Get the names of the columns used
*/
public List<String> getColumnNames() {
return Collections.unmodifiableList(declaredColumns);
}
/**
* Get the names of any generated keys
*/
public String[] getGeneratedKeyNames() {
return generatedKeyNames;
}
/**
* Set the names of any generated keys
*/
public void setGeneratedKeyNames(String[] generatedKeyNames) {
checkIfConfigurationModificationIsAllowed();
this.generatedKeyNames = generatedKeyNames;
}
/**
* Specify the name of a single generated key column
*/
public void setGeneratedKeyName(String generatedKeyName) {
checkIfConfigurationModificationIsAllowed();
this.generatedKeyNames = new String[] {generatedKeyName};
}
/**
* Get the insert string to be used
*/
public String getInsertString() {
return insertString;
}
/**
* Get the array of {@link java.sql.Types} to be used for insert
*/
public int[] getInsertTypes() {
return insertTypes;
}
/**
* Get the {@link JdbcTemplate} that is configured to be used
*/
protected JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
//-------------------------------------------------------------------------
// Methods handling compilation issues
//-------------------------------------------------------------------------
/**
* Compile this JdbcInsert using provided parameters and meta data plus other settings. This
* finalizes the configuration for this object and subsequent attempts to compile are ignored.
* This will be implicitly called the first time an un-compiled insert is executed.
* @throws org.springframework.dao.InvalidDataAccessApiUsageException if the object hasn't
* been correctly initialized, for example if no DataSource has been provided
*/
public final void compile() throws InvalidDataAccessApiUsageException {
if (!isCompiled()) {
if (getTableName() == null) {
throw new InvalidDataAccessApiUsageException("Table name is required");
}
try {
this.jdbcTemplate.afterPropertiesSet();
}
catch (IllegalArgumentException ex) {
throw new InvalidDataAccessApiUsageException(ex.getMessage());
}
compileInternal();
this.compiled = true;
if (logger.isDebugEnabled()) {
logger.debug("JdbcInsert for table [" + getTableName() + "] compiled");
}
}
}
/**
* Method to perform the actual compilation. Subclasses can override this template method to perform
* their own compilation. Invoked after this base class's compilation is complete.
*/
protected void compileInternal() {
tableMetaDataContext.processMetaData(getJdbcTemplate().getDataSource(), getColumnNames(), getGeneratedKeyNames());
insertString = tableMetaDataContext.createInsertString(getGeneratedKeyNames());
insertTypes = tableMetaDataContext.createInsertTypes();
if (logger.isDebugEnabled()) {
logger.debug("Compiled JdbcInsert. Insert string is [" + getInsertString() + "]");
}
onCompileInternal();
}
/**
* Hook method that subclasses may override to react to compilation.
* This implementation does nothing.
*/
protected void onCompileInternal() {
}
/**
* Is this operation "compiled"?
* @return whether this operation is compiled, and ready to use.
*/
public boolean isCompiled() {
return this.compiled;
}
/**
* Check whether this operation has been compiled already;
* lazily compile it if not already compiled.
* <p>Automatically called by <code>validateParameters</code>.
*/
protected void checkCompiled() {
if (!isCompiled()) {
logger.debug("JdbcInsert not compiled before execution - invoking compile");
compile();
}
}
/**
* Method to check whether we are allowd to make any configuration changes at this time. If the class has been
* compiled, then no further changes to the configuration are allowed.
*/
protected void checkIfConfigurationModificationIsAllowed() {
if (isCompiled()) {
throw new InvalidDataAccessApiUsageException("Configuration can't be altered once the class has been compiled or used.");
}
}
//-------------------------------------------------------------------------
// Methods handling execution
//-------------------------------------------------------------------------
/**
* Method that provides execution of the insert using the passed in Map of parameters
*
* @param args Map with parameter names and values to be used in insert
* @return number of rows affected
*/
protected int doExecute(Map<String, Object> args) {
checkCompiled();
List<Object> values = matchInParameterValuesWithInsertColumns(args);
return executeInsertInternal(values);
}
/**
* Method that provides execution of the insert using the passed in {@link SqlParameterSource}
*
* @param parameterSource parameter names and values to be used in insert
* @return number of rows affected
*/
protected int doExecute(SqlParameterSource parameterSource) {
checkCompiled();
List<Object> values = matchInParameterValuesWithInsertColumns(parameterSource);
return executeInsertInternal(values);
}
/**
* Method to execute the insert
*/
private int executeInsertInternal(List<Object> values) {
if (logger.isDebugEnabled()) {
logger.debug("The following parameters are used for insert " + getInsertString() + " with: " + values);
}
int updateCount = jdbcTemplate.update(getInsertString(), values.toArray());
return updateCount;
}
/**
* Method that provides execution of the insert using the passed in Map of parameters
* and returning a generated key
*
* @param args Map with parameter names and values to be used in insert
* @return the key generated by the insert
*/
protected Number doExecuteAndReturnKey(Map<String, Object> args) {
checkCompiled();
List<Object> values = matchInParameterValuesWithInsertColumns(args);
return executeInsertAndReturnKeyInternal(values);
}
/**
* Method that provides execution of the insert using the passed in {@link SqlParameterSource}
* and returning a generated key
*
* @param parameterSource parameter names and values to be used in insert
* @return the key generated by the insert
*/
protected Number doExecuteAndReturnKey(SqlParameterSource parameterSource) {
checkCompiled();
List<Object> values = matchInParameterValuesWithInsertColumns(parameterSource);
return executeInsertAndReturnKeyInternal(values);
}
/**
* Method that provides execution of the insert using the passed in Map of parameters
* and returning all generated keys
*
* @param args Map with parameter names and values to be used in insert
* @return the KeyHolder containing keys generated by the insert
*/
protected KeyHolder doExecuteAndReturnKeyHolder(Map<String, Object> args) {
checkCompiled();
List<Object> values = matchInParameterValuesWithInsertColumns(args);
return executeInsertAndReturnKeyHolderInternal(values);
}
/**
* Method that provides execution of the insert using the passed in {@link SqlParameterSource}
* and returning all generated keys
*
* @param parameterSource parameter names and values to be used in insert
* @return the KeyHolder containing keys generated by the insert
*/
protected KeyHolder doExecuteAndReturnKeyHolder(SqlParameterSource parameterSource) {
checkCompiled();
List<Object> values = matchInParameterValuesWithInsertColumns(parameterSource);
return executeInsertAndReturnKeyHolderInternal(values);
}
/**
* Method to execute the insert generating single key
*/
private Number executeInsertAndReturnKeyInternal(final List<Object> values) {
KeyHolder kh = executeInsertAndReturnKeyHolderInternal(values);
if (kh != null && kh.getKey() != null) {
return kh.getKey();
}
else {
throw new DataIntegrityViolationException("Unable to retreive the generated key for the insert: " +
getInsertString());
}
}
/**
* Method to execute the insert generating any number of keys
*/
private KeyHolder executeInsertAndReturnKeyHolderInternal(final List<Object> values) {
if (logger.isDebugEnabled()) {
logger.debug("The following parameters are used for call " + getInsertString() + " with: " + values);
}
final KeyHolder keyHolder = new GeneratedKeyHolder();
if (this.tableMetaDataContext.isGetGeneratedKeysSupported()) {
jdbcTemplate.update(
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = prepareStatementForGeneratedKeys(con);
setParameterValues(ps, values, null);
return ps;
}
},
keyHolder);
}
else {
if (!this.tableMetaDataContext.isGetGeneratedKeysSimulated()) {
throw new InvalidDataAccessResourceUsageException(
"The getGeneratedKeys feature is not supported by this database");
}
if (getGeneratedKeyNames().length < 1) {
throw new InvalidDataAccessApiUsageException("Generated Key Name(s) not specificed. " +
"Using the generated keys features requires specifying the name(s) of the generated column(s)");
}
if (getGeneratedKeyNames().length > 1) {
throw new InvalidDataAccessApiUsageException(
"Current database only supports retreiving the key for a single column. There are " +
getGeneratedKeyNames().length + " columns specified: " + Arrays.asList(getGeneratedKeyNames()));
}
// This is a hack to be able to get the generated key from a database that doesn't support
// get generated keys feature. HSQL is one, PostgreSQL is another. Postgres uses a RETURNING
// clause while HSQL uses a second query that has to be executed with the same connection.
final String keyQuery = tableMetaDataContext.getSimulationQueryForGetGeneratedKey(
tableMetaDataContext.getTableName(),
getGeneratedKeyNames()[0]);
Assert.notNull(keyQuery, "Query for simulating get generated keys can't be null");
if (keyQuery.toUpperCase().startsWith("RETURNING")) {
Long key = jdbcTemplate.queryForLong(
getInsertString() + " " + keyQuery,
values.toArray(new Object[values.size()]));
HashMap keys = new HashMap(1);
keys.put(getGeneratedKeyNames()[0], key);
keyHolder.getKeyList().add(keys);
}
else {
jdbcTemplate.execute(new ConnectionCallback() {
public Object doInConnection(Connection con) throws SQLException, DataAccessException {
// Do the insert
PreparedStatement ps = null;
try {
ps = con.prepareStatement(getInsertString());
setParameterValues(ps, values, null);
ps.executeUpdate();
} finally {
JdbcUtils.closeStatement(ps);
}
//Get the key
Statement keyStmt = null;
ResultSet rs = null;
HashMap keys = new HashMap(1);
try {
keyStmt = con.createStatement();
rs = keyStmt.executeQuery(keyQuery);
if (rs.next()) {
long key = rs.getLong(1);
keys.put(getGeneratedKeyNames()[0], key);
keyHolder.getKeyList().add(keys);
}
} finally {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(keyStmt);
}
return null;
}
});
}
return keyHolder;
}
return keyHolder;
}
/**
* Create the PreparedStatement to be used for insert that have generated keys
*
* @param con the connection used
* @return PreparedStatement to use
* @throws SQLException
*/
private PreparedStatement prepareStatementForGeneratedKeys(Connection con) throws SQLException {
if (getGeneratedKeyNames().length < 1) {
throw new InvalidDataAccessApiUsageException("Generated Key Name(s) not specificed. " +
"Using the generated keys features requires specifying the name(s) of the generated column(s)");
}
PreparedStatement ps;
if (this.tableMetaDataContext.isGeneratedKeysColumnNameArraySupported()) {
if (logger.isDebugEnabled()) {
logger.debug("Using generated keys support with array of column names.");
}
ps = con.prepareStatement(getInsertString(), getGeneratedKeyNames());
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Using generated keys support with Statement.RETURN_GENERATED_KEYS.");
}
ps = con.prepareStatement(getInsertString(), Statement.RETURN_GENERATED_KEYS);
}
return ps;
}
/**
* Method that provides execution of a batch insert using the passed in Maps of parameters
*
* @param batch array of Maps with parameter names and values to be used in batch insert
* @return array of number of rows affected
*/
protected int[] doExecuteBatch(Map<String, Object>[] batch) {
checkCompiled();
List[] batchValues = new ArrayList[batch.length];
int i = 0;
for (Map<String, Object> args : batch) {
List<Object> values = matchInParameterValuesWithInsertColumns(args);
batchValues[i++] = values;
}
return executeBatchInternal(batchValues);
}
/**
* Method that provides execution of a batch insert using the passed in array of {@link SqlParameterSource}
*
* @param batch array of SqlParameterSource with parameter names and values to be used in insert
* @return array of number of rows affected
*/
protected int[] doExecuteBatch(SqlParameterSource[] batch) {
checkCompiled();
List[] batchValues = new ArrayList[batch.length];
int i = 0;
for (SqlParameterSource parameterSource : batch) {
List<Object> values = matchInParameterValuesWithInsertColumns(parameterSource);
batchValues[i++] = values;
}
return executeBatchInternal(batchValues);
}
/**
* Method to execute the batch insert
*/
//TODO synchronize parameter setters with the SimpleJdbcTemplate
private int[] executeBatchInternal(final List<Object>[] batchValues) {
if (logger.isDebugEnabled()) {
logger.debug("Executing statement " + getInsertString() + " with batch of size: " + batchValues.length);
}
final int[] columnTypes = getInsertTypes();
int[] updateCounts = jdbcTemplate.batchUpdate(
getInsertString(),
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
List<Object> values = batchValues[i];
setParameterValues(ps, values, columnTypes);
}
public int getBatchSize() {
return batchValues.length;
}
});
return updateCounts;
}
/**
* Internal implementation for setting parameter values
* @param preparedStatement the PreparedStatement
* @param values the values to be set
*/
private void setParameterValues(PreparedStatement preparedStatement, List<Object> values, int[] columnTypes)
throws SQLException {
int colIndex = 0;
for (Object value : values) {
colIndex++;
if (columnTypes == null || colIndex < columnTypes.length) {
StatementCreatorUtils.setParameterValue(preparedStatement, colIndex, SqlTypeValue.TYPE_UNKNOWN, value);
}
else {
StatementCreatorUtils.setParameterValue(preparedStatement, colIndex, columnTypes[colIndex - 1], value);
}
}
}
/**
* Match the provided in parameter values with regitered parameters and parameters defined via metedata
* processing.
*
* @param parameterSource the parameter vakues provided as a {@link SqlParameterSource}
* @return Map with parameter names and values
*/
protected List<Object> matchInParameterValuesWithInsertColumns(SqlParameterSource parameterSource) {
return tableMetaDataContext.matchInParameterValuesWithInsertColumns(parameterSource);
}
/**
* Match the provided in parameter values with regitered parameters and parameters defined via metedata
* processing.
*
* @param args the parameter values provided in a Map
* @return Map with parameter names and values
*/
protected List<Object> matchInParameterValuesWithInsertColumns(Map<String, Object> args) {
return tableMetaDataContext.matchInParameterValuesWithInsertColumns(args);
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
/**
* {@link ParameterizedRowMapper} implementation that converts a row into a new instance
* of the specified mapped target class. The mapped target class must be a top-level class
* and it must have a default or no-arg constructor.
*
* <p>Uses Java 5 covariant return types to override the return type of the {@link #mapRow}
* method to be the type parameter <code>T</code>.
*
* <p>Column values are mapped based on matching the column name as obtained from result set
* metadata to public setters for the corresponding properties. The names are matched either
* directly or by transforming a name separating the parts with underscores to the same name
* using "camel" case.
*
* <p>Mapping is provided for fields in the target class for many common types, e.g.:
* String, boolean, Boolean, byte, Byte, short, Short, int, Integer, long, Long,
* float, Float, double, Double, BigDecimal, <code>java.util.Date</code>, etc.
*
* <p>To facilitate mapping between columns and fields that don't have matching names,
* try using column aliases in the SQL statement like "select fname as first_name from customer".
*
* <p>Please note that this class is designed to provide convenience rather than high performance.
* For best performance consider using a custom RowMapper.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.5
* @see ParameterizedRowMapper
*/
public class ParameterizedBeanPropertyRowMapper<T> extends BeanPropertyRowMapper
implements ParameterizedRowMapper<T> {
/**
* Create a new ParameterizedBeanPropertyRowMapper.
* <p>Generally prefer the {@link #newInstance(Class)} method instead,
* which avoids the need for specifying the mapped type twice.
* @see #setMappedClass
*/
public ParameterizedBeanPropertyRowMapper() {
}
@SuppressWarnings("unchecked")
public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
return (T) super.mapRow(rs, rowNumber);
}
/**
* Static factory method to create a new ParameterizedBeanPropertyRowMapper
* (with the mapped class specified only once).
* @param mappedClass the class that each row should be mapped to
*/
public static <T> ParameterizedBeanPropertyRowMapper<T> newInstance(Class<T> mappedClass) {
ParameterizedBeanPropertyRowMapper<T> newInstance = new ParameterizedBeanPropertyRowMapper<T>();
newInstance.setMappedClass(mappedClass);
return newInstance;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
/**
* Extension of the {@link org.springframework.jdbc.core.RowMapper} interface,
* adding type parameterization. Uses Java 5 covariant return types to override
* the return type of the {@link #mapRow} method to be the type parameter
* <code>T</code>.
*
* @author Rob Harrop
* @since 2.0
* @see org.springframework.jdbc.core.simple.SimpleJdbcOperations
*/
public interface ParameterizedRowMapper<T> extends RowMapper {
/**
* Implementations should return the object representation of
* the current row in the supplied {@link ResultSet}.
* @see org.springframework.jdbc.core.RowMapper#mapRow
*/
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.SingleColumnRowMapper;
/**
* {@link ParameterizedRowMapper} implementation that converts a single column
* into a single result value per row. Expects to operate on a
* <code>java.sql.ResultSet</code> that just contains a single column.
*
* <p>The type of the result value for each row can be specified. The value
* for the single column will be extracted from the <code>ResultSet</code>
* and converted into the specified target type.
*
* <p>Uses Java 5 covariant return types to override the return type of the
* {@link #mapRow} method to be the type parameter <code>T</code>.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
public class ParameterizedSingleColumnRowMapper<T> extends SingleColumnRowMapper
implements ParameterizedRowMapper<T> {
/**
* Create a new ParameterizedSingleColumnRowMapper.
* <p>Generally prefer the {@link #newInstance(Class)} method instead,
* which avoids the need for specifying the mapped type twice.
* @see #setRequiredType
*/
public ParameterizedSingleColumnRowMapper() {
}
@SuppressWarnings("unchecked")
public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
return (T) super.mapRow(rs, rowNumber);
}
/**
* Static factory method to create a new ParameterizedSingleColumnRowMapper
* (with the required type specified only once).
* @param requiredType the type that each result object is expected to match
*/
public static <T> ParameterizedSingleColumnRowMapper<T> newInstance(Class<T> requiredType) {
ParameterizedSingleColumnRowMapper<T> rm = new ParameterizedSingleColumnRowMapper<T>();
rm.setRequiredType(requiredType);
return rm;
}
}

View File

@ -0,0 +1,167 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
/**
* A SimpleJdbcCall is a multi-threaded, reusable object representing a call
* to a stored procedure or a stored function. It provides meta data processing
* to simplify the code needed to access basic stored procedures/functions.
* All you need to provide is the name of the procedure/function and a Map
* containing the parameters when you execute the call. The names of the
* supplied parameters will be matched up with in and out parameters declared
* when the stored procedure was created.
*
* <p>The meta data processing is based on the DatabaseMetaData provided by
* the JDBC driver. Since we rely on the JDBC driver this "auto-detection"
* can only be used for databases that are known to provide accurate meta data.
* These currently include Derby, MySQL, Microsoft SQL Server, Oracle and DB2.
* For any other databases you are required to declare all parameters explicitly.
* You can of course declare all parameters explicitly even if the database provides
* the necessary meta data. In that case your declared parameters will take precedence.
* You can also turn off any mete data processing if you want to use parameter names
* that do not match what is declared during the stored procedure compilation.
*
* <p>The actual insert is being handled using Spring's
* {@link org.springframework.jdbc.core.JdbcTemplate}.
*
* <p>Many of the configuration methods return the current instance of the SimpleJdbcCall
* to provide the ability to string multiple ones together in a "fluid" interface style.
*
* @author Thomas Risberg
* @since 2.5
* @see java.sql.DatabaseMetaData
* @see org.springframework.jdbc.core.JdbcTemplate
*/
public class SimpleJdbcCall extends AbstractJdbcCall implements SimpleJdbcCallOperations {
/**
* Constructor that takes one parameter with the JDBC DataSource to use when creating the
* JdbcTemplate.
* @param dataSource the <code>DataSource</code> to use
* @see org.springframework.jdbc.core.JdbcTemplate#setDataSource
*/
public SimpleJdbcCall(DataSource dataSource) {
super(dataSource);
}
/**
* Alternative Constructor that takes one parameter with the JdbcTemplate to be used.
* @param jdbcTemplate the <code>JdbcTemplate</code> to use
* @see org.springframework.jdbc.core.JdbcTemplate#setDataSource
*/
public SimpleJdbcCall(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
public SimpleJdbcCall withProcedureName(String procedureName) {
setProcedureName(procedureName);
setFunction(false);
return this;
}
public SimpleJdbcCall withFunctionName(String functionName) {
setProcedureName(functionName);
setFunction(true);
return this;
}
public SimpleJdbcCall withSchemaName(String schemaName) {
setSchemaName(schemaName);
return this;
}
public SimpleJdbcCall withCatalogName(String catalogName) {
setCatalogName(catalogName);
return this;
}
public SimpleJdbcCall withReturnValue() {
setReturnValueRequired(true);
return this;
}
public SimpleJdbcCall declareParameters(SqlParameter... sqlParameters) {
for (SqlParameter sqlParameter : sqlParameters) {
if (sqlParameter != null) {
addDeclaredParameter(sqlParameter);
}
}
return this;
}
public SimpleJdbcCall useInParameterNames(String... inParameterNames) {
setInParameterNames(new HashSet<String>(Arrays.asList(inParameterNames)));
return this;
}
public SimpleJdbcCall returningResultSet(String parameterName, ParameterizedRowMapper rowMapper) {
addDeclaredRowMapper(parameterName, rowMapper);
return this;
}
public SimpleJdbcCall withoutProcedureColumnMetaDataAccess() {
setAccessCallParameterMetaData(false);
return this;
}
@SuppressWarnings("unchecked")
public <T> T executeFunction(Class<T> returnType, Map args) {
return (T) doExecute(args).get(getScalarOutParameterName());
}
@SuppressWarnings("unchecked")
public <T> T executeFunction(Class<T> returnType, MapSqlParameterSource args) {
return (T) doExecute(args).get(getScalarOutParameterName());
}
@SuppressWarnings("unchecked")
public <T> T executeObject(Class<T> returnType, Map args) {
return (T) doExecute(args).get(getScalarOutParameterName());
}
@SuppressWarnings("unchecked")
public <T> T executeObject(Class<T> returnType, MapSqlParameterSource args) {
return (T) doExecute(args).get(getScalarOutParameterName());
}
public Map<String, Object> execute() {
return doExecute(new HashMap<String, Object>());
}
public Map<String, Object> execute(Map<String, Object> args) {
return doExecute(args);
}
public Map<String, Object> execute(SqlParameterSource parameterSource) {
return doExecute(parameterSource);
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.util.Map;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
/**
* Interface specifying the API for a Simple JDBC Call implemented by {@link SimpleJdbcCall}.
* This interface is not often used directly, but provides the
* option to enhance testability, as it can easily be mocked or stubbed.
*
* @author Thomas Risberg
* @since 2.5
*/
public interface SimpleJdbcCallOperations {
/**
* Specify the procedure name to be used - this implies that we will be calling a stored procedure.
* @param procedureName the name of the stored procedure
* @return the instance of this SimpleJdbcCall
*/
SimpleJdbcCallOperations withProcedureName(String procedureName);
/**
* Specify the procedure name to be used - this implies that we will be calling a stored function.
* @param functionName the name of the stored function
* @return the instance of this SimpleJdbcCall
*/
SimpleJdbcCallOperations withFunctionName(String functionName);
/**
* Optionally, specify the name of the schema that contins the stored procedure.
* @param schemaName the name of the schema
* @return the instance of this SimpleJdbcCall
*/
SimpleJdbcCallOperations withSchemaName(String schemaName);
/**
* Optionally, specify the name of the catalog that contins the stored procedure.
* To provide consistency with the Oracle DatabaseMetaData, this is used to specify the package name if
* the procedure is declared as part of a package.
* @param catalogName the catalog or package name
* @return the instance of this SimpleJdbcCall
*/
SimpleJdbcCallOperations withCatalogName(String catalogName);
/**
* Indicates the procedure's return value should be included in the results returned.
* @return the instance of this SimpleJdbcCall
*/
SimpleJdbcCallOperations withReturnValue();
/**
* Specify one or more parameters if desired. These parameters will be supplemented with any
* parameter information retrieved from the database meta data.
* Note that only parameters declared as <code>SqlParameter</code> and <code>SqlInOutParameter</code>
* will be used to provide input values. This is different from the <code>StoredProcedure</code> class
* which for backwards compatibility reasons allows input values to be provided for parameters declared
* as <code>SqlOutParameter</code>.
*
* @param sqlParameters the parameters to use
* @return the instance of this SimpleJdbcCall
*/
SimpleJdbcCallOperations declareParameters(SqlParameter... sqlParameters);
/** Not used yet */
SimpleJdbcCallOperations useInParameterNames(String... inParameterNames);
/**
* Used to specify when a ResultSet is returned by the stored procedure and you want it mapped
* by a RowMapper. The results will be returned using the parameter name specified. Multiple
* ResultSets must be declared in the correct order. If the database you are using uses ref cursors
* then the name specified must match the name of the parameter declared for the procedure in the
* database.
* @param parameterName the name of the returned results and/or the name of the ref cursor parameter
* @param rowMapper the RowMapper implementation that will map the data returned for each row
* */
SimpleJdbcCallOperations returningResultSet(String parameterName, ParameterizedRowMapper rowMapper);
/**
* Turn off any processing of parameter meta data information obtained via JDBC.
* @return the instance of this SimpleJdbcCall
*/
SimpleJdbcCallOperations withoutProcedureColumnMetaDataAccess();
/**
* Execute the stored function and return the results obtained as an Object of the specified return type.
* @param returnType the type of the value to return
* @param args Map containing the parameter values to be used in the call.
*/
<T> T executeFunction(Class<T> returnType, Map args);
/**
* Execute the stored function and return the results obtained as an Object of the specified return type.
* @param returnType the type of the value to return
* @param args MapSqlParameterSource containing the parameter values to be used in the call.
*/
<T> T executeFunction(Class<T> returnType, MapSqlParameterSource args);
/**
* Execute the stored procedure and return the single out parameter as an Object of the specified return type.
* In the case where there are multiple out parameters, the first one is returned and additional out parameters
* are ignored.
* @param returnType the type of the value to return
* @param args Map containing the parameter values to be used in the call.
*/
<T> T executeObject(Class<T> returnType, Map args);
/**
* Execute the stored procedure and return the single out parameter as an Object of the specified return type.
* In the case where there are multiple out parameters, the first one is returned and additional out parameters
* are ignored.
* @param returnType the type of the value to return
* @param args MapSqlParameterSource containing the parameter values to be used in the call.
*/
<T> T executeObject(Class<T> returnType, MapSqlParameterSource args);
/**
* Execute the stored procedure and return a map of output params, keyed by name as in parameter declarations..
* @return map of output params.
*/
Map<String, Object> execute();
/**
* Execute the stored procedure and return a map of output params, keyed by name as in parameter declarations..
* @param args Map containing the parameter values to be used in the call.
* @return map of output params.
*/
Map<String, Object> execute(Map<String, Object> args);
/**
* Execute the stored procedure and return a map of output params, keyed by name as in parameter declarations..
* @param args SqlParameterSource containing the parameter values to be used in the call.
* @return map of output params.
*/
Map<String, Object> execute(SqlParameterSource args);
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* Extension of {@link org.springframework.jdbc.core.support.JdbcDaoSupport}
* that exposes a {@link #getSimpleJdbcTemplate() SimpleJdbcTemplate} as well.
* Only usable on Java 5 and above.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see SimpleJdbcTemplate
*/
public class SimpleJdbcDaoSupport extends JdbcDaoSupport {
private SimpleJdbcTemplate simpleJdbcTemplate;
/**
* Create a SimpleJdbcTemplate based on the configured JdbcTemplate.
*/
protected void initTemplateConfig() {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(getJdbcTemplate());
}
/**
* Return a SimpleJdbcTemplate wrapping the configured JdbcTemplate.
*/
public SimpleJdbcTemplate getSimpleJdbcTemplate() {
return this.simpleJdbcTemplate;
}
}

View File

@ -0,0 +1,129 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.util.Arrays;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.KeyHolder;
/**
* A SimpleJdbcInsert is a multi-threaded, reusable object providing easy insert
* capabilities for a table. It provides meta data processing to simplify the code
* needed to construct a basic insert statement. All you need to provide is the
* name of the table and a Map containing the column names and the column values.
*
* <p>The meta data processing is based on the DatabaseMetaData provided by the
* JDBC driver. As long as the JBDC driver can provide the names of the columns
* for a specified table than we can rely on this auto-detection feature. If that
* is not the case then the column names must be specified explicitly.
*
* <p>The actual insert is being handled using Spring's
* {@link org.springframework.jdbc.core.JdbcTemplate}.
*
* <p>Many of the configuration methods return the current instance of the SimpleJdbcInsert
* to provide the ability to string multiple ones together in a "fluid" interface style.
*
* @author Thomas Risberg
* @since 2.5
* @see java.sql.DatabaseMetaData
* @see org.springframework.jdbc.core.JdbcTemplate
*/
public class SimpleJdbcInsert extends AbstractJdbcInsert implements SimpleJdbcInsertOperations {
/**
* Constructor that takes one parameter with the JDBC DataSource to use when creating the
* JdbcTemplate.
* @param dataSource the <code>DataSource</code> to use
* @see org.springframework.jdbc.core.JdbcTemplate#setDataSource
*/
public SimpleJdbcInsert(DataSource dataSource) {
super(dataSource);
}
/**
* Alternative Constructor that takes one parameter with the JdbcTemplate to be used.
* @param jdbcTemplate the <code>JdbcTemplate</code> to use
* @see org.springframework.jdbc.core.JdbcTemplate#setDataSource
*/
public SimpleJdbcInsert(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
public SimpleJdbcInsert withTableName(String tableName) {
setTableName(tableName);
return this;
}
public SimpleJdbcInsert withSchemaName(String schemaName) {
setSchemaName(schemaName);
return this;
}
public SimpleJdbcInsert withCatalogName(String catalogName) {
setCatalogName(catalogName);
return this;
}
public SimpleJdbcInsert usingColumns(String... columnNames) {
setColumnNames(Arrays.asList(columnNames));
return this;
}
public SimpleJdbcInsert usingGeneratedKeyColumns(String... columnNames) {
setGeneratedKeyNames(columnNames);
return this;
}
public int execute(Map<String, Object> args) {
return doExecute(args);
}
public int execute(SqlParameterSource parameterSource) {
return doExecute(parameterSource);
}
public Number executeAndReturnKey(Map<String, Object> args) {
return doExecuteAndReturnKey(args);
}
public Number executeAndReturnKey(SqlParameterSource parameterSource) {
return doExecuteAndReturnKey(parameterSource);
}
public KeyHolder executeAndReturnKeyHolder(Map<String, Object> args) {
return doExecuteAndReturnKeyHolder(args);
}
public KeyHolder executeAndReturnKeyHolder(SqlParameterSource parameterSource) {
return doExecuteAndReturnKeyHolder(parameterSource);
}
public int[] executeBatch(Map<String, Object>[] batch) {
return doExecuteBatch(batch);
}
public int[] executeBatch(SqlParameterSource[] batch) {
return doExecuteBatch(batch);
}
}

View File

@ -0,0 +1,134 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.util.Map;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.KeyHolder;
/**
* Interface specifying the API for a Simple JDBC Insert implemented by {@link SimpleJdbcInsert}.
* This interface is not often used directly, but provides the
* option to enhance testability, as it can easily be mocked or stubbed.
*
* @author Thomas Risberg
* @since 2.5
*/
public interface SimpleJdbcInsertOperations {
/**
* Specify the table name to be used for the insert.
* @param tableName the name of the stored table
* @return the instance of this SimpleJdbcInsert
*/
SimpleJdbcInsertOperations withTableName(String tableName);
/**
* Specify the shema name, if any, to be used for the insert.
* @param schemaName the name of the schema
* @return the instance of this SimpleJdbcInsert
*/
SimpleJdbcInsertOperations withSchemaName(String schemaName);
/**
* Specify the catalog name, if any, to be used for the insert.
* @param catalogName the name of the catalog
* @return the instance of this SimpleJdbcInsert
*/
SimpleJdbcInsertOperations withCatalogName(String catalogName);
/**
* Specify the column names that the insert statement should be limited to use.
* @param columnNames one or more column names
* @return the instance of this SimpleJdbcInsert
*/
SimpleJdbcInsertOperations usingColumns(String... columnNames);
/**
* Specify the name sof any columns that have auto generated keys.
* @param columnNames one or more column names
* @return the instance of this SimpleJdbcInsert
*/
SimpleJdbcInsertOperations usingGeneratedKeyColumns(String... columnNames);
/**
* Execute the insert using the values passed in.
* @param args Map containing column names and corresponding value
* @return the number of rows affected as returned by the JDBC driver
*/
int execute(Map<String, Object> args);
/**
* Execute the insert using the values passed in.
* @param parameterSource SqlParameterSource containing values to use for insert
* @return the number of rows affected as returned by the JDBC driver
*/
int execute(SqlParameterSource parameterSource);
/**
* Execute the insert using the values passed in and return the generated key. This requires that
* the name of the columns with auto generated keys have been specified. This method will always
* return a key or throw an exception if a key was not returned.
* @param args Map containing column names and corresponding value
* @return the generated key value
*/
Number executeAndReturnKey(Map<String, Object> args);
/**
* Execute the insert using the values passed in and return the generated key. This requires that
* the name of the columns with auto generated keys have been specified. This method will always
* return a key or throw an exception if a key was not returned.
* @param parameterSource SqlParameterSource containing values to use for insert
* @return the generated key value.
*/
Number executeAndReturnKey(SqlParameterSource parameterSource);
/**
* Execute the insert using the values passed in and return the generated keys. This requires that
* the name of the columns with auto generated keys have been specified. This method will always return
* a KeyHolder but the caller must verify that it actually contains the generated keys.
* @param args Map containing column names and corresponding value
* @return the KeyHolder containing all generated keys
*/
KeyHolder executeAndReturnKeyHolder(Map<String, Object> args);
/**
* Execute the insert using the values passed in and return the generated keys. This requires that
* the name of the columns with auto generated keys have been specified. This method will always return
* a KeyHolder but the caller must verify that it actually contains the generated keys.
* @param parameterSource SqlParameterSource containing values to use for insert
* @return the KeyHolder containing all generated keys
*/
KeyHolder executeAndReturnKeyHolder(SqlParameterSource parameterSource);
/**
* Execute a batch insert using the batch of values passed in.
* @param batch an array of Maps containing a batch of column names and corresponding value
* @return the array of number of rows affected as returned by the JDBC driver
*/
int[] executeBatch(Map<String, Object>[] batch);
/**
* Execute a batch insert using the batch of values passed in.
* @param batch an array of SqlParameterSource containing values for the batch
* @return the array of number of rows affected as returned by the JDBC driver
*/
int[] executeBatch(SqlParameterSource[] batch);
}

View File

@ -0,0 +1,386 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.util.List;
import java.util.Map;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
/**
* JDBC operations interface usable on Java 5 and above, exposing a
* set of common JDBC operations, whose interface is simplified
* through the use of varargs and autoboxing.
*
* @author Rod Johnson
* @author Rob Harrop
* @author Thomas Risberg
* @since 2.0
* @see org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
* @see SimpleJdbcTemplate
* @see org.springframework.jdbc.core.JdbcOperations
*/
public interface SimpleJdbcOperations {
/**
* Expose the classic Spring JdbcTemplate to allow invocation of less
* commonly used methods.
*/
JdbcOperations getJdbcOperations();
/**
* Expose the Spring NamedParameterJdbcTemplate to allow invocation of less
* commonly used methods.
*/
NamedParameterJdbcOperations getNamedParameterJdbcOperations();
/**
* Query for an <code>int</code> passing in a SQL query
* using the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* and a map containing the arguments.
* @param sql the SQL query to run.
* @param args the map containing the arguments for the query.
*/
int queryForInt(String sql, Map args) throws DataAccessException;
/**
* Query for an <code>int</code> passing in a SQL query
* using the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* and a <code>SqlParameterSource</code> containing the arguments.
* @param sql the SQL query to run.
* @param args the <code>SqlParameterSource</code> containing the arguments for the query.
*/
int queryForInt(String sql, SqlParameterSource args) throws DataAccessException;
/**
* Query for an <code>int</code> passing in a SQL query
* using the standard '?' placeholders for parameters
* and a variable number of arguments.
* @param sql the SQL query to run.
* @param args the variable number of arguments for the query.
*/
int queryForInt(String sql, Object... args) throws DataAccessException;
/**
* Query for an <code>long</code> passing in a SQL query
* using the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* and a map containing the arguments.
* @param sql the SQL query to run.
* @param args the map containing the arguments for the query.
*/
long queryForLong(String sql, Map args) throws DataAccessException;
/**
* Query for an <code>long</code> passing in a SQL query
* using the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* and a <code>SqlParameterSource</code> containing the arguments.
* @param sql the SQL query to run.
* @param args the <code>SqlParameterSource</code> containing the arguments for the query.
*/
long queryForLong(String sql, SqlParameterSource args) throws DataAccessException;
/**
* Query for an <code>long</code> passing in a SQL query
* using the standard '?' placeholders for parameters
* and a variable number of arguments.
* @param sql the SQL query to run.
* @param args the variable number of arguments for the query.
*/
long queryForLong(String sql, Object... args) throws DataAccessException;
/**
* Query for an object of type <code>T</code> identified by the supplied @{@link Class}.
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL query to run.
* @param requiredType the required type of the return value.
* @param args the map containing the arguments for the query.
* @see JdbcOperations#queryForObject(String, Class)
* @see JdbcOperations#queryForObject(String, Object[], Class)
*/
<T> T queryForObject(String sql, Class<T> requiredType, Map args)
throws DataAccessException;
/**
* Query for an object of type <code>T</code> identified by the supplied @{@link Class}.
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL query to run.
* @param requiredType the required type of the return value.
* @param args the <code>SqlParameterSource</code> containing the arguments for the query.
* @see JdbcOperations#queryForObject(String, Class)
* @see JdbcOperations#queryForObject(String, Object[], Class)
*/
<T> T queryForObject(String sql, Class<T> requiredType, SqlParameterSource args)
throws DataAccessException;
/**
* Query for an object of type <code>T</code> identified by the supplied @{@link Class}.
* Uses sql with the standard '?' placeholders for parameters
* @param sql the SQL query to run.
* @param requiredType the required type of the return value.
* @param args the variable number of arguments for the query.
* @see JdbcOperations#queryForObject(String, Class)
* @see JdbcOperations#queryForObject(String, Object[], Class)
*/
<T> T queryForObject(String sql, Class<T> requiredType, Object... args)
throws DataAccessException;
/**
* Query for an object of type <code>T</code> using the supplied
* {@link ParameterizedRowMapper} to the query results to the object.
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL query to run.
* @param rm the @{@link ParameterizedRowMapper} to use for result mapping
* @param args the map containing the arguments for the query.
* @see JdbcOperations#queryForObject(String, org.springframework.jdbc.core.RowMapper)
* @see JdbcOperations#queryForObject(String, Object[], org.springframework.jdbc.core.RowMapper)
*/
<T> T queryForObject(String sql, ParameterizedRowMapper<T> rm, Map args)
throws DataAccessException;
/**
* Query for an object of type <code>T</code> using the supplied
* {@link ParameterizedRowMapper} to the query results to the object.
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL query to run.
* @param rm the @{@link ParameterizedRowMapper} to use for result mapping
* @param args the <code>SqlParameterSource</code> containing the arguments for the query.
* @see JdbcOperations#queryForObject(String, org.springframework.jdbc.core.RowMapper)
* @see JdbcOperations#queryForObject(String, Object[], org.springframework.jdbc.core.RowMapper)
*/
<T> T queryForObject(String sql, ParameterizedRowMapper<T> rm, SqlParameterSource args)
throws DataAccessException;
/**
* Query for an object of type <code>T</code> using the supplied
* {@link ParameterizedRowMapper} to the query results to the object.
* Uses sql with the standard '?' placeholders for parameters
* @param sql the SQL query to run.
* @param rm the @{@link ParameterizedRowMapper} to use for result mapping
* @param args the variable number of arguments for the query.
* @see JdbcOperations#queryForObject(String, org.springframework.jdbc.core.RowMapper)
* @see JdbcOperations#queryForObject(String, Object[], org.springframework.jdbc.core.RowMapper)
*/
<T> T queryForObject(String sql, ParameterizedRowMapper<T> rm, Object... args)
throws DataAccessException;
/**
* Query for a {@link List} of <code>Objects</code> of type <code>T</code> using
* the supplied {@link ParameterizedRowMapper} to the query results to the object.
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL query to run.
* @param rm the @{@link ParameterizedRowMapper} to use for result mapping
* @param args the map containing the arguments for the query.
* @see JdbcOperations#queryForObject(String, org.springframework.jdbc.core.RowMapper)
* @see JdbcOperations#queryForObject(String, Object[], org.springframework.jdbc.core.RowMapper)
*/
<T> List<T> query(String sql, ParameterizedRowMapper<T> rm, Map args)
throws DataAccessException;
/**
* Query for a {@link List} of <code>Objects</code> of type <code>T</code> using
* the supplied {@link ParameterizedRowMapper} to the query results to the object.
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL query to run.
* @param rm the @{@link ParameterizedRowMapper} to use for result mapping
* @param args the <code>SqlParameterSource</code> containing the arguments for the query.
* @see JdbcOperations#queryForObject(String, org.springframework.jdbc.core.RowMapper)
* @see JdbcOperations#queryForObject(String, Object[], org.springframework.jdbc.core.RowMapper)
*/
<T> List<T> query(String sql, ParameterizedRowMapper<T> rm, SqlParameterSource args)
throws DataAccessException;
/**
* Query for a {@link List} of <code>Objects</code> of type <code>T</code> using
* the supplied {@link ParameterizedRowMapper} to the query results to the object.
* Uses sql with the standard '?' placeholders for parameters
* @param sql the SQL query to run.
* @param rm the @{@link ParameterizedRowMapper} to use for result mapping
* @param args the variable number of arguments for the query.
* @see JdbcOperations#queryForObject(String, org.springframework.jdbc.core.RowMapper)
* @see JdbcOperations#queryForObject(String, Object[], org.springframework.jdbc.core.RowMapper)
*/
<T> List<T> query(String sql, ParameterizedRowMapper<T> rm, Object... args)
throws DataAccessException;
/**
* Execute the supplied query with the supplied arguments.
* <p>The query is expected to be a single row query; the result row will be
* mapped to a Map (one entry for each column, using the column name as the key).
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL query to run.
* @param args the map containing the arguments for the query.
* @see JdbcOperations#queryForMap(String)
* @see JdbcOperations#queryForMap(String, Object[])
*/
Map<String, Object> queryForMap(String sql, Map args)
throws DataAccessException;
/**
* Execute the supplied query with the supplied arguments.
* <p>The query is expected to be a single row query; the result row will be
* mapped to a Map (one entry for each column, using the column name as the key).
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL query to run.
* @param args the <code>SqlParameterSource</code> containing the arguments for the query.
* @see JdbcOperations#queryForMap(String)
* @see JdbcOperations#queryForMap(String, Object[])
*/
Map<String, Object> queryForMap(String sql, SqlParameterSource args)
throws DataAccessException;
/**
* Execute the supplied query with the (optional) supplied arguments.
* <p>The query is expected to be a single row query; the result row will be
* mapped to a Map (one entry for each column, using the column name as the key).
* Uses sql with the standard '?' placeholders for parameters
* @param sql the SQL query to run.
* @param args the variable number of arguments for the query.
* @see JdbcOperations#queryForMap(String)
* @see JdbcOperations#queryForMap(String, Object[])
*/
Map<String, Object> queryForMap(String sql, Object... args)
throws DataAccessException;
/**
* Execute the supplied query with the supplied arguments.
* <p>Each element in the returned {@link List} is constructed as a {@link Map}
* as described in {@link #queryForMap}
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL query to run.
* @param args the map containing the arguments for the query.
* @see JdbcOperations#queryForList(String)
* @see JdbcOperations#queryForList(String, Object[])
*/
List<Map<String, Object>> queryForList(String sql, Map args)
throws DataAccessException;
/**
* Execute the supplied query with the supplied arguments.
* <p>Each element in the returned {@link List} is constructed as a {@link Map}
* as described in {@link #queryForMap}
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL query to run.
* @param args the <code>SqlParameterSource</code> containing the arguments for the query.
* @see JdbcOperations#queryForList(String)
* @see JdbcOperations#queryForList(String, Object[])
*/
List<Map<String, Object>> queryForList(String sql, SqlParameterSource args)
throws DataAccessException;
/**
* Execute the supplied query with the (optional) supplied arguments.
* <p>Each element in the returned {@link List} is constructed as a {@link Map}
* as described in {@link #queryForMap}
* Uses sql with the standard '?' placeholders for parameters
* @param sql the SQL query to run.
* @param args the variable number of arguments for the query.
* @see JdbcOperations#queryForList(String)
* @see JdbcOperations#queryForList(String, Object[])
*/
List<Map<String, Object>> queryForList(String sql, Object... args)
throws DataAccessException;
/**
* Execute the supplied SQL statement with (optional) supplied arguments.
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL statement to execute.
* @param args the map containing the arguments for the query.
* @return the numbers of rows affected by the update.
* @see NamedParameterJdbcOperations#update(String, Map)
*/
int update(String sql, Map args) throws DataAccessException;
/**
* Execute the supplied SQL statement with supplied arguments.
* Uses sql with the named parameter support provided by the
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* @param sql the SQL statement to execute.
* @param args the <code>SqlParameterSource</code> containing the arguments for the statement.
* @return the numbers of rows affected by the update.
* @see NamedParameterJdbcOperations#update(String, SqlParameterSource)
*/
int update(String sql, SqlParameterSource args) throws DataAccessException;
/**
* Execute the supplied SQL statement with supplied arguments.
* Uses sql with the standard '?' placeholders for parameters
* @param sql the SQL statement to execute.
* @param args the variable number of arguments for the query.
* @return the numbers of rows affected by the update.
* @see JdbcOperations#update(String)
* @see JdbcOperations#update(String, Object[])
*/
int update(String sql, Object... args) throws DataAccessException;
/**
* Executes a batch using the supplied SQL statement with the batch of supplied arguments.
* Uses sql with the named parameter support.
* @param sql the SQL statement to execute.
* @param batchValues the array of Maps containing the batch of arguments for the query.
* @return an array containing the numbers of rows affected by each update in the batch.
*/
public int[] batchUpdate(String sql, Map[] batchValues);
/**
* Execute a batch using the supplied SQL statement with the batch of supplied arguments.
* Uses sql with the named parameter support.
* @param sql the SQL statement to execute.
* @param batchArgs the array of {@link SqlParameterSource} containing the batch of arguments for the query.
* @return an array containing the numbers of rows affected by each update in the batch.
*/
public int[] batchUpdate(String sql, SqlParameterSource[] batchArgs);
/**
* Execute a batch using the supplied SQL statement with the batch of supplied arguments.
* Uses sql with the standard '?' placeholders for parameters
* @param sql the SQL statement to execute.
* @param batchArgs the List of Object arrays containing the batch of arguments for the query.
* @return an array containing the numbers of rows affected by each update in the batch.
*/
public int[] batchUpdate(String sql, List<Object[]> batchArgs);
/**
* Execute a batch using the supplied SQL statement with the batch of supplied arguments.
* Uses sql with the standard '?' placeholders for parameters
* @param sql the SQL statement to execute.
* @param batchArgs the List of Object arrays containing the batch of arguments for the query.
* @param argTypes SQL types of the arguments
* (constants from <code>java.sql.Types</code>)
* @return an array containing the numbers of rows affected by each update in the batch.
*/
public int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes);
}

View File

@ -0,0 +1,338 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.simple;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterUtils;
import org.springframework.jdbc.core.namedparam.ParsedSql;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.ObjectUtils;
/**
* Java-5-based convenience wrapper for the classic Spring
* {@link org.springframework.jdbc.core.JdbcTemplate},
* taking advantage of varargs and autoboxing, and exposing only the most
* commonly required operations in order to simplify JdbcTemplate usage.
*
* <p>Use the {@link #getJdbcOperations()} method (or a straight JdbcTemplate)
* if you need to invoke less commonly used template methods. This includes
* any methods specifying SQL types, methods using less commonly used callbacks
* such as RowCallbackHandler, updates with PreparedStatementSetters rather than
* argument arrays, and stored procedures as well as batch operations.
*
* @author Rod Johnson
* @author Rob Harrop
* @author Juergen Hoeller
* @author Thomas Risberg
* @since 2.0
* @see ParameterizedRowMapper
* @see SimpleJdbcDaoSupport
* @see org.springframework.jdbc.core.JdbcTemplate
*/
public class SimpleJdbcTemplate implements SimpleJdbcOperations {
/** The NamedParameterJdbcTemplate that we are wrapping */
private final NamedParameterJdbcOperations namedParameterJdbcOperations;
/**
* Create a new SimpleJdbcTemplate for the given DataSource.
* <p>Creates a classic Spring JdbcTemplate and wraps it.
* @param dataSource the JDBC DataSource to access
*/
public SimpleJdbcTemplate(DataSource dataSource) {
this.namedParameterJdbcOperations = new NamedParameterJdbcTemplate(dataSource);
}
/**
* Create a new SimpleJdbcTemplate for the given classic Spring JdbcTemplate.
* @param classicJdbcTemplate the classic Spring JdbcTemplate to wrap
*/
public SimpleJdbcTemplate(JdbcOperations classicJdbcTemplate) {
this.namedParameterJdbcOperations = new NamedParameterJdbcTemplate(classicJdbcTemplate);
}
/**
* Create a new SimpleJdbcTemplate for the given Spring NamedParameterJdbcTemplate.
* @param namedParameterJdbcTemplate the Spring NamedParameterJdbcTemplate to wrap
*/
public SimpleJdbcTemplate(NamedParameterJdbcOperations namedParameterJdbcTemplate) {
this.namedParameterJdbcOperations = namedParameterJdbcTemplate;
}
/**
* Expose the classic Spring JdbcTemplate to allow invocation of
* less commonly used methods.
*/
public JdbcOperations getJdbcOperations() {
return this.namedParameterJdbcOperations.getJdbcOperations();
}
/**
* Expose the Spring NamedParameterJdbcTemplate to allow invocation of
* less commonly used methods.
*/
public NamedParameterJdbcOperations getNamedParameterJdbcOperations() {
return this.namedParameterJdbcOperations;
}
public int queryForInt(String sql, Map args) throws DataAccessException {
return getNamedParameterJdbcOperations().queryForInt(sql, args);
}
public int queryForInt(String sql, SqlParameterSource args) throws DataAccessException {
return getNamedParameterJdbcOperations().queryForInt(sql, args);
}
public int queryForInt(String sql, Object... args) throws DataAccessException {
return (ObjectUtils.isEmpty(args) ?
getJdbcOperations().queryForInt(sql) :
getJdbcOperations().queryForInt(sql, getArguments(args)));
}
public long queryForLong(String sql, Map args) throws DataAccessException {
return getNamedParameterJdbcOperations().queryForLong(sql, args);
}
public long queryForLong(String sql, SqlParameterSource args) throws DataAccessException {
return getNamedParameterJdbcOperations().queryForLong(sql, args);
}
public long queryForLong(String sql, Object... args) throws DataAccessException {
return (ObjectUtils.isEmpty(args) ?
getJdbcOperations().queryForLong(sql) :
getJdbcOperations().queryForLong(sql, getArguments(args)));
}
@SuppressWarnings("unchecked")
public <T> T queryForObject(String sql, Class<T> requiredType, Map args) throws DataAccessException {
return (T) getNamedParameterJdbcOperations().queryForObject(sql, args, requiredType);
}
@SuppressWarnings("unchecked")
public <T> T queryForObject(String sql, Class<T> requiredType, SqlParameterSource args)
throws DataAccessException {
return (T) getNamedParameterJdbcOperations().queryForObject(sql, args, requiredType);
}
@SuppressWarnings("unchecked")
public <T> T queryForObject(String sql, Class<T> requiredType, Object... args) throws DataAccessException {
return (T) (ObjectUtils.isEmpty(args) ?
getJdbcOperations().queryForObject(sql, requiredType) :
getJdbcOperations().queryForObject(sql, getArguments(args), requiredType));
}
@SuppressWarnings("unchecked")
public <T> T queryForObject(String sql, ParameterizedRowMapper<T> rm, Map args) throws DataAccessException {
return (T) getNamedParameterJdbcOperations().queryForObject(sql, args, rm);
}
@SuppressWarnings("unchecked")
public <T> T queryForObject(String sql, ParameterizedRowMapper<T> rm, SqlParameterSource args)
throws DataAccessException {
return (T) getNamedParameterJdbcOperations().queryForObject(sql, args, rm);
}
@SuppressWarnings("unchecked")
public <T> T queryForObject(String sql, ParameterizedRowMapper<T> rm, Object... args) throws DataAccessException {
return (T) (ObjectUtils.isEmpty(args) ?
getJdbcOperations().queryForObject(sql, rm):
getJdbcOperations().queryForObject(sql, getArguments(args), rm));
}
@SuppressWarnings("unchecked")
public <T> List<T> query(String sql, ParameterizedRowMapper<T> rm, Map args) throws DataAccessException {
return (List<T>) getNamedParameterJdbcOperations().query(sql, args, rm);
}
@SuppressWarnings("unchecked")
public <T> List<T> query(String sql, ParameterizedRowMapper<T> rm, SqlParameterSource args)
throws DataAccessException {
return (List<T>) getNamedParameterJdbcOperations().query(sql, args, rm);
}
@SuppressWarnings("unchecked")
public <T> List<T> query(String sql, ParameterizedRowMapper<T> rm, Object... args) throws DataAccessException {
return (List<T>) (ObjectUtils.isEmpty(args) ?
getJdbcOperations().query(sql, rm) :
getJdbcOperations().query(sql, getArguments(args), rm));
}
@SuppressWarnings("unchecked")
public Map<String, Object> queryForMap(String sql, Map args) throws DataAccessException {
return getNamedParameterJdbcOperations().queryForMap(sql, args);
}
@SuppressWarnings("unchecked")
public Map<String, Object> queryForMap(String sql, SqlParameterSource args)
throws DataAccessException {
return getNamedParameterJdbcOperations().queryForMap(sql, args);
}
@SuppressWarnings("unchecked")
public Map<String, Object> queryForMap(String sql, Object... args) throws DataAccessException {
return (ObjectUtils.isEmpty(args) ?
getJdbcOperations().queryForMap(sql) :
getJdbcOperations().queryForMap(sql, getArguments(args)));
}
@SuppressWarnings("unchecked")
public List<Map<String, Object>> queryForList(String sql, Map args) throws DataAccessException {
return getNamedParameterJdbcOperations().queryForList(sql, args);
}
@SuppressWarnings("unchecked")
public List<Map<String, Object>> queryForList(String sql, SqlParameterSource args)
throws DataAccessException {
return getNamedParameterJdbcOperations().queryForList(sql, args);
}
@SuppressWarnings("unchecked")
public List<Map<String, Object>> queryForList(String sql, Object... args) throws DataAccessException {
return (ObjectUtils.isEmpty(args) ?
getJdbcOperations().queryForList(sql) :
getJdbcOperations().queryForList(sql, getArguments(args)));
}
public int update(String sql, Map args) throws DataAccessException {
return getNamedParameterJdbcOperations().update(sql, args);
}
public int update(String sql, SqlParameterSource args) throws DataAccessException {
return getNamedParameterJdbcOperations().update(sql, args);
}
public int update(String sql, Object ... args) throws DataAccessException {
return (ObjectUtils.isEmpty(args) ?
getJdbcOperations().update(sql) :
getJdbcOperations().update(sql, getArguments(args)));
}
public int[] batchUpdate(String sql, List<Object[]> batchArgs) {
return doExecuteBatchUpdate(sql, batchArgs, new int[0]);
}
public int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes) {
return doExecuteBatchUpdate(sql, batchArgs, argTypes);
}
public int[] batchUpdate(String sql, Map[] batchValues) {
SqlParameterSource[] batchArgs = new SqlParameterSource[batchValues.length];
int i = 0;
for (Map values : batchValues) {
batchArgs[i] = new MapSqlParameterSource(values);
i++;
}
return doExecuteBatchUpdateWithNamedParameters(sql, batchArgs);
}
public int[] batchUpdate(String sql, SqlParameterSource[] batchArgs) {
return doExecuteBatchUpdateWithNamedParameters(sql, batchArgs);
}
private int[] doExecuteBatchUpdate(String sql, final List<Object[]> batchValues, final int[] columnTypes) {
return getJdbcOperations().batchUpdate(
sql,
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
Object[] values = batchValues.get(i);
doSetStatementParameters(values, ps, columnTypes);
}
public int getBatchSize() {
return batchValues.size();
}
});
}
private int[] doExecuteBatchUpdateWithNamedParameters(String sql, final SqlParameterSource[] batchArgs) {
if (batchArgs.length <= 0) {
return new int[] {0};
}
final ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, batchArgs[0]);
return getJdbcOperations().batchUpdate(
sqlToUse,
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
Object[] values = NamedParameterUtils.buildValueArray(parsedSql, batchArgs[i], null);
int[] columnTypes = NamedParameterUtils.buildSqlTypeArray(parsedSql, batchArgs[i]);
doSetStatementParameters(values, ps, columnTypes);
}
public int getBatchSize() {
return batchArgs.length;
}
});
}
private void doSetStatementParameters(Object[] values, PreparedStatement ps, int[] columnTypes) throws SQLException {
int colIndex = 0;
for (Object value : values) {
colIndex++;
if (value instanceof SqlParameterValue) {
SqlParameterValue paramValue = (SqlParameterValue) value;
StatementCreatorUtils.setParameterValue(ps, colIndex, paramValue, paramValue.getValue());
}
else {
int colType;
if (columnTypes == null || columnTypes.length < colIndex) {
colType = SqlTypeValue.TYPE_UNKNOWN;
}
else {
colType = columnTypes[colIndex - 1];
}
StatementCreatorUtils.setParameterValue(ps, colIndex, colType, value);
}
}
}
/**
* Considers an Object array passed into a varargs parameter as
* collection of arguments rather than as single argument.
*/
private Object[] getArguments(Object[] varArgs) {
if (varArgs.length == 1 && varArgs[0] instanceof Object[]) {
return (Object[]) varArgs[0];
}
else {
return varArgs;
}
}
}

View File

@ -0,0 +1,17 @@
<html>
<body>
Simplification layer over JdbcTemplate for Java 5 and above.
<p>SimpleJdbcTemplate is a wrapper around JdbcTemplate that takes advantage
of varargs and autoboxing. It also offers only a subset of the methods
available on JdbcTemplate: Hence, it does not implement the JdbcOperations
interface or extend JdbcTemplate, but implements the dedicated
SimpleJdbcOperations interface.
<P>If you need the full power of Spring JDBC for less common operations,
use the <code>getJdbcOperations()</code> method of SimpleJdbcTemplate and work
with the returned classic template, or use a JdbcTemplate instance directly.
</body>
</html>

View File

@ -0,0 +1,76 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.support;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.jdbc.core.InterruptibleBatchPreparedStatementSetter;
/**
* Abstract implementation of the {@link InterruptibleBatchPreparedStatementSetter}
* interface, combining the check for available values and setting of those
* into a single callback method {@link #setValuesIfAvailable}.
*
* @author Juergen Hoeller
* @since 2.0
* @see #setValuesIfAvailable
*/
public abstract class AbstractInterruptibleBatchPreparedStatementSetter
implements InterruptibleBatchPreparedStatementSetter {
private boolean exhausted;
/**
* This implementation calls {@link #setValuesIfAvailable}
* and sets this instance's exhaustion flag accordingly.
*/
public final void setValues(PreparedStatement ps, int i) throws SQLException {
this.exhausted = !setValuesIfAvailable(ps, i);
}
/**
* This implementation return this instance's current exhaustion flag.
*/
public final boolean isBatchExhausted(int i) {
return this.exhausted;
}
/**
* This implementation returns <code>Integer.MAX_VALUE</code>.
* Can be overridden in subclasses to lower the maximum batch size.
*/
public int getBatchSize() {
return Integer.MAX_VALUE;
}
/**
* Check for available values and set them on the given PreparedStatement.
* If no values are available anymore, return <code>false</code>.
* @param ps PreparedStatement we'll invoke setter methods on
* @param i index of the statement we're issuing in the batch, starting from 0
* @return whether there were values to apply (that is, whether the applied
* parameters should be added to the batch and this method should be called
* for a further iteration)
* @throws SQLException if a SQLException is encountered
* (i.e. there is no need to catch SQLException)
*/
protected abstract boolean setValuesIfAvailable(PreparedStatement ps, int i) throws SQLException;
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.support;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
/**
* Abstract PreparedStatementCallback implementation that manages a LobCreator.
* Typically used as inner class, with access to surrounding method arguments.
*
* <p>Delegates to the <code>setValues</code> template method for setting values
* on the PreparedStatement, using a given LobCreator for BLOB/CLOB arguments.
*
* <p>A usage example with JdbcTemplate:
*
* <pre class="code">JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); // reusable object
* LobHandler lobHandler = new DefaultLobHandler(); // reusable object
*
* jdbcTemplate.execute(
* "INSERT INTO imagedb (image_name, content, description) VALUES (?, ?, ?)",
* new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
* protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException {
* ps.setString(1, name);
* lobCreator.setBlobAsBinaryStream(ps, 2, contentStream, contentLength);
* lobCreator.setClobAsString(ps, 3, description);
* }
* }
* );</pre>
*
* @author Juergen Hoeller
* @since 1.0.2
* @see org.springframework.jdbc.support.lob.LobCreator
*/
public abstract class AbstractLobCreatingPreparedStatementCallback implements PreparedStatementCallback {
private final LobHandler lobHandler;
/**
* Create a new AbstractLobCreatingPreparedStatementCallback for the
* given LobHandler.
* @param lobHandler the LobHandler to create LobCreators with
*/
public AbstractLobCreatingPreparedStatementCallback(LobHandler lobHandler) {
this.lobHandler = lobHandler;
}
public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
LobCreator lobCreator = this.lobHandler.getLobCreator();
try {
setValues(ps, lobCreator);
return new Integer(ps.executeUpdate());
}
finally {
lobCreator.close();
}
}
/**
* Set values on the given PreparedStatement, using the given
* LobCreator for BLOB/CLOB arguments.
* @param ps the PreparedStatement to use
* @param lobCreator the LobCreator to use
* @throws SQLException if thrown by JDBC methods
* @throws DataAccessException in case of custom exceptions
*/
protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator)
throws SQLException, DataAccessException;
}

View File

@ -0,0 +1,120 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.support;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.LobRetrievalFailureException;
import org.springframework.jdbc.core.ResultSetExtractor;
/**
* Abstract ResultSetExtractor implementation that assumes streaming of LOB data.
* Typically used as inner class, with access to surrounding method arguments.
*
* <p>Delegates to the <code>streamData</code> template method for streaming LOB
* content to some OutputStream, typically using a LobHandler. Converts an
* IOException thrown during streaming to a LobRetrievalFailureException.
*
* <p>A usage example with JdbcTemplate:
*
* <pre class="code">JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); // reusable object
* final LobHandler lobHandler = new DefaultLobHandler(); // reusable object
*
* jdbcTemplate.query(
* "SELECT content FROM imagedb WHERE image_name=?", new Object[] {name},
* new AbstractLobStreamingResultSetExtractor() {
* public void streamData(ResultSet rs) throws SQLException, IOException {
* FileCopyUtils.copy(lobHandler.getBlobAsBinaryStream(rs, 1), contentStream);
* }
* }
* );</pre>
*
* @author Juergen Hoeller
* @since 1.0.2
* @see org.springframework.jdbc.support.lob.LobHandler
* @see org.springframework.jdbc.LobRetrievalFailureException
*/
public abstract class AbstractLobStreamingResultSetExtractor implements ResultSetExtractor {
/**
* Delegates to handleNoRowFound, handleMultipleRowsFound and streamData,
* according to the ResultSet state. Converts an IOException thrown by
* streamData to a LobRetrievalFailureException.
* @see #handleNoRowFound
* @see #handleMultipleRowsFound
* @see #streamData
* @see org.springframework.jdbc.LobRetrievalFailureException
*/
public final Object extractData(ResultSet rs) throws SQLException, DataAccessException {
if (!rs.next()) {
handleNoRowFound();
}
else {
try {
streamData(rs);
if (rs.next()) {
handleMultipleRowsFound();
}
}
catch (IOException ex) {
throw new LobRetrievalFailureException("Couldn't stream LOB content", ex);
}
}
return null;
}
/**
* Handle the case where the ResultSet does not contain a row.
* @throws DataAccessException a corresponding exception,
* by default an EmptyResultDataAccessException
* @see org.springframework.dao.EmptyResultDataAccessException
*/
protected void handleNoRowFound() throws DataAccessException {
throw new EmptyResultDataAccessException(
"LobStreamingResultSetExtractor did not find row in database", 1);
}
/**
* Handle the case where the ResultSet contains multiple rows.
* @throws DataAccessException a corresponding exception,
* by default an IncorrectResultSizeDataAccessException
* @see org.springframework.dao.IncorrectResultSizeDataAccessException
*/
protected void handleMultipleRowsFound() throws DataAccessException {
throw new IncorrectResultSizeDataAccessException(
"LobStreamingResultSetExtractor found multiple rows in database", 1);
}
/**
* Stream LOB content from the given ResultSet to some OutputStream.
* <p>Typically used as inner class, with access to surrounding method arguments
* and to a LobHandler instance variable of the surrounding class.
* @param rs the ResultSet to take the LOB content from
* @throws SQLException if thrown by JDBC methods
* @throws IOException if thrown by stream access methods
* @throws DataAccessException in case of custom exceptions
* @see org.springframework.jdbc.support.lob.LobHandler#getBlobAsBinaryStream
* @see org.springframework.util.FileCopyUtils
*/
protected abstract void streamData(ResultSet rs) throws SQLException, IOException, DataAccessException;
}

View File

@ -0,0 +1,79 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.support;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.jdbc.core.SqlTypeValue;
/**
* Abstract implementation of the SqlTypeValue interface, for convenient
* creation of type values that are supposed to be passed into the
* <code>PreparedStatement.setObject</code> method. The <code>createTypeValue</code>
* callback method has access to the underlying Connection, if that should
* be needed to create any database-specific objects.
*
* <p>A usage example from a StoredProcedure (compare this to the plain
* SqlTypeValue version in the superclass javadoc):
*
* <pre class="code">proc.declareParameter(new SqlParameter("myarray", Types.ARRAY, "NUMBERS"));
* ...
*
* Map in = new HashMap();
* in.put("myarray", new AbstractSqlTypeValue() {
* public Object createTypeValue(Connection con, int sqlType, String typeName) throws SQLException {
* oracle.sql.ArrayDescriptor desc = new oracle.sql.ArrayDescriptor(typeName, con);
* return new oracle.sql.ARRAY(desc, con, seats);
* }
* });
* Map out = execute(in);
* </pre>
*
* @author Juergen Hoeller
* @since 1.1
* @see java.sql.PreparedStatement#setObject(int, Object, int)
* @see org.springframework.jdbc.object.StoredProcedure
*/
public abstract class AbstractSqlTypeValue implements SqlTypeValue {
public final void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName)
throws SQLException {
Object value = createTypeValue(ps.getConnection(), sqlType, typeName);
if (sqlType == TYPE_UNKNOWN) {
ps.setObject(paramIndex, value);
}
else {
ps.setObject(paramIndex, value, sqlType);
}
}
/**
* Create the type value to be passed into <code>PreparedStatement.setObject</code>.
* @param con the JDBC Connection, if needed to create any database-specific objects
* @param sqlType SQL type of the parameter we are setting
* @param typeName the type name of the parameter
* @return the type value
* @throws SQLException if a SQLException is encountered setting
* parameter values (that is, there's no need to catch SQLException)
* @see java.sql.PreparedStatement#setObject(int, Object, int)
*/
protected abstract Object createTypeValue(Connection con, int sqlType, String typeName) throws SQLException;
}

View File

@ -0,0 +1,121 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.support;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.util.Assert;
/**
* Bean definition reader that reads values from a database table,
* based on a given SQL statement.
*
* <p>Expects columns for bean name, property name and value as String.
* Formats for each are identical to the properties format recognized
* by PropertiesBeanDefinitionReader.
*
* <p><b>NOTE:</b> This is mainly intended as an example for a custom
* JDBC-based bean definition reader. It does not aim to offer
* comprehensive functionality.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see #loadBeanDefinitions
* @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
*/
public class JdbcBeanDefinitionReader {
private final PropertiesBeanDefinitionReader propReader;
private JdbcTemplate jdbcTemplate;
/**
* Create a new JdbcBeanDefinitionReader for the given bean factory,
* using a default PropertiesBeanDefinitionReader underneath.
* <p>DataSource or JdbcTemplate still need to be set.
* @see #setDataSource
* @see #setJdbcTemplate
*/
public JdbcBeanDefinitionReader(BeanDefinitionRegistry beanFactory) {
this.propReader = new PropertiesBeanDefinitionReader(beanFactory);
}
/**
* Create a new JdbcBeanDefinitionReader that delegates to the
* given PropertiesBeanDefinitionReader underneath.
* <p>DataSource or JdbcTemplate still need to be set.
* @see #setDataSource
* @see #setJdbcTemplate
*/
public JdbcBeanDefinitionReader(PropertiesBeanDefinitionReader beanDefinitionReader) {
Assert.notNull(beanDefinitionReader, "Bean definition reader must not be null");
this.propReader = beanDefinitionReader;
}
/**
* Set the DataSource to use to obtain database connections.
* Will implicitly create a new JdbcTemplate with the given DataSource.
*/
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* Set the JdbcTemplate to be used by this bean factory.
* Contains settings for DataSource, SQLExceptionTranslator, NativeJdbcExtractor, etc.
*/
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
Assert.notNull(jdbcTemplate, "JdbcTemplate must not be null");
this.jdbcTemplate = jdbcTemplate;
}
/**
* Load bean definitions from the database via the given SQL string.
* @param sql SQL query to use for loading bean definitions.
* The first three columns must be bean name, property name and value.
* Any join and any other columns are permitted: e.g.
* <code>SELECT BEAN_NAME, PROPERTY, VALUE FROM CONFIG WHERE CONFIG.APP_ID = 1</code>
* It's also possible to perform a join. Column names are not significant --
* only the ordering of these first three columns.
*/
public void loadBeanDefinitions(String sql) {
Assert.notNull(this.jdbcTemplate, "Not fully configured - specify DataSource or JdbcTemplate");
final Properties props = new Properties();
this.jdbcTemplate.query(sql, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
String beanName = rs.getString(1);
String property = rs.getString(2);
String value = rs.getString(3);
// Make a properties entry by combining bean name and property.
props.setProperty(beanName + "." + property, value);
}
});
this.propReader.registerBeanDefinitions(props);
}
}

View File

@ -0,0 +1,145 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.support;
import java.sql.Connection;
import javax.sql.DataSource;
import org.springframework.dao.support.DaoSupport;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.SQLExceptionTranslator;
/**
* Convenient super class for JDBC-based data access objects.
*
* <p>Requires a {@link javax.sql.DataSource} to be set, providing a
* {@link org.springframework.jdbc.core.JdbcTemplate} based on it to
* subclasses through the {@link #getJdbcTemplate()} method.
*
* <p>This base class is mainly intended for JdbcTemplate usage but can
* also be used when working with a Connection directly or when using
* <code>org.springframework.jdbc.object</code> operation objects.
*
* @author Juergen Hoeller
* @since 28.07.2003
* @see #setDataSource
* @see #getJdbcTemplate
* @see org.springframework.jdbc.core.JdbcTemplate
*/
public abstract class JdbcDaoSupport extends DaoSupport {
private JdbcTemplate jdbcTemplate;
/**
* Set the JDBC DataSource to be used by this DAO.
*/
public final void setDataSource(DataSource dataSource) {
if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
this.jdbcTemplate = createJdbcTemplate(dataSource);
initTemplateConfig();
}
}
/**
* Create a JdbcTemplate for the given DataSource.
* Only invoked if populating the DAO with a DataSource reference!
* <p>Can be overridden in subclasses to provide a JdbcTemplate instance
* with different configuration, or a custom JdbcTemplate subclass.
* @param dataSource the JDBC DataSource to create a JdbcTemplate for
* @return the new JdbcTemplate instance
* @see #setDataSource
*/
protected JdbcTemplate createJdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
/**
* Return the JDBC DataSource used by this DAO.
*/
public final DataSource getDataSource() {
return (this.jdbcTemplate != null ? this.jdbcTemplate.getDataSource() : null);
}
/**
* Set the JdbcTemplate for this DAO explicitly,
* as an alternative to specifying a DataSource.
*/
public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
initTemplateConfig();
}
/**
* Return the JdbcTemplate for this DAO,
* pre-initialized with the DataSource or set explicitly.
*/
public final JdbcTemplate getJdbcTemplate() {
return this.jdbcTemplate;
}
/**
* Initialize the template-based configuration of this DAO.
* Called after a new JdbcTemplate has been set, either directly
* or through a DataSource.
* <p>This implementation is empty. Subclasses may override this
* to configure further objects based on the JdbcTemplate.
* @see #getJdbcTemplate()
*/
protected void initTemplateConfig() {
}
protected void checkDaoConfig() {
if (this.jdbcTemplate == null) {
throw new IllegalArgumentException("'dataSource' or 'jdbcTemplate' is required");
}
}
/**
* Return the SQLExceptionTranslator of this DAO's JdbcTemplate,
* for translating SQLExceptions in custom JDBC access code.
* @see org.springframework.jdbc.core.JdbcTemplate#getExceptionTranslator()
*/
protected final SQLExceptionTranslator getExceptionTranslator() {
return getJdbcTemplate().getExceptionTranslator();
}
/**
* Get a JDBC Connection, either from the current transaction or a new one.
* @return the JDBC Connection
* @throws CannotGetJdbcConnectionException if the attempt to get a Connection failed
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection(javax.sql.DataSource)
*/
protected final Connection getConnection() throws CannotGetJdbcConnectionException {
return DataSourceUtils.getConnection(getDataSource());
}
/**
* Close the given JDBC Connection, created via this DAO's DataSource,
* if it isn't bound to the thread.
* @param con Connection to close
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
*/
protected final void releaseConnection(Connection con) {
DataSourceUtils.releaseConnection(con, getDataSource());
}
}

View File

@ -0,0 +1,215 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core.support;
import java.io.InputStream;
import java.io.Reader;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import org.springframework.jdbc.core.DisposableSqlTypeValue;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
/**
* Object to represent an SQL BLOB/CLOB value parameter. BLOBs can either be an
* InputStream or a byte array. CLOBs can be in the form of a Reader, InputStream
* or String. Each CLOB/BLOB value will be stored together with its length.
* The type is based on which constructor is used. Objects of this class are
* immutable except for the LobCreator reference. Use them and discard them.
*
* <p>This class holds a reference to a LocCreator that must be closed after the
* update has completed. This is done via a call to the closeLobCreator method.
* All handling of the LobCreator is done by the framework classes that use it -
* no need to set or close the LobCreator for end users of this class.
*
* <p>A usage example:
*
* <pre class="code">JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); // reusable object
* LobHandler lobHandler = new DefaultLobHandler(); // reusable object
*
* jdbcTemplate.update(
* "INSERT INTO imagedb (image_name, content, description) VALUES (?, ?, ?)",
* new Object[] {
* name,
* new SqlLobValue(contentStream, contentLength, lobHandler),
* new SqlLobValue(description, lobHandler)
* },
* new int[] {Types.VARCHAR, Types.BLOB, Types.CLOB});
* </pre>
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 1.1
* @see org.springframework.jdbc.support.lob.LobHandler
* @see org.springframework.jdbc.support.lob.LobCreator
* @see org.springframework.jdbc.core.JdbcTemplate#update(String, Object[], int[])
* @see org.springframework.jdbc.object.SqlUpdate#update(Object[])
* @see org.springframework.jdbc.object.StoredProcedure#execute(java.util.Map)
*/
public class SqlLobValue implements DisposableSqlTypeValue {
private final Object content;
private final int length;
/**
* This contains a reference to the LobCreator - so we can close it
* once the update is done.
*/
private final LobCreator lobCreator;
/**
* Create a new BLOB value with the given byte array,
* using a DefaultLobHandler.
* @param bytes the byte array containing the BLOB value
* @see org.springframework.jdbc.support.lob.DefaultLobHandler
*/
public SqlLobValue(byte[] bytes) {
this(bytes, new DefaultLobHandler());
}
/**
* Create a new BLOB value with the given byte array.
* @param bytes the byte array containing the BLOB value
* @param lobHandler the LobHandler to be used
*/
public SqlLobValue(byte[] bytes, LobHandler lobHandler) {
this.content = bytes;
this.length = (bytes != null ? bytes.length : 0);
this.lobCreator = lobHandler.getLobCreator();
}
/**
* Create a new CLOB value with the given content string,
* using a DefaultLobHandler.
* @param content the String containing the CLOB value
* @see org.springframework.jdbc.support.lob.DefaultLobHandler
*/
public SqlLobValue(String content) {
this(content, new DefaultLobHandler());
}
/**
* Create a new CLOB value with the given content string.
* @param content the String containing the CLOB value
* @param lobHandler the LobHandler to be used
*/
public SqlLobValue(String content, LobHandler lobHandler) {
this.content = content;
this.length = (content != null ? content.length() : 0);
this.lobCreator = lobHandler.getLobCreator();
}
/**
* Create a new BLOB/CLOB value with the given stream,
* using a DefaultLobHandler.
* @param stream the stream containing the LOB value
* @param length the length of the LOB value
* @see org.springframework.jdbc.support.lob.DefaultLobHandler
*/
public SqlLobValue(InputStream stream, int length) {
this(stream, length, new DefaultLobHandler());
}
/**
* Create a new BLOB/CLOB value with the given stream.
* @param stream the stream containing the LOB value
* @param length the length of the LOB value
* @param lobHandler the LobHandler to be used
*/
public SqlLobValue(InputStream stream, int length, LobHandler lobHandler) {
this.content = stream;
this.length = length;
this.lobCreator = lobHandler.getLobCreator();
}
/**
* Create a new CLOB value with the given character stream,
* using a DefaultLobHandler.
* @param reader the character stream containing the CLOB value
* @param length the length of the CLOB value
* @see org.springframework.jdbc.support.lob.DefaultLobHandler
*/
public SqlLobValue(Reader reader, int length) {
this(reader, length, new DefaultLobHandler());
}
/**
* Create a new CLOB value with the given character stream.
* @param reader the character stream containing the CLOB value
* @param length the length of the CLOB value
* @param lobHandler the LobHandler to be used
*/
public SqlLobValue(Reader reader, int length, LobHandler lobHandler) {
this.content = reader;
this.length = length;
this.lobCreator = lobHandler.getLobCreator();
}
/**
* Set the specified content via the LobCreator.
*/
public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName)
throws SQLException {
if (sqlType == Types.BLOB) {
if (this.content instanceof byte[] || this.content == null) {
this.lobCreator.setBlobAsBytes(ps, paramIndex, (byte[]) this.content);
}
else if (this.content instanceof String) {
this.lobCreator.setBlobAsBytes(ps, paramIndex, ((String) this.content).getBytes());
}
else if (this.content instanceof InputStream) {
this.lobCreator.setBlobAsBinaryStream(ps, paramIndex, (InputStream) this.content, this.length);
}
else {
throw new IllegalArgumentException(
"Content type [" + this.content.getClass().getName() + "] not supported for BLOB columns");
}
}
else if (sqlType == Types.CLOB) {
if (this.content instanceof String || this.content == null) {
this.lobCreator.setClobAsString(ps, paramIndex, (String) this.content);
}
else if (this.content instanceof InputStream) {
this.lobCreator.setClobAsAsciiStream(ps, paramIndex, (InputStream) this.content, this.length);
}
else if (this.content instanceof Reader) {
this.lobCreator.setClobAsCharacterStream(ps, paramIndex, (Reader) this.content, this.length);
}
else {
throw new IllegalArgumentException(
"Content type [" + this.content.getClass().getName() + "] not supported for CLOB columns");
}
}
else {
throw new IllegalArgumentException("SqlLobValue only supports SQL types BLOB and CLOB");
}
}
/**
* Close the LobCreator, if any.
*/
public void cleanup() {
this.lobCreator.close();
}
}

Some files were not shown because too many files have changed in this diff Show More