Browse Source

extract some interfaces

labs/build-time-domain-type-information
Christoph Strobl 5 years ago
parent
commit
326a10f1bb
  1. 1
      spring-data-mongodb/src/main/java/org/springframework/data/mapping/PreferredConstructor.java
  2. 34
      spring-data-mongodb/src/main/java/org/springframework/data/mapping/PreferredConstructorProvider.java
  3. 22
      spring-data-mongodb/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java
  4. 54
      spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/AccessorFunctionProvider.java
  5. 22
      spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java
  6. 6
      spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/EntityInstantiators.java
  7. 50
      spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/EntiyInstantiatorProvider.java
  8. 35
      spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/PersistentPropertyAccessorFactoryProvider.java
  9. 38
      spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/StaticPropertyAccessorFactory.java
  10. 44
      spring-data-mongodb/src/main/java/org/springframework/data/util/AddressTypeInformation.java
  11. 32
      spring-data-mongodb/src/main/java/org/springframework/data/util/AnnotationProvider.java
  12. 5
      spring-data-mongodb/src/main/java/org/springframework/data/util/ClassTypeInformation.java
  13. 179
      spring-data-mongodb/src/main/java/org/springframework/data/util/Field.java
  14. 76
      spring-data-mongodb/src/main/java/org/springframework/data/util/Fields.java
  15. 10
      spring-data-mongodb/src/main/java/org/springframework/data/util/ListTypeInformation.java
  16. 15
      spring-data-mongodb/src/main/java/org/springframework/data/util/Person.java
  17. 119
      spring-data-mongodb/src/main/java/org/springframework/data/util/PersonTypeInformation.java
  18. 155
      spring-data-mongodb/src/main/java/org/springframework/data/util/StaticTypeInformation.java
  19. 5
      spring-data-mongodb/src/main/java/org/springframework/data/util/StringTypeInformation.java
  20. 104
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java
  21. 109
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/staticmetadata/StaticMetadataTests.java

1
spring-data-mongodb/src/main/java/org/springframework/data/mapping/PreferredConstructor.java

