flowable/flowable-engine · error · ActivitiException
no insert statement for <persistentObjectClass> in the ibati
Error message
no insert statement for <persistentObjectClass> in the ibatis mapping files
What it means
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.
Source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/db/DbSqlSession.java:774
protected void flushPersistentObjects(Class<? extends PersistentObject> persistentObjectClass, List<PersistentObject> persistentObjectsToInsert) {
if (persistentObjectsToInsert.size() == 1) {
flushRegularInsert(persistentObjectsToInsert.get(0), persistentObjectClass);
} else if (Boolean.FALSE.equals(dbSqlSessionFactory.isBulkInsertable(persistentObjectClass))) {
for (PersistentObject persistentObject : persistentObjectsToInsert) {
flushRegularInsert(persistentObject, persistentObjectClass);
}
} else {
flushBulkInsert(insertedObjects.get(persistentObjectClass), persistentObjectClass);
}
insertedObjects.remove(persistentObjectClass);
}
protected void flushRegularInsert(PersistentObject persistentObject, Class<? extends PersistentObject> clazz) {
String insertStatement = dbSqlSessionFactory.getInsertStatement(persistentObject);
insertStatement = dbSqlSessionFactory.mapStatement(insertStatement);
if (insertStatement == null) {
throw new ActivitiException("no insert statement for " + persistentObject.getClass() + " in the ibatis mapping files");
}
LOGGER.debug("inserting: {}", persistentObject);
sqlSession.insert(insertStatement, persistentObject);
// See https://activiti.atlassian.net/browse/ACT-1290
if (persistentObject instanceof HasRevision) {
HasRevision revisionEntity = (HasRevision) persistentObject;
if (revisionEntity.getRevision() == 0) {
revisionEntity.setRevision(revisionEntity.getRevisionNext());
}
}
}
protected void flushBulkInsert(List<PersistentObject> persistentObjectList, Class<? extends PersistentObject> clazz) {
String insertStatement = dbSqlSessionFactory.getBulkInsertStatement(clazz);
insertStatement = dbSqlSessionFactory.mapStatement(insertStatement);
View on GitHub (pinned to d6d39ce1c6)
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.
Example fix
// before
// CustomEntity.xml has only <select>, no <insert> → null statement
// after
<insert id="insertCustomEntity" parameterType="org.example.CustomEntityEntityImpl">
INSERT INTO ACT_CUSTOM_ENTITY (ID_, NAME_) VALUES (#{id}, #{name})
</insert> Defensive patterns
Strategy: validation
Validate before calling
// Check the insert statement resolves before inserting
String stmt = dbSqlSessionFactory.getInsertStatement(entity);
if (stmt == null || dbSqlSessionFactory.mapStatement(stmt) == null) {
throw new IllegalStateException("No insert statement mapped for " + entity.getClass().getName());
} Try / catch
try {
taskService.saveTask(customTask); // triggers flush insert
} catch (ActivitiException e) {
if (e.getMessage().contains("no insert statement")) {
// mapping file missing/not loaded — fail fast with a clear message
throw new ConfigurationException("CustomTask mapping XML not registered", e);
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- no bulk delete statement for <persistentObjectClass> in the
- no update statement for <updatedObjectClass> in the ibatis m
- no insert statement for ${entity.getClass()} in the ibatis m
- JDBC requires that the JdbcType must be specified for all nu
- Error setting null for parameter #${i} with JdbcType ${jdbcT
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/15ec7b2d1d04d986.
Report an issue: GitHub.