Add REF_CURSOR support for stored procedures.
When registered procedure output parameters, if the method annotated with @Procedure has a collection return type or an entity return type, then use ResultSet. Otherwise, handle either the array (Object[]) or the primitive types. * Throw a proper exception when using an @Procedure-annotated method and no transaction is active. * Introduce refCursor as a boolean flag to @Procedure denoting when to use REF_CURSORs for OUT parameters.. * Extract a ProcedureParameter command object to ease usage of a parameter's name, type and mode. (NOTE: javax.persistence already has "StoredProcedureParameter".) NOTE: Integration testing of stored procedures is very limited by lack of HSQL's support for REF CURSORS. See ##2256 to track future work for potential testing against MySQL, Oracle, Postgres, or SQL Server. See: #1959, #409. Related: #2014.
This commit is contained in:
committed by
Greg L. Turnquist
parent
2e30a353c9
commit
554bd3d6ed
@@ -53,6 +53,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @author Christoph Strobl
|
||||
* @author Nicolas Cirigliano
|
||||
* @author Jens Schauder
|
||||
* @author Gabriel Basilio
|
||||
*/
|
||||
public abstract class JpaQueryExecution {
|
||||
|
||||
@@ -301,6 +302,8 @@ public abstract class JpaQueryExecution {
|
||||
*/
|
||||
static class ProcedureExecution extends JpaQueryExecution {
|
||||
|
||||
private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a @Procedure method without a surrounding transaction that keeps the connection open so that the ResultSet can actually be consumed. Make sure the consumer code uses @Transactional or any other way of declaring a (read-only) transaction.";
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[])
|
||||
@@ -312,7 +315,24 @@ public abstract class JpaQueryExecution {
|
||||
|
||||
StoredProcedureJpaQuery storedProcedureJpaQuery = (StoredProcedureJpaQuery) jpaQuery;
|
||||
StoredProcedureQuery storedProcedure = storedProcedureJpaQuery.createQuery(accessor);
|
||||
storedProcedure.execute();
|
||||
|
||||
boolean returnsResultSet = storedProcedure.execute();
|
||||
|
||||
if (returnsResultSet) {
|
||||
if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive())
|
||||
throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION);
|
||||
|
||||
List<?> result = storedProcedure.getResultList();
|
||||
|
||||
if (!storedProcedureJpaQuery.getQueryMethod().isCollectionQuery()) {
|
||||
if (result.isEmpty())
|
||||
return null;
|
||||
if (result.size() == 1)
|
||||
return result.get(0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return storedProcedureJpaQuery.extractOutputValue(storedProcedure);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.lang.annotation.Target;
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Gabriel Basilio
|
||||
* @since 1.6
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
@@ -51,4 +52,9 @@ public @interface Procedure {
|
||||
* The name of the outputParameter, defaults to {@code ""}.
|
||||
*/
|
||||
String outputParameterName() default "";
|
||||
|
||||
/**
|
||||
* Whether the procedure returns a Ref Cursor from the database {@code false}.
|
||||
*/
|
||||
boolean refCursor() default false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2014-2020 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
|
||||
*
|
||||
* https://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 org.springframework.lang.Nullable;
|
||||
|
||||
import javax.persistence.ParameterMode;
|
||||
|
||||
/**
|
||||
* This class represents a Stored Procedure Parameter
|
||||
* and an instance of the annotation {@link javax.persistence.StoredProcedureParameter}.
|
||||
*
|
||||
* @author Gabriel Basilio
|
||||
*/
|
||||
public class ProcedureParameter {
|
||||
|
||||
private final String name;
|
||||
private final ParameterMode mode;
|
||||
private final Class<?> type;
|
||||
|
||||
public ProcedureParameter(@Nullable String name, ParameterMode mode, Class<?> type) {
|
||||
this.name = name;
|
||||
this.mode = mode;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ParameterMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import java.util.List;
|
||||
|
||||
import javax.persistence.NamedStoredProcedureQueries;
|
||||
import javax.persistence.NamedStoredProcedureQuery;
|
||||
import javax.persistence.ParameterMode;
|
||||
import javax.persistence.StoredProcedureParameter;
|
||||
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
@@ -38,6 +39,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Mark Paluch
|
||||
* @author Diego Diez
|
||||
* @author Jeff Sheets
|
||||
* @author Gabriel Basilio
|
||||
* @since 1.6
|
||||
*/
|
||||
enum StoredProcedureAttributeSource {
|
||||
@@ -72,7 +74,7 @@ enum StoredProcedureAttributeSource {
|
||||
+ method);
|
||||
}
|
||||
|
||||
return new StoredProcedureAttributes(procedureName, procedure.outputParameterName(), method.getReturnType());
|
||||
return new StoredProcedureAttributes(procedureName, createOutputProcedureParameterFrom(method, procedure));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,28 +104,29 @@ enum StoredProcedureAttributeSource {
|
||||
private StoredProcedureAttributes newProcedureAttributesFrom(Method method,
|
||||
NamedStoredProcedureQuery namedStoredProc, Procedure procedure) {
|
||||
|
||||
List<String> outputParameterNames = new ArrayList<>();
|
||||
List<Class<?>> outputParameterTypes = new ArrayList<>();
|
||||
List<ProcedureParameter> outputParameters = new ArrayList<>();
|
||||
|
||||
if (!procedure.outputParameterName().isEmpty()) {
|
||||
// we give the output parameter definition from the @Procedure annotation precedence
|
||||
outputParameterNames.add(procedure.outputParameterName());
|
||||
outputParameters.add(createOutputProcedureParameterFrom(method, procedure));
|
||||
} else {
|
||||
|
||||
// try to discover the output parameter
|
||||
List<StoredProcedureParameter> outputParameters = extractOutputParametersFrom(namedStoredProc);
|
||||
List<StoredProcedureParameter> namedProcedureOutputParameters = extractOutputParametersFrom(namedStoredProc);
|
||||
|
||||
for (StoredProcedureParameter outputParameter : outputParameters) {
|
||||
outputParameterNames.add(outputParameter.name());
|
||||
outputParameterTypes.add(outputParameter.type());
|
||||
for (StoredProcedureParameter outputParameter : namedProcedureOutputParameters) {
|
||||
outputParameters.add(new ProcedureParameter(
|
||||
outputParameter.name(), outputParameter.mode(), outputParameter.type()));
|
||||
}
|
||||
}
|
||||
|
||||
if (outputParameterTypes.isEmpty()) {
|
||||
outputParameterTypes.add(method.getReturnType());
|
||||
}
|
||||
return new StoredProcedureAttributes(namedStoredProc.name(), outputParameters, true);
|
||||
}
|
||||
|
||||
return new StoredProcedureAttributes(namedStoredProc.name(), outputParameterNames, outputParameterTypes, true);
|
||||
private ProcedureParameter createOutputProcedureParameterFrom(Method method, Procedure procedure) {
|
||||
return new ProcedureParameter(procedure.outputParameterName(),
|
||||
procedure.refCursor() ? ParameterMode.REF_CURSOR : ParameterMode.OUT,
|
||||
method.getReturnType());
|
||||
}
|
||||
|
||||
private List<StoredProcedureParameter> extractOutputParametersFrom(NamedStoredProcedureQuery namedStoredProc) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import java.util.stream.IntStream;
|
||||
* @author Mark Paluch
|
||||
* @author Jeff Sheets
|
||||
* @author Jens Schauder
|
||||
* @author Gabriel Basilio
|
||||
* @since 1.6
|
||||
*/
|
||||
class StoredProcedureAttributes {
|
||||
@@ -42,52 +43,51 @@ class StoredProcedureAttributes {
|
||||
|
||||
private final boolean namedStoredProcedure;
|
||||
private final String procedureName;
|
||||
private final List<String> outputParameterNames;
|
||||
private final List<Class<?>> outputParameterTypes;
|
||||
private final List<ProcedureParameter> outputProcedureParameters;
|
||||
|
||||
/**
|
||||
* Creates a new {@link StoredProcedureAttributes}.
|
||||
*
|
||||
* @param procedureName must not be {@literal null}.
|
||||
* @param outputParameterName may be {@literal null}.
|
||||
* @param outputParameterType must not be {@literal null}.
|
||||
*/
|
||||
StoredProcedureAttributes(String procedureName, @Nullable String outputParameterName,
|
||||
Class<?> outputParameterType) {
|
||||
this(procedureName, Collections.singletonList(outputParameterName), Collections.singletonList(outputParameterType), false);
|
||||
StoredProcedureAttributes(String procedureName, ProcedureParameter parameter) {
|
||||
this(procedureName, Collections.singletonList(parameter), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link StoredProcedureAttributes}.
|
||||
*
|
||||
* @param procedureName must not be {@literal null}.
|
||||
* @param outputParameterNames may be empty, but not {@literal null}.
|
||||
* @param outputParameterTypes must not be empty, and cannot be a single element of {@literal null}.
|
||||
* @param namedStoredProcedure flag signaling if the stored procedure has a name.
|
||||
*/
|
||||
StoredProcedureAttributes(String procedureName, List<String> outputParameterNames,
|
||||
List<Class<?>> outputParameterTypes, boolean namedStoredProcedure) {
|
||||
StoredProcedureAttributes(String procedureName, List<ProcedureParameter> outputProcedureParameters, boolean namedStoredProcedure) {
|
||||
|
||||
Assert.notNull(procedureName, "ProcedureName 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");
|
||||
Assert.notNull(outputProcedureParameters, "OutputProcedureParameters must not be null!");
|
||||
Assert.isTrue(outputProcedureParameters.size() != 1 || outputProcedureParameters.get(0) != null, "ProcedureParameters must not have size 1 with a null value");
|
||||
|
||||
this.procedureName = procedureName;
|
||||
this.outputParameterNames = namedStoredProcedure
|
||||
? outputParameterNames
|
||||
: completeOutputParameterNames(outputParameterNames);
|
||||
this.outputParameterTypes = outputParameterTypes;
|
||||
this.namedStoredProcedure = namedStoredProcedure;
|
||||
|
||||
if (namedStoredProcedure) {
|
||||
this.outputProcedureParameters = outputProcedureParameters;
|
||||
} else {
|
||||
this.outputProcedureParameters = getParametersWithCompletedNames(outputProcedureParameters);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> completeOutputParameterNames(List<String> outputParameterNames) {
|
||||
|
||||
return IntStream.range(0, outputParameterNames.size()) //
|
||||
.mapToObj(i -> completeOutputParameterName(i, outputParameterNames.get(i))) //
|
||||
private List<ProcedureParameter> getParametersWithCompletedNames(List<ProcedureParameter> procedureParameters) {
|
||||
return IntStream.range(0, procedureParameters.size())
|
||||
.mapToObj(i -> getParameterWithCompletedName(procedureParameters.get(i), i))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private ProcedureParameter getParameterWithCompletedName(ProcedureParameter parameter, int i) {
|
||||
return new ProcedureParameter(
|
||||
completeOutputParameterName(i, parameter.getName()),
|
||||
parameter.getMode(), parameter.getType());
|
||||
}
|
||||
|
||||
private String completeOutputParameterName(int i, String paramName) {
|
||||
|
||||
return StringUtils.hasText(paramName) //
|
||||
@@ -113,34 +113,27 @@ class StoredProcedureAttributes {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getOutputParameterNames() {
|
||||
return outputParameterNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the types of the output parameters.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<Class<?>> getOutputParameterTypes() {
|
||||
return outputParameterTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the stored procedure is a named one.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isNamedStoredProcedure() {
|
||||
return namedStoredProcedure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the stored procedure output parameter list
|
||||
*/
|
||||
public List<ProcedureParameter> getOutputProcedureParameters() {
|
||||
return outputProcedureParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the stored procedure will produce a result.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasReturnValue() {
|
||||
return !(outputParameterTypes.size() == 1 && (void.class.equals(outputParameterTypes.get(0)) || Void.class.equals(outputParameterTypes.get(0))));
|
||||
if (getOutputProcedureParameters().isEmpty())
|
||||
return false;
|
||||
|
||||
Class<?> outputType = getOutputProcedureParameters().get(0).getType();
|
||||
return !(void.class.equals(outputType) || Void.class.equals(outputType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.stream.IntStream;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NamedStoredProcedureQuery;
|
||||
import javax.persistence.ParameterMode;
|
||||
import javax.persistence.StoredProcedureParameter;
|
||||
import javax.persistence.StoredProcedureQuery;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
@@ -132,42 +133,32 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, Object> outputValues = new HashMap<>();
|
||||
List<String> parameterNames = procedureAttributes.getOutputParameterNames();
|
||||
List<ProcedureParameter> outputParameters = procedureAttributes.getOutputProcedureParameters();
|
||||
|
||||
for (int i = 0; i < parameterNames.size(); i++) {
|
||||
|
||||
String name = parameterNames.get(i);
|
||||
outputValues.put(name, extractOutputParameter(storedProcedureQuery, i));
|
||||
if (outputParameters.size() == 1) {
|
||||
return extractOutputParameterValue(outputParameters.get(0), 0, storedProcedureQuery);
|
||||
}
|
||||
|
||||
return outputValues.size() == 1 ? outputValues.values().iterator().next() : outputValues;
|
||||
}
|
||||
Map<String, Object> outputValues = new HashMap<>();
|
||||
|
||||
private Object extractOutputParameter(StoredProcedureQuery storedProcedureQuery, Integer index) {
|
||||
for (int i = 0; i < outputParameters.size(); i++) {
|
||||
ProcedureParameter outputParameter = outputParameters.get(i);
|
||||
outputValues.put(outputParameter.getName(), extractOutputParameterValue(outputParameter, i, storedProcedureQuery));
|
||||
}
|
||||
|
||||
String outputParameterName = procedureAttributes.getOutputParameterNames().get(index);
|
||||
JpaParameters parameters = getQueryMethod().getParameters();
|
||||
|
||||
return extractOutputParameterValue(storedProcedureQuery, outputParameterName, index,
|
||||
parameters.getNumberOfParameters());
|
||||
return outputValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* extract the value of an output parameter either by name or by index.
|
||||
*
|
||||
* @param storedProcedureQuery the query object of the stored procedure.
|
||||
* @param name the name of the output parameter
|
||||
* @param index index of the output parameter
|
||||
* @param offset for index based access the index after which to find the output parameter values
|
||||
* @return the value
|
||||
* @return The value of an output parameter either by name or by index.
|
||||
*/
|
||||
private Object extractOutputParameterValue(StoredProcedureQuery storedProcedureQuery, String name, Integer index,
|
||||
int offset) {
|
||||
private Object extractOutputParameterValue(ProcedureParameter outputParameter, Integer index, StoredProcedureQuery storedProcedureQuery) {
|
||||
|
||||
return useNamedParameters && StringUtils.hasText(name) ? //
|
||||
storedProcedureQuery.getOutputParameterValue(name)
|
||||
: storedProcedureQuery.getOutputParameterValue(offset + index + 1);
|
||||
JpaParameters methodParameters = getQueryMethod().getParameters();
|
||||
|
||||
return useNamedParameters && StringUtils.hasText(outputParameter.getName()) ?
|
||||
storedProcedureQuery.getOutputParameterValue(outputParameter.getName())
|
||||
: storedProcedureQuery.getOutputParameterValue(methodParameters.getNumberOfParameters() + index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,9 +183,7 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery {
|
||||
private StoredProcedureQuery newAdhocStoredProcedureQuery() {
|
||||
|
||||
JpaParameters params = getQueryMethod().getParameters();
|
||||
String procedureName = procedureAttributes.getProcedureName();
|
||||
|
||||
StoredProcedureQuery procedureQuery = getEntityManager().createStoredProcedureQuery(procedureName);
|
||||
StoredProcedureQuery procedureQuery = createAdhocStoredProcedureQuery();
|
||||
|
||||
for (JpaParameter param : params) {
|
||||
|
||||
@@ -214,23 +203,41 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery {
|
||||
|
||||
if (procedureAttributes.hasReturnValue()) {
|
||||
|
||||
ParameterMode mode = ParameterMode.OUT;
|
||||
ProcedureParameter procedureOutput = procedureAttributes.getOutputProcedureParameters().get(0);
|
||||
|
||||
IntStream.range(0, procedureAttributes.getOutputParameterTypes().size()).forEach(i -> {
|
||||
Class<?> outputParameterType = procedureAttributes.getOutputParameterTypes().get(i);
|
||||
/* If the stored procedure returns a ResultSet without using REF_CURSOR,
|
||||
it is not necessary to declare an output parameter */
|
||||
if ((isResultSetProcedure() && procedureOutput.getMode() == ParameterMode.REF_CURSOR) || !isResultSetProcedure()) {
|
||||
|
||||
if (useNamedParameters) {
|
||||
|
||||
String outputParameterName = procedureAttributes.getOutputParameterNames().get(i);
|
||||
procedureQuery.registerStoredProcedureParameter(outputParameterName, outputParameterType, mode);
|
||||
|
||||
procedureQuery.registerStoredProcedureParameter(procedureOutput.getName(), procedureOutput.getType(), procedureOutput.getMode());
|
||||
} else {
|
||||
procedureQuery.registerStoredProcedureParameter(params.getNumberOfParameters() + i + 1, outputParameterType,
|
||||
mode);
|
||||
// Output parameter should be after the input parameters
|
||||
int outputParameterIndex = params.getNumberOfParameters() + 1;
|
||||
procedureQuery.registerStoredProcedureParameter(outputParameterIndex, procedureOutput.getType(),
|
||||
procedureOutput.getMode());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return procedureQuery;
|
||||
}
|
||||
|
||||
private StoredProcedureQuery createAdhocStoredProcedureQuery() {
|
||||
String procedureName = procedureAttributes.getProcedureName();
|
||||
|
||||
if (getQueryMethod().isQueryForEntity()) {
|
||||
return getEntityManager().createStoredProcedureQuery(procedureName,
|
||||
getQueryMethod().getEntityInformation().getJavaType());
|
||||
}
|
||||
|
||||
return getEntityManager().createStoredProcedureQuery(procedureName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the stored procedure will use a ResultSet to return data and not output parameters
|
||||
*/
|
||||
private boolean isResultSetProcedure() {
|
||||
return getQueryMethod().isCollectionQuery() || getQueryMethod().isQueryForEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,7 @@ import java.util.stream.Stream;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Tuple;
|
||||
|
||||
import com.querydsl.core.types.EntityPath;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.jpa.projection.CollectionAwareProjectionFactory;
|
||||
@@ -40,6 +38,7 @@ import org.springframework.data.jpa.repository.query.EscapeCharacter;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryMethod;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory;
|
||||
import org.springframework.data.jpa.repository.query.Procedure;
|
||||
import org.springframework.data.jpa.util.JpaMetamodel;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.querydsl.EntityPathResolver;
|
||||
@@ -59,6 +58,8 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.querydsl.core.types.EntityPath;
|
||||
|
||||
/**
|
||||
* JPA specific generic repository factory.
|
||||
*
|
||||
@@ -97,7 +98,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor);
|
||||
addRepositoryProxyPostProcessor((factory, repositoryInformation) -> {
|
||||
|
||||
if (hasMethodReturningStream(repositoryInformation.getRepositoryInterface())) {
|
||||
if (isTransactionNeeded(repositoryInformation.getRepositoryInterface())) {
|
||||
factory.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE);
|
||||
}
|
||||
});
|
||||
@@ -272,12 +273,12 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
return RepositoryFragments.empty();
|
||||
}
|
||||
|
||||
private static boolean hasMethodReturningStream(Class<?> repositoryClass) {
|
||||
private static boolean isTransactionNeeded(Class<?> repositoryClass) {
|
||||
|
||||
Method[] methods = ReflectionUtils.getAllDeclaredMethods(repositoryClass);
|
||||
|
||||
for (Method method : methods) {
|
||||
if (Stream.class.isAssignableFrom(method.getReturnType())) {
|
||||
if (Stream.class.isAssignableFrom(method.getReturnType()) || method.isAnnotationPresent(Procedure.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -293,7 +294,8 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 2.0.5
|
||||
* @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141">https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141</a>
|
||||
* @see <a href=
|
||||
* "https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141">https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141</a>
|
||||
*/
|
||||
private static class EclipseLinkProjectionQueryCreationListener implements QueryCreationListener<AbstractJpaQuery> {
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Jens Schauder
|
||||
* @author Gabriel Basilio
|
||||
* @see scripts/schema-stored-procedures.sql for procedure definitions.
|
||||
*/
|
||||
@Transactional
|
||||
@@ -54,7 +55,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@ExtendWith(SpringExtension.class)
|
||||
public class StoredProcedureIntegrationTests {
|
||||
|
||||
private static final String NOT_SUPPORTED = "Stored procedures with ResultSets are currently not supported for any JPA provider";
|
||||
private static final String NOT_SUPPORTED = "Stored procedures with REF_CURSOR are currently not supported by HSQL dialect";
|
||||
|
||||
@PersistenceContext EntityManager em;
|
||||
@Autowired DummyRepository repository;
|
||||
|
||||
@@ -21,9 +21,11 @@ import static org.mockito.Mockito.*;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.ParameterMode;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -32,8 +34,8 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.data.jpa.domain.sample.Dummy;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -47,6 +49,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @author Diego Diez
|
||||
* @author Jeff Sheets
|
||||
* @author Jens Schauder
|
||||
* @author Gabriel Basilio
|
||||
* @since 1.6
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@@ -54,8 +57,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
class StoredProcedureAttributeSourceUnitTests {
|
||||
|
||||
private StoredProcedureAttributeSource creator;
|
||||
@Mock
|
||||
JpaEntityMetadata<User> entityMetadata;
|
||||
@Mock JpaEntityMetadata<User> entityMetadata;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
@@ -70,10 +72,11 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
void shouldCreateStoredProcedureAttributesFromProcedureMethodWithImplicitProcedureName() {
|
||||
|
||||
StoredProcedureAttributes attr = creator.createFrom(method("plus1inout", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("plus1inout");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-455
|
||||
@@ -82,9 +85,11 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
StoredProcedureAttributes attr = creator.createFrom(method("explicitlyNamedPlus1inout", Integer.class),
|
||||
entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("plus1inout");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-455
|
||||
@@ -93,9 +98,11 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
StoredProcedureAttributes attr = creator.createFrom(method("explicitlyNamedPlus1inout", Integer.class),
|
||||
entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("plus1inout");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-455
|
||||
@@ -104,9 +111,11 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
StoredProcedureAttributes attr = creator
|
||||
.createFrom(method("explicitPlus1inoutViaProcedureNameAlias", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("plus1inout");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1297
|
||||
@@ -115,9 +124,11 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
StoredProcedureAttributes attr = creator.createFrom(
|
||||
method("explicitPlus1inoutViaProcedureNameAliasAndOutputParameterName", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("plus1inout");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("res");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo("res");
|
||||
}
|
||||
|
||||
@Test // DATAJPA-455
|
||||
@@ -126,9 +137,11 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
StoredProcedureAttributes attr = creator
|
||||
.createFrom(method("entityAnnotatedCustomNamedProcedurePlus1IO", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("User.plus1IO");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("res");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo("res");
|
||||
}
|
||||
|
||||
@Test // DATAJPA-707
|
||||
@@ -137,9 +150,11 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
StoredProcedureAttributes attr = creator
|
||||
.createFrom(method("entityAnnotatedCustomNamedProcedureOutputParamNamePlus1IO", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("User.plus1IO");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("override");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo("override");
|
||||
}
|
||||
|
||||
@Test // DATAJPA-707
|
||||
@@ -148,11 +163,18 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
StoredProcedureAttributes attr = creator
|
||||
.createFrom(method("entityAnnotatedCustomNamedProcedurePlus1IO2", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter firstOutputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
ProcedureParameter secondOutputParameter = attr.getOutputProcedureParameters().get(1);
|
||||
|
||||
assertThat(attr.getProcedureName()).isEqualTo("User.plus1IO2");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("res");
|
||||
assertThat(attr.getOutputParameterTypes().get(1)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(1)).isEqualTo("res2");
|
||||
|
||||
assertThat(firstOutputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(firstOutputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(firstOutputParameter.getName()).isEqualTo("res");
|
||||
|
||||
assertThat(secondOutputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(secondOutputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(secondOutputParameter.getName()).isEqualTo("res2");
|
||||
}
|
||||
|
||||
@Test // DATAJPA-455
|
||||
@@ -160,9 +182,11 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
|
||||
StoredProcedureAttributes attr = creator.createFrom(method("plus1", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("User.plus1");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("res");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo("res");
|
||||
}
|
||||
|
||||
@Test // DATAJPA-871
|
||||
@@ -171,9 +195,11 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
StoredProcedureAttributes attr = creator
|
||||
.createFrom(method("plus1inoutWithComposedAnnotationOverridingProcedureName", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("plus1inout");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-871
|
||||
@@ -182,9 +208,102 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
StoredProcedureAttributes attr = creator
|
||||
.createFrom(method("plus1inoutWithComposedAnnotationOverridingName", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("User.plus1");
|
||||
assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class);
|
||||
assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("res");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Integer.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo("res");
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1657
|
||||
public void testSingleEntityFrom1RowResultSetAndNoInput() {
|
||||
|
||||
StoredProcedureAttributes attr = creator.createFrom(method("singleEntityFrom1RowResultSetAndNoInput"),
|
||||
entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("0_input_1_row_resultset");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Dummy.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1657
|
||||
public void testSingleEntityFrom1RowResultSetWithInput() {
|
||||
|
||||
StoredProcedureAttributes attr = creator.createFrom(method("singleEntityFrom1RowResultSetWithInput", Integer.class),
|
||||
entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("1_input_1_row_resultset");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(Dummy.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1657
|
||||
public void testEntityListFromResultSetWithNoInput() {
|
||||
|
||||
StoredProcedureAttributes attr = creator.createFrom(method("entityListFromResultSetWithNoInput"), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("0_input_1_resultset");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(List.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
//
|
||||
@Test // DATAJPA-1657
|
||||
public void testEntityListFromResultSetWithInput() {
|
||||
|
||||
StoredProcedureAttributes attr = creator.createFrom(method("entityListFromResultSetWithInput", Integer.class),
|
||||
entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(List.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1657
|
||||
public void testGenericObjectListFromResultSetWithInput() {
|
||||
|
||||
StoredProcedureAttributes attr = creator
|
||||
.createFrom(method("genericObjectListFromResultSetWithInput", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(List.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1657
|
||||
public void testEntityListFromResultSetWithInputAndNamedOutput() {
|
||||
|
||||
StoredProcedureAttributes attr = creator
|
||||
.createFrom(method("entityListFromResultSetWithInputAndNamedOutput", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT);
|
||||
assertThat(outputParameter.getType()).isEqualTo(List.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo("dummies");
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1657
|
||||
public void testEntityListFromResultSetWithInputAndNamedOutputAndCursor() {
|
||||
|
||||
StoredProcedureAttributes attr = creator
|
||||
.createFrom(method("entityListFromResultSetWithInputAndNamedOutputAndCursor", Integer.class), entityMetadata);
|
||||
|
||||
ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0);
|
||||
assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset");
|
||||
assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.REF_CURSOR);
|
||||
assertThat(outputParameter.getType()).isEqualTo(List.class);
|
||||
assertThat(outputParameter.getName()).isEqualTo("dummies");
|
||||
}
|
||||
|
||||
private static Method method(String name, Class<?>... paramTypes) {
|
||||
@@ -195,8 +314,7 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private
|
||||
interface DummyRepository {
|
||||
private interface DummyRepository {
|
||||
|
||||
/**
|
||||
* Explicitly mapped to a procedure with name "plus1inout" in database.
|
||||
@@ -235,8 +353,7 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);
|
||||
|
||||
/**
|
||||
* Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager}.
|
||||
* With a outputParameterName
|
||||
* Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager}. With a outputParameterName
|
||||
*/
|
||||
@Procedure(name = "User.plus1IO", outputParameterName = "override")
|
||||
// DATAJPA-707
|
||||
@@ -261,6 +378,34 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
|
||||
@ComposedProcedureUsingAliasFor(emProcedureName = "User.plus1")
|
||||
Integer plus1inoutWithComposedAnnotationOverridingName(Integer arg);
|
||||
|
||||
@Procedure("0_input_1_row_resultset")
|
||||
// DATAJPA-1657
|
||||
Dummy singleEntityFrom1RowResultSetAndNoInput();
|
||||
|
||||
@Procedure("1_input_1_row_resultset")
|
||||
// DATAJPA-1657
|
||||
Dummy singleEntityFrom1RowResultSetWithInput(Integer arg);
|
||||
|
||||
@Procedure("0_input_1_resultset")
|
||||
// DATAJPA-1657
|
||||
List<Dummy> entityListFromResultSetWithNoInput();
|
||||
|
||||
@Procedure("1_input_1_resultset")
|
||||
// DATAJPA-1657
|
||||
List<Dummy> entityListFromResultSetWithInput(Integer arg);
|
||||
|
||||
@Procedure("1_input_1_resultset")
|
||||
// DATAJPA-1657
|
||||
List<Object[]> genericObjectListFromResultSetWithInput(Integer arg);
|
||||
|
||||
@Procedure(value = "1_input_1_resultset", outputParameterName = "dummies")
|
||||
// DATAJPA-1657
|
||||
List<Dummy> entityListFromResultSetWithInputAndNamedOutput(Integer arg);
|
||||
|
||||
@Procedure(value = "1_input_1_resultset", outputParameterName = "dummies", refCursor = true)
|
||||
// DATAJPA-1657
|
||||
List<Dummy> entityListFromResultSetWithInputAndNamedOutputAndCursor(Integer arg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@@ -20,6 +20,8 @@ import static org.springframework.data.jpa.repository.query.StoredProcedureAttri
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.persistence.ParameterMode;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link StoredProcedureAttributes}.
|
||||
*
|
||||
@@ -31,7 +33,8 @@ class StoredProcedureAttributesUnitTests {
|
||||
@Test // DATAJPA-681
|
||||
void usesSyntheticOutputParameterNameForAdhocProcedureWithoutOutputName() {
|
||||
|
||||
StoredProcedureAttributes attributes = new StoredProcedureAttributes("procedure", null, Long.class);
|
||||
assertThat(attributes.getOutputParameterNames().get(0)).isEqualTo(SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
ProcedureParameter outputParameter = new ProcedureParameter(null, ParameterMode.OUT, Long.class);
|
||||
StoredProcedureAttributes attributes = new StoredProcedureAttributes("procedure", outputParameter);
|
||||
assertThat(attributes.getOutputProcedureParameters().get(0).getName()).isEqualTo(SYNTHETIC_OUTPUT_PARAMETER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user