mybatis/mybatis-3 · error · ExecutorException
No setter found for the keyProperty '${propertyName}' in '${
Error message
No setter found for the keyProperty '${propertyName}' in '${className}'. What it means
KeyAssigner.assign writes a generated key into the target parameter object via reflection. Before writing it verifies with MetaObject.hasSetter that the parameter object actually has a setter for the configured keyProperty; if not, it throws this ExecutorException naming the missing property and the target class. Note the check is against the object reached after resolving any paramName prefix.
Source
Thrown at src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java:266
String propertyName) {
this.configuration = configuration;
this.rsmd = rsmd;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.columnPosition = columnPosition;
this.paramName = paramName;
this.propertyName = propertyName;
}
protected void assign(ResultSet rs, Object param) {
if (paramName != null) {
// If paramName is set, param is ParamMap
param = ((ParamMap<?>) param).get(paramName);
}
MetaObject metaParam = configuration.newMetaObject(param);
try {
if (typeHandler == null) {
if (!metaParam.hasSetter(propertyName)) {
throw new ExecutorException("No setter found for the keyProperty '" + propertyName + "' in '"
+ metaParam.getOriginalObject().getClass().getName() + "'.");
}
Type propertyType = metaParam.getGenericSetterType(propertyName).getKey();
JdbcType jdbcType = JdbcType.forCode(rsmd.getColumnType(columnPosition));
typeHandler = typeHandlerRegistry.getTypeHandler(propertyType, jdbcType);
if (typeHandler == null) {
typeHandler = typeHandlerRegistry.getTypeHandler(jdbcType);
}
}
if (typeHandler == null) {
// Error?
} else {
Object value = typeHandler.getResult(rs, columnPosition);
metaParam.setValue(propertyName, value);
}
} catch (SQLException e) {
throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e,
e);View on GitHub (pinned to 008069adb1)
Solutions
- Add a setter for the keyProperty on the parameter class (or make it a normal mutable POJO)
- Fix the property name spelling/case to match the class's setter
- If keyProperty includes a parameter prefix, verify it resolves to the right nested object
Example fix
// before
class User { private Long id; public Long getId() { return id; } } // no setter
// after
class User { private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } } Defensive patterns
Strategy: validation
Validate before calling
// Fail fast at startup that the insert parameter has a writable keyProperty
MetaObject mo = configuration.newMetaObject(new User());
if (!mo.hasSetter("id")) throw new IllegalStateException("User needs setId for useGeneratedKeys"); Prevention
- Keep insert parameter classes as mutable POJOs with setters
- Add an integration test that reads back the id after every insert
When it happens
Trigger: useGeneratedKeys with keyProperty="userId" when the parameter object only has a field/getter but no setter, or the property name is misspelled, or keyProperty still contains a parameter prefix that was not stripped (checked against the raw object).
Common situations: Immutable parameter objects (no setters), Lombok @Getter-only classes, records, or property-name casing mistakes (id vs Id).
Related errors
- No setter found for the keyProperty '" + property + "' in "
- There is no setter for property named '{}' in '{}'
- Could not set property '{}' of '{}' with value '{}' Cause: {
- Error creating instance. Cause: {cause}
- Error in result map '{resultMapId}'. Failed to find a constr
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/367470c1ba28fc2d.
Report an issue: GitHub.