Updating dynamic attributes does not update the related record

Updating (or creating) a dynamic attribute does not cause the updateTs or the version of the related record to be updated.

For example, if I update the value of attributeXYZ on recordABC, I would expect that recordABC would be updated and that the entity listener for recordABC would be fired.

Dynamic attributes work on the DataManager level, which is a higher-level mechanism comparing to ORM-based entity listeners and update timestamps. It’s just implemented this way, and actually has some advantages. For example, you can have dynamic attributes for an entity from an additional data store, so the entity record is in one database and dynamic attributes are in the other.

We wouldn’t like to introduce additional dependencies between these layers. Instead, we are thinking about extending the DataManager API to make it the main and recommended API for writing business logic.

If you really want to update timestamps and invoke entity listeners on the master entity, use a listener on the CategoryAttributeValue like this:

package com.company.playground.listener;

import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.EntityManager;
import com.haulmont.cuba.core.entity.CategoryAttributeValue;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.Updatable;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.core.global.TimeSource;
import com.haulmont.cuba.core.listener.BeforeInsertEntityListener;
import com.haulmont.cuba.core.listener.BeforeUpdateEntityListener;
import com.haulmont.cuba.core.sys.events.AppContextInitializedEvent;
import com.haulmont.cuba.core.sys.listener.EntityListenerManager;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import javax.inject.Inject;

@Component("playground_CavEntityListener")
public class CavEntityListener implements BeforeInsertEntityListener<CategoryAttributeValue>, BeforeUpdateEntityListener<CategoryAttributeValue> {

    @Inject
    private EntityListenerManager entityListenerManager;

    @Inject
    private Metadata metadata;

    @Inject
    private TimeSource timeSource;

    @EventListener(AppContextInitializedEvent.class)
    public void initEntityListener() {
        entityListenerManager.addListener(CategoryAttributeValue.class, "playground_CavEntityListener");
    }

    @Override
    public void onBeforeInsert(CategoryAttributeValue cav, EntityManager entityManager) {
        updateEntity(cav, entityManager);
    }

    @Override
    public void onBeforeUpdate(CategoryAttributeValue cav, EntityManager entityManager) {
        updateEntity(cav, entityManager);
    }

    private void updateEntity(CategoryAttributeValue cav, EntityManager entityManager) {
        MetaClass metaClass = metadata.getClassNN(cav.getCategoryAttribute().getCategoryEntityType());
        Entity entity = entityManager.find((Class<Entity>) metaClass.getJavaClass(), cav.getEntity().getEntityId());
        if (entity instanceof Updatable)
            ((Updatable) entity).setUpdateTs(timeSource.currentTimestamp());
    }
}

It should work as is for entities with UUID keys stored in the main data store.