mybatis/mybatis-3 · error · TooManyResultsException
Expected one result (or null) to be returned by selectOne(),
Error message
Expected one result (or null) to be returned by selectOne(), but found: " + list.size()
What it means
DefaultSqlSession.selectOne() runs the query as a list and, by explicit design decision (the comment records 'popular vote'), returns null for zero rows but throws TooManyResultsException when more than one row comes back. The contract is at-most-one: the statement was expected to match a single record but the database returned several.
Source
Thrown at src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java:79
public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
}
@Override
public <T> T selectOne(String statement) {
return this.selectOne(statement, null);
}
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
}
if (list.size() > 1) {
throw new TooManyResultsException(
"Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {View on GitHub (pinned to 008069adb1)
Solutions
- Tighten the WHERE clause with a unique key or add missing conditions so at most one row matches.
- If multiple rows are legitimate, switch to selectList() and take the first row explicitly (or return List<T> from the mapper).
- Clean duplicate rows and add a unique constraint where business rules say the field is unique.
- Add LIMIT 1 / FETCH FIRST 1 ROWS ONLY only if any-row semantics are acceptable.
Example fix
// before
User findByName(String name); <!-- SELECT * FROM users WHERE name = #{name} -->
// after
List<User> findByName(String name); <!-- same SQL, caller decides -->
// or ensure uniqueness:
<!-- SELECT * FROM users WHERE id = #{id} --> Defensive patterns
Strategy: try-catch
Try / catch
try { User u = mapper.findByName(name); }
catch (TooManyResultsException e) {
List<User> all = mapper.findByNameList(name); // fall back to list variant
return all.get(0); // or apply real selection logic
} Prevention
- Use selectOne only with unique-key predicates (id, unique index columns).
- Return List<T> from mappers whenever uniqueness is not guaranteed by a constraint.
- Add DB unique constraints for fields you query single-row by.
When it happens
Trigger: selectOne()/sqlSession.selectOne(statement, param) where the WHERE clause matches multiple rows: non-unique filter column, missing AND condition, LIKE filters, joins fan-out duplicating rows; also mapper interface methods returning a single object whose SQL lacks a LIMIT/unique key.
Common situations: Querying by a non-unique column (username, status); data drift inserting duplicate rows where uniqueness was assumed; dynamic <if> conditions all skipping so the query degrades to SELECT ... without WHERE; pagination removed during refactoring.
Related errors
- Statement returned {} results where exactly one (1) was expe
- Unknown execution method for: {name}
- Mapper method '{name}' attempted to return null from a metho
- Mapper method '{name}' has an unsupported return type: {retu
- method {name} needs either a @ResultMap annotation, a @Resul
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/69a08c75b35894c9.
Report an issue: GitHub.