diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java
index d1997dc88a..1ac44cb03c 100644
--- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java
+++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java
@@ -53,6 +53,7 @@
  * @author Christoph Strobl
  * @author Nicolas Cirigliano
  * @author Jens Schauder
+ * @author Gabriel Basilio
  */
 public abstract class JpaQueryExecution {
 
@@ -300,6 +301,8 @@ protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccesso
 	 */
 	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[])
@@ -311,7 +314,24 @@ protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAcce
 
 			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);
 		}
diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java b/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java
index f740c59892..fef0ac184b 100644
--- a/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java
+++ b/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java
@@ -26,6 +26,7 @@
  * @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 @@
 	 * 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;
 }
diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java b/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java
new file mode 100644
index 0000000000..6d185e5025
--- /dev/null
+++ b/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java
@@ -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;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java
index 84dac1e123..05da37fe34 100644
--- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java
+++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java
@@ -22,6 +22,7 @@
 
 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 @@
  * @author Mark Paluch
  * @author Diego Diez
  * @author Jeff Sheets
+ * @author Gabriel Basilio
  * @since 1.6
  */
 enum StoredProcedureAttributeSource {
@@ -72,7 +74,7 @@ public StoredProcedureAttributes createFrom(Method method, JpaEntityMetadata<?>
 					+ method);
 		}
 
-		return new StoredProcedureAttributes(procedureName, procedure.outputParameterName(), method.getReturnType());
+		return new StoredProcedureAttributes(procedureName, createOutputProcedureParameterFrom(method, procedure));
 	}
 
 	/**
@@ -102,28 +104,29 @@ private String deriveProcedureNameFrom(Method method, Procedure procedure) {
 	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) {
diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java
index 4699f0376a..2c4c751840 100644
--- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java
+++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java
@@ -33,6 +33,7 @@
  * @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;
-	}
 
-	private List<String> completeOutputParameterNames(List<String> outputParameterNames) {
+		if (namedStoredProcedure) {
+			this.outputProcedureParameters = outputProcedureParameters;
+		} else {
+			this.outputProcedureParameters = getParametersWithCompletedNames(outputProcedureParameters);
+		}
+	}
 
-		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,26 +113,15 @@ public String getProcedureName() {
 	 *
 	 * @return
 	 */
