From 28668d774b99e2a22ed816139e3cca565e7a2051 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 5 Aug 2024 15:53:17 +0200 Subject: [PATCH] Enforce non-null value in requiredSingleResult/requiredUniqueResult Closes gh-33300 --- .../dao/support/DataAccessUtils.java | 14 +- .../dao/support/DataAccessUtilsTests.java | 154 +++++++++++------- 2 files changed, 109 insertions(+), 59 deletions(-) diff --git a/spring-tx/src/main/java/org/springframework/dao/support/DataAccessUtils.java b/spring-tx/src/main/java/org/springframework/dao/support/DataAccessUtils.java index ce310ff46b..6d610a9466 100644 --- a/spring-tx/src/main/java/org/springframework/dao/support/DataAccessUtils.java +++ b/spring-tx/src/main/java/org/springframework/dao/support/DataAccessUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -167,7 +167,11 @@ public abstract class DataAccessUtils { if (results.size() > 1) { throw new IncorrectResultSizeDataAccessException(1, results.size()); } - return results.iterator().next(); + T result = results.iterator().next(); + if (result == null) { + throw new TypeMismatchDataAccessException("Result value is null but no null value expected"); + } + return result; } /** @@ -235,7 +239,11 @@ public abstract class DataAccessUtils { if (!CollectionUtils.hasUniqueObject(results)) { throw new IncorrectResultSizeDataAccessException(1, results.size()); } - return results.iterator().next(); + T result = results.iterator().next(); + if (result == null) { + throw new TypeMismatchDataAccessException("Result value is null but no null value expected"); + } + return result; } /** diff --git a/spring-tx/src/test/java/org/springframework/dao/support/DataAccessUtilsTests.java b/spring-tx/src/test/java/org/springframework/dao/support/DataAccessUtilsTests.java index f80e92df13..28dd0d2f0e 100644 --- a/spring-tx/src/test/java/org/springframework/dao/support/DataAccessUtilsTests.java +++ b/spring-tx/src/test/java/org/springframework/dao/support/DataAccessUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -52,21 +52,25 @@ class DataAccessUtilsTests { assertThat(DataAccessUtils.optionalResult(col.stream())).isEmpty(); assertThat(DataAccessUtils.optionalResult(col.iterator())).isEmpty(); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.requiredUniqueResult(col)) - .satisfies(sizeRequirements(1, 0)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.requiredSingleResult(col)) + .satisfies(sizeRequirements(1, 0)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.objectResult(col, String.class)) - .satisfies(sizeRequirements(1, 0)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.requiredUniqueResult(col)) + .satisfies(sizeRequirements(1, 0)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.intResult(col)) - .satisfies(sizeRequirements(1, 0)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.objectResult(col, String.class)) + .satisfies(sizeRequirements(1, 0)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.longResult(col)) - .satisfies(sizeRequirements(1, 0)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.intResult(col)) + .satisfies(sizeRequirements(1, 0)); + + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.longResult(col)) + .satisfies(sizeRequirements(1, 0)); } @Test @@ -75,49 +79,83 @@ class DataAccessUtilsTests { col.add("test1"); col.add("test2"); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.uniqueResult(col)) - .satisfies(sizeRequirements(1, 2)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.uniqueResult(col)) + .satisfies(sizeRequirements(1, 2)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.requiredUniqueResult(col)) - .satisfies(sizeRequirements(1, 2)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.requiredUniqueResult(col)) + .satisfies(sizeRequirements(1, 2)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.objectResult(col, String.class)) - .satisfies(sizeRequirements(1, 2)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.objectResult(col, String.class)) + .satisfies(sizeRequirements(1, 2)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.intResult(col)) - .satisfies(sizeRequirements(1, 2)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.intResult(col)) + .satisfies(sizeRequirements(1, 2)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.longResult(col)) - .satisfies(sizeRequirements(1, 2)); + 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.requiredSingleResult(col)) + .satisfies(sizeRequirements(1, 2)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.singleResult(col.stream())) - .satisfies(sizeRequirements(1)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.singleResult(col)) + .satisfies(sizeRequirements(1, 2)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.singleResult(col.iterator())) - .satisfies(sizeRequirements(1)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.singleResult(col.stream())) + .satisfies(sizeRequirements(1)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.optionalResult(col)) - .satisfies(sizeRequirements(1, 2)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.singleResult(col.iterator())) + .satisfies(sizeRequirements(1)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.optionalResult(col.stream())) - .satisfies(sizeRequirements(1)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.optionalResult(col)) + .satisfies(sizeRequirements(1, 2)); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> - DataAccessUtils.optionalResult(col.iterator())) - .satisfies(sizeRequirements(1)); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.optionalResult(col.stream())) + .satisfies(sizeRequirements(1)); + + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.optionalResult(col.iterator())) + .satisfies(sizeRequirements(1)); + } + + @Test + void withNullValueInCollection() { + Collection col = new HashSet<>(); + col.add(null); + + 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(TypeMismatchDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.requiredSingleResult(col)); + + assertThatExceptionOfType(TypeMismatchDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.requiredUniqueResult(col)); + + assertThatExceptionOfType(TypeMismatchDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.objectResult(col, String.class)); + + assertThatExceptionOfType(TypeMismatchDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.intResult(col)); + + assertThatExceptionOfType(TypeMismatchDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.longResult(col)); } @Test @@ -131,6 +169,7 @@ class DataAccessUtilsTests { assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo("5"); assertThat(DataAccessUtils.intResult(col)).isEqualTo(5); assertThat(DataAccessUtils.longResult(col)).isEqualTo(5); + assertThat(DataAccessUtils.requiredSingleResult(col)).isEqualTo(Integer.valueOf(5)); assertThat(DataAccessUtils.singleResult(col)).isEqualTo(5); assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo(5); assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo(5); @@ -159,8 +198,8 @@ class DataAccessUtilsTests { Collection col = Arrays.asList(555, 555); assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) - .isThrownBy(() -> DataAccessUtils.uniqueResult(col)) - .satisfies(sizeRequirements(1, 2)); + .isThrownBy(() -> DataAccessUtils.uniqueResult(col)) + .satisfies(sizeRequirements(1, 2)); } @Test @@ -174,6 +213,7 @@ class DataAccessUtilsTests { assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo("5"); assertThat(DataAccessUtils.intResult(col)).isEqualTo(5); assertThat(DataAccessUtils.longResult(col)).isEqualTo(5); + assertThat(DataAccessUtils.requiredSingleResult(col)).isEqualTo(Long.valueOf(5L)); 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)); @@ -190,6 +230,7 @@ class DataAccessUtilsTests { assertThat(DataAccessUtils.uniqueResult(col)).isEqualTo("test1"); assertThat(DataAccessUtils.requiredUniqueResult(col)).isEqualTo("test1"); assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo("test1"); + assertThat(DataAccessUtils.requiredSingleResult(col)).isEqualTo("test1"); assertThat(DataAccessUtils.singleResult(col)).isEqualTo("test1"); assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo("test1"); assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo("test1"); @@ -197,11 +238,11 @@ class DataAccessUtilsTests { 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)); + assertThatExceptionOfType(TypeMismatchDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.intResult(col)); - assertThatExceptionOfType(TypeMismatchDataAccessException.class).isThrownBy(() -> - DataAccessUtils.longResult(col)); + assertThatExceptionOfType(TypeMismatchDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.longResult(col)); } @Test @@ -214,6 +255,7 @@ class DataAccessUtilsTests { 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.requiredSingleResult(col)).isEqualTo(date); assertThat(DataAccessUtils.singleResult(col)).isEqualTo(date); assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo(date); assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo(date); @@ -221,11 +263,11 @@ class DataAccessUtilsTests { 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)); + assertThatExceptionOfType(TypeMismatchDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.intResult(col)); - assertThatExceptionOfType(TypeMismatchDataAccessException.class).isThrownBy(() -> - DataAccessUtils.longResult(col)); + assertThatExceptionOfType(TypeMismatchDataAccessException.class) + .isThrownBy(() -> DataAccessUtils.longResult(col)); } @Test