DATACMNS-89 - Infrastructure for projections on repository queries.

QueryMethods now expose a ResourceProcessor which is exposes information about the final to be created object types which can either be DTOs containing a persistence constructor (see @PersistenceConstructor) or projection interfaces. The former are analyzed for constructor properties so that store implementations can use that information to create projected queries that return exactly the fields required for that DTO.

Projection interfaces are inspected, their properties are considered input properties and the same projection queries can be issued against the data store. If a projection contains dynamically calculated properties (i.e. it uses SpEL expressions via @Value) the original entities have to be queried and can be projected during post processing.

ProjectionFactory now exposes a more advanced ProjectionInformation that has additional meta information about the projection type. ProxyProjectionFactory now refers to the BeanClassLoader instead of the ResourceLoader.

RepositoryFactory(Bean)Support now also implement BeanFactoryAware to forward the BeanFactory to the SpelAwareProxyProjectionFactory which in turn now gets handed into the QueryLookupStrategy as well as the QueryMethod.

Parameter now knows about a dynamic projection type, which is a query method parameter of type Class bound to a generic method parameter and will be used to determine the projection to be used on a per call basis.

Original pull request: #150.
This commit is contained in:
Oliver Gierke
2015-11-27 22:42:41 +01:00
parent c60cd647cc
commit dbe79f89a9
28 changed files with 1523 additions and 97 deletions

View File

@@ -0,0 +1,78 @@
/*
* 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 org.springframework.data.projection;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
/**
* Unit tests for {@link DefaultProjectionInformation}.
*
* @author Oliver Gierke
*/
public class DefaultProjectionInformationUnitTests {
/**
* @see DATACMNS-89
*/
@Test
public void discoversInputProperties() {
ProjectionInformation information = new DefaultProjectionInformation(CustomerProjection.class);
assertThat(toNames(information.getInputProperties()), contains("firstname", "lastname"));
}
/**
* @see DATACMNS-89
*/
@Test
public void discoversAllInputProperties() {
ProjectionInformation information = new DefaultProjectionInformation(ExtendedProjection.class);
assertThat(toNames(information.getInputProperties()), hasItems("age", "firstname", "lastname"));
}
private static List<String> toNames(List<PropertyDescriptor> descriptors) {
List<String> names = new ArrayList<String>(descriptors.size());
for (PropertyDescriptor descriptor : descriptors) {
names.add(descriptor.getName());
}
return names;
}
interface CustomerProjection {
String getFirstname();
String getLastname();
}
interface ExtendedProjection extends CustomerProjection {
int getAge();
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.projection;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
@@ -155,10 +156,10 @@ public class ProxyProjectionFactoryUnitTests {
@Test
public void returnsAllPropertiesAsInputProperties() {
List<String> result = factory.getInputProperties(CustomerExcerpt.class);
ProjectionInformation projectionInformation = factory.getProjectionInformation(CustomerExcerpt.class);
List<PropertyDescriptor> result = projectionInformation.getInputProperties();
assertThat(result, hasSize(5));
assertThat(result, hasItems("firstname", "address", "shippingAddresses", "picture"));
}
/**
@@ -234,6 +235,18 @@ public class ProxyProjectionFactoryUnitTests {
assertThat(excerpt.getId(), is(customer.id.toString()));
}
/**
* @see DATACMNS-89
*/
@Test
public void exposesProjectionInformationCorrectly() {
ProjectionInformation information = factory.getProjectionInformation(CustomerExcerpt.class);
assertThat(information.getType(), is(typeCompatibleWith(CustomerExcerpt.class)));
assertThat(information.isClosed(), is(true));
}
static class Customer {
public Long id;

View File

@@ -65,6 +65,17 @@ public class SpelAwareProxyProjectionFactoryUnitTests {
assertThat(properties, hasItem("firstname"));
}
/**
* @see DATACMNS-89
*/
@Test
public void considersProjectionUsingAtValueNotClosed() {
ProjectionInformation information = factory.getProjectionInformation(CustomerExcerpt.class);
assertThat(information.isClosed(), is(false));
}
static class Customer {
public String firstname, lastname;