mirror of
https://github.com/spring-projects/spring-data-jpa.git
synced 2024-10-23 08:34:54 +08:00
Fix retrieval of multiple OUT parameters from a stored procedure returning also a ResultSet
.
Closes #2381
This commit is contained in:
parent
e5ec3ad418
commit
b454c70131
@ -23,6 +23,7 @@ import jakarta.persistence.StoredProcedureQuery;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
@ -339,6 +340,7 @@ public abstract class JpaQueryExecution {
|
||||
|
||||
StoredProcedureJpaQuery query = (StoredProcedureJpaQuery) jpaQuery;
|
||||
StoredProcedureQuery procedure = query.createQuery(accessor);
|
||||
Class<?> returnType = query.getQueryMethod().getReturnType();
|
||||
|
||||
try {
|
||||
|
||||
@ -350,7 +352,9 @@ public abstract class JpaQueryExecution {
|
||||
throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION);
|
||||
}
|
||||
|
||||
return collectionQuery ? procedure.getResultList() : procedure.getSingleResult();
|
||||
if (!Map.class.isAssignableFrom(returnType)) {
|
||||
return collectionQuery ? procedure.getResultList() : procedure.getSingleResult();
|
||||
}
|
||||
}
|
||||
|
||||
return query.extractOutputValue(procedure);
|
||||
|
@ -15,14 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import jakarta.persistence.StoredProcedureQuery;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import jakarta.persistence.StoredProcedureQuery;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@ -138,7 +139,12 @@ class StoredProcedureAttributes {
|
||||
if (getOutputProcedureParameters().isEmpty())
|
||||
return false;
|
||||
|
||||
Class<?> outputType = getOutputProcedureParameters().get(0).getType();
|
||||
return !(void.class.equals(outputType) || Void.class.equals(outputType));
|
||||
for (ProcedureParameter parameter : getOutputProcedureParameters()) {
|
||||
if (!ClassUtils.isVoidType(parameter.getType())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ import org.testcontainers.containers.PostgreSQLContainer;
|
||||
* @author Greg Turnquist
|
||||
* @author Yanming Zhou
|
||||
* @author Thorben Janssen
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Transactional
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ -140,7 +141,7 @@ class PostgresStoredProcedureIntegrationTests {
|
||||
new Employee(4, "Gabriel"));
|
||||
}
|
||||
|
||||
@Test // 3460
|
||||
@Test // GH-3460
|
||||
void testPositionalInOutParameter() {
|
||||
|
||||
Map results = repository.positionalInOut(1, 2);
|
||||
@ -149,6 +150,15 @@ class PostgresStoredProcedureIntegrationTests {
|
||||
assertThat(results.get("3")).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test // GH-3460
|
||||
void supportsMultipleOutParameters() {
|
||||
|
||||
Map<String, Object> results = repository.multiple_out(5);
|
||||
|
||||
assertThat(results).containsEntry("result1", 5).containsEntry("result2", 10);
|
||||
assertThat(results).containsKey("some_cursor");
|
||||
}
|
||||
|
||||
@Entity
|
||||
@NamedStoredProcedureQuery( //
|
||||
name = "get_employees_postgres", //
|
||||
@ -160,6 +170,13 @@ class PostgresStoredProcedureIntegrationTests {
|
||||
name = "Employee.noResultSet", //
|
||||
procedureName = "get_employees_count", //
|
||||
parameters = { @StoredProcedureParameter(mode = ParameterMode.OUT, name = "results", type = Integer.class) })
|
||||
@NamedStoredProcedureQuery( //
|
||||
name = "Employee.multiple_out", //
|
||||
procedureName = "multiple_out", //
|
||||
parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "someNumber", type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, name = "some_cursor", type = void.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "result1", type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "result2", type = Integer.class) })
|
||||
@NamedStoredProcedureQuery( //
|
||||
name = "positional_inout", //
|
||||
procedureName = "positional_inout_parameter_issue3460", //
|
||||
@ -243,6 +260,9 @@ class PostgresStoredProcedureIntegrationTests {
|
||||
@Procedure(value = "get_employees_count")
|
||||
Integer noResultSet();
|
||||
|
||||
@Procedure(value = "multiple_out")
|
||||
Map<String, Object> multiple_out(int someNumber);
|
||||
|
||||
@Procedure(name = "get_employees_postgres", refCursor = true)
|
||||
List<Employee> entityListFromNamedProcedure();
|
||||
|
||||
|
@ -51,4 +51,17 @@ $BODY$
|
||||
BEGIN
|
||||
outParam = 3;
|
||||
END;
|
||||
$BODY$;;
|
||||
$BODY$;;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE multiple_out(IN someNumber integer, OUT some_cursor REFCURSOR,
|
||||
OUT result1 integer, OUT result2 integer)
|
||||
LANGUAGE 'plpgsql'
|
||||
AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
result1 = 1 * someNumber;
|
||||
result2 = 2 * someNumber;
|
||||
|
||||
OPEN some_cursor FOR SELECT COUNT(*) FROM employee;
|
||||
END;
|
||||
$BODY$;;
|
||||
|
@ -94,4 +94,28 @@ Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);
|
||||
If the stored procedure getting called has a single out parameter that parameter may be returned as the return value of the method.
|
||||
If there are multiple out parameters specified in a `@NamedStoredProcedureQuery` annotation those can be returned as a `Map` with the key being the parameter name given in the `@NamedStoredProcedureQuery` annotation.
|
||||
|
||||
NOTE: Note that if the stored procedure returns a `ResultSet` then any `OUT` parameters are omitted as Java can only return a single method return value.
|
||||
NOTE: Note that if the stored procedure returns a `ResultSet` then any `OUT` parameters are omitted as Java can only return a single method return value unless the method declares a `Map` return type.
|
||||
|
||||
The following example shows how to obtain multiple `OUT` parameters if the stored procedure has multiple `OUT` parameters and is registered as `@NamedStoredProcedureQuery`. `@NamedStoredProcedureQuery` registration is required to provide parameter metadata.
|
||||
|
||||
.StoredProcedure metadata definitions on an entity.
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Entity
|
||||
@NamedStoredProcedureQuery(name = "User.multiple_out_parameters", procedureName = "multiple_out_parameters", parameters = {
|
||||
@StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, name = "some_cursor", type = void.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) })
|
||||
public class User {}
|
||||
----
|
||||
====
|
||||
|
||||
.Returning multiple OUT parameters
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Procedure(name = "User.multiple_out_parameters")
|
||||
Map<String, Object> returnsMultipleOutParameters(@Param("arg") Integer arg);
|
||||
----
|
||||
====
|
||||
|
Loading…
Reference in New Issue
Block a user