Initial commit.

Moved Spring Data JPA examples from spring-data-jpa-examples repo to this one. Added samples for MongoDB.
This commit is contained in:
Oliver Gierke
2014-01-31 14:19:08 +01:00
parent 4c7d78a410
commit 4b11a9dc8f
89 changed files with 3905 additions and 3 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.project
.classpath
.springBeans
.settings/
target/

View File

@@ -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.

50
jpa/pom.xml Normal file
View File

@@ -0,0 +1,50 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-jpa-examples</artifactId>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-examples</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</parent>
<name>Spring Data JPA - Examples</name>
<description>Sample projects for Spring Data JPA</description>
<url>http://www.springframework.org/spring-data</url>
<inceptionYear>2011</inceptionYear>
<modules>
<module>spring-data-jpa-example</module>
<module>spring-data-jpa-showcase</module>
<module>spring-data-jpa-interceptors</module>
<module>spring-data-jpa-java8-auditing</module>
</modules>
<properties>
<spring-data-jpa.version>1.5.0.RC1</spring-data-jpa.version>
<hibernate-entitymanager.version>4.3.1.Final</hibernate-entitymanager.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,40 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-jpa-example</artifactId>
<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-jpa-examples</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Spring Data JPA - Example</name>
<description>Small sample project showing the usage of Spring Data JPA.</description>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -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<AuditableUser, Long> {
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;
}
}

View File

@@ -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<AuditableUser, Long> {
}

View File

@@ -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 <em>not</em>
* automatically add the {@code orm.xml} <em>if</em> 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();
}
}

View File

@@ -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.
* <p>
* TODO: change back to {@code AuditorAware<AuditableUser>} 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<Object> {
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;
}
}

View File

@@ -0,0 +1,4 @@
/**
* Package showing auditing support with Spring Data repositories.
*/
package org.springframework.data.jpa.example.repository.auditing;

View File

@@ -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;
}
}

View File

@@ -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<User, Long> {
@Override
@CacheEvict("byUsername")
<S extends User> S save(S entity);
@Cacheable("byUsername")
User findByUsername(String username);
}

View File

@@ -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<Long> {
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;
}
}

View File

@@ -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;

View File

@@ -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 {}

View File

@@ -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<Long> {
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;
}
}

View File

@@ -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<User, Long>, 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<User> 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<User> findByFirstname(String firstname);
}

View File

@@ -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<User> myCustomBatchOperation();
}

View File

@@ -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:
*
* <pre>
* EntityManager em = ... // Obtain EntityManager
*
* UserRepositoryCustom custom = new UserRepositoryImpl();
* custom.setEntityManager(em);
*
* RepositoryFactorySupport factory = new JpaRepositoryFactory(em);
* UserRepository repository = factory.getRepository(UserRepository.class, custom);
* </pre>
*
* Using the Spring namespace the implementation will just get picked up due to the classpath scanning for
* implementations with the {@code Impl} postfix.
*
* <pre>
* &lt;jpa:repositories base-package=&quot;com.acme.repository&quot; /&gt;
* </pre>
*
* 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<User> myCustomBatchOperation() {
CriteriaQuery<User> criteriaQuery = em.getCriteriaBuilder().createQuery(User.class);
criteriaQuery.select(criteriaQuery.from(User.class));
return em.createQuery(criteriaQuery).getResultList();
}
}

View File

