Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataAccessUtils result accessors with Optional return value #27735

Merged
merged 11 commits into from
Jul 14, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
package org.springframework.dao.support;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
Expand Down Expand Up @@ -56,6 +60,89 @@ public static <T> T singleResult(@Nullable Collection<T> results) throws Incorre
return results.iterator().next();
}

/**
* Return a single result object from the given Stream.
* <p>Returns {@code null} if 0 result objects found;
* throws an exception if more than 1 element found.
* @param results the result Stream (can be {@code null})
* @return the single result object, or {@code null} if none
* @throws IncorrectResultSizeDataAccessException if more than one
* element has been found in the given Stream
*/
@Nullable
public static <T> T singleResult(@Nullable Stream<T> results) throws IncorrectResultSizeDataAccessException {
if (results == null) {
return null;
}
try (results) {
List<T> resultList = results.limit(2).toList();
if (resultList.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, resultList.size());
KaidosGH marked this conversation as resolved.
Show resolved Hide resolved
}
return CollectionUtils.isEmpty(resultList) ? null : resultList.get(0);
}
}

/**
* Return a single result object from the given Iterator.
* <p>Returns {@code null} if 0 result objects found;
* throws an exception if more than 1 element found.
* @param results the result Iterator (can be {@code null})
* @return the single result object, or {@code null} if none
* @throws IncorrectResultSizeDataAccessException if more than one
* element has been found in the given Iterator
*/
@Nullable
public static <T> T singleResult(@Nullable Iterator<T> results) throws IncorrectResultSizeDataAccessException {
if (results == null) {
return null;
}
T result = results.hasNext() ? results.next() : null;
if (results.hasNext()) {
throw new IncorrectResultSizeDataAccessException(1, 2);
KaidosGH marked this conversation as resolved.
Show resolved Hide resolved
}
return result;
}

/**
* Return a single result object from the given Collection.
* <p>Returns {@code Optional.empty()} if 0 result objects found;
* throws an exception if more than 1 element found.
* @param results the result Collection (can be {@code null})
* @return the single optional result object, or {@code Optional.empty()} if none
* @throws IncorrectResultSizeDataAccessException if more than one
* element has been found in the given Collection
*/
public static <T> Optional<T> optionalResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException {
return Optional.ofNullable(singleResult(results));
}

/**
* Return a single result object from the given Stream.
* <p>Returns {@code Optional.empty()} if 0 result objects found;
* throws an exception if more than 1 element found.
* @param results the result Stream (can be {@code null})
* @return the single optional result object, or {@code Optional.empty()} if none
* @throws IncorrectResultSizeDataAccessException if more than one
* element has been found in the given Stream
*/
public static <T> Optional<T> optionalResult(@Nullable Stream<T> results) throws IncorrectResultSizeDataAccessException {
return Optional.ofNullable(singleResult(results));
}

/**
* Return a single result object from the given Iterator.
* <p>Returns {@code Optional.empty()} if 0 result objects found;
* throws an exception if more than 1 element found.
* @param results the result Iterator (can be {@code null})
* @return the single optional result object, or {@code Optional.empty()} if none
* @throws IncorrectResultSizeDataAccessException if more than one
* element has been found in the given Iterator
*/
public static <T> Optional<T> optionalResult(@Nullable Iterator<T> results) throws IncorrectResultSizeDataAccessException {
return Optional.ofNullable(singleResult(results));
}

