From 54ada02ae845e5a74eb503abd59d547a79252daa Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 27 Apr 2011 18:30:34 +0200 Subject: [PATCH] DATADOC-109 - Refactored mapping subsystem. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored BasicMappingContext into AbstractMappingContext that now only contains the general algorithm to build PersistentEntity and PersistentProperty instances. The algorithm delegates to two template methods that are responsible for instantiating concrete implementations of those classes. Extracted an AnnotationBasedPersistentProperty that now contains all annotation references so that AbstractPersistentEntity does not take annotations into account in the first places. BasicPersistentEntity now does PreferredConstructor lookup as well. Removed unnecessary methods from MappingCOntext and PersistentEntity interface. Added a verify() method to BasicPersistentEntity that AbstractMappingContext calls to verify validity of the entity. This will be used by the Mongo module for example to make sure @Document annotated entities have an @Id property. Refactored getValueAnnotation() to getSpelExpression() to not imply we're using annotations in every case. Added shortcut in TypeDiscoverer.createInfo(…) to prevent endless loop in case a type has a field of it's very same type. TypeDiscoverer now returns the plain compound type for arrays now, which means you get String for String[], String[][] and so on. Opened up signature of AbstractMappingContext.setInitialEntitySet(…) to allow effortless programatic invocation. --- .../data/mapping/AbstractMappingContext.java | 282 +++++++++++++ ...y.java => AbstractPersistentProperty.java} | 62 +-- .../AnnotationBasedPersistentProperty.java | 108 +++++ .../data/mapping/BasicMappingContext.java | 397 ++---------------- .../data/mapping/BasicPersistentEntity.java | 128 +++++- .../data/mapping/MappingBeanHelper.java | 7 +- .../mapping/context/MappingContextAware.java | 2 +- .../data/mapping/model/MappingContext.java | 29 +- .../data/mapping/model/PersistentEntity.java | 24 +- .../mapping/model/PersistentProperty.java | 4 +- .../data/util/ClassTypeInformation.java | 10 +- .../data/util/TypeDiscoverer.java | 4 + .../data/mapping/MappingMetadataTests.java | 30 +- .../data/mapping/PersonNoId.java | 1 + .../data/mapping/PersonWithChildren.java | 4 + .../DomainClassPropertyEditorUnitTests.java | 2 - src/main/resources/changelog.txt | 1 + 17 files changed, 586 insertions(+), 509 deletions(-) create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractMappingContext.java rename spring-data-commons-core/src/main/java/org/springframework/data/mapping/{BasicPersistentProperty.java => AbstractPersistentProperty.java} (67%) create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/mapping/AnnotationBasedPersistentProperty.java diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractMappingContext.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractMappingContext.java new file mode 100644 index 000000000..cc0b20a0e --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractMappingContext.java @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * 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 java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.core.convert.support.ConversionServiceFactory; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.data.mapping.event.MappingContextEvent; +import org.springframework.data.mapping.model.MappingContext; +import org.springframework.data.mapping.model.MappingException; +import org.springframework.data.mapping.model.PersistentEntity; +import org.springframework.data.mapping.model.PersistentProperty; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.ReflectionUtils.FieldCallback; +import org.springframework.validation.Validator; + + +/** + * Base class to build mapping metadata and thus create instances of {@link PersistentEntity} and {@link PersistentProperty}. + * + * @param E the concrete {@link PersistentEntity} type the {@link MappingContext} implementation creates + * @param P the concrete {@link PersistentProperty} type the {@link MappingContext} implementation creates + * @author Jon Brisbin + * @author Oliver Gierke + */ +public abstract class AbstractMappingContext, P extends PersistentProperty> implements MappingContext, InitializingBean, ApplicationEventPublisherAware { + + private static final Set UNMAPPED_FIELDS = new HashSet(Arrays.asList("class", "this$0")); + + private ApplicationEventPublisher applicationEventPublisher; + private ConcurrentMap persistentEntities = new ConcurrentHashMap(); + private ConcurrentMap> validators = new ConcurrentHashMap>(); + private List> customSimpleTypes = new ArrayList>(); + private Set> initialEntitySet = new HashSet>(); + private boolean strict = false; + + /** + * @param customSimpleTypes the customSimpleTypes to set + */ + public void setCustomSimpleTypes(List> customSimpleTypes) { + this.customSimpleTypes = customSimpleTypes; + } + + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher) + */ + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } + + /** + * Sets the {@link Set} of types to populate the context initially. + * + * @param initialEntitySet + */ + public void setInitialEntitySet(Set> initialEntitySet) { + this.initialEntitySet = initialEntitySet; + } + + /** + * Configures whether the {@link MappingContext} is in strict mode which means, that it will throw + * {@link MappingException}s in case one tries to lookup a {@link PersistentEntity} not already in the context. This + * defaults to {@literal false} so that unknown types will be transparently added to the MappingContext if not known + * in advance. + * + * @param strict + */ + public void setStrict(boolean strict) { + this.strict = strict; + } + + public Collection getPersistentEntities() { + return persistentEntities.values(); + } + + /* (non-Javadoc) + * @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(java.lang.Class) + */ + public E getPersistentEntity(Class type) { + + return getPersistentEntity(new ClassTypeInformation(type)); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(org.springframework.data.util.TypeInformation) + */ + public E getPersistentEntity(TypeInformation type) { + + E entity = persistentEntities.get(type); + + if (entity != null) { + return entity; + } + + if (strict) { + throw new MappingException("Unknown persistent entity " + type); + } + + return addPersistentEntity(type); + } + + /** + * Adds the given type to the {@link MappingContext}. + * + * @param type + * @return + */ + protected E addPersistentEntity(Class type) { + + return addPersistentEntity(new ClassTypeInformation(type)); + } + + /** + * Adds the given {@link TypeInformation} to the {@link MappingContext}. + * + * @param typeInformation + * @return + */ + protected E addPersistentEntity(TypeInformation typeInformation) { + + E persistentEntity = persistentEntities.get(typeInformation); + + if (persistentEntity != null) { + return persistentEntity; + } + + Class type = typeInformation.getType(); + + try { + final E entity = createPersistentEntity(typeInformation); + BeanInfo info = Introspector.getBeanInfo(type); + + final Map descriptors = new HashMap(); + for (PropertyDescriptor descriptor : info.getPropertyDescriptors()) { + descriptors.put(descriptor.getName(), descriptor); + } + + ReflectionUtils.doWithFields(type, new FieldCallback() { + + public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { + + PropertyDescriptor descriptor = descriptors.get(field.getName()); + + ReflectionUtils.makeAccessible(field); + P property = createPersistentProperty(field, descriptor, entity); + + if (property.isTransient()) { + return; + } + + entity.addPersistentProperty(property); + + if (property.isAssociation()) { + entity.addAssociation(property.getAssociation()); + } + + if (property.isIdProperty()) { + entity.setIdProperty(property); + } + + TypeInformation nestedType = getNestedTypeToAdd(property, entity); + if (nestedType != null) { + addPersistentEntity(nestedType); + } + + + } + }, new ReflectionUtils.FieldFilter() { + public boolean matches(Field field) { + return !Modifier.isStatic(field.getModifiers()) && !UNMAPPED_FIELDS.contains(field.getName()); + } + }); + + entity.verify(); + + // Inform listeners + if (null != applicationEventPublisher) { + applicationEventPublisher.publishEvent(new MappingContextEvent(entity, typeInformation)); + } + + // Cache + persistentEntities.put(entity.getPropertyInformation(), (E) entity); + + return entity; + } catch (IntrospectionException e) { + throw new MappingException(e.getMessage(), e); + } + } + + /** + * Returns a potential nested type tha needs to be added when adding the given property in the course of adding a + * {@link PersistentEntity}. Will return the property's {@link TypeInformation} directly if it is a potential entity, + * a collections component type if it's a collection as well as the value type of a {@link Map} if it's a map + * property. + * + * @param property + * @return the TypeInformation to be added as {@link PersistentEntity} or {@literal + */ + private TypeInformation getNestedTypeToAdd(PersistentProperty property, PersistentEntity entity) { + + if (entity.getType().equals(property.getRawType())) { + return null; + } + + TypeInformation typeInformation = property.getTypeInformation(); + + if (customSimpleTypes.contains(typeInformation.getType())) { + return null; + } + + if (property.isEntity()) { + return typeInformation; + } + + if (property.isCollection()) { + return getTypeInformationIfNotSimpleType(typeInformation.getComponentType()); + } + + if (property.isMap()) { + return getTypeInformationIfNotSimpleType(typeInformation.getMapValueType()); + } + + return null; + } + + private TypeInformation getTypeInformationIfNotSimpleType(TypeInformation information) { + return information == null || MappingBeanHelper.isSimpleType(information.getType()) ? null : information; + } + + public List getEntityValidators(E entity) { + return validators.get(entity); + } + + protected abstract E createPersistentEntity(TypeInformation typeInformation); + + protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner); + + + + public void afterPropertiesSet() throws Exception { + for (Class initialEntity : initialEntitySet) { + addPersistentEntity(initialEntity); + } + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentProperty.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractPersistentProperty.java similarity index 67% rename from spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentProperty.java rename to spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractPersistentProperty.java index 2d404d231..f9f6d4528 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentProperty.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AbstractPersistentProperty.java @@ -17,15 +17,13 @@ package org.springframework.data.mapping; import java.beans.PropertyDescriptor; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Map; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.Transient; +import org.springframework.data.annotation.Reference; import org.springframework.data.mapping.model.Association; import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.data.mapping.model.PersistentProperty; @@ -37,45 +35,30 @@ import org.springframework.data.util.TypeInformation; * @author Jon Brisbin * @author Oliver Gierke */ -public class BasicPersistentProperty implements PersistentProperty { +public abstract class AbstractPersistentProperty implements PersistentProperty { protected final String name; protected final PropertyDescriptor propertyDescriptor; protected final TypeInformation information; protected final Class rawType; protected final Field field; - protected Association association; - protected Value value; - protected boolean isTransient = false; - protected PersistentEntity owner; + protected final Association association; + protected final PersistentEntity owner; - public BasicPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, TypeInformation information) { + public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity owner) { this.name = field.getName(); this.rawType = field.getType(); - this.information = information.getProperty(this.name); + this.information = owner.getPropertyInformation().getProperty(this.name); this.propertyDescriptor = propertyDescriptor; this.field = field; - this.isTransient = Modifier.isTransient(field.getModifiers()) || field.isAnnotationPresent(Transient.class); - if (field.isAnnotationPresent(Value.class)) { - this.value = field.getAnnotation(Value.class); - // Fields with @Value annotations are considered the same as transient fields - this.isTransient = true; - } - if (field.isAnnotationPresent(Autowired.class)) { - this.isTransient = true; - } + this.association = isAssociation() ? new Association(this, null) : null; + this.owner = owner; } - public Object getOwner() { + public PersistentEntity getOwner() { return owner; } - public void setOwner(Object owner) { - if (null != owner && owner.getClass().isAssignableFrom(PersistentEntity.class)) { - this.owner = (PersistentEntity) owner; - } - } - public String getName() { return name; } @@ -100,26 +83,31 @@ public class BasicPersistentProperty implements PersistentProperty { return field; } - public Value getValueAnnotation() { - return value; + public String getSpelExpression() { + return null; } public boolean isTransient() { - return isTransient; + return Modifier.isTransient(field.getModifiers()); } public boolean isAssociation() { - return null != association; + if (field.isAnnotationPresent(Reference.class)) { + return true; + } + for (Annotation annotation : field.getDeclaredAnnotations()) { + if (annotation.annotationType().isAnnotationPresent(Reference.class)) { + return true; + } + } + + return false; } public Association getAssociation() { return association; } - public void setAssociation(Association association) { - this.association = association; - } - public boolean isCollection() { return Collection.class.isAssignableFrom(getType()) || isArray(); } @@ -157,8 +145,4 @@ public class BasicPersistentProperty implements PersistentProperty { public Class getMapValueType() { return isMap() ? information.getMapValueType().getType() : null; } - - public boolean isIdProperty() { - return field.isAnnotationPresent(Id.class); - } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AnnotationBasedPersistentProperty.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AnnotationBasedPersistentProperty.java new file mode 100644 index 000000000..e3d59bb8f --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/AnnotationBasedPersistentProperty.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * 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 java.beans.PropertyDescriptor; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Reference; +import org.springframework.data.annotation.Transient; +import org.springframework.data.mapping.model.Association; +import org.springframework.data.mapping.model.PersistentEntity; +import org.springframework.data.mapping.model.PersistentProperty; + + +/** + * Special {@link PersistentProperty} that takes annotations at a property into account. + * + * @author Oliver Gierke + */ +public class AnnotationBasedPersistentProperty extends AbstractPersistentProperty { + + private final Value value; + + /** + * Creates a new {@link AnnotationBasedPersistentProperty}. + * + * @param field + * @param propertyDescriptor + * @param owner + */ + public AnnotationBasedPersistentProperty(Field field, + PropertyDescriptor propertyDescriptor, PersistentEntity owner) { + + super(field, propertyDescriptor, owner); + this.value = field.getAnnotation(Value.class); + field.isAnnotationPresent(Autowired.class); + } + + /** + * Inspects a potentially available {@link Value} annotation at the property and returns the {@link String} value of + * it. + * + * @see org.springframework.data.mapping.AbstractPersistentProperty#getSpelExpression() + */ + public String getSpelExpression() { + return value == null ? null :value.value(); + } + + /** + * Considers plain transient fields, fields annotated with {@link Transient}, {@link Value} or {@link Autowired} as + * transien. + * + * @see org.springframework.data.mapping.BasicPersistentProperty#isTransient() + */ + public boolean isTransient() { + + boolean isTransient = super.isTransient() || field.isAnnotationPresent(Transient.class); + + return isTransient || field.isAnnotationPresent(Value.class) || field.isAnnotationPresent(Autowired.class); + } + + /** + * Regards the property as ID if there is an {@link Id} annotation found on it. + */ + public boolean isIdProperty() { + return field.isAnnotationPresent(Id.class); + } + + /** + * Considers the property an {@link Association} if it is annotated with {@link Reference}. + */ + @Override + public boolean isAssociation() { + + if (isTransient()) { + return false; + } + if (field.isAnnotationPresent(Reference.class)) { + return true; + } + + // TODO: do we need this? Shouldn't the section above already find that annotation? + for (Annotation annotation : field.getDeclaredAnnotations()) { + if (annotation.annotationType().isAnnotationPresent(Reference.class)) { + return true; + } + } + + return false; + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicMappingContext.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicMappingContext.java index a34e4af9a..d274c5403 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicMappingContext.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicMappingContext.java @@ -13,394 +13,45 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.data.mapping; -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; import java.beans.PropertyDescriptor; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.ApplicationEventPublisherAware; -import org.springframework.core.LocalVariableTableParameterNameDiscoverer; -import org.springframework.core.convert.support.ConversionServiceFactory; -import org.springframework.core.convert.support.GenericConversionService; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.annotation.Persistent; -import org.springframework.data.annotation.Reference; -import org.springframework.data.annotation.Transient; -import org.springframework.data.mapping.event.MappingContextEvent; -import org.springframework.data.mapping.model.Association; -import org.springframework.data.mapping.model.MappingConfigurationException; import org.springframework.data.mapping.model.MappingContext; -import org.springframework.data.mapping.model.MappingException; -import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.data.mapping.model.PersistentProperty; -import org.springframework.data.mapping.model.PreferredConstructor; -import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; -import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.ReflectionUtils.FieldCallback; -import org.springframework.validation.Validator; /** + * Simple implementation of {@link MappingContext} creating {@link BasicPersistentEntity} and + * {@link AnnotationBasedPersistentProperty} instances. + * * @author Jon Brisbin * @author Oliver Gierke */ -public class BasicMappingContext implements MappingContext, InitializingBean, ApplicationEventPublisherAware { +public class BasicMappingContext extends AbstractMappingContext, PersistentProperty> { - private static final Set UNMAPPED_FIELDS = new HashSet(Arrays.asList("class", "this$0")); + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation) + */ + @Override + @SuppressWarnings("rawtypes") + protected BasicPersistentEntity createPersistentEntity( + TypeInformation typeInformation) { - protected Log log = LogFactory.getLog(getClass()); - protected ApplicationEventPublisher applicationEventPublisher; - protected ConcurrentMap> persistentEntities = new ConcurrentHashMap>(); - protected ConcurrentMap, List> validators = new ConcurrentHashMap, List>(); - protected final GenericConversionService conversionService; - private List> customSimpleTypes = new ArrayList>(); - private Set> initialEntitySet = new HashSet>(); + return new BasicPersistentEntity(typeInformation); + } - public BasicMappingContext() { - this(ConversionServiceFactory.createDefaultConversionService()); - } + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.util.TypeInformation, org.springframework.data.mapping.BasicPersistentEntity) + */ + @Override + protected PersistentProperty createPersistentProperty(Field field, + PropertyDescriptor descriptor, + BasicPersistentEntity owner) { - public BasicMappingContext(GenericConversionService conversionService) { - Assert.notNull(conversionService); - this.conversionService = conversionService; - } - - /** - * @param customSimpleTypes the customSimpleTypes to set - */ - public void setCustomSimpleTypes(List> customSimpleTypes) { - this.customSimpleTypes = customSimpleTypes; - } - - - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { - this.applicationEventPublisher = applicationEventPublisher; - } - - public void setInitialEntitySet(Set> initialEntitySet) { - this.initialEntitySet = initialEntitySet; - } - - public Collection> getPersistentEntities() { - return persistentEntities.values(); - } - - /* (non-Javadoc) - * @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(java.lang.Class) - */ - public PersistentEntity getPersistentEntity(Class type) { - return getPersistentEntity(new ClassTypeInformation(type)); - } - - @SuppressWarnings({"unchecked"}) - public PersistentEntity getPersistentEntity(TypeInformation type) { - return (PersistentEntity) persistentEntities.get(type); - } - - public PersistentEntity addPersistentEntity(Class type) { - return addPersistentEntity(new ClassTypeInformation(type)); - } - - @SuppressWarnings("unchecked") - public PersistentEntity addPersistentEntity(TypeInformation typeInformation) { - - PersistentEntity persistentEntity = persistentEntities.get(typeInformation); - - if (persistentEntity != null) { - return (PersistentEntity) persistentEntity; - } - - Class type = (Class) typeInformation.getType(); - - try { - final BasicPersistentEntity entity = createPersistentEntity(typeInformation, this); - BeanInfo info = Introspector.getBeanInfo(type); - - final Map descriptors = new HashMap(); - for (PropertyDescriptor descriptor : info.getPropertyDescriptors()) { - descriptors.put(descriptor.getName(), descriptor); - } - - ReflectionUtils.doWithFields(type, new FieldCallback() { - - public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { - try { - PropertyDescriptor descriptor = descriptors.get(field.getName()); - if (isPersistentProperty(field, descriptor)) { - ReflectionUtils.makeAccessible(field); - BasicPersistentProperty property = createPersistentProperty(field, descriptor, entity.getPropertyInformation()); - property.setOwner(entity); - entity.addPersistentProperty(property); - if (isAssociation(field, descriptor)) { - Association association = createAssociation(property); - entity.addAssociation(association); - } - - if (property.isIdProperty()) { - entity.setIdProperty(property); - } - - TypeInformation nestedType = getNestedTypeToAdd(property); - if (nestedType != null) { - addPersistentEntity(nestedType); - } - } - } catch (MappingConfigurationException e) { - log.error(e.getMessage(), e); - } - } - }, new ReflectionUtils.FieldFilter() { - public boolean matches(Field field) { - return !Modifier.isStatic(field.getModifiers()); - } - }); - - entity.setPreferredConstructor(getPreferredConstructor(type)); - - // Inform listeners - if (null != applicationEventPublisher) { - applicationEventPublisher.publishEvent(new MappingContextEvent(entity, typeInformation)); - } - - // Cache - persistentEntities.put(entity.getPropertyInformation(), entity); - - return entity; - } catch (MappingConfigurationException e) { - log.error(e.getMessage(), e); - } catch (IntrospectionException e) { - throw new MappingException(e.getMessage(), e); - } - - return null; - } - - /** - * Returns a potential nested type tha needs to be added when adding the given property in the course of adding a - * {@link PersistentEntity}. Will return the property's {@link TypeInformation} directly if it is a potential entity, - * a collections component type if it's a collection as well as the value type of a {@link Map} if it's a map - * property. - * - * @param property - * @return the TypeInformation to be added as {@link PersistentEntity} or {@literal - */ - private TypeInformation getNestedTypeToAdd(PersistentProperty property) { - - TypeInformation typeInformation = property.getTypeInformation(); - - if (customSimpleTypes.contains(typeInformation.getType())) { - return null; - } - - if (property.isEntity()) { - return typeInformation; - } - - if (property.isCollection()) { - return getTypeInformationIfNotSimpleType(typeInformation.getComponentType()); - } - - if (property.isMap()) { - return getTypeInformationIfNotSimpleType(typeInformation.getMapValueType()); - } - - return null; - } - - private TypeInformation getTypeInformationIfNotSimpleType(TypeInformation information) { - return information == null || MappingBeanHelper.isSimpleType(information.getType()) ? null : information; - } - - public List getEntityValidators(PersistentEntity entity) { - return validators.get(entity); - } - - public boolean isPersistentEntity(Object value) { - if (null != value) { - Class clazz; - if (value instanceof Class) { - clazz = ((Class) value); - } else { - clazz = value.getClass(); - } - return isPersistentEntity(clazz); - } - return false; - } - - public boolean isPersistentEntity(Class type) { - if (type.isAnnotationPresent(Persistent.class)) { - return true; - } else { - for (Annotation annotation : type.getDeclaredAnnotations()) { - if (annotation.annotationType().isAnnotationPresent(Persistent.class)) { - return true; - } - } - for (Field field : type.getDeclaredFields()) { - if (field.isAnnotationPresent(Id.class)) { - return true; - } - } - } - return false; - } - - protected BasicPersistentEntity createPersistentEntity(TypeInformation typeInformation, MappingContext mappingContext) - throws MappingConfigurationException { - return new BasicPersistentEntity(mappingContext, typeInformation); - } - - protected BasicPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, - TypeInformation information) throws MappingConfigurationException { - return new BasicPersistentProperty(field, descriptor, information); - } - - public boolean isPersistentProperty(Field field, PropertyDescriptor descriptor) throws MappingConfigurationException { - if (UNMAPPED_FIELDS.contains(field.getName()) || isTransient(field)) { - return false; - } - return true; - } - - @SuppressWarnings({"unchecked"}) - public PreferredConstructor getPreferredConstructor(Class type) throws MappingConfigurationException { - // Find the right constructor - PreferredConstructor preferredConstructor = null; - - for (Constructor constructor : type.getDeclaredConstructors()) { - if (constructor.getParameterTypes().length != 0) { - // Non-no-arg constructor - if (null == preferredConstructor || constructor.isAnnotationPresent(PersistenceConstructor.class)) { - preferredConstructor = new PreferredConstructor((Constructor) constructor); - - String[] paramNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(constructor); - Type[] paramTypes = constructor.getGenericParameterTypes(); - - for (int i = 0; i < paramTypes.length; i++) { - Class targetType = Object.class; - Class rawType = constructor.getParameterTypes()[i]; - if (paramTypes[i] instanceof ParameterizedType) { - ParameterizedType ptype = (ParameterizedType) paramTypes[i]; - targetType = getTargetType(ptype); - } else { - if (paramTypes[i] instanceof TypeVariable) { - targetType = getTargetType((TypeVariable) paramTypes[i]); - } else if (paramTypes[i] instanceof Class) { - targetType = (Class) paramTypes[i]; - } - } - String paramName = (null != paramNames ? paramNames[i] : "param" + i); - preferredConstructor.addParameter(paramName, targetType, rawType, targetType.getDeclaredAnnotations()); - } - - if (constructor.isAnnotationPresent(PersistenceConstructor.class)) { - // We're done - break; - } - } - } - } - - return preferredConstructor; - } - - public boolean isAssociation(Field field, PropertyDescriptor descriptor) throws MappingConfigurationException { - if (!isTransient(field)) { - if (field.isAnnotationPresent(Reference.class)) { - return true; - } - for (Annotation annotation : field.getDeclaredAnnotations()) { - if (annotation.annotationType().isAnnotationPresent(Reference.class)) { - return true; - } - } - } - return false; - } - - public Association createAssociation(BasicPersistentProperty property) { - // Only support uni-directional associations in the Basic configuration - Association association = new Association(property, null); - property.setAssociation(association); - - return association; - } - - protected boolean isTransient(Field field) { - if (Modifier.isTransient(field.getModifiers()) - || null != field.getAnnotation(Transient.class) - || null != field.getAnnotation(Value.class)) { - return true; - } - return false; - } - - protected Class getTargetType(TypeVariable tv) { - Class targetType = Object.class; - Type[] bounds = tv.getBounds(); - if (bounds.length > 0) { - if (bounds[0] instanceof ParameterizedType) { - return getTargetType((ParameterizedType) bounds[0]); - } else if (bounds[0] instanceof TypeVariable) { - return getTargetType((TypeVariable) bounds[0]); - } else { - targetType = (Class) bounds[0]; - } - } - return targetType; - } - - protected Class getTargetType(ParameterizedType ptype) { - Class targetType = Object.class; - Type[] types = ptype.getActualTypeArguments(); - if (types.length == 1) { - if (types[0] instanceof TypeVariable) { - // Placeholder type - targetType = Object.class; - } else { - if (types[0] instanceof ParameterizedType) { - return getTargetType((ParameterizedType) types[0]); - } else { - targetType = (Class) types[0]; - } - } - } else { - targetType = (Class) ptype.getRawType(); - } - return targetType; - } - - public void afterPropertiesSet() throws Exception { - for (Class initialEntity : initialEntitySet) { - addPersistentEntity(initialEntity); - } - } + return new AnnotationBasedPersistentProperty(field, descriptor, owner); + } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentEntity.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentEntity.java index 04ebd1aab..976eab08e 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentEntity.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/BasicPersistentEntity.java @@ -13,50 +13,132 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.data.mapping; +import java.lang.reflect.Constructor; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; +import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.mapping.model.Association; -import org.springframework.data.mapping.model.MappingContext; import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.data.mapping.model.PersistentProperty; import org.springframework.data.mapping.model.PreferredConstructor; import org.springframework.data.util.TypeInformation; /** + * Simple value object to capture information of {@link PersistentEntity}s. + * * @author Jon Brisbin */ public class BasicPersistentEntity implements PersistentEntity { protected final Class type; - protected PreferredConstructor preferredConstructor; - protected PersistentProperty idProperty; - protected Map persistentProperties = new HashMap(); - protected Map associations = new HashMap(); + protected final PreferredConstructor preferredConstructor; protected final TypeInformation information; - - protected MappingContext mappingContext; + protected final Map persistentProperties = new HashMap(); + protected final Map associations = new HashMap(); + protected PersistentProperty idProperty; - @SuppressWarnings({"unchecked"}) - public BasicPersistentEntity(MappingContext mappingContext, TypeInformation information) { - this.mappingContext = mappingContext; + /** + * Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}. + * + * @param information + */ + @SuppressWarnings("unchecked") + public BasicPersistentEntity(TypeInformation information) { this.type = (Class) information.getType(); this.information = information; + this.preferredConstructor = getPreferredConstructor(type); } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private final PreferredConstructor getPreferredConstructor(Class type) { + // Find the right constructor + PreferredConstructor preferredConstructor = null; + + for (Constructor constructor : type.getDeclaredConstructors()) { + if (constructor.getParameterTypes().length != 0) { + // Non-no-arg constructor + if (null == preferredConstructor || constructor.isAnnotationPresent(PersistenceConstructor.class)) { + preferredConstructor = new PreferredConstructor((Constructor) constructor); + + String[] paramNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(constructor); + Type[] paramTypes = constructor.getGenericParameterTypes(); + + for (int i = 0; i < paramTypes.length; i++) { + Class targetType = Object.class; + Class rawType = constructor.getParameterTypes()[i]; + if (paramTypes[i] instanceof ParameterizedType) { + ParameterizedType ptype = (ParameterizedType) paramTypes[i]; + targetType = getTargetType(ptype); + } else { + if (paramTypes[i] instanceof TypeVariable) { + targetType = getTargetType((TypeVariable) paramTypes[i]); + } else if (paramTypes[i] instanceof Class) { + targetType = (Class) paramTypes[i]; + } + } + String paramName = (null != paramNames ? paramNames[i] : "param" + i); + preferredConstructor.addParameter(paramName, targetType, rawType, targetType.getDeclaredAnnotations()); + } + + if (constructor.isAnnotationPresent(PersistenceConstructor.class)) { + // We're done + break; + } + } + } + } + + return preferredConstructor; +} + + private Class getTargetType(TypeVariable tv) { + Class targetType = Object.class; + Type[] bounds = tv.getBounds(); + if (bounds.length > 0) { + if (bounds[0] instanceof ParameterizedType) { + return getTargetType((ParameterizedType) bounds[0]); + } else if (bounds[0] instanceof TypeVariable) { + return getTargetType((TypeVariable) bounds[0]); + } else { + targetType = (Class) bounds[0]; + } + } + return targetType; +} + + private Class getTargetType(ParameterizedType ptype) { + Class targetType = Object.class; + Type[] types = ptype.getActualTypeArguments(); + if (types.length == 1) { + if (types[0] instanceof TypeVariable) { + // Placeholder type + targetType = Object.class; + } else { + if (types[0] instanceof ParameterizedType) { + return getTargetType((ParameterizedType) types[0]); + } else { + targetType = (Class) types[0]; + } + } + } else { + targetType = (Class) ptype.getRawType(); + } + return targetType; +} public PreferredConstructor getPreferredConstructor() { return preferredConstructor; } - public void setPreferredConstructor(PreferredConstructor constructor) { - this.preferredConstructor = constructor; - } - public String getName() { return type.getName(); } @@ -101,14 +183,6 @@ public class BasicPersistentEntity implements PersistentEntity { return persistentProperties.keySet(); } - public MappingContext getMappingContext() { - return mappingContext; - } - - public void afterPropertiesSet() throws Exception { - - } - public void doWithProperties(PropertyHandler handler) { for (PersistentProperty property : persistentProperties.values()) { if (!property.isTransient() && !property.isAssociation() && !property.isIdProperty()) { @@ -122,4 +196,12 @@ public class BasicPersistentEntity implements PersistentEntity { handler.doWithAssociation(association); } } + + /** + * Callback method to trigger validation of the {@link PersistentEntity}. As {@link BasicPersistentEntity} is not + * immutable there might be some verification steps necessary after the object has reached is final state. + */ + public void verify() { + + } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MappingBeanHelper.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MappingBeanHelper.java index 679080811..08bef45c0 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MappingBeanHelper.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/MappingBeanHelper.java @@ -106,15 +106,12 @@ public abstract class MappingBeanHelper { return type.isEnum(); } - public static T constructInstance(PersistentEntity entity, - PreferredConstructor.ParameterValueProvider provider) { + public static T constructInstance(PersistentEntity entity, PreferredConstructor.ParameterValueProvider provider) { return constructInstance(entity, provider, new StandardEvaluationContext()); } @SuppressWarnings({"unchecked"}) - public static T constructInstance(PersistentEntity entity, - PreferredConstructor.ParameterValueProvider provider, - EvaluationContext spelCtx) { + public static T constructInstance(PersistentEntity entity, PreferredConstructor.ParameterValueProvider provider, EvaluationContext spelCtx) { PreferredConstructor constructor = entity.getPreferredConstructor(); if (null == constructor) { diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAware.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAware.java index 0513804e1..21ebcc642 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAware.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/context/MappingContextAware.java @@ -30,6 +30,6 @@ public interface MappingContextAware { * * @param mappingContext */ - void setMappingContext(MappingContext mappingContext); + void setMappingContext(MappingContext mappingContext); } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/MappingContext.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/MappingContext.java index 23e6aeaa1..166cbbc14 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/MappingContext.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/MappingContext.java @@ -17,7 +17,6 @@ package org.springframework.data.mapping.model; import java.util.Collection; import java.util.List; -import org.springframework.beans.factory.InitializingBean; import org.springframework.data.util.TypeInformation; import org.springframework.validation.Validator; @@ -36,41 +35,25 @@ import org.springframework.validation.Validator; * @author Jon Brisbin * @author Oliver Gierke */ -public interface MappingContext extends InitializingBean { +public interface MappingContext> { - /** - * Adds a PersistentEntity instance - * - * @param type The Java class representing the entity - * @return The PersistentEntity instance - */ - PersistentEntity addPersistentEntity(Class type); - /** * Obtains a list of PersistentEntity instances * * @return A list of PersistentEntity instances */ - Collection> getPersistentEntities(); + Collection getPersistentEntities(); - PersistentEntity getPersistentEntity(Class type); + E getPersistentEntity(Class type); - PersistentEntity getPersistentEntity(TypeInformation type); + E getPersistentEntity(TypeInformation type); /** * Obtains a validator for the given entity + * TODO: Why do we need validators at the {@link MappingContext}? * * @param entity The entity * @return A validator or null if none exists for the given entity */ - List getEntityValidators(PersistentEntity entity); - - /** - * Returns whether the specified value is a persistent entity - * - * @param value The value to check - * @return True if it is - */ - boolean isPersistentEntity(Object value); - + List getEntityValidators(E entity); } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntity.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntity.java index b64269a24..89668bee7 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntity.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntity.java @@ -1,6 +1,5 @@ package org.springframework.data.mapping.model; -import org.springframework.beans.factory.InitializingBean; import org.springframework.data.mapping.AssociationHandler; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.util.TypeInformation; @@ -15,7 +14,7 @@ import java.util.Collection; * @author Jon Brisbin * @author Oliver Gierke */ -public interface PersistentEntity extends InitializingBean { +public interface PersistentEntity { /** * The entity name including any package prefix @@ -26,11 +25,14 @@ public interface PersistentEntity extends InitializingBean { PreferredConstructor getPreferredConstructor(); - /** - * Returns the identity of the instance - * - * @return The identity - */ + + /** + * Returns the id property of the {@link PersistentEntity}. Must never + * return {@literal null} as a {@link PersistentEntity} instance must not be + * created if there is no id property. + * + * @return the id property of the {@link PersistentEntity}. + */ PersistentProperty getIdProperty(); /** @@ -69,15 +71,7 @@ public interface PersistentEntity extends InitializingBean { */ Collection getPersistentPropertyNames(); - /** - * Obtains the MappingContext where this PersistentEntity is defined - * - * @return The MappingContext instance - */ - MappingContext getMappingContext(); - void doWithProperties(PropertyHandler handler); void doWithAssociations(AssociationHandler handler); - } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentProperty.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentProperty.java index 13617cad9..c4a1bdcf9 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentProperty.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentProperty.java @@ -15,7 +15,7 @@ import org.springframework.data.util.TypeInformation; */ public interface PersistentProperty { - Object getOwner(); + PersistentEntity getOwner(); /** * The name of the property @@ -37,7 +37,7 @@ public interface PersistentProperty { Field getField(); - Value getValueAnnotation(); + String getSpelExpression(); boolean isTransient(); diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java b/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java index 3794105b8..73383cd8f 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/util/ClassTypeInformation.java @@ -4,6 +4,8 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Map; +import org.springframework.util.Assert; + /** * Property information for a plain {@link Class}. * @@ -50,12 +52,18 @@ public class ClassTypeInformation extends TypeDiscoverer { public TypeInformation getComponentType() { if (type.isArray()) { - return createInfo(type.getComponentType()); + return createInfo(resolveArrayType(type)); } TypeVariable[] typeParameters = type.getTypeParameters(); return typeParameters.length > 0 ? new TypeVariableTypeInformation(typeParameters[0], this.getType(), this) : null; } + + private static Type resolveArrayType(Class type) { + Assert.isTrue(type.isArray()); + Class componentType = type.getComponentType(); + return componentType.isArray() ? resolveArrayType(componentType) : componentType; + } /* (non-Javadoc) * @see org.springframework.data.util.TypeDiscoverer#equals(java.lang.Object) diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeDiscoverer.java b/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeDiscoverer.java index 8bf3d0a08..e46d2db31 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeDiscoverer.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/util/TypeDiscoverer.java @@ -62,6 +62,10 @@ class TypeDiscoverer implements TypeInformation { * @return */ protected TypeInformation createInfo(Type fieldType) { + + if (fieldType.equals(this.type)) { + return this; + } if (fieldType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) fieldType; diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/MappingMetadataTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/MappingMetadataTests.java index 5f736180a..9fe6413af 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/MappingMetadataTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/MappingMetadataTests.java @@ -19,12 +19,14 @@ package org.springframework.data.mapping; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.data.annotation.Id; import org.springframework.data.mapping.model.Association; +import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import static junit.framework.Assert.*; +import static org.junit.Assert.*; /** * @author Jon Brisbin @@ -40,43 +42,21 @@ public class MappingMetadataTests { ctx = new BasicMappingContext(); } - @Test - public void testDoesRecognizePersistentEntities() { - boolean persistentEntity = ctx.isPersistentEntity(PersonNoId.class); - assertFalse(persistentEntity); - - persistentEntity = ctx.isPersistentEntity(PersonPersistent.class); - assertTrue(persistentEntity); - } @Test public void testPojoWithId() { - PersistentEntity person = ctx.addPersistentEntity(PersonWithId.class); + PersistentEntity person = ctx.addPersistentEntity(PersonWithId.class); assertNotNull(person.getIdProperty()); assertEquals(String.class, person.getIdProperty().getType()); } - @Test - public void testPojoWithoutId() { - PersistentEntity person = ctx.addPersistentEntity(PersonNoId.class); - assertNull(person.getIdProperty()); - } - @Test public void testAssociations() { - PersistentEntity person = ctx.addPersistentEntity(PersonWithChildren.class); + PersistentEntity person = ctx.addPersistentEntity(PersonWithChildren.class); assertNotNull(person.getAssociations()); for (Association association : person.getAssociations()) { assertEquals(Child.class, association.getInverse().getComponentType()); } } - - @Test - public void testDoesRecognizeDocumentEntities() { - // PersistentEntity person = ctx.addPersistentEntity(PersonDocument.class); - boolean persistentEntity = ctx.isPersistentEntity(PersonDocument.class); - assertTrue(persistentEntity); - } - } \ No newline at end of file diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonNoId.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonNoId.java index d193b4fcc..d6851bae7 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonNoId.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonNoId.java @@ -19,6 +19,7 @@ package org.springframework.data.mapping; /** * @author Jon Brisbin */ +@Document public class PersonNoId extends Person { public PersonNoId(Integer ssn, String firstName, String lastName) { diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonWithChildren.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonWithChildren.java index b3cb4481f..dca6a8ebd 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonWithChildren.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PersonWithChildren.java @@ -16,6 +16,7 @@ package org.springframework.data.mapping; +import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Reference; import java.util.List; @@ -25,6 +26,9 @@ import java.util.List; */ public class PersonWithChildren extends Person { + @Id + private String id; + @Reference private List children; diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/DomainClassPropertyEditorUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/DomainClassPropertyEditorUnitTests.java index 6657ed46c..ed82034e3 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/DomainClassPropertyEditorUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/DomainClassPropertyEditorUnitTests.java @@ -152,7 +152,6 @@ public class DomainClassPropertyEditorUnitTests { * * @see org.springframework.data.domain.Persistable#getId() */ - @Override public Integer getId() { return id; @@ -164,7 +163,6 @@ public class DomainClassPropertyEditorUnitTests { * * @see org.springframework.data.domain.Persistable#isNew() */ - @Override public boolean isNew() { return getId() != null; diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index 1c35db42f..8083828ed 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -7,6 +7,7 @@ Changes in version 1.0.1 General * Extracted Querydsl support code from JPA and Mongo modules (DATACMNS-32) * Implementations of Page, Pageable and Sort are now serializable (DATACMNS-30) +* Refactored mapping subsystem (DATADOC-109) Changes in version 1.0.0 (2011-04-18) ----------------------------------------