{"record":{"id":"15ec7b2d1d04d986","repo":"flowable/flowable-engine","slug":"no-insert-statement-for-persistentobjectclass-in","errorCode":null,"errorMessage":"no insert statement for <persistentObjectClass> in the ibatis mapping files","messagePattern":"no insert statement for <persistentObjectClass> 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":774,"sourceCode":"    protected void flushPersistentObjects(Class<? extends PersistentObject> persistentObjectClass, List<PersistentObject> persistentObjectsToInsert) {\n        if (persistentObjectsToInsert.size() == 1) {\n            flushRegularInsert(persistentObjectsToInsert.get(0), persistentObjectClass);\n        } else if (Boolean.FALSE.equals(dbSqlSessionFactory.isBulkInsertable(persistentObjectClass))) {\n            for (PersistentObject persistentObject : persistentObjectsToInsert) {\n                flushRegularInsert(persistentObject, persistentObjectClass);\n            }\n        } else {\n            flushBulkInsert(insertedObjects.get(persistentObjectClass), persistentObjectClass);\n        }\n        insertedObjects.remove(persistentObjectClass);\n    }\n\n    protected void flushRegularInsert(PersistentObject persistentObject, Class<? extends PersistentObject> clazz) {\n        String insertStatement = dbSqlSessionFactory.getInsertStatement(persistentObject);\n        insertStatement = dbSqlSessionFactory.mapStatement(insertStatement);\n\n        if (insertStatement == null) {\n            throw new ActivitiException(\"no insert statement for \" + persistentObject.getClass() + \" in the ibatis mapping files\");\n        }\n\n        LOGGER.debug(\"inserting: {}\", persistentObject);\n        sqlSession.insert(insertStatement, persistentObject);\n\n        // See https://activiti.atlassian.net/browse/ACT-1290\n        if (persistentObject instanceof HasRevision) {\n            HasRevision revisionEntity = (HasRevision) persistentObject;\n            if (revisionEntity.getRevision() == 0) {\n                revisionEntity.setRevision(revisionEntity.getRevisionNext());\n            }\n        }\n    }\n\n    protected void flushBulkInsert(List<PersistentObject> persistentObjectList, Class<? extends PersistentObject> clazz) {\n        String insertStatement = dbSqlSessionFactory.getBulkInsertStatement(clazz);\n        insertStatement = dbSqlSessionFactory.mapStatement(insertStatement);\n","sourceCodeStart":756,"sourceCodeEnd":792,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/db/DbSqlSession.java#L756-L792","documentation":"flushRegularInsert() resolves the insert statement name for a single persistent object via dbSqlSessionFactory.getInsertStatement(persistentObject) and mapStatement(). If the resolved statement is null — no 'insert' mapping exists for that entity class in the loaded iBatis/MyBatis mapping files — the flush throws this ActivitiException before executing SQL. It signals a persistence-mapping gap for the entity being inserted.","triggerScenarios":"Flushing a DbSqlSession that contains a cached/inserted PersistentObject whose class has no insert statement mapping, e.g. a custom entity added via a custom Session/EntityManager without registering its insert statement in the mapping XML.","commonSituations":"Extending the engine with custom entities and forgetting to add the <insert> statement or to load the new XML in the configuration; class rename/refactor breaking the statement-name convention; old custom mappings incompatible after an engine upgrade.","solutions":["Add the insert mapped statement for the entity class (correct id per the naming convention, e.g. 'insert.selectKey'+'insert' pattern used by DbSqlSessionFactory) to its mapping XML and register the XML in the engine configuration's mapping resources.","Check DbSqlSessionFactory.getInsertStatement/mapStatement for how the statement name is derived and ensure a custom subclass returns a valid, mapped name for the entity.","Confirm the statement id in XML matches exactly (case-sensitive) what getInsertStatement returns; fix typos in the id."],"exampleFix":"// before\n// CustomEntity.xml has only <select>, no <insert> → null statement\n\n// after\n<insert id=\"insertCustomEntity\" parameterType=\"org.example.CustomEntityEntityImpl\">\n  INSERT INTO ACT_CUSTOM_ENTITY (ID_, NAME_) VALUES (#{id}, #{name})\n</insert>","handlingStrategy":"validation","validationCode":"// Check the insert statement resolves before inserting\nString stmt = dbSqlSessionFactory.getInsertStatement(entity);\nif (stmt == null || dbSqlSessionFactory.mapStatement(stmt) == null) {\n    throw new IllegalStateException(\"No insert statement mapped for \" + entity.getClass().getName());\n}","typeGuard":null,"tryCatchPattern":"try {\n    taskService.saveTask(customTask); // triggers flush insert\n} catch (ActivitiException e) {\n    if (e.getMessage().contains(\"no insert statement\")) {\n        // mapping file missing/not loaded — fail fast with a clear message\n        throw new ConfigurationException(\"CustomTask mapping XML not registered\", e);\n    }\n    throw e;\n}","preventionTips":["Add the <insert> statement to each custom entity's mapping XML before first use.","Include all mapping XMLs in engine configuration mapping resources and verify with a startup CRUD test.","Keep statement ids aligned with the DbSqlSessionFactory naming convention when refactoring class names."],"tags":["database","mybatis","mapping","insert"],"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"}