From 99d537402ba4d8bb9adf54fa49437a40017ca2e6 Mon Sep 17 00:00:00 2001 From: JyotirmoyVS Date: Tue, 23 Jul 2019 11:44:50 +0530 Subject: [PATCH] DATAJPA-1579 - Support multiple out parameters with nullable output data ad hoc stored procedure. Original pull request: #390. --- .../query/StoredProcedureJpaQuery.java | 12 +++++++----- .../data/jpa/domain/sample/User.java | 7 ++++++- ...epositoryStoredProcedureIntegrationTests.java | 16 +++++++++++++--- .../jpa/repository/sample/UserRepository.java | 12 ++++++++++-- .../scripts/schema-stored-procedures.sql | 7 +++++++ 5 files changed, 43 insertions(+), 11 deletions(-) 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 1724bc2a9..4cf2bb5cb 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 @@ -28,6 +28,7 @@ import javax.persistence.ParameterMode; import javax.persistence.StoredProcedureQuery; import javax.persistence.TypedQuery; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -41,6 +42,7 @@ import java.util.stream.IntStream; * @author Jens Schauder * @author Mark Paluch * @author Jeff Sheets + * @author JyotirmoyVS * @since 1.6 */ class StoredProcedureJpaQuery extends AbstractJpaQuery { @@ -110,7 +112,7 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { * * @param storedProcedureQuery must not be {@literal null}. *

- * Result is either a single value, or a Map of output parameter names to values + * Result is either a single value, or a Map> of output parameter names to nullable values */ @Nullable Object extractOutputValue(StoredProcedureQuery storedProcedureQuery) { @@ -121,21 +123,21 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { return null; } - Map outputValues = IntStream.range(0, procedureAttributes.getOutputParameterNames().size()) // + Map> outputValues = IntStream.range(0, procedureAttributes.getOutputParameterNames().size()) // .boxed() // .collect(Collectors.toMap( // procedureAttributes.getOutputParameterNames()::get, // i -> extractOutputParameter(storedProcedureQuery, i))); - return outputValues.size() == 1 ? outputValues.values().iterator().next() : outputValues; + return outputValues.size() == 1 ? outputValues.values().iterator().next().orElse(null) : outputValues; } - private Object extractOutputParameter(StoredProcedureQuery storedProcedureQuery, Integer index) { + private Optional extractOutputParameter(StoredProcedureQuery storedProcedureQuery, Integer index) { String outputParameterName = procedureAttributes.getOutputParameterNames().get(index); JpaParameters parameters = getQueryMethod().getParameters(); - return extractOutputParameterValue(storedProcedureQuery, outputParameterName, index, parameters.getNumberOfParameters()); + return Optional.ofNullable(extractOutputParameterValue(storedProcedureQuery, outputParameterName, index, parameters.getNumberOfParameters())); } /** diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/src/test/java/org/springframework/data/jpa/domain/sample/User.java index 5179446c0..734b2c65f 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/User.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/User.java @@ -59,6 +59,7 @@ import javax.persistence.TemporalType; * @author Christoph Strobl * @author Jens Schauder * @author Jeff Sheets + * @author JyotirmoyVS */ @Entity @NamedEntityGraphs({ @NamedEntityGraph(name = "User.overview", attributeNodes = { @NamedAttributeNode("roles") }), @@ -97,7 +98,11 @@ import javax.persistence.TemporalType; @NamedStoredProcedureQuery(name = "User.plus1IO2", procedureName = "plus1inout2", parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class), @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class), - @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res2", type = Integer.class) }) // + @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res2", type = Integer.class) }), // + @NamedStoredProcedureQuery(name = "User.plus1IOoptional", procedureName = "plus1inoutoptional", + parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class), + @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class), + @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res2", type = Integer.class) }) // DATAJPA-1579 }) @NamedStoredProcedureQuery(name = "User.plus1IO", procedureName = "plus1inout", parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class), diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java index 0809bd99b..722b53204 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java @@ -29,6 +29,7 @@ import javax.persistence.ParameterMode; import javax.persistence.PersistenceContext; import javax.persistence.StoredProcedureQuery; import java.util.Map; +import java.util.Optional; import static org.assertj.core.api.Assertions.*; @@ -39,6 +40,7 @@ import static org.assertj.core.api.Assertions.*; * @author Oliver Gierke * @author Jeff Sheets * @author Jens Schauder + * @author JyotirmoyVS * @since 1.6 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -90,12 +92,20 @@ public class UserRepositoryStoredProcedureIntegrationTests { assertThat(repository.entityAnnotatedCustomNamedProcedurePlus1IO2TwoOutParamsButNamingOne(1)).isEqualTo(3); } - @Test // DATAJPA-707 + @Test // DATAJPA-707 DATAJPA-1579 public void entityAnnotatedCustomNamedProcedurePlus1IO2() { - Map result = repository.entityAnnotatedCustomNamedProcedurePlus1IO2(1); + Map> result = repository.entityAnnotatedCustomNamedProcedurePlus1IO2(1); - assertThat(result).containsExactly(entry("res", 2), entry("res2", 3)); + assertThat(result).containsExactly(entry("res", Optional.of(2)), entry("res2", Optional.of(3))); + } + + @Test // DATAJPA-1579 + public void entityAnnotatedCustomNamedProcedurePlus1IOoptional() { + + Map> result = repository.entityAnnotatedCustomNamedProcedurePlus1IOoptional(1); + + assertThat(result).containsExactly(entry("res", Optional.of(2)), entry("res2", Optional.empty())); } @Test // DATAJPA-455 diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 1ba992337..577373391 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -52,6 +52,7 @@ import com.google.common.base.Optional; * @author Kevin Peters * @author Jeff Sheets * @author Andrey Kovalev + * @author JyotirmoyVS */ public interface UserRepository extends JpaRepository, JpaSpecificationExecutor, UserRepositoryCustom { @@ -351,8 +352,15 @@ public interface UserRepository * Explicitly mapped to named stored procedure "User.plus1IO2" in {@link EntityManager}. * Returns 2 out params as a Map. */ - @Procedure(name = "User.plus1IO2") // DATAJPA-707 - Map entityAnnotatedCustomNamedProcedurePlus1IO2(@Param("arg") Integer arg); + @Procedure(name = "User.plus1IO2") // DATAJPA-707 DATAJPA-1579 + Map> entityAnnotatedCustomNamedProcedurePlus1IO2(@Param("arg") Integer arg); + + /** + * Explicitly mapped to named stored procedure "User.plus1IOoptional" in {@link EntityManager}. + * Returns 2 out params as a Map, second one amoung which is null. + */ + @Procedure(name = "User.plus1IOoptional") // DATAJPA-1579 + Map> entityAnnotatedCustomNamedProcedurePlus1IOoptional(@Param("arg") Integer arg); /** * Implicitly mapped to named stored procedure "User.plus1" in {@link EntityManager}. diff --git a/src/test/resources/scripts/schema-stored-procedures.sql b/src/test/resources/scripts/schema-stored-procedures.sql index 12433eb92..859133de0 100644 --- a/src/test/resources/scripts/schema-stored-procedures.sql +++ b/src/test/resources/scripts/schema-stored-procedures.sql @@ -14,6 +14,13 @@ BEGIN ATOMIC set res2 = arg + 2; END /; +DROP procedure IF EXISTS plus1inoutoptional +/; +CREATE procedure plus1inoutoptional (IN arg int, OUT res int, OUT res2 int) +BEGIN ATOMIC + set res = arg + 1; +END +/; DROP procedure IF EXISTS procedure_in1_out1 /; DROP procedure IF EXISTS procedure_in1_out0