Polished *JdbcCall/Insert classes

This commit is contained in:
Juergen Hoeller 2013-02-15 18:23:10 +01:00
parent 06c6cbb6b9
commit acffcdaa01
4 changed files with 67 additions and 83 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -97,7 +97,7 @@ public abstract class AbstractJdbcCall {
/** /**
* Get the configured {@link JdbcTemplate} * Get the configured {@link JdbcTemplate}.
*/ */
public JdbcTemplate getJdbcTemplate() { public JdbcTemplate getJdbcTemplate() {
return this.jdbcTemplate; return this.jdbcTemplate;
@ -110,7 +110,6 @@ public abstract class AbstractJdbcCall {
return this.callableStatementFactory; return this.callableStatementFactory;
} }
/** /**
* Set the name of the stored procedure. * Set the name of the stored procedure.
*/ */
@ -294,7 +293,7 @@ public abstract class AbstractJdbcCall {
this.callMetaDataContext.createReturnResultSetParameter(entry.getKey(), entry.getValue()); this.callMetaDataContext.createReturnResultSetParameter(entry.getKey(), entry.getValue());
this.declaredParameters.add(resultSetParameter); this.declaredParameters.add(resultSetParameter);
} }
callMetaDataContext.processParameters(this.declaredParameters); this.callMetaDataContext.processParameters(this.declaredParameters);
this.callString = this.callMetaDataContext.createCallString(); this.callString = this.callMetaDataContext.createCallString();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -74,9 +74,9 @@ public abstract class AbstractJdbcInsert {
private final List<String> declaredColumns = new ArrayList<String>(); private final List<String> declaredColumns = new ArrayList<String>();
/** /**
* Has this operation been compiled? Compilation means at * Has this operation been compiled? Compilation means at least checking
* least checking that a DataSource or JdbcTemplate has been provided, * that a DataSource or JdbcTemplate has been provided, but subclasses
* but subclasses may also implement their own custom validation. * may also implement their own custom validation.
*/ */
private boolean compiled = false; private boolean compiled = false;
@ -108,9 +108,16 @@ public abstract class AbstractJdbcInsert {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Methods dealing with configuaration properties // Methods dealing with configuration properties
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
/**
* Get the {@link JdbcTemplate} that is configured to be used.
*/
public JdbcTemplate getJdbcTemplate() {
return this.jdbcTemplate;
}
/** /**
* Set the name of the table for this insert * Set the name of the table for this insert
*/ */
@ -230,41 +237,31 @@ public abstract class AbstractJdbcInsert {
return this.insertTypes; return this.insertTypes;
} }
/**
* Get the {@link JdbcTemplate} that is configured to be used
*/
protected JdbcTemplate getJdbcTemplate() {
return this.jdbcTemplate;
}
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Methods handling compilation issues // Methods handling compilation issues
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
/** /**
* Compile this JdbcInsert using provided parameters and meta data plus other settings. This * Compile this JdbcInsert using provided parameters and meta data plus other settings.
* finalizes the configuration for this object and subsequent attempts to compile are ignored. * This finalizes the configuration for this object and subsequent attempts to compile are
* This will be implicitly called the first time an un-compiled insert is executed. * 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 * @throws InvalidDataAccessApiUsageException if the object hasn't been correctly initialized,
* been correctly initialized, for example if no DataSource has been provided * for example if no DataSource has been provided
*/ */
public synchronized final void compile() throws InvalidDataAccessApiUsageException { public synchronized final void compile() throws InvalidDataAccessApiUsageException {
if (!isCompiled()) { if (!isCompiled()) {
if (getTableName() == null) { if (getTableName() == null) {
throw new InvalidDataAccessApiUsageException("Table name is required"); throw new InvalidDataAccessApiUsageException("Table name is required");
} }
try { try {
this.jdbcTemplate.afterPropertiesSet(); this.jdbcTemplate.afterPropertiesSet();
} }
catch (IllegalArgumentException ex) { catch (IllegalArgumentException ex) {
throw new InvalidDataAccessApiUsageException(ex.getMessage()); throw new InvalidDataAccessApiUsageException(ex.getMessage());
} }
compileInternal(); compileInternal();
this.compiled = true; this.compiled = true;
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("JdbcInsert for table [" + getTableName() + "] compiled"); logger.debug("JdbcInsert for table [" + getTableName() + "] compiled");
} }
@ -272,21 +269,17 @@ public abstract class AbstractJdbcInsert {
} }
/** /**
* Method to perform the actual compilation. Subclasses can override this template method to perform * Method to perform the actual compilation. Subclasses can override this template method
* their own compilation. Invoked after this base class's compilation is complete. * to perform their own compilation. Invoked after this base class's compilation is complete.
*/ */
protected void compileInternal() { protected void compileInternal() {
this.tableMetaDataContext.processMetaData(
tableMetaDataContext.processMetaData(getJdbcTemplate().getDataSource(), getColumnNames(), getGeneratedKeyNames()); getJdbcTemplate().getDataSource(), getColumnNames(), getGeneratedKeyNames());
this.insertString = this.tableMetaDataContext.createInsertString(getGeneratedKeyNames());
insertString = tableMetaDataContext.createInsertString(getGeneratedKeyNames()); this.insertTypes = this.tableMetaDataContext.createInsertTypes();
insertTypes = tableMetaDataContext.createInsertTypes();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Compiled JdbcInsert. Insert string is [" + getInsertString() + "]"); logger.debug("Compiled insert object: insert string is [" + getInsertString() + "]");
} }
onCompileInternal(); onCompileInternal();
} }
@ -318,12 +311,13 @@ public abstract class AbstractJdbcInsert {
} }
/** /**
* Method to check whether we are allowd to make any configuration changes at this time. If the class has been * Method to check whether we are allowd to make any configuration changes at this time.
* compiled, then no further changes to the configuration are allowed. * If the class has been compiled, then no further changes to the configuration are allowed.
*/ */
protected void checkIfConfigurationModificationIsAllowed() { protected void checkIfConfigurationModificationIsAllowed() {
if (isCompiled()) { if (isCompiled()) {
throw new InvalidDataAccessApiUsageException("Configuration can't be altered once the class has been compiled or used."); throw new InvalidDataAccessApiUsageException(
"Configuration can't be altered once the class has been compiled or used");
} }
} }
@ -334,7 +328,6 @@ public abstract class AbstractJdbcInsert {
/** /**
* Method that provides execution of the insert using the passed in Map of parameters * 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 * @param args Map with parameter names and values to be used in insert
* @return number of rows affected * @return number of rows affected
*/ */
@ -346,7 +339,6 @@ public abstract class AbstractJdbcInsert {
/** /**
* Method that provides execution of the insert using the passed in {@link SqlParameterSource} * Method that provides execution of the insert using the passed in {@link SqlParameterSource}
*
* @param parameterSource parameter names and values to be used in insert * @param parameterSource parameter names and values to be used in insert
* @return number of rows affected * @return number of rows affected
*/ */
@ -357,14 +349,13 @@ public abstract class AbstractJdbcInsert {
} }
/** /**
* Method to execute the insert * Method to execute the insert.
*/ */
private int executeInsertInternal(List<Object> values) { private int executeInsertInternal(List<Object> values) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("The following parameters are used for insert " + getInsertString() + " with: " + values); logger.debug("The following parameters are used for insert " + getInsertString() + " with: " + values);
} }
int updateCount = jdbcTemplate.update(getInsertString(), values.toArray(), getInsertTypes()); return getJdbcTemplate().update(getInsertString(), values.toArray(), getInsertTypes());
return updateCount;
} }
/** /**
@ -428,8 +419,8 @@ public abstract class AbstractJdbcInsert {
return kh.getKey(); return kh.getKey();
} }
else { else {
throw new DataIntegrityViolationException("Unable to retrieve the generated key for the insert: " + throw new DataIntegrityViolationException(
getInsertString()); "Unable to retrieve the generated key for the insert: " + getInsertString());
} }
} }
@ -442,7 +433,7 @@ public abstract class AbstractJdbcInsert {
} }
final KeyHolder keyHolder = new GeneratedKeyHolder(); final KeyHolder keyHolder = new GeneratedKeyHolder();
if (this.tableMetaDataContext.isGetGeneratedKeysSupported()) { if (this.tableMetaDataContext.isGetGeneratedKeysSupported()) {
jdbcTemplate.update( getJdbcTemplate().update(
new PreparedStatementCreator() { new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException { public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = prepareStatementForGeneratedKeys(con); PreparedStatement ps = prepareStatementForGeneratedKeys(con);
@ -469,20 +460,18 @@ public abstract class AbstractJdbcInsert {
// This is a hack to be able to get the generated key from a database that doesn't support // 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 // 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. // clause while HSQL uses a second query that has to be executed with the same connection.
final String keyQuery = tableMetaDataContext.getSimulationQueryForGetGeneratedKey( final String keyQuery = this.tableMetaDataContext.getSimulationQueryForGetGeneratedKey(
tableMetaDataContext.getTableName(), this.tableMetaDataContext.getTableName(), getGeneratedKeyNames()[0]);
getGeneratedKeyNames()[0]);
Assert.notNull(keyQuery, "Query for simulating get generated keys can't be null"); Assert.notNull(keyQuery, "Query for simulating get generated keys can't be null");
if (keyQuery.toUpperCase().startsWith("RETURNING")) { if (keyQuery.toUpperCase().startsWith("RETURNING")) {
Long key = jdbcTemplate.queryForLong( Long key = getJdbcTemplate().queryForObject(getInsertString() + " " + keyQuery,
getInsertString() + " " + keyQuery, values.toArray(new Object[values.size()]), Long.class);
values.toArray(new Object[values.size()])); Map<String, Object> keys = new HashMap<String, Object>(1);
HashMap keys = new HashMap(1);
keys.put(getGeneratedKeyNames()[0], key); keys.put(getGeneratedKeyNames()[0], key);
keyHolder.getKeyList().add(keys); keyHolder.getKeyList().add(keys);
} }
else { else {
jdbcTemplate.execute(new ConnectionCallback() { getJdbcTemplate().execute(new ConnectionCallback<Object>() {
public Object doInConnection(Connection con) throws SQLException, DataAccessException { public Object doInConnection(Connection con) throws SQLException, DataAccessException {
// Do the insert // Do the insert
PreparedStatement ps = null; PreparedStatement ps = null;
@ -490,13 +479,14 @@ public abstract class AbstractJdbcInsert {
ps = con.prepareStatement(getInsertString()); ps = con.prepareStatement(getInsertString());
setParameterValues(ps, values, getInsertTypes()); setParameterValues(ps, values, getInsertTypes());
ps.executeUpdate(); ps.executeUpdate();
} finally { }
finally {
JdbcUtils.closeStatement(ps); JdbcUtils.closeStatement(ps);
} }
//Get the key //Get the key
Statement keyStmt = null; Statement keyStmt = null;
ResultSet rs = null; ResultSet rs = null;
HashMap keys = new HashMap(1); Map<String, Object> keys = new HashMap<String, Object>(1);
try { try {
keyStmt = con.createStatement(); keyStmt = con.createStatement();
rs = keyStmt.executeQuery(keyQuery); rs = keyStmt.executeQuery(keyQuery);
@ -505,7 +495,8 @@ public abstract class AbstractJdbcInsert {
keys.put(getGeneratedKeyNames()[0], key); keys.put(getGeneratedKeyNames()[0], key);
keyHolder.getKeyList().add(keys); keyHolder.getKeyList().add(keys);
} }
} finally { }
finally {
JdbcUtils.closeResultSet(rs); JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(keyStmt); JdbcUtils.closeStatement(keyStmt);
} }
@ -547,14 +538,14 @@ public abstract class AbstractJdbcInsert {
} }
/** /**
* Method that provides execution of a batch insert using the passed in Maps of parameters * 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 * @param batch array of Maps with parameter names and values to be used in batch insert
* @return array of number of rows affected * @return array of number of rows affected
*/ */
@SuppressWarnings("unchecked")
protected int[] doExecuteBatch(Map<String, Object>[] batch) { protected int[] doExecuteBatch(Map<String, Object>[] batch) {
checkCompiled(); checkCompiled();
List[] batchValues = new ArrayList[batch.length]; List<Object>[] batchValues = new ArrayList[batch.length];
int i = 0; int i = 0;
for (Map<String, Object> args : batch) { for (Map<String, Object> args : batch) {
List<Object> values = matchInParameterValuesWithInsertColumns(args); List<Object> values = matchInParameterValuesWithInsertColumns(args);
@ -565,13 +556,13 @@ public abstract class AbstractJdbcInsert {
/** /**
* Method that provides execution of a batch insert using the passed in array of {@link SqlParameterSource} * 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 * @param batch array of SqlParameterSource with parameter names and values to be used in insert
* @return array of number of rows affected * @return array of number of rows affected
*/ */
@SuppressWarnings("unchecked")
protected int[] doExecuteBatch(SqlParameterSource[] batch) { protected int[] doExecuteBatch(SqlParameterSource[] batch) {
checkCompiled(); checkCompiled();
List[] batchValues = new ArrayList[batch.length]; List<Object>[] batchValues = new ArrayList[batch.length];
int i = 0; int i = 0;
for (SqlParameterSource parameterSource : batch) { for (SqlParameterSource parameterSource : batch) {
List<Object> values = matchInParameterValuesWithInsertColumns(parameterSource); List<Object> values = matchInParameterValuesWithInsertColumns(parameterSource);
@ -581,27 +572,22 @@ public abstract class AbstractJdbcInsert {
} }
/** /**
* Method to execute the batch insert * Method to execute the batch insert.
*/ */
//TODO synchronize parameter setters with the SimpleJdbcTemplate
private int[] executeBatchInternal(final List<Object>[] batchValues) { private int[] executeBatchInternal(final List<Object>[] batchValues) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Executing statement " + getInsertString() + " with batch of size: " + batchValues.length); logger.debug("Executing statement " + getInsertString() + " with batch of size: " + batchValues.length);
} }
int[] updateCounts = jdbcTemplate.batchUpdate( return getJdbcTemplate().batchUpdate(getInsertString(),
getInsertString(),
new BatchPreparedStatementSetter() { new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException { public void setValues(PreparedStatement ps, int i) throws SQLException {
List<Object> values = batchValues[i]; List<Object> values = batchValues[i];
setParameterValues(ps, values, getInsertTypes()); setParameterValues(ps, values, getInsertTypes());
} }
public int getBatchSize() { public int getBatchSize() {
return batchValues.length; return batchValues.length;
} }
}); });
return updateCounts;
} }
/** /**
@ -611,6 +597,7 @@ public abstract class AbstractJdbcInsert {
*/ */
private void setParameterValues(PreparedStatement preparedStatement, List<Object> values, int[] columnTypes) private void setParameterValues(PreparedStatement preparedStatement, List<Object> values, int[] columnTypes)
throws SQLException { throws SQLException {
int colIndex = 0; int colIndex = 0;
for (Object value : values) { for (Object value : values) {
colIndex++; colIndex++;
@ -624,25 +611,23 @@ public abstract class AbstractJdbcInsert {
} }
/** /**
* Match the provided in parameter values with regitered parameters and parameters defined via metedata * Match the provided in parameter values with regitered parameters and parameters defined
* processing. * via metadata processing.
*
* @param parameterSource the parameter vakues provided as a {@link SqlParameterSource} * @param parameterSource the parameter vakues provided as a {@link SqlParameterSource}
* @return Map with parameter names and values * @return Map with parameter names and values
*/ */
protected List<Object> matchInParameterValuesWithInsertColumns(SqlParameterSource parameterSource) { protected List<Object> matchInParameterValuesWithInsertColumns(SqlParameterSource parameterSource) {
return tableMetaDataContext.matchInParameterValuesWithInsertColumns(parameterSource); return this.tableMetaDataContext.matchInParameterValuesWithInsertColumns(parameterSource);
} }
/** /**
* Match the provided in parameter values with regitered parameters and parameters defined via metedata * Match the provided in parameter values with regitered parameters and parameters defined
* processing. * via metadata processing.
*
* @param args the parameter values provided in a Map * @param args the parameter values provided in a Map
* @return Map with parameter names and values * @return Map with parameter names and values
*/ */
protected List<Object> matchInParameterValuesWithInsertColumns(Map<String, Object> args) { protected List<Object> matchInParameterValuesWithInsertColumns(Map<String, Object> args) {
return tableMetaDataContext.matchInParameterValuesWithInsertColumns(args); return this.tableMetaDataContext.matchInParameterValuesWithInsertColumns(args);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -35,7 +35,7 @@ import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
* <p>The meta data processing is based on the DatabaseMetaData provided by the * <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 * 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 * 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. * is not the case, then the column names must be specified explicitly.
* *
* <p>The actual insert is being handled using Spring's * <p>The actual insert is being handled using Spring's
* {@link org.springframework.jdbc.core.JdbcTemplate}. * {@link org.springframework.jdbc.core.JdbcTemplate}.