diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java index 0da8dbf57..2c6c0f931 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-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. @@ -18,6 +18,7 @@ package org.springframework.data.jpa.repository.query; import javax.persistence.StoredProcedureQuery; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Stored procedure configuration for JPA 2.1 {@link StoredProcedureQuery}s. @@ -28,6 +29,9 @@ import org.springframework.util.Assert; */ class StoredProcedureAttributes { + // A syntheic output parameter name to be used in case of derived stored procedures and named parameters + static final String SYNTHETIC_OUTPUT_PARAMETER_NAME = "out"; + private final boolean namedStoredProcedure; private final String procedureName; private final String outputParameterName; @@ -48,7 +52,8 @@ class StoredProcedureAttributes { Assert.notNull(outputParameterType, "OutputParameterType must not be null!"); this.procedureName = procedureName; - this.outputParameterName = outputParameterName; + this.outputParameterName = !namedStoredProcedure && !StringUtils.hasText(outputParameterName) ? SYNTHETIC_OUTPUT_PARAMETER_NAME + : outputParameterName; this.outputParameterType = outputParameterType; this.namedStoredProcedure = namedStoredProcedure; } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index a3402424c..b6e610781 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -22,6 +22,8 @@ import javax.persistence.StoredProcedureQuery; import javax.persistence.TypedQuery; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; +import org.springframework.data.repository.query.Parameter; +import org.springframework.data.repository.query.QueryMethod; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -36,6 +38,7 @@ import org.springframework.util.StringUtils; class StoredProcedureJpaQuery extends AbstractJpaQuery { private final StoredProcedureAttributes procedureAttributes; + private final boolean useNamedParameters; /** * Creates a new {@link StoredProcedureJpaQuery}. @@ -47,6 +50,25 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { super(method, em); this.procedureAttributes = method.getProcedureAttributes(); + this.useNamedParameters = useNamedParameters(method); + + } + + /** + * Determine whether to used named parameters for the given query method. + * + * @param method must not be {@literal null}. + * @return + */ + private static boolean useNamedParameters(QueryMethod method) { + + for (Parameter parameter : method.getParameters()) { + if (parameter.isNamedParameter()) { + return true; + } + } + + return false; } /* @@ -64,10 +86,7 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { */ @Override protected StoredProcedureQuery doCreateQuery(Object[] values) { - - StoredProcedureQuery proc = createStoredProcedure(); - - return createBinder(values).bind(proc); + return createBinder(values).bind(createStoredProcedure()); } /* @@ -93,11 +112,12 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { return null; } - if (StringUtils.hasText(procedureAttributes.getOutputParameterName())) { - return storedProcedureQuery.getOutputParameterValue(procedureAttributes.getOutputParameterName()); - } + String outputParameterName = procedureAttributes.getOutputParameterName(); + JpaParameters parameters = getQueryMethod().getParameters(); - return storedProcedureQuery.getOutputParameterValue(getQueryMethod().getParameters().getNumberOfParameters() + 1); + return useNamedParameters && StringUtils.hasText(outputParameterName) ? // + storedProcedureQuery.getOutputParameterValue(outputParameterName) + : storedProcedureQuery.getOutputParameterValue(parameters.getNumberOfParameters() + 1); } /** @@ -126,17 +146,18 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { */ private StoredProcedureQuery newAdhocStoredProcedureQuery() { - StoredProcedureQuery procedureQuery = getEntityManager().createStoredProcedureQuery( - procedureAttributes.getProcedureName()); - JpaParameters params = getQueryMethod().getParameters(); + String procedureName = procedureAttributes.getProcedureName(); + + StoredProcedureQuery procedureQuery = getEntityManager().createStoredProcedureQuery(procedureName); + for (JpaParameter param : params) { if (!param.isBindable()) { continue; } - if (param.isNamedParameter()) { + if (useNamedParameters) { procedureQuery.registerStoredProcedureParameter(param.getName(), param.getType(), ParameterMode.IN); } else { procedureQuery.registerStoredProcedureParameter(param.getIndex() + 1, param.getType(), ParameterMode.IN); @@ -144,8 +165,18 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { } if (procedureAttributes.hasReturnValue()) { - procedureQuery.registerStoredProcedureParameter(params.getNumberOfParameters() + 1, - procedureAttributes.getOutputParameterType(), ParameterMode.OUT); + + Class outputParameterType = procedureAttributes.getOutputParameterType(); + ParameterMode mode = ParameterMode.OUT; + + if (useNamedParameters) { + + String outputParameterName = procedureAttributes.getOutputParameterName(); + procedureQuery.registerStoredProcedureParameter(outputParameterName, outputParameterType, mode); + + } else { + procedureQuery.registerStoredProcedureParameter(params.getNumberOfParameters() + 1, outputParameterType, mode); + } } return procedureQuery; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java index b4cced5ac..27f3cf659 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java @@ -66,7 +66,7 @@ public class StoredProcedureAttributeSourceUnitTests { assertThat(attr.getProcedureName(), is("plus1inout")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); - assertThat(attr.getOutputParameterName(), is(nullValue())); + assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME)); } /** @@ -80,7 +80,7 @@ public class StoredProcedureAttributeSourceUnitTests { assertThat(attr.getProcedureName(), is("plus1inout")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); - assertThat(attr.getOutputParameterName(), is(nullValue())); + assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME)); } /** @@ -94,7 +94,7 @@ public class StoredProcedureAttributeSourceUnitTests { assertThat(attr.getProcedureName(), is("plus1inout")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); - assertThat(attr.getOutputParameterName(), is(nullValue())); + assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME)); } /** @@ -108,7 +108,7 @@ public class StoredProcedureAttributeSourceUnitTests { assertThat(attr.getProcedureName(), is("plus1inout")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); - assertThat(attr.getOutputParameterName(), is(nullValue())); + assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME)); } /** diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java new file mode 100644 index 000000000..ddaff6454 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java @@ -0,0 +1,40 @@ +/* + * Copyright 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. + * 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.data.jpa.repository.query; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.jpa.repository.query.StoredProcedureAttributes.*; + +import org.junit.Test; + +/** + * Unit tests for {@link StoredProcedureAttributes}. + * + * @author Oliver Gierke + */ +public class StoredProcedureAttributesUnitTests { + + /** + * @see DATAJPA-681 + */ + @Test + public void usesSyntheticOutputParameterNameForAdhocProcedureWithoutOutputName() { + + StoredProcedureAttributes attributes = new StoredProcedureAttributes("procedure", null, Long.class, false); + assertThat(attributes.getOutputParameterName(), is(SYNTHETIC_OUTPUT_PARAMETER_NAME)); + } +}