diff --git a/jpa/jpa21/README.md b/jpa/jpa21/README.md new file mode 100644 index 00000000..b99fb4d7 --- /dev/null +++ b/jpa/jpa21/README.md @@ -0,0 +1,54 @@ +# Spring Data JPA - JPA 2.1 example + +This project contains samples of JPA 2.1 specific features of Spring Data JPA. + +## Support for stored procedure execution + +You can execute stored procedures either predefined using the JPA 2.1 mapping annotations or dynamically let the stored procedure definition be derived from the repository method name. + +Stored procedure declaration in the database (schema.sql): + +```sql +DROP procedure IF EXISTS plus1inout +/; +CREATE procedure plus1inout (IN arg int, OUT res int) +BEGIN ATOMIC + set res = arg + 1; +END +/; +``` + +JPA 2.1 stored procedure declaration: + +```java +@Entity +@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = { + @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class), + @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) }) +public class User { + + @Id @GeneratedValue// + private Long id; +} + +``` + +Spring Data JPA repository declaration to execute procedures: + +```java +public interface UserRepository extends CrudRepository { + + // Explicitly mapped to named stored procedure {@code User.plus1} in the {@link EntityManager}. + // By default, we would've try to find a procedure declaration named User.plus1BackedByOtherNamedStoredProcedure + @Procedure(name = "User.plus1") + Integer plus1BackedByOtherNamedStoredProcedure(@Param("arg") Integer arg); + + // Directly map the method to the stored procedure in the database (to avoid the annotation madness on your domain classes). + @Procedure("plus1inout") + Integer derivedStoredProcedureDeclaration(Integer arg); +} +``` + +Calling `UserRepository.plus1BackedByOtherNamedStoredProcedure(…)` will execute the stored procedure `plus1inout` using the meta-data declared on the `User` domain class. + +`UserRepository.derivedStoredProcedureDeclaration(…)` will use the stored procedure name declared in the annotation and default to positional parameter binding and expect a single output parameter of the backing stored procedure. \ No newline at end of file diff --git a/jpa/jpa21/pom.xml b/jpa/jpa21/pom.xml new file mode 100644 index 00000000..67a63ec4 --- /dev/null +++ b/jpa/jpa21/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + + + org.springframework.data.examples + spring-data-jpa-examples + 1.0.0.BUILD-SNAPSHOT + + + spring-data-jpa-jpa21 + Spring Data JPA - JPA 2.1 specific features + + + 1.6.0.DATAJPA-455-SNAPSHOT + + + \ No newline at end of file diff --git a/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/StoredProcedureConfiguration.java b/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/StoredProcedureConfiguration.java new file mode 100644 index 00000000..7278fe42 --- /dev/null +++ b/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/StoredProcedureConfiguration.java @@ -0,0 +1,27 @@ +/* + * Copyright 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 example.springdata.jpa.storedprocedures; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; + +/** + * @author Thomas Darimont + * @author Oliver Gierke + */ +@Configuration +@EnableAutoConfiguration +class StoredProcedureConfiguration {} diff --git a/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/User.java b/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/User.java new file mode 100644 index 00000000..be0147f8 --- /dev/null +++ b/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/User.java @@ -0,0 +1,39 @@ +/* + * Copyright 2013-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 example.springdata.jpa.storedprocedures; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.ParameterMode; +import javax.persistence.StoredProcedureParameter; + +/** + * Sample user class. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +@Entity +@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = { + @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class), + @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) }) +public class User { + + @Id @GeneratedValue// + private Long id; +} diff --git a/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/UserRepository.java b/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/UserRepository.java new file mode 100644 index 00000000..32922037 --- /dev/null +++ b/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/UserRepository.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013 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 example.springdata.jpa.storedprocedures; + +import javax.persistence.EntityManager; + +import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; + +/** + * Simple repository interface for {@link User} instances. The interface is used to declare so called query methods, + * methods to retrieve single entities or collections of them. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public interface UserRepository extends CrudRepository { + + /** + * Explicitly mapped to named stored procedure {@code User.plus1IO} in the {@link EntityManager} + * + * @see User + */ + @Procedure(name = "User.plus1") + Integer plus1BackedByOtherNamedStoredProcedure(@Param("arg") Integer arg); + + /** + * Directly map the method to the stored procedure in the database (to avoid the annotation madness on your domain + * classes). + */ + @Procedure("plus1inout") + Integer derivedStoredProcedureDeclaration(Integer arg); +} diff --git a/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/package-info.java b/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/package-info.java new file mode 100644 index 00000000..2375c0f8 --- /dev/null +++ b/jpa/jpa21/src/main/java/example/springdata/jpa/storedprocedures/package-info.java @@ -0,0 +1,7 @@ +/** + * Sample showing JPA 2.1 related features of Spring Data JPA. + * + * @author Thomas Darimont + * @author Oliver Gierke + */ +package example.springdata.jpa.storedprocedures; \ No newline at end of file diff --git a/jpa/jpa21/src/main/resources/application.properties b/jpa/jpa21/src/main/resources/application.properties new file mode 100644 index 00000000..d98d035f --- /dev/null +++ b/jpa/jpa21/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.datasource.separator=/; \ No newline at end of file diff --git a/jpa/jpa21/src/main/resources/logback.xml b/jpa/jpa21/src/main/resources/logback.xml new file mode 100644 index 00000000..40a8f339 --- /dev/null +++ b/jpa/jpa21/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + + + \ No newline at end of file diff --git a/jpa/jpa21/src/main/resources/schema.sql b/jpa/jpa21/src/main/resources/schema.sql new file mode 100644 index 00000000..a2f3db41 --- /dev/null +++ b/jpa/jpa21/src/main/resources/schema.sql @@ -0,0 +1,7 @@ +DROP procedure IF EXISTS plus1inout +/; +CREATE procedure plus1inout (IN arg int, OUT res int) +BEGIN ATOMIC + set res = arg + 1; +END +/; diff --git a/jpa/jpa21/src/test/java/example/springdata/jpa/storedprocedures/UserRepositoryIntegrationTests.java b/jpa/jpa21/src/test/java/example/springdata/jpa/storedprocedures/UserRepositoryIntegrationTests.java new file mode 100644 index 00000000..80b6c554 --- /dev/null +++ b/jpa/jpa21/src/test/java/example/springdata/jpa/storedprocedures/UserRepositoryIntegrationTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 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 example.springdata.jpa.storedprocedures; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import javax.persistence.EntityManager; +import javax.persistence.ParameterMode; +import javax.persistence.StoredProcedureQuery; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * Intergration test showing the usage of JPA 2.1 stored procedures support through Spring Data repositories. + * + * @author Thomas Darimont + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@SpringApplicationConfiguration(classes = StoredProcedureConfiguration.class) +public class UserRepositoryIntegrationTests { + + @Autowired UserRepository repository; + + /** + * @see DATAJPA-455 + */ + @Test + public void entityAnnotatedCustomNamedProcedurePlus1IO() { + assertThat(repository.plus1BackedByOtherNamedStoredProcedure(1), is(2)); + } + + /** + * @see DATAJPA-455 + */ + @Test + public void invokeDerivedStoredProcedure() { + assertThat(repository.derivedStoredProcedureDeclaration(1), is(2)); + } + + // This is what it would look like implemented manually. + + @Autowired EntityManager em; + + @Test + public void plainJpa21() { + + StoredProcedureQuery proc = em.createStoredProcedureQuery("plus1inout"); + proc.registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN); + proc.registerStoredProcedureParameter(2, Integer.class, ParameterMode.OUT); + + proc.setParameter(1, 1); + proc.execute(); + + assertThat(proc.getOutputParameterValue(2), is((Object) 2)); + } + + @Test + public void plainJpa21_entityAnnotatedCustomNamedProcedurePlus1IO() { + + StoredProcedureQuery proc = em.createNamedStoredProcedureQuery("User.plus1"); + + proc.setParameter("arg", 1); + proc.execute(); + + assertThat(proc.getOutputParameterValue("res"), is((Object) 2)); + } +} diff --git a/jpa/pom.xml b/jpa/pom.xml index 5b738ff8..70aff593 100644 --- a/jpa/pom.xml +++ b/jpa/pom.xml @@ -21,6 +21,7 @@ showcase interceptors java8 + jpa21