DATACMNS-638, DATACMNS-643 - Support for JSR-310 and ThreeTen datetime types.

Extend AuditableBeanWrapper to allow access to the last modification date of a target object. Made AuditableBeanWrapperFactory an interface and renamed what’s been previously known under this name as DefaultAuditableBeanWrapperFactory.

The components previously relying on a MappingContext to lookup a PersistentEntity now use PersistentEntities to be able to back a collection of MappingContexts behind that and also avoid unmanaged types to be added to the MappingContext.

We now also register the JSR-310 and ThreeTen back-port converters with the ConversionService to be able to get and set auditing dates as these types.
This commit is contained in:
Oliver Gierke
2015-02-04 19:30:02 +01:00
parent fe49e4cf14
commit 0c65c7bb67
17 changed files with 656 additions and 313 deletions

View File

@@ -27,6 +27,8 @@ 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.convert.Jsr310Converters;
import org.springframework.data.convert.ThreeTenBackPortConverters;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.data.util.ReflectionUtils.AnnotationFieldFilter;
import org.springframework.util.Assert;
@@ -93,7 +95,7 @@ final class AnnotationAuditingMetadata {
/**
* Checks whether the given field has a type that is a supported date type.
*
* @param field
* @param field can be {@literal null}.
*/
private void assertValidDateFieldType(Field field) {
@@ -101,14 +103,15 @@ final class AnnotationAuditingMetadata {
return;
}
// Support JDK 8 date types if runtime allows
if (IS_JDK_8 && field.getType().getPackage().getName().startsWith(JDK8_TIME_PACKAGE_PREFIX)) {
Class<?> type = field.getType();
if (Jsr310Converters.supports(type) || ThreeTenBackPortConverters.supports(type)) {
return;
}
throw new IllegalStateException(String.format(
"Found created/modified date field with type %s but only %s as well as java.time types are supported!",
field.getType(), SUPPORTED_DATE_TYPES));
"Found created/modified date field with type %s but only %s as well as java.time types are supported!", type,
SUPPORTED_DATE_TYPES));
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,6 +46,14 @@ public interface AuditableBeanWrapper {
*/
void setLastModifiedBy(Object value);
/**
* Returns the date of the last modification date of the backing bean.
*
* @return the date of the last modification, can be {@literal null}.
* @since 1.10
*/
Calendar getLastModifiedDate();
/**
* Set the last modification date.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,256 +15,20 @@
*/
package org.springframework.data.auditing;
import java.lang.reflect.Field;
import java.util.Calendar;
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.data.domain.Auditable;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* A factory class to {@link AuditableBeanWrapper} instances.
* A factory to lookup {@link AuditableBeanWrapper}s.
*
* @author Oliver Gierke
* @since 1.5
* @since 1.10
*/
class AuditableBeanWrapperFactory {
public interface AuditableBeanWrapperFactory {
/**
* Returns an {@link AuditableBeanWrapper} if the given object is capable of being equipped with auditing information.
* Returns the {@link AuditableBeanWrapper} for the given source obejct if it's eligible for auditing.
*
* @param source the auditing candidate.
* @return
* @param source can be {@literal null}.
* @return the {@link AuditableBeanWrapper} for the given source obejct if it's eligible for auditing or
* {@literal null} otherwise.
*/
@SuppressWarnings("unchecked")
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
if (source == null) {
return null;
}
if (source instanceof Auditable) {
return new AuditableInterfaceBeanWrapper((Auditable<Object, ?>) source);
}
AnnotationAuditingMetadata metadata = AnnotationAuditingMetadata.getMetadata(source.getClass());
if (metadata.isAuditable()) {
return new ReflectionAuditingBeanWrapper(source);
}
return null;
}
/**
* An {@link AuditableBeanWrapper} that works with objects implementing
*
* @author Oliver Gierke
*/
static class AuditableInterfaceBeanWrapper implements AuditableBeanWrapper {
private final Auditable<Object, ?> auditable;
public AuditableInterfaceBeanWrapper(Auditable<Object, ?> auditable) {
this.auditable = auditable;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
*/
public void setCreatedBy(Object value) {
auditable.setCreatedBy(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(org.joda.time.DateTime)
*/
public void setCreatedDate(Calendar value) {
auditable.setCreatedDate(new DateTime(value));
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
*/
public void setLastModifiedBy(Object value) {
auditable.setLastModifiedBy(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(org.joda.time.DateTime)
*/
public void setLastModifiedDate(Calendar value) {
auditable.setLastModifiedDate(new DateTime(value));
}
}
/**
* 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
*/
abstract static class DateConvertingAuditableBeanWrapper implements AuditableBeanWrapper {
private static final 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 extends DateConvertingAuditableBeanWrapper {
private final AnnotationAuditingMetadata metadata;
private final Object target;
/**
* Creates a new {@link ReflectionAuditingBeanWrapper} to set auditing data on the given target object.
*
* @param target must not be {@literal null}.
*/
public ReflectionAuditingBeanWrapper(Object target) {
Assert.notNull(target, "Target object must not be null!");
this.metadata = AnnotationAuditingMetadata.getMetadata(target.getClass());
this.target = target;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
*/
public void setCreatedBy(Object value) {
setField(metadata.getCreatedByField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Calendar)
*/
public void setCreatedDate(Calendar value) {
setDateField(metadata.getCreatedDateField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
*/
public void setLastModifiedBy(Object value) {
setField(metadata.getLastModifiedByField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)
*/
public void setLastModifiedDate(Calendar value) {
setDateField(metadata.getLastModifiedDateField(), value);
}
/**
* Sets the given field to the given value if the field is not {@literal null}.
*
* @param field
* @param value
*/
private void setField(Field field, Object value) {
if (field != null) {
ReflectionUtils.setField(field, target, value);
}
}
/**
* Sets the given field to the given value if the field is not {@literal null}.
*
* @param field
* @param value
*/
private void setDateField(Field field, Calendar value) {
if (field == null) {
return;
}
ReflectionUtils.setField(field, target, getDateValueToSet(value, field.getType(), field));
}
}
private static enum CalendarToDateTimeConverter implements Converter<Calendar, DateTime> {
INSTANCE;
@Override
public DateTime convert(Calendar source) {
return new DateTime(source);
}
}
private static enum CalendarToLocalDateTimeConverter implements Converter<Calendar, LocalDateTime> {
INSTANCE;
@Override
public LocalDateTime convert(Calendar source) {
return new LocalDateTime(source);
}
}
AuditableBeanWrapper getBeanWrapperFor(Object source);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package org.springframework.data.auditing;
import java.util.Arrays;
import java.util.Calendar;
import org.joda.time.DateTime;
@@ -26,6 +27,7 @@ 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.data.mapping.context.PersistentEntities;
import org.springframework.util.Assert;
/**
@@ -38,7 +40,7 @@ public class AuditingHandler implements InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(AuditingHandler.class);
private final AuditableBeanWrapperFactory factory;
private final DefaultAuditableBeanWrapperFactory factory;
private DateTimeProvider dateTimeProvider = CurrentDateTimeProvider.INSTANCE;
private AuditorAware<?> auditorAware;
@@ -51,12 +53,26 @@ public class AuditingHandler implements InitializingBean {
*
* @param mappingContext must not be {@literal null}.
* @since 1.8
* @deprecated use {@link AuditingHandler(PersistentEntities)} instead.
*/
@Deprecated
@SuppressWarnings("unchecked")
public AuditingHandler(
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
this(new PersistentEntities(Arrays.asList(mappingContext)));
}
Assert.notNull(mappingContext, "MappingContext must not be null!");
this.factory = new MappingAuditableBeanWrapperFactory(mappingContext);
/**
* Creates a new {@link AuditableBeanWrapper} using the given {@link PersistentEntities} when looking up auditing
* metadata via reflection.
*
* @param entities must not be {@literal null}.
* @since 1.10
*/
public AuditingHandler(PersistentEntities entities) {
Assert.notNull(entities, "PersistentEntities must not be null!");
this.factory = new MappingAuditableBeanWrapperFactory(entities);
}
/**

View File

@@ -0,0 +1,326 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.auditing;
import java.lang.reflect.Field;
import java.util.Calendar;
import java.util.Date;
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.data.convert.Jsr310Converters;
import org.springframework.data.convert.ThreeTenBackPortConverters;
import org.springframework.data.domain.Auditable;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* A factory class to {@link AuditableBeanWrapper} instances.
*
* @author Oliver Gierke
* @since 1.5
*/
class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory {
/**
* Returns an {@link AuditableBeanWrapper} if the given object is capable of being equipped with auditing information.
*
* @param source the auditing candidate.
* @return
*/
@SuppressWarnings("unchecked")
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
if (source == null) {
return null;
}
if (source instanceof Auditable) {
return new AuditableInterfaceBeanWrapper((Auditable<Object, ?>) source);
}
AnnotationAuditingMetadata metadata = AnnotationAuditingMetadata.getMetadata(source.getClass());
if (metadata.isAuditable()) {
return new ReflectionAuditingBeanWrapper(source);
}
return null;
}
/**
* An {@link AuditableBeanWrapper} that works with objects implementing
*
* @author Oliver Gierke
*/
static class AuditableInterfaceBeanWrapper extends DateConvertingAuditableBeanWrapper {
private final Auditable<Object, ?> auditable;
public AuditableInterfaceBeanWrapper(Auditable<Object, ?> auditable) {
this.auditable = auditable;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
*/
public void setCreatedBy(Object value) {
auditable.setCreatedBy(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(org.joda.time.DateTime)
*/
public void setCreatedDate(Calendar value) {
auditable.setCreatedDate(new DateTime(value));
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
*/
public void setLastModifiedBy(Object value) {
auditable.setLastModifiedBy(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#getLastModifiedDate()
*/
@Override
public Calendar getLastModifiedDate() {
return getAsCalendar(auditable.getLastModifiedDate());
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(org.joda.time.DateTime)
*/
public void setLastModifiedDate(Calendar value) {
auditable.setLastModifiedDate(new DateTime(value));
}
}
/**
* 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
*/
abstract static class DateConvertingAuditableBeanWrapper implements AuditableBeanWrapper {
private static final 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);
}
for (Converter<?, ?> converter : Jsr310Converters.getConvertersToRegister()) {
conversionService.addConverter(converter);
}
for (Converter<?, ?> converter : ThreeTenBackPortConverters.getConvertersToRegister()) {
conversionService.addConverter(converter);
}
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);
}
if (conversionService.canConvert(Date.class, targetType)) {
Date date = conversionService.convert(value, Date.class);
return conversionService.convert(date, targetType);
}
throw new IllegalArgumentException(String.format("Invalid date type for member %s! Supported types are %s.",
source, AnnotationAuditingMetadata.SUPPORTED_DATE_TYPES));
}
/**
* Returns the given object as {@link Calendar}.
*
* @param source can be {@literal null}.
* @return
*/
protected Calendar getAsCalendar(Object source) {
if (source == null || source instanceof Calendar) {
return (Calendar) source;
}
// Apply conversion to date if necessary and possible
source = !(source instanceof Date) && conversionService.canConvert(source.getClass(), Date.class) ? conversionService
.convert(source, Date.class) : source;
return conversionService.convert(source, Calendar.class);
}
}
/**
* An {@link AuditableBeanWrapper} implementation that sets values on the target object using refelction.
*
* @author Oliver Gierke
*/
static class ReflectionAuditingBeanWrapper extends DateConvertingAuditableBeanWrapper {
private final AnnotationAuditingMetadata metadata;
private final Object target;
/**
* Creates a new {@link ReflectionAuditingBeanWrapper} to set auditing data on the given target object.
*
* @param target must not be {@literal null}.
*/
public ReflectionAuditingBeanWrapper(Object target) {
Assert.notNull(target, "Target object must not be null!");
this.metadata = AnnotationAuditingMetadata.getMetadata(target.getClass());
this.target = target;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
*/
public void setCreatedBy(Object value) {
setField(metadata.getCreatedByField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Calendar)
*/
public void setCreatedDate(Calendar value) {
setDateField(metadata.getCreatedDateField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
*/
public void setLastModifiedBy(Object value) {
setField(metadata.getLastModifiedByField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#getLastModifiedDate()
*/
@Override
public Calendar getLastModifiedDate() {
return getAsCalendar(org.springframework.util.ReflectionUtils.getField(metadata.getLastModifiedDateField(),
target));
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)
*/
public void setLastModifiedDate(Calendar value) {
setDateField(metadata.getLastModifiedDateField(), value);
}
/**
* Sets the given field to the given value if the field is not {@literal null}.
*
* @param field
* @param value
*/
private void setField(Field field, Object value) {
if (field != null) {
ReflectionUtils.setField(field, target, value);
}
}
/**
* Sets the given field to the given value if the field is not {@literal null}.
*
* @param field
* @param value
*/
private void setDateField(Field field, Calendar value) {
if (field == null) {
return;
}
ReflectionUtils.setField(field, target, getDateValueToSet(value, field.getType(), field));
}
}
private static enum CalendarToDateTimeConverter implements Converter<Calendar, DateTime> {
INSTANCE;
@Override
public DateTime convert(Calendar source) {
return new DateTime(source);
}
}
private static enum CalendarToLocalDateTimeConverter implements Converter<Calendar, LocalDateTime> {
INSTANCE;
@Override
public LocalDateTime convert(Calendar source) {
return new LocalDateTime(source);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,13 +15,15 @@
*/
package org.springframework.data.auditing;
import java.util.Arrays;
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.mapping.context.PersistentEntities;
import org.springframework.data.support.IsNewStrategy;
import org.springframework.data.support.IsNewStrategyFactory;
import org.springframework.util.Assert;
/**
* {@link AuditingHandler} extension that uses an {@link IsNewStrategyFactory} to expose a generic
@@ -40,14 +42,26 @@ public class IsNewAwareAuditingHandler extends AuditingHandler {
*
* @param mappingContext must not be {@literal null}.
* @since 1.8
* @deprecated use {@link IsNewAwareAuditingHandler(PersistentEntities)} instead.
*/
@Deprecated
@SuppressWarnings("unchecked")
public IsNewAwareAuditingHandler(
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
this(new PersistentEntities(Arrays.asList(mappingContext)));
}
super(mappingContext);
/**
* Creates a new {@link IsNewAwareAuditingHandler} for the given {@link MappingContext}.
*
* @param mappingContext must not be {@literal null}.
* @since 1.10
*/
public IsNewAwareAuditingHandler(PersistentEntities entities) {
Assert.notNull(mappingContext, "MappingContext must not be null!");
this.isNewStrategyFactory = new MappingContextIsNewStrategyFactory(mappingContext);
super(entities);
this.isNewStrategyFactory = new MappingContextIsNewStrategyFactory(entities);
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.util.Assert;
@@ -39,22 +40,21 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @since 1.8
*/
class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory {
public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrapperFactory {
private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext;
private final PersistentEntities entities;
private final Map<Class<?>, MappingAuditingMetadata> metadataCache;
/**
* Creates a new {@link MappingAuditableBeanWrapperFactory} using the given {@link MappingContext}.
* Creates a new {@link MappingAuditableBeanWrapperFactory} using the given {@link PersistentEntities}.
*
* @param mappingContext must not be {@literal null}.
* @param entities must not be {@literal null}.
*/
public MappingAuditableBeanWrapperFactory(
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
public MappingAuditableBeanWrapperFactory(PersistentEntities entities) {
Assert.notNull(mappingContext, "MappingContext must not be null!");
Assert.notNull(entities, "PersistentEntities must not be null!");
this.mappingContext = mappingContext;
this.entities = entities;
this.metadataCache = new HashMap<Class<?>, MappingAuditingMetadata>();
}
@@ -65,12 +65,16 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory {
@Override
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
if (source == null) {
return null;
}
if (source instanceof Auditable) {
return super.getBeanWrapperFor(source);
}
Class<?> type = source.getClass();
PersistentEntity<?, ?> entity = mappingContext.getPersistentEntity(type);
PersistentEntity<?, ?> entity = entities.getPersistentEntity(type);
if (entity == null) {
return super.getBeanWrapperFor(source);
@@ -191,6 +195,22 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#getLastModifiedDate()
*/
@Override
public Calendar getLastModifiedDate() {
PersistentProperty<?> property = metadata.lastModifiedDateProperty;
if (property == null) {
return null;
}
return getAsCalendar(accessor.getProperty(property));
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)

View File

@@ -24,6 +24,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -66,6 +67,16 @@ public abstract class Jsr310Converters {
return converters;
}
public static boolean supports(Class<?> type) {
if (!JAVA_8_IS_PRESENT) {
return false;
}
return Arrays.<Class<?>> asList(LocalDateTime.class, LocalDate.class, LocalTime.class, Instant.class)
.contains(type);
}
public static enum DateToLocalDateTimeConverter implements Converter<Date, LocalDateTime> {
INSTANCE;

View File

@@ -21,6 +21,7 @@ import static org.threeten.bp.LocalDateTime.*;
import static org.threeten.bp.ZoneId.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -70,6 +71,16 @@ public abstract class ThreeTenBackPortConverters {
return converters;
}
public static boolean supports(Class<?> type) {
if (!THREE_TEN_BACK_PORT_IS_PRESENT) {
return false;
}
return Arrays.<Class<?>> asList(LocalDateTime.class, LocalDate.class, LocalTime.class, Instant.class)
.contains(type);
}
public static enum DateToLocalDateTimeConverter implements Converter<Date, LocalDateTime> {
INSTANCE;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package org.springframework.data.mapping.context;
import java.util.Arrays;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
@@ -34,17 +36,30 @@ import org.springframework.util.Assert;
*/
public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupport {
private final MappingContext<? extends PersistentEntity<?, ?>, ?> context;
private final PersistentEntities context;
/**
* Creates a new {@link MappingContextIsNewStrategyFactory} using the given {@link MappingContext}.
*
* @param context must not be {@literal null}.
* @deprecated use {@link MappingContextIsNewStrategyFactory(PersistentEntities)} instead.
*/
@Deprecated
@SuppressWarnings("unchecked")
public MappingContextIsNewStrategyFactory(MappingContext<? extends PersistentEntity<?, ?>, ?> context) {
this(new PersistentEntities(Arrays.asList(context)));
}
Assert.notNull(context, "MappingContext must not be null!");
this.context = context;
/**
* Creates a new {@link MappingContextIsNewStrategyFactory} using the given {@link PersistentEntities}.
*
* @param context must not be {@literal null}.
* @since 1.10
*/
public MappingContextIsNewStrategyFactory(PersistentEntities entities) {
Assert.notNull(entities, "PersistentEntities must not be null!");
this.context = entities;
}
/*