org.springframework.jdbc.core.simple.AbstractJdbcInsert Maven / Gradle / Ivy
/*
* 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;
/**
* 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 declaredColumns = new ArrayList();
/**
* 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 columnNames) {
checkIfConfigurationModificationIsAllowed();
declaredColumns.clear();
declaredColumns.addAll(columnNames);
}
/**
* Get the names of the columns used
*/
public List 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.
* Automatically called by validateParameters
.
*/
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 args) {
checkCompiled();
List