Skip to content

Commit 28668d7

Browse files
committed
Enforce non-null value in requiredSingleResult/requiredUniqueResult
Closes gh-33300
1 parent 1e804d8 commit 28668d7

File tree

2 files changed

+109
-59
lines changed

2 files changed

+109
-59
lines changed

spring-tx/src/main/java/org/springframework/dao/support/DataAccessUtils.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -167,7 +167,11 @@ public static <T> T requiredSingleResult(@Nullable Collection<T> results) throws
167167
if (results.size() > 1) {
168168
throw new IncorrectResultSizeDataAccessException(1, results.size());
169169
}
170-
return results.iterator().next();
170+
T result = results.iterator().next();
171+
if (result == null) {
172+
throw new TypeMismatchDataAccessException("Result value is null but no null value expected");
173+
}
174+
return result;
171175
}
172176

173177
/**
@@ -235,7 +239,11 @@ public static <T> T requiredUniqueResult(@Nullable Collection<T> results) throws
235239
if (!CollectionUtils.hasUniqueObject(results)) {
236240
throw new IncorrectResultSizeDataAccessException(1, results.size());
237241
}
238-
return results.iterator().next();
242+
T result = results.iterator().next();
243+
if (result == null) {
244+
throw new TypeMismatchDataAccessException("Result value is null but no null value expected");
245+
}
246+
return result;
239247
}
240248

241249
/**

spring-tx/src/test/java/org/springframework/dao/support/DataAccessUtilsTests.java

+98-56
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,21 +52,25 @@ void withEmptyCollection() {
5252
assertThat(DataAccessUtils.optionalResult(col.stream())).isEmpty();
5353
assertThat(DataAccessUtils.optionalResult(col.iterator())).isEmpty();
5454

55-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
56-
DataAccessUtils.requiredUniqueResult(col))
57-
.satisfies(sizeRequirements(1, 0));
55+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
56+
.isThrownBy(() -> DataAccessUtils.requiredSingleResult(col))
57+
.satisfies(sizeRequirements(1, 0));
5858

59-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
60-
DataAccessUtils.objectResult(col, String.class))
61-
.satisfies(sizeRequirements(1, 0));
59+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
60+
.isThrownBy(() -> DataAccessUtils.requiredUniqueResult(col))
61+
.satisfies(sizeRequirements(1, 0));
6262

63-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
64-
DataAccessUtils.intResult(col))
65-
.satisfies(sizeRequirements(1, 0));
63+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
64+
.isThrownBy(() -> DataAccessUtils.objectResult(col, String.class))
65+
.satisfies(sizeRequirements(1, 0));
66+
67+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
68+
.isThrownBy(() -> DataAccessUtils.intResult(col))
69+
.satisfies(sizeRequirements(1, 0));
6670

67-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
68-
DataAccessUtils.longResult(col))
69-
.satisfies(sizeRequirements(1, 0));
71+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
72+
.isThrownBy(() -> DataAccessUtils.longResult(col))
73+
.satisfies(sizeRequirements(1, 0));
7074
}
7175

7276
@Test
@@ -75,49 +79,83 @@ void withTooLargeCollection() {
7579
col.add("test1");
7680
col.add("test2");
7781

78-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
79-
DataAccessUtils.uniqueResult(col))
80-
.satisfies(sizeRequirements(1, 2));
82+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
83+
.isThrownBy(() -> DataAccessUtils.uniqueResult(col))
84+
.satisfies(sizeRequirements(1, 2));
8185

82-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
83-
DataAccessUtils.requiredUniqueResult(col))
84-
.satisfies(sizeRequirements(1, 2));
86+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
87+
.isThrownBy(() -> DataAccessUtils.requiredUniqueResult(col))
88+
.satisfies(sizeRequirements(1, 2));
8589

86-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
87-
DataAccessUtils.objectResult(col, String.class))
88-
.satisfies(sizeRequirements(1, 2));
90+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
91+
.isThrownBy(() -> DataAccessUtils.objectResult(col, String.class))
92+
.satisfies(sizeRequirements(1, 2));
8993

90-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
91-
DataAccessUtils.intResult(col))
92-
.satisfies(sizeRequirements(1, 2));
94+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
95+
.isThrownBy(() -> DataAccessUtils.intResult(col))
96+
.satisfies(sizeRequirements(1, 2));
9397

94-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
95-
DataAccessUtils.longResult(col))
96-
.satisfies(sizeRequirements(1, 2));
98+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
99+
.isThrownBy(() -> DataAccessUtils.longResult(col))
100+
.satisfies(sizeRequirements(1, 2));
101+
102+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
103+
.isThrownBy(() -> DataAccessUtils.requiredSingleResult(col))
104+
.satisfies(sizeRequirements(1, 2));
105+
106+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
107+
.isThrownBy(() -> DataAccessUtils.singleResult(col))
108+
.satisfies(sizeRequirements(1, 2));
109+
110+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
111+
.isThrownBy(() -> DataAccessUtils.singleResult(col.stream()))
112+
.satisfies(sizeRequirements(1));
113+
114+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
115+
.isThrownBy(() -> DataAccessUtils.singleResult(col.iterator()))
116+
.satisfies(sizeRequirements(1));
97117

98-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
99-
DataAccessUtils.singleResult(col))
100-
.satisfies(sizeRequirements(1, 2));
118+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
119+
.isThrownBy(() -> DataAccessUtils.optionalResult(col))
120+
.satisfies(sizeRequirements(1, 2));
121+
122+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
123+
.isThrownBy(() -> DataAccessUtils.optionalResult(col.stream()))
124+
.satisfies(sizeRequirements(1));
125+
126+
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
127+
.isThrownBy(() -> DataAccessUtils.optionalResult(col.iterator()))
128+
.satisfies(sizeRequirements(1));
129+
}
130+
131+
@Test
132+
void withNullValueInCollection() {
133+
Collection<String> col = new HashSet<>();
134+
col.add(null);
135+
136+
assertThat(DataAccessUtils.uniqueResult(col)).isNull();
137+
138+
assertThat(DataAccessUtils.singleResult(col)).isNull();
139+
assertThat(DataAccessUtils.singleResult(col.stream())).isNull();
140+
assertThat(DataAccessUtils.singleResult(col.iterator())).isNull();
141+
assertThat(DataAccessUtils.optionalResult(col)).isEmpty();
142+
assertThat(DataAccessUtils.optionalResult(col.stream())).isEmpty();
143+
assertThat(DataAccessUtils.optionalResult(col.iterator())).isEmpty();
101144

102-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
103-
DataAccessUtils.singleResult(col.stream()))
104-
.satisfies(sizeRequirements(1));
145+
assertThatExceptionOfType(TypeMismatchDataAccessException.class)
146+
.isThrownBy(() -> DataAccessUtils.requiredSingleResult(col));
105147

106-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
107-
DataAccessUtils.singleResult(col.iterator()))
108-
.satisfies(sizeRequirements(1));
148+
assertThatExceptionOfType(TypeMismatchDataAccessException.class)
149+
.isThrownBy(() -> DataAccessUtils.requiredUniqueResult(col));
109150

110-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
111-
DataAccessUtils.optionalResult(col))
112-
.satisfies(sizeRequirements(1, 2));
151+
assertThatExceptionOfType(TypeMismatchDataAccessException.class)
152+
.isThrownBy(() -> DataAccessUtils.objectResult(col, String.class));
113153

114-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
115-
DataAccessUtils.optionalResult(col.stream()))
116-
.satisfies(sizeRequirements(1));
154+
assertThatExceptionOfType(TypeMismatchDataAccessException.class)
155+
.isThrownBy(() -> DataAccessUtils.intResult(col));
117156

118-
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
119-
DataAccessUtils.optionalResult(col.iterator()))
120-
.satisfies(sizeRequirements(1));
157+
assertThatExceptionOfType(TypeMismatchDataAccessException.class)
158+
.isThrownBy(() -> DataAccessUtils.longResult(col));
121159
}
122160

123161
@Test
@@ -131,6 +169,7 @@ void withInteger() {
131169
assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo("5");
132170
assertThat(DataAccessUtils.intResult(col)).isEqualTo(5);
133171
assertThat(DataAccessUtils.longResult(col)).isEqualTo(5);
172+
assertThat(DataAccessUtils.requiredSingleResult(col)).isEqualTo(Integer.valueOf(5));
134173
assertThat(DataAccessUtils.singleResult(col)).isEqualTo(5);
135174
assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo(5);
136175
assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo(5);
@@ -159,8 +198,8 @@ void withEquivalentIntegerInstanceTwice() {
159198
Collection<Integer> col = Arrays.asList(555, 555);
160199

161200
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
162-
.isThrownBy(() -> DataAccessUtils.uniqueResult(col))
163-
.satisfies(sizeRequirements(1, 2));
201+
.isThrownBy(() -> DataAccessUtils.uniqueResult(col))
202+
.satisfies(sizeRequirements(1, 2));
164203
}
165204

166205
@Test
@@ -174,6 +213,7 @@ void withLong() {
174213
assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo("5");
175214
assertThat(DataAccessUtils.intResult(col)).isEqualTo(5);
176215
assertThat(DataAccessUtils.longResult(col)).isEqualTo(5);
216+
assertThat(DataAccessUtils.requiredSingleResult(col)).isEqualTo(Long.valueOf(5L));
177217
assertThat(DataAccessUtils.singleResult(col)).isEqualTo(Long.valueOf(5L));
178218
assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo(Long.valueOf(5L));
179219
assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo(Long.valueOf(5L));
@@ -190,18 +230,19 @@ void withString() {
190230
assertThat(DataAccessUtils.uniqueResult(col)).isEqualTo("test1");
191231
assertThat(DataAccessUtils.requiredUniqueResult(col)).isEqualTo("test1");
192232
assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo("test1");
233+
assertThat(DataAccessUtils.requiredSingleResult(col)).isEqualTo("test1");
193234
assertThat(DataAccessUtils.singleResult(col)).isEqualTo("test1");
194235
assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo("test1");
195236
assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo("test1");
196237
assertThat(DataAccessUtils.optionalResult(col)).isEqualTo(Optional.of("test1"));
197238
assertThat(DataAccessUtils.optionalResult(col.stream())).isEqualTo(Optional.of("test1"));
198239
assertThat(DataAccessUtils.optionalResult(col.iterator())).isEqualTo(Optional.of("test1"));
199240

200-
assertThatExceptionOfType(TypeMismatchDataAccessException.class).isThrownBy(() ->
201-
DataAccessUtils.intResult(col));
241+
assertThatExceptionOfType(TypeMismatchDataAccessException.class)
242+
.isThrownBy(() -> DataAccessUtils.intResult(col));
202243

203-
assertThatExceptionOfType(TypeMismatchDataAccessException.class).isThrownBy(() ->
204-
DataAccessUtils.longResult(col));
244+
assertThatExceptionOfType(TypeMismatchDataAccessException.class)
245+
.isThrownBy(() -> DataAccessUtils.longResult(col));
205246
}
206247

207248
@Test
@@ -214,18 +255,19 @@ void withDate() {
214255
assertThat(DataAccessUtils.requiredUniqueResult(col)).isEqualTo(date);
215256
assertThat(DataAccessUtils.objectResult(col, Date.class)).isEqualTo(date);
216257
assertThat(DataAccessUtils.objectResult(col, String.class)).isEqualTo(date.toString());
258+
assertThat(DataAccessUtils.requiredSingleResult(col)).isEqualTo(date);
217259
assertThat(DataAccessUtils.singleResult(col)).isEqualTo(date);
218260
assertThat(DataAccessUtils.singleResult(col.stream())).isEqualTo(date);
219261
assertThat(DataAccessUtils.singleResult(col.iterator())).isEqualTo(date);
220262
assertThat(DataAccessUtils.optionalResult(col)).isEqualTo(Optional.of(date));
221263
assertThat(DataAccessUtils.optionalResult(col.stream())).isEqualTo(Optional.of(date));
222264
assertThat(DataAccessUtils.optionalResult(col.iterator())).isEqualTo(Optional.of(date));
223265

224-
assertThatExceptionOfType(TypeMismatchDataAccessException.class).isThrownBy(() ->
225-
DataAccessUtils.intResult(col));
266+
assertThatExceptionOfType(TypeMismatchDataAccessException.class)
267+
.isThrownBy(() -> DataAccessUtils.intResult(col));
226268

227-
assertThatExceptionOfType(TypeMismatchDataAccessException.class).isThrownBy(() ->
228-
DataAccessUtils.longResult(col));
269+
assertThatExceptionOfType(TypeMismatchDataAccessException.class)
270+
.isThrownBy(() -> DataAccessUtils.longResult(col));
229271
}
230272

231273
@Test

0 commit comments

Comments
 (0)