@@ -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.
* <p>
* 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:
*
* <pre>
* &lt;jpa:repository base-package=&quot;com.acme.repository&quot; /&gt;
*
* &lt;bean id=&quot;userRepositoryImpl&quot; class=&quot;...UserRepositoryJdbcImpl&quot;&gt;
* &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot; /&gt;
* &lt;/bean&gt;
* </pre>
*
* 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<User> myCustomBatchOperation() {
return getJdbcTemplate().query(COMPLICATED_SQL, new UserRowMapper());
}
private static class UserRowMapper implements ParameterizedRowMapper<User> {
/*
* (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;
}
}
}

View File

@@ -0,0 +1,5 @@
/**
* Package showing a repository interface to use basic query method execution functionality as well as <em>customized</em> repository functionality.
*/
package org.springframework.data.jpa.example.repository.custom;

View File

@@ -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 {}

View File

@@ -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<User, Long> {
/**
* 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<User> 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<User> 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<User> findByFirstnameOrLastname(@Param("name") String name);
}

View File

@@ -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<Long> {
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;
}
}

View File

@@ -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;

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" version="2.0">
<persistence-unit-metadata>
<persistence-unit-defaults>
<entity-listeners>
<entity-listener class="org.springframework.data.jpa.domain.support.AuditingEntityListener" />
</entity-listeners>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>

View File

@@ -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());
}
}

View File

@@ -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));
}
}

View File

@@ -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<User, Long> 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, Long>(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()));
}
}

View File

@@ -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;

View File

@@ -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));
}
}

View File

@@ -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<User> 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<User> users = repository.myCustomBatchOperation();
assertNotNull(users);
assertTrue(users.contains(user));
}
}

View File

@@ -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<User> users = repository.findByLastname("lastname");
assertNotNull(users);
assertTrue(users.contains(user));
}
@Test
public void findByFirstnameOrLastname() throws Exception {
user = repository.save(user);
List<User> users = repository.findByFirstnameOrLastname("lastname");
assertTrue(users.contains(user));
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="jpa.sample.plain">
<class>org.springframework.data.jpa.example.repository.simple.User</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
<property name="hibernate.connection.url" value="jdbc:hsqldb:mem:spring" />
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />
<property name="hibernate.connection.username" value="sa" />
<property name="hibernate.connection.password" value="" />
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
</properties>
</persistence-unit>
</persistence>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<logger name="org.springframework" level="error" />
<root level="error">
<appender-ref ref="console" />
</root>
</configuration>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-jpa-examples</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>spring-data-jpa-interceptors</artifactId>
<name>Spring Data JPA - Interceptor sample</name>
</project>

View File

@@ -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());
}
}

View File

@@ -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;
}

View File

@@ -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<Customer, Long> {
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<logger name="org.springframework" level="error" />
<logger name="org.springframework.aop.interceptor" level="trace" />
<root level="error">
<appender-ref ref="console" />
</root>
</configuration>

View File

@@ -0,0 +1,23 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-jpa-examples</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-data-jpa-java8-auditing</artifactId>
<name>Spring Data JPA - Auditing on Java 8</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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<Customer, Long> {
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" version="2.0">
<persistence-unit-metadata>
<persistence-unit-defaults>
<entity-listeners>
<entity-listener class="org.springframework.data.jpa.domain.support.AuditingEntityListener" />
</entity-listeners>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>

View File

@@ -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()));
}
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<logger name="org.springframework" level="info" />
<logger name="org.springframework.boot" level="debug" />
<logger name="org.hibernate.tool.hbm2ddl" level="trace" />
<root level="error">
<appender-ref ref="console" />
</root>
</configuration>

View File

@@ -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.

View File

@@ -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

View File

@@ -0,0 +1,128 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-jpa-showcase</artifactId>
<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-jpa-examples</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Spring Data JPA - Refactoring showcase</name>
<description>Sample project showing how Spring Data JPA eases implementing repositories over a plain JPA/Spring approach</description>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/snippets/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${basedir}/src/snippets/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-test-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>${basedir}/src/test-snippets/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/snippets/**/*.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>querydsl</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>querydsl</id>
<name>QueryDsl</name>
<url>http://source.mysema.com/maven2/releases</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>${apt.version}</version>
<dependencies>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/queries</outputDirectory>
<processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -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<Account, Long> {
/**
* Returns all accounts belonging to the given {@link Customer}.
*
* @param customer
* @return
*/
List<Account> findByCustomer(Customer customer);
}

View File

@@ -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<Customer, Long>, JpaSpecificationExecutor<Customer> {
/**
* Returns a page of {@link Customer}s with the given lastname.
*
* @param lastname
* @param pageable
* @return
*/
Page<Customer> findByLastname(String lastname, Pageable pageable);
}

View File

@@ -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<Account> findByCustomer(Customer customer);
}

View File

@@ -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<Account> findByCustomer(Customer customer) {
TypedQuery<Account> query = em.createQuery("select a from Account a where a.customer = ?1", Account.class);
query.setParameter(1, customer);
return query.getResultList();
}
}

View File

@@ -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<Customer> findAll();
/**
* Returns the page of {@link Customer}s with the given index of the given size.
*
* @param page
* @param pageSize
* @return
*/
List<Customer> 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<Customer> findByLastname(String lastname, int page, int pageSize);
}

View File

@@ -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<Customer> 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<Customer> findAll(int page, int pageSize) {
TypedQuery<Customer> 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<Customer> findByLastname(String lastname, int page, int pageSize) {
TypedQuery<Customer> 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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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');

View File

@@ -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());
}
}

View File

@@ -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<Account, Long>, AccountRepositoryCustom,
QueryDslPredicateExecutor<Account> {
/**
* Returns all accounts belonging to the given {@link Customer}.
*
* @param customer
* @return
*/
List<Account> findByCustomer(Customer customer);
}

View File

@@ -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);
}

View File

@@ -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<Account> query = cb.createQuery(Account.class);
Root<Account> 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);
}
}
}

View File

@@ -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());
}
}

View File

@@ -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<Customer> accountExpiresBefore(final LocalDate date) {
return new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Root<Account> accounts = query.from(Account.class);
Path<Date> expiryDate = accounts.<Date> get("expiryDate");
Predicate customerIsAccountOwner = cb.equal(accounts.<Customer> get("customer"), root);
Predicate accountExpiryDateBefore = cb.lessThan(expiryDate, date.toDateTimeAtStartOfDay().toDate());
return cb.and(customerIsAccountOwner, accountExpiryDateBefore);
}
};
}
}