@ -52,7 +52,6 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
public PreferredConstructor() { public PreferredConstructor() {
this.constructor = null; this.constructor = null;
this.parameters = Collections.emptyList(); this.parameters = Collections.emptyList();
} }
/** /**

34
spring-data-mongodb/src/main/java/org/springframework/data/mapping/PreferredConstructorProvider.java

@ -0,0 +1,34 @@
/*
* Copyright 2020. 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 org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public interface PreferredConstructorProvider<T> {
@Nullable
<P extends PersistentProperty<P>> PreferredConstructor<T, P> getPreferredConstructor();
default <P extends PersistentProperty<P>> PreferredConstructor<T, P> getPreferredConstructorOrDefault(PreferredConstructor<T, P> fallback) {
PreferredConstructor<T, P> preferredConstructor = getPreferredConstructor();
return preferredConstructor != null ? preferredConstructor : fallback;
}
}

22
spring-data-mongodb/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java

@ -16,16 +16,13 @@
package org.springframework.data.mapping.context; package org.springframework.data.mapping.context;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
@ -379,18 +376,17 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
if (typeInformation instanceof StaticTypeInformation<?>) { if (typeInformation instanceof StaticTypeInformation<?>) {
// ((StaticTypeInformation<?>)typeInformation).doWithProperties() final E pEntity = entity;
((StaticTypeInformation<?>) typeInformation).doWithFields((fieldName, field) -> {
Map<String, TypeInformation<?>> properties = ((StaticTypeInformation<?>) typeInformation).getProperties(); System.out.println("Creating PersistentProperty for " + fieldName + " via static configuration.");
Map<String, List<Annotation>> annotations = ((StaticTypeInformation<?>) typeInformation).getPropertyAnnotations(); P target = createPersistentProperty(
for (Entry<String, TypeInformation<?>> entry : properties.entrySet()) { Property.of(field.getTypeInformation(), fieldName, field.getAnnotations()), pEntity, simpleTypeHolder);
pEntity.addPersistentProperty(target);
P target = createPersistentProperty(Property.of(entry.getValue(), entry.getKey(), annotations.get(entry.getKey())), entity, simpleTypeHolder); });
entity.addPersistentProperty(target); pEntity.setPersistentPropertyAccessorFactory(StaticPropertyAccessorFactory.instance());
return Optional.of(pEntity);
}
entity.setPersistentPropertyAccessorFactory(new StaticPropertyAccessorFactory());
return Optional.of(entity);
} }
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(type); PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(type);

54
spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/AccessorFunctionProvider.java

@ -0,0 +1,54 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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.model;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public interface AccessorFunctionProvider<S> {
default boolean hasSetFunctionFor(String fieldName) {
return getSetFunctionFor(fieldName) != null;
}
default boolean hasGetFunctionFor(String fieldName) {
return getGetFunctionFor(fieldName) != null;
}
BiFunction<S, Object, S> getSetFunctionFor(String fieldName);
Function<S, Object> getGetFunctionFor(String fieldName);
}

22
spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java

@ -37,8 +37,8 @@ import org.springframework.data.mapping.*;
import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.support.IsNewStrategy; import org.springframework.data.support.IsNewStrategy;
import org.springframework.data.support.PersistableIsNewStrategy; import org.springframework.data.support.PersistableIsNewStrategy;
import org.springframework.data.util.AnnotationProvider;
import org.springframework.data.util.Lazy; import org.springframework.data.util.Lazy;
import org.springframework.data.util.StaticTypeInformation;
import org.springframework.data.util.TypeInformation; import org.springframework.data.util.TypeInformation;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -109,14 +109,30 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
this.properties = new ArrayList<>(); this.properties = new ArrayList<>();
this.persistentPropertiesCache = new ArrayList<>(); this.persistentPropertiesCache = new ArrayList<>();
this.comparator = comparator; this.comparator = comparator;
this.constructor = information instanceof StaticTypeInformation ? ((StaticTypeInformation)information).getPreferredConstructor() : PreferredConstructorDiscoverer.discover(this);
this.constructor = information instanceof PreferredConstructorProvider
? ((PreferredConstructorProvider<T>) information).getPreferredConstructor()
: PreferredConstructorDiscoverer.discover(this);
this.associations = comparator == null ? new HashSet<>() : new TreeSet<>(new AssociationComparator<>(comparator)); this.associations = comparator == null ? new HashSet<>() : new TreeSet<>(new AssociationComparator<>(comparator));
this.propertyCache = new HashMap<>(16, 1f); this.propertyCache = new HashMap<>(16, 1f);
this.annotationCache = new ConcurrentReferenceHashMap<>(16, ReferenceType.WEAK); this.annotationCache = new ConcurrentReferenceHashMap<>(16, ReferenceType.WEAK);
if(information instanceof AnnotationProvider) {
for(Annotation annotation : ((AnnotationProvider)information).getAnnotations()) {
annotationCache.put(annotation.annotationType(), Optional.of(annotation));
}
}
this.propertyAnnotationCache = CollectionUtils this.propertyAnnotationCache = CollectionUtils
.toMultiValueMap(new ConcurrentReferenceHashMap<>(16, ReferenceType.WEAK)); .toMultiValueMap(new ConcurrentReferenceHashMap<>(16, ReferenceType.WEAK));
this.propertyAccessorFactory = BeanWrapperPropertyAccessorFactory.INSTANCE;
this.propertyAccessorFactory = information instanceof PersistentPropertyAccessorFactoryProvider
? ((PersistentPropertyAccessorFactoryProvider) information).getPersistentPropertyAccessorFactory()
: BeanWrapperPropertyAccessorFactory.INSTANCE;
this.typeAlias = Lazy.of(() -> getAliasFromAnnotation(getType())); this.typeAlias = Lazy.of(() -> getAliasFromAnnotation(getType()));
this.isNewStrategy = Lazy.of(() -> Persistable.class.isAssignableFrom(information.getType()) // this.isNewStrategy = Lazy.of(() -> Persistable.class.isAssignableFrom(information.getType()) //
? PersistableIsNewStrategy.INSTANCE ? PersistableIsNewStrategy.INSTANCE

6
spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/EntityInstantiators.java

@ -19,7 +19,6 @@ import java.util.Collections;
import java.util.Map; import java.util.Map;
import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.util.StaticTypeInformation;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -92,9 +91,8 @@ public class EntityInstantiators {
if (!customInstantiators.containsKey(type)) { if (!customInstantiators.containsKey(type)) {
if(entity.getTypeInformation() instanceof StaticTypeInformation) { if (entity.getTypeInformation() instanceof EntiyInstantiatorProvider) {
EntityInstantiator instantiator = ((StaticTypeInformation)entity.getTypeInformation()).getInstantiator(); return ((EntiyInstantiatorProvider) entity.getTypeInformation()).getEntiyInstantiatorOrDefault(fallback);
return instantiator != null ? instantiator : fallback;
} }
return fallback; return fallback;
} }

50
spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/EntiyInstantiatorProvider.java

@ -0,0 +1,50 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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.model;
import org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public interface EntiyInstantiatorProvider {
@Nullable
EntityInstantiator getEntityInstantiator();
default EntityInstantiator getEntiyInstantiatorOrDefault(EntityInstantiator fallback) {
EntityInstantiator entityInstantiator = getEntityInstantiator();
return entityInstantiator != null ? entityInstantiator : fallback;
}
}

35
spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/PersistentPropertyAccessorFactoryProvider.java

@ -0,0 +1,35 @@
/*
* Copyright 2020. 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.model;
import org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public interface PersistentPropertyAccessorFactoryProvider {
@Nullable
PersistentPropertyAccessorFactory getPersistentPropertyAccessorFactory();
default PersistentPropertyAccessorFactory getPersistentPropertyAccessorFactoryOrDefault(
PersistentPropertyAccessorFactory fallback) {
PersistentPropertyAccessorFactory factory = getPersistentPropertyAccessorFactory();
return factory != null ? factory : fallback;
}
}

38
spring-data-mongodb/src/main/java/org/springframework/data/mapping/model/StaticPropertyAccessorFactory.java

@ -31,13 +31,9 @@
*/ */
package org.springframework.data.mapping.model; package org.springframework.data.mapping.model;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.util.StaticTypeInformation;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
@ -46,13 +42,21 @@ import org.springframework.lang.Nullable;
*/ */
public class StaticPropertyAccessorFactory implements PersistentPropertyAccessorFactory { public class StaticPropertyAccessorFactory implements PersistentPropertyAccessorFactory {
private static final StaticPropertyAccessorFactory INSTANCE = new StaticPropertyAccessorFactory();
public static StaticPropertyAccessorFactory instance() {
return INSTANCE;
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.springframework.data.mapping.model.PersistentPropertyAccessorFactory#getPropertyAccessor(org.springframework.data.mapping.PersistentEntity, java.lang.Object) * @see org.springframework.data.mapping.model.PersistentPropertyAccessorFactory#getPropertyAccessor(org.springframework.data.mapping.PersistentEntity, java.lang.Object)
*/ */
@Override @Override
public <T> PersistentPropertyAccessor<T> getPropertyAccessor(PersistentEntity<?, ?> entity, T bean) { public <T> PersistentPropertyAccessor<T> getPropertyAccessor(PersistentEntity<?, ?> entity, T bean) {
return new StaticPropertyAccessor<>((StaticTypeInformation<T>) entity.getTypeInformation(), bean);
System.out.println("Obtaining static property acessor for entity " + entity.getName());
return new StaticPropertyAccessor<>((AccessorFunctionProvider<T>) entity.getTypeInformation(), bean);
} }
/* /*
@ -62,7 +66,7 @@ public class StaticPropertyAccessorFactory implements PersistentPropertyAccessor
@Override @Override
public boolean isSupported(PersistentEntity<?, ?> entity) { public boolean isSupported(PersistentEntity<?, ?> entity) {
boolean isStaticTypedEntity = entity.getTypeInformation() instanceof StaticTypeInformation; boolean isStaticTypedEntity = entity.getTypeInformation() instanceof AccessorFunctionProvider;
System.out.println(entity.getName() + " isStaticTypedEntity: " + isStaticTypedEntity); System.out.println(entity.getName() + " isStaticTypedEntity: " + isStaticTypedEntity);
return isStaticTypedEntity; return isStaticTypedEntity;
} }
@ -70,32 +74,36 @@ public class StaticPropertyAccessorFactory implements PersistentPropertyAccessor
static class StaticPropertyAccessor<T> implements PersistentPropertyAccessor<T> { static class StaticPropertyAccessor<T> implements PersistentPropertyAccessor<T> {
T bean; T bean;
StaticTypeInformation<T> typeInformation; AccessorFunctionProvider<T> accessorFunctionProvider;
public StaticPropertyAccessor(StaticTypeInformation<T> typeInformation, T bean) { public StaticPropertyAccessor(AccessorFunctionProvider<T> accessorFunctionProvider, T bean) {
this.bean = bean; this.bean = bean;
this.typeInformation = typeInformation; this.accessorFunctionProvider = accessorFunctionProvider;
} }
@Override @Override
public void setProperty(PersistentProperty<?> property, @Nullable Object value) { public void setProperty(PersistentProperty<?> property, @Nullable Object value) {
BiFunction<T, Object, T> setFunction = typeInformation.getSetter().get(property.getName()); if (!accessorFunctionProvider.hasSetFunctionFor(property.getName())) {
if (setFunction == null) {
return; return;
} }
this.bean = setFunction.apply(bean, value);
this.bean = accessorFunctionProvider.getSetFunctionFor(property.getName()).apply(bean, value);
System.out.println(
"setting value " + value + " via setter function for " + property.getName() + " resulting in " + bean);
} }
@Nullable @Nullable
@Override @Override
public Object getProperty(PersistentProperty<?> property) { public Object getProperty(PersistentProperty<?> property) {
Function<T, Object> getFunction = typeInformation.getGetter().get(property.getName()); if (!accessorFunctionProvider.hasGetFunctionFor(property.getName())) {
if (getFunction == null) {
return null; return null;
} }
return getFunction.apply(bean);
Object value = accessorFunctionProvider.getGetFunctionFor(property.getName()).apply(bean);
System.out.println("obtaining value " + value + " from getter function for " + property.getName());
return value;
} }
@Override @Override

44
spring-data-mongodb/src/main/java/org/springframework/data/util/AddressTypeInformation.java

@ -32,10 +32,6 @@
package org.springframework.data.util; package org.springframework.data.util;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentProperty;
@ -50,40 +46,21 @@ import org.springframework.data.mapping.model.ParameterValueProvider;
*/ */
public class AddressTypeInformation extends StaticTypeInformation<Address> { public class AddressTypeInformation extends StaticTypeInformation<Address> {
public AddressTypeInformation() { private static final AddressTypeInformation INSTANCE = new AddressTypeInformation();
private AddressTypeInformation() {
super(Address.class); super(Address.class);
} }
@Override public static AddressTypeInformation instance() {
protected Map<String, TypeInformation<?>> computePropertiesMap() { return INSTANCE;
Map<String, TypeInformation<?>> properties = new LinkedHashMap<>();
properties.put("city", new StringTypeInformation());
properties.put("street", new StringTypeInformation());
return properties;
} }
@Override @Override
protected Map<String, Function<Address, Object>> computeGetter() { protected void computeFields() {
Map<String, Function<Address, Object>> getters = new LinkedHashMap<>();
getters.put("city", Address::getCity);
getters.put("street", Address::getStreet);
return getters; addField(Field.<Address> string("city").getter(Address::getCity));
} addField(Field.<Address> string("street").getter(Address::getStreet));
@Override
protected Map<String, BiFunction<Address, Object, Address>> computeSetter() {
Map<String, BiFunction<Address, Object, Address>> setter = new LinkedHashMap<>();
// setter.put("city", (bean, id) -> {
// bean.setCity((String) id);
// return bean;
// });
// setter.put("street", (bean, id) -> {
// bean.setStreet((String) id);
// return bean;
// });
return setter;
} }
@Override @Override
@ -99,7 +76,10 @@ public class AddressTypeInformation extends StaticTypeInformation<Address> {
String street = (String) provider String street = (String) provider
.getParameterValue(new Parameter("street", new StringTypeInformation(), new Annotation[] {}, entity)); .getParameterValue(new Parameter("street", new StringTypeInformation(), new Annotation[] {}, entity));
return (T) new Address(city, street); T address = (T) new Address(city, street);
System.out.println("Created new Address instance via constructor using values (" + city + ", " + street
+ ") resulting in " + address);
return address;
} }
}; };
} }

