#125 - Added example to demonstrate support for JPA 2.1 EntityGraphs.

This demonstrates the use of JPA 2.1 fetch graphs in two ways.
1. By referencing a NamedEntityGraph declared on the Product entity
2. Declaratively defining an entity graph on a repository query method.

Original pull request: #126.
This commit is contained in:
Thomas Darimont
2015-08-12 23:46:03 +02:00
committed by Jens Schauder
parent 01b0a5674a
commit 322c8b5ba3
8 changed files with 296 additions and 3 deletions

View File

@@ -2,6 +2,28 @@
This project contains samples of JPA 2.1 specific features of Spring Data JPA.
## Support for declarative Fetch Graphs customization
You can customize the loading of entity associations via EntityGraphs. JPA 2.1 provides the `NamedEntityGraph` annotation
that allows you define fetching behavior in a flexible way.
In Spring Data JPA we support to specify which fetch-graph to use for a repository query method via the `EntityGraph` annotation.
You can refer to a fetch graph by name like in the following example.
```java
@EntityGraph("product-with-tags")
Product findOneById(Long id);
```
We also offer an alternative and more concise way to declarativly specify a fetch graph for a repository query method in an
ad-hoc manner:
```java
@EntityGraph(attributePaths = "tags")
Product getOneById(Long id);
```
By explicitly specifying which associations to fetch via the `attributePaths` attribute you don't need to specify a
`NamedEntityGraph` annotation on your entity :)
## Support for stored procedure execution
You can execute stored procedures either predefined using the JPA 2.1 mapping annotations or dynamically let the stored procedure definition be derived from the repository method name.

View File

@@ -16,6 +16,10 @@
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jpa.fetchgraph;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Thomas Darimont
*/
@SpringBootApplication
class FetchGraphConfiguration {}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jpa.fetchgraph;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.NamedEntityGraphs;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Thomas Darimont
*/
@Data
@NoArgsConstructor
@Entity
@NamedEntityGraphs(@NamedEntityGraph(name = "product-with-tags", attributeNodes = { @NamedAttributeNode("tags") }))
public class Product {
@Id @GeneratedValue//
Long id;
String name;
@ManyToMany(fetch = FetchType.LAZY, targetEntity = Tag.class, cascade = CascadeType.ALL)//
Set<Tag> tags = new HashSet<>();
public Product(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jpa.fetchgraph;
import javax.persistence.FetchType;
import javax.persistence.NamedEntityGraph;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author Thomas Darimont
*/
public interface ProductRepository extends JpaRepository<Product, Long> {
/**
* Here we use the {@link EntityGraph} annotation to specify that we want to use the {@link NamedEntityGraph}
* <code>product-with-tags</code> specified on the {@link Product} entity.
*
* @param id
* @return
*/
@EntityGraph("product-with-tags")
Product findWithNamedEntityGraphById(Long id);
/**
* Here we use the {@link EntityGraph} annotation to specify that we want the {@link Product#tags} association which
* is marked as {@link FetchType#LAZY} to be fetched eagerly.
*
* @param id
* @return
*/
@EntityGraph(attributePaths = "tags")
Product findWithAdhocEntityGraphById(Long id);
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jpa.fetchgraph;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Thomas Darimont
*/
@Data
@NoArgsConstructor
@Entity
public class Tag {
@Id @GeneratedValue//
Long id;
String name;
public Tag(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jpa.fetchgraph;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.Collections;
import javax.persistence.EntityManager;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration test showing the usage of JPA 2.1 fetch graph support through Spring Data JPA repositories.
*
* @author Thomas Darimont
*/
@RunWith(SpringRunner.class)
@Transactional
@SpringBootTest(classes = FetchGraphConfiguration.class)
public class FetchGraphIntegrationTests {
@Autowired EntityManager em;
@Autowired ProductRepository repository;
@Test
public void shouldFetchAssociationMarkedAsLazyViaNamedEntityFetchGraph() {
Product xps = new Product("Dell XPS 15");
Collections.addAll(xps.getTags(), new Tag("cool"), new Tag("macbook-killer"), new Tag("speed"));
xps = repository.save(xps);
repository.flush();
em.detach(xps);
Product loadedXps = repository.findById(xps.getId()).get();
em.detach(loadedXps);
try {
loadedXps.getTags().toString();
fail("Expected LazyInitializationException to occur when trying to access uninitialized association 'tags'.");
} catch (Exception expected) {
System.out.println(expected.getMessage());
}
// here we use the findOneById that uses a NamedEntityGraph
Product loadedXpsWithFetchGraph = repository.findWithNamedEntityGraphById(xps.getId());
Assert.assertThat(loadedXpsWithFetchGraph.getTags(), hasSize(3));
}
@Test
public void shouldFetchAssociationMarkedAsLazyViaCustomEntityFetchGraph() {
Product xps = new Product("Dell XPS 15");
Collections.addAll(xps.getTags(), new Tag("cool"), new Tag("macbook-killer"), new Tag("speed"));
xps = repository.save(xps);
repository.flush();
em.detach(xps);
Product loadedXps = repository.findById(xps.getId()).get();
em.detach(loadedXps);
try {
loadedXps.getTags().toString();
fail("Expected LazyInitializationException to occur when trying to access uninitialized association 'tags'.");
} catch (Exception expected) {
System.out.println(expected.getMessage());
}
// here we use getOneById which uses an ad-hoc declarative fetch graph definition
Product loadedXpsWithFetchGraph = repository.findWithAdhocEntityGraphById(xps.getId());
Assert.assertThat(loadedXpsWithFetchGraph.getTags(), hasSize(3));
}
}

View File

@@ -15,8 +15,8 @@
*/
package example.springdata.jpa.storedprocedures;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import javax.persistence.EntityManager;
import javax.persistence.ParameterMode;
@@ -30,7 +30,7 @@ import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* Intergration test showing the usage of JPA 2.1 stored procedures support through Spring Data repositories.
* Integration test showing the usage of JPA 2.1 stored procedures support through Spring Data repositories.
*
* @author Thomas Darimont
* @author Oliver Gierke