mybatis/mybatis-3 · error · ExecutorException
SelectKey returned more than one value.
Error message
SelectKey returned more than one value.
What it means
SelectKeyGenerator requires the <selectKey> statement to produce exactly one row, because a single key value is assigned to one keyProperty set. If the query returns two or more rows, MyBatis cannot decide which value is the key and throws this ExecutorException.
Source
Thrown at src/main/java/org/apache/ibatis/executor/keygen/SelectKeyGenerator.java:72
processGeneratedKeys(executor, ms, parameter);
}
}
private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
try {
if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
String[] keyProperties = keyStatement.getKeyProperties();
final Configuration configuration = ms.getConfiguration();
final MetaObject metaParam = configuration.newMetaObject(parameter);
// Do not close keyExecutor.
// The transaction will be closed by parent executor.
Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
if (values.isEmpty()) {
throw new ExecutorException("SelectKey returned no data.");
}
if (values.size() > 1) {
throw new ExecutorException("SelectKey returned more than one value.");
} else {
MetaObject metaResult = configuration.newMetaObject(values.get(0));
if (keyProperties.length == 1) {
if (metaResult.hasGetter(keyProperties[0])) {
setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
} else {
// no getter for the property - maybe just a single value object
// so try that
setValue(metaParam, keyProperties[0], values.get(0));
}
} else {
handleMultipleProperties(keyProperties, metaParam, metaResult);
}
}
}
} catch (ExecutorException e) {
throw e;
} catch (Exception e) {View on GitHub (pinned to 008069adb1)
Solutions
- Constrain the selectKey SQL to one row: use aggregates (MAX), LIMIT 1 / FETCH FIRST 1 ROWS ONLY, or a proper sequence
- Add uniqueness guarantees (unique index) for lookup-based key strategies
- Test the selectKey SQL in a DB client against production-like data
Example fix
<!-- before -->
<selectKey keyProperty="id" resultType="long" order="BEFORE">
SELECT id FROM codes WHERE name = #{name}
</selectKey>
<!-- after -->
<selectKey keyProperty="id" resultType="long" order="BEFORE">
SELECT id FROM codes WHERE name = #{name} LIMIT 1
</selectKey> Defensive patterns
Strategy: validation
Validate before calling
// For lookup-based selectKeys, assert uniqueness first
Integer count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM codes WHERE name=?", Integer.class, name);
if (count != null && count > 1) throw new IllegalStateException("selectKey lookup not unique"); Try / catch
try { mapper.insert(entity); } catch (PersistenceException e) { if (String.valueOf(e.getMessage()).contains("more than one value")) { /* constrain the selectKey query */ } throw e; } Prevention
- Constrain selectKey SQL with aggregates or LIMIT 1
- Add unique indexes for columns used in key lookups
When it happens
Trigger: A <selectKey> like SELECT id FROM t WHERE name = #{name} matching duplicates; a sequence select accidentally returning multiple rows (bad FROM clause, join fan-out); MAX(id) queries without aggregation over a filtered join.
Common situations: Non-unique lookup columns inside selectKey; forgetting FROM DUAL on Oracle scalar selects; test data introducing duplicates.
Related errors
- SelectKey returned no data.
- Statement returned more than one row, where no more than one
- Error getting generated key or setting result to parameter o
- Error selecting key or setting result to parameter object. C
- If SelectKey has key columns, the number must match the numb
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/fc1eb9efe31f831e.
Report an issue: GitHub.