DATACMNS-365 - Enhanced auditing subsystem to work with accessor annotations.
Added a MappingAuditableBeanWrapperFactory to be able to use themapping metamodel to lookup annotations on persistent properties. This propagates into AuditingHandler and IsNewAwareAuditingHandler getting new constructors taking a MappingContext to set themselves up correctly. This will probably need store specific updates in the setup of the auditing infrastructure for namespace implementations and annotation based JavaConfig. Introduced ….getPersistentProperty(Class<? extends Annotation> annotationType) on PersistentEntity to be able to access properties with a given annotation. Updated SonarGraph architecture description and moved auditing related config classes into auditing.config package.
This commit is contained in:
@@ -99,6 +99,20 @@
|
||||
<dependency toName="Project|spring-data-commons::Layer|Core" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-commons::Layer|Mapping" type="AllowedDependency"/>
|
||||
</element>
|
||||
<element type="Layer" name="Auditing">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="**.auditing.**"/>
|
||||
</element>
|
||||
<element type="Subsystem" name="Configuration">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="**.config.**"/>
|
||||
</element>
|
||||
</element>
|
||||
<dependency toName="External|External" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-commons::Layer|Application" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-commons::Layer|Core" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-commons::Layer|Mapping" type="AllowedDependency"/>
|
||||
</element>
|
||||
<element type="Layer" name="Mapping">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="**.mapping.**"/>
|
||||
@@ -135,12 +149,6 @@
|
||||
<element type="IncludeTypePattern" name="**.authentication.**"/>
|
||||
</element>
|
||||
</element>
|
||||
<element type="Subsystem" name="Configuration">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="WeakTypePattern" name="**.config.**"/>
|
||||
</element>
|
||||
<stereotype name="Unrestricted"/>
|
||||
</element>
|
||||
<element type="Subsystem" name="History">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="WeakTypePattern" name="**.history.**"/>
|
||||
@@ -151,18 +159,24 @@
|
||||
<element type="WeakTypePattern" name="**.support.**"/>
|
||||
</element>
|
||||
</element>
|
||||
<element type="Subsystem" name="Auditing">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="**.auditing.**"/>
|
||||
</element>
|
||||
<stereotype name="Unrestricted"/>
|
||||
</element>
|
||||
<element type="Subsystem" name="Transactions">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="**.transaction.**"/>
|
||||
</element>
|
||||
<stereotype name="Unrestricted"/>
|
||||
</element>
|
||||
<element type="Subsystem" name="Configuration">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="WeakTypePattern" name="**.config.**"/>
|
||||
</element>
|
||||
<stereotype name="Unrestricted"/>
|
||||
</element>
|
||||
<element type="Subsystem" name="Geo">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="**.geo.**"/>
|
||||
</element>
|
||||
<stereotype name="Unrestricted"/>
|
||||
</element>
|
||||
<dependency toName="Project|spring-data-commons::Layer|Application" type="AllowedDependency"/>
|
||||
</element>
|
||||
<element type="Layer" name="Application">
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.data.domain.Auditable;
|
||||
import org.springframework.data.util.ReflectionUtils;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
@@ -37,9 +36,6 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
class AuditableBeanWrapperFactory {
|
||||
|
||||
private static boolean IS_JODA_TIME_PRESENT = ClassUtils.isPresent("org.joda.time.DateTime",
|
||||
ReflectionAuditingBeanWrapper.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* Returns an {@link AuditableBeanWrapper} if the given object is capable of being equipped with auditing information.
|
||||
*
|
||||
@@ -112,14 +108,69 @@ class AuditableBeanWrapperFactory {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for {@link AuditableBeanWrapper} implementations that might need to convert {@link Calendar} values into
|
||||
* compatible types when setting date/time information.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.8
|
||||
*/
|
||||
static abstract class DateConvertingAuditableBeanWrapper implements AuditableBeanWrapper {
|
||||
|
||||
private static boolean IS_JODA_TIME_PRESENT = ClassUtils.isPresent("org.joda.time.DateTime",
|
||||
ReflectionAuditingBeanWrapper.class.getClassLoader());
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DateConvertingAuditableBeanWrapper}.
|
||||
*/
|
||||
public DateConvertingAuditableBeanWrapper() {
|
||||
|
||||
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
|
||||
|
||||
if (IS_JODA_TIME_PRESENT) {
|
||||
conversionService.addConverter(CalendarToDateTimeConverter.INSTANCE);
|
||||
conversionService.addConverter(CalendarToLocalDateTimeConverter.INSTANCE);
|
||||
}
|
||||
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Calendar} in a type, compatible to the given field.
|
||||
*
|
||||
* @param value can be {@literal null}.
|
||||
* @param targetType must not be {@literal null}.
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected Object getDateValueToSet(Calendar value, Class<?> targetType, Object source) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Calendar.class.equals(targetType)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (conversionService.canConvert(Calendar.class, targetType)) {
|
||||
return conversionService.convert(value, targetType);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Invalid date type for member %s! Supported types are %s.",
|
||||
source, AnnotationAuditingMetadata.SUPPORTED_DATE_TYPES));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link AuditableBeanWrapper} implementation that sets values on the target object using refelction.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class ReflectionAuditingBeanWrapper implements AuditableBeanWrapper {
|
||||
static class ReflectionAuditingBeanWrapper extends DateConvertingAuditableBeanWrapper {
|
||||
|
||||
private final ConversionService conversionService;
|
||||
private final AnnotationAuditingMetadata metadata;
|
||||
private final Object target;
|
||||
|
||||
@@ -134,15 +185,6 @@ class AuditableBeanWrapperFactory {
|
||||
|
||||
this.metadata = AnnotationAuditingMetadata.getMetadata(target.getClass());
|
||||
this.target = target;
|
||||
|
||||
ConfigurableConversionService conversionService = new DefaultFormattingConversionService();
|
||||
|
||||
if (IS_JODA_TIME_PRESENT) {
|
||||
conversionService.addConverter(CalendarToDateTimeConverter.INSTANCE);
|
||||
conversionService.addConverter(CalendarToLocalDateTimeConverter.INSTANCE);
|
||||
}
|
||||
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -202,38 +244,11 @@ class AuditableBeanWrapperFactory {
|
||||
return;
|
||||
}
|
||||
|
||||
ReflectionUtils.setField(field, target, getDateValueToSet(value, field));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link DateTime} in a type compatible to the given field.
|
||||
*
|
||||
* @param value
|
||||
* @param field must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private Object getDateValueToSet(Calendar value, Field field) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> targetType = field.getType();
|
||||
|
||||
if (Calendar.class.equals(targetType)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (conversionService.canConvert(Calendar.class, targetType)) {
|
||||
return conversionService.convert(value, targetType);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Invalid date type for field %s! Supported types are %s.",
|
||||
field, AnnotationAuditingMetadata.SUPPORTED_DATE_TYPES));
|
||||
ReflectionUtils.setField(field, target, getDateValueToSet(value, field.getType(), field));
|
||||
}
|
||||
}
|
||||
|
||||
static enum CalendarToDateTimeConverter implements Converter<Calendar, DateTime> {
|
||||
private static enum CalendarToDateTimeConverter implements Converter<Calendar, DateTime> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@@ -243,7 +258,7 @@ class AuditableBeanWrapperFactory {
|
||||
}
|
||||
}
|
||||
|
||||
static enum CalendarToLocalDateTimeConverter implements Converter<Calendar, LocalDateTime> {
|
||||
private static enum CalendarToLocalDateTimeConverter implements Converter<Calendar, LocalDateTime> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.domain.Auditable;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -35,16 +38,41 @@ public class AuditingHandler implements InitializingBean {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AuditingHandler.class);
|
||||
|
||||
private final AuditableBeanWrapperFactory factory = new AuditableBeanWrapperFactory();
|
||||
private final AuditableBeanWrapperFactory factory;
|
||||
|
||||
private DateTimeProvider dateTimeProvider = CurrentDateTimeProvider.INSTANCE;
|
||||
private AuditorAware<?> auditorAware;
|
||||
private boolean dateTimeForNow = true;
|
||||
private boolean modifyOnCreation = true;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AuditingHandler}.
|
||||
*
|
||||
* @deprecated use the constructor taking a {@link MappingContext}.
|
||||
*/
|
||||
@Deprecated
|
||||
public AuditingHandler() {
|
||||
this.factory = new AuditableBeanWrapperFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link AuditableBeanWrapper} using the given {@link MappingContext} when looking up auditing metadata
|
||||
* via reflection.
|
||||
*
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
* @since 1.8
|
||||
*/
|
||||
public AuditingHandler(
|
||||
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
|
||||
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
this.factory = new MappingAuditableBeanWrapperFactory(mappingContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter to inject a {@code AuditorAware} component to retrieve the current auditor.
|
||||
*
|
||||
* @param auditorAware the auditorAware to set
|
||||
* @param auditorAware must not be {@literal null}.
|
||||
*/
|
||||
public void setAuditorAware(final AuditorAware<?> auditorAware) {
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.auditing;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory;
|
||||
import org.springframework.data.support.IsNewStrategy;
|
||||
import org.springframework.data.support.IsNewStrategyFactory;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -31,14 +35,31 @@ public class IsNewAwareAuditingHandler extends AuditingHandler {
|
||||
|
||||
private final IsNewStrategyFactory isNewStrategyFactory;
|
||||
|
||||
/**
|
||||
* Creates a new {@link IsNewAwareAuditingHandler} for the given {@link MappingContext}.
|
||||
*
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
* @since 1.8
|
||||
*/
|
||||
public IsNewAwareAuditingHandler(
|
||||
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
|
||||
|
||||
super(mappingContext);
|
||||
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
this.isNewStrategyFactory = new MappingContextIsNewStrategyFactory(mappingContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link IsNewAwareAuditingHandler} using the given {@link IsNewStrategyFactory}.
|
||||
*
|
||||
* @param isNewStrategyFactory must not be {@literal null}.
|
||||
* @deprecated use constructor taking a {@link MappingContext} directly. Will be removed in 1.9.
|
||||
*/
|
||||
@Deprecated
|
||||
public IsNewAwareAuditingHandler(IsNewStrategyFactory isNewStrategyFactory) {
|
||||
|
||||
Assert.notNull(isNewStrategyFactory, "IsNewStrategy must not be null!");
|
||||
Assert.notNull(isNewStrategyFactory, "IsNewStrategyFactory must not be null!");
|
||||
this.isNewStrategyFactory = isNewStrategyFactory;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2014 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.auditing;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.domain.Auditable;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link AuditableBeanWrapperFactory} that will create am {@link AuditableBeanWrapper} using mapping information
|
||||
* obtained from a {@link MappingContext} to detect auditing configuration and eventually invoking setting the auditing
|
||||
* values.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.8
|
||||
*/
|
||||
class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory {
|
||||
|
||||
private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext;
|
||||
private final Map<Class<?>, MappingAuditingMetadata> metadataCache;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappingAuditableBeanWrapperFactory} using the given {@link MappingContext}.
|
||||
*
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
*/
|
||||
public MappingAuditableBeanWrapperFactory(
|
||||
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
|
||||
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
|
||||
this.mappingContext = mappingContext;
|
||||
this.metadataCache = new HashMap<Class<?>, MappingAuditingMetadata>();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapperFactory#getBeanWrapperFor(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
|
||||
|
||||
if (source instanceof Auditable) {
|
||||
return super.getBeanWrapperFor(source);
|
||||
}
|
||||
|
||||
Class<?> type = source.getClass();
|
||||
PersistentEntity<?, ?> entity = mappingContext.getPersistentEntity(type);
|
||||
|
||||
if (entity == null) {
|
||||
return super.getBeanWrapperFor(source);
|
||||
}
|
||||
|
||||
MappingAuditingMetadata metadata = metadataCache.get(type);
|
||||
|
||||
if (metadata == null) {
|
||||
metadata = new MappingAuditingMetadata(entity);
|
||||
metadataCache.put(type, metadata);
|
||||
}
|
||||
|
||||
return metadata.isAuditable() ? new MappingMetadataAuditableBeanWrapper(source, metadata) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures {@link PersistentProperty} instances equipped with auditing annotations.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.8
|
||||
*/
|
||||
static class MappingAuditingMetadata {
|
||||
|
||||
private final PersistentProperty<?> createdByProperty, createdDateProperty, lastModifiedByProperty,
|
||||
lastModifiedDateProperty;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappingAuditingMetadata} instance from the given {@link PersistentEntity}.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
*/
|
||||
public MappingAuditingMetadata(PersistentEntity<?, ? extends PersistentProperty<?>> entity) {
|
||||
|
||||
Assert.notNull(entity, "PersistentEntity must not be null!");
|
||||
|
||||
this.createdByProperty = entity.getPersistentProperty(CreatedBy.class);
|
||||
this.createdDateProperty = entity.getPersistentProperty(CreatedDate.class);
|
||||
this.lastModifiedByProperty = entity.getPersistentProperty(LastModifiedBy.class);
|
||||
this.lastModifiedDateProperty = entity.getPersistentProperty(LastModifiedDate.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link PersistentEntity} is auditable at all (read: any of the auditing annotations is
|
||||
* present).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAuditable() {
|
||||
return createdByProperty != null || createdDateProperty != null || lastModifiedByProperty != null
|
||||
|| lastModifiedDateProperty != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AuditableBeanWrapper} using {@link MappingAuditingMetadata} and a {@link BeanWrapper} to set values on
|
||||
* auditing properties.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.8
|
||||
*/
|
||||
static class MappingMetadataAuditableBeanWrapper extends DateConvertingAuditableBeanWrapper {
|
||||
|
||||
private final BeanWrapper<Object> wrapper;
|
||||
private final MappingAuditingMetadata metadata;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappingMetadataAuditableBeanWrapper} for the given taregt and
|
||||
* {@link MappingAuditingMetadata}.
|
||||
*
|
||||
* @param target must not be {@literal null}.
|
||||
* @param metadata must not be {@literal null}.
|
||||
*/
|
||||
public MappingMetadataAuditableBeanWrapper(Object target, MappingAuditingMetadata metadata) {
|
||||
|
||||
Assert.notNull(target, "Target object must not be null!");
|
||||
Assert.notNull(metadata, "Auditing metadata must not be null!");
|
||||
|
||||
this.wrapper = BeanWrapper.create(target, null);
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void setCreatedBy(Object value) {
|
||||
|
||||
if (metadata.createdByProperty != null) {
|
||||
this.wrapper.setProperty(metadata.createdByProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Calendar)
|
||||
*/
|
||||
@Override
|
||||
public void setCreatedDate(Calendar value) {
|
||||
|
||||
PersistentProperty<?> property = metadata.createdDateProperty;
|
||||
|
||||
if (property != null) {
|
||||
this.wrapper.setProperty(property, getDateValueToSet(value, property.getType(), property));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void setLastModifiedBy(Object value) {
|
||||
|
||||
if (metadata.lastModifiedByProperty != null) {
|
||||
this.wrapper.setProperty(metadata.lastModifiedByProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)
|
||||
*/
|
||||
@Override
|
||||
public void setLastModifiedDate(Calendar value) {
|
||||
|
||||
PersistentProperty<?> property = metadata.lastModifiedDateProperty;
|
||||
|
||||
if (property != null) {
|
||||
this.wrapper.setProperty(property, getDateValueToSet(value, property.getType(), property));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-2014 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.
|
||||
@@ -13,17 +13,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.config;
|
||||
package org.springframework.data.auditing.config;
|
||||
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactoryBean;
|
||||
import org.springframework.aop.target.LazyInitTargetSource;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.auditing.AuditingHandler;
|
||||
import org.springframework.data.config.ParsingUtils;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
@@ -37,6 +43,30 @@ public class AuditingHandlerBeanDefinitionParser extends AbstractSingleBeanDefin
|
||||
|
||||
private static final String AUDITOR_AWARE_REF = "auditor-aware-ref";
|
||||
|
||||
private final String mappingContextBeanName;
|
||||
private String resolvedBeanName;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AuditingHandlerBeanDefinitionParser} to point to a {@link MappingContext} with the given bean
|
||||
* name.
|
||||
*
|
||||
* @param mappingContextBeanName must not be {@literal null} or empty.
|
||||
*/
|
||||
public AuditingHandlerBeanDefinitionParser(String mappingContextBeanName) {
|
||||
|
||||
Assert.hasText(mappingContextBeanName, "MappingContext bean name must not be null!");
|
||||
this.mappingContextBeanName = mappingContextBeanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the bean definition the {@link AuditingHandler} was registered under.
|
||||
*
|
||||
* @return the resolvedBeanName
|
||||
*/
|
||||
public String getResolvedBeanName() {
|
||||
return resolvedBeanName;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
|
||||
@@ -62,6 +92,8 @@ public class AuditingHandlerBeanDefinitionParser extends AbstractSingleBeanDefin
|
||||
@Override
|
||||
protected void doParse(Element element, BeanDefinitionBuilder builder) {
|
||||
|
||||
builder.addConstructorArgReference(mappingContextBeanName);
|
||||
|
||||
String auditorAwareRef = element.getAttribute(AUDITOR_AWARE_REF);
|
||||
|
||||
if (StringUtils.hasText(auditorAwareRef)) {
|
||||
@@ -73,6 +105,18 @@ public class AuditingHandlerBeanDefinitionParser extends AbstractSingleBeanDefin
|
||||
ParsingUtils.setPropertyValue(builder, element, "modify-on-creation", "modifyOnCreation");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
|
||||
*/
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||
throws BeanDefinitionStoreException {
|
||||
|
||||
this.resolvedBeanName = super.resolveId(element, definition, parserContext);
|
||||
return resolvedBeanName;
|
||||
}
|
||||
|
||||
private BeanDefinition createLazyInitTargetSourceBeanDefinition(String auditorAwareRef) {
|
||||
|
||||
BeanDefinitionBuilder targetSourceBuilder = rootBeanDefinition(LazyInitTargetSource.class);
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.auditing.config;
|
||||
|
||||
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link AuditingHandlerBeanDefinitionParser} that will register am {@link IsNewAwareAuditingHandler}. Needs to get the
|
||||
* bean id of the {@link MappingContext} it shall refer to.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.5
|
||||
*/
|
||||
public class IsNewAwareAuditingHandlerBeanDefinitionParser extends AuditingHandlerBeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Creates a new {@link IsNewAwareAuditingHandlerBeanDefinitionParser}.
|
||||
*
|
||||
* @param mappingContextBeanName must not be {@literal null} or empty.
|
||||
*/
|
||||
public IsNewAwareAuditingHandlerBeanDefinitionParser(String mappingContextBeanName) {
|
||||
super(mappingContextBeanName);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.config.AuditingHandlerBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return IsNewAwareAuditingHandler.class;
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.config;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link AuditingHandlerBeanDefinitionParser} that will register am {@link IsNewAwareAuditingHandler}. Needs to get the
|
||||
* bean id of the
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class IsNewAwareAuditingHandlerBeanDefinitionParser extends AuditingHandlerBeanDefinitionParser {
|
||||
|
||||
private final String isNewStrategyFactoryBeanId;
|
||||
private String resolvedBeanName;
|
||||
|
||||
/**
|
||||
* Creates a new {@link IsNewAwareAuditingHandlerBeanDefinitionParser}.
|
||||
*
|
||||
* @param isNewStrategyFactoryBeanId must not be {@literal null} or empty.
|
||||
*/
|
||||
public IsNewAwareAuditingHandlerBeanDefinitionParser(String isNewStrategyFactoryBeanId) {
|
||||
|
||||
Assert.hasText(isNewStrategyFactoryBeanId);
|
||||
this.isNewStrategyFactoryBeanId = isNewStrategyFactoryBeanId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bean name that was used to register the {@link IsNewAwareAuditingHandler}.
|
||||
*
|
||||
* @return the resolvedBeanName
|
||||
*/
|
||||
public String getResolvedBeanName() {
|
||||
return resolvedBeanName;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.config.AuditingHandlerBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return IsNewAwareAuditingHandler.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.config.AuditingHandlerBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.support.BeanDefinitionBuilder)
|
||||
*/
|
||||
@Override
|
||||
protected void doParse(Element element, BeanDefinitionBuilder builder) {
|
||||
|
||||
builder.addConstructorArgReference(isNewStrategyFactoryBeanId);
|
||||
super.doParse(element, builder);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
|
||||
*/
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||
throws BeanDefinitionStoreException {
|
||||
|
||||
this.resolvedBeanName = super.resolveId(element, definition, parserContext);
|
||||
return resolvedBeanName;
|
||||
}
|
||||
}
|
||||
@@ -96,6 +96,15 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> {
|
||||
*/
|
||||
P getPersistentProperty(String name);
|
||||
|
||||
/**
|
||||
* Returns the property equipped with an annotation of the given type.
|
||||
*
|
||||
* @param annotationType must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.8
|
||||
*/
|
||||
P getPersistentProperty(Class<? extends Annotation> annotationType);
|
||||
|
||||
/**
|
||||
* Returns whether the {@link PersistentEntity} has an id property. If this call returns {@literal true},
|
||||
* {@link #getIdProperty()} will return a non-{@literal null} value.
|
||||
@@ -158,6 +167,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> {
|
||||
*
|
||||
* @param annotationType must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.8
|
||||
*/
|
||||
<A extends Annotation> A findAnnotation(Class<A> annotationType);
|
||||
}
|
||||
|
||||
@@ -228,6 +228,33 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
|
||||
return propertyCache.get(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.PersistentEntity#getPersistentProperty(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public P getPersistentProperty(Class<? extends Annotation> annotationType) {
|
||||
|
||||
Assert.notNull(annotationType, "Annotation type must not be null!");
|
||||
|
||||
for (P property : properties) {
|
||||
if (property.isAnnotationPresent(annotationType)) {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
for (Association<P> association : associations) {
|
||||
|
||||
P property = association.getInverse();
|
||||
|
||||
if (property.isAnnotationPresent(annotationType)) {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.PersistentEntity#getType()
|
||||
|
||||
@@ -46,6 +46,7 @@ public class AuditingHandlerUnitTests {
|
||||
when(auditorAware.getCurrentAuditor()).thenReturn(user);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected AuditingHandler getHandler() {
|
||||
return new AuditingHandler();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.SampleMappingContext;
|
||||
import org.springframework.data.support.IsNewStrategy;
|
||||
import org.springframework.data.support.IsNewStrategyFactory;
|
||||
|
||||
@@ -46,6 +50,7 @@ public class IsNewAwareAuditingHandlerUnitTests extends AuditingHandlerUnitTests
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected IsNewAwareAuditingHandler getHandler() {
|
||||
return new IsNewAwareAuditingHandler(factory);
|
||||
}
|
||||
@@ -71,4 +76,21 @@ public class IsNewAwareAuditingHandlerUnitTests extends AuditingHandlerUnitTests
|
||||
assertThat(user.createdDate, is(nullValue()));
|
||||
assertThat(user.modifiedDate, is(notNullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-365
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullMappingContext() {
|
||||
new IsNewAwareAuditingHandler(
|
||||
(MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>>) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-365
|
||||
*/
|
||||
@Test
|
||||
public void setsUpHandlerWithMappingContext() {
|
||||
new IsNewAwareAuditingHandler(new SampleMappingContext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2014 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.auditing;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.auditing.AuditableBeanWrapperFactory.AuditableInterfaceBeanWrapper;
|
||||
import org.springframework.data.domain.Auditable;
|
||||
import org.springframework.data.mapping.context.SampleMappingContext;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MappingAuditableBeanWrapperFactory}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.8
|
||||
*/
|
||||
public class MappingAuditableBeanWrapperFactoryUnitTests {
|
||||
|
||||
AuditableBeanWrapperFactory factory;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
factory = new MappingAuditableBeanWrapperFactory(new SampleMappingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-365
|
||||
*/
|
||||
@Test
|
||||
public void discoversAuditingPropertyOnField() {
|
||||
|
||||
Sample sample = new Sample();
|
||||
AuditableBeanWrapper wrapper = factory.getBeanWrapperFor(sample);
|
||||
|
||||
assertThat(wrapper, is(notNullValue()));
|
||||
|
||||
wrapper.setCreatedBy("Me!");
|
||||
assertThat(sample.createdBy, is(notNullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-365
|
||||
*/
|
||||
@Test
|
||||
public void discoversAuditingPropertyOnAccessor() {
|
||||
|
||||
Sample sample = new Sample();
|
||||
AuditableBeanWrapper wrapper = factory.getBeanWrapperFor(sample);
|
||||
|
||||
assertThat(wrapper, is(notNullValue()));
|
||||
|
||||
wrapper.setLastModifiedBy("Me, too!");
|
||||
assertThat(sample.lastModifiedBy, is(notNullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-365
|
||||
*/
|
||||
@Test
|
||||
public void settingInavailablePropertyIsNoop() {
|
||||
|
||||
Sample sample = new Sample();
|
||||
AuditableBeanWrapper wrapper = factory.getBeanWrapperFor(sample);
|
||||
|
||||
wrapper.setLastModifiedDate(new GregorianCalendar());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-365
|
||||
*/
|
||||
@Test
|
||||
public void doesNotReturnWrapperForEntityNotUsingAuditing() {
|
||||
assertThat(factory.getBeanWrapperFor(new NoAuditing()), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-365
|
||||
*/
|
||||
@Test
|
||||
public void returnsAuditableWrapperForAuditable() {
|
||||
|
||||
assertThat(factory.getBeanWrapperFor(mock(ExtendingAuditable.class)),
|
||||
is(instanceOf(AuditableInterfaceBeanWrapper.class)));
|
||||
}
|
||||
|
||||
static class Sample {
|
||||
|
||||
@CreatedBy private Object createdBy;
|
||||
private Object lastModifiedBy;
|
||||
|
||||
@LastModifiedBy
|
||||
public Object getLastModifiedBy() {
|
||||
return lastModifiedBy;
|
||||
}
|
||||
}
|
||||
|
||||
static class NoAuditing {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
static abstract class ExtendingAuditable implements Auditable<Object, Long> {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2011-2014 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 static org.hamcrest.CoreMatchers.*;
|
||||
@@ -8,16 +23,23 @@ import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.TypeAlias;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentEntitySpec;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.Person;
|
||||
import org.springframework.data.mapping.context.SampleMappingContext;
|
||||
import org.springframework.data.mapping.context.SamplePersistentProperty;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@@ -29,8 +51,9 @@ import org.springframework.test.util.ReflectionTestUtils;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> {
|
||||
|
||||
@Mock
|
||||
T property;
|
||||
@Rule public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Mock T property;
|
||||
|
||||
@Test
|
||||
public void assertInvariants() {
|
||||
@@ -122,13 +145,28 @@ public class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> {
|
||||
when(property.isIdProperty()).thenReturn(true);
|
||||
|
||||
entity.addPersistentProperty(property);
|
||||
exception.expect(MappingException.class);
|
||||
entity.addPersistentProperty(property);
|
||||
}
|
||||
|
||||
try {
|
||||
entity.addPersistentProperty(property);
|
||||
fail("Expected MappingException!");
|
||||
} catch (MappingException e) {
|
||||
// expected
|
||||
}
|
||||
/**
|
||||
* @see DATACMNS-365
|
||||
*/
|
||||
@Test
|
||||
public void detectsPropertyWithAnnotation() {
|
||||
|
||||
SampleMappingContext context = new SampleMappingContext();
|
||||
PersistentEntity<Object, SamplePersistentProperty> entity = context.getPersistentEntity(Entity.class);
|
||||
|
||||
PersistentProperty<?> property = entity.getPersistentProperty(LastModifiedBy.class);
|
||||
assertThat(property, is(notNullValue()));
|
||||
assertThat(property.getName(), is("field"));
|
||||
|
||||
property = entity.getPersistentProperty(CreatedBy.class);
|
||||
assertThat(property, is(notNullValue()));
|
||||
assertThat(property.getName(), is("property"));
|
||||
|
||||
assertThat(entity.getPersistentProperty(CreatedDate.class), is(nullValue()));
|
||||
}
|
||||
|
||||
private BasicPersistentEntity<Person, T> createEntity(Comparator<T> comparator) {
|
||||
@@ -142,5 +180,15 @@ public class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> {
|
||||
|
||||
static class Entity {
|
||||
|
||||
@LastModifiedBy String field;
|
||||
String property;
|
||||
|
||||
/**
|
||||
* @return the property
|
||||
*/
|
||||
@CreatedBy
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user