From 4b11a9dc8fc762ee87ab781eec69679e89066c24 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Fri, 31 Jan 2014 14:19:08 +0100 Subject: [PATCH] Initial commit. Moved Spring Data JPA examples from spring-data-jpa-examples repo to this one. Added samples for MongoDB. --- .gitignore | 5 + README.md | 18 ++- jpa/pom.xml | 50 +++++++ jpa/spring-data-jpa-example/pom.xml | 40 ++++++ .../repository/auditing/AuditableUser.java | 54 ++++++++ .../auditing/AuditableUserRepository.java | 25 ++++ .../auditing/AuditingConfiguration.java | 68 ++++++++++ .../repository/auditing/AuditorAwareImpl.java | 49 +++++++ .../repository/auditing/package-info.java | 4 + .../caching/CachingConfiguration.java | 53 ++++++++ .../caching/CachingUserRepository.java | 36 +++++ .../jpa/example/repository/caching/User.java | 96 +++++++++++++ .../repository/caching/package-info.java | 5 + .../custom/CustomRepositoryConfig.java | 32 +++++ .../jpa/example/repository/custom/User.java | 96 +++++++++++++ .../repository/custom/UserRepository.java | 60 ++++++++ .../custom/UserRepositoryCustom.java | 34 +++++ .../repository/custom/UserRepositoryImpl.java | 73 ++++++++++ .../custom/UserRepositoryImplJdbc.java | 86 ++++++++++++ .../repository/custom/package-info.java | 5 + .../simple/SimpleConfiguration.java | 29 ++++ .../simple/SimpleUserRepository.java | 71 ++++++++++ .../jpa/example/repository/simple/User.java | 96 +++++++++++++ .../repository/simple/package-info.java | 5 + .../src/main/resources/META-INF/orm.xml | 12 ++ .../auditing/AuditableUserSample.java | 59 ++++++++ .../repository/basics/BasicFactorySetup.java | 87 ++++++++++++ .../repository/basics/BasicSample.java | 79 +++++++++++ .../repository/basics/package-info.java | 20 +++ .../caching/CachingRepositoryTests.java | 62 +++++++++ .../UserRepositoryCustomizationTests.java | 91 +++++++++++++ .../simple/SimpleUserRepositoryTests.java | 80 +++++++++++ .../test/resources/META-INF/persistence.xml | 16 +++ .../src/test/resources/logback.xml | 16 +++ jpa/spring-data-jpa-interceptors/pom.xml | 16 +++ .../ApplicationConfiguration.java | 53 ++++++++ .../jpa/sample/interceptors/Customer.java | 32 +++++ .../interceptors/CustomerRepository.java | 22 +++ .../InterceptorIntegrationTest.java | 40 ++++++ .../src/test/resources/logback.xml | 17 +++ jpa/spring-data-jpa-java8-auditing/pom.xml | 23 ++++ .../jpa/examples/java8/AbstractEntity.java | 42 ++++++ .../data/jpa/examples/java8/Customer.java | 33 +++++ .../examples/java8/CustomerRepository.java | 25 ++++ .../src/main/resources/META-INF/orm.xml | 12 ++ .../java8/Java8AuditingIntegrationTests.java | 58 ++++++++ .../src/test/resources/logback.xml | 18 +++ jpa/spring-data-jpa-showcase/README.txt | 4 + jpa/spring-data-jpa-showcase/demo.txt | 62 +++++++++ jpa/spring-data-jpa-showcase/pom.xml | 128 ++++++++++++++++++ .../jpa/showcase/after/AccountRepository.java | 23 ++++ .../showcase/after/CustomerRepository.java | 24 ++++ .../jpa/showcase/before/AccountService.java | 30 ++++ .../showcase/before/AccountServiceImpl.java | 54 ++++++++ .../jpa/showcase/before/CustomerService.java | 56 ++++++++ .../showcase/before/CustomerServiceImpl.java | 90 ++++++++++++ .../data/jpa/showcase/core/Account.java | 40 ++++++ .../data/jpa/showcase/core/Customer.java | 32 +++++ .../src/main/resources/data.sql | 10 ++ .../showcase/snippets/AccountPredicates.java | 25 ++++ .../showcase/snippets/AccountRepository.java | 27 ++++ .../snippets/AccountRepositoryCustom.java | 11 ++ .../snippets/AccountRepositoryImpl.java | 40 ++++++ .../snippets/AccountRepositoryJdbcImpl.java | 27 ++++ .../snippets/CustomerSpecifications.java | 45 ++++++ .../src/snippets/resources/jdbc.xml | 7 + .../AccountRepositoryIntegrationTest.java | 34 +++++ .../CustomerRepositoryIntegrationTest.java | 34 +++++ .../jpa/showcase/AbstractShowcaseTest.java | 35 +++++ .../AccountRepositoryIntegrationTest.java | 55 ++++++++ .../CustomerRepositoryIntegrationTest.java | 80 +++++++++++ .../before/AccountServiceIntegrationTest.java | 56 ++++++++ .../CustomerServiceIntegrationTest.java | 62 +++++++++ .../src/test/resources/logback.xml | 16 +++ mongodb/pom.xml | 70 ++++++++++ .../examples/mongodb/customer/Address.java | 35 +++++ .../examples/mongodb/customer/Customer.java | 54 ++++++++ .../mongodb/customer/CustomerRepository.java | 51 +++++++ .../data/examples/mongodb/shop/Invoice.java | 36 +++++ .../data/examples/mongodb/shop/LineItem.java | 43 ++++++ .../data/examples/mongodb/shop/Order.java | 66 +++++++++ .../mongodb/shop/OrderRepository.java | 28 ++++ .../mongodb/shop/OrderRepositoryCustom.java | 32 +++++ .../mongodb/shop/OrderRepositoryImpl.java | 67 +++++++++ .../examples/mongodb/TestConfiguration.java | 44 ++++++ .../CustomerRepositoryIntegrationTest.java | 101 ++++++++++++++ .../shop/OrderRepositoryIntegrationTests.java | 68 ++++++++++ mongodb/src/test/resources/logback.xml | 17 +++ pom.xml | 86 ++++++++++++ 89 files changed, 3905 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 jpa/pom.xml create mode 100644 jpa/spring-data-jpa-example/pom.xml create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditableUser.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditableUserRepository.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditingConfiguration.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditorAwareImpl.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/package-info.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/CachingConfiguration.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/CachingUserRepository.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/User.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/package-info.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/CustomRepositoryConfig.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/User.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepository.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryCustom.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryImpl.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryImplJdbc.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/package-info.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/SimpleConfiguration.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/SimpleUserRepository.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/User.java create mode 100644 jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/package-info.java create mode 100644 jpa/spring-data-jpa-example/src/main/resources/META-INF/orm.xml create mode 100644 jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/auditing/AuditableUserSample.java create mode 100644 jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/BasicFactorySetup.java create mode 100644 jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/BasicSample.java create mode 100644 jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/package-info.java create mode 100644 jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/caching/CachingRepositoryTests.java create mode 100644 jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryCustomizationTests.java create mode 100644 jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/simple/SimpleUserRepositoryTests.java create mode 100644 jpa/spring-data-jpa-example/src/test/resources/META-INF/persistence.xml create mode 100644 jpa/spring-data-jpa-example/src/test/resources/logback.xml create mode 100644 jpa/spring-data-jpa-interceptors/pom.xml create mode 100644 jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/ApplicationConfiguration.java create mode 100644 jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/Customer.java create mode 100644 jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/CustomerRepository.java create mode 100644 jpa/spring-data-jpa-interceptors/src/test/java/org/spring/data/jpa/sample/interceptors/InterceptorIntegrationTest.java create mode 100644 jpa/spring-data-jpa-interceptors/src/test/resources/logback.xml create mode 100644 jpa/spring-data-jpa-java8-auditing/pom.xml create mode 100644 jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/AbstractEntity.java create mode 100644 jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/Customer.java create mode 100644 jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/CustomerRepository.java create mode 100644 jpa/spring-data-jpa-java8-auditing/src/main/resources/META-INF/orm.xml create mode 100644 jpa/spring-data-jpa-java8-auditing/src/test/java/org/springframework/data/jpa/examples/java8/Java8AuditingIntegrationTests.java create mode 100644 jpa/spring-data-jpa-java8-auditing/src/test/resources/logback.xml create mode 100644 jpa/spring-data-jpa-showcase/README.txt create mode 100644 jpa/spring-data-jpa-showcase/demo.txt create mode 100644 jpa/spring-data-jpa-showcase/pom.xml create mode 100644 jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/after/AccountRepository.java create mode 100644 jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/after/CustomerRepository.java create mode 100644 jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/AccountService.java create mode 100644 jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/AccountServiceImpl.java create mode 100644 jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/CustomerService.java create mode 100644 jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/CustomerServiceImpl.java create mode 100644 jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/core/Account.java create mode 100644 jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/core/Customer.java create mode 100644 jpa/spring-data-jpa-showcase/src/main/resources/data.sql create mode 100644 jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountPredicates.java create mode 100644 jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepository.java create mode 100644 jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryCustom.java create mode 100644 jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryImpl.java create mode 100644 jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryJdbcImpl.java create mode 100644 jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/CustomerSpecifications.java create mode 100644 jpa/spring-data-jpa-showcase/src/snippets/resources/jdbc.xml create mode 100644 jpa/spring-data-jpa-showcase/src/test-snippets/java/org/springframework/data/jpa/showcase/snippets/test/AccountRepositoryIntegrationTest.java create mode 100644 jpa/spring-data-jpa-showcase/src/test-snippets/java/org/springframework/data/jpa/showcase/snippets/test/CustomerRepositoryIntegrationTest.java create mode 100644 jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/AbstractShowcaseTest.java create mode 100644 jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/after/AccountRepositoryIntegrationTest.java create mode 100644 jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/after/CustomerRepositoryIntegrationTest.java create mode 100644 jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/before/AccountServiceIntegrationTest.java create mode 100644 jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/before/CustomerServiceIntegrationTest.java create mode 100644 jpa/spring-data-jpa-showcase/src/test/resources/logback.xml create mode 100644 mongodb/pom.xml create mode 100644 mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/Address.java create mode 100644 mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/Customer.java create mode 100644 mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/CustomerRepository.java create mode 100644 mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/Invoice.java create mode 100644 mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/LineItem.java create mode 100644 mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/Order.java create mode 100644 mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepository.java create mode 100644 mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryCustom.java create mode 100644 mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryImpl.java create mode 100644 mongodb/src/test/java/org/springframework/data/examples/mongodb/TestConfiguration.java create mode 100644 mongodb/src/test/java/org/springframework/data/examples/mongodb/customer/CustomerRepositoryIntegrationTest.java create mode 100644 mongodb/src/test/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryIntegrationTests.java create mode 100644 mongodb/src/test/resources/logback.xml create mode 100644 pom.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..24633650 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.project +.classpath +.springBeans +.settings/ +target/ \ No newline at end of file diff --git a/README.md b/README.md index 22190abd..b219ab09 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,16 @@ -spring-data-examples -==================== +# Spring Data Examples -Spring Data Example Projects +This repository contains example projects for the different Spring Data modules to showcase the API and how to use the features provided by the modules. + +We have separate folders for the samples of individual modules: + +## Spring Data JPA + +* `spring-data-jpa-example` - Probably the project you want to have a look at first. Contains a variety of sample packages, showcasing the different levels at which you can use Spring Data JPA. Have a look at the `simple` package for the most basic setup. +* `java8-auditing` - Example of how to use Spring Data JPA auditing with Java 8 date time types. +* `spring-data-jpa-showcase` - Refactoring show case of how to improve a plain-JPA-based persistence layer by using Spring Data JPA (read: removing close to all of the implementation code). Follow the `demo.txt` file for detailed instructions. +* `spring-data-jpa-interceptors` - Example of how to enrich the repositories with AOP. + +## Spring Data MongoDB + +* `spring-data-mongodb-example` - Example project for general repository functionality as well as aggregation framework support. diff --git a/jpa/pom.xml b/jpa/pom.xml new file mode 100644 index 00000000..dd351060 --- /dev/null +++ b/jpa/pom.xml @@ -0,0 +1,50 @@ + + 4.0.0 + + spring-data-jpa-examples + pom + + + org.springframework.data.examples + spring-data-examples + 1.0.0.BUILD-SNAPSHOT + + + Spring Data JPA - Examples + Sample projects for Spring Data JPA + http://www.springframework.org/spring-data + 2011 + + + spring-data-jpa-example + spring-data-jpa-showcase + spring-data-jpa-interceptors + spring-data-jpa-java8-auditing + + + + 1.5.0.RC1 + 4.3.1.Final + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + joda-time + joda-time + + + + org.hsqldb + hsqldb + + + + + \ No newline at end of file diff --git a/jpa/spring-data-jpa-example/pom.xml b/jpa/spring-data-jpa-example/pom.xml new file mode 100644 index 00000000..9115e231 --- /dev/null +++ b/jpa/spring-data-jpa-example/pom.xml @@ -0,0 +1,40 @@ + + 4.0.0 + + spring-data-jpa-example + + + org.springframework.data.examples + spring-data-jpa-examples + 1.0.0.BUILD-SNAPSHOT + ../pom.xml + + + Spring Data JPA - Example + Small sample project showing the usage of Spring Data JPA. + + + + + org.springframework + spring-aspects + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*.java + + + + + + + \ No newline at end of file diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditableUser.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditableUser.java new file mode 100644 index 00000000..4884c91d --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditableUser.java @@ -0,0 +1,54 @@ +/* + * 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 org.springframework.data.jpa.example.repository.auditing; + +import javax.persistence.Entity; + +import org.springframework.data.domain.Auditable; +import org.springframework.data.jpa.domain.AbstractAuditable; + +/** + * User domain class that uses auditing functionality of Spring Data that can either be aquired implementing + * {@link Auditable} or extend {@link AbstractAuditable}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +@Entity +public class AuditableUser extends AbstractAuditable { + + private static final long serialVersionUID = 1L; + + private String username; + + /** + * Set's the user's name. + * + * @param username the username to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * Returns the user's name. + * + * @return the username + */ + public String getUsername() { + return username; + } +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditableUserRepository.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditableUserRepository.java new file mode 100644 index 00000000..c3984527 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditableUserRepository.java @@ -0,0 +1,25 @@ +/* + * 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 org.springframework.data.jpa.example.repository.auditing; + +import org.springframework.data.repository.CrudRepository; + +/** + * @author Oliver Gierke + */ +public interface AuditableUserRepository extends CrudRepository { + +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditingConfiguration.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditingConfiguration.java new file mode 100644 index 00000000..0fd712ab --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditingConfiguration.java @@ -0,0 +1,68 @@ +/* + * 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 org.springframework.data.jpa.example.repository.auditing; + +import javax.sql.DataSource; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.Database; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; + +/** + * @author Oliver Gierke + */ +@Configuration +@EnableAutoConfiguration +@EnableJpaAuditing +@EnableJpaRepositories +// TODO: Remove once Boot can work with Codd +class AuditingConfiguration { + + /** + * We need to configure a {@link LocalContainerEntityManagerFactoryBean} manually here as Spring does not + * automatically add the {@code orm.xml} if a {@code persistence.xml} is located right beside it. This is + * necessary to get the {@link org.springframework.data.jpa.example.repository.basics.BasicFactorySetup} sample + * working. However, in a {code persistence.xml}-less codebase you can rely on Spring Boot on setting the correct + * defaults. + * + * @return + */ + @Bean + LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { + + HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); + adapter.setDatabase(Database.HSQL); + adapter.setGenerateDdl(true); + + LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); + factoryBean.setPackagesToScan(getClass().getPackage().getName()); + factoryBean.setMappingResources("META-INF/orm.xml"); + factoryBean.setJpaVendorAdapter(adapter); + factoryBean.setDataSource(dataSource); + + return factoryBean; + } + + @Bean + AuditorAwareImpl auditorAware() { + return new AuditorAwareImpl(); + } +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditorAwareImpl.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditorAwareImpl.java new file mode 100644 index 00000000..2ee7fd6b --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/AuditorAwareImpl.java @@ -0,0 +1,49 @@ +/* + * 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 org.springframework.data.jpa.example.repository.auditing; + +import org.springframework.data.domain.AuditorAware; + +/** + * Dummy implementation of {@link AuditorAware}. It will return the configured {@link AuditableUser} as auditor on every + * call to {@link #getCurrentAuditor()}. Normally you would access the applications security subsystem to return the + * current user. + *

