Skip to content

Commit 05f7c0b

Browse files
committed
#3695 Behaviour change: findSingleAttribute() to throw when multiple rows returned by query
On the plus, this makes the behaviour consistent with all the other findOne() methods (ORM, DTO, SqlQuery) and this very much feels like buggy behaviour [to just get the first result and ignore any subsequent results in the ResultSet] On the negative of merging this bug fix, any application code relying on the existing behaviour with this fix will break and throw a NonUniqueResultException at RUNTIME (not ideal). However, for these cases where the application now throws an exception, people might not be aware that they were relying on this behaviour and this exception could be useful to highlight that (a potential non-deterministic query result was being used and a potential source of bugs was being).
1 parent d554780 commit 05f7c0b

File tree

2 files changed

+21
-1
lines changed

2 files changed

+21
-1
lines changed

ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultRelationalQueryEngine.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.ebeaninternal.server.core.RowReader;
1515
import io.ebeaninternal.server.persist.Binder;
1616

17+
import jakarta.persistence.NonUniqueResultException;
1718
import jakarta.persistence.PersistenceException;
1819

1920
import java.sql.SQLException;
@@ -160,13 +161,17 @@ public <T> T findSingleAttribute(RelationalQueryRequest request, Class<T> cls) {
160161
T value = null;
161162
if (dataReader.next()) {
162163
value = scalarType.read(dataReader);
164+
if (dataReader.next()) {
165+
throw new NonUniqueResultException("Got more than 1 result for findSingleAttribute");
166+
}
163167
}
164168
request.logSummary();
165169
return value;
166170

171+
} catch (NonUniqueResultException e) {
172+
throw e;
167173
} catch (Exception e) {
168174
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
169-
170175
} finally {
171176
request.close();
172177
}

ebean-test/src/test/java/org/tests/query/sqlquery/SqlQueryTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.ebean.annotation.Platform;
77
import io.ebean.meta.MetaTimedMetric;
88
import io.ebean.test.LoggedSql;
9+
import jakarta.persistence.NonUniqueResultException;
910
import org.junit.jupiter.api.Test;
1011
import org.tests.model.basic.Order;
1112
import org.tests.model.basic.ResetBasicData;
@@ -21,6 +22,7 @@
2122
import java.util.concurrent.atomic.AtomicLong;
2223

2324
import static org.assertj.core.api.Assertions.assertThat;
25+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2426
import static org.junit.jupiter.api.Assertions.assertEquals;
2527

2628
class SqlQueryTests extends BaseTestCase {
@@ -85,6 +87,19 @@ void selectFunction() {
8587
assertThat(val2).isEqualTo(11);
8688
}
8789

90+
@Test
91+
void findSingleAttribute_whenMultipleRows_expect_NonUniqueResultException() {
92+
ResetBasicData.reset();
93+
String sql = "select id from o_order order by id desc";
94+
95+
assertThatThrownBy(() -> {
96+
DB.sqlQuery(sql)
97+
.mapToScalar(Long.class)
98+
.findOne();
99+
}).isInstanceOf(NonUniqueResultException.class)
100+
.hasMessageContaining("Got more than 1 result for findSingleAttribute");
101+
}
102+
88103
@Test
89104
void findSingleAttributeList_decimal() {
90105
ResetBasicData.reset();

0 commit comments

Comments
 (0)