/**
* Return a single result object from the given Collection.
* <p>Throws an exception if 0 or more than 1 element found.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;

import org.junit.jupiter.api.Test;
Expand All @@ -47,6 +48,13 @@ public void withEmptyCollection() {

assertThat(DataAccessUtils.uniqueResult(col)).isNull();

assertThat(DataAccessUtils.singleResult(col)).isNull();
assertThat(DataAccessUtils.singleResult(col.stream())).isNull();
assertThat(DataAccessUtils.singleResult(col.iterator())).isNull();
assertThat(DataAccessUtils.optionalResult(col)).isEmpty();
assertThat(DataAccessUtils.optionalResult(col.stream())).isEmpty();
assertThat(DataAccessUtils.optionalResult(col.iterator())).isEmpty();

assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
DataAccessUtils.requiredUniqueResult(col))
.satisfies(sizeRequirements(1, 0));
Expand Down Expand Up @@ -89,6 +97,30 @@ public void withTooLargeCollection() {
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
DataAccessUtils.longResult(col))
.satisfies(sizeRequirements(1, 2));

assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
DataAccessUtils.singleResult(col))
.satisfies(sizeRequirements(1, 2));

assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
DataAccessUtils.singleResult(col.stream()))
.satisfies(sizeRequirements(1, 2));

assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
DataAccessUtils.singleResult(col.iterator()))
.satisfies(sizeRequirements(1, 2));

assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
DataAccessUtils.optionalResult(col))
.satisfies(sizeRequirements(1, 2));

assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
DataAccessUtils.optionalResult(col.stream()))
.satisfies(sizeRequirements(1, 2));

assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
DataAccessUtils.optionalResult(col.iterator()))
.satisfies(sizeRequirements(1, 2));
}

@Test
Expand All @@ -102,6 +134,12 @@ public void withInteger() {
assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo("5");
assertThat(DataAccessUtils.intResult(col)).isEqualTo(5);
assertThat(DataAccessUtils.longResult(col)).isEqualTo(5);
assertThat(DataAccessUtils.singleResult(col)).isEqualTo(5);
assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo(5);
assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo(5);
assertThat(DataAccessUtils.optionalResult(col)).isEqualTo(Optional.of(5));
assertThat(DataAccessUtils.optionalResult(col.stream())).isEqualTo(Optional.of(5));
assertThat(DataAccessUtils.optionalResult(col.iterator())).isEqualTo(Optional.of(5));
}

@Test
Expand Down Expand Up @@ -139,6 +177,12 @@ public void withLong() {
assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo("5");
assertThat(DataAccessUtils.intResult(col)).isEqualTo(5);
assertThat(DataAccessUtils.longResult(col)).isEqualTo(5);
assertThat(DataAccessUtils.singleResult(col)).isEqualTo(Long.valueOf(5L));
assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo(Long.valueOf(5L));
assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo(Long.valueOf(5L));
assertThat(DataAccessUtils.optionalResult(col)).isEqualTo(Optional.of(5L));
assertThat(DataAccessUtils.optionalResult(col.stream())).isEqualTo(Optional.of(5L));
assertThat(DataAccessUtils.optionalResult(col.iterator())).isEqualTo(Optional.of(5L));
}

@Test
Expand All @@ -149,6 +193,12 @@ public void withString() {
assertThat(DataAccessUtils.uniqueResult(col)).isEqualTo("test1");
assertThat(DataAccessUtils.requiredUniqueResult(col)).isEqualTo("test1");
assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo("test1");
assertThat(DataAccessUtils.singleResult(col)).isEqualTo("test1");
assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo("test1");
assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo("test1");
assertThat(DataAccessUtils.optionalResult(col)).isEqualTo(Optional.of("test1"));
assertThat(DataAccessUtils.optionalResult(col.stream())).isEqualTo(Optional.of("test1"));
assertThat(DataAccessUtils.optionalResult(col.iterator())).isEqualTo(Optional.of("test1"));

assertThatExceptionOfType(TypeMismatchDataAccessException.class).isThrownBy(() ->
DataAccessUtils.intResult(col));
Expand All @@ -167,6 +217,12 @@ public void withDate() {
assertThat(DataAccessUtils.requiredUniqueResult(col)).isEqualTo(date);
assertThat(DataAccessUtils.objectResult(col, Date.class)).isEqualTo(date);
assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo(date.toString());
assertThat(DataAccessUtils.singleResult(col)).isEqualTo(date);
assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo(date);
assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo(date);
assertThat(DataAccessUtils.optionalResult(col)).isEqualTo(Optional.of(date));
assertThat(DataAccessUtils.optionalResult(col.stream())).isEqualTo(Optional.of(date));
assertThat(DataAccessUtils.optionalResult(col.iterator())).isEqualTo(Optional.of(date));

assertThatExceptionOfType(TypeMismatchDataAccessException.class).isThrownBy(() ->
DataAccessUtils.intResult(col));
Expand Down