DATAJPA-696 - Support ad-hoc entity graph definitions for repository finder methods.
We now support the specification of ad-hoc entity graphs on repository finder methods by allowing to specify the fetch graph paths via the "attributePaths" attribute on the EntityGraph annotation. Configured EclipseLink tests to use dynamic weaving, required for dynamic entity fetch graphs. Fixed typo in CrudMethodMetadataPopulatingMethodInterceptor. Original pull request: #140.
This commit is contained in:
committed by
Oliver Gierke
parent
7f45d759b3
commit
1443b155db
@@ -52,6 +52,8 @@ import javax.persistence.TemporalType;
|
||||
@NamedEntityGraphs({
|
||||
@NamedEntityGraph(name = "User.overview", attributeNodes = { @NamedAttributeNode("roles") }),
|
||||
@NamedEntityGraph(name = "User.detail", attributeNodes = { @NamedAttributeNode("roles"),
|
||||
@NamedAttributeNode("manager"), @NamedAttributeNode("colleagues") }),
|
||||
@NamedEntityGraph(name = "User.getOneWithDefinedEntityGraphById", attributeNodes = { @NamedAttributeNode("roles"),
|
||||
@NamedAttributeNode("manager"), @NamedAttributeNode("colleagues") }) })
|
||||
@NamedQuery(name = "User.findByEmailAddress", query = "SELECT u FROM User u WHERE u.emailAddress = ?1")
|
||||
@NamedStoredProcedureQueries({ //
|
||||
|
||||
@@ -15,22 +15,37 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.provider;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.*;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.*;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.ECLIPSELINK;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.GENERIC_JPA;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.HIBERNATE;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.OPEN_JPA;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.fromEntityManager;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.ECLIPSELINK_ENTITY_MANAGER_INTERFACE;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.HIBERNATE43_ENTITY_MANAGER_INTERFACE;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.HIBERNATE_ENTITY_MANAGER_INTERFACE;
|
||||
import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.OPENJPA_ENTITY_MANAGER_INTERFACE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityGraph;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Subgraph;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.asm.ClassWriter;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
|
||||
import org.springframework.data.jpa.repository.query.JpaEntityGraph;
|
||||
import org.springframework.instrument.classloading.ShadowingClassLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -103,6 +118,26 @@ public class PersistenceProviderUnitTests {
|
||||
assertThat(fromEntityManager(em), is(GENERIC_JPA));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-696
|
||||
*/
|
||||
@Test
|
||||
public void shouldBuildCorrectSubgraphForJpaEntityGraph() throws Exception {
|
||||
|
||||
EntityGraph<?> entityGraph = mock(EntityGraph.class);
|
||||
Subgraph<?> subgraph = mock(Subgraph.class);
|
||||
doReturn(subgraph).when(entityGraph).addSubgraph(anyString());
|
||||
|
||||
JpaEntityGraph jpaEntityGraph = new JpaEntityGraph("foo", EntityGraphType.FETCH,
|
||||
new String[] { "foo", "gugu.gaga" });
|
||||
|
||||
PersistenceProvider.GENERIC_JPA.configureFetchGraphFrom(jpaEntityGraph, entityGraph);
|
||||
|
||||
verify(entityGraph, times(1)).addAttributeNodes("foo");
|
||||
verify(entityGraph, times(1)).addSubgraph("gugu");
|
||||
verify(subgraph, times(1)).addAttributeNodes("gaga");
|
||||
}
|
||||
|
||||
private EntityManager mockProviderSpecificEntityManagerInterface(String interfaceName) throws ClassNotFoundException {
|
||||
|
||||
Class<?> providerSpecificEntityManagerInterface = InterfaceGenerator.generate(interfaceName, shadowingClassLoader,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -60,6 +60,10 @@ public class EntityGraphRepositoryMethodsIntegrationTests {
|
||||
role = new Role("Developer");
|
||||
em.persist(role);
|
||||
tom.getRoles().add(role);
|
||||
tom = repository.save(tom);
|
||||
|
||||
olli = repository.save(olli);
|
||||
tom.getColleagues().add(olli);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,11 +74,9 @@ public class EntityGraphRepositoryMethodsIntegrationTests {
|
||||
|
||||
Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
|
||||
|
||||
tom = repository.save(tom);
|
||||
|
||||
List<User> result = repository.findAll();
|
||||
|
||||
assertThat(result.size(), is(1));
|
||||
assertThat(result.size(), is(2));
|
||||
assertThat(Persistence.getPersistenceUtil().isLoaded(result.get(0).getRoles()), is(true));
|
||||
assertThat(result.get(0), is(tom));
|
||||
}
|
||||
@@ -87,16 +89,40 @@ public class EntityGraphRepositoryMethodsIntegrationTests {
|
||||
|
||||
Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
|
||||
|
||||
olli = repository.save(olli);
|
||||
tom.getColleagues().add(olli);
|
||||
tom = repository.save(tom);
|
||||
|
||||
em.flush();
|
||||
|
||||
User user = repository.findOne(tom.getId());
|
||||
|
||||
assertThat(user, is(notNullValue()));
|
||||
assertThat("colleages should be fetched with 'user.detail' fetchgraph",
|
||||
Persistence.getPersistenceUtil().isLoaded(user.getColleagues()), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-696
|
||||
*/
|
||||
@Test
|
||||
public void shouldRespectInferFetchGraphFromMethodName() {
|
||||
|
||||
Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
|
||||
|
||||
User user = repository.getOneWithDefinedEntityGraphById(tom.getId());
|
||||
|
||||
assertThat(user, is(notNullValue()));
|
||||
assertThat("colleages should be fetched with 'user.detail' fetchgraph",
|
||||
Persistence.getPersistenceUtil().isLoaded(user.getColleagues()), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-696
|
||||
*/
|
||||
@Test
|
||||
public void shouldRespectDynamicFetchGraphForGetOneWithAttributeNamesById() {
|
||||
|
||||
Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
|
||||
|
||||
User user = repository.getOneWithAttributeNamesById(tom.getId());
|
||||
|
||||
assertThat(user, is(notNullValue()));
|
||||
assertThat("colleages should be fetched with 'user.detail' fetchgraph",
|
||||
Persistence.getPersistenceUtil().isLoaded(user.getColleagues()), is(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,6 +324,9 @@ public class JpaQueryMethodUnitTests {
|
||||
*/
|
||||
@Test
|
||||
public void shouldStoreJpa21FetchGraphInformationAsHint() {
|
||||
|
||||
doReturn(User.class).when(metadata).getDomainType();
|
||||
doReturn(User.class).when(metadata).getReturnedDomainClass(queryMethodWithCustomEntityFetchGraph);
|
||||
|
||||
JpaQueryMethod method = new JpaQueryMethod(queryMethodWithCustomEntityFetchGraph, metadata, extractor);
|
||||
|
||||
@@ -338,6 +341,9 @@ public class JpaQueryMethodUnitTests {
|
||||
@Test
|
||||
public void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethod() throws Exception {
|
||||
|
||||
doReturn(User.class).when(metadata).getDomainType();
|
||||
doReturn(User.class).when(metadata).getReturnedDomainClass((Method)any());
|
||||
|
||||
JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findAll"), metadata, extractor);
|
||||
|
||||
assertThat(method.getEntityGraph(), is(notNullValue()));
|
||||
@@ -351,12 +357,31 @@ public class JpaQueryMethodUnitTests {
|
||||
@Test
|
||||
public void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethodFindOne() throws Exception {
|
||||
|
||||
JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findOne"), metadata, extractor);
|
||||
doReturn(User.class).when(metadata).getDomainType();
|
||||
doReturn(User.class).when(metadata).getReturnedDomainClass((Method)any());
|
||||
|
||||
JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findOne", Long.class), metadata, extractor);
|
||||
|
||||
assertThat(method.getEntityGraph(), is(notNullValue()));
|
||||
assertThat(method.getEntityGraph().getName(), is("User.detail"));
|
||||
assertThat(method.getEntityGraph().getType(), is(EntityGraphType.FETCH));
|
||||
}
|
||||
|
||||
/**
|
||||
* DATAJPA-696
|
||||
*/
|
||||
@Test
|
||||
public void shouldFindEntityGraphAnnotationOnQueryMethodGetOneByWithDerivedName() throws Exception {
|
||||
|
||||
doReturn(User.class).when(metadata).getDomainType();
|
||||
doReturn(User.class).when(metadata).getReturnedDomainClass((Method)any());
|
||||
|
||||
JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("getOneById", Long.class), metadata, extractor);
|
||||
|
||||
assertThat(method.getEntityGraph(), is(notNullValue()));
|
||||
assertThat(method.getEntityGraph().getName(), is("User.getOneById"));
|
||||
assertThat(method.getEntityGraph().getType(), is(EntityGraphType.FETCH));
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to define invalid repository methods for testing.
|
||||
@@ -433,7 +458,13 @@ public class JpaQueryMethodUnitTests {
|
||||
* DATAJPA-689
|
||||
*/
|
||||
@EntityGraph("User.detail")
|
||||
User findOne();
|
||||
User findOne(Long id);
|
||||
|
||||
/**
|
||||
* DATAJPA-696
|
||||
*/
|
||||
@EntityGraph
|
||||
User getOneById(Long id);
|
||||
}
|
||||
|
||||
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
|
||||
|
||||
@@ -41,4 +41,16 @@ public interface RepositoryMethodsWithEntityGraphConfigJpaRepository extends Jpa
|
||||
*/
|
||||
@EntityGraph(type = EntityGraphType.FETCH, value = "User.detail")
|
||||
User findOne(Integer id);
|
||||
|
||||
/**
|
||||
* @see DATAJPA-696
|
||||
*/
|
||||
@EntityGraph
|
||||
User getOneWithDefinedEntityGraphById(Integer id);
|
||||
|
||||
/**
|
||||
* @see DATAJPA-696
|
||||
*/
|
||||
@EntityGraph(attributePaths = { "roles", "colleagues.roles" })
|
||||
User getOneWithAttributeNamesById(Integer id);
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.jpa.repository.Lock;
|
||||
import org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor.CrudMethodMetadataPopulatingMethodIntercceptor;
|
||||
import org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor.CrudMethodMetadataPopulatingMethodInterceptor;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link CrudMethodMetadataPopulatingMethodIntercceptor}.
|
||||
* Unit tests for {@link CrudMethodMetadataPopulatingMethodInterceptor}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -51,7 +51,7 @@ public class CrudMethodMetadataPopulatingMethodInterceptorUnitTests {
|
||||
Method method = Sample.class.getMethod("someMethod");
|
||||
when(invocation.getMethod()).thenReturn(method);
|
||||
|
||||
CrudMethodMetadataPopulatingMethodIntercceptor interceptor = CrudMethodMetadataPopulatingMethodIntercceptor.INSTANCE;
|
||||
CrudMethodMetadataPopulatingMethodInterceptor interceptor = CrudMethodMetadataPopulatingMethodInterceptor.INSTANCE;
|
||||
interceptor.invoke(invocation);
|
||||
|
||||
assertThat(TransactionSynchronizationManager.getResource(method), is(nullValue()));
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.support;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.EntityGraph;
|
||||
import javax.persistence.EntityManager;
|
||||
@@ -33,7 +37,7 @@ import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
|
||||
import org.springframework.data.jpa.repository.query.JpaEntityGraph;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SimpleJpaRepository}.
|
||||
@@ -55,6 +59,7 @@ public class SimpleJpaRepositoryUnitTests {
|
||||
@Mock JpaEntityInformation<User, Long> information;
|
||||
@Mock CrudMethodMetadata metadata;
|
||||
@Mock EntityGraph<User> entityGraph;
|
||||
@Mock org.springframework.data.jpa.repository.EntityGraph entityGraphAnnotation;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -97,15 +102,20 @@ public class SimpleJpaRepositoryUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAJPA-689
|
||||
* @see DATAJPA-696
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void shouldPropagateConfiguredEntityGraphToFindOne() {
|
||||
public void shouldPropagateConfiguredEntityGraphToFindOne() throws Exception{
|
||||
|
||||
String entityGraphName = "User.detail";
|
||||
when(metadata.getEntityGraph()).thenReturn(new JpaEntityGraph(entityGraphName, EntityGraphType.LOAD));
|
||||
when(entityGraphAnnotation.value()).thenReturn(entityGraphName);
|
||||
when(entityGraphAnnotation.type()).thenReturn(EntityGraphType.LOAD);
|
||||
when(metadata.getEntityGraph()).thenReturn(entityGraphAnnotation);
|
||||
when(em.getEntityGraph(entityGraphName)).thenReturn((EntityGraph) entityGraph);
|
||||
|
||||
when(information.getEntityName()).thenReturn("User");
|
||||
when(metadata.getMethod()).thenReturn(CrudRepository.class.getMethod("findOne", Serializable.class));
|
||||
|
||||
Integer id = 0;
|
||||
repo.findOne(id);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user