baomidou/mybatis-plus · 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: %s
What it means
BaseMapper.selectOne(wrapper, throwEx=true) executed the query, received more than one row, and threw TooManyResultsException because strict mode was requested. With throwEx=false it would silently return the first row; the boolean overload exists to make one-row expectations explicit.
Source
Thrown at mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/mapper/BaseMapper.java:341
default T selectOne(Wrapper<T> queryWrapper) {
return this.selectOne(queryWrapper, true);
}
/**
* 根据 entity 条件,查询一条记录,现在会根据{@code throwEx}参数判断是否抛出异常,如果为false就直接返回一条数据
* <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常</p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
* @param throwEx boolean 参数,为true如果存在多个结果直接抛出异常
*/
default T selectOne(Wrapper<T> queryWrapper, boolean throwEx) {
List<T> list = this.selectList(queryWrapper);
int size = list.size();
if (size == 1) {
return list.get(0);
} else if (size > 1) {
if (throwEx) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + size);
}
return list.get(0);
}
return null;
}
/**
* 根据 Wrapper 条件,游标方式查询全部记录
* <p>注意!需要在事务中 @Transactional 注解方法,或者手动管理 sqlSession 避免会话中断</p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Cursor<T> selectWithCursor(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,判断是否存在记录
*
* @param queryWrapper 实体对象封装操作类View on GitHub (pinned to bf67d90747)
Solutions
- Decide intent: if duplicates are legitimate, pass throwEx=false or add qw.last("LIMIT 1")
- If one row is the contract, fix the data (add a unique constraint) and the query predicate so it truly selects one row
- Inspect the rows returned by the same wrapper via selectList to understand the duplication
Example fix
// before
User u = userMapper.selectOne(qw, true); // throws if duplicates exist
// after (dupes acceptable, want first)
User u = userMapper.selectOne(qw.last("LIMIT 1"));
// after (dupes are a bug)
// dedupe data + ALTER TABLE user ADD UNIQUE KEY uk_email (email); Defensive patterns
Strategy: validation
Validate before calling
// Pre-check when duplicates are possible
Long cnt = userMapper.selectCount(qw);
if (cnt != null && cnt > 1) {
// decide: first row, or fail with context
log.warn("selectOne matched {} rows", cnt);
}
User u = cnt != null && cnt == 1 ? userMapper.selectOne(qw) : userMapper.selectOne(qw.last("LIMIT 1")); Try / catch
try { return userMapper.selectOne(qw, true); } catch (TooManyResultsException e) { log.warn("Duplicate rows for filter, returning first"); return userMapper.selectOne(qw.last("LIMIT 1")); } // only when duplicates are acceptable Prevention
- Enforce uniqueness in the schema (unique constraints) for fields used in selectOne filters
- Prefer selectOne(qw.last("LIMIT 1")) when the data model allows ties
- Add data-quality checks/alerts on tables queried with selectOne
When it happens
Trigger: Calling selectOne(queryWrapper, true) where the query matches 2+ rows — e.g. a unique-ish filter that is not actually unique, or missing a limit while duplicate data exists.
Common situations: Assuming a field is unique when the table contains duplicates; race between check and insert creating duplicates; upgrading code from selectOne(wrapper) (which also throws by default in MyBatis) and hitting dirty data.
Related errors
- %s already contains value for %s
- %s does not contain value for %s
- %s is ambiguous in %s (try using the full name including the
- Should be specified either value() or name() attribute in th
- Cannot use both value() and name() attribute in the @CacheNa
AI-assisted analysis of baomidou/mybatis-plus@bf67d90747 (2026-08-14).
Data as JSON: /api/errors/5d89810be6b941d1.
Report an issue: GitHub.