+ * TODO: change back to {@code AuditorAware} after SD Commons 1.7 RC1 as generic autowiring with Spring 4 + * will work with that version again. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public class AuditorAwareImpl implements AuditorAware { + + private AuditableUser auditor; + + /** + * @param auditor the auditor to set + */ + public void setAuditor(AuditableUser auditor) { + this.auditor = auditor; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.AuditorAware#getCurrentAuditor() + */ + public AuditableUser getCurrentAuditor() { + return auditor; + } +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/package-info.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/package-info.java new file mode 100644 index 00000000..b0467812 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/auditing/package-info.java @@ -0,0 +1,4 @@ +/** + * Package showing auditing support with Spring Data repositories. + */ +package org.springframework.data.jpa.example.repository.auditing; \ No newline at end of file diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/CachingConfiguration.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/CachingConfiguration.java new file mode 100644 index 00000000..98806c27 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/CachingConfiguration.java @@ -0,0 +1,53 @@ +/* + * 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 org.springframework.data.jpa.example.repository.caching; + +import java.util.Arrays; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCache; +import org.springframework.cache.support.SimpleCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +/** + * Java config to use Spring Data JPA alongside the Spring caching support. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +@Configuration +@EnableCaching +@EnableAutoConfiguration +// TODO: Remove once Boot can work with Codd +@EnableJpaRepositories +class CachingConfiguration { + + @Bean + public CacheManager cacheManager() { + + Cache cache = new ConcurrentMapCache("byUsername"); + + SimpleCacheManager manager = new SimpleCacheManager(); + manager.setCaches(Arrays.asList(cache)); + + return manager; + } +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/CachingUserRepository.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/CachingUserRepository.java new file mode 100644 index 00000000..ed82aa23 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/CachingUserRepository.java @@ -0,0 +1,36 @@ +/* + * 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 org.springframework.data.jpa.example.repository.caching; + +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.repository.CrudRepository; + +/** + * User repository using Spring's caching abstraction. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public interface CachingUserRepository extends CrudRepository { + + @Override + @CacheEvict("byUsername") + S save(S entity); + + @Cacheable("byUsername") + User findByUsername(String username); +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/User.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/User.java new file mode 100644 index 00000000..da23c5fd --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/User.java @@ -0,0 +1,96 @@ +/* + * 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 org.springframework.data.jpa.example.repository.caching; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.NamedQuery; + +import org.springframework.data.jpa.domain.AbstractPersistable; + +/** + * Sample user class. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +@Entity +@NamedQuery(name = "User.findByTheUsersName", query = "from User u where u.username = ?1") +public class User extends AbstractPersistable { + + private static final long serialVersionUID = -2952735933715107252L; + + @Column(unique = true) private String username; + + private String firstname; + private String lastname; + + public User() { + this(null); + } + + /** + * Creates a new user instance. + */ + public User(Long id) { + this.setId(id); + } + + /** + * Returns the username. + * + * @return + */ + public String getUsername() { + + return username; + } + + /** + * @param username the username to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @return the firstname + */ + public String getFirstname() { + return firstname; + } + + /** + * @param firstname the firstname to set + */ + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + /** + * @return the lastname + */ + public String getLastname() { + return lastname; + } + + /** + * @param lastname the lastname to set + */ + public void setLastname(String lastname) { + this.lastname = lastname; + } +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/package-info.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/package-info.java new file mode 100644 index 00000000..a5ea9748 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/caching/package-info.java @@ -0,0 +1,5 @@ +/** + * Sample for the integration of the Spring caching abstraction with Spring Data repositories. + */ +package org.springframework.data.jpa.example.repository.caching; + diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/CustomRepositoryConfig.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/CustomRepositoryConfig.java new file mode 100644 index 00000000..63796393 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/CustomRepositoryConfig.java @@ -0,0 +1,32 @@ +/* + * 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 org.springframework.data.jpa.example.repository.custom; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +/** + * Sample configuration to bootstrap Spring Data JPA through JavaConfig + * + * @author Thomas Darimont + * @author Oliver Gierke + */ +@Configuration +@EnableAutoConfiguration +// TODO: Remove once Boot can work with Codd +@EnableJpaRepositories +class CustomRepositoryConfig {} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/User.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/User.java new file mode 100644 index 00000000..7ef5f61b --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/User.java @@ -0,0 +1,96 @@ +/* + * 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 org.springframework.data.jpa.example.repository.custom; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.NamedQuery; + +import org.springframework.data.jpa.domain.AbstractPersistable; + +/** + * Sample user class. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +@Entity +@NamedQuery(name = "User.findByTheUsersName", query = "from User u where u.username = ?1") +public class User extends AbstractPersistable { + + private static final long serialVersionUID = -2952735933715107252L; + + @Column(unique = true) private String username; + + private String firstname; + private String lastname; + + public User() { + this(null); + } + + /** + * Creates a new user instance. + */ + public User(Long id) { + this.setId(id); + } + + /** + * Returns the username. + * + * @return + */ + public String getUsername() { + + return username; + } + + /** + * @param username the username to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @return the firstname + */ + public String getFirstname() { + return firstname; + } + + /** + * @param firstname the firstname to set + */ + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + /** + * @return the lastname + */ + public String getLastname() { + return lastname; + } + + /** + * @param lastname the lastname to set + */ + public void setLastname(String lastname) { + this.lastname = lastname; + } +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepository.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepository.java new file mode 100644 index 00000000..e6e0d7ae --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepository.java @@ -0,0 +1,60 @@ +/* + * 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 org.springframework.data.jpa.example.repository.custom; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; + +/** + * Repository interface for {@link User} instances. Provides basic CRUD operations due to the extension of + * {@link JpaRepository}. Includes custom implemented functionality by extending {@link UserRepositoryCustom}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public interface UserRepository extends CrudRepository, UserRepositoryCustom { + + /** + * Find the user with the given username. This method will be translated into a query using the + * {@link javax.persistence.NamedQuery} annotation at the {@link User} class. + * + * @param username + * @return + */ + User findByTheUsersName(String username); + + /** + * Find all users with the given lastname. This method will be translated into a query by constructing it directly + * from the method name as there is no other query declared. + * + * @param lastname + * @return + */ + List findByLastname(String lastname); + + /** + * Returns all users with the given firstname. This method will be translated into a query using the one declared in + * the {@link Query} annotation declared one. + * + * @param firstname + * @return + */ + @Query("select u from User u where u.firstname = ?1") + List findByFirstname(String firstname); +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryCustom.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryCustom.java new file mode 100644 index 00000000..6ff76e7b --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryCustom.java @@ -0,0 +1,34 @@ +/* + * 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 org.springframework.data.jpa.example.repository.custom; + +import java.util.List; + +/** + * Interface for repository functionality that ought to be implemented manually. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +interface UserRepositoryCustom { + + /** + * Custom repository operation. + * + * @return + */ + List myCustomBatchOperation(); +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryImpl.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryImpl.java new file mode 100644 index 00000000..dd75b817 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryImpl.java @@ -0,0 +1,73 @@ +/* + * 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 org.springframework.data.jpa.example.repository.custom; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.criteria.CriteriaQuery; + +/** + * Implementation fo the custom repository functionality declared in {@link UserRepositoryCustom} based on JPA. To use + * this implementation in combination with Spring Data JPA you can either register it programatically: + * + *
+ * EntityManager em = ... // Obtain EntityManager
+ * 
+ * UserRepositoryCustom custom = new UserRepositoryImpl();
+ * custom.setEntityManager(em);
+ * 
+ * RepositoryFactorySupport factory = new JpaRepositoryFactory(em);
+ * UserRepository repository = factory.getRepository(UserRepository.class, custom);
+ * 
+ * + * Using the Spring namespace the implementation will just get picked up due to the classpath scanning for + * implementations with the {@code Impl} postfix. + * + *
+ * <jpa:repositories base-package="com.acme.repository" />
+ * 
+ * + * If you need to manually configure the custom instance see {@link UserRepositoryImplJdbc} for an example. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +class UserRepositoryImpl implements UserRepositoryCustom { + + @PersistenceContext private EntityManager em; + + /** + * Configure the entity manager to be used. + * + * @param em the {@link EntityManager} to set. + */ + public void setEntityManager(EntityManager em) { + this.em = em; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.example.repository.UserRepositoryCustom#myCustomBatchOperation() + */ + public List myCustomBatchOperation() { + + CriteriaQuery criteriaQuery = em.getCriteriaBuilder().createQuery(User.class); + criteriaQuery.select(criteriaQuery.from(User.class)); + return em.createQuery(criteriaQuery).getResultList(); + } +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryImplJdbc.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryImplJdbc.java new file mode 100644 index 00000000..750d09f7 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryImplJdbc.java @@ -0,0 +1,86 @@ +/* + * 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 org.springframework.data.jpa.example.repository.custom; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.jdbc.core.simple.ParameterizedRowMapper; +import org.springframework.jdbc.core.support.JdbcDaoSupport; +import org.springframework.stereotype.Component; + +/** + * Class with the implementation of the custom repository code. Uses JDBC in this case. For basic programatic setup see + * {@link UserRepositoryImpl} for examples. + *

+ * As you need to hand the instance a {@link javax.sql.DataSource} or + * {@link org.springframework.jdbc.core.simple.SimpleJdbcTemplate} you manually need to declare it as Spring bean: + * + *

+ * <jpa:repository base-package="com.acme.repository" />
+ * 
+ * <bean id="userRepositoryImpl" class="...UserRepositoryJdbcImpl">
+ *   <property name="dataSource" ref="dataSource" />
+ * </bean>
+ * 
+ * + * Using {@code userRepositoryImpl} will cause the repository instance get this bean injected for custom repository + * logic as the default postfix for custom DAO instances is {@code Impl}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +@Profile("jdbc") +@Component("userRepositoryImpl") +class UserRepositoryImplJdbc extends JdbcDaoSupport implements UserRepositoryCustom { + + private static final String COMPLICATED_SQL = "SELECT * FROM User"; + + @Autowired + public UserRepositoryImplJdbc(DataSource dataSource) { + setDataSource(dataSource); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.example.repository.UserRepositoryCustom#myCustomBatchOperation() + */ + public List myCustomBatchOperation() { + return getJdbcTemplate().query(COMPLICATED_SQL, new UserRowMapper()); + } + + private static class UserRowMapper implements ParameterizedRowMapper { + + /* + * (non-Javadoc) + * @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int) + */ + public User mapRow(ResultSet rs, int rowNum) throws SQLException { + + User user = new User(rs.getLong("id")); + user.setUsername(rs.getString("username")); + user.setLastname(rs.getString("lastname")); + user.setFirstname(rs.getString("firstname")); + + return user; + } + } +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/package-info.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/package-info.java new file mode 100644 index 00000000..2218bc34 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/custom/package-info.java @@ -0,0 +1,5 @@ +/** + * Package showing a repository interface to use basic query method execution functionality as well as customized repository functionality. + */ +package org.springframework.data.jpa.example.repository.custom; + diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/SimpleConfiguration.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/SimpleConfiguration.java new file mode 100644 index 00000000..531bb664 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/SimpleConfiguration.java @@ -0,0 +1,29 @@ +/* + * 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 org.springframework.data.jpa.example.repository.simple; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +/** + * @author Oliver Gierke + */ +@Configuration +@EnableAutoConfiguration +// TODO: Remove once Boot can work with Codd +@EnableJpaRepositories +class SimpleConfiguration {} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/SimpleUserRepository.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/SimpleUserRepository.java new file mode 100644 index 00000000..a01dbff3 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/SimpleUserRepository.java @@ -0,0 +1,71 @@ +/* + * 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 org.springframework.data.jpa.example.repository.simple; + +import java.util.List; + +import org.springframework.data.jpa.repository.Query; +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 SimpleUserRepository extends CrudRepository { + + /** + * Find the user with the given username. This method will be translated into a query using the + * {@link javax.persistence.NamedQuery} annotation at the {@link User} class. + * + * @param lastname + * @return + */ + User findByTheUsersName(String username); + + /** + * Find all users with the given lastname. This method will be translated into a query by constructing it directly + * from the method name as there is no other query declared. + * + * @param lastname + * @return + */ + List findByLastname(String lastname); + + /** + * Returns all users with the given firstname. This method will be translated into a query using the one declared in + * the {@link Query} annotation declared one. + * + * @param firstname + * @return + */ + @Query("select u from User u where u.firstname = ?") + List findByFirstname(String firstname); + + /** + * Returns all users with the given name as first- or lastname. Makes use of the {@link Param} annotation to use named + * parameters in queries. This makes the query to method relation much more refactoring safe as the order of the + * method parameters is completely irrelevant. + * + * @param name + * @return + */ + @Query("select u from User u where u.firstname = :name or u.lastname = :name") + List findByFirstnameOrLastname(@Param("name") String name); +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/User.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/User.java new file mode 100644 index 00000000..ef7955e1 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/User.java @@ -0,0 +1,96 @@ +/* + * 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 org.springframework.data.jpa.example.repository.simple; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.NamedQuery; + +import org.springframework.data.jpa.domain.AbstractPersistable; + +/** + * Sample user class. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +@Entity +@NamedQuery(name = "User.findByTheUsersName", query = "from User u where u.username = ?1") +public class User extends AbstractPersistable { + + private static final long serialVersionUID = -2952735933715107252L; + + @Column(unique = true) private String username; + + private String firstname; + private String lastname; + + public User() { + this(null); + } + + /** + * Creates a new user instance. + */ + public User(Long id) { + this.setId(id); + } + + /** + * Returns the username. + * + * @return + */ + public String getUsername() { + + return username; + } + + /** + * @param username the username to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @return the firstname + */ + public String getFirstname() { + return firstname; + } + + /** + * @param firstname the firstname to set + */ + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + /** + * @return the lastname + */ + public String getLastname() { + return lastname; + } + + /** + * @param lastname the lastname to set + */ + public void setLastname(String lastname) { + this.lastname = lastname; + } +} diff --git a/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/package-info.java b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/package-info.java new file mode 100644 index 00000000..338e0e5a --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/java/org/springframework/data/jpa/example/repository/simple/package-info.java @@ -0,0 +1,5 @@ +/** + * Package showing a simple repository interface to use basic query method execution functionality. + */ +package org.springframework.data.jpa.example.repository.simple; + diff --git a/jpa/spring-data-jpa-example/src/main/resources/META-INF/orm.xml b/jpa/spring-data-jpa-example/src/main/resources/META-INF/orm.xml new file mode 100644 index 00000000..da5ba62b --- /dev/null +++ b/jpa/spring-data-jpa-example/src/main/resources/META-INF/orm.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/auditing/AuditableUserSample.java b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/auditing/AuditableUserSample.java new file mode 100644 index 00000000..bec34f10 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/auditing/AuditableUserSample.java @@ -0,0 +1,59 @@ +/* + * 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 org.springframework.data.jpa.example.repository.auditing; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Oliver Gierke + * @author Thomas Darimont + */ +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@ContextConfiguration(classes = AuditingConfiguration.class) +public class AuditableUserSample { + + @Autowired AuditableUserRepository repository; + @Autowired AuditorAwareImpl auditorAware; + @Autowired AuditingEntityListener listener; + + @Test + public void auditEntityCreation() throws Exception { + + assertThat(ReflectionTestUtils.getField(listener, "handler"), is(notNullValue())); + + AuditableUser user = new AuditableUser(); + user.setUsername("username"); + + auditorAware.setAuditor(user); + + user = repository.save(user); + user = repository.save(user); + + assertEquals(user, user.getCreatedBy()); + assertEquals(user, user.getLastModifiedBy()); + } +} diff --git a/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/BasicFactorySetup.java b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/BasicFactorySetup.java new file mode 100644 index 00000000..fab9f93a --- /dev/null +++ b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/BasicFactorySetup.java @@ -0,0 +1,87 @@ +/* + * 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 org.springframework.data.jpa.example.repository.basics; + +import static org.junit.Assert.*; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.data.jpa.example.repository.simple.SimpleUserRepository; +import org.springframework.data.jpa.example.repository.simple.User; +import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; + +/** + * Test case showing how to use the basic {@link GenericDaoFactory} + * + * @author Oliver Gierke + */ +public class BasicFactorySetup { + + private static final EntityManagerFactory factory = Persistence.createEntityManagerFactory("jpa.sample.plain"); + + private SimpleUserRepository userRepository; + private EntityManager em; + + private User user; + + /** + * Creates a {@link SimpleUserRepository} instance. + * + * @throws Exception + */ + @Before + public void setUp() { + + em = factory.createEntityManager(); + + userRepository = new JpaRepositoryFactory(em).getRepository(SimpleUserRepository.class); + + em.getTransaction().begin(); + + user = new User(); + user.setUsername("username"); + user.setFirstname("firstname"); + user.setLastname("lastname"); + + user = userRepository.save(user); + + } + + /** + * Rollback transaction. + */ + @After + public void tearDown() { + + em.getTransaction().rollback(); + } + + /** + * Showing invocation of finder method. + */ + @Test + public void executingFinders() { + + assertEquals(user, userRepository.findByTheUsersName("username")); + assertEquals(user, userRepository.findByLastname("lastname").get(0)); + assertEquals(user, userRepository.findByFirstname("firstname").get(0)); + } +} diff --git a/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/BasicSample.java b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/BasicSample.java new file mode 100644 index 00000000..74d7454f --- /dev/null +++ b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/BasicSample.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2014 the original author or authors. + * + * Licenseimport static org.junit.Assert.*; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.data.jpa.example.domain.User; +import org.springframework.data.jpa.repository.support.SimpleJpaRepository; +import org.springframework.data.repository.CrudRepository; +ess or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.example.repository.basics; + +import static org.junit.Assert.*; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.data.jpa.example.repository.simple.User; +import org.springframework.data.jpa.repository.support.SimpleJpaRepository; +import org.springframework.data.repository.CrudRepository; + +/** + * This unit tests shows plain usage of {@link SimpleJpaRepository}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public class BasicSample { + + private CrudRepository userRepository; + private EntityManager em; + + /** + * Sets up a {@link SimpleJpaRepository} instance. + */ + @Before + public void setUp() { + + EntityManagerFactory factory = Persistence.createEntityManagerFactory("jpa.sample.plain"); + em = factory.createEntityManager(); + + userRepository = new SimpleJpaRepository(User.class, em); + + em.getTransaction().begin(); + } + + @After + public void tearDown() { + em.getTransaction().rollback(); + } + + /** + * Tests saving users. Don't mimic transactionality shown here. It seriously lacks resource cleanup in case of an + * exception. Simplification serves descriptivness. + */ + @Test + public void savingUsers() { + + User user = new User(); + user.setUsername("username"); + + user = userRepository.save(user); + + assertEquals(user, userRepository.findOne(user.getId())); + } +} diff --git a/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/package-info.java b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/package-info.java new file mode 100644 index 00000000..e3b82d7c --- /dev/null +++ b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/basics/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +/** + * + * @author Thomas Darimont + */ +package org.springframework.data.jpa.example.repository.basics; \ No newline at end of file diff --git a/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/caching/CachingRepositoryTests.java b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/caching/CachingRepositoryTests.java new file mode 100644 index 00000000..8684f8ac --- /dev/null +++ b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/caching/CachingRepositoryTests.java @@ -0,0 +1,62 @@ +/* + * 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 org.springframework.data.jpa.example.repository.caching; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.Cache; +import org.springframework.cache.Cache.ValueWrapper; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration test to show how to use {@link Cacheable} with a Spring Data repository. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@ContextConfiguration(classes = CachingConfiguration.class) +public abstract class CachingRepositoryTests { + + @Autowired CachingUserRepository repository; + @Autowired CacheManager cacheManager; + + @Test + public void cachesValuesReturnedForQueryMethod() { + + User dave = new User(); + dave.setUsername("dmatthews"); + + dave = repository.save(dave); + + User result = repository.findByUsername("dmatthews"); + assertThat(result, is(dave)); + + // Verify entity cached + Cache cache = cacheManager.getCache("byUsername"); + ValueWrapper wrapper = cache.get("dmatthews"); + assertThat(wrapper.get(), is((Object) dave)); + } +} diff --git a/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryCustomizationTests.java b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryCustomizationTests.java new file mode 100644 index 00000000..a5c2abe4 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/custom/UserRepositoryCustomizationTests.java @@ -0,0 +1,91 @@ +/* + * 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 org.springframework.data.jpa.example.repository.custom; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * Intergration test showing the basic usage of {@link UserRepository}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@ContextConfiguration(classes = CustomRepositoryConfig.class) +// @ActiveProfiles("jdbc") // Uncomment @ActiveProfiles to enable the JDBC Implementation of the custom repository +public class UserRepositoryCustomizationTests { + + @Autowired UserRepository repository; + + /** + * Tests inserting a user and asserts it can be loaded again. + */ + @Test + public void testInsert() { + + User user = new User(); + user.setUsername("username"); + + user = repository.save(user); + + assertEquals(user, repository.findOne(user.getId())); + } + + @Test + public void saveAndFindByLastNameAndFindByUserName() { + + User user = new User(); + user.setUsername("foobar"); + user.setLastname("lastname"); + + user = repository.save(user); + + List users = repository.findByLastname("lastname"); + + assertNotNull(users); + assertTrue(users.contains(user)); + + User reference = repository.findByTheUsersName("foobar"); + assertEquals(user, reference); + } + + /** + * Test invocation of custom method. + */ + @Test + public void testCustomMethod() { + + User user = new User(); + user.setUsername("username"); + + user = repository.save(user); + + List users = repository.myCustomBatchOperation(); + + assertNotNull(users); + assertTrue(users.contains(user)); + } +} diff --git a/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/simple/SimpleUserRepositoryTests.java b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/simple/SimpleUserRepositoryTests.java new file mode 100644 index 00000000..e9e7f3de --- /dev/null +++ b/jpa/spring-data-jpa-example/src/test/java/org/springframework/data/jpa/example/repository/simple/SimpleUserRepositoryTests.java @@ -0,0 +1,80 @@ +/* + * 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 org.springframework.data.jpa.example.repository.simple; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * Intergration test showing the basic usage of {@link SimpleUserRepository}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@ContextConfiguration(classes = SimpleConfiguration.class) +public class SimpleUserRepositoryTests { + + @Autowired SimpleUserRepository repository; + User user; + + @Before + public void setUp() { + user = new User(); + user.setUsername("foobar"); + user.setFirstname("firstname"); + user.setLastname("lastname"); + } + + @Test + public void findSavedUserById() { + + user = repository.save(user); + + assertEquals(user, repository.findOne(user.getId())); + } + + @Test + public void findSavedUserByLastname() throws Exception { + + user = repository.save(user); + + List users = repository.findByLastname("lastname"); + + assertNotNull(users); + assertTrue(users.contains(user)); + } + + @Test + public void findByFirstnameOrLastname() throws Exception { + + user = repository.save(user); + + List users = repository.findByFirstnameOrLastname("lastname"); + + assertTrue(users.contains(user)); + } +} diff --git a/jpa/spring-data-jpa-example/src/test/resources/META-INF/persistence.xml b/jpa/spring-data-jpa-example/src/test/resources/META-INF/persistence.xml new file mode 100644 index 00000000..7f39b498 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,16 @@ + + + + + org.springframework.data.jpa.example.repository.simple.User + + + + + + + + + + + diff --git a/jpa/spring-data-jpa-example/src/test/resources/logback.xml b/jpa/spring-data-jpa-example/src/test/resources/logback.xml new file mode 100644 index 00000000..40a8f339 --- /dev/null +++ b/jpa/spring-data-jpa-example/src/test/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + + + \ No newline at end of file diff --git a/jpa/spring-data-jpa-interceptors/pom.xml b/jpa/spring-data-jpa-interceptors/pom.xml new file mode 100644 index 00000000..64e0acc1 --- /dev/null +++ b/jpa/spring-data-jpa-interceptors/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + org.springframework.data.examples + spring-data-jpa-examples + 1.0.0.BUILD-SNAPSHOT + ../pom.xml + + + spring-data-jpa-interceptors + Spring Data JPA - Interceptor sample + + diff --git a/jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/ApplicationConfiguration.java b/jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/ApplicationConfiguration.java new file mode 100644 index 00000000..3f456d1d --- /dev/null +++ b/jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/ApplicationConfiguration.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-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.spring.data.jpa.sample.interceptors; + +import org.springframework.aop.Advisor; +import org.springframework.aop.aspectj.AspectJExpressionPointcut; +import org.springframework.aop.interceptor.CustomizableTraceInterceptor; +import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@Configuration +@EnableAspectJAutoProxy +@EnableAutoConfiguration +// TODO: Remove once Boot can work with Codd +@EnableJpaRepositories +public class ApplicationConfiguration { + + @Bean + public CustomizableTraceInterceptor interceptor() { + + CustomizableTraceInterceptor interceptor = new CustomizableTraceInterceptor(); + interceptor.setEnterMessage("Entering $[methodName]($[arguments])."); + interceptor.setExitMessage("Leaving $[methodName](..) with return value $[returnValue], took $[invocationTime]ms."); + + return interceptor; + } + + @Bean + public Advisor traceAdvisor() { + + AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); + pointcut.setExpression("execution(public * org.springframework.data.repository.Repository+.*(..))"); + + return new DefaultPointcutAdvisor(pointcut, interceptor()); + } +} diff --git a/jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/Customer.java b/jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/Customer.java new file mode 100644 index 00000000..ac026355 --- /dev/null +++ b/jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/Customer.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012 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.spring.data.jpa.sample.interceptors; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class Customer { + + @Id + @GeneratedValue + Long id; + + String firstname; + String lastname; + +} diff --git a/jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/CustomerRepository.java b/jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/CustomerRepository.java new file mode 100644 index 00000000..1a2a788a --- /dev/null +++ b/jpa/spring-data-jpa-interceptors/src/main/java/org/spring/data/jpa/sample/interceptors/CustomerRepository.java @@ -0,0 +1,22 @@ +/* + * Copyright 2012 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.spring.data.jpa.sample.interceptors; + +import org.springframework.data.repository.CrudRepository; + +public interface CustomerRepository extends CrudRepository { + +} diff --git a/jpa/spring-data-jpa-interceptors/src/test/java/org/spring/data/jpa/sample/interceptors/InterceptorIntegrationTest.java b/jpa/spring-data-jpa-interceptors/src/test/java/org/spring/data/jpa/sample/interceptors/InterceptorIntegrationTest.java new file mode 100644 index 00000000..6bfb2372 --- /dev/null +++ b/jpa/spring-data-jpa-interceptors/src/test/java/org/spring/data/jpa/sample/interceptors/InterceptorIntegrationTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012 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.spring.data.jpa.sample.interceptors; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = ApplicationConfiguration.class) +public class InterceptorIntegrationTest { + + @Autowired + CustomerRepository repository; + + @Test + public void foo() { + + Customer customer = new Customer(); + customer.firstname = "Dave"; + customer.lastname = "Matthews"; + + repository.save(customer); + } +} diff --git a/jpa/spring-data-jpa-interceptors/src/test/resources/logback.xml b/jpa/spring-data-jpa-interceptors/src/test/resources/logback.xml new file mode 100644 index 00000000..fd48d21f --- /dev/null +++ b/jpa/spring-data-jpa-interceptors/src/test/resources/logback.xml @@ -0,0 +1,17 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + + + + \ No newline at end of file diff --git a/jpa/spring-data-jpa-java8-auditing/pom.xml b/jpa/spring-data-jpa-java8-auditing/pom.xml new file mode 100644 index 00000000..23a2291d --- /dev/null +++ b/jpa/spring-data-jpa-java8-auditing/pom.xml @@ -0,0 +1,23 @@ + + 4.0.0 + + + org.springframework.data.examples + spring-data-jpa-examples + 1.0.0.BUILD-SNAPSHOT + + + spring-data-jpa-java8-auditing + Spring Data JPA - Auditing on Java 8 + + + + + org.springframework + spring-aspects + + + + + \ No newline at end of file diff --git a/jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/AbstractEntity.java b/jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/AbstractEntity.java new file mode 100644 index 00000000..33a1b0ac --- /dev/null +++ b/jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/AbstractEntity.java @@ -0,0 +1,42 @@ +/* + * 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 org.springframework.data.jpa.examples.java8; + +import java.time.ZonedDateTime; + +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; + +/** + * @author Oliver Gierke + */ +@MappedSuperclass +public class AbstractEntity { + + @Id @GeneratedValue Long id; + + @CreatedDate// + // @Type(type = "org.jadira.usertype.dateandtime.threetenbp.PersistentZonedDateTime")// + ZonedDateTime createdDate; + + @LastModifiedDate// + // @Type(type = "org.jadira.usertype.dateandtime.threetenbp.PersistentZonedDateTime")// + ZonedDateTime modifiedDate; +} diff --git a/jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/Customer.java b/jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/Customer.java new file mode 100644 index 00000000..132c9449 --- /dev/null +++ b/jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/Customer.java @@ -0,0 +1,33 @@ +/* + * 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 org.springframework.data.jpa.examples.java8; + +import javax.persistence.Entity; + +/** + * @author Oliver Gierke + */ +@Entity +public class Customer extends AbstractEntity { + + String firstname, lastname; + + public Customer(String firstname, String lastname) { + + this.firstname = firstname; + this.lastname = lastname; + } +} diff --git a/jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/CustomerRepository.java b/jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/CustomerRepository.java new file mode 100644 index 00000000..cf0207a9 --- /dev/null +++ b/jpa/spring-data-jpa-java8-auditing/src/main/java/org/springframework/data/jpa/examples/java8/CustomerRepository.java @@ -0,0 +1,25 @@ +/* + * 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 org.springframework.data.jpa.examples.java8; + +import org.springframework.data.repository.CrudRepository; + +/** + * @author Oliver Gierke + */ +public interface CustomerRepository extends CrudRepository { + +} diff --git a/jpa/spring-data-jpa-java8-auditing/src/main/resources/META-INF/orm.xml b/jpa/spring-data-jpa-java8-auditing/src/main/resources/META-INF/orm.xml new file mode 100644 index 00000000..da5ba62b --- /dev/null +++ b/jpa/spring-data-jpa-java8-auditing/src/main/resources/META-INF/orm.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/jpa/spring-data-jpa-java8-auditing/src/test/java/org/springframework/data/jpa/examples/java8/Java8AuditingIntegrationTests.java b/jpa/spring-data-jpa-java8-auditing/src/test/java/org/springframework/data/jpa/examples/java8/Java8AuditingIntegrationTests.java new file mode 100644 index 00000000..9a808343 --- /dev/null +++ b/jpa/spring-data-jpa-java8-auditing/src/test/java/org/springframework/data/jpa/examples/java8/Java8AuditingIntegrationTests.java @@ -0,0 +1,58 @@ +/* + * 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 org.springframework.data.jpa.examples.java8; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration test to show the usage of Java 8 date time APIs with Spring Data JPA auditing. + * + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class Java8AuditingIntegrationTests { + + @Configuration + @EnableAutoConfiguration + @EnableJpaRepositories + @EnableJpaAuditing + static class Config { + + } + + @Autowired CustomerRepository repository; + + @Test + public void auditingSetsJdk8DateTimeTypes() { + + Customer customer = repository.save(new Customer("Dave", "Matthews")); + + assertThat(customer.createdDate, is(notNullValue())); + assertThat(customer.modifiedDate, is(notNullValue())); + } +} diff --git a/jpa/spring-data-jpa-java8-auditing/src/test/resources/logback.xml b/jpa/spring-data-jpa-java8-auditing/src/test/resources/logback.xml new file mode 100644 index 00000000..b48bf208 --- /dev/null +++ b/jpa/spring-data-jpa-java8-auditing/src/test/resources/logback.xml @@ -0,0 +1,18 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + + + + + \ No newline at end of file diff --git a/jpa/spring-data-jpa-showcase/README.txt b/jpa/spring-data-jpa-showcase/README.txt new file mode 100644 index 00000000..1db5a3ec --- /dev/null +++ b/jpa/spring-data-jpa-showcase/README.txt @@ -0,0 +1,4 @@ +Spring Data JPA showcase +------------------------ + +This is the sample app to demo Spring Data JPA features at conferences. The two main packages to take a look at are org.springframework.data.jpa.showcase.before and org.springframework.data.jpa.showcase.after. The first one shows a typical data access implementation with JPA 2. The second one shows what's left if you use Spring Data JPA. \ No newline at end of file diff --git a/jpa/spring-data-jpa-showcase/demo.txt b/jpa/spring-data-jpa-showcase/demo.txt new file mode 100644 index 00000000..25607e27 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/demo.txt @@ -0,0 +1,62 @@ +1. Introduction +--------------- +- Project structure +- Spring configuration +- Services +- Test cases + +2. Issues +--------- +- Generics, simple queries, pagination (no metadata), is new?, arbitrary queries +- Approach: step by step refactoring -> introduce Spring Data JPA + +5. AccountRepository +-------------------- +- replace save(…) +- findByCustomer -> show log -> method created from method name +- explain proxy mechanism, method signature possibilities (@Param, return types) + +4. Explain general proxy mechanism, SimpleJpaRepository +------------------------------------------------- +- show JpaRepository interface +- findAll(Pageable pageble) + +3. CustomerRepository +--------------------- +- add dependency to CustomerServiceImpl +- replace findById, findAll, save +- run test +- explain extended method signature possibilities (Pageable, Sort) +- replace findByLastname(Pageable pageable) in CustomerService + +6. First summary +---------------- +- implementation got obsolete +- switch to after package + +7. Querydsl / Specifications +---------------------------- +- Introduce Querydsl +- Show Maven setup +- Show predicates +- Integrate test case + +7. Custom implementation +------------------------ +- copy interface and implementation (hint to visibility) +- adapt AccountRepository +- copy test case +- explain implementation class lookup + +8. JDBC implementation (optional) +----------------------- +- copy implementation -> configuration necessary +- copy XML +- run tests -> fail (shows that JDBc bean is used) +- fix SQL by adding FROM -> tests run again + +9. Auditing (optional) +----------------------- +- show auditing test from Hades sample project + +10. Back to slide deck \ No newline at end of file diff --git a/jpa/spring-data-jpa-showcase/pom.xml b/jpa/spring-data-jpa-showcase/pom.xml new file mode 100644 index 00000000..dabf62ee --- /dev/null +++ b/jpa/spring-data-jpa-showcase/pom.xml @@ -0,0 +1,128 @@ + + 4.0.0 + + spring-data-jpa-showcase + + + org.springframework.data.examples + spring-data-jpa-examples + 1.0.0.BUILD-SNAPSHOT + ../pom.xml + + + Spring Data JPA - Refactoring showcase + Sample project showing how Spring Data JPA eases implementing repositories over a plain JPA/Spring approach + + + + + + src/main/resources + + + src/snippets/resources + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + ${basedir}/src/snippets/java + + + + + add-test-source + generate-sources + + add-test-source + + + + ${basedir}/src/test-snippets/java + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/snippets/**/*.java + + + + + + + + + querydsl + + true + + + + + com.mysema.querydsl + querydsl-jpa + ${querydsl.version} + + + + + + + querydsl + QueryDsl + http://source.mysema.com/maven2/releases + + + + + + + com.mysema.maven + apt-maven-plugin + ${apt.version} + + + com.mysema.querydsl + querydsl-apt + ${querydsl.version} + + + + + generate-sources + + process + + + target/generated-sources/queries + com.mysema.query.apt.jpa.JPAAnnotationProcessor + + + + + + + + + + \ No newline at end of file diff --git a/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/after/AccountRepository.java b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/after/AccountRepository.java new file mode 100644 index 00000000..3d40aa72 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/after/AccountRepository.java @@ -0,0 +1,23 @@ +package org.springframework.data.jpa.showcase.after; + +import java.util.List; + +import org.springframework.data.jpa.showcase.core.Account; +import org.springframework.data.jpa.showcase.core.Customer; +import org.springframework.data.repository.CrudRepository; + +/** + * Repository to manage {@link Account} instances. + * + * @author Oliver Gierke + */ +public interface AccountRepository extends CrudRepository { + + /** + * Returns all accounts belonging to the given {@link Customer}. + * + * @param customer + * @return + */ + List findByCustomer(Customer customer); +} diff --git a/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/after/CustomerRepository.java b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/after/CustomerRepository.java new file mode 100644 index 00000000..9bd4e718 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/after/CustomerRepository.java @@ -0,0 +1,24 @@ +package org.springframework.data.jpa.showcase.after; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.showcase.core.Customer; +import org.springframework.data.repository.CrudRepository; + +/** + * Repository to manage {@link Customer} instances. + * + * @author Oliver Gierke + */ +public interface CustomerRepository extends CrudRepository, JpaSpecificationExecutor { + + /** + * Returns a page of {@link Customer}s with the given lastname. + * + * @param lastname + * @param pageable + * @return + */ + Page findByLastname(String lastname, Pageable pageable); +} diff --git a/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/AccountService.java b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/AccountService.java new file mode 100644 index 00000000..a5c4a09f --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/AccountService.java @@ -0,0 +1,30 @@ +package org.springframework.data.jpa.showcase.before; + +import java.util.List; + +import org.springframework.data.jpa.showcase.core.Account; +import org.springframework.data.jpa.showcase.core.Customer; + +/** + * Service interface for {@link Account}s. + * + * @author Oliver Gierke + */ +public interface AccountService { + + /** + * Saves the given {@link Account}. + * + * @param account + * @return + */ + Account save(Account account); + + /** + * Returns all {@link Account}s of the given {@link Customer}. + * + * @param customer + * @return + */ + List findByCustomer(Customer customer); +} diff --git a/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/AccountServiceImpl.java b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/AccountServiceImpl.java new file mode 100644 index 00000000..728acbcc --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/AccountServiceImpl.java @@ -0,0 +1,54 @@ +package org.springframework.data.jpa.showcase.before; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.TypedQuery; + +import org.springframework.data.jpa.showcase.core.Account; +import org.springframework.data.jpa.showcase.core.Customer; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +/** + * Plain JPA implementation of {@link AccountService}. + * + * @author Oliver Gierke + */ +@Repository +@Transactional(readOnly = true) +class AccountServiceImpl implements AccountService { + + @PersistenceContext + private EntityManager em; + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.showcase.before.AccountService#save(org.springframework.data.jpa.showcase.core.Account) + */ + @Override + @Transactional + public Account save(Account account) { + + if (account.getId() == null) { + em.persist(account); + return account; + } else { + return em.merge(account); + } + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.showcase.before.AccountService#findByCustomer(org.springframework.data.jpa.showcase.core.Customer) + */ + @Override + public List findByCustomer(Customer customer) { + + TypedQuery query = em.createQuery("select a from Account a where a.customer = ?1", Account.class); + query.setParameter(1, customer); + + return query.getResultList(); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/CustomerService.java b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/CustomerService.java new file mode 100644 index 00000000..96413871 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/CustomerService.java @@ -0,0 +1,56 @@ +package org.springframework.data.jpa.showcase.before; + +import java.util.List; + +import org.springframework.data.jpa.showcase.core.Customer; + +/** + * Service interface for {@link Customer}s. + * + * @author Oliver Gierke + */ +public interface CustomerService { + + /** + * Returns the {@link Customer} with the given id or {@literal null} if no {@link Customer} with the given id was + * found. + * + * @param id + * @return + */ + Customer findById(Long id); + + /** + * Saves the given {@link Customer}. + * + * @param customer + * @return + */ + Customer save(Customer customer); + + /** + * Returns all customers. + * + * @return + */ + List findAll(); + + /** + * Returns the page of {@link Customer}s with the given index of the given size. + * + * @param page + * @param pageSize + * @return + */ + List findAll(int page, int pageSize); + + /** + * Returns the page of {@link Customer}s with the given lastname and the given page index and page size. + * + * @param lastname + * @param page + * @param pageSize + * @return + */ + List findByLastname(String lastname, int page, int pageSize); +} diff --git a/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/CustomerServiceImpl.java b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/CustomerServiceImpl.java new file mode 100644 index 00000000..074e767d --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/before/CustomerServiceImpl.java @@ -0,0 +1,90 @@ +package org.springframework.data.jpa.showcase.before; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.TypedQuery; + +import org.springframework.data.jpa.showcase.core.Customer; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +/** + * Plain JPA implementation of {@link CustomerService}. + * + * @author Oliver Gierke + */ +@Repository +@Transactional(readOnly = true) +public class CustomerServiceImpl implements CustomerService { + + @PersistenceContext + private EntityManager em; + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.showcase.before.CustomerService#findById(java.lang.Long) + */ + @Override + public Customer findById(Long id) { + return em.find(Customer.class, id); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.showcase.before.CustomerService#findAll() + */ + @Override + public List findAll() { + return em.createQuery("select c from Customer c", Customer.class).getResultList(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.showcase.before.CustomerService#findAll(int, int) + */ + @Override + public List findAll(int page, int pageSize) { + + TypedQuery query = em.createQuery("select c from Customer c", Customer.class); + + query.setFirstResult(page * pageSize); + query.setMaxResults(pageSize); + + return query.getResultList(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.showcase.before.CustomerService#save(org.springframework.data.jpa.showcase.core.Customer) + */ + @Override + @Transactional + public Customer save(Customer customer) { + + // Is new? + if (customer.getId() == null) { + em.persist(customer); + return customer; + } else { + return em.merge(customer); + } + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.showcase.before.CustomerService#findByLastname(java.lang.String, int, int) + */ + @Override + public List findByLastname(String lastname, int page, int pageSize) { + + TypedQuery query = em.createQuery("select c from Customer c where c.lastname = ?1", Customer.class); + + query.setParameter(1, lastname); + query.setFirstResult(page * pageSize); + query.setMaxResults(pageSize); + + return query.getResultList(); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/core/Account.java b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/core/Account.java new file mode 100644 index 00000000..097dc356 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/core/Account.java @@ -0,0 +1,40 @@ +package org.springframework.data.jpa.showcase.core; + +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +/** + * @author Oliver Gierke + */ +@Entity +public class Account { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @ManyToOne + private Customer customer; + + @Temporal(TemporalType.DATE) + private Date expiryDate; + + public Long getId() { + return id; + } + + public Customer getCustomer() { + return customer; + } + + public Date getExpiryDate() { + return expiryDate; + } +} diff --git a/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/core/Customer.java b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/core/Customer.java new file mode 100644 index 00000000..77da23c5 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/main/java/org/springframework/data/jpa/showcase/core/Customer.java @@ -0,0 +1,32 @@ +package org.springframework.data.jpa.showcase.core; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +/** + * @author Oliver Gierke + */ +@Entity +public class Customer { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String firstname; + private String lastname; + + public Long getId() { + return id; + } + + public String getFirstname() { + return firstname; + } + + public String getLastname() { + return lastname; + } +} diff --git a/jpa/spring-data-jpa-showcase/src/main/resources/data.sql b/jpa/spring-data-jpa-showcase/src/main/resources/data.sql new file mode 100644 index 00000000..443923bc --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/main/resources/data.sql @@ -0,0 +1,10 @@ +INSERT INTO Customer (id, firstname, lastname) VALUES (1, 'Dave', 'Matthews'); +INSERT INTO Customer (id, firstname, lastname) VALUES (2, 'Carter', 'Beauford'); +INSERT INTO Customer (id, firstname, lastname) VALUES (3, 'Stephan', 'Lassard'); + +INSERT INTO Account (id, customer_id, expiry_date) VALUES (1, 1, '2010-12-31'); +INSERT INTO Account (id, customer_id, expiry_date) VALUES (2, 1, '2011-03-31'); + +INSERT INTO Customer (id, firstname, lastname) VALUES (4, 'Charly', 'Matthews'); +INSERT INTO Customer (id, firstname, lastname) VALUES (5, 'Chris', 'Matthews'); +INSERT INTO Customer (id, firstname, lastname) VALUES (6, 'Paula', 'Matthews'); \ No newline at end of file diff --git a/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountPredicates.java b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountPredicates.java new file mode 100644 index 00000000..828a0f22 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountPredicates.java @@ -0,0 +1,25 @@ +package org.springframework.data.jpa.showcase.snippets; + +import org.joda.time.LocalDate; +import org.springframework.data.jpa.showcase.core.Account; +import org.springframework.data.jpa.showcase.core.QAccount; + +import com.mysema.query.types.expr.BooleanExpression; + +/** + * Predicates for {@link Account}s. + * + * @author Oliver Gierke + */ +public class AccountPredicates { + + private static QAccount $ = QAccount.account; + + public static BooleanExpression isExpired() { + return expiresBefore(new LocalDate()); + } + + public static BooleanExpression expiresBefore(LocalDate date) { + return $.expiryDate.before(date.toDateTimeAtStartOfDay().toDate()); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepository.java b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepository.java new file mode 100644 index 00000000..85520f76 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepository.java @@ -0,0 +1,27 @@ +package org.springframework.data.jpa.showcase.snippets; + +import java.util.List; + +import org.springframework.data.jpa.showcase.core.Account; +import org.springframework.data.jpa.showcase.core.Customer; +import org.springframework.data.querydsl.QueryDslPredicateExecutor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.NoRepositoryBean; + +/** + * Repository to manage {@link Account} instances. + * + * @author Oliver Gierke + */ +@NoRepositoryBean +public interface AccountRepository extends CrudRepository, AccountRepositoryCustom, + QueryDslPredicateExecutor { + + /** + * Returns all accounts belonging to the given {@link Customer}. + * + * @param customer + * @return + */ + List findByCustomer(Customer customer); +} diff --git a/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryCustom.java b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryCustom.java new file mode 100644 index 00000000..dcae0a5f --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryCustom.java @@ -0,0 +1,11 @@ +package org.springframework.data.jpa.showcase.snippets; + +import org.joda.time.LocalDate; + +/** + * @author Oliver Gierke + */ +interface AccountRepositoryCustom { + + void removedExpiredAccounts(LocalDate reference); +} diff --git a/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryImpl.java b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryImpl.java new file mode 100644 index 00000000..9dce0b0f --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryImpl.java @@ -0,0 +1,40 @@ +package org.springframework.data.jpa.showcase.snippets; + +import java.util.Date; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.joda.time.LocalDate; +import org.springframework.data.jpa.showcase.core.Account; +import org.springframework.stereotype.Repository; + +/** + * @author Oliver Gierke + */ +@Repository +class AccountRepositoryImpl implements AccountRepositoryCustom { + + @PersistenceContext private EntityManager em; + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.showcase.snippets.AccountRepositoryCustom#removedExpiredAccounts(org.joda.time.LocalDate) + */ + @Override + public void removedExpiredAccounts(LocalDate reference) { + + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Account.class); + Root account = query.from(Account.class); + + query.where(cb.lessThan(account.get("expiryDate").as(Date.class), reference.toDateTimeAtStartOfDay().toDate())); + + for (Account each : em.createQuery(query).getResultList()) { + em.remove(each); + } + } +} diff --git a/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryJdbcImpl.java b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryJdbcImpl.java new file mode 100644 index 00000000..bb6448f8 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/AccountRepositoryJdbcImpl.java @@ -0,0 +1,27 @@ +package org.springframework.data.jpa.showcase.snippets; + +import org.joda.time.LocalDate; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +/** + * @author Oliver Gierke + */ +@Repository +class AccountRepositoryJdbcImpl implements AccountRepositoryCustom { + + private JdbcTemplate template; + + public void setTemplate(JdbcTemplate template) { + this.template = template; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.showcase.snippets.AccountRepositoryCustom#removedExpiredAccounts(org.joda.time.LocalDate) + */ + @Override + public void removedExpiredAccounts(LocalDate reference) { + template.update("DELETE Account AS a WHERE a.expiryDate < ?", reference.toDateTimeAtStartOfDay().toDate()); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/CustomerSpecifications.java b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/CustomerSpecifications.java new file mode 100644 index 00000000..34e0c461 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/snippets/java/org/springframework/data/jpa/showcase/snippets/CustomerSpecifications.java @@ -0,0 +1,45 @@ +package org.springframework.data.jpa.showcase.snippets; + +import java.util.Date; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Path; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + +import org.joda.time.LocalDate; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.showcase.core.Account; +import org.springframework.data.jpa.showcase.core.Customer; + +/** + * Collection of {@link Specification} implementations. + * + * @author Oliver Gierke + */ +public class CustomerSpecifications { + + /** + * All customers with an {@link Account} expiring before the given date. + * + * @param date + * @return + */ + public static Specification accountExpiresBefore(final LocalDate date) { + + return new Specification() { + + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + + Root accounts = query.from(Account.class); + Path expiryDate = accounts. get("expiryDate"); + Predicate customerIsAccountOwner = cb.equal(accounts. get("customer"), root); + Predicate accountExpiryDateBefore = cb.lessThan(expiryDate, date.toDateTimeAtStartOfDay().toDate()); + + return cb.and(customerIsAccountOwner, accountExpiryDateBefore); + } + }; + } +} diff --git a/jpa/spring-data-jpa-showcase/src/snippets/resources/jdbc.xml b/jpa/spring-data-jpa-showcase/src/snippets/resources/jdbc.xml new file mode 100644 index 00000000..41a3a29b --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/snippets/resources/jdbc.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/jpa/spring-data-jpa-showcase/src/test-snippets/java/org/springframework/data/jpa/showcase/snippets/test/AccountRepositoryIntegrationTest.java b/jpa/spring-data-jpa-showcase/src/test-snippets/java/org/springframework/data/jpa/showcase/snippets/test/AccountRepositoryIntegrationTest.java new file mode 100644 index 00000000..752cf626 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/test-snippets/java/org/springframework/data/jpa/showcase/snippets/test/AccountRepositoryIntegrationTest.java @@ -0,0 +1,34 @@ +package org.springframework.data.jpa.showcase.snippets.test; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.jpa.showcase.snippets.AccountPredicates.*; + +import org.joda.time.LocalDate; +import org.springframework.data.jpa.showcase.core.Account; +import org.springframework.data.jpa.showcase.snippets.AccountRepository; + +/** + * @author Oliver Gierke + */ +public abstract class AccountRepositoryIntegrationTest { + + private AccountRepository accountRepository; + + public void removesExpiredAccountsCorrectly() throws Exception { + + accountRepository.removedExpiredAccounts(new LocalDate(2011, 1, 1)); + assertThat(accountRepository.count(), is(1L)); + } + + public void findsExpiredAccounts() { + + Account expired = accountRepository.findOne(1L); + Account valid = accountRepository.findOne(2L); + + Iterable findAll = accountRepository.findAll(expiresBefore(new LocalDate(2011, 3, 1))); + + assertThat(findAll, hasItem(expired)); + assertThat(findAll, not(hasItem(valid))); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/test-snippets/java/org/springframework/data/jpa/showcase/snippets/test/CustomerRepositoryIntegrationTest.java b/jpa/spring-data-jpa-showcase/src/test-snippets/java/org/springframework/data/jpa/showcase/snippets/test/CustomerRepositoryIntegrationTest.java new file mode 100644 index 00000000..d51cf49d --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/test-snippets/java/org/springframework/data/jpa/showcase/snippets/test/CustomerRepositoryIntegrationTest.java @@ -0,0 +1,34 @@ +package org.springframework.data.jpa.showcase.snippets.test; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.jpa.domain.Specifications.*; +import static org.springframework.data.jpa.showcase.snippets.CustomerSpecifications.*; + +import java.util.List; + +import org.joda.time.LocalDate; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.showcase.after.CustomerRepository; +import org.springframework.data.jpa.showcase.core.Customer; + +/** + * Snippets to show the usage of {@link Specification}s. + * + * @author Oliver Gierke + */ +public class CustomerRepositoryIntegrationTest { + + private CustomerRepository repository; + + public void findsCustomersBySpecification() throws Exception { + + Customer dave = repository.findOne(1L); + + LocalDate expiryLimit = new LocalDate(2011, 3, 1); + List result = repository.findAll(where(accountExpiresBefore(expiryLimit))); + + assertThat(result.size(), is(1)); + assertThat(result, hasItems(dave)); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/AbstractShowcaseTest.java b/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/AbstractShowcaseTest.java new file mode 100644 index 00000000..4f23ba21 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/AbstractShowcaseTest.java @@ -0,0 +1,35 @@ +package org.springframework.data.jpa.showcase; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.jpa.showcase.AbstractShowcaseTest.TestConfig; +import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; +import org.springframework.test.context.transaction.BeforeTransaction; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Oliver Gierke + */ +@SpringApplicationConfiguration(classes = TestConfig.class) +@Transactional +public abstract class AbstractShowcaseTest extends AbstractTransactionalJUnit4SpringContextTests { + + @Configuration + @EnableAutoConfiguration + // TODO: Remove once Boot can work with Codd + @EnableJpaRepositories + @ComponentScan + static class TestConfig { + + } + + @BeforeTransaction + public void setupData() throws Exception { + + deleteFromTables("account", "customer"); + executeSqlScript("classpath:data.sql", false); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/after/AccountRepositoryIntegrationTest.java b/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/after/AccountRepositoryIntegrationTest.java new file mode 100644 index 00000000..efcb9e3c --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/after/AccountRepositoryIntegrationTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2011-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 org.springframework.data.jpa.showcase.after; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.showcase.AbstractShowcaseTest; +import org.springframework.data.jpa.showcase.core.Account; +import org.springframework.data.jpa.showcase.core.Customer; + +/** + * Integration tests for Spring Data JPA {@link AccountRepository}. + * + * @author Oliver Gierke + */ +public class AccountRepositoryIntegrationTest extends AbstractShowcaseTest { + + @Autowired AccountRepository accountRepository; + @Autowired CustomerRepository customerRepository; + + @Test + public void savesAccount() { + + Account account = accountRepository.save(new Account()); + assertThat(account.getId(), is(notNullValue())); + } + + @Test + public void findsCustomersAccounts() { + + Customer customer = customerRepository.findOne(1L); + List accounts = accountRepository.findByCustomer(customer); + + assertFalse(accounts.isEmpty()); + assertThat(accounts.get(0).getCustomer(), is(customer)); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/after/CustomerRepositoryIntegrationTest.java b/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/after/CustomerRepositoryIntegrationTest.java new file mode 100644 index 00000000..41e6359c --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/after/CustomerRepositoryIntegrationTest.java @@ -0,0 +1,80 @@ +/* + * Copyright 2011-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 org.springframework.data.jpa.showcase.after; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.jpa.domain.Specifications.*; +import static org.springframework.data.jpa.showcase.snippets.CustomerSpecifications.*; + +import java.util.List; + +import org.joda.time.LocalDate; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.showcase.AbstractShowcaseTest; +import org.springframework.data.jpa.showcase.core.Customer; + +/** + * Integration tests for Spring Data JPA {@link CustomerRepository}. + * + * @author Oliver Gierke + */ +public class CustomerRepositoryIntegrationTest extends AbstractShowcaseTest { + + @Autowired CustomerRepository repository; + + @Test + public void findsAllCustomers() throws Exception { + + Iterable result = repository.findAll(); + + assertThat(result, is(notNullValue())); + assertTrue(result.iterator().hasNext()); + } + + @Test + public void findsFirstPageOfMatthews() throws Exception { + + Page customers = repository.findByLastname("Matthews", new PageRequest(0, 2)); + + assertThat(customers.getContent().size(), is(2)); + assertFalse(customers.hasPreviousPage()); + } + + @Test + public void findsCustomerById() throws Exception { + + Customer customer = repository.findOne(2L); + + assertThat(customer.getFirstname(), is("Carter")); + assertThat(customer.getLastname(), is("Beauford")); + } + + @Test + public void findsCustomersBySpecification() throws Exception { + + Customer dave = repository.findOne(1L); + + LocalDate expiryLimit = new LocalDate(2011, 3, 1); + List result = repository.findAll(where(accountExpiresBefore(expiryLimit))); + + assertThat(result.size(), is(1)); + assertThat(result, hasItems(dave)); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/before/AccountServiceIntegrationTest.java b/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/before/AccountServiceIntegrationTest.java new file mode 100644 index 00000000..134d0123 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/before/AccountServiceIntegrationTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2011-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 org.springframework.data.jpa.showcase.before; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.showcase.AbstractShowcaseTest; +import org.springframework.data.jpa.showcase.core.Account; +import org.springframework.data.jpa.showcase.core.Customer; + +/** + * Integration test for {@link AccountService}. + * + * @author Oliver Gierke + */ +public class AccountServiceIntegrationTest extends AbstractShowcaseTest { + + @Autowired AccountService accountService; + @Autowired CustomerService customerService; + + @Test + public void savesAccount() { + + Account account = accountService.save(new Account()); + assertThat(account.getId(), is(notNullValue())); + } + + @Test + public void testname() throws Exception { + + Customer customer = customerService.findById(1L); + + List accounts = accountService.findByCustomer(customer); + + assertThat(accounts, is(not(empty()))); + assertThat(accounts.get(0).getCustomer(), is(customer)); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/before/CustomerServiceIntegrationTest.java b/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/before/CustomerServiceIntegrationTest.java new file mode 100644 index 00000000..2ada0d05 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/test/java/org/springframework/data/jpa/showcase/before/CustomerServiceIntegrationTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2011-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 org.springframework.data.jpa.showcase.before; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.showcase.AbstractShowcaseTest; +import org.springframework.data.jpa.showcase.core.Customer; + +/** + * Integration test for {@link CustomerService}. + * + * @author Oliver Gierke + */ +public class CustomerServiceIntegrationTest extends AbstractShowcaseTest { + + @Autowired CustomerService repository; + + @Test + public void findsAllCustomers() throws Exception { + + List result = repository.findAll(); + + assertThat(result, is(notNullValue())); + assertFalse(result.isEmpty()); + } + + @Test + public void findsPageOfMatthews() throws Exception { + + List customers = repository.findByLastname("Matthews", 0, 2); + + assertThat(customers.size(), is(2)); + } + + @Test + public void findsCustomerById() throws Exception { + + Customer customer = repository.findById(2L); + + assertThat(customer.getFirstname(), is("Carter")); + assertThat(customer.getLastname(), is("Beauford")); + } +} diff --git a/jpa/spring-data-jpa-showcase/src/test/resources/logback.xml b/jpa/spring-data-jpa-showcase/src/test/resources/logback.xml new file mode 100644 index 00000000..31cf72b4 --- /dev/null +++ b/jpa/spring-data-jpa-showcase/src/test/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + + + \ No newline at end of file diff --git a/mongodb/pom.xml b/mongodb/pom.xml new file mode 100644 index 00000000..7e10f7a7 --- /dev/null +++ b/mongodb/pom.xml @@ -0,0 +1,70 @@ + + 4.0.0 + + spring-data-mongodb-example + + Spring Data MongoDB - Example + + + org.springframework.data.examples + spring-data-examples + 1.0.0.BUILD-SNAPSHOT + + + + 1.4.0.DATAMONGO-838-SNAPSHOT + + + + + + org.springframework.data + spring-data-mongodb + + + + com.mysema.querydsl + querydsl-mongodb + ${querydsl.version} + + + + + + + + com.mysema.maven + apt-maven-plugin + ${apt.version} + + + com.mysema.querydsl + querydsl-apt + ${querydsl.version} + + + + + generate-sources + + process + + + target/generated-sources/annotations + org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor + + + + + + + + + + spring-libs-snapshot + http://repo.spring.io/libs-snapshot + + + + \ No newline at end of file diff --git a/mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/Address.java b/mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/Address.java new file mode 100644 index 00000000..1f7e2a1f --- /dev/null +++ b/mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/Address.java @@ -0,0 +1,35 @@ +/* + * 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 org.springframework.data.examples.mongodb.customer; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import org.springframework.data.mongodb.core.geo.Point; + +/** + * A domain object to capture addresses. + * + * @author Oliver Gierke + */ +@Getter +@RequiredArgsConstructor +public class Address { + + private final Point location; + private String street; + private String zipCode; +} diff --git a/mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/Customer.java b/mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/Customer.java new file mode 100644 index 00000000..18a6e0cf --- /dev/null +++ b/mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/Customer.java @@ -0,0 +1,54 @@ +/* + * 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 org.springframework.data.examples.mongodb.customer; + +import lombok.Data; + +import org.bson.types.ObjectId; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.util.Assert; + +/** + * An entity to represent a customer. + * + * @author Oliver Gierke + */ +@Data +@Document +public class Customer { + + private ObjectId id; + private String firstname, lastname; + + @GeoSpatialIndexed(name = "address.location")// + private Address address; + + /** + * Creates a new {@link Customer} with the given firstname and lastname. + * + * @param firstname must not be {@literal null} or empty. + * @param lastname must not be {@literal null} or empty. + */ + public Customer(String firstname, String lastname) { + + Assert.hasText(firstname, "Firstname must not be null or empty!"); + Assert.hasText(lastname, "Lastname must not be null or empty!"); + + this.firstname = firstname; + this.lastname = lastname; + } +} diff --git a/mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/CustomerRepository.java b/mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/CustomerRepository.java new file mode 100644 index 00000000..81592d79 --- /dev/null +++ b/mongodb/src/main/java/org/springframework/data/examples/mongodb/customer/CustomerRepository.java @@ -0,0 +1,51 @@ +/* + * 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 org.springframework.data.examples.mongodb.customer; + +import java.util.List; + +import org.bson.types.ObjectId; +import org.springframework.data.domain.Sort; +import org.springframework.data.mongodb.core.geo.Distance; +import org.springframework.data.mongodb.core.geo.GeoResults; +import org.springframework.data.mongodb.core.geo.Point; +import org.springframework.data.repository.CrudRepository; + +/** + * Repository interface to manage {@link Customer} instances. + * + * @author Oliver Gierke + */ +public interface CustomerRepository extends CrudRepository { + + /** + * Derived query using dynamic sort information. + * + * @param lastname + * @param sort + * @return + */ + List findByLastname(String lastname, Sort sort); + + /** + * Showcase for a repository query using geo-spatial functionality. + * + * @param point + * @param distance + * @return + */ + GeoResults findByAddressLocationNear(Point point, Distance distance); +} diff --git a/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/Invoice.java b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/Invoice.java new file mode 100644 index 00000000..cc1a5fc4 --- /dev/null +++ b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/Invoice.java @@ -0,0 +1,36 @@ +/* + * 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 org.springframework.data.examples.mongodb.shop; + +import java.util.List; + +import lombok.Value; + +/** + * A DTO to represent invoices. + * + * @author Thomas Darimont + * @author Oliver Gierke + */ +@Value +public class Invoice { + + private final String orderId; + private final double taxAmount; + private final double netAmount; + private final double totalAmount; + private final List items; +} diff --git a/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/LineItem.java b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/LineItem.java new file mode 100644 index 00000000..ee3cc0b2 --- /dev/null +++ b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/LineItem.java @@ -0,0 +1,43 @@ +/* + * 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 org.springframework.data.examples.mongodb.shop; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import org.springframework.data.annotation.PersistenceConstructor; + +/** + * A line item. + * + * @author Thomas Darimont + * @author Oliver Gierke + */ +@Data +@RequiredArgsConstructor(onConstructor = @__(@PersistenceConstructor)) +public class LineItem { + + private final String caption; + private final double price; + + int quantity = 1; + + public LineItem(String caption, double price, int quantity) { + + this(caption, price); + this.quantity = quantity; + } +} diff --git a/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/Order.java b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/Order.java new file mode 100644 index 00000000..0b67d3ad --- /dev/null +++ b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/Order.java @@ -0,0 +1,66 @@ +/* + * 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 org.springframework.data.examples.mongodb.shop; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.mongodb.core.mapping.Document; + +/** + * An entity representing an {@link Order}. Note how we don't need any MongoDB mapping annotations as {@code id} is + * recognized as the id property by default. + * + * @author Thomas Darimont + * @author Oliver Gierke + */ +@Data +@RequiredArgsConstructor(onConstructor = @__(@PersistenceConstructor)) +@Document +public class Order { + + private final String id; + private final String customerId; + private final Date orderDate; + private final List items; + + /** + * Creates a new {@link Order} for the given customer id and order date. + * + * @param customerId + * @param orderDate + */ + public Order(String customerId, Date orderDate) { + this(null, customerId, orderDate, new ArrayList()); + } + + /** + * Adds a {@link LineItem} to the {@link Order}. + * + * @param item + * @return + */ + public Order addItem(LineItem item) { + + this.items.add(item); + return this; + } +} diff --git a/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepository.java b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepository.java new file mode 100644 index 00000000..f459ed78 --- /dev/null +++ b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepository.java @@ -0,0 +1,28 @@ +/* + * 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 org.springframework.data.examples.mongodb.shop; + +import org.springframework.data.repository.CrudRepository; + +/** + * A repository interface assembling CRUD functionality as well as the API to invoke the methods implemented manually. + * + * @author Thomas Darimont + * @author Oliver Gierke + */ +public interface OrderRepository extends CrudRepository, OrderRepositoryCustom { + +} diff --git a/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryCustom.java b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryCustom.java new file mode 100644 index 00000000..c3c795f4 --- /dev/null +++ b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryCustom.java @@ -0,0 +1,32 @@ +/* + * 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 org.springframework.data.examples.mongodb.shop; + +/** + * The interface for repository functionality that will be implemented manually. + * + * @author Oliver Gierke + */ +interface OrderRepositoryCustom { + + /** + * Creates an {@link Invoice} for the given {@link Order}. + * + * @param order must not be {@literal null}. + * @return + */ + Invoice getInvoiceFor(Order order); +} diff --git a/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryImpl.java b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryImpl.java new file mode 100644 index 00000000..c67c2fb4 --- /dev/null +++ b/mongodb/src/main/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryImpl.java @@ -0,0 +1,67 @@ +/* + * 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 org.springframework.data.examples.mongodb.shop; + +import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; +import static org.springframework.data.mongodb.core.query.Criteria.*; +import lombok.RequiredArgsConstructor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.aggregation.AggregationResults; + +/** + * The manual implementation parts for {@link OrderRepository}. This will automatically be picked up by the Spring Data + * infrastructure as we follow the naming convention of extending the core repository interface's name with {@code Impl} + * . + * + * @author Thomas Darimont + * @author Oliver Gierke + */ +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +class OrderRepositoryImpl implements OrderRepositoryCustom { + + private MongoOperations operations; + + private double taxRate = 0.19; + + /** + * The implementation uses the MongoDB aggregation framework support Spring Data provides as well as SpEL expressions + * to define arithmetical expressions. Note how we work with property names only and don't have to mitigate the nested + * {@code $_id} fields MongoDB usually requires. + * + * @see org.springframework.data.examples.mongodb.shop.OrderRepositoryCustom#getInvoiceFor(org.springframework.data.examples.mongodb.shop.Order) + */ + @Override + public Invoice getInvoiceFor(Order order) { + + AggregationResults results = operations.aggregate(newAggregation(Order.class, // + match(where("id").is(order.getId())), // + unwind("items"), // + project("id", "customerId", "items") // + .andExpression("'$items.price' * '$items.quantity'").as("lineTotal"), // + group("id") // + .sum("lineTotal").as("netAmount") // + .addToSet("items").as("items"), // + project("id", "items", "netAmount") // + .and("orderId").previousOperation() // + .andExpression("netAmount * [0]", taxRate).as("taxAmount") // + .andExpression("netAmount * (1 + [0])", taxRate).as("totalAmount") // + ), Invoice.class); + + return results.getUniqueMappedResult(); + } +} diff --git a/mongodb/src/test/java/org/springframework/data/examples/mongodb/TestConfiguration.java b/mongodb/src/test/java/org/springframework/data/examples/mongodb/TestConfiguration.java new file mode 100644 index 00000000..86eee972 --- /dev/null +++ b/mongodb/src/test/java/org/springframework/data/examples/mongodb/TestConfiguration.java @@ -0,0 +1,44 @@ +/* + * 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 org.springframework.data.examples.mongodb; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.config.AbstractMongoConfiguration; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; + +import com.mongodb.Mongo; +import com.mongodb.MongoClient; + +/** + * Test configuration to connect to a MongoDB named "test" and using a {@link MongoClient}. Also enables Spring Data + * repositories for MongoDB. + * + * @author Oliver Gierke + */ +@Configuration +@EnableMongoRepositories +public class TestConfiguration extends AbstractMongoConfiguration { + + @Override + protected String getDatabaseName() { + return "test"; + } + + @Override + public Mongo mongo() throws Exception { + return new MongoClient(); + } +} diff --git a/mongodb/src/test/java/org/springframework/data/examples/mongodb/customer/CustomerRepositoryIntegrationTest.java b/mongodb/src/test/java/org/springframework/data/examples/mongodb/customer/CustomerRepositoryIntegrationTest.java new file mode 100644 index 00000000..6d3528af --- /dev/null +++ b/mongodb/src/test/java/org/springframework/data/examples/mongodb/customer/CustomerRepositoryIntegrationTest.java @@ -0,0 +1,101 @@ +/* + * 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 org.springframework.data.examples.mongodb.customer; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.examples.mongodb.TestConfiguration; +import org.springframework.data.mongodb.core.geo.Distance; +import org.springframework.data.mongodb.core.geo.GeoResults; +import org.springframework.data.mongodb.core.geo.Metrics; +import org.springframework.data.mongodb.core.geo.Point; +import org.springframework.data.querydsl.QSort; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration test for {@link CustomerRepository}. + * + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestConfiguration.class) +public class CustomerRepositoryIntegrationTest { + + @Autowired CustomerRepository repository; + + Customer dave, oliver, carter; + + @Before + public void setUp() { + + repository.deleteAll(); + + dave = repository.save(new Customer("Dave", "Matthews")); + oliver = repository.save(new Customer("Oliver August", "Matthews")); + carter = repository.save(new Customer("Carter", "Beauford")); + } + + /** + * Test case to show that automatically generated ids are assigned to the domain objects. + */ + @Test + public void setsIdOnSave() { + + Customer dave = repository.save(new Customer("Dave", "Matthews")); + assertThat(dave.getId(), is(notNullValue())); + } + + /** + * Test case to show the usage of the Querydsl-specific {@link QSort} to define the sort order in a type-safe way. + */ + @Test + public void findCustomersUsingQuerydslSort() { + + QCustomer customer = QCustomer.customer; + List result = repository.findByLastname("Matthews", new QSort(customer.firstname.asc())); + + assertThat(result, hasSize(2)); + assertThat(result.get(0), is(dave)); + assertThat(result.get(1), is(oliver)); + } + + /** + * Test case to show the usage of the geo-spatial APIs to lookup people within a given distance of a reference point. + */ + @Test + public void exposesGeoSpatialFunctionality() { + + Customer ollie = new Customer("Oliver", "Gierke"); + ollie.setAddress(new Address(new Point(52.52548, 13.41477))); + ollie = repository.save(ollie); + + Point referenceLocation = new Point(52.51790, 13.41239); + Distance oneKilometer = new Distance(1, Metrics.KILOMETERS); + + GeoResults result = repository.findByAddressLocationNear(referenceLocation, oneKilometer); + + assertThat(result.getContent(), hasSize(1)); + assertThat(result.getContent().get(0).getDistance(), is(new Distance(0.8624842788060683, Metrics.KILOMETERS))); + } +} diff --git a/mongodb/src/test/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryIntegrationTests.java b/mongodb/src/test/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryIntegrationTests.java new file mode 100644 index 00000000..4edb0cff --- /dev/null +++ b/mongodb/src/test/java/org/springframework/data/examples/mongodb/shop/OrderRepositoryIntegrationTests.java @@ -0,0 +1,68 @@ +/* + * 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 org.springframework.data.examples.mongodb.shop; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.number.IsCloseTo.*; +import static org.junit.Assert.*; + +import java.util.Date; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.examples.mongodb.TestConfiguration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration tests for {@link OrderRepository}. + * + * @author Thomas Darimont + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestConfiguration.class) +public class OrderRepositoryIntegrationTests { + + @Autowired OrderRepository repository; + + private final static LineItem product1 = new LineItem("p1", 1.23); + private final static LineItem product2 = new LineItem("p2", 0.87, 2); + private final static LineItem product3 = new LineItem("p3", 5.33); + + @Before + public void setup() { + repository.deleteAll(); + } + + @Test + public void createsInvoiceViaAggregation() { + + Order order = new Order("c42", new Date()).// + addItem(product1).addItem(product2).addItem(product3); + order = repository.save(order); + + Invoice invoice = repository.getInvoiceFor(order); + + assertThat(invoice, is(notNullValue())); + assertThat(invoice.getOrderId(), is(order.getId())); + assertThat(invoice.getNetAmount(), is(closeTo(8.3, 000001))); + assertThat(invoice.getTaxAmount(), is(closeTo(1.577, 000001))); + assertThat(invoice.getTotalAmount(), is(closeTo(9.877, 000001))); + } +} diff --git a/mongodb/src/test/resources/logback.xml b/mongodb/src/test/resources/logback.xml new file mode 100644 index 00000000..e5b225a3 --- /dev/null +++ b/mongodb/src/test/resources/logback.xml @@ -0,0 +1,17 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..509a3990 --- /dev/null +++ b/pom.xml @@ -0,0 +1,86 @@ + + 4.0.0 + + org.springframework.data.examples + spring-data-examples + 1.0.0.BUILD-SNAPSHOT + pom + + Spring Data - Examples + + + org.springframework.boot + spring-boot-starter-parent + 1.0.0.RC1 + + + + jpa + mongodb + + + + + 4.0.1.RELEASE + 3.3.0 + 1.1.1 + + UTF-8 + + + + + + ogierke + Oliver Gierke + ogierke@gopivotal.com + + + tdarimont + Thomas Darimont + tdarimont@gopivotal.com + + + + + + + org.projectlombok + lombok + 1.12.2 + provided + + + + + + org.springframework.boot + spring-boot-starter-test + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + + + + + + spring-libs-milestone + http://repo.spring.io/libs-milestone + + + + \ No newline at end of file