flowable/flowable-engine · error · ActivitiException

no update statement for <updatedObjectClass> in the ibatis m

Error message

no update statement for <updatedObjectClass> in the ibatis mapping files

What it means

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.

Source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/db/DbSqlSession.java:825

        }

        if (persistentObjectList.get(0) instanceof HasRevision) {
            for (PersistentObject insertedObject : persistentObjectList) {
                HasRevision revisionEntity = (HasRevision) insertedObject;
                if (revisionEntity.getRevision() == 0) {
                    revisionEntity.setRevision(revisionEntity.getRevisionNext());
                }
            }
        }
    }

    protected void flushUpdates(List<PersistentObject> updatedObjects) {
        for (PersistentObject updatedObject : updatedObjects) {
            String updateStatement = dbSqlSessionFactory.getUpdateStatement(updatedObject);
            updateStatement = dbSqlSessionFactory.mapStatement(updateStatement);

            if (updateStatement == null) {
                throw new ActivitiException("no update statement for " + updatedObject.getClass() + " in the ibatis mapping files");
            }

            LOGGER.debug("updating: {}", updatedObject);
            int updatedRecords = sqlSession.update(updateStatement, updatedObject);
            if (updatedRecords != 1) {
                throw new ActivitiOptimisticLockingException(updatedObject + " was updated by another transaction concurrently");
            }

            // See https://activiti.atlassian.net/browse/ACT-1290
            if (updatedObject instanceof HasRevision) {
                ((HasRevision) updatedObject).setRevision(((HasRevision) updatedObject).getRevisionNext());
            }

        }
        updatedObjects.clear();
    }

    protected void flushDeletes(List<DeleteOperation> removedOperations) {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. 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.
  2. Audit the mapping file for the entity to ensure insert, update, and delete statements all exist with conventional ids.
  3. If the object should never be updated, exclude it from the persistence cache or mark it non-dirty rather than letting flushUpdates process it.

Example fix

// before: XML has insert/select only

// after
<update id="updateCustomEntity" parameterType="org.example.CustomEntityEntityImpl">
  UPDATE ACT_CUSTOM_ENTITY SET NAME_ = #{name}, REV_ = #{revisionNext} WHERE ID_ = #{id} AND REV_ = #{revision}
</update>
Defensive patterns

Strategy: validation

Validate before calling

// Verify update mapping exists before making the entity dirty
String stmt = dbSqlSessionFactory.getUpdateStatement(entity);
if (stmt == null || dbSqlSessionFactory.mapStatement(stmt) == null) {
    throw new IllegalStateException("No update statement mapped for " + entity.getClass().getName());
}

Try / catch

try {
    runtimeService.setVariable(executionId, "k", v); // may flush updates
} catch (ActivitiException e) {
    if (e.getMessage().contains("no update statement")) {
        throw new ConfigurationException("Update mapping missing for custom entity", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/95b7b4f1742fc777. Report an issue: GitHub.