From 049fc4535fa4b1273f84cf59e9d75561993f82da Mon Sep 17 00:00:00 2001 From: Thomas Darimont <tdarimont@pivotal.io> Date: Wed, 14 Jan 2015 09:13:57 +0100 Subject: [PATCH 1/3] DATAJPA-652 - Provide support for ParameterMode.REF_CURSOR. Prepare issue branch. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bd4b95e11a..d503670b70 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> - <version>1.8.0.BUILD-SNAPSHOT</version> + <version>1.8.0.DATAJPA-652-SNAPSHOT</version> <name>Spring Data JPA</name> <description>Spring Data module for JPA repositories.</description> From 943337dad13733c8f4c53b7d3f949b6312da44a0 Mon Sep 17 00:00:00 2001 From: Thomas Darimont <tdarimont@pivotal.io> Date: Fri, 16 Jan 2015 10:01:31 +0100 Subject: [PATCH 2/3] DATAJPA-652 - Added support for REF_CURSOR output parameters for procedures. We now support detecting output parameters for stored-procedures with ParameterMode.REF_CURSOR. Previously we only considered parameters with OUT or INOUT mode as output parameters. Note that since it is a bit tricky to setup a proper test case with hsql / eclipselink, I had to verify this with a standalone spring boot app: https://gist.github.com/thomasdarimont/129bc15d0ccc459610c2 A proper test case will follow. Original pull request: #130. --- .../query/StoredProcedureAttributeSource.java | 62 ++++++++++++------- .../data/jpa/domain/sample/User.java | 6 +- .../UserRepositoryStoredProcedureTests.java | 15 ++++- .../jpa/repository/sample/UserRepository.java | 10 ++- .../scripts/schema-stored-procedures.sql | 8 +++ 5 files changed, 75 insertions(+), 26 deletions(-) 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 98035fd033..47fef757c4 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 @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -57,7 +57,7 @@ public StoredProcedureAttributes createFrom(Method method, JpaEntityMetadata<?> procedure); if (namedStoredProc != null) { - return newProcedureAttributesFrom(method, namedStoredProc); + return newProcedureAttributesFrom(method, namedStoredProc, procedure); } String procedureName = deriveProcedureNameFrom(method, procedure); @@ -90,31 +90,55 @@ private String deriveProcedureNameFrom(Method method, Procedure procedure) { /** * @param method * @param namedStoredProc + * @param procedure * @return */ - private StoredProcedureAttributes newProcedureAttributesFrom(Method method, NamedStoredProcedureQuery namedStoredProc) { + private StoredProcedureAttributes newProcedureAttributesFrom(Method method, + NamedStoredProcedureQuery namedStoredProc, Procedure procedure) { String outputParameterName = null; Class<?> outputParameterType = null; - int outputParameterCount = 0; + if (!procedure.outputParameterName().isEmpty()) { + + // we give the output parameter definition from the @Procedure annotation precedence + outputParameterName = procedure.outputParameterName(); + } else { + + // try to discover the output parameter + List<StoredProcedureParameter> outputParameters = extractOutputParametersFrom(namedStoredProc); + + if (outputParameters.size() != 1 && !void.class.equals(method.getReturnType())) { + throw new IllegalStateException(String.format( + "Could not create ProcedureAttributes from %s. We currently support exactly one output parameter!", method)); + } + + if (!outputParameters.isEmpty()) { + StoredProcedureParameter outputParameter = outputParameters.get(0); + outputParameterName = outputParameter.name(); + outputParameterType = outputParameter.type(); + } + } + + if (outputParameterType == null || Object.class.equals(outputParameterType) + || void.class.equals(outputParameterType)) { + outputParameterType = method.getReturnType(); + } + + return new StoredProcedureAttributes(namedStoredProc.name(), outputParameterName, outputParameterType, true); + } + + private List<StoredProcedureParameter> extractOutputParametersFrom(NamedStoredProcedureQuery namedStoredProc) { + + List<StoredProcedureParameter> outputParameters = new ArrayList<StoredProcedureParameter>(); for (StoredProcedureParameter param : namedStoredProc.parameters()) { + switch (param.mode()) { case OUT: case INOUT: - - if (outputParameterCount > 0) { - throw new IllegalStateException( - String.format( - "Could not create ProcedureAttributes from %s. We currently support only one output parameter!", - method)); - } - - outputParameterName = param.name(); - outputParameterType = param.type(); - - outputParameterCount++; + case REF_CURSOR: + outputParameters.add(param); break; case IN: default: @@ -122,11 +146,7 @@ private StoredProcedureAttributes newProcedureAttributesFrom(Method method, Name } } - if (outputParameterType == null) { - outputParameterType = method.getReturnType(); - } - - return new StoredProcedureAttributes(namedStoredProc.name(), outputParameterName, outputParameterType, true); + return outputParameters; } /** 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 94c7e213ca..3ca89ed801 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 @@ -1,5 +1,5 @@ /* - * Copyright 2008-2014 the original author or authors. + * Copyright 2008-2015 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. @@ -84,7 +84,7 @@ public class User { @Lob private byte[] binaryData; @ElementCollection private Set<String> attributes; - + @Temporal(TemporalType.DATE) private Date dateOfBirth; /** @@ -367,7 +367,7 @@ public Set<String> getAttributes() { public void setAttributes(Set<String> attributes) { this.attributes = attributes; } - + public Date getDateOfBirth() { return dateOfBirth; } diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureTests.java index 6415df777a..92431721d1 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -118,4 +118,17 @@ public void plainJpa21_entityAnnotatedCustomNamedProcedurePlus1IO() { assertThat(proc.getOutputParameterValue("res"), is((Object) 2)); } + + /** + * @see DATAJPA-652 + */ + @Test + public void executesProcedureWithNoOutput() { + + assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + + repository.executeNoOutputProcedure(1); + + assertTrue(true); + } } 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 b9f2ce83a8..ff8fed3e27 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 @@ -1,5 +1,5 @@ /* - * Copyright 2008-2014 the original author or authors. + * Copyright 2008-2015 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. @@ -549,4 +549,12 @@ List<User> findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity * DATAJPA-606 */ List<User> queryByAgeInOrFirstname(Integer[] ages, String firstname); + + /** + * Explicitly mapped to a procedure with name "nooutput" in database. + * + * @see DATAJPA-652 + */ + @Procedure(procedureName = "nooutput") + void executeNoOutputProcedure(Integer arg); } diff --git a/src/test/resources/scripts/schema-stored-procedures.sql b/src/test/resources/scripts/schema-stored-procedures.sql index dee6b566c1..3fa8acaff4 100644 --- a/src/test/resources/scripts/schema-stored-procedures.sql +++ b/src/test/resources/scripts/schema-stored-procedures.sql @@ -5,4 +5,12 @@ CREATE procedure plus1inout (IN arg int, OUT res int) BEGIN ATOMIC set res = arg + 1; END +/; +DROP procedure IF EXISTS nooutput +/; +CREATE procedure nooutput (IN arg int) +BEGIN ATOMIC + declare res int; + set res = arg + 1; +END /; \ No newline at end of file From 2d2fc8071cf2eae5844d3a19395fccee9a3c4052 Mon Sep 17 00:00:00 2001 From: Thomas Darimont <tdarimont@pivotal.io> Date: Wed, 21 Jan 2015 16:48:53 +0100 Subject: [PATCH 3/3] DATAJPA-652 - Added support for REF_CURSOR output parameters for procedures. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now support detecting output parameters for stored-procedures with ParameterMode.REF_CURSOR. Previously we only considered parameters with OUT or INOUT mode as output parameters. Added additional test cases for various procedure definition options. Introduced new Dummy test type to avoid polluting the User test type anymore with additional procedure definitions. Added test for eclipse link and openjpa but I had to deactivate them since they currently need to be run with hsqldb v1 which doesn’t support stored procedures. --- .../data/jpa/domain/sample/Dummy.java | 116 ++++++++++ ...seLinkStoredProcedureIntegrationTests.java | 28 +++ ...penJpaStoredProcedureIntegrationTests.java | 31 +++ .../StoredProcedureIntegrationTests.java | 214 ++++++++++++++++++ .../UserRepositoryStoredProcedureTests.java | 13 -- .../repository/sample/DummyRepository.java | 63 ++++++ .../jpa/repository/sample/UserRepository.java | 8 - .../jpa/support/EntityManagerTestUtils.java | 6 +- src/test/resources/META-INF/persistence.xml | 5 + src/test/resources/META-INF/persistence2.xml | 4 +- .../scripts/schema-stored-procedures.sql | 67 +++++- 11 files changed, 528 insertions(+), 27 deletions(-) create mode 100644 src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java create mode 100644 src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java create mode 100644 src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java create mode 100644 src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java create mode 100644 src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java b/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java new file mode 100644 index 0000000000..ead55531d2 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java @@ -0,0 +1,116 @@ +/* + * Copyright 2015 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 + * + * http://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.domain.sample; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.NamedStoredProcedureQueries; +import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.ParameterMode; +import javax.persistence.StoredProcedureParameter; + +import org.springframework.util.ObjectUtils; + +/** + * Sample domain class representing used for Stored Procedure tests. + * + * @author Thomas Darimont + */ +@Entity +@NamedStoredProcedureQueries({ // + @NamedStoredProcedureQuery(name = "Dummy.procedureWith1InputAnd1OutputParameter", + procedureName = "procedure_in1_out1", parameters = { + @StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class), + @StoredProcedureParameter(mode = ParameterMode.OUT, type = Integer.class) }) // + , + @NamedStoredProcedureQuery(name = "Dummy.procedureWith1InputAndNoOutputParameter", + procedureName = "procedure_in1_out0", parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, + type = Integer.class) }) // + , + @NamedStoredProcedureQuery(name = "Dummy.procedureWithNoInputAnd1OutputParameter", + procedureName = "procedure_in0_out1", parameters = { @StoredProcedureParameter(mode = ParameterMode.OUT, + type = Integer.class) }) // + , + @NamedStoredProcedureQuery(name = "Dummy.procedureWith1InputAnd1OutputParameterWithResultSet", + procedureName = "procedure_in1_out0_return_rs_no_update", parameters = { + @StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class), + @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class) }) // + , + @NamedStoredProcedureQuery(name = "Dummy.procedureWith1InputAnd1OutputParameterWithResultSetWithUpdate", + procedureName = "procedure_in1_out0_return_rs_with_update", parameters = { + @StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class), + @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class) }) // + , + @NamedStoredProcedureQuery(name = "Dummy.procedureWith1InputAndNoOutputParameterWithUpdate", + procedureName = "procedure_in1_out0_no_return_with_update", parameters = { @StoredProcedureParameter( + mode = ParameterMode.IN, type = String.class) }) // +}) +public class Dummy { + + @Id @GeneratedValue private Integer id; + private String name; + + public Dummy() {} + + public Dummy(String name) { + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "Dummy [id=" + id + ", name=" + name + "]"; + } + + @Override + public int hashCode() { + return ObjectUtils.nullSafeHashCode(name); + } + + @Override + public boolean equals(Object that) { + + if (that == this) { + return true; + } + + if (that == null) { + return false; + } + + if (!(that instanceof Dummy)) { + return false; + } + + return ObjectUtils.nullSafeEquals(this.name, ((Dummy) that).name); + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java new file mode 100644 index 0000000000..c23799c387 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java @@ -0,0 +1,28 @@ +/* + * Copyright 2008-2014 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 + * + * http://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; + +import org.springframework.context.annotation.ImportResource; +import org.springframework.test.context.ContextConfiguration; + +/** + * Testcase to run {@link StoredProcedureIntegrationTests} integration tests on top of EclipseLink. + * + * @author Thomas Darimont + */ +@ContextConfiguration(classes = { StoredProcedureIntegrationTests.Config.class }) +@ImportResource("classpath:eclipselink.xml") +public class EclipseLinkStoredProcedureIntegrationTests extends StoredProcedureIntegrationTests {} diff --git a/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java new file mode 100644 index 0000000000..00c24ccf1c --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java @@ -0,0 +1,31 @@ +/* + * Copyright 2015 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 + * + * http://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; + +import org.junit.Ignore; +import org.springframework.context.annotation.ImportResource; +import org.springframework.test.context.ContextConfiguration; + +/** + * Test case to run {@link StoredProcedureIntegrationTests} integration tests on top of OpenJpa. This is currently not + * supported since, the OpenJPA tests need to be executed with hsqldb1 which doesn't supported stored procedures. + * + * @author Thomas Darimont + */ +@Ignore +@ContextConfiguration(classes = { StoredProcedureIntegrationTests.Config.class }) +@ImportResource("classpath:openjpa.xml") +public class OpenJpaStoredProcedureIntegrationTests extends StoredProcedureIntegrationTests {} diff --git a/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java new file mode 100644 index 0000000000..cce06e9ba7 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java @@ -0,0 +1,214 @@ +/* + * Copyright 2015 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 + * + * http://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; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.junit.Assume.*; +import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.ImportResource; +import org.springframework.data.jpa.domain.sample.Dummy; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.jpa.repository.sample.DummyRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * @see scripts/schema-stored-procedures.sql for procedure definitions. + * @author Thomas Darimont + */ +@Transactional +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class StoredProcedureIntegrationTests { + + @PersistenceContext EntityManager em; + @Autowired DummyRepository repository; + + Dummy dummyA; + Dummy dummyB; + Dummy dummyC; + + @Configuration + @EnableJpaRepositories(basePackageClasses = DummyRepository.class, includeFilters = { @Filter( + pattern = ".*DummyRepository", type = FilterType.REGEX) }) + @ImportResource("classpath:infrastructure.xml") + static class Config {} + + @Before + public void setup() { + + assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + + dummyA = em.merge(new Dummy("A")); + dummyB = em.merge(new Dummy("B")); + dummyC = em.merge(new Dummy("C")); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteAdHocProcedureWithNoInputAnd1OutputParameter() { + assertThat(repository.adHocProcedureWithNoInputAnd1OutputParameter(), is(equalTo(42))); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteAdHocProcedureWith1InputAnd1OutputParameter() { + assertThat(repository.adHocProcedureWith1InputAnd1OutputParameter(23), is(equalTo(24))); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteAdHocProcedureWith1InputAndNoOutputParameter() { + + repository.adHocProcedureWith1InputAndNoOutputParameter(42); + + assertTrue(true); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteAdHocProcedureWith1InputAnd1OutputParameterWithResultSet() { + + // hibernate currently (v4.3) doesn't support returning ResultSets in output parameters + assumeFalse(currentEntityManagerIsHibernateEntityManager(em)); + + List<Dummy> dummies = repository.adHocProcedureWith1InputAnd1OutputParameterWithResultSet("FOO"); + + System.out.println("### Found dummies: " + dummies); + + assertThat(dummies, is(notNullValue())); + assertThat(dummies.size(), is(equalTo(3))); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteAdHocProcedureWith1InputAnd1OutputParameterWithResultSetWithUpdate() { + + // hibernate currently (v4.3) doesn't support returning ResultSets in output parameters + assumeFalse(currentEntityManagerIsHibernateEntityManager(em)); + + List<Dummy> dummies = repository.adHocProcedureWith1InputAnd1OutputParameterWithResultSetWithUpdate("FOO"); + + System.out.println("### Found dummies: " + dummies); + + assertThat(dummies, is(notNullValue())); + assertThat(dummies.size(), is(equalTo(3))); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteAdHocProcedureWith1InputAnd1OutputParameterWithUpdate() { + + repository.adHocProcedureWith1InputAndNoOutputParameterWithUpdate("FOO"); + + assertTrue(true); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteProcedureWithNoInputAnd1OutputParameter() { + assertThat(repository.procedureWithNoInputAnd1OutputParameter(), is(equalTo(42))); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteProcedureWith1InputAnd1OutputParameter() { + assertThat(repository.procedureWith1InputAnd1OutputParameter(23), is(equalTo(24))); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteProcedureWith1InputAndNoOutputParameter() { + + repository.procedureWith1InputAndNoOutputParameter(42); + + assertTrue(true); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteProcedureWith1InputAnd1OutputParameterWithResultSet() { + + // hibernate currently (v4.3) doesn't support returning ResultSets in output parameters + assumeFalse(currentEntityManagerIsHibernateEntityManager(em)); + + List<Dummy> dummies = repository.procedureWith1InputAnd1OutputParameterWithResultSet("FOO"); + + assertThat(dummies, is(notNullValue())); + assertThat(dummies.size(), is(equalTo(3))); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteProcedureWith1InputAnd1OutputParameterWithResultSetWithUpdate() { + + // hibernate currently (v4.3) doesn't support returning ResultSets in output parameters + assumeFalse(currentEntityManagerIsHibernateEntityManager(em)); + + List<Dummy> dummies = repository.procedureWith1InputAnd1OutputParameterWithResultSetWithUpdate("FOO"); + + assertThat(dummies, is(notNullValue())); + assertThat(dummies.size(), is(equalTo(3))); + } + + /** + * @see DATAJPA-652 + */ + @Test + public void shouldExecuteProcedureWith1InputAnd1OutputParameterWithUpdate() { + + repository.procedureWith1InputAndNoOutputParameterWithUpdate("FOO"); + + assertTrue(true); + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureTests.java index 92431721d1..163a297094 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureTests.java @@ -118,17 +118,4 @@ public void plainJpa21_entityAnnotatedCustomNamedProcedurePlus1IO() { assertThat(proc.getOutputParameterValue("res"), is((Object) 2)); } - - /** - * @see DATAJPA-652 - */ - @Test - public void executesProcedureWithNoOutput() { - - assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); - - repository.executeNoOutputProcedure(1); - - assertTrue(true); - } } diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java new file mode 100644 index 0000000000..67c98399ca --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015 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 + * + * http://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.sample; + +import java.util.List; + +import org.springframework.data.jpa.domain.sample.Dummy; +import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.repository.CrudRepository; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public interface DummyRepository extends CrudRepository<Dummy, Long> { + + @Procedure("procedure_in1_out1") + Integer adHocProcedureWith1InputAnd1OutputParameter(Integer in); + + @Procedure("procedure_in1_out0") + void adHocProcedureWith1InputAndNoOutputParameter(Integer in); + + @Procedure("procedure_in0_out1") + Integer adHocProcedureWithNoInputAnd1OutputParameter(); + + @Procedure("procedure_in1_out0_return_rs_no_update") + List<Dummy> adHocProcedureWith1InputAnd1OutputParameterWithResultSet(String in); + + @Procedure("procedure_in1_out0_return_rs_with_update") + List<Dummy> adHocProcedureWith1InputAnd1OutputParameterWithResultSetWithUpdate(String in); + + @Procedure("procedure_in1_out0_no_return_with_update") + void adHocProcedureWith1InputAndNoOutputParameterWithUpdate(String in); + + @Procedure + Integer procedureWith1InputAnd1OutputParameter(Integer in); + + @Procedure + void procedureWith1InputAndNoOutputParameter(Integer in); + + @Procedure + Integer procedureWithNoInputAnd1OutputParameter(); + + @Procedure + List<Dummy> procedureWith1InputAnd1OutputParameterWithResultSet(String in); + + @Procedure + List<Dummy> procedureWith1InputAnd1OutputParameterWithResultSetWithUpdate(String in); + + @Procedure + void procedureWith1InputAndNoOutputParameterWithUpdate(String in); +} 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 ff8fed3e27..0dd9a2e4be 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 @@ -549,12 +549,4 @@ List<User> findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity * DATAJPA-606 */ List<User> queryByAgeInOrFirstname(Integer[] ages, String firstname); - - /** - * Explicitly mapped to a procedure with name "nooutput" in database. - * - * @see DATAJPA-652 - */ - @Procedure(procedureName = "nooutput") - void executeNoOutputProcedure(Integer arg); } diff --git a/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java b/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java index f1797888f9..c9494d573d 100644 --- a/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java +++ b/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -32,4 +32,8 @@ public static boolean currentEntityManagerIsAJpa21EntityManager(EntityManager em return ReflectionUtils.findMethod(((org.springframework.orm.jpa.EntityManagerProxy) em).getTargetEntityManager() .getClass(), "getEntityGraph", String.class) != null; } + + public static boolean currentEntityManagerIsHibernateEntityManager(EntityManager em) { + return em.getDelegate().getClass().getName().toLowerCase().contains("hibernate"); + } } diff --git a/src/test/resources/META-INF/persistence.xml b/src/test/resources/META-INF/persistence.xml index eac9906983..82189b1b0c 100644 --- a/src/test/resources/META-INF/persistence.xml +++ b/src/test/resources/META-INF/persistence.xml @@ -38,6 +38,7 @@ <class>org.springframework.data.jpa.domain.sample.SpecialUser</class> <class>org.springframework.data.jpa.domain.sample.User</class> <class>org.springframework.data.jpa.domain.sample.VersionedUser</class> + <class>org.springframework.data.jpa.domain.sample.Dummy</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> </persistence-unit> <persistence-unit name="querydsl"> @@ -46,6 +47,7 @@ <class>org.springframework.data.jpa.domain.sample.MailSender</class> <class>org.springframework.data.jpa.domain.sample.MailUser</class> <class>org.springframework.data.jpa.domain.sample.User</class> + <class>org.springframework.data.jpa.domain.sample.Dummy</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> </persistence-unit> <persistence-unit name="cdi"> @@ -55,6 +57,7 @@ <class>org.springframework.data.jpa.domain.sample.MailUser</class> <class>org.springframework.data.jpa.domain.sample.User</class> <class>org.springframework.data.jpa.repository.cdi.Person</class> + <class>org.springframework.data.jpa.domain.sample.Dummy</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> <properties> <property name="hibernate.connection.username" value="sa" /> @@ -101,6 +104,7 @@ <class>org.springframework.data.jpa.domain.sample.MailUser</class> <class>org.springframework.data.jpa.domain.sample.User</class> <class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample</class> + <class>org.springframework.data.jpa.domain.sample.Dummy</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> </persistence-unit> <persistence-unit name="metadata_oj"> @@ -111,6 +115,7 @@ <class>org.springframework.data.jpa.domain.sample.MailUser</class> <class>org.springframework.data.jpa.domain.sample.User</class> <class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample</class> + <class>org.springframework.data.jpa.domain.sample.Dummy</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> <properties> <property name="openjpa.jdbc.DBDictionary" value="hsql" /> diff --git a/src/test/resources/META-INF/persistence2.xml b/src/test/resources/META-INF/persistence2.xml index f0d7af10a0..18c3c67b12 100644 --- a/src/test/resources/META-INF/persistence2.xml +++ b/src/test/resources/META-INF/persistence2.xml @@ -15,6 +15,7 @@ <class>org.springframework.data.jpa.domain.sample.Role</class> <class>org.springframework.data.jpa.domain.sample.SpecialUser</class> <class>org.springframework.data.jpa.domain.sample.User</class> + <class>org.springframework.data.jpa.domain.sample.Dummy</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> </persistence-unit> <persistence-unit name="second"> @@ -22,7 +23,7 @@ <class>org.springframework.data.jpa.domain.sample.AuditableUser</class> <class>org.springframework.data.jpa.domain.sample.AuditableRole</class> <class>org.springframework.data.jpa.domain.sample.Category</class> -<class>org.springframework.data.jpa.domain.sample.CustomAbstractPersistable</class> + <class>org.springframework.data.jpa.domain.sample.CustomAbstractPersistable</class> <class>org.springframework.data.jpa.domain.sample.MailMessage</class> <class>org.springframework.data.jpa.domain.sample.MailSender</class> <class>org.springframework.data.jpa.domain.sample.MailUser</class> @@ -30,6 +31,7 @@ <class>org.springframework.data.jpa.domain.sample.Role</class> <class>org.springframework.data.jpa.domain.sample.SpecialUser</class> <class>org.springframework.data.jpa.domain.sample.User</class> + <class>org.springframework.data.jpa.domain.sample.Dummy</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> </persistence-unit> </persistence> diff --git a/src/test/resources/scripts/schema-stored-procedures.sql b/src/test/resources/scripts/schema-stored-procedures.sql index 3fa8acaff4..5e199e3409 100644 --- a/src/test/resources/scripts/schema-stored-procedures.sql +++ b/src/test/resources/scripts/schema-stored-procedures.sql @@ -6,11 +6,70 @@ BEGIN ATOMIC set res = arg + 1; END /; -DROP procedure IF EXISTS nooutput +DROP procedure IF EXISTS procedure_in1_out1 /; -CREATE procedure nooutput (IN arg int) +DROP procedure IF EXISTS procedure_in1_out0 +/; +DROP procedure IF EXISTS procedure_in0_out1 +/; +DROP procedure IF EXISTS procedure_in1_out0_return_rs_no_update +/; +DROP procedure IF EXISTS procedure_in1_out0_return_rs_with_update +/; +DROP procedure IF EXISTS procedure_in1_out0_no_return_with_update +/; + +DROP table dummy if exists +/; +create table dummy (id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name VARCHAR(32)) +/; +insert into public.dummy(name) values ('A') +/; +insert into public.dummy(name) values ('B') +/; +insert into public.dummy(name) values ('C') +/; + +/; +CREATE procedure procedure_in1_out1 (IN arg int, OUT res int) BEGIN ATOMIC - declare res int; - set res = arg + 1; +set res = arg + 1; +END +/; + +CREATE procedure procedure_in1_out0 (IN arg int) +BEGIN ATOMIC +DECLARE res int; +set res = arg + 1; +END +/; + +CREATE procedure procedure_in0_out1 (OUT res int) +BEGIN ATOMIC +set res = 42; +END +/; + +CREATE procedure procedure_in1_out0_return_rs_no_update (IN arg varchar(32)) +READS SQL DATA DYNAMIC RESULT SETS 1 +BEGIN ATOMIC +DECLARE result CURSOR WITH RETURN FOR SELECT * FROM public.dummy FOR READ ONLY; +open result; +END +/; + +CREATE procedure procedure_in1_out0_return_rs_with_update (IN arg varchar(32)) +MODIFIES SQL DATA DYNAMIC RESULT SETS 1 +BEGIN ATOMIC +DECLARE result CURSOR WITH RETURN FOR SELECT * FROM public.dummy FOR READ ONLY; +update public.dummy set name = name; +OPEN result; +END +/; + +CREATE procedure procedure_in1_out0_no_return_with_update (IN arg varchar(32)) +MODIFIES SQL DATA +BEGIN ATOMIC +update public.dummy set name = name; END /; \ No newline at end of file