View File

@@ -0,0 +1,7 @@
<bean id="accountDaoImpl" class="org.springframework.data.jpa.showcase.after.AccountDaoJdbcImpl">
<property name="template">
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
</property>
</bean>

View File

@@ -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<Account> findAll = accountRepository.findAll(expiresBefore(new LocalDate(2011, 3, 1)));
assertThat(findAll, hasItem(expired));
assertThat(findAll, not(hasItem(valid)));
}
}

View File

@@ -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<Customer> result = repository.findAll(where(accountExpiresBefore(expiryLimit)));
assertThat(result.size(), is(1));
assertThat(result, hasItems(dave));
}
}

View File

@@ -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);
}
}

View File

@@ -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<Account> accounts = accountRepository.findByCustomer(customer);
assertFalse(accounts.isEmpty());
assertThat(accounts.get(0).getCustomer(), is(customer));
}
}

View File

@@ -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<Customer> result = repository.findAll();
assertThat(result, is(notNullValue()));
assertTrue(result.iterator().hasNext());
}
@Test
public void findsFirstPageOfMatthews() throws Exception {
Page<Customer> 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<Customer> result = repository.findAll(where(accountExpiresBefore(expiryLimit)));
assertThat(result.size(), is(1));
assertThat(result, hasItems(dave));
}
}

View File

@@ -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<Account> accounts = accountService.findByCustomer(customer);
assertThat(accounts, is(not(empty())));
assertThat(accounts.get(0).getCustomer(), is(customer));
}
}

View File

@@ -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<Customer> result = repository.findAll();
assertThat(result, is(notNullValue()));
assertFalse(result.isEmpty());
}
@Test
public void findsPageOfMatthews() throws Exception {
List<Customer> 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"));
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.data" level="debug" />
<root level="info">
<appender-ref ref="console" />
</root>
</configuration>

70
mongodb/pom.xml Normal file
View File

@@ -0,0 +1,70 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-mongodb-example</artifactId>
<name>Spring Data MongoDB - Example</name>
<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-examples</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</parent>
<properties>
<spring-data-mongo.version>1.4.0.DATAMONGO-838-SNAPSHOT</spring-data-mongo.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-mongodb</artifactId>
<version>${querydsl.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>${apt.version}</version>
<dependencies>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/annotations</outputDirectory>
<processor>org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-libs-snapshot</id>
<url>http://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
</project>

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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<Customer, ObjectId> {
/**
* Derived query using dynamic sort information.
*
* @param lastname
* @param sort
* @return
*/
List<Customer> findByLastname(String lastname, Sort sort);
/**
* Showcase for a repository query using geo-spatial functionality.
*
* @param point
* @param distance
* @return
*/
GeoResults<Customer> findByAddressLocationNear(Point point, Distance distance);
}

View File

@@ -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<LineItem> items;
}

View File

@@ -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;
}
}

View File

@@ -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<LineItem> 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<LineItem>());
}
/**
* Adds a {@link LineItem} to the {@link Order}.
*
* @param item
* @return
*/
public Order addItem(LineItem item) {
this.items.add(item);
return this;
}
}

View File

@@ -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<Order, String>, OrderRepositoryCustom {
}

View File

@@ -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);
}

View File

@@ -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<Invoice> 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();
}
}

View File

@@ -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();
}
}

View File

@@ -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<Customer> 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<Customer> result = repository.findByAddressLocationNear(referenceLocation, oneKilometer);
assertThat(result.getContent(), hasSize(1));
assertThat(result.getContent().get(0).getDistance(), is(new Distance(0.8624842788060683, Metrics.KILOMETERS)));
}
}

View File

@@ -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)));
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.data.mongodb" level="debug" />
<logger name="org.springframework.data.mongodb.core.aggregation" level="debug" />
<root level="error">
<appender-ref ref="console" />
</root>
</configuration>

86
pom.xml Normal file
View File

@@ -0,0 +1,86 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-examples</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Data - Examples</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.0.0.RC1</version>
</parent>
<modules>
<module>jpa</module>
<module>mongodb</module>
</modules>
<properties>
<spring.version>4.0.1.RELEASE</spring.version>
<querydsl.version>3.3.0</querydsl.version>
<apt.version>1.1.1</apt.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<developers>
<developer>
<id>ogierke</id>
<name>Oliver Gierke</name>
<email>ogierke@gopivotal.com</email>
</developer>
<developer>
<id>tdarimont</id>
<name>Thomas Darimont</name>
<email>tdarimont@gopivotal.com</email>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.12.2</version>
<scope>provided</scope>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-libs-milestone</id>
<url>http://repo.spring.io/libs-milestone</url>
</repository>
</repositories>
</project>