DATAJPA-707 - Implement Stored Procedures with multiple output parameters.
Original pull request: #297.
This commit is contained in:
committed by
Jens Schauder
parent
17fa5f454a
commit
e41236f722
@@ -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) {
|
||||
|
||||
@@ -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))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}.
|
||||
*/
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user