DATACMNS-867 - First draft.

This commit is contained in:
Oliver Gierke
2016-11-14 20:10:22 +01:00
parent 1b17271915
commit cc63e5b7a4
278 changed files with 4737 additions and 5714 deletions

View File

@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.data.annotation.CreatedBy;
@@ -29,6 +30,7 @@ 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.Optionals;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.data.util.ReflectionUtils.AnnotationFieldFilter;
import org.springframework.util.Assert;
@@ -68,10 +70,10 @@ final class AnnotationAuditingMetadata {
SUPPORTED_DATE_TYPES = Collections.unmodifiableList(types);
}
private final Field createdByField;
private final Field createdDateField;
private final Field lastModifiedByField;
private final Field lastModifiedDateField;
private final Optional<Field> createdByField;
private final Optional<Field> createdDateField;
private final Optional<Field> lastModifiedByField;
private final Optional<Field> lastModifiedDateField;
/**
* Creates a new {@link AnnotationAuditingMetadata} instance for the given type.
@@ -82,10 +84,10 @@ final class AnnotationAuditingMetadata {
Assert.notNull(type, "Given type must not be null!");
this.createdByField = ReflectionUtils.findField(type, CREATED_BY_FILTER);
this.createdDateField = ReflectionUtils.findField(type, CREATED_DATE_FILTER);
this.lastModifiedByField = ReflectionUtils.findField(type, LAST_MODIFIED_BY_FILTER);
this.lastModifiedDateField = ReflectionUtils.findField(type, LAST_MODIFIED_DATE_FILTER);
this.createdByField = Optional.ofNullable(ReflectionUtils.findField(type, CREATED_BY_FILTER));
this.createdDateField = Optional.ofNullable(ReflectionUtils.findField(type, CREATED_DATE_FILTER));
this.lastModifiedByField = Optional.ofNullable(ReflectionUtils.findField(type, LAST_MODIFIED_BY_FILTER));
this.lastModifiedDateField = Optional.ofNullable(ReflectionUtils.findField(type, LAST_MODIFIED_DATE_FILTER));
assertValidDateFieldType(createdDateField);
assertValidDateFieldType(lastModifiedDateField);
@@ -94,23 +96,26 @@ final class AnnotationAuditingMetadata {
/**
* Checks whether the given field has a type that is a supported date type.
*
* @param field can be {@literal null}.
* @param field
*/
private void assertValidDateFieldType(Field field) {
private void assertValidDateFieldType(Optional<Field> field) {
if (field == null || SUPPORTED_DATE_TYPES.contains(field.getType().getName())) {
return;
}
field.ifPresent(it -> {
Class<?> type = field.getType();
if (SUPPORTED_DATE_TYPES.contains(it.getType().getName())) {
return;
}
if (Jsr310Converters.supports(type) || ThreeTenBackPortConverters.supports(type)) {
return;
}
Class<?> type = it.getType();
throw new IllegalStateException(String.format(
"Found created/modified date field with type %s but only %s as well as java.time types are supported!", type,
SUPPORTED_DATE_TYPES));
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!", type,
SUPPORTED_DATE_TYPES));
});
}
/**
@@ -119,52 +124,41 @@ final class AnnotationAuditingMetadata {
* @param type the type to inspect, must not be {@literal null}.
*/
public static AnnotationAuditingMetadata getMetadata(Class<?> type) {
if (METADATA_CACHE.containsKey(type)) {
return METADATA_CACHE.get(type);
}
AnnotationAuditingMetadata metadata = new AnnotationAuditingMetadata(type);
METADATA_CACHE.put(type, metadata);
return metadata;
return METADATA_CACHE.computeIfAbsent(type, it -> new AnnotationAuditingMetadata(it));
}
/**
* Returns whether the {@link Class} represented in this instance is auditable or not.
*/
public boolean isAuditable() {
if (createdByField == null && createdDateField == null && lastModifiedByField == null
&& lastModifiedDateField == null) {
return false;
}
return true;
return Optionals.isAnyPresent(createdByField, createdDateField, lastModifiedByField, lastModifiedDateField);
}
/**
* Return the field annotated by {@link CreatedBy}, or {@literal null}.
* Return the field annotated by {@link CreatedBy}.
*/
public Field getCreatedByField() {
public Optional<Field> getCreatedByField() {
return createdByField;
}
/**
* Return the field annotated by {@link CreatedDate}, or {@literal null}.
* Return the field annotated by {@link CreatedDate}.
*/
public Field getCreatedDateField() {
public Optional<Field> getCreatedDateField() {
return createdDateField;
}
/**
* Return the field annotated by {@link LastModifiedBy}, or {@literal null}.
* Return the field annotated by {@link LastModifiedBy}.
*/
public Field getLastModifiedByField() {
public Optional<Field> getLastModifiedByField() {
return lastModifiedByField;
}
/**
* Return the field annotated by {@link LastModifiedDate}, or {@literal null}.
* Return the field annotated by {@link LastModifiedDate}.
*/
public Field getLastModifiedDateField() {
public Optional<Field> getLastModifiedDateField() {
return lastModifiedDateField;
}
}

View File

@@ -15,7 +15,8 @@
*/
package org.springframework.data.auditing;
import java.util.Calendar;
import java.time.temporal.TemporalAccessor;
import java.util.Optional;
/**
* Interface to abstract the ways setting the auditing information can be implemented.
@@ -30,34 +31,34 @@ public interface AuditableBeanWrapper {
*
* @param value
*/
void setCreatedBy(Object value);
Optional<? extends Object> setCreatedBy(Optional<? extends Object> value);
/**
* Set the date the object was created.
*
* @param value
*/
void setCreatedDate(Calendar value);
Optional<TemporalAccessor> setCreatedDate(Optional<TemporalAccessor> value);
/**
* Set the last modifier of the object.
*
* @param value
*/
void setLastModifiedBy(Object value);
Optional<? extends Object> setLastModifiedBy(Optional<? extends 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}.
* @return the date of the last modification.
* @since 1.10
*/
Calendar getLastModifiedDate();
Optional<TemporalAccessor> getLastModifiedDate();
/**
* Set the last modification date.
*
* @param value
*/
void setLastModifiedDate(Calendar value);
Optional<TemporalAccessor> setLastModifiedDate(Optional<TemporalAccessor> value);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.auditing;
import java.util.Optional;
/**
* A factory to lookup {@link AuditableBeanWrapper}s.
*
@@ -24,11 +26,10 @@ package org.springframework.data.auditing;
public interface AuditableBeanWrapperFactory {
/**
* Returns the {@link AuditableBeanWrapper} for the given source obejct if it's eligible for auditing.
* Returns the {@link AuditableBeanWrapper} for the given source object if it's eligible for auditing.
*
* @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.
* @param source.
* @return the {@link AuditableBeanWrapper} for the given source object if it's eligible for auditing.
*/
AuditableBeanWrapper getBeanWrapperFor(Object source);
Optional<AuditableBeanWrapper> getBeanWrapperFor(Optional<? extends Object> source);
}

View File

@@ -15,8 +15,9 @@
*/
package org.springframework.data.auditing;
import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Optional;
import org.joda.time.DateTime;
import org.slf4j.Logger;
@@ -43,7 +44,7 @@ public class AuditingHandler implements InitializingBean {
private final DefaultAuditableBeanWrapperFactory factory;
private DateTimeProvider dateTimeProvider = CurrentDateTimeProvider.INSTANCE;
private AuditorAware<?> auditorAware;
private Optional<AuditorAware<?>> auditorAware;
private boolean dateTimeForNow = true;
private boolean modifyOnCreation = true;
@@ -56,7 +57,6 @@ public class AuditingHandler implements InitializingBean {
* @deprecated use {@link AuditingHandler(PersistentEntities)} instead.
*/
@Deprecated
@SuppressWarnings("unchecked")
public AuditingHandler(
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
this(new PersistentEntities(Arrays.asList(mappingContext)));
@@ -72,7 +72,9 @@ public class AuditingHandler implements InitializingBean {
public AuditingHandler(PersistentEntities entities) {
Assert.notNull(entities, "PersistentEntities must not be null!");
this.factory = new MappingAuditableBeanWrapperFactory(entities);
this.auditorAware = Optional.empty();
}
/**
@@ -80,10 +82,10 @@ public class AuditingHandler implements InitializingBean {
*
* @param auditorAware must not be {@literal null}.
*/
public void setAuditorAware(final AuditorAware<?> auditorAware) {
public void setAuditorAware(AuditorAware<?> auditorAware) {
Assert.notNull(auditorAware, "AuditorAware must not be null!");
this.auditorAware = auditorAware;
this.auditorAware = Optional.of(auditorAware);
}
/**
@@ -121,7 +123,7 @@ public class AuditingHandler implements InitializingBean {
*
* @param source
*/
public void markCreated(Object source) {
public void markCreated(Optional<? extends Object> source) {
touch(source, true);
}
@@ -130,35 +132,35 @@ public class AuditingHandler implements InitializingBean {
*
* @param source
*/
public void markModified(Object source) {
public void markModified(Optional<? extends Object> source) {
touch(source, false);
}
/**
* Returns whether the given source is considered to be auditable in the first place
*
* @param source can be {@literal null}.
*
* @param source must not be {@literal null}.
* @return
*/
protected final boolean isAuditable(Object source) {
return factory.getBeanWrapperFor(source) != null;
protected final boolean isAuditable(Optional<Object> source) {
return source.flatMap(o -> factory.getBeanWrapperFor(source)).isPresent();
}
private void touch(Object target, boolean isNew) {
private void touch(Optional<? extends Object> target, boolean isNew) {
AuditableBeanWrapper wrapper = factory.getBeanWrapperFor(target);
Optional<AuditableBeanWrapper> wrapper = factory.getBeanWrapperFor(target);
if (wrapper == null) {
return;
}
wrapper.ifPresent(it -> {
Object auditor = touchAuditor(wrapper, isNew);
Calendar now = dateTimeForNow ? touchDate(wrapper, isNew) : null;
Optional<Object> auditor = touchAuditor(it, isNew);
Optional<TemporalAccessor> now = dateTimeForNow ? touchDate(it, isNew) : Optional.empty();
Object defaultedNow = now == null ? "not set" : now;
Object defaultedAuditor = auditor == null ? "unknown" : auditor;
Object defaultedNow = now.map(Object::toString).orElse("not set");
Object defaultedAuditor = auditor.map(Object::toString).orElse("unknown");
LOGGER.debug("Touched {} - Last modification at {} by {}", new Object[] { target, defaultedNow, defaultedAuditor });
LOGGER.debug("Touched {} - Last modification at {} by {}",
new Object[] { target, defaultedNow, defaultedAuditor });
});
}
/**
@@ -167,23 +169,22 @@ public class AuditingHandler implements InitializingBean {
* @param auditable
* @return
*/
private Object touchAuditor(AuditableBeanWrapper wrapper, boolean isNew) {
private Optional<Object> touchAuditor(AuditableBeanWrapper wrapper, boolean isNew) {
if (null == auditorAware) {
return null;
}
return auditorAware.map(it -> {
Object auditor = auditorAware.getCurrentAuditor();
Optional<?> auditor = it.getCurrentAuditor();
if (isNew) {
wrapper.setCreatedBy(auditor);
if (!modifyOnCreation) {
return auditor;
if (isNew) {
wrapper.setCreatedBy(auditor);
if (!modifyOnCreation) {
return auditor;
}
}
}
wrapper.setLastModifiedBy(auditor);
return auditor;
return wrapper.setLastModifiedBy(auditor);
});
}
/**
@@ -192,9 +193,9 @@ public class AuditingHandler implements InitializingBean {
* @param wrapper
* @return
*/
private Calendar touchDate(AuditableBeanWrapper wrapper, boolean isNew) {
private Optional<TemporalAccessor> touchDate(AuditableBeanWrapper wrapper, boolean isNew) {
Calendar now = dateTimeProvider.getNow();
Optional<TemporalAccessor> now = dateTimeProvider.getNow();
if (isNew) {
wrapper.setCreatedDate(now);
@@ -203,8 +204,7 @@ public class AuditingHandler implements InitializingBean {
}
}
wrapper.setLastModifiedDate(now);
return now;
return wrapper.setLastModifiedDate(now);
}
/*

View File

@@ -15,8 +15,9 @@
*/
package org.springframework.data.auditing;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Optional;
import org.joda.time.DateTime;
@@ -35,7 +36,7 @@ public enum CurrentDateTimeProvider implements DateTimeProvider {
* @see org.springframework.data.auditing.DateTimeProvider#getNow()
*/
@Override
public Calendar getNow() {
return new GregorianCalendar();
public Optional<TemporalAccessor> getNow() {
return Optional.of(ZonedDateTime.now());
}
}

View File

@@ -15,7 +15,8 @@
*/
package org.springframework.data.auditing;
import java.util.Calendar;
import java.time.temporal.TemporalAccessor;
import java.util.Optional;
/**
* SPI to calculate the current time to be used when auditing.
@@ -30,5 +31,5 @@ public interface DateTimeProvider {
*
* @return
*/
Calendar getNow();
Optional<TemporalAccessor> getNow();
}

View File

@@ -15,21 +15,24 @@
*/
package org.springframework.data.auditing;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.lang.reflect.Field;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.Optional;
import org.joda.time.DateTime;
import org.joda.time.LocalDateTime;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.JodaTimeConverters;
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.
@@ -46,23 +49,22 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @return
*/
@SuppressWarnings("unchecked")
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
public Optional<AuditableBeanWrapper> getBeanWrapperFor(Optional<? extends Object> source) {
return source.map(it -> {
if (it instanceof Auditable) {
return new AuditableInterfaceBeanWrapper((Auditable<Object, ?, TemporalAccessor>) it);
}
AnnotationAuditingMetadata metadata = AnnotationAuditingMetadata.getMetadata(it.getClass());
if (metadata.isAuditable()) {
return new ReflectionAuditingBeanWrapper(it);
}
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;
});
}
/**
@@ -70,36 +72,53 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
*
* @author Oliver Gierke
*/
@RequiredArgsConstructor
static class AuditableInterfaceBeanWrapper extends DateConvertingAuditableBeanWrapper {
private final Auditable<Object, ?> auditable;
private final @NonNull Auditable<Object, ?, TemporalAccessor> auditable;
private final Class<? extends TemporalAccessor> type;
@SuppressWarnings("unchecked")
public AuditableInterfaceBeanWrapper(Auditable<Object, ?, TemporalAccessor> auditable) {
public AuditableInterfaceBeanWrapper(Auditable<Object, ?> auditable) {
this.auditable = auditable;
this.type = (Class<? extends TemporalAccessor>) ResolvableType.forClass(Auditable.class, auditable.getClass())
.getGeneric(2).getRawClass();
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.util.Optional)
*/
public void setCreatedBy(Object value) {
@Override
public Optional<? extends Object> setCreatedBy(Optional<? extends 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));
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Optional)
*/
public void setLastModifiedBy(Object value) {
@Override
public Optional<TemporalAccessor> setCreatedDate(Optional<TemporalAccessor> value) {
auditable.setCreatedDate(getAsTemporalAccessor(value, type));
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.DefaultAuditableBeanWrapperFactory.AuditableInterfaceBeanWrapper#setLastModifiedBy(java.util.Optional)
*/
@Override
public Optional<? extends Object> setLastModifiedBy(Optional<? extends Object> value) {
auditable.setLastModifiedBy(value);
return value;
}
/*
@@ -107,16 +126,20 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @see org.springframework.data.auditing.AuditableBeanWrapper#getLastModifiedDate()
*/
@Override
public Calendar getLastModifiedDate() {
return getAsCalendar(auditable.getLastModifiedDate());
public Optional<TemporalAccessor> getLastModifiedDate() {
return getAsTemporalAccessor(auditable.getLastModifiedDate(), TemporalAccessor.class);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(org.joda.time.DateTime)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Optional)
*/
public void setLastModifiedDate(Calendar value) {
auditable.setLastModifiedDate(new DateTime(value));
@Override
public Optional<TemporalAccessor> setLastModifiedDate(Optional<TemporalAccessor> value) {
auditable.setLastModifiedDate(getAsTemporalAccessor(value, type));
return value;
}
}
@@ -129,9 +152,6 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
*/
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;
/**
@@ -141,18 +161,9 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
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);
}
JodaTimeConverters.getConvertersToRegister().forEach(it -> conversionService.addConverter(it));
Jsr310Converters.getConvertersToRegister().forEach(it -> conversionService.addConverter(it));
ThreeTenBackPortConverters.getConvertersToRegister().forEach(it -> conversionService.addConverter(it));
this.conversionService = conversionService;
}
@@ -165,28 +176,27 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @param source must not be {@literal null}.
* @return
*/
protected Object getDateValueToSet(Calendar value, Class<?> targetType, Object source) {
protected Optional<Object> getDateValueToSet(Optional<TemporalAccessor> value, Class<?> targetType, Object source) {
if (value == null) {
return null;
}
return value.map(it -> {
if (Calendar.class.equals(targetType)) {
return value;
}
if (TemporalAccessor.class.equals(targetType)) {
return it;
}
if (conversionService.canConvert(Calendar.class, targetType)) {
return conversionService.convert(value, targetType);
}
if (conversionService.canConvert(it.getClass(), targetType)) {
return conversionService.convert(it, targetType);
}
if (conversionService.canConvert(Date.class, targetType)) {
if (conversionService.canConvert(Date.class, targetType)) {
Date date = conversionService.convert(value, Date.class);
return conversionService.convert(date, targetType);
}
Date date = conversionService.convert(it, 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));
throw new IllegalArgumentException(String.format("Invalid date type for member %s! Supported types are %s.",
source, AnnotationAuditingMetadata.SUPPORTED_DATE_TYPES));
});
}
/**
@@ -195,22 +205,17 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @param source can be {@literal null}.
* @return
*/
protected Calendar getAsCalendar(Object source) {
@SuppressWarnings("unchecked")
protected <T> Optional<T> getAsTemporalAccessor(Optional<?> source, Class<T> target) {
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);
return source.map(it -> {
return target.isInstance(it) ? (T) it : conversionService.convert(it, target);
});
}
}
/**
* An {@link AuditableBeanWrapper} implementation that sets values on the target object using refelction.
* An {@link AuditableBeanWrapper} implementation that sets values on the target object using reflection.
*
* @author Oliver Gierke
*/
@@ -234,26 +239,29 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.util.Optional)
*/
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);
@Override
public Optional<? extends Object> setCreatedBy(Optional<? extends Object> value) {
return setField(metadata.getCreatedByField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Optional)
*/
public void setLastModifiedBy(Object value) {
setField(metadata.getLastModifiedByField(), value);
@Override
public Optional<TemporalAccessor> setCreatedDate(Optional<TemporalAccessor> value) {
return setDateField(metadata.getCreatedDateField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.util.Optional)
*/
@Override
public Optional<? extends Object> setLastModifiedBy(Optional<? extends Object> value) {
return setField(metadata.getLastModifiedByField(), value);
}
/*
@@ -261,18 +269,39 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @see org.springframework.data.auditing.AuditableBeanWrapper#getLastModifiedDate()
*/
@Override
public Calendar getLastModifiedDate() {
public Optional<TemporalAccessor> getLastModifiedDate() {
return getAsCalendar(org.springframework.util.ReflectionUtils.getField(metadata.getLastModifiedDateField(),
target));
return getAsTemporalAccessor(metadata.getLastModifiedDateField().map(field -> {
Object value = org.springframework.util.ReflectionUtils.getField(field, target);
return Optional.class.isInstance(value) ? ((Optional<?>) value).orElse(null) : value;
}), TemporalAccessor.class);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Optional)
*/
public void setLastModifiedDate(Calendar value) {
setDateField(metadata.getLastModifiedDateField(), value);
@Override
public Optional<TemporalAccessor> setLastModifiedDate(Optional<TemporalAccessor> value) {
return setDateField(metadata.getLastModifiedDateField(), value);
}
/**
* Sets the given field to the given value if present.
*
* @param field
* @param value
*/
private Optional<? extends Object> setField(Optional<Field> field, Optional<? extends Object> value) {
field.ifPresent(it -> {
ReflectionUtils.setField(it, target,
Optional.class.isAssignableFrom(it.getType()) ? value : value.orElse(null));
});
return value;
}
/**
@@ -281,46 +310,15 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @param field
* @param value
*/
private void setField(Field field, Object value) {
private Optional<TemporalAccessor> setDateField(Optional<Field> field, Optional<TemporalAccessor> value) {
if (field != null) {
ReflectionUtils.setField(field, target, value);
}
}
field.ifPresent(it -> {
Optional<Object> toSet = getDateValueToSet(value, it.getType(), it);
ReflectionUtils.setField(it, target,
Optional.class.isAssignableFrom(it.getType()) ? toSet : toSet.orElse(null));
});
/**
* 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);
return value;
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.auditing;
import java.util.Arrays;
import java.util.Optional;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
@@ -24,11 +25,12 @@ import org.springframework.data.mapping.context.MappingContextIsNewStrategyFacto
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
* {@link #markAudited(Object)} method that will route calls to {@link #markCreated(Object)} or
* {@link #markModified(Object)} based on the {@link IsNewStrategy} determined from the factory.
* {@link #markAudited(Optional)} method that will route calls to {@link #markCreated(Optional)} or
* {@link #markModified(Optional)} based on the {@link IsNewStrategy} determined from the factory.
*
* @author Oliver Gierke
* @since 1.5
@@ -45,7 +47,6 @@ public class IsNewAwareAuditingHandler extends AuditingHandler {
* @deprecated use {@link IsNewAwareAuditingHandler(PersistentEntities)} instead.
*/
@Deprecated
@SuppressWarnings("unchecked")
public IsNewAwareAuditingHandler(
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
this(new PersistentEntities(Arrays.asList(mappingContext)));
@@ -54,7 +55,7 @@ public class IsNewAwareAuditingHandler extends AuditingHandler {
/**
* Creates a new {@link IsNewAwareAuditingHandler} for the given {@link MappingContext}.
*
* @param mappingContext must not be {@literal null}.
* @param entities must not be {@literal null}.
* @since 1.10
*/
public IsNewAwareAuditingHandler(PersistentEntities entities) {
@@ -66,23 +67,28 @@ public class IsNewAwareAuditingHandler extends AuditingHandler {
/**
* Marks the given object created or modified based on the {@link IsNewStrategy} returned by the
* {@link IsNewStrategyFactory} configured. Will rout the calls to {@link #markCreated(Object)} and
* {@link #markModified(Object)} accordingly.
* {@link IsNewStrategyFactory} configured. Will rout the calls to {@link #markCreated(Optional)} and
* {@link #markModified(Optional)} accordingly.
*
* @param object
*/
public void markAudited(Object object) {
public void markAudited(Optional<Object> object) {
Assert.notNull(object, "Source object must not be null!");
if (!isAuditable(object)) {
return;
}
IsNewStrategy strategy = isNewStrategyFactory.getIsNewStrategy(object.getClass());
object.ifPresent(it -> {
if (strategy.isNew(object)) {
markCreated(object);
} else {
markModified(object);
}
IsNewStrategy strategy = isNewStrategyFactory.getIsNewStrategy(it.getClass());
if (strategy.isNew(object)) {
markCreated(object);
} else {
markModified(object);
}
});
}
}

View File

@@ -15,9 +15,10 @@
*/
package org.springframework.data.auditing;
import java.util.Calendar;
import java.time.temporal.TemporalAccessor;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
@@ -29,6 +30,7 @@ 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.util.Optionals;
import org.springframework.util.Assert;
/**
@@ -62,32 +64,27 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
* @see org.springframework.data.auditing.AuditableBeanWrapperFactory#getBeanWrapperFor(java.lang.Object)
*/
@Override
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
public Optional<AuditableBeanWrapper> getBeanWrapperFor(Optional<? extends Object> source) {
if (source == null) {
return null;
}
return source.flatMap(it -> {
if (source instanceof Auditable) {
return super.getBeanWrapperFor(source);
}
if (it instanceof Auditable) {
return super.getBeanWrapperFor(source);
}
Class<?> type = source.getClass();
PersistentEntity<?, ?> entity = entities.getPersistentEntity(type);
Class<?> type = it.getClass();
PersistentEntity<?, ?> entity = entities.getPersistentEntity(type);
if (entity == null) {
return super.getBeanWrapperFor(source);
}
if (entity == null) {
return super.getBeanWrapperFor(source);
}
MappingAuditingMetadata metadata = metadataCache.get(type);
MappingAuditingMetadata metadata = metadataCache.computeIfAbsent(type,
foo -> new MappingAuditingMetadata(entity));
if (metadata == null) {
metadata = new MappingAuditingMetadata(entity);
metadataCache.put(type, metadata);
}
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(source);
return metadata.isAuditable() ? new MappingMetadataAuditableBeanWrapper(accessor, metadata) : null;
return Optional.ofNullable(metadata.isAuditable()
? new MappingMetadataAuditableBeanWrapper(entity.getPropertyAccessor(it), metadata) : null);
});
}
/**
@@ -98,8 +95,8 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
*/
static class MappingAuditingMetadata {
private final PersistentProperty<?> createdByProperty, createdDateProperty, lastModifiedByProperty,
lastModifiedDateProperty;
private final Optional<? extends PersistentProperty<?>> createdByProperty, createdDateProperty,
lastModifiedByProperty, lastModifiedDateProperty;
/**
* Creates a new {@link MappingAuditingMetadata} instance from the given {@link PersistentEntity}.
@@ -123,8 +120,8 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
* @return
*/
public boolean isAuditable() {
return createdByProperty != null || createdDateProperty != null || lastModifiedByProperty != null
|| lastModifiedDateProperty != null;
return Optionals.isAnyPresent(createdByProperty, createdDateProperty, lastModifiedByProperty,
lastModifiedDateProperty);
}
}
@@ -158,40 +155,44 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.util.Optional)
*/
@Override
public void setCreatedBy(Object value) {
public Optional<? extends Object> setCreatedBy(Optional<? extends Object> value) {
if (metadata.createdByProperty != null) {
this.accessor.setProperty(metadata.createdByProperty, value);
}
metadata.createdByProperty.ifPresent(it -> {
this.accessor.setProperty(it, value);
});
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Calendar)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Optional)
*/
@Override
public void setCreatedDate(Calendar value) {
public Optional<TemporalAccessor> setCreatedDate(Optional<TemporalAccessor> value) {
PersistentProperty<?> property = metadata.createdDateProperty;
metadata.createdDateProperty.ifPresent(it -> {
this.accessor.setProperty(it, getDateValueToSet(value, it.getType(), it));
});
if (property != null) {
this.accessor.setProperty(property, getDateValueToSet(value, property.getType(), property));
}
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.util.Optional)
*/
@Override
public void setLastModifiedBy(Object value) {
public Optional<? extends Object> setLastModifiedBy(Optional<? extends Object> value) {
if (metadata.lastModifiedByProperty != null) {
this.accessor.setProperty(metadata.lastModifiedByProperty, value);
}
metadata.lastModifiedByProperty.ifPresent(it -> {
this.accessor.setProperty(it, value);
});
return value;
}
/*
@@ -199,29 +200,23 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
* @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));
public Optional<TemporalAccessor> getLastModifiedDate() {
return getAsTemporalAccessor(metadata.lastModifiedDateProperty.map(it -> accessor.getProperty(it)),
TemporalAccessor.class);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Optional)
*/
@Override
public void setLastModifiedDate(Calendar value) {
public Optional<TemporalAccessor> setLastModifiedDate(Optional<TemporalAccessor> value) {
PersistentProperty<?> property = metadata.lastModifiedDateProperty;
metadata.lastModifiedDateProperty.ifPresent(it -> {
this.accessor.setProperty(it, getDateValueToSet(value, it.getType(), it));
});
if (property != null) {
this.accessor.setProperty(property, getDateValueToSet(value, property.getType(), property));
}
return value;
}
}
}

View File

@@ -1,60 +0,0 @@
/*
* 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.
* 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.convert;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.model.ParameterValueProvider;
/**
* An {@link EntityInstantiator} that can generate byte code to speed-up dynamic object instantiation. Uses the
* {@link PersistentEntity}'s {@link PreferredConstructor} to instantiate an instance of the entity by dynamically
* generating factory methods with appropriate constructor invocations via ASM. If we cannot generate byte code for a
* type, we gracefully fall-back to the {@link ReflectionEntityInstantiator}.
*
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.10
* @deprecated since 1.11 in favor of {@link ClassGeneratingEntityInstantiator}.
*/
@Deprecated
public enum BytecodeGeneratingEntityInstantiator implements EntityInstantiator {
INSTANCE;
private final ClassGeneratingEntityInstantiator delegate = new ClassGeneratingEntityInstantiator();
/*
* (non-Javadoc)
* @see org.springframework.data.convert.EntityInstantiator#createInstance(org.springframework.data.mapping.PersistentEntity, org.springframework.data.mapping.model.ParameterValueProvider)
*/
@Override
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
ParameterValueProvider<P> provider) {
return this.delegate.createInstance(entity, provider);
}
/**
* @author Thomas Darimont
* @deprecated
*/
public interface ObjectInstantiator {
Object newInstance(Object... args);
}
}

View File

@@ -21,11 +21,10 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.asm.ClassWriter;
import org.springframework.asm.MethodVisitor;
@@ -34,7 +33,6 @@ import org.springframework.asm.Type;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.model.MappingInstantiationException;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.util.TypeInformation;
@@ -140,12 +138,10 @@ public class ClassGeneratingEntityInstantiator implements EntityInstantiator {
return true;
}
PreferredConstructor<?, ?> persistenceConstructor = entity.getPersistenceConstructor();
if (persistenceConstructor == null || !Modifier.isPublic(persistenceConstructor.getConstructor().getModifiers())) {
return true;
}
return false;
return entity.getPersistenceConstructor()//
.map(PreferredConstructor::getConstructor)//
.map(Constructor::getModifiers)//
.map(modifier -> !Modifier.isPublic(modifier)).orElse(false);
}
/**
@@ -200,7 +196,7 @@ public class ClassGeneratingEntityInstantiator implements EntityInstantiator {
try {
return (T) instantiator.newInstance(params);
} catch (Exception e) {
throw new MappingInstantiationException(entity, Arrays.asList(params), e);
throw new MappingInstantiationException(Optional.of(entity), Arrays.asList(params), e);
}
}
@@ -212,19 +208,19 @@ public class ClassGeneratingEntityInstantiator implements EntityInstantiator {
* @return
*/
private <P extends PersistentProperty<P>, T> Object[] extractInvocationArguments(
PreferredConstructor<? extends T, P> constructor, ParameterValueProvider<P> provider) {
Optional<? extends PreferredConstructor<? extends T, P>> constructor, ParameterValueProvider<P> provider) {
if (provider == null || constructor == null || !constructor.hasParameters()) {
return EMPTY_ARRAY;
}
return constructor.map(it -> {
List<Object> params = new ArrayList<Object>();
if (provider == null || !it.hasParameters()) {
return EMPTY_ARRAY;
}
for (Parameter<?, P> parameter : constructor.getParameters()) {
params.add(provider.getParameterValue(parameter));
}
return it.getParameters().stream()//
.map(parameter -> provider.getParameterValue(parameter))//
.toArray();
return params.toArray();
}).orElse(EMPTY_ARRAY);
}
}
@@ -366,26 +362,31 @@ public class ClassGeneratingEntityInstantiator implements EntityInstantiator {
mv.visitTypeInsn(NEW, entityTypeResourcePath);
mv.visitInsn(DUP);
Constructor<?> ctor = entity.getPersistenceConstructor().getConstructor();
Class<?>[] parameterTypes = ctor.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
mv.visitVarInsn(ALOAD, 1);
entity.getPersistenceConstructor().ifPresent(constructor -> {
visitArrayIndex(mv, i);
Constructor<?> ctor = constructor.getConstructor();
Class<?>[] parameterTypes = ctor.getParameterTypes();
mv.visitInsn(AALOAD);
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i].isPrimitive()) {
insertUnboxInsns(mv, Type.getType(parameterTypes[i]).toString().charAt(0), "");
} else {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(parameterTypes[i]));
mv.visitVarInsn(ALOAD, 1);
visitArrayIndex(mv, i);
mv.visitInsn(AALOAD);
if (parameterTypes[i].isPrimitive()) {
insertUnboxInsns(mv, Type.getType(parameterTypes[i]).toString().charAt(0), "");
} else {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(parameterTypes[i]));
}
}
}
mv.visitMethodInsn(INVOKESPECIAL, entityTypeResourcePath, INIT, Type.getConstructorDescriptor(ctor), false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0); // (0, 0) = computed via ClassWriter.COMPUTE_MAXS
mv.visitEnd();
mv.visitMethodInsn(INVOKESPECIAL, entityTypeResourcePath, INIT, Type.getConstructorDescriptor(ctor), false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0); // (0, 0) = computed via ClassWriter.COMPUTE_MAXS
mv.visitEnd();
});
}
/**

View File

@@ -61,6 +61,9 @@ public abstract class JodaTimeConverters {
converters.add(DateToDateTimeConverter.INSTANCE);
converters.add(DateToDateMidnightConverter.INSTANCE);
converters.add(LocalDateTimeToJodaLocalDateTime.INSTANCE);
converters.add(LocalDateTimeToJodaDateTime.INSTANCE);
return converters;
}
@@ -135,4 +138,36 @@ public abstract class JodaTimeConverters {
return source == null ? null : new DateMidnight(source.getTime());
}
}
public static enum LocalDateTimeToJodaLocalDateTime implements Converter<java.time.LocalDateTime, LocalDateTime> {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Override
public LocalDateTime convert(java.time.LocalDateTime source) {
return source == null ? null
: LocalDateTime.fromDateFields(
org.springframework.data.convert.Jsr310Converters.LocalDateTimeToDateConverter.INSTANCE.convert(source));
}
}
public static enum LocalDateTimeToJodaDateTime implements Converter<java.time.LocalDateTime, DateTime> {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Override
public DateTime convert(java.time.LocalDateTime source) {
return source == null ? null
: new DateTime(
org.springframework.data.convert.Jsr310Converters.LocalDateTimeToDateConverter.INSTANCE.convert(source));
}
}
}

View File

@@ -16,16 +16,16 @@
package org.springframework.data.convert;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.model.MappingInstantiationException;
import org.springframework.data.mapping.model.ParameterValueProvider;
@@ -43,39 +43,45 @@ public enum ReflectionEntityInstantiator implements EntityInstantiator {
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
ParameterValueProvider<P> provider) {
PreferredConstructor<? extends T, P> constructor = entity.getPersistenceConstructor();
return entity.getPersistenceConstructor().map(constructor -> {
if (constructor == null) {
List<Object> params = Optional.ofNullable(provider)//
.map(it -> constructor.getParameters().stream()//
.map(parameter -> it.getParameterValue(parameter).orElse(null))//
.collect(Collectors.toList()))//
.orElse(Collections.emptyList());
try {
Class<?> clazz = entity.getType();
return (T) BeanUtils.instantiateClass(constructor.getConstructor(), params.toArray());
} catch (BeanInstantiationException e) {
throw new MappingInstantiationException(Optional.of(entity), params, e);
}
}).orElseGet(() -> {
try {
Class<? extends T> clazz = entity.getType();
if (clazz.isArray()) {
Class<?> ctype = clazz;
int dims = 0;
while (ctype.isArray()) {
ctype = ctype.getComponentType();
dims++;
}
return (T) Array.newInstance(clazz, dims);
} else {
return BeanUtils.instantiateClass(entity.getType());
return BeanUtils.instantiateClass(clazz);
}
} catch (BeanInstantiationException e) {
throw new MappingInstantiationException(entity, Collections.emptyList(), e);
throw new MappingInstantiationException(Optional.of(entity), Collections.emptyList(), e);
}
}
List<Object> params = new ArrayList<Object>();
if (null != provider && constructor.hasParameters()) {
for (Parameter<?, P> parameter : constructor.getParameters()) {
params.add(provider.getParameterValue(parameter));
}
}
try {
return BeanUtils.instantiateClass(constructor.getConstructor(), params.toArray());
} catch (BeanInstantiationException e) {
throw new MappingInstantiationException(entity, params, e);
}
});
}
}

View File

@@ -186,4 +186,5 @@ public abstract class ThreeTenBackPortConverters {
return ZoneId.of(source);
}
}
}

View File

@@ -16,8 +16,8 @@
package org.springframework.data.domain;
import java.io.Serializable;
import org.joda.time.DateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Optional;
/**
* Interface for auditable entities. Allows storing and retrieving creation and modification information. The changing
@@ -27,61 +27,61 @@ import org.joda.time.DateTime;
* @param <ID> the type of the audited type's identifier
* @author Oliver Gierke
*/
public interface Auditable<U, ID extends Serializable> extends Persistable<ID> {
public interface Auditable<U, ID extends Serializable, T extends TemporalAccessor> extends Persistable<ID> {
/**
* Returns the user who created this entity.
*
* @return the createdBy
*/
U getCreatedBy();
Optional<U> getCreatedBy();
/**
* Sets the user who created this entity.
*
* @param createdBy the creating entity to set
*/
void setCreatedBy(final U createdBy);
void setCreatedBy(Optional<? extends U> createdBy);
/**
* Returns the creation date of the entity.
*
* @return the createdDate
*/
DateTime getCreatedDate();
Optional<T> getCreatedDate();
/**
* Sets the creation date of the entity.
*
* @param creationDate the creation date to set
*/
void setCreatedDate(final DateTime creationDate);
void setCreatedDate(Optional<? extends T> creationDate);
/**
* Returns the user who modified the entity lastly.
*
* @return the lastModifiedBy
*/
U getLastModifiedBy();
Optional<U> getLastModifiedBy();
/**
* Sets the user who modified the entity lastly.
*
* @param lastModifiedBy the last modifying entity to set
*/
void setLastModifiedBy(final U lastModifiedBy);
void setLastModifiedBy(Optional<? extends U> lastModifiedBy);
/**
* Returns the date of the last modification.
*
* @return the lastModifiedDate
*/
DateTime getLastModifiedDate();
Optional<T> getLastModifiedDate();
/**
* Sets the date of the last modification.
*
* @param lastModifiedDate the date of the last modification to set
*/
void setLastModifiedDate(final DateTime lastModifiedDate);
void setLastModifiedDate(Optional<? extends T> lastModifiedDate);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.domain;
import java.util.Optional;
/**
* Interface for components that are aware of the application's current auditor. This will be some kind of user mostly.
*
@@ -28,5 +30,5 @@ public interface AuditorAware<T> {
*
* @return the current auditor
*/
T getCurrentAuditor();
Optional<T> getCurrentAuditor();
}

View File

@@ -1,93 +1,94 @@
/*
* Copyright 2012 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.history;
import java.lang.annotation.Annotation;
import org.joda.time.DateTime;
import org.springframework.data.util.AnnotationDetectionFieldCallback;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* A {@link RevisionMetadata} implementation that inspects the given object for fields with the configured annotations
* and returns the field's values on calls to {@link #getRevisionDate()} and {@link #getRevisionNumber()}.
*
* @author Oliver Gierke
*/
public class AnnotationRevisionMetadata<N extends Number & Comparable<N>> implements RevisionMetadata<N> {
private final Object entity;
private final N revisionNumber;
private final DateTime revisionDate;
/**
* Creates a new {@link AnnotationRevisionMetadata} inspecing the given entity for the given annotations. If no
* annotations will be provided these values will not be looked up from the entity and return {@literal null}.
*
* @param entity must not be {@literal null}.
* @param revisionNumberAnnotation
* @param revisionTimeStampAnnotation
*/
public AnnotationRevisionMetadata(final Object entity, Class<? extends Annotation> revisionNumberAnnotation,
Class<? extends Annotation> revisionTimeStampAnnotation) {
Assert.notNull(entity, "Entity must not be null!");
this.entity = entity;
if (revisionNumberAnnotation != null) {
AnnotationDetectionFieldCallback numberCallback = new AnnotationDetectionFieldCallback(revisionNumberAnnotation);
ReflectionUtils.doWithFields(entity.getClass(), numberCallback);
this.revisionNumber = numberCallback.getValue(entity);
} else {
this.revisionNumber = null;
}
if (revisionTimeStampAnnotation != null) {
AnnotationDetectionFieldCallback revisionCallback = new AnnotationDetectionFieldCallback(
revisionTimeStampAnnotation);
ReflectionUtils.doWithFields(entity.getClass(), revisionCallback);
this.revisionDate = new DateTime(revisionCallback.<Object> getValue(entity));
} else {
this.revisionDate = null;
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.history.RevisionMetadata#getRevisionNumber()
*/
public N getRevisionNumber() {
return revisionNumber;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.history.RevisionMetadata#getRevisionDate()
*/
public DateTime getRevisionDate() {
return revisionDate;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.history.RevisionMetadata#getDelegate()
*/
@SuppressWarnings("unchecked")
public <T> T getDelegate() {
return (T) entity;
}
}
/*
* Copyright 2012 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.history;
import java.lang.annotation.Annotation;
import java.time.LocalDateTime;
import java.util.Optional;
import org.springframework.data.util.AnnotationDetectionFieldCallback;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* A {@link RevisionMetadata} implementation that inspects the given object for fields with the configured annotations
* and returns the field's values on calls to {@link #getRevisionDate()} and {@link #getRevisionNumber()}.
*
* @author Oliver Gierke
*/
public class AnnotationRevisionMetadata<N extends Number & Comparable<N>> implements RevisionMetadata<N> {
private final Object entity;
private final N revisionNumber;
private final LocalDateTime revisionDate;
/**
* Creates a new {@link AnnotationRevisionMetadata} inspecting the given entity for the given annotations. If no
* annotations will be provided these values will not be looked up from the entity and return {@literal null}.
*
* @param entity must not be {@literal null}.
* @param revisionNumberAnnotation
* @param revisionTimeStampAnnotation
*/
public AnnotationRevisionMetadata(final Object entity, Class<? extends Annotation> revisionNumberAnnotation,
Class<? extends Annotation> revisionTimeStampAnnotation) {
Assert.notNull(entity, "Entity must not be null!");
this.entity = entity;
if (revisionNumberAnnotation != null) {
AnnotationDetectionFieldCallback numberCallback = new AnnotationDetectionFieldCallback(revisionNumberAnnotation);
ReflectionUtils.doWithFields(entity.getClass(), numberCallback);
this.revisionNumber = numberCallback.getValue(entity);
} else {
this.revisionNumber = null;
}
if (revisionTimeStampAnnotation != null) {
AnnotationDetectionFieldCallback revisionCallback = new AnnotationDetectionFieldCallback(
revisionTimeStampAnnotation);
ReflectionUtils.doWithFields(entity.getClass(), revisionCallback);
this.revisionDate = revisionCallback.getValue(entity);
} else {
this.revisionDate = null;
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.history.RevisionMetadata#getRevisionNumber()
*/
public Optional<N> getRevisionNumber() {
return Optional.ofNullable(revisionNumber);
}
/*
* (non-Javadoc)
* @see org.springframework.data.history.RevisionMetadata#getRevisionDate()
*/
public Optional<LocalDateTime> getRevisionDate() {
return Optional.ofNullable(revisionDate);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.history.RevisionMetadata#getDelegate()
*/
@SuppressWarnings("unchecked")
public <T> T getDelegate() {
return (T) entity;
}
}

View File

@@ -15,8 +15,13 @@
*/
package org.springframework.data.history;
import org.joda.time.DateTime;
import org.springframework.util.Assert;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import java.time.LocalDateTime;
import java.util.Optional;
/**
* Wrapper to contain {@link RevisionMetadata} as well as the revisioned entity.
@@ -24,24 +29,29 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Philipp Huegelmeyer
*/
@Value
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public final class Revision<N extends Number & Comparable<N>, T> implements Comparable<Revision<N, ?>> {
private final RevisionMetadata<N> metadata;
private final T entity;
/**
* The {@link RevisionMetadata} for the current {@link Revision}.
*/
@NonNull RevisionMetadata<N> metadata;
/**
* Creates a new {@link Revision} consisting of the given {@link RevisionMetadata} and entity.
* The underlying entity.
*/
@NonNull T entity;
/**
* Creates a new {@link Revision} for the given {@link RevisionMetadata} and entity.
*
* @param metadata must not be {@literal null}.
* @param entity must not be {@literal null}.
* @return
*/
public Revision(RevisionMetadata<N> metadata, T entity) {
Assert.notNull(metadata, "Metadata must not be null!");
Assert.notNull(entity, "Entity must not be null!");
this.metadata = metadata;
this.entity = entity;
public static <N extends Number & Comparable<N>, T> Revision<N, T> of(RevisionMetadata<N> metadata, T entity) {
return new Revision<N, T>(metadata, entity);
}
/**
@@ -49,7 +59,7 @@ public final class Revision<N extends Number & Comparable<N>, T> implements Comp
*
* @return the revision number.
*/
public N getRevisionNumber() {
public Optional<N> getRevisionNumber() {
return metadata.getRevisionNumber();
}
@@ -58,67 +68,22 @@ public final class Revision<N extends Number & Comparable<N>, T> implements Comp
*
* @return
*/
public DateTime getRevisionDate() {
public Optional<LocalDateTime> getRevisionDate() {
return metadata.getRevisionDate();
}
/**
* Returns the underlying entity.
*
* @return the entity
*/
public T getEntity() {
return entity;
}
/**
* Returns the {@link RevisionMetadata} for the current {@link Revision}.
*
* @return the metadata
*/
public RevisionMetadata<N> getMetadata() {
return metadata;
}
/*
* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Revision<N, ?> that) {
return getRevisionNumber().compareTo(that.getRevisionNumber());
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
@SuppressWarnings("unchecked")
public boolean equals(Object obj) {
Optional<N> thisRevisionNumber = getRevisionNumber();
Optional<N> thatRevisionNumber = that.getRevisionNumber();
if (this == obj) {
return true;
}
if (!(obj instanceof Revision)) {
return false;
}
Revision<N, T> that = (Revision<N, T>) obj;
boolean sameRevisionNumber = this.metadata.getRevisionNumber().equals(that.metadata.getRevisionNumber());
return !sameRevisionNumber ? false : this.entity.equals(that.entity);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * metadata.hashCode();
result += 31 * entity.hashCode();
return result;
return thisRevisionNumber.map(left -> {
return thatRevisionNumber.map(right -> left.compareTo(right)).orElse(1);
}).orElse(-1);
}
/*

View File

@@ -1,48 +1,49 @@
/*
* Copyright 2012 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.history;
import org.joda.time.DateTime;
/**
* Metadata about a revision.
*
* @author Philipp Huegelmeyer
* @author Oliver Gierke
*/
public interface RevisionMetadata<N extends Number & Comparable<N>> {
/**
* Returns the revision number of the revision.
*
* @return
*/
N getRevisionNumber();
/**
* Returns the date of the revision.
*
* @return
*/
DateTime getRevisionDate();
/**
* Returns the underlying revision metadata which might provider more detailed implementation specific information.
*
* @return
*/
<T> T getDelegate();
}
/*
* Copyright 2012 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.history;
import java.time.LocalDateTime;
import java.util.Optional;
/**
* Metadata about a revision.
*
* @author Philipp Huegelmeyer
* @author Oliver Gierke
*/
public interface RevisionMetadata<N extends Number & Comparable<N>> {
/**
* Returns the revision number of the revision.
*
* @return
*/
Optional<N> getRevisionNumber();
/**
* Returns the date of the revision.
*
* @return
*/
Optional<LocalDateTime> getRevisionDate();
/**
* Returns the underlying revision metadata which might provider more detailed implementation specific information.
*
* @return
*/
<T> T getDelegate();
}

View File

@@ -15,10 +15,11 @@
*/
package org.springframework.data.history;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.util.Assert;
@@ -30,6 +31,8 @@ import org.springframework.util.Assert;
*/
public class Revisions<N extends Number & Comparable<N>, T> implements Iterable<Revision<N, T>> {
private final Comparator<Revision<N, T>> NATURAL_ORDER = Comparator.naturalOrder();
private final List<Revision<N, T>> revisions;
private final boolean latestLast;
@@ -39,7 +42,7 @@ public class Revisions<N extends Number & Comparable<N>, T> implements Iterable<
*
* @param revisions must not be {@literal null}.
*/
public Revisions(List<? extends Revision<N, T>> revisions) {
private Revisions(List<? extends Revision<N, T>> revisions) {
this(revisions, true);
}
@@ -52,14 +55,16 @@ public class Revisions<N extends Number & Comparable<N>, T> implements Iterable<
private Revisions(List<? extends Revision<N, T>> revisions, boolean latestLast) {
Assert.notNull(revisions, "Revisions must not be null!");
this.revisions = new ArrayList<Revision<N, T>>(revisions);
this.revisions = revisions.stream()//
.sorted(latestLast ? NATURAL_ORDER : NATURAL_ORDER.reversed())//
.collect(Collectors.toList());
this.latestLast = latestLast;
}
Collections.sort(this.revisions);
if (!latestLast) {
Collections.reverse(this.revisions);
}
public static <N extends Number & Comparable<N>, T> Revisions<N, T> of(List<? extends Revision<N, T>> revisions) {
return new Revisions<N, T>(revisions);
}
/**
@@ -78,9 +83,7 @@ public class Revisions<N extends Number & Comparable<N>, T> implements Iterable<
* @return
*/
public Revisions<N, T> reverse() {
List<Revision<N, T>> result = new ArrayList<Revision<N, T>>(revisions);
Collections.reverse(result);
return new Revisions<N, T>(result, !latestLast);
return new Revisions<N, T>(revisions, !latestLast);
}
/*

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mapping;
import java.util.Optional;
/**
* Interface for a component allowing the access of identifier values.
*
@@ -27,5 +29,5 @@ public interface IdentifierAccessor {
*
* @return
*/
Object getIdentifier();
Optional<? extends Object> getIdentifier();
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.mapping;
import java.lang.annotation.Annotation;
import java.util.Optional;
import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.util.TypeInformation;
@@ -40,11 +41,11 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> {
/**
* Returns the {@link PreferredConstructor} to be used to instantiate objects of this {@link PersistentEntity}.
*
* @return {@literal null} in case no suitable constructor for automatic construction can be found. This usually
* indicates that the instantiation of the object of that persistent entity is done through either a customer
* {@link EntityInstantiator} or handled by custom conversion mechanisms entirely.
* @return An empty {@link Optional} in case no suitable constructor for automatic construction can be found. This
* usually indicates that the instantiation of the object of that persistent entity is done through either a
* customer {@link EntityInstantiator} or handled by custom conversion mechanisms entirely.
*/
PreferredConstructor<T, P> getPersistenceConstructor();
Optional<PreferredConstructor<T, P>> getPersistenceConstructor();
/**
* Returns whether the given {@link PersistentProperty} is referred to by a constructor argument of the
@@ -78,7 +79,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> {
*
* @return the id property of the {@link PersistentEntity}.
*/
P getIdProperty();
Optional<P> getIdProperty();
/**
* Returns the version property of the {@link PersistentEntity}. Can be {@literal null} in case no version property is
@@ -86,7 +87,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> {
*
* @return the version property of the {@link PersistentEntity}.
*/
P getVersionProperty();
Optional<P> getVersionProperty();
/**
* Obtains a {@link PersistentProperty} instance by name.
@@ -94,7 +95,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> {
* @param name The name of the property
* @return the {@link PersistentProperty} or {@literal null} if it doesn't exist.
*/
P getPersistentProperty(String name);
Optional<P> getPersistentProperty(String name);
/**
* Returns the property equipped with an annotation of the given type.
@@ -103,7 +104,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> {
* @return
* @since 1.8
*/
P getPersistentProperty(Class<? extends Annotation> annotationType);
Optional<P> getPersistentProperty(Class<? extends Annotation> annotationType);
/**
* Returns whether the {@link PersistentEntity} has an id property. If this call returns {@literal true},
@@ -134,7 +135,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> {
*
* @return
*/
Object getTypeAlias();
Optional<? extends Object> getTypeAlias();
/**
* Returns the {@link TypeInformation} backing this {@link PersistentEntity}.
@@ -169,7 +170,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> {
* @return
* @since 1.8
*/
<A extends Annotation> A findAnnotation(Class<A> annotationType);
<A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType);
/**
* Returns a {@link PersistentPropertyAccessor} to access property values of the given bean.

View File

@@ -20,6 +20,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.util.TypeInformation;
@@ -74,19 +75,19 @@ public interface PersistentProperty<P extends PersistentProperty<P>> {
*
* @return the getter method to access the property value if available, otherwise {@literal null}.
*/
Method getGetter();
Optional<Method> getGetter();
/**
* Returns the setter method to set a property value. Might return {@literal null} in case there is no setter
* available.
*
* @returnthe setter method to set a property value if available, otherwise {@literal null}.
* @return the setter method to set a property value if available, otherwise {@literal null}.
*/
Method getSetter();
Optional<Method> getSetter();
Field getField();
Optional<Field> getField();
String getSpelExpression();
Optional<String> getSpelExpression();
Association<P> getAssociation();
@@ -201,7 +202,7 @@ public interface PersistentProperty<P extends PersistentProperty<P>> {
* @return the annotation of the given type if present or {@literal null} otherwise.
* @see AnnotationUtils#findAnnotation(Method, Class)
*/
<A extends Annotation> A findAnnotation(Class<A> annotationType);
<A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType);
/**
* Looks up the annotation of the given type on the property and the owning type if no annotation can be found on it.
@@ -210,7 +211,7 @@ public interface PersistentProperty<P extends PersistentProperty<P>> {
* @param annotationType must not be {@literal null}.
* @return
*/
<A extends Annotation> A findPropertyOrOwnerAnnotation(Class<A> annotationType);
<A extends Annotation> Optional<A> findPropertyOrOwnerAnnotation(Class<A> annotationType);
/**
* Returns whether the {@link PersistentProperty} has an annotation of the given type.

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mapping;
import java.util.Optional;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
/**
@@ -38,7 +40,7 @@ public interface PersistentPropertyAccessor {
* @throws org.springframework.data.mapping.model.MappingException in case an exception occurred when setting the
* property value.
*/
void setProperty(PersistentProperty<?> property, Object value);
void setProperty(PersistentProperty<?> property, Optional<? extends Object> value);
/**
* Returns the value of the given {@link PersistentProperty} of the underlying bean instance.
@@ -47,7 +49,7 @@ public interface PersistentPropertyAccessor {
* @param property must not be {@literal null}.
* @return can be {@literal null}.
*/
Object getProperty(PersistentProperty<?> property);
Optional<? extends Object> getProperty(PersistentProperty<?> property);
/**
* Returns the underlying bean.

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.mapping;
import static org.springframework.util.ObjectUtils.*;
import lombok.EqualsAndHashCode;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
@@ -23,11 +23,14 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.Streamable;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -56,6 +59,7 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
* @param constructor must not be {@literal null}.
* @param parameters must not be {@literal null}.
*/
@SafeVarargs
public PreferredConstructor(Constructor<T> constructor, Parameter<Object, P>... parameters) {
Assert.notNull(constructor, "Constructor must not be null!");
@@ -80,8 +84,8 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
*
* @return
*/
public Iterable<Parameter<Object, P>> getParameters() {
return parameters;
public Streamable<Parameter<Object, P>> getParameters() {
return Streamable.of(parameters);
}
/**
@@ -181,15 +185,16 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
* @param <T> the type of the parameter
* @author Oliver Gierke
*/
@EqualsAndHashCode(exclude = { "enclosingClassCache", "hasSpelExpression" })
public static class Parameter<T, P extends PersistentProperty<P>> {
private final String name;
private final Optional<String> name;
private final TypeInformation<T> type;
private final String key;
private final PersistentEntity<T, P> entity;
private final Optional<String> key;
private final Optional<PersistentEntity<T, P>> entity;
private Boolean enclosingClassCache;
private Boolean hasSpelExpression;
private final Lazy<Boolean> enclosingClassCache;
private final Lazy<Boolean> hasSpelExpression;
/**
* Creates a new {@link Parameter} with the given name, {@link TypeInformation} as well as an array of
@@ -199,9 +204,10 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
* @param name the name of the parameter, can be {@literal null}
* @param type must not be {@literal null}
* @param annotations must not be {@literal null} but can be empty
* @param entity can be {@literal null}.
* @param entity must not be {@literal null}.
*/
public Parameter(String name, TypeInformation<T> type, Annotation[] annotations, PersistentEntity<T, P> entity) {
public Parameter(Optional<String> name, TypeInformation<T> type, Annotation[] annotations,
Optional<PersistentEntity<T, P>> entity) {
Assert.notNull(type, "Type must not be null!");
Assert.notNull(annotations, "Annotations must not be null!");
@@ -210,23 +216,27 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
this.type = type;
this.key = getValue(annotations);
this.entity = entity;
this.enclosingClassCache = Lazy.of(() -> {
Class<T> owningType = entity.orElseThrow(() -> new IllegalStateException()).getType();
return owningType.isMemberClass() && type.getType().equals(owningType.getEnclosingClass());
});
this.hasSpelExpression = Lazy.of(() -> getSpelExpression().map(StringUtils::hasText).orElse(false));
}
private String getValue(Annotation[] annotations) {
for (Annotation anno : annotations) {
if (anno.annotationType() == Value.class) {
return ((Value) anno).value();
}
}
return null;
private static Optional<String> getValue(Annotation[] annotations) {
return Arrays.stream(annotations).//
filter(it -> it.annotationType() == Value.class).//
findFirst().map(it -> ((Value) it).value());
}
/**
* Returns the name of the parameter or {@literal null} if none was given.
* Returns the name of the parameter.
*
* @return
*/
public String getName() {
public Optional<String> getName() {
return name;
}
@@ -253,7 +263,7 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
*
* @return
*/
public String getSpelExpression() {
public Optional<String> getSpelExpression() {
return key;
}
@@ -263,12 +273,7 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
* @return
*/
public boolean hasSpelExpression() {
if (this.hasSpelExpression == null) {
this.hasSpelExpression = StringUtils.hasText(getSpelExpression());
}
return this.hasSpelExpression;
return hasSpelExpression.get();
}
/**
@@ -279,60 +284,17 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
*/
boolean maps(PersistentProperty<?> property) {
P referencedProperty = entity == null ? null : entity.getPersistentProperty(name);
return property == null ? false : property.equals(referencedProperty);
}
private boolean isEnclosingClassParameter() {
if (enclosingClassCache == null) {
Class<T> owningType = entity.getType();
this.enclosingClassCache = owningType.isMemberClass() && type.getType().equals(owningType.getEnclosingClass());
}
return enclosingClassCache;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Parameter)) {
if (!name.isPresent()) {
return false;
}
Parameter<?, ?> that = (Parameter<?, ?>) obj;
boolean nameEquals = this.name == null ? that.name == null : this.name.equals(that.name);
boolean keyEquals = this.key == null ? that.key == null : this.key.equals(that.key);
boolean entityEquals = this.entity == null ? that.entity == null : this.entity.equals(that.entity);
// Explicitly delay equals check on type as it might be expensive
return nameEquals && keyEquals && entityEquals && this.type.equals(that.type);
return entity//
.flatMap(it -> it.getPersistentProperty(name.get()))//
.map(it -> property.equals(it)).orElse(false);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * nullSafeHashCode(this.name);
result += 31 * nullSafeHashCode(this.key);
result += 31 * nullSafeHashCode(this.entity);
result += 31 * this.type.hashCode();
return result;
private boolean isEnclosingClassParameter() {
return enclosingClassCache.get();
}
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mapping;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -24,9 +26,9 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Streamable;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -34,7 +36,8 @@ import org.springframework.util.StringUtils;
*
* @author Oliver Gierke
*/
public class PropertyPath implements Iterable<PropertyPath> {
@EqualsAndHashCode
public class PropertyPath implements Streamable<PropertyPath> {
private static final String DELIMITERS = "_\\.";
private static final String ALL_UPPERCASE = "[A-Z0-9._$]+";
@@ -170,43 +173,6 @@ public class PropertyPath implements Iterable<PropertyPath> {
return isCollection;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
PropertyPath that = (PropertyPath) obj;
return this.name.equals(that.name) && this.type.equals(that.type)
&& ObjectUtils.nullSafeEquals(this.next, that.next);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * name.hashCode();
result += 31 * type.hashCode();
result += 31 * (next == null ? 0 : next.hashCode());
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()

View File

@@ -25,6 +25,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -43,6 +44,7 @@ import org.springframework.data.mapping.model.MutablePersistentEntity;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Pair;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -251,27 +253,34 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
while (iterator.hasNext()) {
String segment = iterator.next();
P persistentProperty = current.getPersistentProperty(segment);
final DefaultPersistentPropertyPath<P> foo = path;
final E bar = current;
if (persistentProperty == null) {
Pair<DefaultPersistentPropertyPath<P>, E> pair = getPair(path, iterator, segment, current).orElseThrow(() -> {
String source = StringUtils.collectionToDelimitedString(parts, ".");
String resolvedPath = path.toDotPath();
String resolvedPath = foo.toDotPath();
throw new InvalidPersistentPropertyPath(source, type, segment, resolvedPath,
String.format("No property %s found on %s!", segment, current.getName()));
}
return new InvalidPersistentPropertyPath(source, type, segment, resolvedPath,
String.format("No property %s found on %s!", segment, bar.getName()));
});
path = path.append(persistentProperty);
if (iterator.hasNext()) {
current = getPersistentEntity(persistentProperty.getTypeInformation().getActualType());
}
path = pair.getFirst();
current = pair.getSecond();
}
return path;
}
private Optional<Pair<DefaultPersistentPropertyPath<P>, E>> getPair(DefaultPersistentPropertyPath<P> path,
Iterator<String> iterator, String segment, E entity) {
Optional<P> persistentProperty = entity.getPersistentProperty(segment);
return persistentProperty.map(it -> Pair.of(path.append(it),
iterator.hasNext() ? getPersistentEntity(it.getTypeInformation().getActualType()) : entity));
}
/**
* Adds the given type to the {@link MappingContext}.
*
@@ -389,7 +398,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
* @param simpleTypeHolder
* @return
*/
protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner,
protected abstract P createPersistentProperty(Optional<Field> field, PropertyDescriptor descriptor, E owner,
SimpleTypeHolder simpleTypeHolder);
/*
@@ -462,7 +471,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
String fieldName = field.getName();
ReflectionUtils.makeAccessible(field);
createAndRegisterProperty(field, descriptors.get(fieldName));
createAndRegisterProperty(Optional.of(field), descriptors.get(fieldName));
this.remainingDescriptors.remove(fieldName);
}
@@ -477,12 +486,12 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
for (PropertyDescriptor descriptor : remainingDescriptors.values()) {
if (PersistentPropertyFilter.INSTANCE.matches(descriptor)) {
createAndRegisterProperty(null, descriptor);
createAndRegisterProperty(Optional.empty(), descriptor);
}
}
}
private void createAndRegisterProperty(Field field, PropertyDescriptor descriptor) {
private void createAndRegisterProperty(Optional<Field> field, PropertyDescriptor descriptor) {
P property = createPersistentProperty(field, descriptor, entity, simpleTypeHolder);
@@ -511,7 +520,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
}
/**
* Filter rejecting static fields as well as artifically introduced ones. See
* Filter rejecting static fields as well as artificially introduced ones. See
* {@link PersistentPropertyFilter#UNMAPPED_PROPERTIES} for details.
*
* @author Oliver Gierke

View File

@@ -72,7 +72,6 @@ class DefaultPersistentPropertyPath<T extends PersistentProperty<T>> implements
* @return a new {@link DefaultPersistentPropertyPath} with the given property appended to the current one.
* @throws IllegalArgumentException in case the property is not a property of the type of the current leaf property.
*/
@SuppressWarnings("unchecked")
public DefaultPersistentPropertyPath<T> append(T property) {
Assert.notNull(property, "Property must not be null!");

View File

@@ -16,6 +16,7 @@
package org.springframework.data.mapping.context;
import java.util.Arrays;
import java.util.Optional;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
@@ -45,7 +46,6 @@ public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupp
* @deprecated use {@link MappingContextIsNewStrategyFactory(PersistentEntities)} instead.
*/
@Deprecated
@SuppressWarnings("unchecked")
public MappingContextIsNewStrategyFactory(MappingContext<? extends PersistentEntity<?, ?>, ?> context) {
this(new PersistentEntities(Arrays.asList(context)));
}
@@ -76,9 +76,9 @@ public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupp
}
if (entity.hasVersionProperty()) {
return new PropertyIsNullOrZeroNumberIsNewStrategy(entity.getVersionProperty());
return new PropertyIsNullOrZeroNumberIsNewStrategy(entity.getVersionProperty().get());
} else if (entity.hasIdProperty()) {
return new PropertyIsNullIsNewStrategy(entity.getIdProperty());
return new PropertyIsNullIsNewStrategy(entity.getIdProperty().get());
} else {
throw new MappingException(String.format("Cannot determine IsNewStrategy for type %s!", type));
}
@@ -104,16 +104,21 @@ public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupp
this.property = property;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.IsNewStrategy#isNew(java.lang.Object)
* @see org.springframework.data.support.IsNewStrategy#isNew(java.util.Optional)
*/
public boolean isNew(Object entity) {
@Override
public boolean isNew(Optional<? extends Object> entity) {
PersistentPropertyAccessor accessor = property.getOwner().getPropertyAccessor(entity);
Object propertyValue = accessor.getProperty(property);
return entity.map(it -> {
return decideIsNew(propertyValue);
PersistentPropertyAccessor accessor = property.getOwner().getPropertyAccessor(it);
Object propertyValue = accessor.getProperty(property);
return decideIsNew(propertyValue);
}).orElse(false);
}
protected abstract boolean decideIsNew(Object property);

View File

@@ -20,8 +20,8 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.annotation.Reference;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
@@ -46,25 +46,25 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
}
protected final String name;
protected final PropertyDescriptor propertyDescriptor;
protected final Optional<PropertyDescriptor> propertyDescriptor;
protected final TypeInformation<?> information;
protected final Class<?> rawType;
protected final Field field;
protected final Optional<Field> field;
protected final Association<P> association;
protected final PersistentEntity<?, P> owner;
private final SimpleTypeHolder simpleTypeHolder;
private final int hashCode;
public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity<?, P> owner,
SimpleTypeHolder simpleTypeHolder) {
public AbstractPersistentProperty(Optional<Field> field, PropertyDescriptor propertyDescriptor,
PersistentEntity<?, P> owner, SimpleTypeHolder simpleTypeHolder) {
Assert.notNull(simpleTypeHolder, "SimpleTypeHolder must not be null!");
Assert.notNull(owner, "Owner entity must not be null!");
this.name = field == null ? propertyDescriptor.getName() : field.getName();
this.rawType = field == null ? propertyDescriptor.getPropertyType() : field.getType();
this.name = field.map(Field::getName).orElseGet(() -> propertyDescriptor.getName());
this.rawType = field.<Class<?>> map(Field::getType).orElseGet(() -> propertyDescriptor.getPropertyType());
this.information = owner.getTypeInformation().getProperty(this.name);
this.propertyDescriptor = propertyDescriptor;
this.propertyDescriptor = Optional.ofNullable(propertyDescriptor);
this.field = field;
this.association = isAssociation() ? createAssociation() : null;
this.owner = owner;
@@ -150,19 +150,9 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
* @see org.springframework.data.mapping.PersistentProperty#getGetter()
*/
@Override
public Method getGetter() {
if (propertyDescriptor == null) {
return null;
}
Method getter = propertyDescriptor.getReadMethod();
if (getter == null) {
return null;
}
return rawType.isAssignableFrom(getter.getReturnType()) ? getter : null;
public Optional<Method> getGetter() {
return propertyDescriptor.map(it -> it.getReadMethod())//
.filter(it -> rawType.isAssignableFrom(it.getReturnType()));
}
/*
@@ -170,19 +160,9 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
* @see org.springframework.data.mapping.PersistentProperty#getSetter()
*/
@Override
public Method getSetter() {
if (propertyDescriptor == null) {
return null;
}
Method setter = propertyDescriptor.getWriteMethod();
if (setter == null) {
return null;
}
return setter.getParameterTypes()[0].isAssignableFrom(rawType) ? setter : null;
public Optional<Method> getSetter() {
return propertyDescriptor.map(it -> it.getWriteMethod())//
.filter(it -> it.getParameterTypes()[0].isAssignableFrom(rawType));
}
/*
@@ -190,7 +170,7 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
* @see org.springframework.data.mapping.PersistentProperty#getField()
*/
@Override
public Field getField() {
public Optional<Field> getField() {
return field;
}
@@ -199,8 +179,8 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
* @see org.springframework.data.mapping.PersistentProperty#getSpelExpression()
*/
@Override
public String getSpelExpression() {
return null;
public Optional<String> getSpelExpression() {
return Optional.empty();
}
/*
@@ -227,7 +207,7 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
*/
@Override
public boolean isAssociation() {
return field == null ? false : AnnotationUtils.getAnnotation(field, Reference.class) != null;
return isAnnotationPresent(Reference.class);
}
/*
@@ -351,6 +331,8 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
*/
@Override
public String toString() {
return this.field == null ? this.propertyDescriptor.toString() : this.field.toString();
return field.map(Object::toString)//
.orElseGet(() -> propertyDescriptor.map(Object::toString)//
.orElseThrow(() -> new IllegalStateException("Either Field or PropertyDescriptor has to be present!")));
}
}

View File

@@ -17,11 +17,14 @@ package org.springframework.data.mapping.model;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -36,6 +39,7 @@ import org.springframework.data.annotation.Version;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.Optionals;
import org.springframework.util.Assert;
/**
@@ -44,13 +48,13 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Christoph Strobl
*/
public abstract class AnnotationBasedPersistentProperty<P extends PersistentProperty<P>> extends
AbstractPersistentProperty<P> {
public abstract class AnnotationBasedPersistentProperty<P extends PersistentProperty<P>>
extends AbstractPersistentProperty<P> {
private static final String SPRING_DATA_PACKAGE = "org.springframework.data";
private final Value value;
private final Map<Class<? extends Annotation>, Annotation> annotationCache = new HashMap<Class<? extends Annotation>, Annotation>();
private final Optional<Value> value;
private final Map<Class<? extends Annotation>, Optional<? extends Annotation>> annotationCache = new HashMap<>();
private Boolean isTransient;
private boolean usePropertyAccess;
@@ -62,15 +66,15 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
* @param propertyDescriptor can be {@literal null}.
* @param owner must not be {@literal null}.
*/
public AnnotationBasedPersistentProperty(Field field, PropertyDescriptor propertyDescriptor,
public AnnotationBasedPersistentProperty(Optional<Field> field, PropertyDescriptor propertyDescriptor,
PersistentEntity<?, P> owner, SimpleTypeHolder simpleTypeHolder) {
super(field, propertyDescriptor, owner, simpleTypeHolder);
populateAnnotationCache(field);
AccessType accessType = findPropertyOrOwnerAnnotation(AccessType.class);
this.usePropertyAccess = accessType == null ? false : Type.PROPERTY.equals(accessType.value());
this.usePropertyAccess = findPropertyOrOwnerAnnotation(AccessType.class).map(it -> Type.PROPERTY.equals(it.value()))
.orElse(false);
this.value = findAnnotation(Value.class);
}
@@ -81,40 +85,36 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
* @param field
* @throws MappingException in case we find an ambiguous mapping on the accessor methods
*/
private final void populateAnnotationCache(Field field) {
private final void populateAnnotationCache(Optional<Field> field) {
for (Method method : Arrays.asList(getGetter(), getSetter())) {
Optionals.toStream(getGetter(), getSetter()).forEach(it -> {
if (method == null) {
continue;
}
for (Annotation annotation : method.getAnnotations()) {
for (Annotation annotation : it.getAnnotations()) {
Class<? extends Annotation> annotationType = annotation.annotationType();
validateAnnotation(annotation, "Ambiguous mapping! Annotation %s configured "
+ "multiple times on accessor methods of property %s in class %s!", annotationType.getSimpleName(),
getName(), getOwner().getType().getSimpleName());
validateAnnotation(annotation,
"Ambiguous mapping! Annotation %s configured "
+ "multiple times on accessor methods of property %s in class %s!",
annotationType.getSimpleName(), getName(), getOwner().getType().getSimpleName());
annotationCache.put(annotationType, annotation);
annotationCache.put(annotationType, Optional.of(annotation));
}
}
});
if (field == null) {
return;
}
field.ifPresent(it -> {
for (Annotation annotation : field.getAnnotations()) {
for (Annotation annotation : it.getAnnotations()) {
Class<? extends Annotation> annotationType = annotation.annotationType();
Class<? extends Annotation> annotationType = annotation.annotationType();
validateAnnotation(annotation, "Ambiguous mapping! Annotation %s configured "
+ "on field %s and one of its accessor methods in class %s!", annotationType.getSimpleName(),
field.getName(), getOwner().getType().getSimpleName());
validateAnnotation(annotation,
"Ambiguous mapping! Annotation %s configured " + "on field %s and one of its accessor methods in class %s!",
annotationType.getSimpleName(), it.getName(), getOwner().getType().getSimpleName());
annotationCache.put(annotationType, annotation);
}
annotationCache.put(annotationType, Optional.of(annotation));
}
});
}
/**
@@ -133,7 +133,8 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
return;
}
if (annotationCache.containsKey(annotationType) && !annotationCache.get(annotationType).equals(candidate)) {
if (annotationCache.containsKey(annotationType)
&& !annotationCache.get(annotationType).equals(Optional.of(candidate))) {
throw new MappingException(String.format(message, arguments));
}
}
@@ -145,8 +146,8 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#getSpelExpression()
*/
@Override
public String getSpelExpression() {
return value == null ? null : value.value();
public Optional<String> getSpelExpression() {
return value.map(Value::value);
}
/**
@@ -209,29 +210,18 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
* @return
*/
@SuppressWarnings("unchecked")
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
public <A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType) {
Assert.notNull(annotationType, "Annotation type must not be null!");
if (annotationCache != null && annotationCache.containsKey(annotationType)) {
return (A) annotationCache.get(annotationType);
return (Optional<A>) annotationCache.get(annotationType);
}
for (Method method : Arrays.asList(getGetter(), getSetter())) {
if (method == null) {
continue;
}
A annotation = AnnotatedElementUtils.findMergedAnnotation(method, annotationType);
if (annotation != null) {
return cacheAndReturn(annotationType, annotation);
}
}
return cacheAndReturn(annotationType,
field == null ? null : AnnotatedElementUtils.findMergedAnnotation(field, annotationType));
return cacheAndReturn(annotationType, getAccessors()//
.map(it -> it.map(inner -> AnnotatedElementUtils.findMergedAnnotation(inner, annotationType)))//
.flatMap(it -> Optionals.toStream(it))//
.findFirst());
}
/*
@@ -239,10 +229,10 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
* @see org.springframework.data.mapping.PersistentProperty#findPropertyOrOwnerAnnotation(java.lang.Class)
*/
@Override
public <A extends Annotation> A findPropertyOrOwnerAnnotation(Class<A> annotationType) {
public <A extends Annotation> Optional<A> findPropertyOrOwnerAnnotation(Class<A> annotationType) {
A annotation = findAnnotation(annotationType);
return annotation == null ? owner.findAnnotation(annotationType) : annotation;
Optional<A> annotation = findAnnotation(annotationType);
return annotation.isPresent() ? annotation : owner.findAnnotation(annotationType);
}
/**
@@ -251,7 +241,7 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
* @param annotation
* @return
*/
private <A extends Annotation> A cacheAndReturn(Class<? extends A> type, A annotation) {
private <A extends Annotation> Optional<A> cacheAndReturn(Class<? extends A> type, Optional<A> annotation) {
if (annotationCache != null) {
annotationCache.put(type, annotation);
@@ -267,7 +257,7 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
* @return
*/
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return findAnnotation(annotationType) != null;
return findAnnotation(annotationType).isPresent();
}
/*
@@ -290,14 +280,15 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
populateAnnotationCache(field);
}
StringBuilder builder = new StringBuilder();
for (Annotation annotation : annotationCache.values()) {
if (annotation != null) {
builder.append(annotation.toString()).append(" ");
}
}
StringBuilder builder = new StringBuilder().append(annotationCache.values().stream()//
.flatMap(it -> Optionals.toStream(it))//
.map(Object::toString)//
.collect(Collectors.joining(" ")));
return builder.toString() + super.toString();
}
private Stream<Optional<? extends AnnotatedElement>> getAccessors() {
return Arrays.<Optional<? extends AnnotatedElement>> asList(getGetter(), getSetter(), getField()).stream();
}
}

View File

@@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
@@ -44,6 +45,7 @@ import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.SimpleAssociationHandler;
import org.springframework.data.mapping.SimplePropertyHandler;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -64,26 +66,28 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
private static final String TYPE_MISMATCH = "Target bean of type %s is not of type of the persistent entity (%s)!";
private static final String NULL_ASSOCIATION = "%s.addAssociation(…) was called with a null association! Usually indicates a problem in a Spring Data MappingContext implementation. Be sure to file a bug at https://jira.spring.io!";
private final PreferredConstructor<T, P> constructor;
private final Optional<PreferredConstructor<T, P>> constructor;
private final TypeInformation<T> information;
private final List<P> properties;
private final Comparator<P> comparator;
private final Optional<Comparator<P>> comparator;
private final Set<Association<P>> associations;
private final Map<String, P> propertyCache;
private final Map<Class<? extends Annotation>, Annotation> annotationCache;
private final Map<Class<? extends Annotation>, Optional<Annotation>> annotationCache;
private P idProperty;
private P versionProperty;
private Optional<P> idProperty = Optional.empty();
private Optional<P> versionProperty = Optional.empty();
private PersistentPropertyAccessorFactory propertyAccessorFactory;
private final Lazy<Optional<? extends Object>> typeAlias;
/**
* Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}.
*
* @param information must not be {@literal null}.
*/
public BasicPersistentEntity(TypeInformation<T> information) {
this(information, null);
this(information, Optional.empty());
}
/**
@@ -94,27 +98,31 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* @param information must not be {@literal null}.
* @param comparator can be {@literal null}.
*/
public BasicPersistentEntity(TypeInformation<T> information, Comparator<P> comparator) {
public BasicPersistentEntity(TypeInformation<T> information, Optional<Comparator<P>> comparator) {
Assert.notNull(information, "Information must not be null!");
this.information = information;
this.properties = new ArrayList<P>();
this.properties = new ArrayList<>();
this.comparator = comparator;
this.constructor = new PreferredConstructorDiscoverer<T, P>(information, this).getConstructor();
this.associations = comparator == null ? new HashSet<Association<P>>()
: new TreeSet<Association<P>>(new AssociationComparator<P>(comparator));
this.constructor = new PreferredConstructorDiscoverer<>(this).getConstructor();
this.associations = comparator.<Set<Association<P>>> map(it -> new TreeSet<>(new AssociationComparator<>(it)))
.orElse(new HashSet<>());
this.propertyCache = new HashMap<String, P>();
this.annotationCache = new HashMap<Class<? extends Annotation>, Annotation>();
this.propertyCache = new HashMap<>();
this.annotationCache = new HashMap<>();
this.propertyAccessorFactory = BeanWrapperPropertyAccessorFactory.INSTANCE;
this.typeAlias = Lazy
.of(() -> Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(getType(), TypeAlias.class))//
.map(TypeAlias::value).filter(it -> StringUtils.hasText(it)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPersistenceConstructor()
*/
public PreferredConstructor<T, P> getPersistenceConstructor() {
public Optional<PreferredConstructor<T, P>> getPersistenceConstructor() {
return constructor;
}
@@ -123,7 +131,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* @see org.springframework.data.mapping.PersistentEntity#isConstructorArgument(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isConstructorArgument(PersistentProperty<?> property) {
return constructor == null ? false : constructor.isConstructorParameter(property);
return constructor.map(it -> it.isConstructorParameter(property)).orElse(false);
}
/*
@@ -131,7 +139,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* @see org.springframework.data.mapping.PersistentEntity#isIdProperty(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isIdProperty(PersistentProperty<?> property) {
return this.idProperty == null ? false : this.idProperty.equals(property);
return this.idProperty.map(it -> it.equals(property)).orElse(false);
}
/*
@@ -139,7 +147,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* @see org.springframework.data.mapping.PersistentEntity#isVersionProperty(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isVersionProperty(PersistentProperty<?> property) {
return this.versionProperty == null ? false : this.versionProperty.equals(property);
return this.versionProperty.map(it -> it.equals(property)).orElse(false);
}
/*
@@ -154,7 +162,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getIdProperty()
*/
public P getIdProperty() {
public Optional<P> getIdProperty() {
return idProperty;
}
@@ -162,7 +170,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getVersionProperty()
*/
public P getVersionProperty() {
public Optional<P> getVersionProperty() {
return versionProperty;
}
@@ -171,7 +179,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* @see org.springframework.data.mapping.PersistentEntity#hasIdProperty()
*/
public boolean hasIdProperty() {
return idProperty != null;
return idProperty.isPresent();
}
/*
@@ -179,7 +187,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* @see org.springframework.data.mapping.PersistentEntity#hasVersionProperty()
*/
public boolean hasVersionProperty() {
return versionProperty != null;
return versionProperty.isPresent();
}
/*
@@ -203,20 +211,19 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
P candidate = returnPropertyIfBetterIdPropertyCandidateOrNull(property);
if (candidate != null) {
this.idProperty = candidate;
this.idProperty = Optional.of(candidate);
}
if (property.isVersionProperty()) {
if (this.versionProperty != null) {
throw new MappingException(
String.format(
"Attempt to add version property %s but already have property %s registered "
+ "as version. Check your mapping configuration!",
property.getField(), versionProperty.getField()));
}
this.versionProperty.ifPresent(it -> {
this.versionProperty = property;
throw new MappingException(
String.format("Attempt to add version property %s but already have property %s registered "
+ "as version. Check your mapping configuration!", property.getField(), it.getField()));
});
this.versionProperty = Optional.of(property);
}
}
@@ -232,10 +239,10 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
return null;
}
if (this.idProperty != null) {
this.idProperty.ifPresent(it -> {
throw new MappingException(String.format("Attempt to add id property %s but already have property %s registered "
+ "as id. Check your mapping configuration!", property.getField(), idProperty.getField()));
}
+ "as id. Check your mapping configuration!", property.getField(), it.getField()));
});
return property;
}
@@ -260,8 +267,8 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPersistentProperty(java.lang.String)
*/
public P getPersistentProperty(String name) {
return propertyCache.get(name);
public Optional<P> getPersistentProperty(String name) {
return Optional.ofNullable(propertyCache.get(name));
}
/*
@@ -269,26 +276,20 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* @see org.springframework.data.mapping.PersistentEntity#getPersistentProperty(java.lang.Class)
*/
@Override
public P getPersistentProperty(Class<? extends Annotation> annotationType) {
public Optional<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;
}
Optional<P> property = properties.stream()//
.filter(it -> it.isAnnotationPresent(annotationType))//
.findAny();
if (property.isPresent()) {
return property;
}
for (Association<P> association : associations) {
P property = association.getInverse();
if (property.isAnnotationPresent(annotationType)) {
return property;
}
}
return null;
return associations.stream().map(Association::getInverse)//
.filter(it -> it.isAnnotationPresent(annotationType)).findAny();
}
/*
@@ -303,10 +304,8 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getTypeAlias()
*/
public Object getTypeAlias() {
TypeAlias alias = AnnotatedElementUtils.findMergedAnnotation(getType(), TypeAlias.class);
return alias == null ? null : StringUtils.hasText(alias.value()) ? alias.value() : null;
public Optional<? extends Object> getTypeAlias() {
return typeAlias.get();
}
/*
@@ -325,11 +324,9 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
Assert.notNull(handler, "Handler must not be null!");
for (P property : properties) {
if (!property.isTransient() && !property.isAssociation()) {
handler.doWithPersistentProperty(property);
}
}
properties.stream()//
.filter(it -> !it.isTransient() && !it.isAssociation())//
.forEach(it -> handler.doWithPersistentProperty(it));
}
/*
@@ -341,11 +338,9 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
Assert.notNull(handler, "Handler must not be null!");
for (PersistentProperty<?> property : properties) {
if (!property.isTransient() && !property.isAssociation()) {
handler.doWithPersistentProperty(property);
}
}
properties.stream()//
.filter(it -> !it.isTransient() && !it.isAssociation())//
.forEach(it -> handler.doWithPersistentProperty(it));
}
/*
@@ -380,16 +375,10 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
*/
@Override
@SuppressWarnings("unchecked")
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
public <A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType) {
if (annotationCache.containsKey(annotationType)) {
return (A) annotationCache.get(annotationType);
}
A annotation = AnnotatedElementUtils.findMergedAnnotation(getType(), annotationType);
annotationCache.put(annotationType, annotation);
return annotation;
return (Optional<A>) annotationCache.computeIfAbsent(annotationType,
it -> Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(getType(), it)));
}
/*
@@ -397,10 +386,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* @see org.springframework.data.mapping.MutablePersistentEntity#verify()
*/
public void verify() {
if (comparator != null) {
Collections.sort(properties, comparator);
}
comparator.ifPresent(it -> Collections.sort(properties, it));
}
/*
@@ -455,8 +441,8 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
* @see org.springframework.data.mapping.IdentifierAccessor#getIdentifier()
*/
@Override
public Object getIdentifier() {
return null;
public Optional<? extends Object> getIdentifier() {
return Optional.empty();
}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.mapping.model;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Optional;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
@@ -45,9 +46,9 @@ class BeanWrapper<T> implements PersistentPropertyAccessor {
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentPropertyAccessor#setProperty(org.springframework.data.mapping.PersistentProperty, java.lang.Object)
* @see org.springframework.data.mapping.PersistentPropertyAccessor#setProperty(org.springframework.data.mapping.PersistentProperty, java.util.Optional)
*/
public void setProperty(PersistentProperty<?> property, Object value) {
public void setProperty(PersistentProperty<?> property, Optional<? extends Object> value) {
Assert.notNull(property, "PersistentProperty must not be null!");
@@ -55,18 +56,20 @@ class BeanWrapper<T> implements PersistentPropertyAccessor {
if (!property.usePropertyAccess()) {
ReflectionUtils.makeAccessible(property.getField());
ReflectionUtils.setField(property.getField(), bean, value);
Field field = property.getField().get();
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, bean, value.orElse(null));
return;
}
Method setter = property.getSetter();
Optional<Method> setter = property.getSetter();
if (property.usePropertyAccess() && setter != null) {
setter.ifPresent(it -> {
ReflectionUtils.makeAccessible(setter);
ReflectionUtils.invokeMethod(setter, bean, value);
}
ReflectionUtils.makeAccessible(it);
ReflectionUtils.invokeMethod(it, bean, value.orElse(null));
});
} catch (IllegalStateException e) {
throw new MappingException("Could not set object property!", e);
@@ -77,7 +80,7 @@ class BeanWrapper<T> implements PersistentPropertyAccessor {
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentPropertyAccessor#getProperty(org.springframework.data.mapping.PersistentProperty)
*/
public Object getProperty(PersistentProperty<?> property) {
public Optional<? extends Object> getProperty(PersistentProperty<?> property) {
return getProperty(property, property.getType());
}
@@ -91,7 +94,7 @@ class BeanWrapper<T> implements PersistentPropertyAccessor {
* @throws MappingException in case an exception occured when accessing the property.
*/
@SuppressWarnings("unchecked")
public <S> S getProperty(PersistentProperty<?> property, Class<? extends S> type) {
public <S> Optional<S> getProperty(PersistentProperty<?> property, Class<? extends S> type) {
Assert.notNull(property, "PersistentProperty must not be null!");
@@ -99,20 +102,18 @@ class BeanWrapper<T> implements PersistentPropertyAccessor {
if (!property.usePropertyAccess()) {
Field field = property.getField();
Field field = property.getField().get();
ReflectionUtils.makeAccessible(field);
return (S) ReflectionUtils.getField(field, bean);
return Optional.ofNullable((S) ReflectionUtils.getField(field, bean));
}
Method getter = property.getGetter();
Optional<Method> getter = property.getGetter();
if (property.usePropertyAccess() && getter != null) {
return getter.map(it -> {
ReflectionUtils.makeAccessible(getter);
return (S) ReflectionUtils.invokeMethod(getter, bean);
}
return null;
ReflectionUtils.makeAccessible(it);
return (S) ReflectionUtils.invokeMethod(it, bean);
});
} catch (IllegalStateException e) {
throw new MappingException(

View File

@@ -32,8 +32,10 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.springframework.asm.ClassWriter;
import org.springframework.asm.Label;
@@ -46,6 +48,7 @@ import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.SimpleAssociationHandler;
import org.springframework.data.mapping.SimplePropertyHandler;
import org.springframework.data.util.Optionals;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -153,7 +156,7 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
propertyAccessorClass = createAccessorClass(entity);
map = new HashMap<TypeInformation<?>, Class<PersistentPropertyAccessor>>(map);
map = new HashMap<>(map);
map.put(entity.getTypeInformation(), propertyAccessorClass);
this.propertyAccessorClasses = map;
@@ -256,6 +259,7 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
private static final String JAVA_LANG_REFLECT_METHOD = "java/lang/reflect/Method";
private static final String JAVA_LANG_INVOKE_METHOD_HANDLE = "java/lang/invoke/MethodHandle";
private static final String JAVA_LANG_CLASS = "java/lang/Class";
private static final String JAVA_UTIL_OPTIONAL = "java/util/Optional";
private static final String BEAN_FIELD = "bean";
private static final String THIS_REF = "this";
private static final String PERSISTENT_PROPERTY = "org/springframework/data/mapping/PersistentProperty";
@@ -369,30 +373,23 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
for (PersistentProperty<?> property : persistentProperties) {
Method setter = property.getSetter();
if (setter != null && generateMethodHandle(entity, setter)) {
property.getSetter().filter(it -> generateMethodHandle(entity, it)).ifPresent(it -> {
cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, setterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE), null, null).visitEnd();
}
Method getter = property.getGetter();
if (getter != null && generateMethodHandle(entity, getter)) {
});
property.getGetter().filter(it -> generateMethodHandle(entity, it)).ifPresent(it -> {
cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, getterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE), null, null).visitEnd();
}
});
Field field = property.getField();
if (field != null && generateSetterMethodHandle(entity, field)) {
property.getField().filter(it -> generateSetterMethodHandle(entity, it)).ifPresent(it -> {
cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, fieldSetterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE), null, null).visitEnd();
cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, fieldGetterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE), null, null).visitEnd();
}
});
}
}
@@ -513,18 +510,18 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
if (property.usePropertyAccess()) {
if (property.getGetter() != null && generateMethodHandle(entity, property.getGetter())) {
property.getGetter().filter(it -> generateMethodHandle(entity, it)).ifPresent(it -> {
visitPropertyGetterInitializer(property, mv, entityClasses, internalClassName);
}
});
if (property.getSetter() != null && generateMethodHandle(entity, property.getSetter())) {
property.getSetter().filter(it -> generateMethodHandle(entity, it)).ifPresent(it -> {
visitPropertySetterInitializer(property, mv, entityClasses, internalClassName);
}
});
}
if (property.getField() != null && generateSetterMethodHandle(entity, property.getField())) {
property.getField().filter(it -> generateSetterMethodHandle(entity, it)).ifPresent(it -> {
visitFieldGetterSetterInitializer(property, mv, entityClasses, internalClassName);
}
});
}
mv.visitLabel(l1);
@@ -554,24 +551,13 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
*/
private static List<Class<?>> getPropertyDeclaratingClasses(List<PersistentProperty<?>> persistentProperties) {
Set<Class<?>> entityClassesSet = new HashSet<Class<?>>();
return persistentProperties.stream().flatMap(property -> {
return Optionals.toStream(property.getField(), property.getGetter(), property.getSetter())
for (PersistentProperty<?> property : persistentProperties) {
.map(it -> it.getDeclaringClass());
if (property.getField() != null) {
entityClassesSet.add(property.getField().getDeclaringClass());
}
}).collect(Collectors.collectingAndThen(Collectors.toSet(), it -> new ArrayList<>(it)));
if (property.getGetter() != null) {
entityClassesSet.add(property.getGetter().getDeclaringClass());
}
if (property.getSetter() != null) {
entityClassesSet.add(property.getSetter().getDeclaringClass());
}
}
return new ArrayList<Class<?>>(entityClassesSet);
}
/**
@@ -586,11 +572,12 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
List<Class<?>> entityClasses, String internalClassName) {
// getter = <entity>.class.getDeclaredMethod()
Method getter = property.getGetter();
Optional<Method> getter = property.getGetter();
if (getter != null) {
mv.visitVarInsn(ALOAD, classVariableIndex4(entityClasses, getter.getDeclaringClass()));
mv.visitLdcInsn(getter.getName());
getter.ifPresent(it -> {
mv.visitVarInsn(ALOAD, classVariableIndex4(entityClasses, it.getDeclaringClass()));
mv.visitLdcInsn(it.getName());
mv.visitInsn(ICONST_0);
mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_CLASS);
@@ -608,7 +595,9 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP, "unreflect", String.format("(%s)%s",
referenceName(JAVA_LANG_REFLECT_METHOD), referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE)), false);
} else {
});
if (!getter.isPresent()) {
mv.visitInsn(ACONST_NULL);
}
@@ -628,22 +617,22 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
List<Class<?>> entityClasses, String internalClassName) {
// setter = <entity>.class.getDeclaredMethod()
Method setter = property.getSetter();
Optional<Method> setter = property.getSetter();
if (setter != null) {
setter.ifPresent(it -> {
mv.visitVarInsn(ALOAD, classVariableIndex4(entityClasses, setter.getDeclaringClass()));
mv.visitLdcInsn(setter.getName());
mv.visitVarInsn(ALOAD, classVariableIndex4(entityClasses, it.getDeclaringClass()));
mv.visitLdcInsn(it.getName());
mv.visitInsn(ICONST_1);
mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_CLASS);
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
Class<?> parameterType = setter.getParameterTypes()[0];
Class<?> parameterType = it.getParameterTypes()[0];
if (parameterType.isPrimitive()) {
mv.visitFieldInsn(GETSTATIC, Type.getInternalName(autoboxType(setter.getParameterTypes()[0])), "TYPE",
mv.visitFieldInsn(GETSTATIC, Type.getInternalName(autoboxType(it.getParameterTypes()[0])), "TYPE",
referenceName(JAVA_LANG_CLASS));
} else {
mv.visitLdcInsn(Type.getType(referenceName(parameterType)));
@@ -665,7 +654,9 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP, "unreflect", String.format("(%s)%s",
referenceName(JAVA_LANG_REFLECT_METHOD), referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE)), false);
} else {
});
if (!setter.isPresent()) {
mv.visitInsn(ACONST_NULL);
}
@@ -685,33 +676,35 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
List<Class<?>> entityClasses, String internalClassName) {
// field = <entity>.class.getDeclaredField()
Field field = property.getField();
mv.visitVarInsn(ALOAD, classVariableIndex4(entityClasses, field.getDeclaringClass()));
mv.visitLdcInsn(field.getName());
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_CLASS, "getDeclaredField",
String.format("(%s)%s", referenceName(JAVA_LANG_STRING), referenceName(JAVA_LANG_REFLECT_FIELD)), false);
mv.visitVarInsn(ASTORE, 1);
property.getField().ifPresent(it -> {
// field.setAccessible(true)
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ICONST_1);
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_REFLECT_FIELD, SET_ACCESSIBLE, "(Z)V", false);
mv.visitVarInsn(ALOAD, classVariableIndex4(entityClasses, it.getDeclaringClass()));
mv.visitLdcInsn(it.getName());
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_CLASS, "getDeclaredField",
String.format("(%s)%s", referenceName(JAVA_LANG_STRING), referenceName(JAVA_LANG_REFLECT_FIELD)), false);
mv.visitVarInsn(ASTORE, 1);
// $fieldGetter = lookup.unreflectGetter(field)
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP, "unreflectGetter", String.format(
"(%s)%s", referenceName(JAVA_LANG_REFLECT_FIELD), referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE)), false);
mv.visitFieldInsn(PUTSTATIC, internalClassName, fieldGetterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE));
// field.setAccessible(true)
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ICONST_1);
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_REFLECT_FIELD, SET_ACCESSIBLE, "(Z)V", false);
// $fieldSetter = lookup.unreflectSetter(field)
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP, "unreflectSetter", String.format(
"(%s)%s", referenceName(JAVA_LANG_REFLECT_FIELD), referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE)), false);
mv.visitFieldInsn(PUTSTATIC, internalClassName, fieldSetterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE));
// $fieldGetter = lookup.unreflectGetter(field)
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP, "unreflectGetter", String.format(
"(%s)%s", referenceName(JAVA_LANG_REFLECT_FIELD), referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE)), false);
mv.visitFieldInsn(PUTSTATIC, internalClassName, fieldGetterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE));
// $fieldSetter = lookup.unreflectSetter(field)
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP, "unreflectSetter", String.format(
"(%s)%s", referenceName(JAVA_LANG_REFLECT_FIELD), referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE)), false);
mv.visitFieldInsn(PUTSTATIC, internalClassName, fieldSetterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE));
});
}
private static void visitBeanGetter(PersistentEntity<?, ?> entity, String internalClassName, ClassWriter cw) {
@@ -851,7 +844,7 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
mv.visitLabel(propertyStackMap.get(property.getName()).label);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
if (property.getGetter() != null || property.getField() != null) {
if (property.getGetter().isPresent() || property.getField().isPresent()) {
visitGetProperty0(entity, property, mv, internalClassName);
} else {
mv.visitJumpInsn(GOTO, dfltLabel);
@@ -875,10 +868,9 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
private static void visitGetProperty0(PersistentEntity<?, ?> entity, PersistentProperty<?> property,
MethodVisitor mv, String internalClassName) {
Method getter = property.getGetter();
if (property.usePropertyAccess() && getter != null) {
property.getGetter().filter(it -> property.usePropertyAccess()).map(it -> {
if (generateMethodHandle(entity, getter)) {
if (generateMethodHandle(entity, it)) {
// $getter.invoke(bean)
mv.visitFieldInsn(GETSTATIC, internalClassName, getterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE));
@@ -890,35 +882,42 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
mv.visitVarInsn(ALOAD, 2);
int invokeOpCode = INVOKEVIRTUAL;
Class<?> declaringClass = getter.getDeclaringClass();
Class<?> declaringClass = it.getDeclaringClass();
boolean interfaceDefinition = declaringClass.isInterface();
if (interfaceDefinition) {
invokeOpCode = INVOKEINTERFACE;
}
mv.visitMethodInsn(invokeOpCode, Type.getInternalName(declaringClass), getter.getName(),
String.format("()%s", signatureTypeName(getter.getReturnType())), interfaceDefinition);
autoboxIfNeeded(getter.getReturnType(), autoboxType(getter.getReturnType()), mv);
mv.visitMethodInsn(invokeOpCode, Type.getInternalName(declaringClass), it.getName(),
String.format("()%s", signatureTypeName(it.getReturnType())), interfaceDefinition);
autoboxIfNeeded(it.getReturnType(), autoboxType(it.getReturnType()), mv);
}
} else {
Field field = property.getField();
if (generateMethodHandle(entity, field)) {
// $fieldGetter.invoke(bean)
mv.visitFieldInsn(GETSTATIC, internalClassName, fieldGetterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE));
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_INVOKE_METHOD_HANDLE, "invoke",
String.format("(%s)%s", referenceName(JAVA_LANG_OBJECT), referenceName(JAVA_LANG_OBJECT)), false);
} else {
// bean.field
mv.visitVarInsn(ALOAD, 2);
mv.visitFieldInsn(GETFIELD, Type.getInternalName(field.getDeclaringClass()), field.getName(),
signatureTypeName(field.getType()));
autoboxIfNeeded(field.getType(), autoboxType(field.getType()), mv);
}
}
return null;
}).orElseGet(() -> {
property.getField().ifPresent(it -> {
if (generateMethodHandle(entity, it)) {
// $fieldGetter.invoke(bean)
mv.visitFieldInsn(GETSTATIC, internalClassName, fieldGetterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE));
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_INVOKE_METHOD_HANDLE, "invoke",
String.format("(%s)%s", referenceName(JAVA_LANG_OBJECT), referenceName(JAVA_LANG_OBJECT)), false);
} else {
// bean.field
mv.visitVarInsn(ALOAD, 2);
mv.visitFieldInsn(GETFIELD, Type.getInternalName(it.getDeclaringClass()), it.getName(),
signatureTypeName(it.getType()));
autoboxIfNeeded(it.getType(), autoboxType(it.getType()), mv);
}
});
return null;
});
mv.visitInsn(ARETURN);
}
@@ -1029,7 +1028,7 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
mv.visitLabel(propertyStackMap.get(property.getName()).label);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
if (property.getSetter() != null || property.getField() != null) {
if (property.getSetter().isPresent() || property.getField().isPresent()) {
visitSetProperty0(entity, property, mv, internalClassName);
} else {
mv.visitJumpInsn(GOTO, dfltLabel);
@@ -1053,10 +1052,11 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
private static void visitSetProperty0(PersistentEntity<?, ?> entity, PersistentProperty<?> property,
MethodVisitor mv, String internalClassName) {
Method setter = property.getSetter();
if (property.usePropertyAccess() && setter != null) {
Optional<Method> setter = property.getSetter();
if (generateMethodHandle(entity, setter)) {
setter.filter(it -> property.usePropertyAccess()).map(it -> {
if (generateMethodHandle(entity, it)) {
// $setter.invoke(bean)
mv.visitFieldInsn(GETSTATIC, internalClassName, setterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE));
@@ -1069,26 +1069,28 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
mv.visitVarInsn(ALOAD, 3);
mv.visitVarInsn(ALOAD, 2);
Class<?> parameterType = setter.getParameterTypes()[0];
Class<?> parameterType = it.getParameterTypes()[0];
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(autoboxType(parameterType)));
autoboxIfNeeded(autoboxType(parameterType), parameterType, mv);
int invokeOpCode = INVOKEVIRTUAL;
Class<?> declaringClass = setter.getDeclaringClass();
Class<?> declaringClass = it.getDeclaringClass();
boolean interfaceDefinition = declaringClass.isInterface();
if (interfaceDefinition) {
invokeOpCode = INVOKEINTERFACE;
}
mv.visitMethodInsn(invokeOpCode, Type.getInternalName(setter.getDeclaringClass()), setter.getName(),
mv.visitMethodInsn(invokeOpCode, Type.getInternalName(it.getDeclaringClass()), it.getName(),
String.format("(%s)V", signatureTypeName(parameterType)), interfaceDefinition);
}
} else {
Field field = property.getField();
if (field != null) {
if (generateSetterMethodHandle(entity, field)) {
return null;
}).orElseGet(() -> {
property.getField().ifPresent(it -> {
if (generateSetterMethodHandle(entity, it)) {
// $fieldSetter.invoke(bean, object)
mv.visitFieldInsn(GETSTATIC, internalClassName, fieldSetterName(property),
referenceName(JAVA_LANG_INVOKE_METHOD_HANDLE));
@@ -1101,15 +1103,17 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
mv.visitVarInsn(ALOAD, 3);
mv.visitVarInsn(ALOAD, 2);
Class<?> fieldType = field.getType();
Class<?> fieldType = it.getType();
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(autoboxType(fieldType)));
autoboxIfNeeded(autoboxType(fieldType), fieldType, mv);
mv.visitFieldInsn(PUTFIELD, Type.getInternalName(field.getDeclaringClass()), field.getName(),
mv.visitFieldInsn(PUTFIELD, Type.getInternalName(it.getDeclaringClass()), it.getName(),
signatureTypeName(fieldType));
}
}
}
});
return null;
});
mv.visitInsn(RETURN);
}
@@ -1197,7 +1201,7 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
if (isAccessible(entity)) {
if (Modifier.isProtected(member.getModifiers()) || isDefault(member.getModifiers())) {
if (!member.getDeclaringClass().getPackage().equals(entity.getClass().getPackage())) {
if (!member.getDeclaringClass().getPackage().equals(entity.getType().getPackage())) {
return true;
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mapping.model;
import java.util.Optional;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
@@ -54,7 +56,7 @@ public class ConvertingPropertyAccessor implements PersistentPropertyAccessor {
* @see org.springframework.data.mapping.PersistentPropertyAccessor#setProperty(org.springframework.data.mapping.PersistentProperty, java.lang.Object)
*/
@Override
public void setProperty(PersistentProperty<?> property, Object value) {
public void setProperty(PersistentProperty<?> property, Optional<? extends Object> value) {
accessor.setProperty(property, convertIfNecessary(value, property.getType()));
}
@@ -63,7 +65,7 @@ public class ConvertingPropertyAccessor implements PersistentPropertyAccessor {
* @see org.springframework.data.mapping.PersistentPropertyAccessor#getProperty(org.springframework.data.mapping.PersistentProperty)
*/
@Override
public Object getProperty(PersistentProperty<?> property) {
public Optional<? extends Object> getProperty(PersistentProperty<?> property) {
return accessor.getProperty(property);
}
@@ -74,7 +76,7 @@ public class ConvertingPropertyAccessor implements PersistentPropertyAccessor {
* @param targetType must not be {@literal null}.
* @return
*/
public <T> T getProperty(PersistentProperty<?> property, Class<T> targetType) {
public <T> Optional<T> getProperty(PersistentProperty<?> property, Class<T> targetType) {
Assert.notNull(property, "PersistentProperty must not be null!");
Assert.notNull(targetType, "Target type must not be null!");
@@ -100,8 +102,7 @@ public class ConvertingPropertyAccessor implements PersistentPropertyAccessor {
* @return
*/
@SuppressWarnings("unchecked")
private <T> T convertIfNecessary(Object source, Class<T> type) {
return (T) (source == null ? source : type.isAssignableFrom(source.getClass()) ? source : conversionService
.convert(source, type));
private <T> Optional<T> convertIfNecessary(Optional<? extends Object> source, Class<T> type) {
return source.map(it -> type.isAssignableFrom(it.getClass()) ? (T) it : conversionService.convert(it, type));
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mapping.model;
import java.util.Optional;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
@@ -31,7 +33,7 @@ import org.springframework.util.Assert;
public class IdPropertyIdentifierAccessor implements IdentifierAccessor {
private final PersistentPropertyAccessor accessor;
private final PersistentProperty<?> idProperty;
private final Optional<? extends PersistentProperty<?>> idProperty;
/**
* Creates a new {@link IdPropertyIdentifierAccessor} for the given {@link PersistentEntity} and
@@ -54,7 +56,7 @@ public class IdPropertyIdentifierAccessor implements IdentifierAccessor {
* (non-Javadoc)
* @see org.springframework.data.keyvalue.core.IdentifierAccessor#getIdentifier()
*/
public Object getIdentifier() {
return accessor.getProperty(idProperty);
public Optional<? extends Object> getIdentifier() {
return idProperty.map(it -> accessor.getProperty(it));
}
}

View File

@@ -17,10 +17,12 @@ package org.springframework.data.mapping.model;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.util.StringUtils;
/**
* Exception being thrown in case an entity could not be instantiated in the process of a to-object-mapping.
@@ -45,32 +47,33 @@ public class MappingInstantiationException extends RuntimeException {
* @param arguments
* @param cause
*/
public MappingInstantiationException(PersistentEntity<?, ?> entity, List<Object> arguments, Exception cause) {
public MappingInstantiationException(Optional<PersistentEntity<?, ?>> entity, List<Object> arguments,
Exception cause) {
this(entity, arguments, null, cause);
}
private MappingInstantiationException(PersistentEntity<?, ?> entity, List<Object> arguments, String message,
private MappingInstantiationException(Optional<PersistentEntity<?, ?>> entity, List<Object> arguments, String message,
Exception cause) {
super(buildExceptionMessage(entity, arguments, message), cause);
super(buildExceptionMessage(entity, arguments.stream(), message), cause);
this.entityType = entity == null ? null : entity.getType();
this.constructor = entity == null || entity.getPersistenceConstructor() == null ? null : entity
.getPersistenceConstructor().getConstructor();
this.entityType = entity.map(it -> it.getType()).orElse(null);
this.constructor = entity.flatMap(it -> it.getPersistenceConstructor()).map(c -> c.getConstructor()).orElse(null);
this.constructorArguments = arguments;
}
private static final String buildExceptionMessage(PersistentEntity<?, ?> entity, List<Object> arguments,
private static final String buildExceptionMessage(Optional<PersistentEntity<?, ?>> entity, Stream<Object> arguments,
String defaultMessage) {
if (entity == null) {
return defaultMessage;
}
return entity.map(it -> {
PreferredConstructor<?, ?> constructor = entity.getPersistenceConstructor();
Optional<? extends PreferredConstructor<?, ?>> constructor = it.getPersistenceConstructor();
return String.format(TEXT_TEMPLATE, entity.getType().getName(), constructor == null ? "NO_CONSTRUCTOR"
: constructor.getConstructor().toString(), StringUtils.collectionToCommaDelimitedString(arguments));
return String.format(TEXT_TEMPLATE, it.getType().getName(),
constructor.map(c -> c.getConstructor().toString()).orElse("NO_CONSTRUCTOR"),
String.join(",", arguments.map(Object::toString).collect(Collectors.toList())));
}).orElse(defaultMessage);
}
/**
@@ -78,8 +81,8 @@ public class MappingInstantiationException extends RuntimeException {
*
* @return the entityType
*/
public Class<?> getEntityType() {
return entityType;
public Optional<Class<?>> getEntityType() {
return Optional.ofNullable(entityType);
}
/**
@@ -87,8 +90,8 @@ public class MappingInstantiationException extends RuntimeException {
*
* @return the constructor
*/
public Constructor<?> getConstructor() {
return constructor;
public Optional<Constructor<?>> getConstructor() {
return Optional.ofNullable(constructor);
}
/**

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mapping.model;
import java.util.Optional;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
@@ -31,5 +33,5 @@ public interface ParameterValueProvider<P extends PersistentProperty<P>> {
* @param parameter must not be {@literal null}.
* @return
*/
<T> T getParameterValue(Parameter<T, P> parameter);
}
<T> Optional<T> getParameterValue(Parameter<T, P> parameter);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mapping.model;
import java.util.Optional;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
@@ -29,12 +31,12 @@ import org.springframework.util.Assert;
*
* @author Oliver Gierke
*/
public class PersistentEntityParameterValueProvider<P extends PersistentProperty<P>> implements
ParameterValueProvider<P> {
public class PersistentEntityParameterValueProvider<P extends PersistentProperty<P>>
implements ParameterValueProvider<P> {
private final PersistentEntity<?, P> entity;
private final PropertyValueProvider<P> provider;
private final Object parent;
private final Optional<Object> parent;
/**
* Creates a new {@link PersistentEntityParameterValueProvider} for the given {@link PersistentEntity} and
@@ -45,10 +47,11 @@ public class PersistentEntityParameterValueProvider<P extends PersistentProperty
* @param parent the parent object being created currently, can be {@literal null}.
*/
public PersistentEntityParameterValueProvider(PersistentEntity<?, P> entity, PropertyValueProvider<P> provider,
Object parent) {
Optional<Object> parent) {
Assert.notNull(entity, "Entity must not be null!");
Assert.notNull(provider, "Provider must not be null!");
Assert.notNull(parent, "Parent must not be null!");
this.entity = entity;
this.provider = provider;
@@ -60,21 +63,18 @@ public class PersistentEntityParameterValueProvider<P extends PersistentProperty
* @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
*/
@SuppressWarnings("unchecked")
public <T> T getParameterValue(Parameter<T, P> parameter) {
public <T> Optional<T> getParameterValue(Parameter<T, P> parameter) {
PreferredConstructor<?, P> constructor = entity.getPersistenceConstructor();
PreferredConstructor<?, P> constructor = entity.getPersistenceConstructor().get();
if (constructor.isEnclosingClassParameter(parameter)) {
return (T) parent;
return (Optional<T>) parent;
}
P property = entity.getPersistentProperty(parameter.getName());
if (property == null) {
throw new MappingException(String.format("No property %s found on entity %s to bind constructor parameter to!",
parameter.getName(), entity.getType()));
}
return provider.getPropertyValue(property);
return provider.getPropertyValue(parameter.getName()//
.flatMap(it -> entity.getPersistentProperty(it))//
.orElseThrow(() -> new MappingException(
String.format("No property %s found on entity %s to bind constructor parameter to!", parameter.getName(),
entity.getType()))));
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.mapping.model;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Optional;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
@@ -37,7 +38,7 @@ public class PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>>
private final ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
private PreferredConstructor<T, P> constructor;
private Optional<PreferredConstructor<T, P>> constructor;
/**
* Creates a new {@link PreferredConstructorDiscoverer} for the given type.
@@ -54,7 +55,7 @@ public class PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>>
* @param entity must not be {@literal null}.
*/
public PreferredConstructorDiscoverer(PersistentEntity<T, P> entity) {
this(entity.getTypeInformation(), entity);
this(entity.getTypeInformation(), Optional.ofNullable(entity));
}
/**
@@ -63,7 +64,7 @@ public class PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>>
* @param type must not be {@literal null}.
* @param entity
*/
protected PreferredConstructorDiscoverer(TypeInformation<T> type, PersistentEntity<T, P> entity) {
protected PreferredConstructorDiscoverer(TypeInformation<T> type, Optional<PersistentEntity<T, P>> entity) {
boolean noArgConstructorFound = false;
int numberOfArgConstructors = 0;
@@ -75,13 +76,13 @@ public class PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>>
// Explicitly defined constructor trumps all
if (preferredConstructor.isExplicitlyAnnotated()) {
this.constructor = preferredConstructor;
this.constructor = Optional.of(preferredConstructor);
return;
}
// No-arg constructor trumps custom ones
if (this.constructor == null || preferredConstructor.isNoArgConstructor()) {
this.constructor = preferredConstructor;
this.constructor = Optional.of(preferredConstructor);
}
if (preferredConstructor.isNoArgConstructor()) {
@@ -92,13 +93,13 @@ public class PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>>
}
if (!noArgConstructorFound && numberOfArgConstructors > 1) {
this.constructor = null;
this.constructor = Optional.empty();
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private PreferredConstructor<T, P> buildPreferredConstructor(Constructor<?> constructor,
TypeInformation<T> typeInformation, PersistentEntity<T, P> entity) {
TypeInformation<T> typeInformation, Optional<PersistentEntity<T, P>> entity) {
List<TypeInformation<?>> parameterTypes = typeInformation.getParameterTypes(constructor);
@@ -113,7 +114,7 @@ public class PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>>
for (int i = 0; i < parameterTypes.size(); i++) {
String name = parameterNames == null ? null : parameterNames[i];
Optional<String> name = Optional.ofNullable(parameterNames == null ? null : parameterNames[i]);
TypeInformation<?> type = parameterTypes.get(i);
Annotation[] annotations = parameterAnnotations[i];
@@ -128,7 +129,7 @@ public class PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>>
*
* @return
*/
public PreferredConstructor<T, P> getConstructor() {
public Optional<PreferredConstructor<T, P>> getConstructor() {
return constructor;
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mapping.model;
import java.util.Optional;
import org.springframework.data.mapping.PersistentProperty;
/**
@@ -30,5 +32,5 @@ public interface PropertyValueProvider<P extends PersistentProperty<P>> {
* @param property will never be {@literal null}.
* @return
*/
<T> T getPropertyValue(P property);
<T> Optional<T> getPropertyValue(P property);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mapping.model;
import java.util.Optional;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
@@ -26,7 +28,8 @@ import org.springframework.util.Assert;
*
* @author Oliver Gierke
*/
public class SpELExpressionParameterValueProvider<P extends PersistentProperty<P>> implements ParameterValueProvider<P> {
public class SpELExpressionParameterValueProvider<P extends PersistentProperty<P>>
implements ParameterValueProvider<P> {
private final SpELExpressionEvaluator evaluator;
private final ParameterValueProvider<P> delegate;
@@ -57,14 +60,15 @@ public class SpELExpressionParameterValueProvider<P extends PersistentProperty<P
* (non-Javadoc)
* @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
*/
public <T> T getParameterValue(Parameter<T, P> parameter) {
public <T> Optional<T> getParameterValue(Parameter<T, P> parameter) {
if (!parameter.hasSpelExpression()) {
return delegate == null ? null : delegate.getParameterValue(parameter);
return delegate.getParameterValue(parameter);
}
Object object = evaluator.evaluate(parameter.getSpelExpression());
return object == null ? null : potentiallyConvertSpelValue(object, parameter);
Optional<Object> object = Optional.ofNullable(evaluator.evaluate(parameter.getSpelExpression().orElse(null)));
return object.map(it -> potentiallyConvertSpelValue(object, parameter));
}
/**

View File

@@ -47,7 +47,7 @@ class DefaultProjectionInformation implements ProjectionInformation {
Assert.notNull(type, "Projection type must not be null!");
this.projectionType = type;
this.properties = collectDescriptors(type);
this.properties = Arrays.asList(BeanUtils.getPropertyDescriptors(type));
}
/*
@@ -66,7 +66,8 @@ class DefaultProjectionInformation implements ProjectionInformation {
public List<PropertyDescriptor> getInputProperties() {
return properties.stream()//
.filter(it -> isInputProperty(it))//
.filter(this::isInputProperty)//
.distinct()//
.collect(Collectors.toList());
}
@@ -91,30 +92,9 @@ class DefaultProjectionInformation implements ProjectionInformation {
return true;
}
/**
* Collects {@link PropertyDescriptor}s for all properties exposed by the given type and all its super interfaces.
*
* @param type must not be {@literal null}.
* @return
*/
private static List<PropertyDescriptor> collectDescriptors(Class<?> type) {
List<PropertyDescriptor> result = new ArrayList<PropertyDescriptor>();
result.addAll(Arrays.stream(BeanUtils.getPropertyDescriptors(type))//
.filter(it -> !hasDefaultGetter(it))//
.collect(Collectors.toList()));
for (Class<?> interfaze : type.getInterfaces()) {
result.addAll(collectDescriptors(interfaze));
}
return result;
}
/**
* Returns whether the given {@link PropertyDescriptor} has a getter that is a Java 8 default method.
*
*
* @param descriptor must not be {@literal null}.
* @return
*/

View File

@@ -17,7 +17,6 @@ package org.springframework.data.querydsl;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -130,15 +129,6 @@ public class QuerydslRepositoryInvokerAdapter implements RepositoryInvoker {
return delegate.invokeFindOne(id);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryInvoker#invokeQueryMethod(java.lang.reflect.Method, java.util.Map, org.springframework.data.domain.Pageable, org.springframework.data.domain.Sort)
*/
@Override
public Object invokeQueryMethod(Method method, Map<String, String[]> parameters, Pageable pageable, Sort sort) {
return delegate.invokeQueryMethod(method, parameters, pageable, sort);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryInvoker#invokeQueryMethod(java.lang.reflect.Method, org.springframework.util.MultiValueMap, org.springframework.data.domain.Pageable, org.springframework.data.domain.Sort)

View File

@@ -16,6 +16,7 @@
package org.springframework.data.querydsl.binding;
import java.util.Collection;
import java.util.Optional;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
@@ -40,5 +41,5 @@ public interface MultiValueBinding<T extends Path<? extends S>, S> {
* @return can be {@literal null}, in which case the binding will not be incorporated in the overall {@link Predicate}
* .
*/
Predicate bind(T path, Collection<? extends S> value);
Optional<Predicate> bind(T path, Collection<? extends S> value);
}

View File

@@ -0,0 +1,44 @@
/*
* 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.
* 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.querydsl.binding;
import java.util.Optional;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
/**
* {@link OptionalValueBinding} creates a {@link Predicate} out of given {@link Path} and value. Used for specific
* parameter treatment in {@link QuerydslBindings}.
*
* @author Christoph Strobl
* @author Oliver Gierke
* @since 1.11
*/
@FunctionalInterface
public interface OptionalValueBinding<T extends Path<? extends S>, S> {
/**
* Returns the predicate to be applied to the given {@link Path} for the given value. The given value will be the
* first the first one provided for the given path and converted into the expected type.
*
* @param path {@link Path} to the property. Will not be {@literal null}.
* @param value the value that should be bound. Will not be {@literal null}.
* @return can be {@literal null}, in which case the binding will not be incorporated in the overall {@link Predicate}
* .
*/
Optional<Predicate> bind(T path, Optional<? extends S> value);
}

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.data.querydsl.binding;
import static org.springframework.data.querydsl.QueryDslUtils.*;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Value;
@@ -24,15 +22,16 @@ import lombok.Value;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Optionals;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -42,7 +41,7 @@ import com.querydsl.core.types.Predicate;
/**
* {@link QuerydslBindings} allows definition of path specific bindings.
*
*
* <pre>
* <code>
* new QuerydslBindings() {
@@ -53,7 +52,7 @@ import com.querydsl.core.types.Predicate;
* }
* </code>
* </pre>
*
*
* The bindings can either handle a single - see {@link PathBinder#first(SingleValueBinding)} - (the first in case
* multiple ones are supplied) or multiple - see {@link PathBinder#all(MultiValueBinding)} - value binding. If exactly
* one path is deployed, an {@link AliasingPathBinder} is returned which - as the name suggests - allows aliasing of
@@ -61,7 +60,7 @@ import com.querydsl.core.types.Predicate;
* <p>
* {@link QuerydslBindings} are usually manipulated using a {@link QuerydslBinderCustomizer}, either implemented
* directly or using a default method on a Spring Data repository.
*
*
* @author Christoph Strobl
* @author Oliver Gierke
* @since 1.11
@@ -90,7 +89,7 @@ public class QuerydslBindings {
/**
* Returns an {@link AliasingPathBinder} for the given {@link Path} to define bindings for them.
*
*
* @param path must not be {@literal null}.
* @return
*/
@@ -100,17 +99,18 @@ public class QuerydslBindings {
/**
* Returns a new {@link PathBinder} for the given {@link Path}s to define bindings for them.
*
*
* @param paths must not be {@literal null} or empty.
* @return
*/
@SafeVarargs
public final <T extends Path<S>, S> PathBinder<T, S> bind(T... paths) {
return new PathBinder<T, S>(paths);
}
/**
* Returns a new {@link TypeBinder} for the given type.
*
*
* @param type must not be {@literal null}.
* @return
*/
@@ -121,7 +121,7 @@ public class QuerydslBindings {
/**
* Exclude properties from binding. Exclusion of all properties of a nested type can be done by exclusion on a higher
* level. E.g. {@code address} would exclude both {@code address.city} and {@code address.street}.
*
*
* @param paths must not be {@literal null} or empty.
*/
public final void excluding(Path<?>... paths) {
@@ -129,13 +129,13 @@ public class QuerydslBindings {
Assert.notEmpty(paths, "At least one path has to be provided!");
for (Path<?> path : paths) {
this.blackList.add(toDotPath(path));
this.blackList.add(toDotPath(Optional.of(path)));
}
}
/**
* Include properties for binding. Include the property considered a binding candidate.
*
*
* @param properties must not be {@literal null} or empty.
*/
public final void including(Path<?>... paths) {
@@ -143,7 +143,7 @@ public class QuerydslBindings {
Assert.notEmpty(paths, "At least one path has to be provided!");
for (Path<?> path : paths) {
this.whiteList.add(toDotPath(path));
this.whiteList.add(toDotPath(Optional.of(path)));
}
}
@@ -151,7 +151,7 @@ public class QuerydslBindings {
* Returns whether to exclude all properties for which no explicit binding has been defined or it has been explicitly
* white-listed. This defaults to {@literal false} which means that for properties without an explicitly defined
* binding a type specific default binding will be applied.
*
*
* @param excludeUnlistedProperties
* @return
* @see #including(String...)
@@ -165,7 +165,7 @@ public class QuerydslBindings {
/**
* Returns whether the given path is available on the given type.
*
*
* @param path must not be {@literal null}.
* @param type must not be {@literal null}.
* @return
@@ -180,7 +180,7 @@ public class QuerydslBindings {
/**
* Returns whether the given path is available on the given type.
*
*
* @param path must not be {@literal null}.
* @param type
* @return
@@ -196,12 +196,12 @@ public class QuerydslBindings {
/**
* Returns the {@link SingleValueBinding} for the given {@link PropertyPath}. Prefers a path configured for the
* specific path but falls back to the builder registered for a given type.
*
*
* @param path must not be {@literal null}.
* @return can be {@literal null}.
*/
@SuppressWarnings("unchecked")
public <S extends Path<? extends T>, T> MultiValueBinding<S, T> getBindingForPath(PathInformation path) {
public <S extends Path<? extends T>, T> Optional<MultiValueBinding<S, T>> getBindingForPath(PathInformation path) {
Assert.notNull(path, "PropertyPath must not be null!");
@@ -209,30 +209,29 @@ public class QuerydslBindings {
if (pathAndBinding != null) {
MultiValueBinding<S, T> binding = pathAndBinding.getBinding();
Optional<MultiValueBinding<S, T>> binding = pathAndBinding.getBinding();
if (binding != null) {
return pathAndBinding.getBinding();
if (binding.isPresent()) {
return binding;
}
}
pathAndBinding = (PathAndBinding<S, T>) typeSpecs.get(path.getLeafType());
return pathAndBinding == null ? null : pathAndBinding.getBinding();
return pathAndBinding == null ? Optional.empty() : pathAndBinding.getBinding();
}
/**
* Returns a {@link Path} for the {@link PropertyPath} instance.
*
*
* @param path must not be {@literal null}.
* @return
*/
Path<?> getExistingPath(PathInformation path) {
Optional<Path<?>> getExistingPath(PathInformation path) {
Assert.notNull(path, "PropertyPath must not be null!");
PathAndBinding<?, ?> pathAndBuilder = pathSpecs.get(path.toDotPath());
return pathAndBuilder == null ? null : pathAndBuilder.getPath();
return Optional.ofNullable(pathSpecs.get(path.toDotPath())).flatMap(it -> it.getPath());
}
/**
@@ -252,7 +251,9 @@ public class QuerydslBindings {
}
if (pathSpecs.containsKey(path)) {
return QuerydslPathInformation.of(pathSpecs.get(path).getPath());
return pathSpecs.get(path).getPath()//
.map(it -> QuerydslPathInformation.of(it))//
.orElse(null);
}
try {
@@ -265,7 +266,7 @@ public class QuerydslBindings {
/**
* Checks if a given {@link PropertyPath} should be visible for binding values.
*
*
* @param path
* @return
*/
@@ -292,7 +293,7 @@ public class QuerydslBindings {
/**
* Returns whether the given path is visible, which means either an alias and not explicitly blacklisted, explicitly
* white listed or not on the black list if no white list configured.
*
*
* @param path must not be {@literal null}.
* @return
*/
@@ -310,6 +311,17 @@ public class QuerydslBindings {
return whiteList.contains(path);
}
/**
* Returns the property path for the given {@link Path}.
*
* @param path can be {@literal null}.
* @return
*/
private static String toDotPath(Optional<Path<?>> path) {
return path.map(it -> it.toString().substring(it.getMetadata().getRootPath().getMetadata().getName().length() + 1))
.orElse("");
}
/**
* A binder for {@link Path}s.
*
@@ -321,9 +333,10 @@ public class QuerydslBindings {
/**
* Creates a new {@link PathBinder} for the given {@link Path}s.
*
*
* @param paths must not be {@literal null} or empty.
*/
@SafeVarargs
PathBinder(P... paths) {
Assert.notEmpty(paths, "At least one path has to be provided!");
@@ -332,20 +345,26 @@ public class QuerydslBindings {
/**
* Defines the given {@link SingleValueBinding} to be used for the paths.
*
*
* @param binding must not be {@literal null}.
* @return
*/
public void first(SingleValueBinding<P, T> binding) {
public void firstOptional(OptionalValueBinding<P, T> binding) {
Assert.notNull(binding, "Binding must not be null!");
all(new MultiValueBindingAdapter<P, T>(binding));
all((path, value) -> binding.bind(path, Optionals.next(value.iterator())));
}
public void first(SingleValueBinding<P, T> binding) {
Assert.notNull(binding, "Binding must not be null!");
all((path, value) -> Optionals.next(value.iterator()).flatMap(t -> binding.bind(path, t)));
}
/**
* Defines the given {@link MultiValueBinding} to be used for the paths.
*
*
* @param binding must not be {@literal null}.
* @return
*/
@@ -353,9 +372,7 @@ public class QuerydslBindings {
Assert.notNull(binding, "Binding must not be null!");
for (P path : paths) {
registerBinding(new PathAndBinding<P, T>(path, binding));
}
paths.forEach(path -> registerBinding(PathAndBinding.withPath(path).with(binding)));
}
protected void registerBinding(PathAndBinding<P, T> binding) {
@@ -376,7 +393,7 @@ public class QuerydslBindings {
/**
* Creates a new {@link AliasingPathBinder} for the given {@link Path}.
*
*
* @param paths must not be {@literal null}.
*/
AliasingPathBinder(P path) {
@@ -385,11 +402,10 @@ public class QuerydslBindings {
/**
* Creates a new {@link AliasingPathBinder} using the given alias and {@link Path}.
*
*
* @param alias can be {@literal null}.
* @param path must not be {@literal null}.
*/
@SuppressWarnings("unchecked")
private AliasingPathBinder(String alias, P path) {
super(path);
@@ -404,7 +420,7 @@ public class QuerydslBindings {
* Aliases the current binding to be available under the given path. By default, the binding path will be
* blacklisted so that aliasing effectively hides the original path. If you want to keep the original path around,
* include it in an explicit whitelist.
*
*
* @param alias must not be {@literal null}.
* @return will never be {@literal null}.
*/
@@ -418,10 +434,10 @@ public class QuerydslBindings {
* Registers the current aliased binding to use the default binding.
*/
public void withDefaultBinding() {
registerBinding(new PathAndBinding<P, T>(path, null));
registerBinding(PathAndBinding.withPath(path));
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.binding.QuerydslBindings.PathBinder#registerBinding(org.springframework.data.querydsl.binding.QuerydslBindings.PathAndBinding)
*/
@@ -450,60 +466,69 @@ public class QuerydslBindings {
/**
* Configures the given {@link SingleValueBinding} to be used for the current type.
*
*
* @param binding must not be {@literal null}.
*/
public <P extends Path<T>> void firstOptional(OptionalValueBinding<P, T> binding) {
Assert.notNull(binding, "Binding must not be null!");
all(new MultiValueBinding<P, T>() {
@Override
public Optional<Predicate> bind(P path, Collection<? extends T> value) {
return binding.bind(path, Optionals.next(value.iterator()));
}
});
}
public <P extends Path<T>> void first(SingleValueBinding<P, T> binding) {
Assert.notNull(binding, "Binding must not be null!");
all(new MultiValueBindingAdapter<P, T>(binding));
all(new MultiValueBinding<P, T>() {
@Override
public Optional<Predicate> bind(P path, Collection<? extends T> value) {
return Optionals.next(value.iterator()).flatMap(t -> binding.bind(path, t));
}
});
}
/**
* Configures the given {@link MultiValueBinding} to be used for the current type.
*
*
* @param binding must not be {@literal null}.
*/
public <P extends Path<T>> void all(MultiValueBinding<P, T> binding) {
Assert.notNull(binding, "Binding must not be null!");
QuerydslBindings.this.typeSpecs.put(type, new PathAndBinding<P, T>(null, binding));
QuerydslBindings.this.typeSpecs.put(type, PathAndBinding.<T, P> withoutPath().with(binding));
}
}
/**
* A pair of a {@link Path} and the registered {@link MultiValueBinding}.
*
*
* @author Christoph Strobl
* @author Oliver Gierke
* @since 1.11
*/
@Value
private static class PathAndBinding<S extends Path<? extends T>, T> {
private static class PathAndBinding<P extends Path<? extends T>, T> {
Path<?> path;
MultiValueBinding<S, T> binding;
}
@NonNull Optional<Path<?>> path;
@NonNull Optional<MultiValueBinding<P, T>> binding;
/**
* {@link MultiValueBinding} that forwards the first value of the collection values to the delegate
* {@link SingleValueBinding}.
*
* @author Oliver Gierke
*/
@RequiredArgsConstructor
static class MultiValueBindingAdapter<P extends Path<? extends T>, T> implements MultiValueBinding<P, T> {
public static <T, P extends Path<? extends T>> PathAndBinding<P, T> withPath(P path) {
return new PathAndBinding<P, T>(Optional.of(path), Optional.empty());
}
private final @NonNull SingleValueBinding<P, T> delegate;
public static <T, S extends Path<? extends T>> PathAndBinding<S, T> withoutPath() {
return new PathAndBinding<S, T>(Optional.empty(), Optional.empty());
}
/*
* (non-Javadoc)
* @see org.springframework.data.web.querydsl.MultiValueBinding#bind(com.mysema.query.types.Path, java.util.Collection)
*/
@Override
public Predicate bind(P path, Collection<? extends T> value) {
Iterator<? extends T> iterator = value.iterator();
return delegate.bind(path, iterator.hasNext() ? iterator.next() : null);
public PathAndBinding<P, T> with(MultiValueBinding<P, T> binding) {
return new PathAndBinding<P, T>(path, Optional.of(binding));
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.querydsl.binding;
import java.util.Map;
import java.util.Optional;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
@@ -44,8 +45,8 @@ public class QuerydslBindingsFactory implements ApplicationContextAware {
private final EntityPathResolver entityPathResolver;
private final Map<TypeInformation<?>, EntityPath<?>> entityPaths;
private AutowireCapableBeanFactory beanFactory;
private Repositories repositories;
private Optional<AutowireCapableBeanFactory> beanFactory;
private Optional<Repositories> repositories;
/**
* Creates a new {@link QuerydslBindingsFactory} using the given {@link EntityPathResolver}.
@@ -58,6 +59,8 @@ public class QuerydslBindingsFactory implements ApplicationContextAware {
this.entityPathResolver = entityPathResolver;
this.entityPaths = new ConcurrentReferenceHashMap<TypeInformation<?>, EntityPath<?>>();
this.beanFactory = Optional.empty();
this.repositories = Optional.empty();
}
/*
@@ -67,8 +70,8 @@ public class QuerydslBindingsFactory implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanFactory = applicationContext.getAutowireCapableBeanFactory();
this.repositories = new Repositories(applicationContext);
this.beanFactory = Optional.of(applicationContext.getAutowireCapableBeanFactory());
this.repositories = Optional.of(new Repositories(applicationContext));
}
/**
@@ -84,14 +87,15 @@ public class QuerydslBindingsFactory implements ApplicationContextAware {
* Creates the {@link QuerydslBindings} to be used using for the given domain type and a pre-defined
* {@link QuerydslBinderCustomizer}. If no customizer is given, auto-detection will be applied.
*
* @param customizer the {@link QuerydslBinderCustomizer} to use. If {@literal null} is given customizer detection for
* the given domain type will be applied.
* @param domainType must not be {@literal null}.
* @param customizer the {@link QuerydslBinderCustomizer} to use. If an empty {@link Optional} is given customizer
* detection for the given domain type will be applied.
* @return
*/
public QuerydslBindings createBindingsFor(Class<? extends QuerydslBinderCustomizer<?>> customizer,
TypeInformation<?> domainType) {
public QuerydslBindings createBindingsFor(TypeInformation<?> domainType,
Optional<Class<? extends QuerydslBinderCustomizer<?>>> customizer) {
Assert.notNull(customizer, "Customizer must not be null!");
Assert.notNull(domainType, "Domain type must not be null!");
EntityPath<?> path = verifyEntityPathPresent(domainType);
@@ -111,23 +115,15 @@ public class QuerydslBindingsFactory implements ApplicationContextAware {
*/
private EntityPath<?> verifyEntityPathPresent(TypeInformation<?> candidate) {
EntityPath<?> path = entityPaths.get(candidate);
return entityPaths.computeIfAbsent(candidate, key -> {
if (path != null) {
return path;
}
Class<?> type = candidate.getType();
try {
path = entityPathResolver.createPath(type);
} catch (IllegalArgumentException o_O) {
throw new IllegalStateException(
String.format(INVALID_DOMAIN_TYPE, candidate.getType(), QuerydslPredicate.class.getSimpleName()), o_O);
}
entityPaths.put(candidate, path);
return path;
try {
return entityPathResolver.createPath(key.getType());
} catch (IllegalArgumentException o_O) {
throw new IllegalStateException(
String.format(INVALID_DOMAIN_TYPE, key.getType(), QuerydslPredicate.class.getSimpleName()), o_O);
}
});
}
/**
@@ -140,22 +136,16 @@ public class QuerydslBindingsFactory implements ApplicationContextAware {
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private QuerydslBinderCustomizer<EntityPath<?>> findCustomizerForDomainType(
Class<? extends QuerydslBinderCustomizer> customizer, Class<?> domainType) {
Optional<? extends Class<? extends QuerydslBinderCustomizer>> customizer, Class<?> domainType) {
if (customizer != null && !QuerydslBinderCustomizer.class.equals(customizer)) {
return createQuerydslBinderCustomizer(customizer);
}
return customizer//
.filter(it -> !QuerydslBinderCustomizer.class.equals(it))//
.map(it -> createQuerydslBinderCustomizer(it)).orElseGet(() -> {
if (repositories != null && repositories.hasRepositoryFor(domainType)) {
Object repository = repositories.getRepositoryFor(domainType);
if (repository instanceof QuerydslBinderCustomizer) {
return (QuerydslBinderCustomizer<EntityPath<?>>) repository;
}
}
return NoOpCustomizer.INSTANCE;
return repositories.flatMap(it -> it.getRepositoryFor(domainType))//
.map(it -> it instanceof QuerydslBinderCustomizer ? (QuerydslBinderCustomizer<EntityPath<?>>) it : null)//
.orElse(NoOpCustomizer.INSTANCE);
});
}
/**
@@ -171,15 +161,14 @@ public class QuerydslBindingsFactory implements ApplicationContextAware {
private QuerydslBinderCustomizer<EntityPath<?>> createQuerydslBinderCustomizer(
Class<? extends QuerydslBinderCustomizer> type) {
if (beanFactory == null) {
return BeanUtils.instantiateClass(type);
}
return beanFactory.map(it -> {
try {
return beanFactory.getBean(type);
} catch (NoSuchBeanDefinitionException e) {
return beanFactory.createBean(type);
}
try {
return it.getBean(type);
} catch (NoSuchBeanDefinitionException e) {
return it.createBean(type);
}
}).orElseGet(() -> BeanUtils.instantiateClass(type));
}
private static enum NoOpCustomizer implements QuerydslBinderCustomizer<EntityPath<?>> {

View File

@@ -16,6 +16,7 @@
package org.springframework.data.querydsl.binding;
import java.util.Collection;
import java.util.Optional;
import org.springframework.util.Assert;
@@ -46,13 +47,13 @@ class QuerydslDefaultBinding implements MultiValueBinding<Path<? extends Object>
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public Predicate bind(Path<?> path, Collection<? extends Object> value) {
public Optional<Predicate> bind(Path<?> path, Collection<? extends Object> value) {
Assert.notNull(path, "Path must not be null!");
Assert.notNull(value, "Value must not be null!");
if (value.isEmpty()) {
return null;
return Optional.empty();
}
if (path instanceof CollectionPathBase) {
@@ -63,16 +64,16 @@ class QuerydslDefaultBinding implements MultiValueBinding<Path<? extends Object>
builder.and(((CollectionPathBase) path).contains(element));
}
return builder.getValue();
return Optional.of(builder.getValue());
}
if (path instanceof SimpleExpression) {
if (value.size() > 1) {
return ((SimpleExpression) path).in(value);
return Optional.of(((SimpleExpression) path).in(value));
}
return ((SimpleExpression) path).eq(value.iterator().next());
return Optional.of(((SimpleExpression) path).eq(value.iterator().next()));
}
throw new IllegalArgumentException(

View File

@@ -38,7 +38,7 @@ public @interface QuerydslPredicate {
*
* @return
*/
Class<?>root() default Object.class;
Class<?> root() default Object.class;
/**
* To customize the way individual properties' values should be bound to the predicate a
@@ -49,5 +49,5 @@ public @interface QuerydslPredicate {
* @return
*/
@SuppressWarnings("rawtypes")
Class<? extends QuerydslBinderCustomizer>bindings() default QuerydslBinderCustomizer.class;
Class<? extends QuerydslBinderCustomizer> bindings() default QuerydslBinderCustomizer.class;
}

View File

@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import org.springframework.beans.PropertyValues;
import org.springframework.core.convert.ConversionService;
@@ -50,7 +51,7 @@ import com.querydsl.core.types.Predicate;
public class QuerydslPredicateBuilder {
private final ConversionService conversionService;
private final MultiValueBinding<?, ?> defaultBinding;
private final MultiValueBinding<Path<? extends Object>, Object> defaultBinding;
private final Map<PathInformation, Path<?>> paths;
private final EntityPathResolver resolver;
@@ -67,7 +68,7 @@ public class QuerydslPredicateBuilder {
this.defaultBinding = new QuerydslDefaultBinding();
this.conversionService = conversionService;
this.paths = new HashMap<PathInformation, Path<?>>();
this.paths = new HashMap<>();
this.resolver = resolver;
}
@@ -110,11 +111,9 @@ public class QuerydslPredicateBuilder {
}
Collection<Object> value = convertToPropertyPathSpecificType(entry.getValue(), propertyPath);
Predicate predicate = invokeBinding(propertyPath, bindings, value);
Optional<Predicate> predicate = invokeBinding(propertyPath, bindings, value);
if (predicate != null) {
builder.and(predicate);
}
predicate.ifPresent(it -> builder.and(it));
}
return builder.getValue();
@@ -128,15 +127,12 @@ public class QuerydslPredicateBuilder {
* @param values must not be {@literal null}.
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private Predicate invokeBinding(PathInformation dotPath, QuerydslBindings bindings, Collection<Object> values) {
private Optional<Predicate> invokeBinding(PathInformation dotPath, QuerydslBindings bindings,
Collection<Object> values) {
Path<?> path = getPath(dotPath, bindings);
MultiValueBinding binding = bindings.getBindingForPath(dotPath);
binding = binding == null ? defaultBinding : binding;
return binding.bind(path, values);
return bindings.getBindingForPath(dotPath).orElse(defaultBinding).bind(path, values);
}
/**
@@ -150,22 +146,9 @@ public class QuerydslPredicateBuilder {
*/
private Path<?> getPath(PathInformation path, QuerydslBindings bindings) {
Path<?> resolvedPath = bindings.getExistingPath(path);
Optional<Path<?>> resolvedPath = bindings.getExistingPath(path);
if (resolvedPath != null) {
return resolvedPath;
}
resolvedPath = paths.get(resolvedPath);
if (resolvedPath != null) {
return resolvedPath;
}
resolvedPath = path.reifyPath(resolver);
paths.put(path, resolvedPath);
return resolvedPath;
return resolvedPath.orElseGet(() -> paths.computeIfAbsent(path, it -> path.reifyPath(resolver)));
}
/**

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.querydsl.binding;
import java.util.Optional;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
@@ -38,5 +40,5 @@ public interface SingleValueBinding<T extends Path<? extends S>, S> {
* @return can be {@literal null}, in which case the binding will not be incorporated in the overall {@link Predicate}
* .
*/
Predicate bind(T path, S value);
Optional<Predicate> bind(T path, S value);
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.repository;
import java.io.Serializable;
import java.util.Optional;
/**
* Interface for generic CRUD operations on a repository for a specific type.
@@ -51,7 +52,7 @@ public interface CrudRepository<T, ID extends Serializable> extends Repository<T
* @return the entity with the given id or {@literal null} if none found
* @throws IllegalArgumentException if {@code id} is {@literal null}
*/
T findOne(ID id);
Optional<T> findOne(ID id);
/**
* Returns whether an entity with the given id exists.

View File

@@ -22,7 +22,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.spi.CreationalContext;
@@ -37,7 +39,6 @@ import javax.enterprise.inject.spi.PassivationCapable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.data.repository.config.CustomRepositoryImplementationDetector;
import org.springframework.data.repository.config.DefaultRepositoryConfiguration;
import org.springframework.util.Assert;
@@ -59,7 +60,7 @@ public abstract class CdiRepositoryBean<T> implements Bean<T>, PassivationCapabl
private final Set<Annotation> qualifiers;
private final Class<T> repositoryType;
private final CustomRepositoryImplementationDetector detector;
private final Optional<CustomRepositoryImplementationDetector> detector;
private final BeanManager beanManager;
private final String passivationId;
@@ -73,7 +74,7 @@ public abstract class CdiRepositoryBean<T> implements Bean<T>, PassivationCapabl
* @param beanManager the CDI {@link BeanManager}, must not be {@literal null}.
*/
public CdiRepositoryBean(Set<Annotation> qualifiers, Class<T> repositoryType, BeanManager beanManager) {
this(qualifiers, repositoryType, beanManager, null);
this(qualifiers, repositoryType, beanManager, Optional.empty());
}
/**
@@ -86,7 +87,7 @@ public abstract class CdiRepositoryBean<T> implements Bean<T>, PassivationCapabl
* can be {@literal null}.
*/
public CdiRepositoryBean(Set<Annotation> qualifiers, Class<T> repositoryType, BeanManager beanManager,
CustomRepositoryImplementationDetector detector) {
Optional<CustomRepositoryImplementationDetector> detector) {
Assert.notNull(qualifiers, "Qualifiers must not be null!");
Assert.notNull(beanManager, "BeanManager must not be null!");
@@ -141,13 +142,13 @@ public abstract class CdiRepositoryBean<T> implements Bean<T>, PassivationCapabl
* Returns an instance of an the given {@link Bean}.
*
* @param bean the {@link Bean} about to create an instance for.
* @param type the expected type of the componentn instance created for that {@link Bean}.
* @param type the expected type of the component instance created for that {@link Bean}.
* @return the actual component instance.
*/
@SuppressWarnings("unchecked")
protected <S> S getDependencyInstance(Bean<S> bean, Class<S> type) {
protected <S> S getDependencyInstance(Bean<S> bean) {
CreationalContext<S> creationalContext = beanManager.createCreationalContext(bean);
return (S) beanManager.getReference(bean, type, creationalContext);
return (S) beanManager.getReference(bean, bean.getBeanClass(), creationalContext);
}
/**
@@ -193,17 +194,11 @@ public abstract class CdiRepositoryBean<T> implements Bean<T>, PassivationCapabl
*
* @return an available CdiRepositoryConfiguration instance or a default configuration.
*/
@SuppressWarnings("unchecked")
protected CdiRepositoryConfiguration lookupConfiguration(BeanManager beanManager, Set<Annotation> qualifiers) {
Set<Bean<?>> beans = beanManager.getBeans(CdiRepositoryConfiguration.class, getQualifiersArray(qualifiers));
if (beans.isEmpty()) {
return DEFAULT_CONFIGURATION;
}
Bean<CdiRepositoryConfiguration> bean = (Bean<CdiRepositoryConfiguration>) beans.iterator().next();
return getDependencyInstance(bean, (Class<CdiRepositoryConfiguration>) bean.getBeanClass());
return beanManager.getBeans(CdiRepositoryConfiguration.class, getQualifiersArray(qualifiers)).stream().findFirst()//
.map(it -> (CdiRepositoryConfiguration) getDependencyInstance(it)) //
.orElse(DEFAULT_CONFIGURATION);
}
/**
@@ -215,49 +210,42 @@ public abstract class CdiRepositoryBean<T> implements Bean<T>, PassivationCapabl
* @param qualifiers
* @return the custom implementation instance or null
*/
private Bean<?> getCustomImplementationBean(Class<?> repositoryType, BeanManager beanManager,
private Optional<Bean<?>> getCustomImplementationBean(Class<?> repositoryType, BeanManager beanManager,
Set<Annotation> qualifiers) {
if (detector == null) {
return null;
}
return detector.flatMap(it -> {
CdiRepositoryConfiguration cdiRepositoryConfiguration = lookupConfiguration(beanManager, qualifiers);
Class<?> customImplementationClass = getCustomImplementationClass(repositoryType, cdiRepositoryConfiguration);
CdiRepositoryConfiguration cdiRepositoryConfiguration = lookupConfiguration(beanManager, qualifiers);
if (customImplementationClass == null) {
return null;
}
Set<Bean<?>> beans = beanManager.getBeans(customImplementationClass, getQualifiersArray(qualifiers));
return beans.isEmpty() ? null : beans.iterator().next();
return getCustomImplementationClass(repositoryType, cdiRepositoryConfiguration, it)//
.flatMap(type -> beanManager.getBeans(type, getQualifiersArray(qualifiers)).stream().findFirst());
});
}
/**
* Retrieves a custom repository interfaces from a repository type. This works for the whole class hierarchy and can
* find also a custom repo which is inherieted over many levels.
* find also a custom repository which is inherited over many levels.
*
* @param repositoryType The class representing the repository.
* @param cdiRepositoryConfiguration The configuration for CDI usage.
* @return the interface class or {@literal null}.
*/
private Class<?> getCustomImplementationClass(Class<?> repositoryType,
CdiRepositoryConfiguration cdiRepositoryConfiguration) {
private Optional<Class<?>> getCustomImplementationClass(Class<?> repositoryType,
CdiRepositoryConfiguration cdiRepositoryConfiguration, CustomRepositoryImplementationDetector detector) {
String className = getCustomImplementationClassName(repositoryType, cdiRepositoryConfiguration);
AbstractBeanDefinition beanDefinition = detector.detectCustomImplementation(className,
Collections.singleton(repositoryType.getPackage().getName()), Collections.<TypeFilter> emptySet());
Optional<AbstractBeanDefinition> beanDefinition = detector.detectCustomImplementation(className,
Collections.singleton(repositoryType.getPackage().getName()), Collections.emptySet());
if (beanDefinition == null) {
return null;
}
return beanDefinition.map(it -> {
try {
return Class.forName(beanDefinition.getBeanClassName());
} catch (ClassNotFoundException e) {
throw new UnsatisfiedResolutionException(
String.format("Unable to resolve class for '%s'", beanDefinition.getBeanClassName()), e);
}
try {
return Class.forName(it.getBeanClassName());
} catch (ClassNotFoundException e) {
throw new UnsatisfiedResolutionException(
String.format("Unable to resolve class for '%s'", it.getBeanClassName()), e);
}
});
}
private String getCustomImplementationClassName(Class<?> repositoryType,
@@ -295,16 +283,10 @@ public abstract class CdiRepositoryBean<T> implements Bean<T>, PassivationCapabl
*/
public Set<Class<? extends Annotation>> getStereotypes() {
Set<Class<? extends Annotation>> stereotypes = new HashSet<Class<? extends Annotation>>();
for (Annotation annotation : repositoryType.getAnnotations()) {
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType.isAnnotationPresent(Stereotype.class)) {
stereotypes.add(annotationType);
}
}
return stereotypes;
return Arrays.stream(repositoryType.getAnnotations())//
.map(it -> it.annotationType())//
.filter(it -> it.isAnnotationPresent(Stereotype.class))//
.collect(Collectors.toSet());
}
/*
@@ -361,15 +343,12 @@ public abstract class CdiRepositoryBean<T> implements Bean<T>, PassivationCapabl
* @param creationalContext will never be {@literal null}.
* @param repositoryType will never be {@literal null}.
* @return
* @deprecated overide {@link #create(CreationalContext, Class, Object)} instead.
*/
@Deprecated
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType) {
private final T create(CreationalContext<T> creationalContext, Class<T> repositoryType) {
Bean<?> customImplementationBean = getCustomImplementationBean(repositoryType, beanManager, qualifiers);
Object customImplementation = customImplementationBean == null ? null
: beanManager.getReference(customImplementationBean, customImplementationBean.getBeanClass(),
beanManager.createCreationalContext(customImplementationBean));
Optional<Bean<?>> customImplementationBean = getCustomImplementationBean(repositoryType, beanManager, qualifiers);
Optional<Object> customImplementation = customImplementationBean
.map(it -> beanManager.getReference(it, it.getBeanClass(), beanManager.createCreationalContext(it)));
return create(creationalContext, repositoryType, customImplementation);
}
@@ -382,9 +361,11 @@ public abstract class CdiRepositoryBean<T> implements Bean<T>, PassivationCapabl
* @param customImplementation can be {@literal null}.
* @return
*/
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType, Object customImplementation) {
throw new UnsupportedOperationException("You have to implement create(CreationalContext<T>, Class<T>, Object) "
+ "in order to use custom repository implementations");
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType,
Optional<Object> customImplementation) {
throw new UnsupportedOperationException(
"You have to implement create(CreationalContext<T>, Class<T>, Optional<Object>) "
+ "in order to use custom repository implementations");
}
/*

View File

@@ -26,7 +26,7 @@ public interface CdiRepositoryConfiguration {
/**
* Returns the configured postfix to be used for looking up custom implementation classes.
*
* @return the postfix to use or {@literal null} in case none is configured.
* @return the postfix to use, must not be {@literal null}.
*/
String getRepositoryImplementationPostfix();
}

View File

@@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
@@ -138,15 +139,15 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getQueryLookupStrategyKey()
*/
public Object getQueryLookupStrategyKey() {
return attributes.get(QUERY_LOOKUP_STRATEGY);
public Optional<Object> getQueryLookupStrategyKey() {
return Optional.ofNullable(attributes.get(QUERY_LOOKUP_STRATEGY));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getNamedQueryLocation()
*/
public String getNamedQueryLocation() {
public Optional<String> getNamedQueryLocation() {
return getNullDefaultedAttribute(NAMED_QUERIES_LOCATION);
}
@@ -154,7 +155,7 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getRepositoryImplementationPostfix()
*/
public String getRepositoryImplementationPostfix() {
public Optional<String> getRepositoryImplementationPostfix() {
return getNullDefaultedAttribute(REPOSITORY_IMPLEMENTATION_POSTFIX);
}
@@ -197,14 +198,15 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
}
/**
* Returns the {@link String} attribute with the given name and defaults it to {@literal null} in case it's empty.
* Returns the {@link String} attribute with the given name and defaults it to {@literal Optional#empty()} in case
* it's empty.
*
* @param attributeName
* @return
*/
private String getNullDefaultedAttribute(String attributeName) {
private Optional<String> getNullDefaultedAttribute(String attributeName) {
String attribute = attributes.getString(attributeName);
return StringUtils.hasText(attribute) ? attribute : null;
return StringUtils.hasText(attribute) ? Optional.of(attribute) : Optional.empty();
}
/*
@@ -220,14 +222,15 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getRepositoryBaseClassName()
*/
@Override
public String getRepositoryBaseClassName() {
public Optional<String> getRepositoryBaseClassName() {
if (!attributes.containsKey(REPOSITORY_BASE_CLASS)) {
return null;
return Optional.empty();
}
Class<? extends Object> repositoryBaseClass = attributes.getClass(REPOSITORY_BASE_CLASS);
return DefaultRepositoryBaseClass.class.equals(repositoryBaseClass) ? null : repositoryBaseClass.getName();
return DefaultRepositoryBaseClass.class.equals(repositoryBaseClass) ? Optional.empty()
: Optional.of(repositoryBaseClass.getName());
}
/**
@@ -311,10 +314,10 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getAttribute(java.lang.String)
*/
@Override
public String getAttribute(String name) {
public Optional<String> getAttribute(String name) {
String attribute = attributes.getString(name);
return StringUtils.hasText(attribute) ? attribute : null;
return StringUtils.hasText(attribute) ? Optional.of(attribute) : Optional.empty();
}
/*

View File

@@ -16,11 +16,14 @@
package org.springframework.data.repository.config;
import java.util.ArrayList;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
@@ -31,7 +34,6 @@ import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.RegexPatternTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Detects the custom implementation for a {@link org.springframework.data.repository.Repository}
@@ -40,34 +42,14 @@ import org.springframework.util.StringUtils;
* @author Mark Paluch
* @author Peter Rietzler
*/
@RequiredArgsConstructor
public class CustomRepositoryImplementationDetector {
private static final String CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN = "**/*%s.class";
private final MetadataReaderFactory metadataReaderFactory;
private final Environment environment;
private final ResourceLoader resourceLoader;
/**
* Creates a new {@link CustomRepositoryImplementationDetector} from the given
* {@link org.springframework.core.type.classreading.MetadataReaderFactory},
* {@link org.springframework.core.env.Environment} and {@link org.springframework.core.io.ResourceLoader}.
*
* @param metadataReaderFactory must not be {@literal null}.
* @param environment must not be {@literal null}.
* @param resourceLoader must not be {@literal null}.
*/
public CustomRepositoryImplementationDetector(MetadataReaderFactory metadataReaderFactory, Environment environment,
ResourceLoader resourceLoader) {
Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null!");
Assert.notNull(resourceLoader, "ResourceLoader must not be null!");
Assert.notNull(environment, "Environment must not be null!");
this.metadataReaderFactory = metadataReaderFactory;
this.environment = environment;
this.resourceLoader = resourceLoader;
}
private final @NonNull MetadataReaderFactory metadataReaderFactory;
private final @NonNull Environment environment;
private final @NonNull ResourceLoader resourceLoader;
/**
* Tries to detect a custom implementation for a repository bean by classpath scanning.
@@ -75,7 +57,7 @@ public class CustomRepositoryImplementationDetector {
* @param configuration the {@link RepositoryConfiguration} to consider.
* @return the {@code AbstractBeanDefinition} of the custom implementation or {@literal null} if none found.
*/
public AbstractBeanDefinition detectCustomImplementation(RepositoryConfiguration<?> configuration) {
public Optional<AbstractBeanDefinition> detectCustomImplementation(RepositoryConfiguration<?> configuration) {
// TODO 2.0: Extract into dedicated interface for custom implementation lookup configuration.
@@ -91,7 +73,7 @@ public class CustomRepositoryImplementationDetector {
* @param basePackages must not be {@literal null}.
* @return the {@code AbstractBeanDefinition} of the custom implementation or {@literal null} if none found.
*/
public AbstractBeanDefinition detectCustomImplementation(String className, Iterable<String> basePackages,
public Optional<AbstractBeanDefinition> detectCustomImplementation(String className, Iterable<String> basePackages,
Iterable<TypeFilter> excludeFilters) {
Assert.notNull(className, "ClassName must not be null!");
@@ -119,20 +101,17 @@ public class CustomRepositoryImplementationDetector {
}
if (definitions.isEmpty()) {
return null;
return Optional.empty();
}
if (definitions.size() == 1) {
return (AbstractBeanDefinition) definitions.iterator().next();
}
List<String> implementationClassNames = new ArrayList<String>();
for (BeanDefinition bean : definitions) {
implementationClassNames.add(bean.getBeanClassName());
return Optional.of((AbstractBeanDefinition) definitions.iterator().next());
}
throw new IllegalStateException(
String.format("Ambiguous custom implementations detected! Found %s but expected a single implementation!",
StringUtils.collectionToCommaDelimitedString(implementationClassNames)));
String.format("Ambiguous custom implementations detected! Found %s but expected a single implementation!", //
definitions.stream()//
.map(it -> it.getBeanClassName())//
.collect(Collectors.joining(", "))));
}
}

View File

@@ -15,10 +15,14 @@
*/
package org.springframework.data.repository.config;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.Optional;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -27,37 +31,23 @@ import org.springframework.util.StringUtils;
*
* @author Oliver Gierke
*/
@RequiredArgsConstructor
public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSource>
implements RepositoryConfiguration<T> {
public static final String DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX = "Impl";
private static final Key DEFAULT_QUERY_LOOKUP_STRATEGY = Key.CREATE_IF_NOT_FOUND;
private final T configurationSource;
private final BeanDefinition definition;
/**
* Creates a new {@link DefaultRepositoryConfiguration} from the given {@link RepositoryConfigurationSource} and
* source {@link BeanDefinition}.
*
* @param configurationSource must not be {@literal null}.
* @param definition must not be {@literal null}.
*/
public DefaultRepositoryConfiguration(T configurationSource, BeanDefinition definition) {
Assert.notNull(configurationSource, "ConfigurationSource must not be null!");
Assert.notNull(definition, "BeanDefinition must not be null!");
this.configurationSource = configurationSource;
this.definition = definition;
}
private final @NonNull T configurationSource;
private final @NonNull BeanDefinition definition;
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfiguration#getBeanId()
*/
public String getBeanId() {
return StringUtils.uncapitalize(ClassUtils.getShortName(getRepositoryFactoryBeanName()));
return StringUtils.uncapitalize(ClassUtils.getShortName(getRepositoryBaseClassName().orElseThrow(
() -> new IllegalStateException("Can't create bean identifier without a repository base class defined!"))));
}
/*
@@ -65,9 +55,7 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
* @see org.springframework.data.repository.config.RepositoryConfiguration#getQueryLookupStrategyKey()
*/
public Object getQueryLookupStrategyKey() {
Object configuredStrategy = configurationSource.getQueryLookupStrategyKey();
return configuredStrategy != null ? configuredStrategy : DEFAULT_QUERY_LOOKUP_STRATEGY;
return configurationSource.getQueryLookupStrategyKey().orElse(DEFAULT_QUERY_LOOKUP_STRATEGY);
}
/*
@@ -97,7 +85,7 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
/* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfiguration#getNamedQueryLocation()
*/
public String getNamedQueriesLocation() {
public Optional<String> getNamedQueriesLocation() {
return configurationSource.getNamedQueryLocation();
}
@@ -106,7 +94,8 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
* @see org.springframework.data.repository.config.RepositoryConfiguration#getImplementationClassName()
*/
public String getImplementationClassName() {
return ClassUtils.getShortName(getRepositoryInterface()) + getImplementationPostfix();
return ClassUtils.getShortName(getRepositoryInterface()).concat(
configurationSource.getRepositoryImplementationPostfix().orElse(DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX));
}
/*
@@ -117,20 +106,11 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
return StringUtils.uncapitalize(getImplementationClassName());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfiguration#getImplementationPostfix()
*/
public String getImplementationPostfix() {
String configuredPostfix = configurationSource.getRepositoryImplementationPostfix();
return StringUtils.hasText(configuredPostfix) ? configuredPostfix : DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfiguration#getSource()
*/
@Override
public Object getSource() {
return configurationSource.getSource();
}
@@ -139,24 +119,17 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfiguration#getConfigurationSource()
*/
@Override
public T getConfigurationSource() {
return configurationSource;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfiguration#getRepositoryFactoryBeanName()
*/
public String getRepositoryFactoryBeanName() {
return configurationSource.getRepositoryFactoryBeanName();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfiguration#getRepositoryBaseClassName()
*/
@Override
public String getRepositoryBaseClassName() {
public Optional<String> getRepositoryBaseClassName() {
return configurationSource.getRepositoryBaseClassName();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2017 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.repository.config;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
@@ -27,7 +29,6 @@ import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.data.repository.query.ExtensionAwareEvaluationContextProvider;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Builder to create {@link BeanDefinitionBuilder} instance to eventually create Spring Data repository instances.
@@ -44,7 +45,7 @@ class RepositoryBeanDefinitionBuilder {
private final ResourceLoader resourceLoader;
private final MetadataReaderFactory metadataReaderFactory;
private CustomRepositoryImplementationDetector implementationDetector;
private final CustomRepositoryImplementationDetector implementationDetector;
/**
* Creates a new {@link RepositoryBeanDefinitionBuilder} from the given {@link BeanDefinitionRegistry},
@@ -82,11 +83,7 @@ class RepositoryBeanDefinitionBuilder {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
Assert.notNull(resourceLoader, "ResourceLoader must not be null!");
String factoryBeanName = configuration.getRepositoryFactoryBeanName();
factoryBeanName = StringUtils.hasText(factoryBeanName) ? factoryBeanName
: extension.getRepositoryFactoryClassName();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(factoryBeanName);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(extension.getRepositoryFactoryClassName());
builder.getRawBeanDefinition().setSource(configuration.getSource());
builder.addConstructorArgValue(configuration.getRepositoryInterface());
@@ -96,19 +93,16 @@ class RepositoryBeanDefinitionBuilder {
NamedQueriesBeanDefinitionBuilder definitionBuilder = new NamedQueriesBeanDefinitionBuilder(
extension.getDefaultNamedQueryLocation());
if (StringUtils.hasText(configuration.getNamedQueriesLocation())) {
definitionBuilder.setLocations(configuration.getNamedQueriesLocation());
}
configuration.getNamedQueriesLocation().ifPresent(it -> definitionBuilder.setLocations(it));
builder.addPropertyValue("namedQueries", definitionBuilder.build(configuration.getSource()));
String customImplementationBeanName = registerCustomImplementation(configuration);
Optional<String> customImplementationBeanName = registerCustomImplementation(configuration);
if (customImplementationBeanName != null) {
builder.addPropertyReference("customImplementation", customImplementationBeanName);
builder.addDependsOn(customImplementationBeanName);
}
customImplementationBeanName.ifPresent(it -> {
builder.addPropertyReference("customImplementation", it);
builder.addDependsOn(it);
});
RootBeanDefinition evaluationContextProviderDefinition = new RootBeanDefinition(
ExtensionAwareEvaluationContextProvider.class);
@@ -119,30 +113,30 @@ class RepositoryBeanDefinitionBuilder {
return builder;
}
private String registerCustomImplementation(RepositoryConfiguration<?> configuration) {
private Optional<String> registerCustomImplementation(RepositoryConfiguration<?> configuration) {
String beanName = configuration.getImplementationBeanName();
// Already a bean configured?
if (registry.containsBeanDefinition(beanName)) {
return Optional.of(beanName);
}
Optional<AbstractBeanDefinition> beanDefinition = implementationDetector.detectCustomImplementation(
configuration.getImplementationClassName(), configuration.getBasePackages(), configuration.getExcludeFilters());
return beanDefinition.map(it -> {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registering custom repository implementation: " + configuration.getImplementationBeanName() + " "
+ it.getBeanClassName());
}
it.setSource(configuration.getSource());
registry.registerBeanDefinition(beanName, it);
return beanName;
}
AbstractBeanDefinition beanDefinition = implementationDetector.detectCustomImplementation(configuration);
if (null == beanDefinition) {
return null;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registering custom repository implementation: " + configuration.getImplementationBeanName() + " "
+ beanDefinition.getBeanClassName());
}
beanDefinition.setSource(configuration.getSource());
registry.registerBeanDefinition(beanName, beanDefinition);
return beanName;
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2015 the original author or authors.
* Copyright 2008-2017 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.
@@ -65,7 +65,8 @@ public class RepositoryBeanDefinitionParser implements BeanDefinitionParser {
ResourceLoader resourceLoader = readerContext.getResourceLoader();
BeanDefinitionRegistry registry = parser.getRegistry();
XmlRepositoryConfigurationSource configSource = new XmlRepositoryConfigurationSource(element, parser, environment);
XmlRepositoryConfigurationSource configSource = new XmlRepositoryConfigurationSource(element, parser,
environment);
RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configSource, resourceLoader,
environment);

View File

@@ -15,7 +15,8 @@
*/
package org.springframework.data.repository.config;
import org.springframework.beans.factory.FactoryBean;
import java.util.Optional;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.data.repository.query.QueryLookupStrategy;
@@ -53,7 +54,7 @@ public interface RepositoryConfiguration<T extends RepositoryConfigurationSource
*
* @return
*/
String getNamedQueriesLocation();
Optional<String> getNamedQueriesLocation();
/**
* Returns the class name of the custom implementation.
@@ -69,15 +70,6 @@ public interface RepositoryConfiguration<T extends RepositoryConfigurationSource
*/
String getImplementationBeanName();
/**
* Returns the name of the {@link FactoryBean} class to be used to create repository instances.
*
* @return
* @deprecated as of 1.11 in favor of a dedicated repository class name, see {@link #getRepositoryBaseClassName()}.
*/
@Deprecated
String getRepositoryFactoryBeanName();
/**
* Returns the name of the repository base class to be used or {@literal null} if the store specific defaults shall be
* applied.
@@ -85,7 +77,7 @@ public interface RepositoryConfiguration<T extends RepositoryConfigurationSource
* @return
* @since 1.11
*/
String getRepositoryBaseClassName();
Optional<String> getRepositoryBaseClassName();
/**
* Returns the source of the {@link RepositoryConfiguration}.

View File

@@ -16,8 +16,8 @@
package org.springframework.data.repository.config;
import java.util.Collection;
import java.util.Optional;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.filter.TypeFilter;
@@ -52,29 +52,19 @@ public interface RepositoryConfigurationSource {
*
* @return
*/
Object getQueryLookupStrategyKey();
Optional<Object> getQueryLookupStrategyKey();
/**
* Returns the configured postfix to be used for looking up custom implementation classes.
*
* @return the postfix to use or {@literal null} in case none is configured.
*/
String getRepositoryImplementationPostfix();
Optional<String> getRepositoryImplementationPostfix();
/**
* @return
*/
String getNamedQueryLocation();
/**
* Returns the name of the class of the {@link FactoryBean} to actually create repository instances.
*
* @return
* @deprecated as of 1.11 in favor of using a dedicated repository base class name, see
* {@link #getRepositoryBaseClassName()}.
*/
@Deprecated
String getRepositoryFactoryBeanName();
Optional<String> getNamedQueryLocation();
/**
* Returns the name of the repository base class to be used or {@literal null} if the store specific defaults shall be
@@ -83,7 +73,7 @@ public interface RepositoryConfigurationSource {
* @return
* @since 1.11
*/
String getRepositoryBaseClassName();
Optional<String> getRepositoryBaseClassName();
/**
* Returns the source {@link BeanDefinition}s of the repository interfaces to create repository instances for.
@@ -101,7 +91,7 @@ public interface RepositoryConfigurationSource {
* @return the attribute with the given name or {@literal null} if not configured or empty.
* @since 1.8
*/
String getAttribute(String name);
Optional<String> getAttribute(String name);
/**
* Returns whether the configuration uses explicit filtering to scan for repository types.

View File

@@ -17,6 +17,7 @@ package org.springframework.data.repository.config;
import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.env.Environment;
@@ -96,15 +97,15 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getQueryLookupStrategyKey()
*/
public Object getQueryLookupStrategyKey() {
return QueryLookupStrategy.Key.create(getNullDefaultedAttribute(element, QUERY_LOOKUP_STRATEGY));
public Optional<Object> getQueryLookupStrategyKey() {
return getNullDefaultedAttribute(element, QUERY_LOOKUP_STRATEGY).map(it -> QueryLookupStrategy.Key.create(it));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getNamedQueryLocation()
*/
public String getNamedQueryLocation() {
public Optional<String> getNamedQueryLocation() {
return getNullDefaultedAttribute(element, NAMED_QUERIES_LOCATION);
}
@@ -138,7 +139,7 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou
/* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getRepositoryImplementationPostfix()
*/
public String getRepositoryImplementationPostfix() {
public Optional<String> getRepositoryImplementationPostfix() {
return getNullDefaultedAttribute(element, REPOSITORY_IMPL_POSTFIX);
}
@@ -146,7 +147,7 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getRepositoryFactoryBeanName()
*/
public String getRepositoryFactoryBeanName() {
public Optional<String> getRepositoryFactoryBeanName() {
return getNullDefaultedAttribute(element, REPOSITORY_FACTORY_BEAN_CLASS_NAME);
}
@@ -155,13 +156,14 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getRepositoryBaseClassName()
*/
@Override
public String getRepositoryBaseClassName() {
public Optional<String> getRepositoryBaseClassName() {
return getNullDefaultedAttribute(element, REPOSITORY_BASE_CLASS_NAME);
}
private String getNullDefaultedAttribute(Element element, String attributeName) {
private Optional<String> getNullDefaultedAttribute(Element element, String attributeName) {
String attribute = element.getAttribute(attributeName);
return StringUtils.hasText(attribute) ? attribute : null;
return StringUtils.hasText(attribute) ? Optional.of(attribute) : Optional.empty();
}
/*
@@ -171,8 +173,8 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou
@Override
public boolean shouldConsiderNestedRepositories() {
String attribute = getNullDefaultedAttribute(element, CONSIDER_NESTED_REPOSITORIES);
return attribute != null && Boolean.parseBoolean(attribute);
return getNullDefaultedAttribute(element, CONSIDER_NESTED_REPOSITORIES).map(it -> Boolean.parseBoolean(it))
.orElse(false);
}
/*
@@ -180,12 +182,12 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getAttribute(java.lang.String)
*/
@Override
public String getAttribute(String name) {
public Optional<String> getAttribute(String name) {
String xmlAttributeName = ParsingUtils.reconcatenateCamelCase(name, "-");
String attribute = element.getAttribute(xmlAttributeName);
return StringUtils.hasText(attribute) ? attribute : null;
return StringUtils.hasText(attribute) ? Optional.of(attribute) : Optional.empty();
}
/*

View File

@@ -17,6 +17,8 @@ package org.springframework.data.repository.core;
import java.lang.reflect.Method;
import org.springframework.data.util.Streamable;
/**
* Aditional repository specific information
*
@@ -69,7 +71,7 @@ public interface RepositoryInformation extends RepositoryMetadata {
*
* @return
*/
Iterable<Method> getQueryMethods();
Streamable<Method> getQueryMethods();
/**
* Returns the target class method that is backing the given method. This can be necessary if a repository interface
@@ -81,4 +83,8 @@ public interface RepositoryInformation extends RepositoryMetadata {
* @return
*/
Method getTargetClassMethod(Method method);
default boolean hasQueryMethods() {
return getQueryMethods().iterator().hasNext();
}
}

View File

@@ -78,7 +78,6 @@ public class DefaultCrudMethods implements CrudMethods {
* @param metadata must not be {@literal null}.
* @return the most suitable method or {@literal null} if no method could be found.
*/
@SuppressWarnings("unchecked")
private Method selectMostSuitableSaveMethod(RepositoryMetadata metadata) {
for (Class<?> type : asList(metadata.getDomainType(), Object.class)) {
@@ -105,7 +104,6 @@ public class DefaultCrudMethods implements CrudMethods {
* @param metadata must not be {@literal null}.
* @return the most suitable method or {@literal null} if no method could be found.
*/
@SuppressWarnings("unchecked")
private Method selectMostSuitableDeleteMethod(RepositoryMetadata metadata) {
for (Class<?> type : asList(metadata.getDomainType(), metadata.getIdType(), Serializable.class, Iterable.class)) {
@@ -131,7 +129,6 @@ public class DefaultCrudMethods implements CrudMethods {
* @param metadata must not be {@literal null}.
* @return the most suitable method or {@literal null} if no method could be found.
*/
@SuppressWarnings("unchecked")
private Method selectMostSuitableFindAllMethod(RepositoryMetadata metadata) {
for (Class<?> type : asList(Pageable.class, Sort.class)) {
@@ -163,7 +160,6 @@ public class DefaultCrudMethods implements CrudMethods {
* @param metadata must not be {@literal null}.
* @return the most suitable method or {@literal null} if no method could be found.
*/
@SuppressWarnings("unchecked")
private Method selectMostSuitableFindOneMethod(RepositoryMetadata metadata) {
for (Class<?> type : asList(metadata.getIdType(), Serializable.class)) {

View File

@@ -25,9 +25,11 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -40,6 +42,7 @@ import org.springframework.data.repository.core.CrudMethods;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.data.util.Streamable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -61,17 +64,17 @@ class DefaultRepositoryInformation implements RepositoryInformation {
private final RepositoryMetadata metadata;
private final Class<?> repositoryBaseClass;
private final Class<?> customImplementationClass;
private final Optional<Class<?>> customImplementationClass;
/**
* Creates a new {@link DefaultRepositoryMetadata} for the given repository interface and repository base class.
*
* @param metadata must not be {@literal null}.
* @param repositoryBaseClass must not be {@literal null}.
* @param customImplementationClass
* @param customImplementationClass must not be {@literal null}.
*/
public DefaultRepositoryInformation(RepositoryMetadata metadata, Class<?> repositoryBaseClass,
Class<?> customImplementationClass) {
Optional<Class<?>> customImplementationClass) {
Assert.notNull(metadata, "Metadata must not be null!");
Assert.notNull(repositoryBaseClass, "RepositoryBaseClass must not be null!");
@@ -125,7 +128,7 @@ class DefaultRepositoryInformation implements RepositoryInformation {
return cacheAndReturn(method, result);
}
return cacheAndReturn(method, getTargetClassMethod(method, repositoryBaseClass));
return cacheAndReturn(method, getTargetClassMethod(method, Optional.of(repositoryBaseClass)));
}
private Method cacheAndReturn(Method key, Method value) {
@@ -144,19 +147,12 @@ class DefaultRepositoryInformation implements RepositoryInformation {
* @param method
* @return
*/
private boolean isTargetClassMethod(Method method, Class<?> targetType) {
private boolean isTargetClassMethod(Method method, Optional<Class<?>> targetType) {
Assert.notNull(method, "Method must not be null!");
if (targetType == null) {
return false;
}
if (method.getDeclaringClass().isAssignableFrom(targetType)) {
return true;
}
return !method.equals(getTargetClassMethod(method, targetType));
return targetType.map(it -> method.getDeclaringClass().isAssignableFrom(it)
|| !method.equals(getTargetClassMethod(method, targetType))).orElse(false);
}
/*
@@ -164,7 +160,7 @@ class DefaultRepositoryInformation implements RepositoryInformation {
* @see org.springframework.data.repository.support.RepositoryInformation#getQueryMethods()
*/
@Override
public Set<Method> getQueryMethods() {
public Streamable<Method> getQueryMethods() {
Set<Method> result = new HashSet<Method>();
@@ -175,7 +171,7 @@ class DefaultRepositoryInformation implements RepositoryInformation {
}
}
return Collections.unmodifiableSet(result);
return Streamable.of(Collections.unmodifiableSet(result));
}
/**
@@ -217,7 +213,7 @@ class DefaultRepositoryInformation implements RepositoryInformation {
*/
@Override
public boolean isQueryMethod(Method method) {
return getQueryMethods().contains(method);
return getQueryMethods().stream().anyMatch(it -> it.equals(method));
}
/*
@@ -228,7 +224,7 @@ class DefaultRepositoryInformation implements RepositoryInformation {
public boolean isBaseClassMethod(Method method) {
Assert.notNull(method, "Method must not be null!");
return isTargetClassMethod(method, repositoryBaseClass);
return isTargetClassMethod(method, Optional.of(repositoryBaseClass));
}
/**
@@ -240,39 +236,16 @@ class DefaultRepositoryInformation implements RepositoryInformation {
* @param baseClass
* @return
*/
Method getTargetClassMethod(Method method, Class<?> baseClass) {
if (baseClass == null) {
return method;
}
Method result = findMethod(baseClass, method.getName(), method.getParameterTypes());
if (result != null) {
return result;
}
for (Method baseClassMethod : baseClass.getMethods()) {
// Wrong name
if (!method.getName().equals(baseClassMethod.getName())) {
continue;
}
// Wrong number of arguments
if (!(method.getParameterTypes().length == baseClassMethod.getParameterTypes().length)) {
continue;
}
// Check whether all parameters match
if (!parametersMatch(method, baseClassMethod)) {
continue;
}
return baseClassMethod;
}
return method;
Method getTargetClassMethod(Method method, Optional<Class<?>> baseClass) {
return baseClass.map(it -> {
return Optional.ofNullable(findMethod(it, method.getName(), method.getParameterTypes())).orElseGet(() -> {
return Arrays.stream(it.getMethods())//
.filter(baseClassMethod -> method.getName().equals(baseClassMethod.getName()))// Right name
.filter(baseClassMethod -> method.getParameterTypes().length == baseClassMethod.getParameterTypes().length)
.filter(baseClassMethod -> parametersMatch(method, baseClassMethod))// All parameters match
.findFirst().orElse(method);
});
}).orElse(method);
}
/*

View File

@@ -48,7 +48,7 @@ public class PersistentEntityInformation<T, ID extends Serializable> extends Abs
*/
@Override
public ID getId(T entity) {
return (ID) persistentEntity.getIdentifierAccessor(entity).getIdentifier();
return (ID) persistentEntity.getIdentifierAccessor(entity).getIdentifier().orElse(null);
}
/*
@@ -57,6 +57,6 @@ public class PersistentEntityInformation<T, ID extends Serializable> extends Abs
*/
@Override
public Class<ID> getIdType() {
return (Class<ID>) persistentEntity.getIdProperty().getType();
return (Class<ID>) persistentEntity.getIdProperty().map(it -> it.getType()).orElse(null);
}
}

View File

@@ -23,6 +23,7 @@ import lombok.RequiredArgsConstructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.BiPredicate;
import org.springframework.core.MethodParameter;
@@ -53,7 +54,7 @@ public class ReactiveRepositoryInformation extends DefaultRepositoryInformation
* @param customImplementationClass can be {@literal null}.
*/
public ReactiveRepositoryInformation(RepositoryMetadata metadata, Class<?> repositoryBaseClass,
Class<?> customImplementationClass) {
Optional<Class<?>> customImplementationClass) {
super(metadata, repositoryBaseClass, customImplementationClass);
}
@@ -67,31 +68,31 @@ public class ReactiveRepositoryInformation extends DefaultRepositoryInformation
* @return
*/
@Override
Method getTargetClassMethod(Method method, Class<?> baseClass) {
Method getTargetClassMethod(Method method, Optional<Class<?>> baseClass) {
if (baseClass == null) {
return method;
}
return baseClass.map(it -> {
if (usesParametersWithReactiveWrappers(method)) {
if (usesParametersWithReactiveWrappers(method)) {
Method candidate = getMethodCandidate(method, baseClass, new AssignableWrapperMatch(method.getParameterTypes()));
Method candidate = getMethodCandidate(method, it, new AssignableWrapperMatch(method.getParameterTypes()));
if (candidate != null) {
return candidate;
if (candidate != null) {
return candidate;
}
candidate = getMethodCandidate(method, it, WrapperConversionMatch.of(method.getParameterTypes()));
if (candidate != null) {
return candidate;
}
}
candidate = getMethodCandidate(method, baseClass, WrapperConversionMatch.of(method.getParameterTypes()));
Method candidate = getMethodCandidate(method, it,
MatchParameterOrComponentType.of(method, getRepositoryInterface()));
if (candidate != null) {
return candidate;
}
}
return candidate != null ? candidate : method;
Method candidate = getMethodCandidate(method, baseClass,
MatchParameterOrComponentType.of(method, getRepositoryInterface()));
return candidate != null ? candidate : method;
}).orElse(method);
}
/**

View File

@@ -17,6 +17,7 @@ package org.springframework.data.repository.core.support;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
@@ -38,6 +39,7 @@ import org.springframework.data.repository.query.EvaluationContextProvider;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.util.Lazy;
import org.springframework.util.Assert;
/**
@@ -54,19 +56,18 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
private final Class<? extends T> repositoryInterface;
private RepositoryFactorySupport factory;
private Key queryLookupStrategyKey;
private Class<?> repositoryBaseClass;
private Object customImplementation;
private Optional<Class<?>> repositoryBaseClass = Optional.empty();
private Optional<Object> customImplementation = Optional.empty();
private NamedQueries namedQueries;
private MappingContext<?, ?> mappingContext;
private Optional<MappingContext<?, ?>> mappingContext;
private ClassLoader classLoader;
private BeanFactory beanFactory;
private boolean lazyInit = false;
private EvaluationContextProvider evaluationContextProvider = DefaultEvaluationContextProvider.INSTANCE;
private ApplicationEventPublisher publisher;
private T repository;
private Lazy<T> repository;
private RepositoryMetadata repositoryMetadata;
@@ -88,7 +89,7 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
* @since 1.11
*/
public void setRepositoryBaseClass(Class<?> repositoryBaseClass) {
this.repositoryBaseClass = repositoryBaseClass;
this.repositoryBaseClass = Optional.ofNullable(repositoryBaseClass);
}
/**
@@ -106,7 +107,7 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
* @param customImplementation
*/
public void setCustomImplementation(Object customImplementation) {
this.customImplementation = customImplementation;
this.customImplementation = Optional.ofNullable(customImplementation);
}
/**
@@ -125,7 +126,7 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
* @param mappingContext
*/
protected void setMappingContext(MappingContext<?, ?> mappingContext) {
this.mappingContext = mappingContext;
this.mappingContext = Optional.ofNullable(mappingContext);
}
/**
@@ -142,7 +143,7 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
/**
* Configures whether to initialize the repository proxy lazily. This defaults to {@literal false}.
*
* @param lazyInit whether to initialize the repository proxy lazily. This defaults to {@literal false}.
* @param lazy whether to initialize the repository proxy lazily. This defaults to {@literal false}.
*/
public void setLazyInit(boolean lazy) {
this.lazyInit = lazy;
@@ -181,7 +182,6 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
*/
@SuppressWarnings("unchecked")
public EntityInformation<S, ID> getEntityInformation() {
return (EntityInformation<S, ID>) factory.getEntityInformation(repositoryMetadata.getDomainType());
}
@@ -190,9 +190,7 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
* @see org.springframework.data.repository.core.support.RepositoryFactoryInformation#getRepositoryInformation()
*/
public RepositoryInformation getRepositoryInformation() {
return this.factory.getRepositoryInformation(repositoryMetadata,
customImplementation == null ? null : customImplementation.getClass());
return this.factory.getRepositoryInformation(repositoryMetadata, customImplementation.map(Object::getClass));
}
/*
@@ -201,11 +199,9 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
*/
public PersistentEntity<?, ?> getPersistentEntity() {
if (mappingContext == null) {
return null;
}
return mappingContext.getPersistentEntity(repositoryMetadata.getDomainType());
return mappingContext//
.map(context -> context.getPersistentEntity(repositoryMetadata.getDomainType()))//
.orElseGet(null);
}
/* (non-Javadoc)
@@ -220,7 +216,7 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
public T getObject() {
return initAndReturn();
return this.repository.get();
}
/*
@@ -249,7 +245,6 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
this.factory.setNamedQueries(namedQueries);
this.factory.setEvaluationContextProvider(evaluationContextProvider);
this.factory.setRepositoryBaseClass(repositoryBaseClass);
this.factory.setBeanClassLoader(classLoader);
this.factory.setBeanFactory(beanFactory);
@@ -257,29 +252,16 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
}
repositoryBaseClass.ifPresent(it -> this.factory.setRepositoryBaseClass(it));
this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface);
this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, customImplementation));
if (!lazyInit) {
initAndReturn();
this.repository.get();
}
}
/**
* Returns the previously initialized repository proxy or creates and returns the proxy if previously uninitialized.
*
* @return
*/
private T initAndReturn() {
Assert.notNull(repositoryInterface, "Repository interface must not be null on initialization!");
if (this.repository == null) {
this.repository = this.factory.getRepository(repositoryInterface, customImplementation);
}
return this.repository;
}
/**
* Create the actual {@link RepositoryFactorySupport} instance.
*

View File

@@ -15,14 +15,21 @@
*/
package org.springframework.data.repository.core.support;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Optional;
import java.util.stream.Collectors;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@@ -52,9 +59,9 @@ import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.util.ClassUtils;
import org.springframework.data.repository.util.ReactiveWrapperConverters;
import org.springframework.data.repository.util.ReactiveWrappers;
import org.springframework.data.util.Pair;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Factory bean to create instances of a given repository interface. Creates a proxy implementing the configured
@@ -70,20 +77,29 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
RepositoryFactorySupport.class.getClassLoader());
private static final Class<?> TRANSACTION_PROXY_TYPE = getTransactionProxyType();
private final Map<RepositoryInformationCacheKey, RepositoryInformation> repositoryInformationCache = new HashMap<RepositoryInformationCacheKey, RepositoryInformation>();
private final List<RepositoryProxyPostProcessor> postProcessors = new ArrayList<RepositoryProxyPostProcessor>();
private final Map<RepositoryInformationCacheKey, RepositoryInformation> repositoryInformationCache;
private final List<RepositoryProxyPostProcessor> postProcessors;
private Class<?> repositoryBaseClass;
private Optional<Class<?>> repositoryBaseClass;
private QueryLookupStrategy.Key queryLookupStrategyKey;
private List<QueryCreationListener<?>> queryPostProcessors = new ArrayList<QueryCreationListener<?>>();
private NamedQueries namedQueries = PropertiesBasedNamedQueries.EMPTY;
private ClassLoader classLoader = org.springframework.util.ClassUtils.getDefaultClassLoader();
private EvaluationContextProvider evaluationContextProvider = DefaultEvaluationContextProvider.INSTANCE;
private List<QueryCreationListener<?>> queryPostProcessors;
private NamedQueries namedQueries;
private ClassLoader classLoader;
private EvaluationContextProvider evaluationContextProvider;
private BeanFactory beanFactory;
private QueryCollectingQueryCreationListener collectingListener = new QueryCollectingQueryCreationListener();
public RepositoryFactorySupport() {
this.repositoryInformationCache = new HashMap<>();
this.postProcessors = new ArrayList<>();
this.repositoryBaseClass = Optional.empty();
this.namedQueries = PropertiesBasedNamedQueries.EMPTY;
this.classLoader = org.springframework.util.ClassUtils.getDefaultClassLoader();
this.evaluationContextProvider = DefaultEvaluationContextProvider.INSTANCE;
this.queryPostProcessors = new ArrayList<>();
this.queryPostProcessors.add(collectingListener);
}
@@ -142,7 +158,7 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
* @since 1.11
*/
public void setRepositoryBaseClass(Class<?> repositoryBaseClass) {
this.repositoryBaseClass = repositoryBaseClass;
this.repositoryBaseClass = Optional.ofNullable(repositoryBaseClass);
}
/**
@@ -178,7 +194,7 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
* @return
*/
public <T> T getRepository(Class<T> repositoryInterface) {
return getRepository(repositoryInterface, null);
return getRepository(repositoryInterface, Optional.empty());
}
/**
@@ -191,11 +207,10 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
* @return
*/
@SuppressWarnings({ "unchecked" })
public <T> T getRepository(Class<T> repositoryInterface, Object customImplementation) {
public <T> T getRepository(Class<T> repositoryInterface, Optional<Object> customImplementation) {
RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
Class<?> customImplementationClass = null == customImplementation ? null : customImplementation.getClass();
RepositoryInformation information = getRepositoryInformation(metadata, customImplementationClass);
RepositoryInformation information = getRepositoryInformation(metadata, customImplementation.map(Object::getClass));
validate(information, customImplementation);
@@ -213,9 +228,7 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
result.addInterface(TRANSACTION_PROXY_TYPE);
}
for (RepositoryProxyPostProcessor processor : postProcessors) {
processor.postProcess(result, information);
}
postProcessors.forEach(processor -> processor.postProcess(result, information));
if (IS_JAVA_8) {
result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
@@ -248,24 +261,19 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
* @return
*/
protected RepositoryInformation getRepositoryInformation(RepositoryMetadata metadata,
Class<?> customImplementationClass) {
Optional<Class<?>> customImplementationClass) {
RepositoryInformationCacheKey cacheKey = new RepositoryInformationCacheKey(metadata, customImplementationClass);
RepositoryInformation repositoryInformation = repositoryInformationCache.get(cacheKey);
if (repositoryInformation != null) {
return repositoryInformation;
}
Class<?> repositoryBaseClass = this.repositoryBaseClass == null ? getRepositoryBaseClass(metadata)
: this.repositoryBaseClass;
return repositoryInformationCache.computeIfAbsent(cacheKey, key -> new DefaultRepositoryInformation(metadata,
repositoryBaseClass.orElse(getRepositoryBaseClass(metadata)), customImplementationClass));
/*
repositoryInformation = metadata.isReactiveRepository()
? new ReactiveRepositoryInformation(metadata, repositoryBaseClass, customImplementationClass)
: new DefaultRepositoryInformation(metadata, repositoryBaseClass, customImplementationClass);
? new ReactiveRepositoryInformation(metadata, repositoryBaseClass, customImplementationClass)
: new DefaultRepositoryInformation(metadata, repositoryBaseClass, customImplementationClass);
*/
repositoryInformationCache.put(cacheKey, repositoryInformation);
return repositoryInformation;
}
protected List<QueryMethod> getQueryMethods() {
@@ -299,17 +307,6 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
*/
protected abstract Class<?> getRepositoryBaseClass(RepositoryMetadata metadata);
/**
* Returns the {@link QueryLookupStrategy} for the given {@link Key}.
*
* @deprecated favor {@link #getQueryLookupStrategy(Key, EvaluationContextProvider)}
* @param key can be {@literal null}
* @return the {@link QueryLookupStrategy} to use or {@literal null} if no queries should be looked up.
*/
protected QueryLookupStrategy getQueryLookupStrategy(Key key) {
return null;
}
/**
* Returns the {@link QueryLookupStrategy} for the given {@link Key} and {@link EvaluationContextProvider}.
*
@@ -318,8 +315,9 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
* @return the {@link QueryLookupStrategy} to use or {@literal null} if no queries should be looked up.
* @since 1.9
*/
protected QueryLookupStrategy getQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider) {
return null;
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
EvaluationContextProvider evaluationContextProvider) {
return Optional.empty();
}
/**
@@ -328,14 +326,18 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
* @param repositoryInformation
* @param customImplementation
*/
private void validate(RepositoryInformation repositoryInformation, Object customImplementation) {
private void validate(RepositoryInformation repositoryInformation, Optional<Object> customImplementation) {
if (null == customImplementation && repositoryInformation.hasCustomMethod()) {
customImplementation.orElseGet(() -> {
if (!repositoryInformation.hasCustomMethod()) {
return null;
}
throw new IllegalArgumentException(
String.format("You have custom methods in %s but not provided a custom implementation!",
repositoryInformation.getRepositoryInterface()));
}
});
validate(repositoryInformation);
}
@@ -357,22 +359,14 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
Object... constructorArguments) {
Class<?> baseClass = information.getRepositoryBaseClass();
Constructor<?> constructor = ReflectionUtils.findConstructor(baseClass, constructorArguments);
Optional<Constructor<?>> constructor = ReflectionUtils.findConstructor(baseClass, constructorArguments);
if (constructor == null) {
return constructor.map(it -> (R) BeanUtils.instantiateClass(it, constructorArguments)).orElseThrow(() -> {
List<Class<?>> argumentTypes = new ArrayList<Class<?>>(constructorArguments.length);
for (Object argument : constructorArguments) {
argumentTypes.add(argument.getClass());
}
throw new IllegalStateException(String.format(
return new IllegalStateException(String.format(
"No suitable constructor found on %s to match the given arguments: %s. Make sure you implement a constructor taking these",
baseClass, argumentTypes));
}
return (R) BeanUtils.instantiateClass(constructor, constructorArguments);
baseClass, Arrays.stream(constructorArguments).map(Object::getClass).collect(Collectors.toList())));
});
}
/**
@@ -399,8 +393,7 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
*/
public class QueryExecutorMethodInterceptor implements MethodInterceptor {
private final Map<Method, RepositoryQuery> queries = new ConcurrentHashMap<Method, RepositoryQuery>();
private final Map<Method, RepositoryQuery> queries;
private final QueryExecutionResultHandler resultHandler;
/**
@@ -411,33 +404,28 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
this.resultHandler = new QueryExecutionResultHandler();
QueryLookupStrategy lookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
Optional<QueryLookupStrategy> lookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
RepositoryFactorySupport.this.evaluationContextProvider);
lookupStrategy = lookupStrategy == null ? getQueryLookupStrategy(queryLookupStrategyKey) : lookupStrategy;
Iterable<Method> queryMethods = repositoryInformation.getQueryMethods();
if (lookupStrategy == null) {
if (!lookupStrategy.isPresent() && repositoryInformation.hasQueryMethods()) {
if (queryMethods.iterator().hasNext()) {
throw new IllegalStateException("You have defined query method in the repository but "
+ "you don't have any query lookup strategy defined. The "
+ "infrastructure apparently does not support query methods!");
}
return;
throw new IllegalStateException("You have defined query method in the repository but "
+ "you don't have any query lookup strategy defined. The "
+ "infrastructure apparently does not support query methods!");
}
SpelAwareProxyProjectionFactory factory = new SpelAwareProxyProjectionFactory();
factory.setBeanClassLoader(classLoader);
factory.setBeanFactory(beanFactory);
this.queries = lookupStrategy.map(it -> {
for (Method method : queryMethods) {
SpelAwareProxyProjectionFactory factory = new SpelAwareProxyProjectionFactory();
factory.setBeanClassLoader(classLoader);
factory.setBeanFactory(beanFactory);
RepositoryQuery query = lookupStrategy.resolveQuery(method, repositoryInformation, factory, namedQueries);
return repositoryInformation.getQueryMethods().stream()//
.map(method -> Pair.of(method, it.resolveQuery(method, repositoryInformation, factory, namedQueries)))//
.peek(pair -> invokeListeners(pair.getSecond()))//
.collect(Pair.toMap());
invokeListeners(query);
queries.put(method, query);
}
}).orElse(Collections.emptyMap());
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@@ -496,28 +484,13 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
*
* @author Mark Paluch
*/
@RequiredArgsConstructor
public class ImplementationMethodExecutionInterceptor implements MethodInterceptor {
private final Object customImplementation;
private final RepositoryInformation repositoryInformation;
private final Optional<Object> customImplementation;
private final Object target;
/**
* Creates a new {@link QueryExecutorMethodInterceptor}. Builds a model of {@link QueryMethod}s to be invoked on
* execution of repository interface methods.
*/
public ImplementationMethodExecutionInterceptor(RepositoryInformation repositoryInformation,
Object customImplementation, Object target) {
Assert.notNull(repositoryInformation, "RepositoryInformation must not be null!");
Assert.notNull(target, "Target must not be null!");
this.repositoryInformation = repositoryInformation;
this.customImplementation = customImplementation;
this.target = target;
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
@@ -530,7 +503,7 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
if (isCustomMethodInvocation(invocation)) {
Method actualMethod = repositoryInformation.getTargetClassMethod(method);
return executeMethodOn(customImplementation, actualMethod, arguments);
return executeMethodOn(customImplementation.get(), actualMethod, arguments);
}
// Lookup actual method as it might be redeclared in the interface
@@ -589,7 +562,7 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
* @param target
*/
public ConvertingImplementationMethodExecutionInterceptor(RepositoryInformation repositoryInformation,
Object customImplementation, Object target) {
Optional<Object> customImplementation, Object target) {
super(repositoryInformation, customImplementation, target);
}
@@ -641,18 +614,13 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
*
* @author Oliver Gierke
*/
@Getter
private static class QueryCollectingQueryCreationListener implements QueryCreationListener<RepositoryQuery> {
private List<QueryMethod> queryMethods = new ArrayList<QueryMethod>();
/**
* Returns all {@link QueryMethod}s.
*
* @return
* All {@link QueryMethod}s.
*/
public List<QueryMethod> getQueryMethods() {
return queryMethods;
}
private List<QueryMethod> queryMethods = new ArrayList<QueryMethod>();
/* (non-Javadoc)
* @see org.springframework.data.repository.core.support.QueryCreationListener#onCreation(org.springframework.data.repository.query.RepositoryQuery)
@@ -667,6 +635,7 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
*
* @author Oliver Gierke
*/
@EqualsAndHashCode
private static class RepositoryInformationCacheKey {
private final String repositoryInterfaceName;
@@ -679,40 +648,10 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
* @param repositoryInterfaceName must not be {@literal null}.
* @param customImplementationClassName
*/
public RepositoryInformationCacheKey(RepositoryMetadata metadata, Class<?> customImplementationType) {
public RepositoryInformationCacheKey(RepositoryMetadata metadata, Optional<Class<?>> customImplementationType) {
this.repositoryInterfaceName = metadata.getRepositoryInterface().getName();
this.customImplementationClassName = customImplementationType == null ? null : customImplementationType.getName();
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof RepositoryInformationCacheKey)) {
return false;
}
RepositoryInformationCacheKey that = (RepositoryInformationCacheKey) obj;
return this.repositoryInterfaceName.equals(that.repositoryInterfaceName)
&& ObjectUtils.nullSafeEquals(this.customImplementationClassName, that.customImplementationClassName);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 31;
result += 17 * repositoryInterfaceName.hashCode();
result += 17 * ObjectUtils.nullSafeHashCode(customImplementationClassName);
return result;
this.customImplementationClassName = customImplementationType.map(Class::getName).orElse(null);
}
}
}

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.repository.query;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -25,17 +28,17 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.BeanUtils;
import org.springframework.data.repository.query.EvaluationContextExtensionInformation.ExtensionTypeInformation.PublicMethodAndFieldFilter;
import org.springframework.data.repository.query.spi.EvaluationContextExtension;
import org.springframework.data.repository.query.spi.Function;
import org.springframework.data.util.Streamable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.ReflectionUtils.MethodFilter;
/**
@@ -53,7 +56,7 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
class EvaluationContextExtensionInformation {
private final ExtensionTypeInformation extensionTypeInformation;
private final RootObjectInformation rootObjectInformation;
private final Optional<RootObjectInformation> rootObjectInformation;
/**
* Creates a new {@link EvaluationContextExtension} for the given extension type.
@@ -65,7 +68,8 @@ class EvaluationContextExtensionInformation {
Assert.notNull(type, "Extension type must not be null!");
Class<?> rootObjectType = getRootObjectMethod(type).getReturnType();
this.rootObjectInformation = Object.class.equals(rootObjectType) ? null : new RootObjectInformation(rootObjectType);
this.rootObjectInformation = Optional
.ofNullable(Object.class.equals(rootObjectType) ? null : new RootObjectInformation(rootObjectType));
this.extensionTypeInformation = new ExtensionTypeInformation(type);
}
@@ -85,9 +89,10 @@ class EvaluationContextExtensionInformation {
* @param target
* @return
*/
public RootObjectInformation getRootObjectInformation(Object target) {
return target == null ? RootObjectInformation.NONE : rootObjectInformation == null ? new RootObjectInformation(
target.getClass()) : rootObjectInformation;
public RootObjectInformation getRootObjectInformation(Optional<Object> target) {
return target.map(it -> rootObjectInformation.orElse(new RootObjectInformation(it.getClass())))
.orElse(RootObjectInformation.NONE);
}
private static Method getRootObjectMethod(Class<?> type) {
@@ -105,9 +110,21 @@ class EvaluationContextExtensionInformation {
*
* @author Oliver Gierke
*/
@Getter
public static class ExtensionTypeInformation {
/**
* The statically defined properties of the extension type.
*
* @return the properties will never be {@literal null}.
*/
private final Map<String, Object> properties;
/**
* The statically exposed functions of the extension type.
*
* @return the functions will never be {@literal null}.
*/
private final Map<String, Function> functions;
/**
@@ -120,44 +137,21 @@ class EvaluationContextExtensionInformation {
Assert.notNull(type, "Extension type must not be null!");
this.functions = discoverDeclaredFunctions(type);
this.properties = discoverDeclaredProperties(type, PublicMethodAndFieldFilter.STATIC);
}
/**
* Returns the statically defined properties of the extension type.
*
* @return the properties will never be {@literal null}.
*/
public Map<String, Object> getProperties() {
return properties;
}
/**
* Returns the statically exposed functions of the extension type.
*
* @return the functions will never be {@literal null}.
*/
public Map<String, Function> getFunctions() {
return functions;
this.properties = discoverDeclaredProperties(type);
}
private static Map<String, Function> discoverDeclaredFunctions(Class<?> type) {
final Map<String, Function> map = new HashMap<String, Function>();
Map<String, Function> map = new HashMap<String, Function>();
ReflectionUtils.doWithMethods(type, new MethodCallback() {
ReflectionUtils.doWithMethods(type, //
method -> map.put(method.getName(), new Function(method, null)), //
PublicMethodAndFieldFilter.STATIC);
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
if (Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) {
map.put(method.getName(), new Function(method, null));
}
}
});
return map.isEmpty() ? Collections.<String, Function> emptyMap() : Collections.unmodifiableMap(map);
return map.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(map);
}
@RequiredArgsConstructor
static class PublicMethodAndFieldFilter implements MethodFilter, FieldFilter {
public static final PublicMethodAndFieldFilter STATIC = new PublicMethodAndFieldFilter(true);
@@ -165,13 +159,6 @@ class EvaluationContextExtensionInformation {
private final boolean staticOnly;
/**
* @param staticOnly
*/
public PublicMethodAndFieldFilter(boolean forStatic) {
this.staticOnly = forStatic;
}
/*
* (non-Javadoc)
* @see org.springframework.util.ReflectionUtils.MethodFilter#matches(java.lang.reflect.Method)
@@ -235,30 +222,20 @@ class EvaluationContextExtensionInformation {
return;
}
final PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(type);
Streamable<PropertyDescriptor> descriptors = Streamable.of(BeanUtils.getPropertyDescriptors(type));
ReflectionUtils.doWithMethods(type, new MethodCallback() {
ReflectionUtils.doWithMethods(type, method -> {
@Override
public void doWith(Method method) {
RootObjectInformation.this.methods.add(method);
RootObjectInformation.this.methods.add(method);
descriptors.stream()//
.filter(it -> method.equals(it.getReadMethod()))//
.forEach(it -> RootObjectInformation.this.accessors.put(it.getName(), method));
for (PropertyDescriptor descriptor : descriptors) {
if (method.equals(descriptor.getReadMethod())) {
RootObjectInformation.this.accessors.put(descriptor.getName(), method);
}
}
}
}, PublicMethodAndFieldFilter.NON_STATIC);
} , PublicMethodAndFieldFilter.NON_STATIC);
ReflectionUtils.doWithFields(type, new FieldCallback() {
@Override
public void doWith(Field field) {
RootObjectInformation.this.fields.add(field);
}
}, PublicMethodAndFieldFilter.NON_STATIC);
ReflectionUtils.doWithFields(type, field -> RootObjectInformation.this.fields.add(field),
PublicMethodAndFieldFilter.NON_STATIC);
}
/**
@@ -267,20 +244,13 @@ class EvaluationContextExtensionInformation {
* @param target can be {@literal null}.
* @return the methods
*/
public Map<String, Function> getFunctions(Object target) {
if (target == null) {
return Collections.emptyMap();
}
Map<String, Function> functions = new HashMap<String, Function>(methods.size());
for (Method method : methods) {
functions.put(method.getName(), new Function(method, target));
}
return Collections.unmodifiableMap(functions);
public Map<String, Function> getFunctions(Optional<Object> target) {
return target.map(it -> methods.stream()//
.collect(Collectors.toMap(//
Method::getName, //
method -> new Function(method, it))))
.orElse(Collections.emptyMap());
}
/**
@@ -289,38 +259,29 @@ class EvaluationContextExtensionInformation {
*
* @return the properties
*/
public Map<String, Object> getProperties(Object target) {
public Map<String, Object> getProperties(Optional<Object> target) {
if (target == null) {
return Collections.emptyMap();
}
return target.map(it -> {
Map<String, Object> properties = new HashMap<String, Object>();
Map<String, Object> properties = new HashMap<String, Object>();
for (Entry<String, Method> method : accessors.entrySet()) {
properties.put(method.getKey(), new Function(method.getValue(), target));
}
accessors.entrySet().stream()
.forEach(method -> properties.put(method.getKey(), new Function(method.getValue(), it)));
fields.stream().forEach(field -> properties.put(field.getName(), ReflectionUtils.getField(field, it)));
for (Field field : fields) {
properties.put(field.getName(), ReflectionUtils.getField(field, target));
}
return Collections.unmodifiableMap(properties);
return Collections.unmodifiableMap(properties);
}).orElse(Collections.emptyMap());
}
}
private static Map<String, Object> discoverDeclaredProperties(Class<?> type, FieldFilter filter) {
private static Map<String, Object> discoverDeclaredProperties(Class<?> type) {
final Map<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
ReflectionUtils.doWithFields(type, new FieldCallback() {
ReflectionUtils.doWithFields(type, field -> map.put(field.getName(), field.get(null)),
PublicMethodAndFieldFilter.STATIC);
@Override
public void doWith(Field field) throws IllegalAccessException {
map.put(field.getName(), field.get(null));
}
}, filter);
return map.isEmpty() ? Collections.<String, Object> emptyMap() : Collections.unmodifiableMap(map);
return map.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(map);
}
}

View File

@@ -15,12 +15,17 @@
*/
package org.springframework.data.repository.query;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
@@ -34,6 +39,7 @@ import org.springframework.data.repository.query.EvaluationContextExtensionInfor
import org.springframework.data.repository.query.EvaluationContextExtensionInformation.RootObjectInformation;
import org.springframework.data.repository.query.spi.EvaluationContextExtension;
import org.springframework.data.repository.query.spi.Function;
import org.springframework.data.util.Optionals;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.MethodExecutor;
@@ -61,7 +67,7 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
private final Map<Class<?>, EvaluationContextExtensionInformation> extensionInformationCache = new HashMap<Class<?>, EvaluationContextExtensionInformation>();
private List<? extends EvaluationContextExtension> extensions;
private ListableBeanFactory beanFactory;
private Optional<ListableBeanFactory> beanFactory = Optional.empty();
/**
* Creates a new {@link ExtensionAwareEvaluationContextProvider}. Extensions are being looked up lazily from the
@@ -88,7 +94,7 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanFactory = applicationContext;
this.beanFactory = Optional.of(applicationContext);
}
/* (non-Javadoc)
@@ -100,9 +106,7 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
StandardEvaluationContext ec = new StandardEvaluationContext();
if (beanFactory != null) {
ec.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
beanFactory.ifPresent(it -> ec.setBeanResolver(new BeanFactoryResolver(it)));
ExtensionAwarePropertyAccessor accessor = new ExtensionAwarePropertyAccessor(getExtensions());
@@ -129,21 +133,17 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
Map<String, Object> variables = new HashMap<String, Object>();
for (Parameter param : parameters) {
if (param.isSpecialParameter()) {
parameters.stream()//
.filter(Parameter::isSpecialParameter)//
.forEach(it -> variables.put(//
StringUtils.uncapitalize(it.getType().getSimpleName()), //
arguments[it.getIndex()]));
String key = StringUtils.uncapitalize(param.getType().getSimpleName());
Object value = arguments[param.getIndex()];
variables.put(key, value);
}
}
for (Parameter param : parameters) {
if (param.isNamedParameter()) {
variables.put(param.getName(), arguments[param.getIndex()]);
}
}
parameters.stream()//
.filter(Parameter::isNamedParameter)//
.forEach(it -> variables.put(//
it.getName().orElseThrow(() -> new IllegalStateException("Should never occur!")), //
arguments[it.getIndex()]));
return variables;
}
@@ -160,13 +160,12 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
return this.extensions;
}
if (beanFactory == null) {
this.extensions = Collections.emptyList();
return this.extensions;
}
this.extensions = Collections.emptyList();
this.extensions = new ArrayList<EvaluationContextExtension>(beanFactory.getBeansOfType(
EvaluationContextExtension.class, true, false).values());
beanFactory.ifPresent(it -> {
this.extensions = new ArrayList<EvaluationContextExtension>(
it.getBeansOfType(EvaluationContextExtension.class, true, false).values());
});
return extensions;
}
@@ -181,15 +180,9 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
private EvaluationContextExtensionInformation getOrCreateInformation(EvaluationContextExtension extension) {
Class<? extends EvaluationContextExtension> extensionType = extension.getClass();
EvaluationContextExtensionInformation information = extensionInformationCache.get(extensionType);
if (information != null) {
return information;
}
information = new EvaluationContextExtensionInformation(extensionType);
extensionInformationCache.put(extensionType, information);
return information;
return extensionInformationCache.computeIfAbsent(extensionType,
type -> new EvaluationContextExtensionInformation(extensionType));
}
/**
@@ -198,19 +191,12 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
* @param extensions
* @return
*/
private List<EvaluationContextExtensionAdapter> toAdapters(Collection<? extends EvaluationContextExtension> extensions) {
private List<EvaluationContextExtensionAdapter> toAdapters(List<? extends EvaluationContextExtension> extensions) {
List<EvaluationContextExtension> extensionsToSet = new ArrayList<EvaluationContextExtension>(extensions);
Collections.sort(extensionsToSet, AnnotationAwareOrderComparator.INSTANCE);
List<EvaluationContextExtensionAdapter> adapters = new ArrayList<EvaluationContextExtensionAdapter>(
extensions.size());
for (EvaluationContextExtension extension : extensionsToSet) {
adapters.add(new EvaluationContextExtensionAdapter(extension, getOrCreateInformation(extension)));
}
return adapters;
return extensions.stream()//
.sorted(AnnotationAwareOrderComparator.INSTANCE)//
.map(it -> new EvaluationContextExtensionAdapter(it, getOrCreateInformation(it)))//
.collect(Collectors.toList());
}
/**
@@ -233,11 +219,8 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
Assert.notNull(extensions, "Extensions must not be null!");
this.adapters = toAdapters(extensions);
this.adapterMap = new HashMap<String, EvaluationContextExtensionAdapter>(extensions.size());
for (EvaluationContextExtensionAdapter adapter : adapters) {
this.adapterMap.put(adapter.getExtensionId(), adapter);
}
this.adapterMap = adapters.stream()//
.collect(Collectors.toMap(it -> it.getExtensionId(), it -> it));
Collections.reverse(this.adapters);
}
@@ -257,13 +240,7 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
return true;
}
for (EvaluationContextExtensionAdapter extension : adapters) {
if (extension.getProperties().containsKey(name)) {
return true;
}
}
return false;
return adapters.stream().anyMatch(it -> it.getProperties().containsKey(name));
}
/*
@@ -281,16 +258,10 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
return new TypedValue(adapterMap.get(name));
}
for (EvaluationContextExtensionAdapter extension : adapters) {
Map<String, Object> properties = extension.getProperties();
if (properties.containsKey(name)) {
return lookupPropertyFrom(extension, name);
}
}
return null;
return adapters.stream()//
.filter(it -> it.getProperties().containsKey(name))//
.map(it -> lookupPropertyFrom(it, name))//
.findFirst().orElse(null);
}
/*
@@ -302,19 +273,12 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
List<TypeDescriptor> argumentTypes) throws AccessException {
if (target instanceof EvaluationContextExtensionAdapter) {
return getMethodExecutor((EvaluationContextExtensionAdapter) target, name, argumentTypes);
return getMethodExecutor((EvaluationContextExtensionAdapter) target, name, argumentTypes).orElse(null);
}
for (EvaluationContextExtensionAdapter adapter : adapters) {
MethodExecutor executor = getMethodExecutor(adapter, name, argumentTypes);
if (executor != null) {
return executor;
}
}
return null;
return adapters.stream()//
.flatMap(it -> Optionals.toStream(getMethodExecutor(it, name, argumentTypes)))//
.findFirst().orElse(null);
}
/*
@@ -352,22 +316,12 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
* @param argumentTypes
* @return
*/
private MethodExecutor getMethodExecutor(EvaluationContextExtensionAdapter adapter, String name,
private Optional<MethodExecutor> getMethodExecutor(EvaluationContextExtensionAdapter adapter, String name,
List<TypeDescriptor> argumentTypes) {
Map<String, Function> functions = adapter.getFunctions();
if (!functions.containsKey(name)) {
return null;
}
Function function = functions.get(name);
if (!function.supports(argumentTypes)) {
return null;
}
return new FunctionMethodExecutor(function);
return adapter.getFunctions().entrySet().stream()//
.filter(entry -> entry.getKey().equals(name))//
.findFirst().map(Entry::getValue).map(FunctionMethodExecutor::new);
}
/**
@@ -403,18 +357,10 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
* @author Oliver Gierke
* @since 1.9
*/
@RequiredArgsConstructor
private static class FunctionMethodExecutor implements MethodExecutor {
private final Function function;
/**
* Creates a new {@link FunctionMethodExecutor} for the given {@link Function}.
*
* @param function must not be {@literal null}.
*/
public FunctionMethodExecutor(Function function) {
this.function = function;
}
private final @NonNull Function function;
/*
* (non-Javadoc)
@@ -460,7 +406,7 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
Assert.notNull(extension, "Extenstion must not be null!");
Assert.notNull(information, "Extension information must not be null!");
Object target = extension.getRootObject();
Optional<Object> target = Optional.ofNullable(extension.getRootObject());
ExtensionTypeInformation extensionTypeInformation = information.getExtensionTypeInformation();
RootObjectInformation rootObjectInformation = information.getRootObjectInformation(target);

View File

@@ -20,6 +20,7 @@ import static java.lang.String.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
@@ -38,7 +39,7 @@ import org.springframework.util.Assert;
*/
public class Parameter {
@SuppressWarnings("unchecked") static final List<Class<?>> TYPES = Arrays.asList(Pageable.class, Sort.class);
static final List<Class<?>> TYPES = Arrays.asList(Pageable.class, Sort.class);
private static final String NAMED_PARAMETER_TEMPLATE = ":%s";
private static final String POSITION_PARAMETER_TEMPLATE = "?%s";
@@ -97,7 +98,7 @@ public class Parameter {
public String getPlaceholder() {
if (isNamedParameter()) {
return format(NAMED_PARAMETER_TEMPLATE, getName());
return format(NAMED_PARAMETER_TEMPLATE, getName().get());
} else {
return format(POSITION_PARAMETER_TEMPLATE, getIndex());
}
@@ -118,18 +119,18 @@ public class Parameter {
* @return
*/
public boolean isNamedParameter() {
return !isSpecialParameter() && getName() != null;
return !isSpecialParameter() && getName().isPresent();
}
/**
* Returns the name of the parameter (through {@link Param} annotation) or null if none can be found.
* Returns the name of the parameter (through {@link Param} annotation).
*
* @return
*/
public String getName() {
public Optional<String> getName() {
Param annotation = parameter.getParameterAnnotation(Param.class);
return annotation == null ? parameter.getParameterName() : annotation.value();
return Optional.ofNullable(annotation == null ? parameter.getParameterName() : annotation.value());
}
/**

View File

@@ -28,6 +28,7 @@ import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.util.Streamable;
import org.springframework.util.Assert;
/**
@@ -35,9 +36,8 @@ import org.springframework.util.Assert;
*
* @author Oliver Gierke
*/
public abstract class Parameters<S extends Parameters<S, T>, T extends Parameter> implements Iterable<T> {
public abstract class Parameters<S extends Parameters<S, T>, T extends Parameter> implements Streamable<T> {
@SuppressWarnings("unchecked") //
public static final List<Class<?>> TYPES = Arrays.asList(Pageable.class, Sort.class);
private static final String PARAM_ON_SPECIAL = format("You must not user @%s on a parameter typed %s or %s",

View File

@@ -15,9 +15,10 @@
*/
package org.springframework.data.repository.query;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -48,14 +49,9 @@ public class ParametersParameterAccessor implements ParameterAccessor {
Assert.isTrue(parameters.getNumberOfParameters() == values.length, "Invalid number of parameters given!");
this.parameters = parameters;
List<Object> unwrapped = new ArrayList<Object>(values.length);
for (Object element : values.clone()) {
unwrapped.add(QueryExecutionConverters.unwrap(element));
}
this.values = unwrapped;
this.values = Arrays.stream(values)//
.map(QueryExecutionConverters::unwrap)//
.collect(Collectors.toList());
}
/**
@@ -131,13 +127,8 @@ public class ParametersParameterAccessor implements ParameterAccessor {
*/
public boolean hasBindableNullValue() {
for (Parameter parameter : parameters.getBindableParameters()) {
if (values.get(parameter.getIndex()) == null) {
return true;
}
}
return false;
return parameters.getBindableParameters().stream()//
.anyMatch(it -> values.get(it.getIndex()) == null);
}
/*

View File

@@ -29,6 +29,7 @@ import org.springframework.data.repository.core.EntityMetadata;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.util.QueryExecutionConverters;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
@@ -48,8 +49,7 @@ public class QueryMethod {
private final Class<?> unwrappedReturnType;
private final Parameters<?, ?> parameters;
private final ResultProcessor resultProcessor;
private Class<?> domainClass;
private final Lazy<Class<?>> domainClass;
/**
* Creates a new {@link QueryMethod} from the given parameters. Looks up the correct query to use for following
@@ -65,12 +65,13 @@ public class QueryMethod {
Assert.notNull(metadata, "Repository metadata must not be null!");
Assert.notNull(factory, "ProjectionFactory must not be null!");
for (Class<?> type : Parameters.TYPES) {
if (getNumberOfOccurences(method, type) > 1) {
throw new IllegalStateException(String.format("Method must only one argument of type %s! Offending method: %s",
type.getSimpleName(), method.toString()));
}
}
Parameters.TYPES.stream()//
.filter(type -> getNumberOfOccurences(method, type) > 1)//
.findFirst().ifPresent(type -> {
throw new IllegalStateException(
String.format("Method must only one argument of type %s! Offending method: %s", type.getSimpleName(),
method.toString()));
});
this.method = method;
this.unwrappedReturnType = potentiallyUnwrapReturnTypeFor(method);
@@ -97,6 +98,15 @@ public class QueryMethod {
String.format("Paging query needs to have a Pageable parameter! Offending method %s", method.toString()));
}
this.domainClass = Lazy.of(() -> {
Class<?> repositoryDomainClass = metadata.getDomainType();
Class<?> methodDomainClass = metadata.getReturnedDomainClass(method);
return repositoryDomainClass == null || repositoryDomainClass.isAssignableFrom(methodDomainClass)
? methodDomainClass : repositoryDomainClass;
});
this.resultProcessor = new ResultProcessor(this, factory);
}
@@ -116,20 +126,12 @@ public class QueryMethod {
* @return
*/
public String getName() {
return method.getName();
}
@SuppressWarnings("rawtypes")
@SuppressWarnings({ "rawtypes", "unchecked" })
public EntityMetadata<?> getEntityInformation() {
return new EntityMetadata() {
public Class<?> getJavaType() {
return getDomainClass();
}
};
return () -> (Class) getDomainClass();
}
/**
@@ -147,17 +149,7 @@ public class QueryMethod {
* @return will never be {@literal null}.
*/
protected Class<?> getDomainClass() {
if (domainClass == null) {
Class<?> repositoryDomainClass = metadata.getDomainType();
Class<?> methodDomainClass = metadata.getReturnedDomainClass(method);
this.domainClass = repositoryDomainClass == null || repositoryDomainClass.isAssignableFrom(methodDomainClass)
? methodDomainClass : repositoryDomainClass;
}
return domainClass;
return domainClass.get();
}
/**
@@ -224,7 +216,7 @@ public class QueryMethod {
}
/**
* Returns whether the query for theis method actually returns entities.
* Returns whether the query for this method actually returns entities.
*
* @return
*/
@@ -252,7 +244,7 @@ public class QueryMethod {
}
/**
* Returns the {@link ResultProcessor} to be usedwith the query method.
* Returns the {@link ResultProcessor} to be used with the query method.
*
* @return the resultFactory
*/

View File

@@ -23,7 +23,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
import org.springframework.core.CollectionFactory;
@@ -33,7 +32,6 @@ import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.domain.Slice;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.util.ReactiveWrapperConverters;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.util.Assert;
/**
@@ -135,8 +133,7 @@ public class ResultProcessor {
Assert.notNull(preparingConverter, "Preparing converter must not be null!");
final ChainingConverter converter = ChainingConverter.of(type.getReturnedType(), preparingConverter)
.and(this.converter);
ChainingConverter converter = ChainingConverter.of(type.getReturnedType(), preparingConverter).and(this.converter);
if (source instanceof Slice && method.isPageQuery() || method.isSliceQuery()) {
return (T) ((Slice<?>) source).map(converter);
@@ -154,8 +151,8 @@ public class ResultProcessor {
return (T) target;
}
if (ReflectionUtils.isJava8StreamType(source.getClass()) && method.isStreamQuery()) {
return (T) new StreamQueryResultHandler(type, converter).handle(source);
if (source instanceof Stream && method.isStreamQuery()) {
return (T) ((Stream<Object>) source).map(t -> type.isInstance(t) ? t : converter.convert(t));
}
if (ReactiveWrapperConverters.supports(source.getClass())) {
@@ -198,14 +195,10 @@ public class ResultProcessor {
Assert.notNull(converter, "Converter must not be null!");
return new ChainingConverter(targetType, new Converter<Object, Object>() {
return new ChainingConverter(targetType, source -> {
@Override
public Object convert(Object source) {
Object intermediate = ChainingConverter.this.convert(source);
return targetType.isInstance(intermediate) ? intermediate : converter.convert(intermediate);
}
Object intermediate = ChainingConverter.this.convert(source);
return targetType.isInstance(intermediate) ? intermediate : converter.convert(intermediate);
});
}
@@ -313,13 +306,8 @@ public class ResultProcessor {
Assert.isInstanceOf(Stream.class, source, "Source must not be null and an instance of Stream!");
return ((Stream<Object>) source).map(new Function<Object, Object>() {
@Override
public Object apply(Object element) {
return returnType.isInstance(element) ? element : converter.convert(element);
}
});
return ((Stream<Object>) source)
.map(element -> returnType.isInstance(element) ? element : converter.convert(element));
}
}
}

View File

@@ -26,11 +26,12 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.model.PreferredConstructorDiscoverer;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.ProjectionInformation;
import org.springframework.data.util.Optionals;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -208,7 +209,6 @@ public abstract class ReturnedType {
*/
private static final class ReturnedClass extends ReturnedType {
@SuppressWarnings("unchecked") //
private static final Set<Class<?>> VOID_TYPES = new HashSet<Class<?>>(Arrays.asList(Void.class, void.class));
private final Class<?> type;
@@ -284,19 +284,14 @@ public abstract class ReturnedType {
}
PreferredConstructorDiscoverer<?, ?> discoverer = new PreferredConstructorDiscoverer(type);
PreferredConstructor<?, ?> constructor = discoverer.getConstructor();
if (constructor == null) {
return Collections.emptyList();
}
return discoverer.getConstructor().map(it -> {
List<String> properties = new ArrayList<String>();
return it.getParameters().stream()//
.flatMap(parameter -> Optionals.toStream(parameter.getName()))//
.collect(Collectors.toList());
for (PreferredConstructor.Parameter<Object, ?> parameter : constructor.getParameters()) {
properties.add(parameter.getName());
}
return properties;
}).orElse(Collections.emptyList());
}
private boolean isDto() {

View File

@@ -19,6 +19,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -36,7 +37,7 @@ import org.springframework.util.StringUtils;
*
* @author Oliver Gierke
*/
public class OrderBySource {
class OrderBySource {
private static final String BLOCK_SPLIT = "(?<=Asc|Desc)(?=\\p{Lu})";
private static final Pattern DIRECTION_SPLIT = Pattern.compile("(.+?)(Asc|Desc)?$");
@@ -109,10 +110,10 @@ public class OrderBySource {
/**
* Returns the clause as {@link Sort}.
*
* @return the {@link Sort} or null if no orders found.
* @return the {@link Sort}.
*/
public Sort toSort() {
return this.orders.isEmpty() ? null : new Sort(this.orders);
public Optional<Sort> toSort() {
return this.orders.isEmpty() ? Optional.empty() : Optional.of(new Sort(this.orders));
}
/*

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.repository.query.parser;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -35,6 +37,7 @@ import org.springframework.util.StringUtils;
* @author Oliver Gierke
* @author Martin Baumgartner
*/
@EqualsAndHashCode
public class Part {
private static final Pattern IGNORE_CASE = Pattern.compile("Ignor(ing|e)Case");
@@ -69,9 +72,11 @@ public class Part {
Assert.notNull(clazz, "Type must not be null!");
String partToUse = detectAndSetIgnoreCase(source);
if (alwaysIgnoreCase && ignoreCase != IgnoreCaseType.ALWAYS) {
this.ignoreCase = IgnoreCaseType.WHEN_POSSIBLE;
}
this.type = Type.fromProperty(partToUse);
this.propertyPath = PropertyPath.from(type.extractProperty(partToUse), clazz);
}
@@ -89,8 +94,7 @@ public class Part {
return result;
}
public boolean getParameterRequired() {
boolean isParameterRequired() {
return getNumberOfArguments() > 0;
}
@@ -100,7 +104,6 @@ public class Part {
* @return
*/
public int getNumberOfArguments() {
return type.getNumberOfArguments();
}
@@ -108,7 +111,6 @@ public class Part {
* @return the propertyPath
*/
public PropertyPath getProperty() {
return propertyPath;
}
@@ -116,7 +118,6 @@ public class Part {
* @return the type
*/
public Part.Type getType() {
return type;
}
@@ -126,50 +127,16 @@ public class Part {
* @return
*/
public IgnoreCaseType shouldIgnoreCase() {
return ignoreCase;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
Part that = (Part) obj;
return this.propertyPath.equals(that.propertyPath) && this.type.equals(that.type);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 37;
result += 17 * propertyPath.hashCode();
result += 17 * type.hashCode();
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("%s %s", propertyPath.getSegment(), type);
return String.format("%s %s %s", propertyPath.getSegment(), type, ignoreCase);
}
/**

View File

@@ -15,15 +15,20 @@
*/
package org.springframework.data.repository.query.parser;
import java.util.ArrayList;
import lombok.Getter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree.OrPart;
import org.springframework.data.util.Streamable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -38,7 +43,7 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @author Mark Paluch
*/
public class PartTree implements Iterable<OrPart> {
public class PartTree implements Streamable<OrPart> {
/*
* We look for a pattern of: keyword followed by
@@ -81,11 +86,12 @@ public class PartTree implements Iterable<OrPart> {
Assert.notNull(domainClass, "Domain class must not be null");
Matcher matcher = PREFIX_TEMPLATE.matcher(source);
if (!matcher.find()) {
this.subject = new Subject(null);
this.subject = new Subject(Optional.empty());
this.predicate = new Predicate(source, domainClass);
} else {
this.subject = new Subject(matcher.group(0));
this.subject = new Subject(Optional.of(matcher.group(0)));
this.predicate = new Predicate(source.substring(matcher.group().length()), domainClass);
}
}
@@ -104,9 +110,7 @@ public class PartTree implements Iterable<OrPart> {
* @return the sort
*/
public Sort getSort() {
OrderBySource orderBySource = predicate.getOrderBySource();
return orderBySource == null ? null : orderBySource.toSort();
return predicate.getOrderBySource().flatMap(OrderBySource::toSort).orElse(null);
}
/**
@@ -123,7 +127,7 @@ public class PartTree implements Iterable<OrPart> {
*
* @return
*/
public Boolean isCountProjection() {
public boolean isCountProjection() {
return subject.isCountProjection();
}
@@ -143,7 +147,7 @@ public class PartTree implements Iterable<OrPart> {
* @return
* @since 1.8
*/
public Boolean isDelete() {
public boolean isDelete() {
return subject.isDelete();
}
@@ -164,7 +168,7 @@ public class PartTree implements Iterable<OrPart> {
* @since 1.9
*/
public Integer getMaxResults() {
return subject.getMaxResults();
return subject.getMaxResults().orElse(null);
}
/**
@@ -172,15 +176,8 @@ public class PartTree implements Iterable<OrPart> {
*
* @return the iterable {@link Part}s
*/
public Iterable<Part> getParts() {
List<Part> result = new ArrayList<Part>();
for (OrPart orPart : this) {
for (Part part : orPart) {
result.add(part);
}
}
return result;
public Streamable<Part> getParts() {
return Streamable.of(this.stream().flatMap(OrPart::stream).collect(Collectors.toList()));
}
/**
@@ -189,25 +186,19 @@ public class PartTree implements Iterable<OrPart> {
* @param type
* @return
*/
public Iterable<Part> getParts(Type type) {
public Streamable<Part> getParts(Type type) {
List<Part> result = new ArrayList<Part>();
for (Part part : getParts()) {
if (part.getType().equals(type)) {
result.add(part);
}
}
return result;
return Streamable.of(getParts().stream()//
.filter(part -> part.getType().equals(type))//
.collect(Collectors.toList()));
}
@Override
public String toString() {
OrderBySource orderBySource = predicate.getOrderBySource();
Optional<OrderBySource> orderBySource = predicate.getOrderBySource();
return String.format("%s%s", StringUtils.collectionToDelimitedString(predicate.nodes, " or "),
orderBySource == null ? "" : " " + orderBySource);
orderBySource.map(it -> " ".concat(it.toString())).orElse(""));
}
/**
@@ -228,9 +219,9 @@ public class PartTree implements Iterable<OrPart> {
* A part of the parsed source that results from splitting up the resource around {@literal Or} keywords. Consists of
* {@link Part}s that have to be concatenated by {@literal And}.
*/
public static class OrPart implements Iterable<Part> {
public static class OrPart implements Streamable<Part> {
private final List<Part> children = new ArrayList<Part>();
private final List<Part> children;
/**
* Creates a new {@link OrPart}.
@@ -242,21 +233,19 @@ public class PartTree implements Iterable<OrPart> {
OrPart(String source, Class<?> domainClass, boolean alwaysIgnoreCase) {
String[] split = split(source, "And");
for (String part : split) {
if (StringUtils.hasText(part)) {
children.add(new Part(part, domainClass, alwaysIgnoreCase));
}
}
this.children = Arrays.stream(split)//
.filter(part -> StringUtils.hasText(part))//
.map(part -> new Part(part, domainClass, alwaysIgnoreCase))//
.collect(Collectors.toList());
}
public Iterator<Part> iterator() {
return children.iterator();
}
@Override
public String toString() {
return StringUtils.collectionToDelimitedString(children, " and ");
}
}
@@ -277,18 +266,18 @@ public class PartTree implements Iterable<OrPart> {
private static final Pattern EXISTS_BY_TEMPLATE = Pattern.compile("^(" + EXISTS_PATTERN + ")(\\p{Lu}.*?)??By");
private static final Pattern DELETE_BY_TEMPLATE = Pattern.compile("^(" + DELETE_PATTERN + ")(\\p{Lu}.*?)??By");
private static final String LIMITING_QUERY_PATTERN = "(First|Top)(\\d*)?";
private static final Pattern LIMITED_QUERY_TEMPLATE = Pattern.compile("^(" + QUERY_PATTERN + ")(" + DISTINCT + ")?"
+ LIMITING_QUERY_PATTERN + "(\\p{Lu}.*?)??By");
private static final Pattern LIMITED_QUERY_TEMPLATE = Pattern
.compile("^(" + QUERY_PATTERN + ")(" + DISTINCT + ")?" + LIMITING_QUERY_PATTERN + "(\\p{Lu}.*?)??By");
private final boolean distinct;
private final boolean count;
private final boolean exists;
private final boolean delete;
private final Integer maxResults;
private final Optional<Integer> maxResults;
public Subject(String subject) {
public Subject(Optional<String> subject) {
this.distinct = subject == null ? false : subject.contains(DISTINCT);
this.distinct = subject.map(it -> it.contains(DISTINCT)).orElse(false);
this.count = matches(subject, COUNT_BY_TEMPLATE);
this.exists = matches(subject, EXISTS_BY_TEMPLATE);
this.delete = matches(subject, DELETE_BY_TEMPLATE);
@@ -300,19 +289,19 @@ public class PartTree implements Iterable<OrPart> {
* @return
* @since 1.9
*/
private Integer returnMaxResultsIfFirstKSubjectOrNull(String subject) {
private Optional<Integer> returnMaxResultsIfFirstKSubjectOrNull(Optional<String> subject) {
if (subject == null) {
return null;
}
return subject.map(it -> {
Matcher grp = LIMITED_QUERY_TEMPLATE.matcher(subject);
Matcher grp = LIMITED_QUERY_TEMPLATE.matcher(it);
if (!grp.find()) {
return null;
}
if (!grp.find()) {
return null;
}
return StringUtils.hasText(grp.group(4)) ? Integer.valueOf(grp.group(4)) : 1;
});
return StringUtils.hasText(grp.group(4)) ? Integer.valueOf(grp.group(4)) : 1;
}
/**
@@ -321,7 +310,7 @@ public class PartTree implements Iterable<OrPart> {
* @return
* @since 1.8
*/
public Boolean isDelete() {
public boolean isDelete() {
return delete;
}
@@ -343,12 +332,12 @@ public class PartTree implements Iterable<OrPart> {
return distinct;
}
public Integer getMaxResults() {
public Optional<Integer> getMaxResults() {
return maxResults;
}
private final boolean matches(String subject, Pattern pattern) {
return subject == null ? false : pattern.matcher(subject).find();
private final boolean matches(Optional<String> subject, Pattern pattern) {
return subject.map(it -> pattern.matcher(it).find()).orElse(false);
}
}
@@ -358,13 +347,13 @@ public class PartTree implements Iterable<OrPart> {
* @author Oliver Gierke
* @author Phil Webb
*/
private static class Predicate {
private static class Predicate implements Iterable<OrPart> {
private static final Pattern ALL_IGNORE_CASE = Pattern.compile("AllIgnor(ing|e)Case");
private static final String ORDER_BY = "OrderBy";
private final List<OrPart> nodes = new ArrayList<OrPart>();
private final OrderBySource orderBySource;
private final List<OrPart> nodes;
private final @Getter Optional<OrderBySource> orderBySource;
private boolean alwaysIgnoreCase;
public Predicate(String predicate, Class<?> domainClass) {
@@ -375,8 +364,11 @@ public class PartTree implements Iterable<OrPart> {
throw new IllegalArgumentException("OrderBy must not be used more than once in a method name!");
}
buildTree(parts[0], domainClass);
this.orderBySource = parts.length == 2 ? new OrderBySource(parts[1], domainClass) : null;
this.nodes = Arrays.stream(split(parts[0], "Or"))//
.map(part -> new OrPart(part, domainClass, alwaysIgnoreCase))//
.collect(Collectors.toList());
this.orderBySource = Optional.ofNullable(parts.length == 2 ? new OrderBySource(parts[1], domainClass) : null);
}
private String detectAndSetAllIgnoreCase(String predicate) {
@@ -391,20 +383,13 @@ public class PartTree implements Iterable<OrPart> {
return predicate;
}
private void buildTree(String source, Class<?> domainClass) {
String[] split = split(source, "Or");
for (String part : split) {
nodes.add(new OrPart(part, domainClass, alwaysIgnoreCase));
}
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<OrPart> iterator() {
return nodes.iterator();
}
public OrderBySource getOrderBySource() {
return orderBySource;
}
}
}

View File

@@ -15,8 +15,12 @@
*/
package org.springframework.data.repository.support;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Optional;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
@@ -28,10 +32,11 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @since 1.10
*/
@RequiredArgsConstructor
class AnnotationAttribute {
private final Class<? extends Annotation> annotationType;
private final String attributeName;
private final @NonNull Class<? extends Annotation> annotationType;
private final @NonNull Optional<String> attributeName;
/**
* Creates a new {@link AnnotationAttribute} to the {@code value} attribute of the given {@link Annotation} type.
@@ -39,21 +44,7 @@ class AnnotationAttribute {
* @param annotationType must not be {@literal null}.
*/
public AnnotationAttribute(Class<? extends Annotation> annotationType) {
this(annotationType, null);
}
/**
* Creates a new {@link AnnotationAttribute} for the given {@link Annotation} type and annotation attribute name.
*
* @param annotationType must not be {@literal null}.
* @param attributeName can be {@literal null}, defaults to {@code value}.
*/
public AnnotationAttribute(Class<? extends Annotation> annotationType, String attributeName) {
Assert.notNull(annotationType, "AnnotationType must not be null!");
this.annotationType = annotationType;
this.attributeName = attributeName;
this(annotationType, Optional.empty());
}
/**
@@ -71,11 +62,12 @@ class AnnotationAttribute {
* @param parameter must not be {@literal null}.
* @return
*/
public Object getValueFrom(MethodParameter parameter) {
public Optional<Object> getValueFrom(MethodParameter parameter) {
Assert.notNull(parameter, "MethodParameter must not be null!");
Annotation annotation = parameter.getParameterAnnotation(annotationType);
return annotation == null ? null : getValueFrom(annotation);
return Optional.ofNullable(annotation).map(it -> getValueFrom(it));
}
/**
@@ -84,11 +76,12 @@ class AnnotationAttribute {
* @param annotatedElement must not be {@literal null}.
* @return
*/
public Object getValueFrom(AnnotatedElement annotatedElement) {
public Optional<Object> getValueFrom(AnnotatedElement annotatedElement) {
Assert.notNull(annotatedElement, "Annotated element must not be null!");
Annotation annotation = annotatedElement.getAnnotation(annotationType);
return annotation == null ? null : getValueFrom(annotation);
return Optional.ofNullable(annotation).map(it -> getValueFrom(annotation));
}
/**
@@ -100,7 +93,7 @@ class AnnotationAttribute {
public Object getValueFrom(Annotation annotation) {
Assert.notNull(annotation, "Annotation must not be null!");
return (String) (attributeName == null ? AnnotationUtils.getValue(annotation) : AnnotationUtils.getValue(
annotation, attributeName));
return (String) attributeName.map(it -> AnnotationUtils.getValue(annotation, it))
.orElseGet(() -> AnnotationUtils.getValue(annotation));
}
}

View File

@@ -73,17 +73,7 @@ public class DefaultRepositoryInvokerFactory implements RepositoryInvokerFactory
*/
@Override
public RepositoryInvoker getInvokerFor(Class<?> domainType) {
RepositoryInvoker invoker = invokers.get(domainType);
if (invoker != null) {
return invoker;
}
invoker = prepareInvokers(domainType);
invokers.put(domainType, invoker);
return invoker;
return invokers.computeIfAbsent(domainType, type -> prepareInvokers(type));
}
/**

View File

@@ -19,8 +19,6 @@ import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionException;
@@ -33,7 +31,6 @@ import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.Param;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@@ -170,25 +167,6 @@ class ReflectionRepositoryInvoker implements RepositoryInvoker {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryInvoker#invokeQueryMethod(java.lang.reflect.Method, java.util.Map, org.springframework.data.domain.Pageable, org.springframework.data.domain.Sort)
*/
@Override
public Object invokeQueryMethod(Method method, Map<String, String[]> parameters, Pageable pageable, Sort sort) {
Assert.notNull(method, "Method must not be null!");
Assert.notNull(parameters, "Parameters must not be null!");
MultiValueMap<String, String> forward = new LinkedMultiValueMap<String, String>(parameters.size());
for (Entry<String, String[]> entry : parameters.entrySet()) {
forward.put(entry.getKey(), Arrays.asList(entry.getValue()));
}
return invokeQueryMethod(method, forward, pageable, sort);
}
/*
* (non-Javadoc)
* @see org.springframework.data.rest.core.invoke.RepositoryInvoker#invokeQueryMethod(java.lang.reflect.Method, java.util.Map, org.springframework.data.domain.Pageable, org.springframework.data.domain.Sort)

View File

@@ -22,6 +22,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.springframework.beans.factory.BeanFactory;
@@ -50,7 +51,7 @@ public class Repositories implements Iterable<Class<?>> {
private static final RepositoryFactoryInformation<Object, Serializable> EMPTY_REPOSITORY_FACTORY_INFO = EmptyRepositoryFactoryInformation.INSTANCE;
private static final String DOMAIN_TYPE_MUST_NOT_BE_NULL = "Domain type must not be null!";
private final BeanFactory beanFactory;
private final Optional<BeanFactory> beanFactory;
private final Map<Class<?>, String> repositoryBeanNames;
private final Map<Class<?>, RepositoryFactoryInformation<Object, Serializable>> repositoryFactoryInfos;
@@ -59,9 +60,9 @@ public class Repositories implements Iterable<Class<?>> {
*/
private Repositories() {
this.beanFactory = null;
this.repositoryBeanNames = Collections.<Class<?>, String> emptyMap();
this.repositoryFactoryInfos = Collections.<Class<?>, RepositoryFactoryInformation<Object, Serializable>> emptyMap();
this.beanFactory = Optional.empty();
this.repositoryBeanNames = Collections.emptyMap();
this.repositoryFactoryInfos = Collections.emptyMap();
}
/**
@@ -74,7 +75,7 @@ public class Repositories implements Iterable<Class<?>> {
Assert.notNull(factory, "Factory must not be null!");
this.beanFactory = factory;
this.beanFactory = Optional.of(factory);
this.repositoryFactoryInfos = new HashMap<Class<?>, RepositoryFactoryInformation<Object, Serializable>>();
this.repositoryBeanNames = new HashMap<Class<?>, String>();
@@ -92,7 +93,7 @@ public class Repositories implements Iterable<Class<?>> {
@SuppressWarnings({ "rawtypes", "unchecked" })
private synchronized void cacheRepositoryFactory(String name) {
RepositoryFactoryInformation repositoryFactoryInformation = beanFactory.getBean(name,
RepositoryFactoryInformation repositoryFactoryInformation = beanFactory.get().getBean(name,
RepositoryFactoryInformation.class);
Class<?> domainType = ClassUtils
.getUserClass(repositoryFactoryInformation.getRepositoryInformation().getDomainType());
@@ -130,12 +131,12 @@ public class Repositories implements Iterable<Class<?>> {
* @param domainClass must not be {@literal null}.
* @return
*/
public Object getRepositoryFor(Class<?> domainClass) {
public Optional<Object> getRepositoryFor(Class<?> domainClass) {
Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
String repositoryBeanName = repositoryBeanNames.get(domainClass);
return repositoryBeanName == null || beanFactory == null ? null : beanFactory.getBean(repositoryBeanName);
Optional<String> repositoryBeanName = Optional.ofNullable(repositoryBeanNames.get(domainClass));
return beanFactory.flatMap(it -> repositoryBeanName.map(name -> it.getBean(name)));
}
/**
@@ -201,18 +202,12 @@ public class Repositories implements Iterable<Class<?>> {
* repository instance registered for the given interface.
* @since 1.12
*/
public RepositoryInformation getRepositoryInformation(Class<?> repositoryInterface) {
public Optional<RepositoryInformation> getRepositoryInformation(Class<?> repositoryInterface) {
for (RepositoryFactoryInformation<Object, Serializable> factoryInformation : repositoryFactoryInfos.values()) {
RepositoryInformation information = factoryInformation.getRepositoryInformation();
if (information.getRepositoryInterface().equals(repositoryInterface)) {
return information;
}
}
return null;
return repositoryFactoryInfos.values().stream()//
.map(RepositoryFactoryInformation::getRepositoryInformation)//
.filter(information -> information.getRepositoryInterface().equals(repositoryInterface))//
.findFirst();
}
/**

View File

@@ -17,7 +17,6 @@ package org.springframework.data.repository.support;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -88,20 +87,6 @@ public interface RepositoryInvoker extends RepositoryInvocationInformation {
*/
void invokeDelete(Serializable id);
/**
* Invokes the query method backed by the given {@link Method} using the given parameters, {@link Pageable} and
* {@link Sort}.
*
* @param method must not be {@literal null}.
* @param parameters must not be {@literal null}.
* @param pageable can be {@literal null}.
* @param sort can be {@literal null}.
* @return the result of the invoked query method.
* @deprecated use {@link #invokeQueryMethod(Method, MultiValueMap, Pageable, Sort)} instead.
*/
@Deprecated
Object invokeQueryMethod(Method method, Map<String, String[]> parameters, Pageable pageable, Sort sort);
/**
* Invokes the query method backed by the given {@link Method} using the given parameters, {@link Pageable} and
* {@link Sort}.

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.support;
import java.util.Optional;
/**
* Strategy interface to determine whether a given entity is to be considered new.
*
@@ -29,5 +31,5 @@ public interface IsNewStrategy {
* @param entity can be {@literal null}.
* @return
*/
boolean isNew(Object entity);
boolean isNew(Optional<? extends Object> entity);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.support;
import java.util.Optional;
import org.springframework.data.domain.Persistable;
/**
@@ -26,17 +28,21 @@ public enum PersistableIsNewStrategy implements IsNewStrategy {
INSTANCE;
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.IsNewStrategy#isNew(java.lang.Object)
* @see org.springframework.data.support.IsNewStrategy#isNew(java.util.Optional)
*/
public boolean isNew(Object entity) {
public boolean isNew(Optional<? extends Object> entity) {
if (!(entity instanceof Persistable)) {
throw new IllegalArgumentException(String.format("Given object of type %s does not implement %s!",
entity.getClass(), Persistable.class));
}
return entity.map(it -> {
return ((Persistable<?>) entity).isNew();
if (!(it instanceof Persistable)) {
throw new IllegalArgumentException(
String.format("Given object of type %s does not implement %s!", it.getClass(), Persistable.class));
}
return ((Persistable<?>) it).isNew();
}).orElse(false);
}
}

View File

@@ -48,6 +48,7 @@ public class AnnotatedTypeScanner implements ResourceLoaderAware, EnvironmentAwa
*
* @param annotationTypes the annotation types to scan for.
*/
@SafeVarargs
public AnnotatedTypeScanner(Class<? extends Annotation>... annotationTypes) {
this(true, annotationTypes);
}
@@ -58,6 +59,7 @@ public class AnnotatedTypeScanner implements ResourceLoaderAware, EnvironmentAwa
* @param considerInterfaces whether to consider interfaces as well.
* @param annotationTypes the annotations to scan for.
*/
@SafeVarargs
public AnnotatedTypeScanner(boolean considerInterfaces, Class<? extends Annotation>... annotationTypes) {
this.annotationTypess = Arrays.asList(annotationTypes);

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.util;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import java.util.Optional;
import java.util.function.Supplier;
/**
* @author Oliver Gierke
*/
@RequiredArgsConstructor
@EqualsAndHashCode
public class Lazy<T> {
private final Supplier<T> supplier;
private Optional<T> value;
public static <T> Lazy<T> of(Supplier<T> supplier) {
return new Lazy<T>(supplier);
}
/**
* Returns the value created by the configured {@link Supplier}.
*
* @return
*/
public T get() {
if (value == null) {
this.value = Optional.ofNullable(supplier.get());
}
return value.orElse(null);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.util;
import lombok.experimental.UtilityClass;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.util.Assert;
/**
* Utility methods to work with {@link Optional}s.
*
* @author Oliver Gierke
*/
@UtilityClass
public class Optionals {
/**
* Returns whether any of the given {@link Optional}s is present.
*
* @param optionals must not be {@literal null}.
* @return
*/
public static boolean isAnyPresent(Optional<?>... optionals) {
Assert.notNull(optionals, "Optionals must not be null!");
return Arrays.stream(optionals).anyMatch(Optional::isPresent);
}
/**
* Turns the given {@link Optional} into a one-element {@link Stream} or an empty one if not present.
*
* @param optionals must not be {@literal null}.
* @return
*/
@SafeVarargs
public static <T> Stream<T> toStream(Optional<? extends T>... optionals) {
Assert.notNull(optionals, "Optional must not be null!");
return Arrays.asList(optionals).stream().flatMap(it -> it.map(Stream::of).orElse(Stream.empty()));
}
public static <T> Optional<T> next(Iterator<T> iterator) {
return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.empty();
}
}

View File

@@ -21,6 +21,10 @@ import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import java.util.Map;
import java.util.stream.Collector;
import java.util.stream.Collectors;
/**
* A tuple of things.
*
@@ -66,4 +70,8 @@ public final class Pair<S, T> {
public T getSecond() {
return second;
}
public static <S, T> Collector<Pair<S, T>, ?, Map<S, T>> toMap() {
return Collectors.toMap(Pair::getFirst, Pair::getSecond);
}
}

View File

@@ -15,12 +15,16 @@
*/
package org.springframework.data.util;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.beans.BeanUtils;
@@ -105,18 +109,10 @@ public abstract class ReflectionUtils {
*
* @author Oliver Gierke
*/
@RequiredArgsConstructor
public static class AnnotationFieldFilter implements DescribedFieldFilter {
private final Class<? extends Annotation> annotationType;
/**
* Creates a new {@link AnnotationFieldFilter} for the given annotation type.
*/
public AnnotationFieldFilter(Class<? extends Annotation> annotationType) {
Assert.notNull(annotationType, "Annotation type must not be null!");
this.annotationType = annotationType;
}
private final @NonNull Class<? extends Annotation> annotationType;
/*
* (non-Javadoc)
@@ -242,27 +238,20 @@ public abstract class ReflectionUtils {
}
/**
* Finds a constructoron the given type that matches the given constructor arguments.
* Finds a constructor on the given type that matches the given constructor arguments.
*
* @param type must not be {@literal null}.
* @param constructorArguments must not be {@literal null}.
* @return a {@link Constructor} that is compatible with the given arguments or {@literal null} if none found.
* @return a {@link Constructor} that is compatible with the given arguments.
*/
public static Constructor<?> findConstructor(Class<?> type, Object... constructorArguments) {
public static Optional<Constructor<?>> findConstructor(Class<?> type, Object... constructorArguments) {
Assert.notNull(type, "Target type must not be null!");
Assert.notNull(constructorArguments, "Constructor arguments must not be null!");
for (Constructor<?> candidate : type.getDeclaredConstructors()) {
Class<?>[] parameterTypes = candidate.getParameterTypes();
if (argumentsMatch(parameterTypes, constructorArguments)) {
return candidate;
}
}
return null;
return Arrays.stream(type.getDeclaredConstructors())//
.filter(constructor -> argumentsMatch(constructor.getParameterTypes(), constructorArguments))//
.findFirst();
}
/**

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.util;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.springframework.util.Assert;
/**
* Simple interface to ease streamability of {@link Iterable}s.
*
* @author Oliver Gierke
*/
public interface Streamable<T> extends Iterable<T> {
/**
* Creates a non-parallel {@link Stream} of the underlying {@link Iterable}.
*
* @return will never be {@literal null}.
*/
default Stream<T> stream() {
return StreamSupport.stream(spliterator(), false);
}
public static <T> Streamable<T> empty() {
return () -> Collections.emptyIterator();
}
@SafeVarargs
public static <T> Streamable<T> of(T... t) {
return () -> Arrays.asList(t).iterator();
}
/**
* Returns a {@link Streamable} for the given {@link Iterable}.
*
* @param iterable must not be {@literal null}.
* @return
*/
public static <T> Streamable<T> of(Iterable<T> iterable) {
Assert.notNull(iterable, "Iterable must not be null!");
return () -> iterable.iterator();
}
}

View File

@@ -17,8 +17,12 @@ package org.springframework.data.web.config;
import java.util.List;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver;
import org.springframework.data.web.HateoasSortHandlerMethodArgumentResolver;
import org.springframework.data.web.PagedResourcesAssembler;
@@ -36,6 +40,15 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@Configuration
public class HateoasAwareSpringDataWebConfiguration extends SpringDataWebConfiguration {
/**
* @param context must not be {@literal null}.
* @param conversionService must not be {@literal null}.
*/
public HateoasAwareSpringDataWebConfiguration(ApplicationContext context,
@Qualifier("mvcConversionService") ObjectFactory<ConversionService> conversionService) {
super(context, conversionService);
}
/*
* (non-Javadoc)
* @see org.springframework.data.web.config.SpringDataWebConfiguration#pageableResolver()

View File

@@ -16,6 +16,7 @@
package org.springframework.data.web.config;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -54,7 +55,7 @@ public class QuerydslWebConfiguration extends WebMvcConfigurerAdapter {
@Lazy
@Bean
public QuerydslPredicateArgumentResolver querydslPredicateArgumentResolver() {
return new QuerydslPredicateArgumentResolver(querydslBindingsFactory(), conversionService.getObject());
return new QuerydslPredicateArgumentResolver(querydslBindingsFactory(), Optional.of(conversionService.getObject()));
}
@Lazy

View File

@@ -18,7 +18,6 @@ package org.springframework.data.web.config;
import java.util.List;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -52,8 +51,15 @@ import com.fasterxml.jackson.databind.ObjectMapper;
@Configuration
public class SpringDataWebConfiguration extends WebMvcConfigurerAdapter {
@Autowired private ApplicationContext context;
@Autowired @Qualifier("mvcConversionService") ObjectFactory<ConversionService> conversionService;
private final ApplicationContext context;
private final ObjectFactory<ConversionService> conversionService;
public SpringDataWebConfiguration(ApplicationContext context,
@Qualifier("mvcConversionService") ObjectFactory<ConversionService> conversionService) {
this.context = context;
this.conversionService = conversionService;
}
/*
* (non-Javadoc)

View File

@@ -17,6 +17,7 @@ package org.springframework.data.web.querydsl;
import java.util.Arrays;
import java.util.Map.Entry;
import java.util.Optional;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
@@ -56,11 +57,11 @@ public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentR
* @param factory
* @param conversionService defaults to {@link DefaultConversionService} if {@literal null}.
*/
public QuerydslPredicateArgumentResolver(QuerydslBindingsFactory factory, ConversionService conversionService) {
public QuerydslPredicateArgumentResolver(QuerydslBindingsFactory factory,
Optional<ConversionService> conversionService) {
this.bindingsFactory = factory;
this.predicateBuilder = new QuerydslPredicateBuilder(
conversionService == null ? new DefaultConversionService() : conversionService,
this.predicateBuilder = new QuerydslPredicateBuilder(conversionService.orElse(new DefaultConversionService()),
factory.getEntityPathResolver());
}
@@ -88,7 +89,7 @@ public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentR
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory)
*/
@Override
@SuppressWarnings("unchecked")
@SuppressWarnings({ "unchecked", "rawtypes" })
public Predicate resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
@@ -98,12 +99,15 @@ public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentR
parameters.put(entry.getKey(), Arrays.asList(entry.getValue()));
}
QuerydslPredicate annotation = parameter.getParameterAnnotation(QuerydslPredicate.class);
Optional<QuerydslPredicate> annotation = Optional
.ofNullable(parameter.getParameterAnnotation(QuerydslPredicate.class));
TypeInformation<?> domainType = extractTypeInfo(parameter).getActualType();
Class<? extends QuerydslBinderCustomizer<?>> customizer = (Class<? extends QuerydslBinderCustomizer<?>>) (annotation == null
? null : annotation.bindings());
QuerydslBindings bindings = bindingsFactory.createBindingsFor(customizer, domainType);
Optional<? extends Class<? extends QuerydslBinderCustomizer>> map = annotation
.<Class<? extends QuerydslBinderCustomizer>> map(it -> it.bindings());
QuerydslBindings bindings = bindingsFactory.createBindingsFor(domainType,
(Optional<Class<? extends QuerydslBinderCustomizer<?>>>) map);
return predicateBuilder.getPredicate(domainType, parameters, bindings);
}
@@ -117,13 +121,12 @@ public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentR
*/
static TypeInformation<?> extractTypeInfo(MethodParameter parameter) {
QuerydslPredicate annotation = parameter.getParameterAnnotation(QuerydslPredicate.class);
Optional<QuerydslPredicate> annotation = Optional
.ofNullable(parameter.getParameterAnnotation(QuerydslPredicate.class));
if (annotation != null && !Object.class.equals(annotation.root())) {
return ClassTypeInformation.from(annotation.root());
}
return detectDomainType(ClassTypeInformation.fromReturnTypeOf(parameter.getMethod()));
return annotation.filter(it -> !Object.class.equals(it.root()))//
.<TypeInformation<?>> map(it -> ClassTypeInformation.from(it.root()))//
.orElseGet(() -> detectDomainType(ClassTypeInformation.fromReturnTypeOf(parameter.getMethod())));
}
private static TypeInformation<?> detectDomainType(TypeInformation<?> source) {