{"record":{"id":"95b7b4f1742fc777","repo":"flowable/flowable-engine","slug":"no-update-statement-for-updatedobjectclass-in-th","errorCode":null,"errorMessage":"no update statement for <updatedObjectClass> in the ibatis mapping files","messagePattern":"no update statement for <updatedObjectClass> in the ibatis mapping files","errorType":"exception","errorClass":"ActivitiException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/db/DbSqlSession.java","lineNumber":825,"sourceCode":"        }\n\n        if (persistentObjectList.get(0) instanceof HasRevision) {\n            for (PersistentObject insertedObject : persistentObjectList) {\n                HasRevision revisionEntity = (HasRevision) insertedObject;\n                if (revisionEntity.getRevision() == 0) {\n                    revisionEntity.setRevision(revisionEntity.getRevisionNext());\n                }\n            }\n        }\n    }\n\n    protected void flushUpdates(List<PersistentObject> updatedObjects) {\n        for (PersistentObject updatedObject : updatedObjects) {\n            String updateStatement = dbSqlSessionFactory.getUpdateStatement(updatedObject);\n            updateStatement = dbSqlSessionFactory.mapStatement(updateStatement);\n\n            if (updateStatement == null) {\n                throw new ActivitiException(\"no update statement for \" + updatedObject.getClass() + \" in the ibatis mapping files\");\n            }\n\n            LOGGER.debug(\"updating: {}\", updatedObject);\n            int updatedRecords = sqlSession.update(updateStatement, updatedObject);\n            if (updatedRecords != 1) {\n                throw new ActivitiOptimisticLockingException(updatedObject + \" was updated by another transaction concurrently\");\n            }\n\n            // See https://activiti.atlassian.net/browse/ACT-1290\n            if (updatedObject instanceof HasRevision) {\n                ((HasRevision) updatedObject).setRevision(((HasRevision) updatedObject).getRevisionNext());\n            }\n\n        }\n        updatedObjects.clear();\n    }\n\n    protected void flushDeletes(List<DeleteOperation> removedOperations) {","sourceCodeStart":807,"sourceCodeEnd":843,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/db/DbSqlSession.java#L807-L843","documentation":"flushUpdates() resolves the update statement for each dirty persistent object via dbSqlSessionFactory.getUpdateStatement(updatedObject) and mapStatement(). A null result means no update statement is mapped for that class in the iBatis/MyBatis mapping files, and the flush throws this ActivitiException. This is a mapping-configuration error for the update path of the entity.","triggerScenarios":"A PersistentObject becomes dirty during a command and flushUpdates runs; the class has no <update> statement id matching what getUpdateStatement returns, or its mapping XML is not loaded into the engine configuration.","commonSituations":"Custom entities registered as updateable without an update mapping; refactoring that renamed the statement id; partial mapping files where insert/select exist but update was omitted.","solutions":["Add the <update> mapped statement with the id expected by getUpdateStatement (including optimistic-locking REV_ = REV_ + 1 predicate) to the entity's mapping XML and load it in the configuration.","Audit the mapping file for the entity to ensure insert, update, and delete statements all exist with conventional ids.","If the object should never be updated, exclude it from the persistence cache or mark it non-dirty rather than letting flushUpdates process it."],"exampleFix":"// before: XML has insert/select only\n\n// after\n<update id=\"updateCustomEntity\" parameterType=\"org.example.CustomEntityEntityImpl\">\n  UPDATE ACT_CUSTOM_ENTITY SET NAME_ = #{name}, REV_ = #{revisionNext} WHERE ID_ = #{id} AND REV_ = #{revision}\n</update>","handlingStrategy":"validation","validationCode":"// Verify update mapping exists before making the entity dirty\nString stmt = dbSqlSessionFactory.getUpdateStatement(entity);\nif (stmt == null || dbSqlSessionFactory.mapStatement(stmt) == null) {\n    throw new IllegalStateException(\"No update statement mapped for \" + entity.getClass().getName());\n}","typeGuard":null,"tryCatchPattern":"try {\n    runtimeService.setVariable(executionId, \"k\", v); // may flush updates\n} catch (ActivitiException e) {\n    if (e.getMessage().contains(\"no update statement\")) {\n        throw new ConfigurationException(\"Update mapping missing for custom entity\", e);\n    }\n    throw e;\n}","preventionTips":["Author update statements (with REV_ optimistic-lock predicate) for all custom entities.","Audit mapping XMLs so insert/update/delete exist together for each entity.","Add a CI test that creates, mutates, and saves each custom entity to exercise the update path."],"tags":["database","mybatis","mapping","update"],"backgroundTag":"resource-not-found","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}