-	public List<String> getOutputParameterNames() {
-		return outputParameterNames;
-	}
-
-	/**
-	 * Returns the types of the output parameters.
-	 *
-	 * @return
-	 */
-	public List<Class<?>> getOutputParameterTypes() {
-		return outputParameterTypes;
+	public boolean isNamedStoredProcedure() {
+		return namedStoredProcedure;
 	}
 
 	/**
-	 * Returns whether the stored procedure is a named one.
-	 *
-	 * @return
+	 * @return Returns the stored procedure output parameter list
 	 */
-	public boolean isNamedStoredProcedure() {
-		return namedStoredProcedure;
+	public List<ProcedureParameter> getOutputProcedureParameters() {
+		return outputProcedureParameters;
 	}
 
 	/**
@@ -141,6 +130,10 @@ public boolean isNamedStoredProcedure() {
 	 * @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));
 	}
 }
diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java
index b460da57fc..f4a931292a 100644
--- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java
+++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java
@@ -23,6 +23,7 @@
 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 @@ Object extractOutputValue(StoredProcedureQuery storedProcedureQuery) {
 			return null;
 		}
 
-		Map<String, Object> outputValues = new HashMap<>();
-		List<String> parameterNames = procedureAttributes.getOutputParameterNames();
-
-		for (int i = 0; i < parameterNames.size(); i++) {
+		List<ProcedureParameter> outputParameters = procedureAttributes.getOutputProcedureParameters();
 
-			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;
-	}
-
-	private Object extractOutputParameter(StoredProcedureQuery storedProcedureQuery, Integer index) {
+		Map<String, Object> outputValues = new HashMap<>();
 
-		String outputParameterName = procedureAttributes.getOutputParameterNames().get(index);
-		JpaParameters parameters = getQueryMethod().getParameters();
+		for (int i = 0; i < outputParameters.size(); i++) {
+			ProcedureParameter outputParameter = outputParameters.get(i);
+			outputValues.put(outputParameter.getName(), extractOutputParameterValue(outputParameter, i, storedProcedureQuery));
+		}
 
-		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) {
+
+		JpaParameters methodParameters = getQueryMethod().getParameters();
 
-		return useNamedParameters && StringUtils.hasText(name) ? //
-				storedProcedureQuery.getOutputParameterValue(name)
-				: storedProcedureQuery.getOutputParameterValue(offset + index + 1);
+		return useNamedParameters && StringUtils.hasText(outputParameter.getName()) ?
+				storedProcedureQuery.getOutputParameterValue(outputParameter.getName())
+				: storedProcedureQuery.getOutputParameterValue(methodParameters.getNumberOfParameters() + index + 1);
 	}
 
 	/**
@@ -192,9 +183,7 @@ private StoredProcedureQuery newNamedStoredProcedureQuery() {
 	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 @@ private StoredProcedureQuery newAdhocStoredProcedureQuery() {
 
 		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();
+	}
 }
diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java
index 03ec571dbf..437e055bb9 100644
--- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java
+++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java
@@ -38,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.Procedure;
 import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory;
 import org.springframework.data.jpa.util.JpaMetamodel;
 import org.springframework.data.projection.ProjectionFactory;
@@ -97,7 +98,7 @@ public JpaRepositoryFactory(EntityManager entityManager) {
 		addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor);
 		addRepositoryProxyPostProcessor((factory, repositoryInformation) -> {
 
-			if (hasMethodReturningStream(repositoryInformation.getRepositoryInterface())) {
+			if (isTransactionNeeded(repositoryInformation.getRepositoryInterface())) {
 				factory.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE);
 			}
 		});
@@ -259,12 +260,13 @@ protected RepositoryComposition.RepositoryFragments getRepositoryFragments(Repos
 		return fragments;
 	}
 
-	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;
 			}
 		}
diff --git a/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java
index 1901d7107a..4331f7d47a 100644
--- a/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java
@@ -46,6 +46,7 @@
  * @author Thomas Darimont
  * @author Oliver Gierke
  * @author Jens Schauder
+ * @author Gabriel Basilio
  * @see scripts/schema-stored-procedures.sql for procedure definitions.
  */
 @Transactional
@@ -53,7 +54,7 @@
 @RunWith(SpringJUnit4ClassRunner.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;
diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java
index 347249b77a..01a91fd18e 100644
--- a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java
@@ -21,14 +21,17 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 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;
 
 import javax.persistence.EntityManager;
+import javax.persistence.ParameterMode;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.Method;
+import java.util.List;
 import java.util.Map;
 
 import static org.assertj.core.api.Assertions.*;
@@ -43,6 +46,7 @@
  * @author Diego Diez
  * @author Jeff Sheets
  * @author Jens Schauder
+ * @author Gabriel Basilio
  * @since 1.6
  */
 @RunWith(MockitoJUnitRunner.class)
@@ -65,10 +69,11 @@ public void setup() {
 	public 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
@@ -77,9 +82,11 @@ public void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictN
 		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
@@ -88,9 +95,11 @@ public void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictP
 		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
@@ -99,9 +108,11 @@ public void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictP
 		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
@@ -110,9 +121,11 @@ public void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictP
 		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
@@ -121,9 +134,11 @@ public void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithEx
 		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
@@ -132,9 +147,11 @@ public void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithEx
 		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
@@ -143,11 +160,18 @@ public void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithEx
 		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
@@ -155,9 +179,11 @@ public void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithIm
 
 		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
@@ -166,9 +192,11 @@ public void aliasedStoredProcedure() {
 		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
@@ -177,9 +205,104 @@ public void aliasedStoredProcedure2() {
 		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) {
@@ -255,6 +378,34 @@ interface DummyRepository {
 
 		@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")
diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java
index 257b2b67ec..a2981bdf82 100644
--- a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java
@@ -20,6 +20,8 @@
 
 import org.junit.Test;
 
+import javax.persistence.ParameterMode;
+
 /**
  * Unit tests for {@link StoredProcedureAttributes}.
  *
@@ -31,7 +33,8 @@ public class StoredProcedureAttributesUnitTests {
 	@Test // DATAJPA-681
 	public 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);
 	}
 }