32
spring-data-mongodb/src/main/java/org/springframework/data/util/AnnotationProvider.java

@ -0,0 +1,32 @@
/*
* Copyright 2020. 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.util;
import java.lang.annotation.Annotation;
import java.util.List;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public interface AnnotationProvider {
List<Annotation> getAnnotations();
boolean hasAnnotation(Class<?> annotationType);
<T extends Annotation> List<T> findAnnotation(Class<T> annotation);
}

5
spring-data-mongodb/src/main/java/org/springframework/data/util/ClassTypeInformation.java

@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.GenericTypeResolver; import org.springframework.core.GenericTypeResolver;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -48,8 +49,8 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
public static final ClassTypeInformation<Map> MAP = new ClassTypeInformation(Map.class); public static final ClassTypeInformation<Map> MAP = new ClassTypeInformation(Map.class);
public static final ClassTypeInformation<Object> OBJECT = new ClassTypeInformation(Object.class); public static final ClassTypeInformation<Object> OBJECT = new ClassTypeInformation(Object.class);
private static final Map<Class<?>, ClassTypeInformation<?>> cache = new ConcurrentReferenceHashMap<>(64, // cannot use reference hash map cause static type information might not be referenced from outside and get discarded
ReferenceType.WEAK); private static final Map<Class<?>, ClassTypeInformation<?>> cache = new ConcurrentHashMap<>();
static { static {
Arrays.asList(COLLECTION, LIST, SET, MAP, OBJECT).forEach(it -> cache.put(it.getType(), it)); Arrays.asList(COLLECTION, LIST, SET, MAP, OBJECT).forEach(it -> cache.put(it.getType(), it));

179
spring-data-mongodb/src/main/java/org/springframework/data/util/Field.java

@ -0,0 +1,179 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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.util;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class Field<T, O> implements AnnotationProvider {
@Nullable Class<O> owner;
String propertyName;
TypeInformation<T> typeInformation;
@Nullable TypeInformation<?> componentType;
@Nullable TypeInformation<?> keyType;
MultiValueMap<Class<? extends Annotation>, Annotation> annotations;
@Nullable Function<O, T> getterFunction;
@Nullable BiFunction<O, T, O> setterFunction;
public Field(String propertyName, TypeInformation<T> propertyTypeInformation) {
this.propertyName = propertyName;
this.typeInformation = propertyTypeInformation;
this.annotations = new LinkedMultiValueMap<>();
}
public static <T, O> Field<T, O> simple(Class<T> type, String propertyName) {
if (type == String.class) {
return (Field<T, O>) string(propertyName);
}
throw new IllegalArgumentException("Unknown simple type: " + type);
}
public static <S> Field<String, S> string(String propertyName) {
return new Field<>(propertyName, StringTypeInformation.instance());
}
public static <S> Field<Long, S> int64(String propertyName) {
return new Field<>(propertyName, StaticTypeInformation.from(Long.class));
}
public static <S> Field<Integer, S> int32(String propertyName) {
return new Field<>(propertyName, StaticTypeInformation.from(Integer.class));
}
public static <S, T> Field<T, S> type(String propertyName, TypeInformation<T> type) {
return new Field<>(propertyName, type);
}
public Field<T, O> annotation(Annotation annotation) {
annotations.add(annotation.annotationType(), annotation);
return this;
}
public Field<T, O> wither(BiFunction<O, T, O> setterFunction) {
this.setterFunction = setterFunction;
return this;
}
public Field<T, O> setter(BiConsumer<O, T> setterFunction) {
return wither((o, t) -> {
setterFunction.accept(o, t);
return o;
});
}
public Field<T, O> getter(Function<O, T> getterFunction) {
this.getterFunction = getterFunction;
return this;
}
public Field<T, O> valueType(TypeInformation<?> valueTypeInformation) {
this.componentType = valueTypeInformation;
return this;
}
Field<T, O> owner(Class<O> owner) {
this.owner = owner;
return this;
}
public TypeInformation<?> getValueType() {
return componentType != null ? componentType : typeInformation;
}
public String getFieldName() {
return propertyName;
}
public TypeInformation<T> getTypeInformation() {
return typeInformation;
}
public boolean hasSetter() {
return setterFunction != null;
}
public boolean hasGetter() {
return getterFunction != null;
}
public BiFunction<O, T, O> getSetter() {
return setterFunction;
}
@Nullable
public Function<O, T> getGetter() {
return getterFunction;
}
@Override
public List<Annotation> getAnnotations() {
List<Annotation> all = new ArrayList<>();
annotations.values().forEach(all::addAll);
return all;
}
@Override
public boolean hasAnnotation(Class<?> annotationType) {
return annotations.containsKey(annotationType);
}
@Override
public <T extends Annotation> List<T> findAnnotation(Class<T> annotation) {
return (List<T>) annotations.getOrDefault(annotation, Collections.emptyList());
}
}

76
spring-data-mongodb/src/main/java/org/springframework/data/util/Fields.java

@ -0,0 +1,76 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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.util;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class Fields<O> implements Iterable<Field<?, O>> {
private final Class<O> owner;
private final Map<String, Field<?, O>> fields;
public Fields(Class<O> owner) {
this.owner = owner;
this.fields = new LinkedHashMap<>();
}
public Fields<O> add(Field<?, O> field) {
this.fields.put(field.getFieldName(), field.owner(owner));
return this;
}
public boolean hasField(String fieldName) {
return this.fields.containsKey(fieldName);
}
public <S> Field<S, O> getField(String fieldName) {
return (Field<S, O>) this.fields.get(fieldName);
}
public void doWithFields(BiConsumer<String, Field<?, O>> consumer) {
fields.forEach(consumer);
}
@Override
public Iterator<Field<?, O>> iterator() {
return fields.values().iterator();
}
}

10
spring-data-mongodb/src/main/java/org/springframework/data/util/ListTypeInformation.java

@ -37,10 +37,14 @@ import java.util.List;
* @author Christoph Strobl * @author Christoph Strobl
* @since 2020/10 * @since 2020/10
*/ */
public class ListTypeInformation extends StaticTypeInformation<List> { public class ListTypeInformation<S> extends StaticTypeInformation<List<S>> {
public ListTypeInformation(TypeInformation<?> componentType) { public ListTypeInformation(TypeInformation<S> componentType) {
super(List.class, componentType, null); super((Class) List.class, componentType, null);
}
public static <S> ListTypeInformation<S> listOf(TypeInformation<S> componentType) {
return new ListTypeInformation<>(componentType);
} }
@Override @Override

15
spring-data-mongodb/src/main/java/org/springframework/data/util/Person.java

@ -52,6 +52,15 @@ public class Person {
this.lastname = lastname; this.lastname = lastname;
} }
private Person(long id, String firstname, String lastname, int age, Address address, List<String> nicknames) {
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
this.age = age;
this.address = address;
this.nicknames = nicknames;
}
public String getFirstname() { public String getFirstname() {
return firstname; return firstname;
} }
@ -72,8 +81,10 @@ public class Person {
return id; return id;
} }
public void setId(long id) { public Person withId(long id) {
id = id;
return new Person(id, firstname, lastname, age, address, nicknames);
} }
public Address getAddress() { public Address getAddress() {

119
spring-data-mongodb/src/main/java/org/springframework/data/util/PersonTypeInformation.java

@ -32,20 +32,16 @@
package org.springframework.data.util; package org.springframework.data.util;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor; import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter; import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.model.EntityInstantiator; import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.ParameterValueProvider; import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.FieldType; import org.springframework.data.mongodb.core.mapping.FieldType;
/** /**
@ -54,63 +50,63 @@ import org.springframework.data.mongodb.core.mapping.FieldType;
*/ */
public class PersonTypeInformation extends StaticTypeInformation<Person> { public class PersonTypeInformation extends StaticTypeInformation<Person> {
public PersonTypeInformation() { private static final PersonTypeInformation INSTANCE = new PersonTypeInformation();
private PersonTypeInformation() {
super(Person.class); super(Person.class);
} }
@Override public static PersonTypeInformation instance() {
protected Map<String, TypeInformation<?>> computePropertiesMap() { return INSTANCE;
LinkedHashMap<String, TypeInformation<?>> properties = new LinkedHashMap<>();
properties.put("firstname", new StringTypeInformation());
properties.put("lastname", new StringTypeInformation());
properties.put("id", new StaticTypeInformation<>(Long.class));
properties.put("age", new StaticTypeInformation<>(int.class));
properties.put("address", new AddressTypeInformation());
properties.put("nicknames", new ListTypeInformation(new StringTypeInformation()));
return properties;
} }
@Override @Override
protected Map<String, BiFunction<Person, Object, Person>> computeSetter() { protected void computeFields() {
Map<String, BiFunction<Person, Object, Person>> setter = new LinkedHashMap<>(); addField(
setter.put("id", (bean, id) -> { Field.<Person> int64("id").getter(Person::getId).wither((bean, id) -> bean.withId(id)).annotation(new Id() {
bean.setId((Long) id); @Override
return bean; public Class<? extends Annotation> annotationType() {
}); return Id.class;
setter.put("age", (bean, id) -> { }
bean.setAge((int) id); }));
return bean; addField(Field.<Person> string("firstname").getter(Person::getFirstname).annotation(atFieldOnFirstname()));
}); addField(Field.<Person> string("lastname").getter(Person::getLastname));
// setter.put("firstname", (bean, id) -> {bean.setFirstname((String)id); return bean;}); addField(Field.<Person> int32("age").getter(Person::getAge).setter(Person::setAge));
// setter.put("lastname", (bean, id) -> {bean.setLastname((String)id); return bean;}); addField(Field.<Person, Address> type("address", AddressTypeInformation.instance()).getter(Person::getAddress)
setter.put("address", (bean, id) -> { .setter(Person::setAddress));
bean.setAddress((Address) id); addField(Field.<Person, List<String>> type("nicknames", new ListTypeInformation<>(StringTypeInformation.instance()))
return bean; .getter(Person::getNicknames).setter(Person::setNicknames));
}); }
setter.put("nicknames", (bean, id) -> {
bean.setNicknames((List<String>) id);
return bean;
});
return setter; @Override
protected void computeAnnotations() {
addAnnotation(new Document() {
@Override
public Class<? extends Annotation> annotationType() {
return Document.class;
} }
@Override @Override
protected Map<String, Function<Person, Object>> computeGetter() { public String value() {
return collection();
}
Map<String, Function<Person, Object>> getter = new LinkedHashMap<>(); @Override
public String collection() {
return "star-wars";
}
getter.put("firstname", Person::getFirstname); @Override
getter.put("lastname", Person::getLastname); public String language() {
getter.put("id", Person::getId); return "";
getter.put("address", Person::getAddress); }
getter.put("nicknames", Person::getNicknames);
getter.put("age", Person::getAge);
return getter; @Override
public String collation() {
return "";
}
});
} }
@Override @Override
@ -122,12 +118,15 @@ public class PersonTypeInformation extends StaticTypeInformation<Person> {
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity, public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
ParameterValueProvider<P> provider) { ParameterValueProvider<P> provider) {
String firstname = (String) provider String firstname = (String) provider.getParameterValue(
.getParameterValue(new Parameter("firstname", new StringTypeInformation(), new Annotation[] {}, entity)); new Parameter("firstname", StringTypeInformation.instance(), new Annotation[] {}, entity));
String lastname = (String) provider String lastname = (String) provider.getParameterValue(
.getParameterValue(new Parameter("lastname", new StringTypeInformation(), new Annotation[] {}, entity)); new Parameter("lastname", StringTypeInformation.instance(), new Annotation[] {}, entity));
return (T) new Person(firstname, lastname); T person = (T) new Person(firstname, lastname);
System.out.println("Created new Person instance via constructor using values (" + firstname + ", " + lastname
+ ") resulting in " + person);
return person;
} }
}; };
} }
@ -137,15 +136,13 @@ public class PersonTypeInformation extends StaticTypeInformation<Person> {
return StaticPreferredConstructor.of("firstname", "lastname"); return StaticPreferredConstructor.of("firstname", "lastname");
} }
@Override Annotation atFieldOnFirstname() {
protected Map<String, List<Annotation>> computePropertyAnnotations() {
Map<String, List<Annotation>> annotationMap = new LinkedHashMap<>(); return new org.springframework.data.mongodb.core.mapping.Field() {
annotationMap.put("firstname", Collections.singletonList(new Field() {
@Override @Override
public Class<? extends Annotation> annotationType() { public Class<? extends Annotation> annotationType() {
return Field.class; return org.springframework.data.mongodb.core.mapping.Field.class;
} }
@Override @Override
@ -167,8 +164,6 @@ public class PersonTypeInformation extends StaticTypeInformation<Person> {
public FieldType targetType() { public FieldType targetType() {
return FieldType.IMPLICIT; return FieldType.IMPLICIT;
} }
})); };
return annotationMap;
} }
} }

