From 8dec8823fb51e300a8afbe96dc9c2e3e411bb8c8 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 13 May 2015 08:56:32 +0200 Subject: [PATCH] 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 --- .../core/metadata/CallMetaDataContext.java | 34 +++++++++++++++- .../jdbc/core/simple/AbstractJdbcCall.java | 17 +++++++- .../jdbc/core/simple/SimpleJdbcCall.java | 9 ++++- .../core/simple/SimpleJdbcCallOperations.java | 9 ++++- .../jdbc/core/simple/SimpleJdbcCallTests.java | 40 ++++++++++++++++--- 5 files changed, 99 insertions(+), 10 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/CallMetaDataContext.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/CallMetaDataContext.java index 70daa10b55..8265d493a4 100755 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/CallMetaDataContext.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/CallMetaDataContext.java @@ -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"); * 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 Juergen Hoeller + * @author Kiril Nugmanov * @since 2.5 */ public class CallMetaDataContext { @@ -83,6 +84,9 @@ public class CallMetaDataContext { /** Should we access call parameter meta data info or not */ private boolean accessCallParameterMetaData = true; + /** Should we bind parameter by name **/ + private boolean namedBinding; + /** The provider of call meta data */ private CallMetaDataProvider metaDataProvider; @@ -213,6 +217,19 @@ public class CallMetaDataContext { 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 @@ -595,7 +612,7 @@ public class CallMetaDataContext { callString += ", "; } if (parameterCount >= 0) { - callString += "?"; + callString += createParameterBinding(parameter); } parameterCount++; } @@ -605,4 +622,17 @@ public class CallMetaDataContext { 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 "?"; + } + } + } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java index 56cf20842a..1b5f9697e2 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java @@ -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"); * you may not use this file except in compliance with the License. @@ -198,6 +198,21 @@ public abstract class AbstractJdbcCall { 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. */ diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcCall.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcCall.java index df58d5d016..cad54c599c 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcCall.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcCall.java @@ -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"); * 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. * * @author Thomas Risberg + * @author Stephane Nicoll * @since 2.5 * @see java.sql.DatabaseMetaData * @see org.springframework.jdbc.core.JdbcTemplate @@ -139,6 +140,12 @@ public class SimpleJdbcCall extends AbstractJdbcCall implements SimpleJdbcCallOp return this; } + @Override + public SimpleJdbcCall withNamedBinding() { + setNamedBinding(true); + return this; + } + @Override @SuppressWarnings("unchecked") public T executeFunction(Class returnType, Object... args) { diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcCallOperations.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcCallOperations.java index fcdca17f29..6c660df2a7 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcCallOperations.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcCallOperations.java @@ -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"); * 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. * * @author Thomas Risberg + * @author Stephane Nicoll * @since 2.5 */ public interface SimpleJdbcCallOperations { @@ -100,6 +101,12 @@ public interface SimpleJdbcCallOperations { */ 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 diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java index aebf1994be..f801d13c06 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java @@ -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"); * 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 javax.sql.DataSource; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -41,9 +42,10 @@ import static org.mockito.BDDMockito.*; import static org.springframework.tests.Matchers.*; /** - * Mock object based tests for SimpleJdbcCall. + * Tests for {@link SimpleJdbcCall}. * * @author Thomas Risberg + * @author Kiril Nugmanov */ public class SimpleJdbcCallTests { @@ -193,7 +195,8 @@ public class SimpleJdbcCallTests { } - @Test public void testAddInvoiceFuncWithMetaDataUsingArrayParams() throws Exception { + @Test + public void testAddInvoiceFuncWithMetaDataUsingArrayParams() throws Exception { initializeAddInvoiceWithMetaData(true); SimpleJdbcCall adder = new SimpleJdbcCall(dataSource).withFunctionName("add_invoice"); 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) throws SQLException { given(databaseMetaData.getDatabaseProductName()).willReturn("MyDB"); @@ -281,6 +312,5 @@ public class SimpleJdbcCallTests { verify(callableStatement).close(); verify(proceduresResultSet).close(); verify(procedureColumnsResultSet).close(); - -} + } }