diff --git a/pom.xml b/pom.xml
index 74b071dfb..071046199 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,6 +17,7 @@
DATACMNS
+ 2.11.7
@@ -190,6 +191,14 @@
test
+
+
+ org.scala-lang
+ scala-library
+ ${scala}
+ true
+
+
javax.transaction
javax.transaction-api
diff --git a/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java b/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java
index 6d554d901..0461665d7 100644
--- a/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java
+++ b/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java
@@ -15,6 +15,8 @@
*/
package org.springframework.data.repository.util;
+import scala.Option;
+
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -35,7 +37,15 @@ import com.google.common.base.Optional;
/**
* Converters to potentially wrap the execution of a repository method into a variety of wrapper types potentially being
- * available on the classpath.
+ * available on the classpath. Currently supported:
+ *
+ * - {@code java.util.Optional}
+ * - {@code com.google.common.base.Optional}
+ * - {@code scala.Option}
+ * - {@code java.util.concurrent.Future}
+ * - {@code java.util.concurrent.CompletableFuture}
+ * - {@code org.springframework.util.concurrent.ListenableFuture<}
+ *
*
* @author Oliver Gierke
* @since 1.8
@@ -50,6 +60,8 @@ public abstract class QueryExecutionConverters {
QueryExecutionConverters.class.getClassLoader());
private static final boolean JDK_8_PRESENT = ClassUtils.isPresent("java.util.Optional",
QueryExecutionConverters.class.getClassLoader());
+ private static final boolean SCALA_PRESENT = ClassUtils.isPresent("scala.Option",
+ QueryExecutionConverters.class.getClassLoader());
private static final Set> WRAPPER_TYPES = new HashSet>();
private static final Set> UNWRAPPERS = new HashSet>();
@@ -72,6 +84,11 @@ public abstract class QueryExecutionConverters {
if (JDK_8_PRESENT && SPRING_4_2_PRESENT) {
WRAPPER_TYPES.add(NullableWrapperToCompletableFutureConverter.getWrapperType());
}
+
+ if (SCALA_PRESENT) {
+ WRAPPER_TYPES.add(NullableWrapperToScalaOptionConverter.getWrapperType());
+ UNWRAPPERS.add(ScalOptionUnwrapper.INSTANCE);
+ }
}
private QueryExecutionConverters() {}
@@ -113,6 +130,10 @@ public abstract class QueryExecutionConverters {
conversionService.addConverter(new NullableWrapperToCompletableFutureConverter(conversionService));
}
+ if (SCALA_PRESENT) {
+ conversionService.addConverter(new NullableWrapperToScalaOptionConverter(conversionService));
+ }
+
conversionService.addConverter(new NullableWrapperToFutureConverter(conversionService));
}
@@ -151,6 +172,7 @@ public abstract class QueryExecutionConverters {
@SuppressWarnings("unused") //
private final ConversionService conversionService;
private final Class>[] wrapperTypes;
+ private final Object nullValue;
/**
* Creates a new {@link AbstractWrapperTypeConverter} using the given {@link ConversionService} and wrapper type.
@@ -158,13 +180,15 @@ public abstract class QueryExecutionConverters {
* @param conversionService must not be {@literal null}.
* @param wrapperTypes must not be {@literal null}.
*/
- protected AbstractWrapperTypeConverter(ConversionService conversionService, Class>... wrapperTypes) {
+ protected AbstractWrapperTypeConverter(ConversionService conversionService, Object nullValue,
+ Class>... wrapperTypes) {
Assert.notNull(conversionService, "ConversionService must not be null!");
Assert.notEmpty(wrapperTypes, "Wrapper type must not be empty!");
this.conversionService = conversionService;
this.wrapperTypes = wrapperTypes;
+ this.nullValue = nullValue;
}
/*
@@ -194,16 +218,9 @@ public abstract class QueryExecutionConverters {
Object value = wrapper.getValue();
// TODO: Add Recursive conversion once we move to Spring 4
- return value == null ? getNullValue() : wrap(value);
+ return value == null ? nullValue : wrap(value);
}
- /**
- * Return the object that shall be used as a replacement for {@literal null}.
- *
- * @return must not be {@literal null}.
- */
- protected abstract Object getNullValue();
-
/**
* Wrap the given, non-{@literal null} value into the wrapper type.
*
@@ -226,16 +243,7 @@ public abstract class QueryExecutionConverters {
* @param conversionService must not be {@literal null}.
*/
public NullableWrapperToGuavaOptionalConverter(ConversionService conversionService) {
- super(conversionService, Optional.class);
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.repository.util.QueryExecutionConverters.AbstractWrapperTypeConverter#getNullValue()
- */
- @Override
- protected Object getNullValue() {
- return Optional.absent();
+ super(conversionService, Optional.absent(), Optional.class);
}
/*
@@ -265,16 +273,7 @@ public abstract class QueryExecutionConverters {
* @param conversionService must not be {@literal null}.
*/
public NullableWrapperToJdk8OptionalConverter(ConversionService conversionService) {
- super(conversionService, java.util.Optional.class);
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.repository.util.QueryExecutionConverters.AbstractWrapperTypeConverter#getNullValue()
- */
- @Override
- protected Object getNullValue() {
- return java.util.Optional.empty();
+ super(conversionService, java.util.Optional.empty(), java.util.Optional.class);
}
/*
@@ -298,24 +297,13 @@ public abstract class QueryExecutionConverters {
*/
private static class NullableWrapperToFutureConverter extends AbstractWrapperTypeConverter {
- private static final AsyncResult