Allow parameter name binding for SimpleJdbcCall

Update SimpleJdbcCall to offer a way to use named parameters binding
instead of the simple `?` binding it offers thus far.

Issue: SPR-12801
This commit is contained in:
Stephane Nicoll 2015-05-13 08:56:32 +02:00
parent 477d4c5126
commit 8dec8823fb
5 changed files with 99 additions and 10 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 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.
@ -46,6 +46,7 @@ import org.springframework.util.StringUtils;
* *
* @author Thomas Risberg * @author Thomas Risberg
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Kiril Nugmanov
* @since 2.5 * @since 2.5
*/ */
public class CallMetaDataContext { public class CallMetaDataContext {
@ -83,6 +84,9 @@ public class CallMetaDataContext {
/** Should we access call parameter meta data info or not */ /** Should we access call parameter meta data info or not */
private boolean accessCallParameterMetaData = true; private boolean accessCallParameterMetaData = true;
/** Should we bind parameter by name **/
private boolean namedBinding;
/** The provider of call meta data */ /** The provider of call meta data */
private CallMetaDataProvider metaDataProvider; private CallMetaDataProvider metaDataProvider;
@ -213,6 +217,19 @@ public class CallMetaDataContext {
return this.accessCallParameterMetaData; return this.accessCallParameterMetaData;
} }
/**
* Specify whether parameters should be bound by name.
*/
public void setNamedBinding(boolean namedBinding) {
this.namedBinding = namedBinding;
}
/**
* Check whether parameters should be bound by name.
*/
public boolean isNamedBinding() {
return namedBinding;
}
/** /**
* Create a ReturnResultSetParameter/SqlOutParameter depending on the support provided * Create a ReturnResultSetParameter/SqlOutParameter depending on the support provided
@ -595,7 +612,7 @@ public class CallMetaDataContext {
callString += ", "; callString += ", ";
} }
if (parameterCount >= 0) { if (parameterCount >= 0) {
callString += "?"; callString += createParameterBinding(parameter);
} }
parameterCount++; parameterCount++;
} }
@ -605,4 +622,17 @@ public class CallMetaDataContext {
return callString; return callString;
} }
/**
* Build the parameter binding fragment.
* @param parameter call parameter
* @return parameter binding fragment
*/
protected String createParameterBinding(SqlParameter parameter) {
if (isNamedBinding()) {
return parameter.getName() + " => ?";
} else {
return "?";
}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 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.
@ -198,6 +198,21 @@ public abstract class AbstractJdbcCall {
this.callMetaDataContext.setAccessCallParameterMetaData(accessCallParameterMetaData); this.callMetaDataContext.setAccessCallParameterMetaData(accessCallParameterMetaData);
} }
/**
* Does parameters should be bound by name?
*/
public boolean isNamedBinding() {
return this.callMetaDataContext.isNamedBinding();
}
/**
* Specify whether parameters should be bound by name.
* The default is {@code false}.
*/
public void setNamedBinding(boolean namedBinding) {
this.callMetaDataContext.setNamedBinding(namedBinding);
}
/** /**
* Get the call string that should be used based on parameters and meta data. * Get the call string that should be used based on parameters and meta data.
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 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.
@ -53,6 +53,7 @@ import org.springframework.jdbc.core.namedparam.SqlParameterSource;
* to provide the ability to chain multiple ones together in a "fluent" interface style. * to provide the ability to chain multiple ones together in a "fluent" interface style.
* *
* @author Thomas Risberg * @author Thomas Risberg
* @author Stephane Nicoll
* @since 2.5 * @since 2.5
* @see java.sql.DatabaseMetaData * @see java.sql.DatabaseMetaData
* @see org.springframework.jdbc.core.JdbcTemplate * @see org.springframework.jdbc.core.JdbcTemplate
@ -139,6 +140,12 @@ public class SimpleJdbcCall extends AbstractJdbcCall implements SimpleJdbcCallOp
return this; return this;
} }
@Override
public SimpleJdbcCall withNamedBinding() {
setNamedBinding(true);
return this;
}
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T executeFunction(Class<T> returnType, Object... args) { public <T> T executeFunction(Class<T> returnType, Object... args) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 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.
@ -28,6 +28,7 @@ import org.springframework.jdbc.core.namedparam.SqlParameterSource;
* as it can easily be mocked or stubbed. * as it can easily be mocked or stubbed.
* *
* @author Thomas Risberg * @author Thomas Risberg
* @author Stephane Nicoll
* @since 2.5 * @since 2.5
*/ */
public interface SimpleJdbcCallOperations { public interface SimpleJdbcCallOperations {
@ -100,6 +101,12 @@ public interface SimpleJdbcCallOperations {
*/ */
SimpleJdbcCallOperations withoutProcedureColumnMetaDataAccess(); SimpleJdbcCallOperations withoutProcedureColumnMetaDataAccess();
/**
* Indicates that parameters should be bound by name.
* @return the instance of this SimpleJdbcCall
*/
SimpleJdbcCallOperations withNamedBinding();
/** /**
* Execute the stored function and return the results obtained as an Object of the * Execute the stored function and return the results obtained as an Object of the

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2015 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.
@ -24,6 +24,7 @@ import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -41,9 +42,10 @@ import static org.mockito.BDDMockito.*;
import static org.springframework.tests.Matchers.*; import static org.springframework.tests.Matchers.*;
/** /**
* Mock object based tests for SimpleJdbcCall. * Tests for {@link SimpleJdbcCall}.
* *
* @author Thomas Risberg * @author Thomas Risberg
* @author Kiril Nugmanov
*/ */
public class SimpleJdbcCallTests { public class SimpleJdbcCallTests {
@ -193,7 +195,8 @@ public class SimpleJdbcCallTests {
} }
@Test public void testAddInvoiceFuncWithMetaDataUsingArrayParams() throws Exception { @Test
public void testAddInvoiceFuncWithMetaDataUsingArrayParams() throws Exception {
initializeAddInvoiceWithMetaData(true); initializeAddInvoiceWithMetaData(true);
SimpleJdbcCall adder = new SimpleJdbcCall(dataSource).withFunctionName("add_invoice"); SimpleJdbcCall adder = new SimpleJdbcCall(dataSource).withFunctionName("add_invoice");
Number newId = adder.executeFunction(Number.class, 1103, 3); Number newId = adder.executeFunction(Number.class, 1103, 3);
@ -203,6 +206,34 @@ public class SimpleJdbcCallTests {
} }
@Test
public void testCorrectFunctionStatement() throws Exception {
initializeAddInvoiceWithMetaData(true);
SimpleJdbcCall adder = new SimpleJdbcCall(dataSource).withFunctionName("add_invoice");
adder.compile();
verifyStatement(adder, "{? = call ADD_INVOICE(?, ?)}");
}
@Test
public void testCorrectFunctionStatementNamed() throws Exception {
initializeAddInvoiceWithMetaData(true);
SimpleJdbcCall adder = new SimpleJdbcCall(dataSource).withNamedBinding().withFunctionName("add_invoice");
adder.compile();
verifyStatement(adder, "{? = call ADD_INVOICE(AMOUNT => ?, CUSTID => ?)}");
}
@Test
public void testCorrectProcedureStatementNamed() throws Exception {
initializeAddInvoiceWithMetaData(false);
SimpleJdbcCall adder = new SimpleJdbcCall(dataSource).withNamedBinding().withProcedureName("add_invoice");
adder.compile();
verifyStatement(adder, "{call ADD_INVOICE(AMOUNT => ?, CUSTID => ?, NEWID => ?)}");
}
private void verifyStatement(SimpleJdbcCall adder, String expected) {
Assert.assertEquals("Incorrect call statement", expected, adder.getCallString());
}
private void initializeAddInvoiceWithoutMetaData(boolean isFunction) private void initializeAddInvoiceWithoutMetaData(boolean isFunction)
throws SQLException { throws SQLException {
given(databaseMetaData.getDatabaseProductName()).willReturn("MyDB"); given(databaseMetaData.getDatabaseProductName()).willReturn("MyDB");
@ -281,6 +312,5 @@ public class SimpleJdbcCallTests {
verify(callableStatement).close(); verify(callableStatement).close();
verify(proceduresResultSet).close(); verify(proceduresResultSet).close();
verify(procedureColumnsResultSet).close(); verify(procedureColumnsResultSet).close();
}
}
} }