flowable/flowable-engine · error · ActivitiException
no bulk delete statement for <persistentObjectClass> in the
Error message
no bulk delete statement for <persistentObjectClass> in the mapping files
What it means
DbSqlSession.execute() performs a bulk delete of persistent objects by looking up a mapped iBatis/MyBatis statement via dbSqlSessionFactory.getBulkDeleteStatement(persistentObjectClass). When the resolved statement name maps to null, meaning no XML mapping file defines a bulk delete for that entity class, it throws this ActivitiException before touching the database. It is a mapping-configuration error: the entity participates in bulk delete but has no corresponding SQL defined.
Source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/db/DbSqlSession.java:300
@Override
public void clearCache() {
for (PersistentObject persistentObject : persistentObjects) {
cacheRemove(persistentObject.getClass(), persistentObject.getId());
}
}
@Override
public void execute() {
if (persistentObjects.isEmpty()) {
return;
}
String bulkDeleteStatement = dbSqlSessionFactory.getBulkDeleteStatement(persistentObjectClass);
bulkDeleteStatement = dbSqlSessionFactory.mapStatement(bulkDeleteStatement);
if (bulkDeleteStatement == null) {
throw new ActivitiException("no bulk delete statement for " + persistentObjectClass + " in the mapping files");
}
// It only makes sense to check for optimistic locking exceptions for objects that actually have a revision
if (persistentObjects.get(0) instanceof HasRevision) {
int nrOfRowsDeleted = sqlSession.delete(bulkDeleteStatement, persistentObjects);
if (nrOfRowsDeleted < persistentObjects.size()) {
throw new ActivitiOptimisticLockingException("One of the entities " + persistentObjectClass
+ " was updated by another transaction concurrently while trying to do a bulk delete");
}
} else {
sqlSession.delete(bulkDeleteStatement, persistentObjects);
}
}
@Override
public Class<? extends PersistentObject> getPersistentObjectClass() {
return persistentObjectClass;
}View on GitHub (pinned to d6d39ce1c6)
Solutions
- Add a bulk delete mapped statement (e.g. deleteXxx with the expected id) for the entity's class in the entity's MyBatis XML mapping file and ensure that XML is loaded into the process engine configuration's mapping resources.
- Verify the entity class is correctly registered in DbSqlSessionFactory's getBulkDeleteStatement/mapStatement logic (custom subclasses must override/extend the statement mapping).
- If the entity should not be bulk-deleted, change the calling code to delete entities individually (sqlSession.delete per object) instead of the bulk path.
Example fix
// before: custom entity with no mapping
processEngineConfiguration.setMyBatisMappingFiles(...); // CustomTask.xml missing
// after: CustomTask.xml
<mapper namespace="org.activiti.persistence.impl.CustomTaskEntityImpl">
<delete id="bulkDeleteCustomTask" parameterType="java.util.List">
DELETE FROM ACT_CUSTOM_TASK WHERE ID_ IN (...)
</delete>
</mapper> Defensive patterns
Strategy: validation
Validate before calling
// Verify a bulk delete statement is mapped before executing
String stmt = dbSqlSessionFactory.getBulkDeleteStatement(MyEntity.class);
if (stmt == null || dbSqlSessionFactory.mapStatement(stmt) == null) {
throw new IllegalStateException(
"Add a bulk delete statement for " + MyEntity.class.getName() + " to the MyBatis mapping XML");
} Prevention
- For every custom PersistentObject, author insert, update, delete AND bulk-delete statements before wiring it into the engine.
- Keep a smoke test that runs a full CRUD cycle per custom entity at startup or in CI.
- Register all custom mapping XML files in the process engine configuration's mapping resources.
When it happens
Trigger: Calling ContextCommandContext or DbSqlSession.execute() with a bulk-delete operation whose persistentObjectClass (e.g. a custom entity, or one registered via a custom SessionFactory) has no 'bulkDelete' statement registered in dbSqlSessionFactory's statement mappings and no matching id in any Activiti/Flowable XML mapping file.
Common situations: Adding a custom PersistentObject entity and registering it for deletion without authoring the mapping XML; upgrading the engine while using a custom DbSqlSessionFactory that lost statement mappings; typo in the mapped statement name so mapStatement() returns null.
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 insert statement for <persistentObjectClass> in the ibati
- 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/9015951a879da5ec.
Report an issue: GitHub.