{"record":{"id":"367470c1ba28fc2d","repo":"mybatis/mybatis-3","slug":"no-setter-found-for-the-keyproperty-propertynam","errorCode":null,"errorMessage":"No setter found for the keyProperty '${propertyName}' in '${className}'.","messagePattern":"No setter found for the keyProperty '(.+?)' in '(.+?)'\\.","errorType":"exception","errorClass":"ExecutorException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java","lineNumber":266,"sourceCode":"        String propertyName) {\n      this.configuration = configuration;\n      this.rsmd = rsmd;\n      this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();\n      this.columnPosition = columnPosition;\n      this.paramName = paramName;\n      this.propertyName = propertyName;\n    }\n\n    protected void assign(ResultSet rs, Object param) {\n      if (paramName != null) {\n        // If paramName is set, param is ParamMap\n        param = ((ParamMap<?>) param).get(paramName);\n      }\n      MetaObject metaParam = configuration.newMetaObject(param);\n      try {\n        if (typeHandler == null) {\n          if (!metaParam.hasSetter(propertyName)) {\n            throw new ExecutorException(\"No setter found for the keyProperty '\" + propertyName + \"' in '\"\n                + metaParam.getOriginalObject().getClass().getName() + \"'.\");\n          }\n          Type propertyType = metaParam.getGenericSetterType(propertyName).getKey();\n          JdbcType jdbcType = JdbcType.forCode(rsmd.getColumnType(columnPosition));\n          typeHandler = typeHandlerRegistry.getTypeHandler(propertyType, jdbcType);\n          if (typeHandler == null) {\n            typeHandler = typeHandlerRegistry.getTypeHandler(jdbcType);\n          }\n        }\n        if (typeHandler == null) {\n          // Error?\n        } else {\n          Object value = typeHandler.getResult(rs, columnPosition);\n          metaParam.setValue(propertyName, value);\n        }\n      } catch (SQLException e) {\n        throw new ExecutorException(\"Error getting generated key or setting result to parameter object. Cause: \" + e,\n            e);","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java#L248-L284","documentation":"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.","triggerScenarios":"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).","commonSituations":"Immutable parameter objects (no setters), Lombok @Getter-only classes, records, or property-name casing mistakes (id vs Id).","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"],"exampleFix":"// before\nclass User { private Long id; public Long getId() { return id; } } // no setter\n\n// after\nclass User { private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } }","handlingStrategy":"validation","validationCode":"// Fail fast at startup that the insert parameter has a writable keyProperty\nMetaObject mo = configuration.newMetaObject(new User());\nif (!mo.hasSetter(\"id\")) throw new IllegalStateException(\"User needs setId for useGeneratedKeys\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep insert parameter classes as mutable POJOs with setters","Add an integration test that reads back the id after every insert"],"tags":["generated-keys","setter","reflection"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}