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.
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.
- Constructor auto-mapping of
- Could not commit transaction. Cause
- Error getting generated key or setting result to parameter…
- Error selecting key or setting result to parameter object…
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/fc1eb9efe31f831e.
Report an issue: GitHub.
Appendix: 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)