DATACMNS-867 - First draft.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,4 +186,5 @@ public abstract class ThreeTenBackPortConverters {
|
||||
return ZoneId.of(source);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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!")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<?>> {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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(", "))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
51
src/main/java/org/springframework/data/util/Lazy.java
Normal file
51
src/main/java/org/springframework/data/util/Lazy.java
Normal 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);
|
||||
}
|
||||
}
|
||||
65
src/main/java/org/springframework/data/util/Optionals.java
Normal file
65
src/main/java/org/springframework/data/util/Optionals.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
62
src/main/java/org/springframework/data/util/Streamable.java
Normal file
62
src/main/java/org/springframework/data/util/Streamable.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user