DATAJPA-707 - Implement Stored Procedures with multiple output parameters.

Original pull request: #297.
This commit is contained in:
Jeff Sheets
2018-10-18 00:47:12 -05:00
committed by Jens Schauder
parent 17fa5f454a
commit e41236f722
9 changed files with 255 additions and 66 deletions

View File

@@ -37,6 +37,7 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @author Mark Paluch
* @author Diego Diez
* @author Jeff Sheets
* @since 1.6
*/
enum StoredProcedureAttributeSource {
@@ -101,36 +102,28 @@ enum StoredProcedureAttributeSource {
private StoredProcedureAttributes newProcedureAttributesFrom(Method method,
NamedStoredProcedureQuery namedStoredProc, Procedure procedure) {
String outputParameterName = null;
Class<?> outputParameterType = null;
List<String> outputParameterNames = new ArrayList<>();
List<Class<?>> outputParameterTypes = new ArrayList<>();
if (!procedure.outputParameterName().isEmpty()) {
// we give the output parameter definition from the @Procedure annotation precedence
outputParameterName = procedure.outputParameterName();
outputParameterNames.add(procedure.outputParameterName());
} else {
// try to discover the output parameter
List<StoredProcedureParameter> outputParameters = extractOutputParametersFrom(namedStoredProc);
if (outputParameters.size() != 1 && !void.class.equals(method.getReturnType())) {
throw new IllegalStateException(String.format(
"Could not create ProcedureAttributes from %s. We currently support exactly one output parameter!", method));
}
if (!outputParameters.isEmpty()) {
StoredProcedureParameter outputParameter = outputParameters.get(0);
outputParameterName = outputParameter.name();
outputParameterType = outputParameter.type();
for (StoredProcedureParameter outputParameter : outputParameters) {
outputParameterNames.add(outputParameter.name());
outputParameterTypes.add(outputParameter.type());
}
}
if (outputParameterType == null || Object.class.equals(outputParameterType)
|| void.class.equals(outputParameterType)) {
outputParameterType = method.getReturnType();
if (outputParameterTypes.isEmpty()) {
outputParameterTypes.add(method.getReturnType());
}
return new StoredProcedureAttributes(namedStoredProc.name(), outputParameterName, outputParameterType, true);
return new StoredProcedureAttributes(namedStoredProc.name(), outputParameterNames, outputParameterTypes, true);
}
private List<StoredProcedureParameter> extractOutputParametersFrom(NamedStoredProcedureQuery namedStoredProc) {

View File

@@ -21,12 +21,18 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* Stored procedure configuration for JPA 2.1 {@link StoredProcedureQuery}s.
*
* @author Thomas Darimont
* @author Oliver Gierke
* @author Mark Paluch
* @author Jeff Sheets
* @since 1.6
*/
class StoredProcedureAttributes {
@@ -36,8 +42,8 @@ class StoredProcedureAttributes {
private final boolean namedStoredProcedure;
private final String procedureName;
private final String outputParameterName;
private final Class<?> outputParameterType;
private final List<String> outputParameterNames;
private final List<Class<?>> outputParameterTypes;
/**
* Creates a new {@link StoredProcedureAttributes}.
@@ -49,14 +55,31 @@ class StoredProcedureAttributes {
*/
public StoredProcedureAttributes(String procedureName, @Nullable String outputParameterName,
Class<?> outputParameterType, boolean namedStoredProcedure) {
this(procedureName, Arrays.asList(outputParameterName), Arrays.asList(outputParameterType), namedStoredProcedure);
}
/**
* Creates a new {@link StoredProcedureAttributes}.
*
* @param procedureName must not be {@literal null}
* @param outputParameterNames may be empty, but not null
* @param outputParameterTypes must not be empty, and cannot be a single element of null
* @param namedStoredProcedure
*/
public StoredProcedureAttributes(String procedureName, List<String> outputParameterNames,
List<Class<?>> outputParameterTypes, boolean namedStoredProcedure) {
Assert.notNull(procedureName, "ProcedureName must not be null!");
Assert.notNull(outputParameterType, "OutputParameterType must not be null!");
Assert.notNull(outputParameterNames, "OutputParameterNames must not be null!");
Assert.notEmpty(outputParameterTypes, "OutputParameterTypes must not be empty!");
Assert.isTrue(outputParameterTypes.size() != 1 || outputParameterTypes.get(0) != null, "OutputParameterTypes must not have size 1 with a null value");
this.procedureName = procedureName;
this.outputParameterName = !namedStoredProcedure && !StringUtils.hasText(outputParameterName) ? SYNTHETIC_OUTPUT_PARAMETER_NAME
: outputParameterName;
this.outputParameterType = outputParameterType;
this.outputParameterNames = namedStoredProcedure ? outputParameterNames : IntStream.range(0, outputParameterNames.size()).mapToObj(i -> {
String paramName = outputParameterNames.get(i);
return !StringUtils.hasText(paramName) ? SYNTHETIC_OUTPUT_PARAMETER_NAME + (i == 0 ? "" : i) : paramName;
}).collect(Collectors.toList());
this.outputParameterTypes = outputParameterTypes;
this.namedStoredProcedure = namedStoredProcedure;
}
@@ -70,21 +93,21 @@ class StoredProcedureAttributes {
}
/**
* Returns the name of the output parameter.
* Returns the names of the output parameters.
*
* @return
*/
public String getOutputParameterName() {
return outputParameterName;
public List<String> getOutputParameterNames() {
return outputParameterNames;
}
/**
* Returns the type of the output parameter.
* Returns the types of the output parameters.
*
* @return
*/
public Class<?> getOutputParameterType() {
return outputParameterType;
public List<Class<?>> getOutputParameterTypes() {
return outputParameterTypes;
}
/**
@@ -102,6 +125,6 @@ class StoredProcedureAttributes {
* @return
*/
public boolean hasReturnValue() {
return !(void.class.equals(outputParameterType) || Void.class.equals(outputParameterType));
return !(outputParameterTypes.size() == 1 && (void.class.equals(outputParameterTypes.get(0)) || Void.class.equals(outputParameterTypes.get(0))));
}
}

View File

@@ -28,6 +28,10 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* {@link AbstractJpaQuery} implementation that inspects a {@link JpaQueryMethod} for the existence of an
* {@link Procedure} annotation and creates a JPA 2.1 {@link StoredProcedureQuery} from it.
@@ -37,6 +41,7 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @author Jens Schauder
* @author Mark Paluch
* @author Jeff Sheets
* @since 1.6
*/
class StoredProcedureJpaQuery extends AbstractJpaQuery {
@@ -105,6 +110,8 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery {
* Extracts the output value from the given {@link StoredProcedureQuery}.
*
* @param storedProcedureQuery must not be {@literal null}.
*
* Result is either a single value, or a Map<String, Object> of output parameter names to values
*/
@Nullable
Object extractOutputValue(StoredProcedureQuery storedProcedureQuery) {
@@ -115,12 +122,17 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery {
return null;
}
String outputParameterName = procedureAttributes.getOutputParameterName();
JpaParameters parameters = getQueryMethod().getParameters();
Map<String, Object> outputValues = IntStream.range(0, procedureAttributes.getOutputParameterNames().size())
.boxed().collect(Collectors.toMap(procedureAttributes.getOutputParameterNames()::get, i -> {
String outputParameterName = procedureAttributes.getOutputParameterNames().get(i);
JpaParameters parameters = getQueryMethod().getParameters();
return useNamedParameters && StringUtils.hasText(outputParameterName) ? //
storedProcedureQuery.getOutputParameterValue(outputParameterName)
: storedProcedureQuery.getOutputParameterValue(parameters.getNumberOfParameters() + 1);
return useNamedParameters && StringUtils.hasText(outputParameterName) ? //
storedProcedureQuery.getOutputParameterValue(outputParameterName)
: storedProcedureQuery.getOutputParameterValue(parameters.getNumberOfParameters() + i + 1);
}));
return outputValues.size() == 1 ? outputValues.values().iterator().next() : outputValues;
}
/**
@@ -165,17 +177,20 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery {
if (procedureAttributes.hasReturnValue()) {
Class<?> outputParameterType = procedureAttributes.getOutputParameterType();
ParameterMode mode = ParameterMode.OUT;
if (useNamedParameters) {
IntStream.range(0, procedureAttributes.getOutputParameterTypes().size()).forEach(i -> {
Class<?> outputParameterType = procedureAttributes.getOutputParameterTypes().get(i);
String outputParameterName = procedureAttributes.getOutputParameterName();
procedureQuery.registerStoredProcedureParameter(outputParameterName, outputParameterType, mode);
if (useNamedParameters) {
} else {
procedureQuery.registerStoredProcedureParameter(params.getNumberOfParameters() + 1, outputParameterType, mode);
}
String outputParameterName = procedureAttributes.getOutputParameterNames().get(i);
procedureQuery.registerStoredProcedureParameter(outputParameterName, outputParameterType, mode);
} else {
procedureQuery.registerStoredProcedureParameter(params.getNumberOfParameters() + i + 1, outputParameterType, mode);
}
});
}
return procedureQuery;

View File

@@ -58,6 +58,7 @@ import javax.persistence.TemporalType;
* @author Thomas Darimont
* @author Christoph Strobl
* @author Jens Schauder
* @author Jeff Sheets
*/
@Entity
@NamedEntityGraphs({ @NamedEntityGraph(name = "User.overview", attributeNodes = { @NamedAttributeNode("roles") }),
@@ -92,7 +93,11 @@ import javax.persistence.TemporalType;
@NamedStoredProcedureQueries({ //
@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout",
parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) }) //
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) }), //
@NamedStoredProcedureQuery(name = "User.plus1IO2", procedureName = "plus1inout2",
parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class),
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "res2", type = Integer.class) }) //
})
@NamedStoredProcedureQuery(name = "User.plus1IO", procedureName = "plus1inout",
parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),

View File

@@ -16,30 +16,34 @@
package org.springframework.data.jpa.repository;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.hasEntry;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.springframework.data.jpa.support.EntityManagerTestUtils.*;
import javax.persistence.EntityManager;
import javax.persistence.ParameterMode;
import javax.persistence.PersistenceContext;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.*;
import org.junit.Assume;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.jpa.repository.sample.UserRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
/**
* Integration tests for JPA 2.1 stored procedure support.
*
* @author Thomas Darimont
* @author Oliver Gierke
* @author Jeff Sheets
* @since 1.6
*/
@RunWith(SpringJUnit4ClassRunner.class)
@@ -50,6 +54,8 @@ public class UserRepositoryStoredProcedureTests {
@Autowired UserRepository repository;
@PersistenceContext EntityManager em;
@Rule public ExpectedException exception = ExpectedException.none();
@Test // DATAJPA-455
public void callProcedureWithInAndOutParameters() {
@@ -58,6 +64,14 @@ public class UserRepositoryStoredProcedureTests {
assertThat(repository.plus1inout(1), is(2));
}
@Test // DATAJPA-707
public void callProcedureWithInAndOutParametersInvalidOutParamName() {
assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
assertThat(repository.plus1inoutInvalidOutParamName(1), is(2));
}
@Test // DATAJPA-455
public void callProcedureExplicitNameWithInAndOutParameters() {
@@ -74,6 +88,36 @@ public class UserRepositoryStoredProcedureTests {
assertThat(repository.entityAnnotatedCustomNamedProcedurePlus1IO(1), is(2));
}
@Test // DATAJPA-707
public void entityAnnotatedCustomNamedProcedurePlus1IOInvalidOutParamName() {
assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
exception.expect(InvalidDataAccessApiUsageException.class);
exception.expectMessage(containsString("Could not locate parameter registered under that name"));
repository.entityAnnotatedCustomNamedProcedurePlus1IOInvalidOutParamName(1);
}
@Test // DATAJPA-707
public void entityAnnotatedCustomNamedProcedurePlus1IO2TwoOutParamsButNamingOne() {
assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
assertThat(repository.entityAnnotatedCustomNamedProcedurePlus1IO2TwoOutParamsButNamingOne(1), is(3));
}
@Test // DATAJPA-707
public void entityAnnotatedCustomNamedProcedurePlus1IO2() {
assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
Map<String, Integer> result = repository.entityAnnotatedCustomNamedProcedurePlus1IO2(1);
assertThat(result, hasEntry("res", 2));
assertThat(result, hasEntry("res2", 3));
assertThat(result.size(), is(2));
}
@Test // DATAJPA-455
@Ignore
public void plainJpa21() {
@@ -103,4 +147,37 @@ public class UserRepositoryStoredProcedureTests {
assertThat(proc.getOutputParameterValue("res"), is((Object) 2));
}
@Test // DATAJPA-707
@Ignore
public void plainJpa21_twoOutParams() {
assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
StoredProcedureQuery proc = em.createStoredProcedureQuery("plus1inout2");
proc.registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN);
proc.registerStoredProcedureParameter(2, Integer.class, ParameterMode.OUT);
proc.registerStoredProcedureParameter(3, Integer.class, ParameterMode.OUT);
proc.setParameter(1, 1);
proc.execute();
assertThat(proc.getOutputParameterValue(2), is((Object) 2));
assertThat(proc.getOutputParameterValue(3), is((Object) 3));
}
@Test // DATAJPA-707
@Ignore
public void plainJpa21_entityAnnotatedCustomNamedProcedurePlus1IO2() {
Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
StoredProcedureQuery proc = em.createNamedStoredProcedureQuery("User.plus1IO2");
proc.setParameter("arg", 1);
proc.execute();
assertThat(proc.getOutputParameterValue("res"), is((Object) 2));
assertThat(proc.getOutputParameterValue("res2"), is((Object) 3));
}
}

View File

@@ -23,6 +23,7 @@ import static org.mockito.Mockito.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.Map;
import javax.persistence.EntityManager;
@@ -43,6 +44,7 @@ import org.springframework.util.ReflectionUtils;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Diego Diez
* @author Jeff Sheets
* @since 1.6
*/
@RunWith(MockitoJUnitRunner.class)
@@ -66,8 +68,8 @@ public class StoredProcedureAttributeSourceUnitTests {
StoredProcedureAttributes attr = creator.createFrom(method("plus1inout", Integer.class), entityMetadata);
assertThat(attr.getProcedureName(), is("plus1inout"));
assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME));
}
@Test // DATAJPA-455
@@ -77,8 +79,8 @@ public class StoredProcedureAttributeSourceUnitTests {
entityMetadata);
assertThat(attr.getProcedureName(), is("plus1inout"));
assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME));
}
@Test // DATAJPA-455
@@ -88,8 +90,8 @@ public class StoredProcedureAttributeSourceUnitTests {
entityMetadata);
assertThat(attr.getProcedureName(), is("plus1inout"));
assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME));
}
@Test // DATAJPA-455
@@ -99,8 +101,8 @@ public class StoredProcedureAttributeSourceUnitTests {
.createFrom(method("explicitPlus1inoutViaProcedureNameAlias", Integer.class), entityMetadata);
assertThat(attr.getProcedureName(), is("plus1inout"));
assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME));
}
@Test // DATAJPA-1297
@@ -110,8 +112,8 @@ public class StoredProcedureAttributeSourceUnitTests {
method("explicitPlus1inoutViaProcedureNameAliasAndOutputParameterName", Integer.class), entityMetadata);
assertThat(attr.getProcedureName(), is("plus1inout"));
assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterName(), is("res"));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is("res"));
}
@Test // DATAJPA-455
@@ -121,8 +123,32 @@ public class StoredProcedureAttributeSourceUnitTests {
.createFrom(method("entityAnnotatedCustomNamedProcedurePlus1IO", Integer.class), entityMetadata);
assertThat(attr.getProcedureName(), is("User.plus1IO"));
assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterName(), is("res"));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is("res"));
}
@Test // DATAJPA-707
public void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithExplicitlyNamedProcedureAndOutputParamName() {
StoredProcedureAttributes attr = creator
.createFrom(method("entityAnnotatedCustomNamedProcedureOutputParamNamePlus1IO", Integer.class), entityMetadata);
assertThat(attr.getProcedureName(), is("User.plus1IO"));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is("override"));
}
@Test // DATAJPA-707
public void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithExplicitlyNamedProcedureAnd2OutParams() {
StoredProcedureAttributes attr = creator
.createFrom(method("entityAnnotatedCustomNamedProcedurePlus1IO2", Integer.class), entityMetadata);
assertThat(attr.getProcedureName(), is("User.plus1IO2"));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is("res"));
assertThat(attr.getOutputParameterTypes().get(1), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(1), is("res2"));
}
@Test // DATAJPA-455
@@ -131,8 +157,8 @@ public class StoredProcedureAttributeSourceUnitTests {
StoredProcedureAttributes attr = creator.createFrom(method("plus1", Integer.class), entityMetadata);
assertThat(attr.getProcedureName(), is("User.plus1"));
assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterName(), is("res"));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is("res"));
}
@Test // DATAJPA-871
@@ -142,8 +168,8 @@ public class StoredProcedureAttributeSourceUnitTests {
.createFrom(method("plus1inoutWithComposedAnnotationOverridingProcedureName", Integer.class), entityMetadata);
assertThat(attr.getProcedureName(), is(equalTo("plus1inout")));
assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME));
}
@Test // DATAJPA-871
@@ -153,8 +179,8 @@ public class StoredProcedureAttributeSourceUnitTests {
.createFrom(method("plus1inoutWithComposedAnnotationOverridingName", Integer.class), entityMetadata);
assertThat(attr.getProcedureName(), is(equalTo("User.plus1")));
assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterName(), is(equalTo("res")));
assertThat(attr.getOutputParameterTypes().get(0), is(typeCompatibleWith(Integer.class)));
assertThat(attr.getOutputParameterNames().get(0), is(equalTo("res")));
}
private static Method method(String name, Class<?>... paramTypes) {
@@ -198,6 +224,19 @@ public class StoredProcedureAttributeSourceUnitTests {
@Procedure(name = "User.plus1IO") // DATAJPA-455
Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);
/**
* Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager}.
* With a outputParameterName
*/
@Procedure(name = "User.plus1IO", outputParameterName = "override") // DATAJPA-707
Integer entityAnnotatedCustomNamedProcedureOutputParamNamePlus1IO(@Param("arg") Integer arg);
/**
* Explicitly mapped to named stored procedure "User.plus1IO2" in {@link EntityManager}.
*/
@Procedure(name = "User.plus1IO2") // DATAJPA-707
Map<String, Integer> entityAnnotatedCustomNamedProcedurePlus1IO2(@Param("arg") Integer arg);
/**
* Implicitly mapped to named stored procedure "User.plus1" in {@link EntityManager}.
*/

View File

@@ -32,6 +32,6 @@ public class StoredProcedureAttributesUnitTests {
public void usesSyntheticOutputParameterNameForAdhocProcedureWithoutOutputName() {
StoredProcedureAttributes attributes = new StoredProcedureAttributes("procedure", null, Long.class, false);
assertThat(attributes.getOutputParameterName(), is(SYNTHETIC_OUTPUT_PARAMETER_NAME));
assertThat(attributes.getOutputParameterNames().get(0), is(SYNTHETIC_OUTPUT_PARAMETER_NAME));
}
}

View File

@@ -50,6 +50,7 @@ import com.google.common.base.Optional;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Kevin Peters
* @author Jeff Sheets
*/
public interface UserRepository
extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User>, UserRepositoryCustom {
@@ -318,12 +319,40 @@ public interface UserRepository
@Procedure(procedureName = "plus1inout") // DATAJPA-455
Integer plus1inout(Integer arg);
/**
* Implicitly mapped to a procedure with name "plus1inout" in database via alias.
* Showing that outputParameterName is ignored when not a NamedStoredProcedure
*/
@Procedure(procedureName = "plus1inout", outputParameterName = "fakeName") // DATAJPA-707
Integer plus1inoutInvalidOutParamName(Integer arg);
/**
* Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager}.
*/
@Procedure(name = "User.plus1IO") // DATAJPA-455
Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);
/**
* Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager}.
* with an invalid outputParameterName - test will fail
*/
@Procedure(name = "User.plus1IO", outputParameterName = "fakeName") // DATAJPA-707
Integer entityAnnotatedCustomNamedProcedurePlus1IOInvalidOutParamName(@Param("arg") Integer arg);
/**
* Explicitly mapped to named stored procedure "User.plus1IO2" in {@link EntityManager}.
* Stored Proc has 2 out params, but naming one out param here so it only returns one
*/
@Procedure(name = "User.plus1IO2", outputParameterName = "res2") // DATAJPA-707
Integer entityAnnotatedCustomNamedProcedurePlus1IO2TwoOutParamsButNamingOne(@Param("arg") Integer arg);
/**
* Explicitly mapped to named stored procedure "User.plus1IO2" in {@link EntityManager}.
* Returns 2 out params as a List
*/
@Procedure(name = "User.plus1IO2") // DATAJPA-707
Map<String, Integer> entityAnnotatedCustomNamedProcedurePlus1IO2(@Param("arg") Integer arg);
/**
* Implicitly mapped to named stored procedure "User.plus1" in {@link EntityManager}.
*/

View File

@@ -6,6 +6,14 @@ BEGIN ATOMIC
set res = arg + 1;
END
/;
DROP procedure IF EXISTS plus1inout2
/;
CREATE procedure plus1inout2 (IN arg int, OUT res int, OUT res2 int)
BEGIN ATOMIC
set res = arg + 1;
set res2 = arg + 2;
END
/;
DROP procedure IF EXISTS procedure_in1_out1
/;
DROP procedure IF EXISTS procedure_in1_out0