155
spring-data-mongodb/src/main/java/org/springframework/data/util/StaticTypeInformation.java

@ -34,24 +34,35 @@ package org.springframework.data.util;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.function.BiConsumer;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor; import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructorProvider;
import org.springframework.data.mapping.model.AccessorFunctionProvider;
import org.springframework.data.mapping.model.EntityInstantiator; import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.EntiyInstantiatorProvider;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactoryProvider;
import org.springframework.data.mapping.model.StaticPropertyAccessorFactory;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
/** /**
* @author Christoph Strobl * @author Christoph Strobl
* @since 2020/10 * @since 2020/10
*/ */
public class StaticTypeInformation<S> extends ClassTypeInformation<S> { public class StaticTypeInformation<S> extends ClassTypeInformation<S>
implements AnnotationProvider, EntiyInstantiatorProvider, PreferredConstructorProvider<S>,
PersistentPropertyAccessorFactoryProvider, AccessorFunctionProvider<S> {
private final Class<S> type; private final Class<S> type;
@ -60,13 +71,12 @@ public class StaticTypeInformation<S> extends ClassTypeInformation<S> {
private StaticTypeInformation<?> superTypeInformation; private StaticTypeInformation<?> superTypeInformation;
private List<TypeInformation<?>> typeArguments; private List<TypeInformation<?>> typeArguments;
private final Map<String, TypeInformation<?>> properties; private MultiValueMap<Class<? extends Annotation>, Annotation> annotations;
private final Map<String, BiFunction<S,Object,S>> setter;
private final Map<String, Function<S,Object>> getter; private final Fields fields;
private final Map<String, List<Annotation>> propertyAnnotations;
private EntityInstantiator instantiator; private EntityInstantiator instantiator;
private PreferredConstructor preferredConstructor; private PreferredConstructor<S, ?> preferredConstructor;
public StaticTypeInformation(Class<S> type) { public StaticTypeInformation(Class<S> type) {
this(type, null, null); this(type, null, null);
@ -79,18 +89,19 @@ public class StaticTypeInformation<S> extends ClassTypeInformation<S> {
this.type = type; this.type = type;
this.componentType = componentType; this.componentType = componentType;
this.keyType = keyType; this.keyType = keyType;
this.properties = computePropertiesMap();
this.typeArguments = computeTypeArguments(); this.typeArguments = computeTypeArguments();
this.instantiator = computeEntityInstantiator(); this.instantiator = computeEntityInstantiator();
this.setter = computeSetter();
this.getter = computeGetter();
this.preferredConstructor = computePreferredConstructor(); this.preferredConstructor = computePreferredConstructor();
this.propertyAnnotations = computePropertyAnnotations(); this.annotations = new LinkedMultiValueMap<>();
this.fields = new Fields(type);
computeFields();
computeAnnotations();
} }
protected Map<String, TypeInformation<?>> computePropertiesMap() { protected void addField(Field<?, S> field) {
return Collections.emptyMap(); this.fields.add(field);
}; }
protected List<TypeInformation<?>> computeTypeArguments() { protected List<TypeInformation<?>> computeTypeArguments() {
return Collections.emptyList(); return Collections.emptyList();
@ -100,44 +111,29 @@ public class StaticTypeInformation<S> extends ClassTypeInformation<S> {
return null; return null;
} }
protected PreferredConstructor computePreferredConstructor() { protected PreferredConstructor<S, ?> computePreferredConstructor() {
return null; return null;
} }
public PreferredConstructor getPreferredConstructor() { @Override
public PreferredConstructor<S, ?> getPreferredConstructor() {
return preferredConstructor; return preferredConstructor;
} }
protected Map<String, BiFunction<S,Object,S>> computeSetter() { protected void computeFields() {
return Collections.emptyMap(); //
} }
protected Map<String, Function<S,Object>> computeGetter() { protected void computeAnnotations() {
return Collections.emptyMap();
}
protected Map<String, List<Annotation>> computePropertyAnnotations() {
return Collections.emptyMap();
}
public Map<String, TypeInformation<?>> getProperties() {
return properties;
} }
public Map<String, BiFunction<S, Object, S>> getSetter() { protected void addAnnotation(Annotation annotation) {
return setter; this.annotations.add(annotation.annotationType(), annotation);
} }
public Map<String, Function<S, Object>> getGetter() { public void doWithFields(BiConsumer<String, Field<?, S>> consumer) {
return getter; fields.doWithFields(consumer);
}
public EntityInstantiator getInstantiator() {
return instantiator;
}
public Map<String, List<Annotation>> getPropertyAnnotations() {
return propertyAnnotations;
} }
@Override @Override
@ -148,7 +144,12 @@ public class StaticTypeInformation<S> extends ClassTypeInformation<S> {
@Nullable @Nullable
@Override @Override
public TypeInformation<?> getProperty(String property) { public TypeInformation<?> getProperty(String property) {
return properties.get(property);
if (!fields.hasField(property)) {
return null;
}
return fields.getField(property).getTypeInformation();
} }
@Override @Override
@ -216,9 +217,12 @@ public class StaticTypeInformation<S> extends ClassTypeInformation<S> {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o)
if (o == null || getClass() != o.getClass()) return false; return true;
if (!super.equals(o)) return false; if (o == null || getClass() != o.getClass())
return false;
if (!super.equals(o))
return false;
StaticTypeInformation<?> that = (StaticTypeInformation<?>) o; StaticTypeInformation<?> that = (StaticTypeInformation<?>) o;
@ -237,13 +241,7 @@ public class StaticTypeInformation<S> extends ClassTypeInformation<S> {
if (!ObjectUtils.nullSafeEquals(typeArguments, that.typeArguments)) { if (!ObjectUtils.nullSafeEquals(typeArguments, that.typeArguments)) {
return false; return false;
} }
if (!ObjectUtils.nullSafeEquals(properties, that.properties)) { if (!ObjectUtils.nullSafeEquals(fields, that.fields)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(setter, that.setter)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(getter, that.getter)) {
return false; return false;
} }
return ObjectUtils.nullSafeEquals(instantiator, that.instantiator); return ObjectUtils.nullSafeEquals(instantiator, that.instantiator);
@ -257,13 +255,64 @@ public class StaticTypeInformation<S> extends ClassTypeInformation<S> {
result = 31 * result + ObjectUtils.nullSafeHashCode(keyType); result = 31 * result + ObjectUtils.nullSafeHashCode(keyType);
result = 31 * result + ObjectUtils.nullSafeHashCode(superTypeInformation); result = 31 * result + ObjectUtils.nullSafeHashCode(superTypeInformation);
result = 31 * result + ObjectUtils.nullSafeHashCode(typeArguments); result = 31 * result + ObjectUtils.nullSafeHashCode(typeArguments);
result = 31 * result + ObjectUtils.nullSafeHashCode(properties); result = 31 * result + ObjectUtils.nullSafeHashCode(fields);
result = 31 * result + ObjectUtils.nullSafeHashCode(setter);
result = 31 * result + ObjectUtils.nullSafeHashCode(getter);
result = 31 * result + ObjectUtils.nullSafeHashCode(instantiator); result = 31 * result + ObjectUtils.nullSafeHashCode(instantiator);
return result; return result;
} }
@Nullable
@Override
public EntityInstantiator getEntityInstantiator() {
return instantiator;
}
@Override
public List<Annotation> getAnnotations() {
List<Annotation> all = new ArrayList<>();
annotations.values().forEach(all::addAll);
return all;
}
@Override
public boolean hasAnnotation(Class<?> annotationType) {
return annotations.containsKey(annotationType);
}
@Override
public <T extends Annotation> List<T> findAnnotation(Class<T> annotation) {
return (List<T>) annotations.getOrDefault(annotation, Collections.emptyList());
}
@Nullable
@Override
public PersistentPropertyAccessorFactory getPersistentPropertyAccessorFactory() {
return StaticPropertyAccessorFactory.instance();
}
@Override
public BiFunction<S, Object, S> getSetFunctionFor(String fieldName) {
Field<Object, S> entityField = fields.getField(fieldName);
if (entityField == null) {
return null;
}
return entityField.getSetter();
}
@Override
public Function<S, Object> getGetFunctionFor(String fieldName) {
Field<Object, S> entityField = fields.getField(fieldName);
if (entityField == null) {
return null;
}
return entityField.getGetter();
}
public static class StaticPreferredConstructor extends PreferredConstructor { public static class StaticPreferredConstructor extends PreferredConstructor {
private List<String> args; private List<String> args;

5
spring-data-mongodb/src/main/java/org/springframework/data/util/StringTypeInformation.java

@ -37,8 +37,13 @@ package org.springframework.data.util;
*/ */
public class StringTypeInformation extends StaticTypeInformation<String> { public class StringTypeInformation extends StaticTypeInformation<String> {
private static final StringTypeInformation INSTANCE = new StringTypeInformation();
public StringTypeInformation() { public StringTypeInformation() {
super(String.class); super(String.class);
} }
public static TypeInformation<String> instance() {
return INSTANCE;
}
} }

104
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java

@ -71,6 +71,7 @@ import org.springframework.data.mongodb.core.convert.DocumentAccessorUnitTests.N
import org.springframework.data.mongodb.core.convert.DocumentAccessorUnitTests.ProjectingType; import org.springframework.data.mongodb.core.convert.DocumentAccessorUnitTests.ProjectingType;
import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.ClassWithMapUsingEnumAsKey.FooBarEnum; import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.ClassWithMapUsingEnumAsKey.FooBarEnum;
import org.springframework.data.mongodb.core.geo.Sphere; import org.springframework.data.mongodb.core.geo.Sphere;
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.FieldType; import org.springframework.data.mongodb.core.mapping.FieldType;
@ -81,6 +82,7 @@ import org.springframework.data.mongodb.core.mapping.TextScore;
import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback; import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback;
import org.springframework.data.util.AddressTypeInformation; import org.springframework.data.util.AddressTypeInformation;
import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Person;
import org.springframework.data.util.PersonTypeInformation; import org.springframework.data.util.PersonTypeInformation;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.StopWatch; import org.springframework.util.StopWatch;
@ -2183,46 +2185,46 @@ public class MappingMongoConverterUnitTests {
assertThat(((LinkedHashMap) result.get("cluster")).get("_id")).isEqualTo(100L); assertThat(((LinkedHashMap) result.get("cluster")).get("_id")).isEqualTo(100L);
} }
@Test // @Test
public void perf1() { // public void perf1() {
//
ClassTypeInformation.warmCache(new PersonTypeInformation(), new AddressTypeInformation()); // ClassTypeInformation.warmCache(PersonTypeInformation.instance(), AddressTypeInformation.instance());
//
MongoMappingContext mappingContext = new MongoMappingContext(); // MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(new LinkedHashSet<>( // mappingContext.setInitialEntitySet(new LinkedHashSet<>(
Arrays.asList(org.springframework.data.util.Person.class, org.springframework.data.util.Address.class))); // Arrays.asList(org.springframework.data.util.Person.class, org.springframework.data.util.Address.class)));
mappingContext.initialize(); // mappingContext.initialize();
//
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); // MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
//
org.springframework.data.util.Person source = new org.springframework.data.util.Person("spring", "data"); // org.springframework.data.util.Person source = new org.springframework.data.util.Person("spring", "data");
source.setAddress(new org.springframework.data.util.Address("the city", "never sleeps")); // source.setAddress(new org.springframework.data.util.Address("the city", "never sleeps"));
source.setAge(10); // source.setAge(10);
source.setId(9876); // source = source.withId(9876);
source.setNicknames(Arrays.asList("tick", "trick", "track")); // source.setNicknames(Arrays.asList("tick", "trick", "track"));
//
StopWatch stopWatch = new StopWatch(); // StopWatch stopWatch = new StopWatch();
//
List<org.bson.Document> sources = new ArrayList<>(); // List<org.bson.Document> sources = new ArrayList<>();
stopWatch.start("write"); // stopWatch.start("write");
for (int i = 0; i < 10000; i++) { // for (int i = 0; i < 10000; i++) {
//
org.bson.Document targetDocument = new org.bson.Document(); // org.bson.Document targetDocument = new org.bson.Document();
converter.write(source, targetDocument); // converter.write(source, targetDocument);
//
sources.add(targetDocument); // sources.add(targetDocument);
} // }
stopWatch.stop(); // stopWatch.stop();
//
stopWatch.start("read"); // stopWatch.start("read");
for (org.bson.Document sourceDoc : sources) { // for (org.bson.Document sourceDoc : sources) {
assertThat(converter.read(org.springframework.data.util.Person.class, sourceDoc)).isEqualTo(source); // assertThat(converter.read(org.springframework.data.util.Person.class, sourceDoc)).isEqualTo(source);
} // }
stopWatch.stop(); // stopWatch.stop();
//
System.out.println(stopWatch.prettyPrint()); // System.out.println(stopWatch.prettyPrint());
//
} // }
// public void perf2() { // public void perf2() {
// //
@ -2244,9 +2246,9 @@ public class MappingMongoConverterUnitTests {
// } // }
@Test @Test
public void xxx() { public void staticEntityMetadata() {
ClassTypeInformation.warmCache(new PersonTypeInformation(), new AddressTypeInformation()); ClassTypeInformation.warmCache(PersonTypeInformation.instance(), AddressTypeInformation.instance());
MongoMappingContext mappingContext = new MongoMappingContext(); MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(new LinkedHashSet<>( mappingContext.setInitialEntitySet(new LinkedHashSet<>(
@ -2256,19 +2258,33 @@ public class MappingMongoConverterUnitTests {
org.springframework.data.util.Person source = new org.springframework.data.util.Person("spring", "data"); org.springframework.data.util.Person source = new org.springframework.data.util.Person("spring", "data");
source.setAddress(new org.springframework.data.util.Address("the city", "never sleeps")); source.setAddress(new org.springframework.data.util.Address("the city", "never sleeps"));
source.setAge(10); source.setAge(10);
source.setId(9876); source = source.withId(9876);
source.setNicknames(Arrays.asList("tick", "trick", "track")); source.setNicknames(Arrays.asList("tick", "trick", "track"));
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
org.bson.Document targetDocument = new org.bson.Document(); org.bson.Document targetDocument = new org.bson.Document();
System.out.println();
System.out.println("------ WRITE -------");
converter.write(source, targetDocument); converter.write(source, targetDocument);
System.out.println();
System.out.println("target: " + targetDocument); System.out.println("targetDocument: " + targetDocument);
System.out.println();
System.out.println("------ READ -------");
assertThat(targetDocument).containsEntry("_id", 9876L);
assertThat(targetDocument).containsEntry("first-name", "spring");
assertThat(targetDocument).containsEntry("address",
new org.bson.Document("city", "the city").append("street", "never sleeps"));
assertThat(targetDocument).containsEntry("nicknames", Arrays.asList("tick", "trick", "track"));
org.springframework.data.util.Person targetEntity = converter.read(org.springframework.data.util.Person.class, org.springframework.data.util.Person targetEntity = converter.read(org.springframework.data.util.Person.class,
targetDocument); targetDocument);
System.out.println();
System.out.println("targetEntity: " + targetEntity); System.out.println("targetEntity: " + targetEntity);
assertThat(targetEntity).isEqualTo(source);
BasicMongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(org.springframework.data.util.Person.class);
assertThat(entity.getCollection()).isEqualTo("star-wars");
} }
static class GenericType<T> { static class GenericType<T> {

109
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/staticmetadata/StaticMetadataTests.java

@ -0,0 +1,109 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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.mongodb.core.staticmetadata;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import java.util.Arrays;
import java.util.LinkedHashSet;
import org.bson.Document;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.util.AddressTypeInformation;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Person;
import org.springframework.data.util.PersonTypeInformation;
import com.mongodb.client.MongoClients;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class StaticMetadataTests {
MongoMappingContext mappingContext;
MappingMongoConverter mongoConverter;
MongoTemplate template;
Person luke;
@BeforeAll
static void beforeAll() {
ClassTypeInformation.warmCache(PersonTypeInformation.instance(), AddressTypeInformation.instance());
}
@BeforeEach
void beforeEach() {
mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(new LinkedHashSet<>(
Arrays.asList(org.springframework.data.util.Person.class, org.springframework.data.util.Address.class)));
mappingContext.initialize();
mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
mongoConverter.afterPropertiesSet();
template = new MongoTemplate(new SimpleMongoClientDatabaseFactory(MongoClients.create(), "sem"), mongoConverter);
luke = new Person("luke", "skywalker");
luke.setAddress(new org.springframework.data.util.Address("Mos Eisley", "WB154"));
luke.setAge(22);
luke = luke.withId(9876);
luke.setNicknames(Arrays.asList("jedi", "wormie"));
}
@Test
void readWrite() {
template.save(luke);
Document savedDocument = template.execute("star-wars",
collection -> collection.find(new Document("_id", luke.getId())).first());
System.out.println("savedDocument.toJson(): " + savedDocument.toJson());
Person savedEntity = template.findOne(query(where("id").is(luke.getId())), Person.class);
System.out.println("savedEntity: " + savedEntity);
assertThat(savedEntity).isEqualTo(luke);
}
}
Loading…
Cancel
Save