DATACMNS-1275 - Introduced MappingContext.findPersistentPropertyPaths(Class<?>, Predicate<P>).
MappingContext now exposes a method to detect all property paths pointing to properties matching a given predicate. Extracted PersistentPropertyPath creation into a dedicated factory class so that it can be tested individually. DefaultPersistentPropertyPath now exposes a ….containsPropertyOfType(…) to detect whether we've already processed a particular type in the path. Also applied a bit of Java 8 and Lombok polish. InvalidPersistentPropertyPath now collects suggested alternatives to create a better exception message. PersistentEntities now allows to map over a MappingContext and PersistentEntity that a given type is corresponding to. Streamable now exposes an ….isEmpty(). Removed references to equivalent methods implemented in subtypes.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2018 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.mapping;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.mapping.context.SampleMappingContext;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PersistentPropertyAccessorUnitTests {
|
||||
|
||||
SampleMappingContext context = new SampleMappingContext();
|
||||
|
||||
PersistentPropertyPath<? extends PersistentProperty<?>> path;
|
||||
PersistentPropertyAccessor accessor;
|
||||
|
||||
private void setUp(Order order, String path) {
|
||||
|
||||
this.accessor = context.getPersistentEntity(Order.class).getPropertyAccessor(order);
|
||||
this.path = context.getPersistentPropertyPath(path, Order.class);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void looksUpValueForPropertyPath() {
|
||||
|
||||
Order order = new Order(new Customer("Dave"));
|
||||
|
||||
setUp(order, "customer.firstname");
|
||||
|
||||
assertThat(accessor.getProperty(path)).isEqualTo("Dave");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void setsPropertyOnNestedPath() {
|
||||
|
||||
Customer customer = new Customer("Dave");
|
||||
Order order = new Order(customer);
|
||||
|
||||
setUp(order, "customer.firstname");
|
||||
|
||||
accessor.setProperty(path, "Oliver August");
|
||||
|
||||
assertThat(customer.firstname).isEqualTo("Oliver August");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void rejectsEmptyPathToSetValues() {
|
||||
|
||||
setUp(new Order(null), "");
|
||||
|
||||
assertThatExceptionOfType(IllegalArgumentException.class) //
|
||||
.isThrownBy(() -> accessor.setProperty(path, "Oliver August"));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void rejectsIntermediateNullValuesForRead() {
|
||||
|
||||
setUp(new Order(null), "customer.firstname");
|
||||
|
||||
assertThatExceptionOfType(MappingException.class)//
|
||||
.isThrownBy(() -> accessor.getProperty(path));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void rejectsIntermediateNullValuesForWrite() {
|
||||
|
||||
setUp(new Order(null), "customer.firstname");
|
||||
|
||||
assertThatExceptionOfType(MappingException.class)//
|
||||
.isThrownBy(() -> accessor.setProperty(path, "Oliver August"));
|
||||
}
|
||||
|
||||
@Value
|
||||
static class Order {
|
||||
Customer customer;
|
||||
}
|
||||
|
||||
@Value
|
||||
static class Customer {
|
||||
String firstname;
|
||||
}
|
||||
}
|
||||
@@ -34,12 +34,10 @@ import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Unit test for {@link AbstractMappingContext}.
|
||||
@@ -54,17 +52,11 @@ public class AbstractMappingContextUnitTests {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
context = new SampleMappingContext();
|
||||
context.setSimpleTypeHolder(new SimpleTypeHolder(Collections.singleton(LocalDateTime.class), true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotTryToLookupPersistentEntityForLeafProperty() {
|
||||
PersistentPropertyPath<SamplePersistentProperty> path = context
|
||||
.getPersistentPropertyPath(PropertyPath.from("name", Person.class));
|
||||
assertThat(path).isNotNull();
|
||||
}
|
||||
|
||||
@Test(expected = MappingException.class) // DATACMNS-92
|
||||
public void doesNotAddInvalidEntity() {
|
||||
|
||||
@@ -158,22 +150,6 @@ public class AbstractMappingContextUnitTests {
|
||||
.satisfies(inner -> assertThat(((PersistentEntity) inner).getType()).isEqualTo(Person.class)));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-380
|
||||
public void returnsPersistentPropertyPathForDotPath() {
|
||||
|
||||
PersistentPropertyPath<SamplePersistentProperty> path = context.getPersistentPropertyPath("persons.name",
|
||||
Sample.class);
|
||||
|
||||
assertThat(path.getLength()).isEqualTo(2);
|
||||
assertThat(path.getBaseProperty().getName()).isEqualTo("persons");
|
||||
assertThat(path.getLeafProperty().getName()).isEqualTo("name");
|
||||
}
|
||||
|
||||
@Test(expected = MappingException.class) // DATACMNS-380
|
||||
public void rejectsInvalidPropertyReferenceWithMappingException() {
|
||||
context.getPersistentPropertyPath("foo", Sample.class);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-390
|
||||
public void exposesCopyOfPersistentEntitiesToAvoidConcurrentModificationException() {
|
||||
|
||||
@@ -220,29 +196,6 @@ public class AbstractMappingContextUnitTests {
|
||||
assertThat(context.getPersistentEntity(TypeCreatingSyntheticClass.class)).isNotNull();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-695
|
||||
public void persistentPropertyPathTraversesGenericTypesCorrectly() {
|
||||
assertThat(context.getPersistentPropertyPath("field.wrapped.field", Outer.class)).hasSize(3);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-727
|
||||
public void exposesContextForFailingPropertyPathLookup() {
|
||||
|
||||
assertThatExceptionOfType(InvalidPersistentPropertyPath.class)//
|
||||
.isThrownBy(() -> context.getPersistentPropertyPath("persons.firstname", Sample.class))//
|
||||
.matches(e -> StringUtils.hasText(e.getMessage()))//
|
||||
.matches(e -> e.getResolvedPath().equals("persons"))//
|
||||
.matches(e -> e.getUnresolvableSegment().equals("firstname"))//
|
||||
.matches(e -> context.getPersistentPropertyPath(e) != null);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1116
|
||||
public void cachesPersistentPropertyPaths() {
|
||||
|
||||
assertThat(context.getPersistentPropertyPath("persons.name", Sample.class)) //
|
||||
.isSameAs(context.getPersistentPropertyPath("persons.name", Sample.class));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1208
|
||||
public void ensureHasPersistentEntityReportsFalseForTypesThatShouldntBeCreated() {
|
||||
|
||||
@@ -299,17 +252,4 @@ public class AbstractMappingContextUnitTests {
|
||||
static class Extension extends Base {
|
||||
@Id String foo;
|
||||
}
|
||||
|
||||
static class Outer {
|
||||
|
||||
Wrapper<Inner> field;
|
||||
}
|
||||
|
||||
static class Wrapper<T> {
|
||||
T wrapped;
|
||||
}
|
||||
|
||||
static class Inner {
|
||||
String field;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.mapping.context;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -28,6 +29,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultPersistentPropertyPath}.
|
||||
@@ -35,14 +37,14 @@ import org.springframework.data.mapping.PersistentProperty;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DefaultPersistenPropertyPathUnitTests<T extends PersistentProperty<T>> {
|
||||
public class DefaultPersistentPropertyPathUnitTests<P extends PersistentProperty<P>> {
|
||||
|
||||
@Mock T first, second;
|
||||
@Mock P first, second;
|
||||
|
||||
@Mock Converter<T, String> converter;
|
||||
@Mock Converter<P, String> converter;
|
||||
|
||||
PersistentPropertyPath<T> oneLeg;
|
||||
PersistentPropertyPath<T> twoLegs;
|
||||
PersistentPropertyPath<P> oneLeg;
|
||||
PersistentPropertyPath<P> twoLegs;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -96,7 +98,7 @@ public class DefaultPersistenPropertyPathUnitTests<T extends PersistentProperty<
|
||||
@Test
|
||||
public void calculatesExtensionCorrectly() {
|
||||
|
||||
PersistentPropertyPath<T> extension = twoLegs.getExtensionForBaseOf(oneLeg);
|
||||
PersistentPropertyPath<P> extension = twoLegs.getExtensionForBaseOf(oneLeg);
|
||||
|
||||
assertThat(extension).isEqualTo(new DefaultPersistentPropertyPath<>(Collections.singletonList(second)));
|
||||
}
|
||||
@@ -107,8 +109,18 @@ public class DefaultPersistenPropertyPathUnitTests<T extends PersistentProperty<
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsItselfAsParentPathIfSizeOne() {
|
||||
assertThat(oneLeg.getParentPath()).isEqualTo(oneLeg);
|
||||
public void returnsEmptyPathForRootLevelProperty() {
|
||||
assertThat(oneLeg.getParentPath()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnItselfForEmptyPath() {
|
||||
|
||||
PersistentPropertyPath<P> parent = oneLeg.getParentPath();
|
||||
PersistentPropertyPath<P> parentsParent = parent.getParentPath();
|
||||
|
||||
assertThat(parentsParent).isEmpty();
|
||||
assertThat(parentsParent).isSameAs(parent);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2018 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.mapping.context;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.annotation.Reference;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PersistentPropertyPaths;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PersistentPropertyPathFactory}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @soundtrack Cypress Hill - Illusions (Q-Tip Remix, Unreleased & Revamped)
|
||||
*/
|
||||
public class PersistentPropertyPathFactoryUnitTests {
|
||||
|
||||
PersistentPropertyPathFactory<BasicPersistentEntity<Object, SamplePersistentProperty>, SamplePersistentProperty> factory = //
|
||||
new PersistentPropertyPathFactory<>(new SampleMappingContext());
|
||||
|
||||
@Test
|
||||
public void doesNotTryToLookupPersistentEntityForLeafProperty() {
|
||||
assertThat(factory.from(Person.class, "name")).isNotNull();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-380
|
||||
public void returnsPersistentPropertyPathForDotPath() {
|
||||
|
||||
PersistentPropertyPath<SamplePersistentProperty> path = factory.from(PersonSample.class, "persons.name");
|
||||
|
||||
assertThat(path.getLength()).isEqualTo(2);
|
||||
assertThat(path.getBaseProperty().getName()).isEqualTo("persons");
|
||||
assertThat(path.getLeafProperty().getName()).isEqualTo("name");
|
||||
}
|
||||
|
||||
@Test(expected = MappingException.class) // DATACMNS-380
|
||||
public void rejectsInvalidPropertyReferenceWithMappingException() {
|
||||
factory.from(PersonSample.class, "foo");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-695
|
||||
public void persistentPropertyPathTraversesGenericTypesCorrectly() {
|
||||
assertThat(factory.from(Outer.class, "field.wrapped.field")).hasSize(3);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-727
|
||||
public void exposesContextForFailingPropertyPathLookup() {
|
||||
|
||||
assertThatExceptionOfType(InvalidPersistentPropertyPath.class)//
|
||||
.isThrownBy(() -> factory.from(PersonSample.class, "persons.firstname"))//
|
||||
.matches(e -> StringUtils.hasText(e.getMessage()))//
|
||||
.matches(e -> e.getResolvedPath().equals("persons"))//
|
||||
.matches(e -> e.getUnresolvableSegment().equals("firstname"))//
|
||||
.matches(e -> factory.from(PersonSample.class, e.getResolvedPath()) != null);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1116
|
||||
public void cachesPersistentPropertyPaths() {
|
||||
|
||||
assertThat(factory.from(PersonSample.class, "persons.name")) //
|
||||
.isSameAs(factory.from(PersonSample.class, "persons.name"));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void findsNestedPropertyByFilter() {
|
||||
|
||||
PersistentPropertyPaths<?, SamplePersistentProperty> paths = factory.from(Sample.class,
|
||||
property -> property.findAnnotation(Inject.class) != null);
|
||||
|
||||
assertThat(paths).hasSize(1).anySatisfy(it -> it.toDotPath().equals("inner.annotatedField"));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void findsNestedPropertiesByFilter() {
|
||||
|
||||
PersistentPropertyPaths<?, SamplePersistentProperty> paths = factory.from(Wrapper.class,
|
||||
property -> property.findAnnotation(Inject.class) != null);
|
||||
|
||||
assertThat(paths).hasSize(2);//
|
||||
assertThat(paths).element(0).satisfies(it -> it.toDotPath().equals("first.inner.annotatedField"));
|
||||
assertThat(paths).element(1).satisfies(it -> it.toDotPath().equals("second.inner.annotatedField"));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void createsEmptyPropertyPathCorrectly() {
|
||||
assertThat(factory.from(Wrapper.class, "")).isEmpty();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void returnsShortestsPathsFirst() {
|
||||
|
||||
Streamable<String> paths = factory.from(First.class, it -> true, it -> true) //
|
||||
.map(PersistentPropertyPath::toDotPath);
|
||||
|
||||
assertThat(paths).containsExactly("third", "second", "third.lastname", "second.firstname");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void doesNotTraverseAssociationsByDefault() {
|
||||
|
||||
Streamable<String> paths = factory.from(First.class, it -> true) //
|
||||
.map(PersistentPropertyPath::toDotPath);
|
||||
|
||||
assertThat(paths) //
|
||||
.contains("third", "second", "second.firstname")//
|
||||
.doesNotContain("third.lastname");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void traversesAssociationsIfTraversalGuardAllowsIt() {
|
||||
|
||||
PersistentPropertyPaths<First, SamplePersistentProperty> paths = //
|
||||
factory.from(First.class, it -> true, it -> true);
|
||||
|
||||
assertThat(paths.contains("third.lastname")).isTrue();
|
||||
assertThat(paths.contains(PropertyPath.from("third.lastname", First.class)));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void returnsEmptyPropertyPathsIfNoneSelected() {
|
||||
|
||||
PersistentPropertyPaths<Third, SamplePersistentProperty> paths = factory.from(Third.class, it -> false);
|
||||
|
||||
assertThat(paths).isEmpty();
|
||||
assertThat(paths.getFirst()).isEmpty();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1275
|
||||
public void returnsShortestPathFirst() {
|
||||
|
||||
PersistentPropertyPaths<First, SamplePersistentProperty> paths = factory.from(First.class, it -> !it.isEntity(),
|
||||
it -> true);
|
||||
|
||||
assertThat(paths.contains("second.firstname")).isTrue();
|
||||
assertThat(paths.getFirst()) //
|
||||
.hasValueSatisfying(it -> assertThat(it.toDotPath()).isEqualTo("third.lastname"));
|
||||
}
|
||||
|
||||
static class PersonSample {
|
||||
List<Person> persons;
|
||||
}
|
||||
|
||||
class Person {
|
||||
String name;
|
||||
}
|
||||
|
||||
static class Wrapper {
|
||||
Sample first;
|
||||
Sample second;
|
||||
}
|
||||
|
||||
static class Sample {
|
||||
Inner inner;
|
||||
}
|
||||
|
||||
static class Inner {
|
||||
@Inject String annotatedField;
|
||||
Sample cyclic;
|
||||
}
|
||||
|
||||
static class Outer {
|
||||
Generic<Nested> field;
|
||||
}
|
||||
|
||||
static class Generic<T> {
|
||||
T wrapped;
|
||||
}
|
||||
|
||||
static class Nested {
|
||||
String field;
|
||||
}
|
||||
|
||||
static class First {
|
||||
Second second;
|
||||
@Reference Third third;
|
||||
}
|
||||
|
||||
static class Second {
|
||||
String firstname;
|
||||
}
|
||||
|
||||
static class Third {
|
||||
String lastname;
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,6 @@ public class AbstractPersistentPropertyUnitTests {
|
||||
@Test // DATACMNS-101
|
||||
public void returnsNestedEntityTypeCorrectly() {
|
||||
assertThat(getProperty(TestClassComplex.class, "testClassSet").getPersistentEntityTypes()).isEmpty();
|
||||
assertThat(getProperty(TestClassComplex.class, "testClassSet").getPersistentEntityType()).isEmpty();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-132
|
||||
@@ -198,7 +197,6 @@ public class AbstractPersistentPropertyUnitTests {
|
||||
|
||||
SamplePersistentProperty property = getProperty(TreeMapWrapper.class, "map");
|
||||
assertThat(property.getPersistentEntityTypes()).isEmpty();
|
||||
assertThat(property.getPersistentEntityType()).isEmpty();
|
||||
assertThat(property.isEntity()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user