You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2023/06/01 16:08:24 UTC

[causeway] 01/01: CAUSEWAY-3324: some trivial refactoring

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 3324-refactor_entity_prop_change_publishing
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 51eb93506183eb3215678823b31a9a6429b59adc
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Jun 1 18:08:16 2023 +0200

    CAUSEWAY-3324: some trivial refactoring
---
 .../core/metamodel/object/MmEntityUtils.java       | 49 ++++++++++++++++------
 .../objectlifecycle/ObjectLifecyclePublisher.java  |  9 ++--
 .../publish/ObjectLifecyclePublisherDefault.java   | 47 ++++++++++++---------
 .../changetracking/EntityChangeTracker.java        | 23 +++-------
 .../changetracking/EntityChangeTrackerDefault.java | 37 ++++++----------
 .../applib/integration/CausewayEntityListener.java | 19 ++-------
 6 files changed, 90 insertions(+), 94 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmEntityUtils.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmEntityUtils.java
index d468eb1efb..cd174e76e1 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmEntityUtils.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmEntityUtils.java
@@ -19,6 +19,7 @@
 package org.apache.causeway.core.metamodel.object;
 
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import org.springframework.lang.Nullable;
 
@@ -27,6 +28,10 @@ import org.apache.causeway.commons.internal.assertions._Assert;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.config.beans.PersistenceStack;
 import org.apache.causeway.core.metamodel.facets.object.entity.EntityFacet;
+import org.apache.causeway.core.metamodel.facets.properties.property.entitychangepublishing.EntityPropertyChangePublishingPolicyFacet;
+import org.apache.causeway.core.metamodel.services.objectlifecycle.PropertyChangeRecordId;
+import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
+import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
 
 import lombok.NonNull;
 import lombok.val;
@@ -36,7 +41,7 @@ import lombok.experimental.UtilityClass;
 public final class MmEntityUtils {
 
     @NonNull
-    public static Optional<PersistenceStack> getPersistenceStandard(final @Nullable ManagedObject adapter) {
+    public Optional<PersistenceStack> getPersistenceStandard(final @Nullable ManagedObject adapter) {
         if(adapter==null) {
             return Optional.empty();
         }
@@ -50,27 +55,27 @@ public final class MmEntityUtils {
     }
 
     @NonNull
-    public static EntityState getEntityState(final @Nullable ManagedObject adapter) {
+    public EntityState getEntityState(final @Nullable ManagedObject adapter) {
         return adapter!=null
              ? adapter.getEntityState()
              : EntityState.NOT_PERSISTABLE;
     }
 
-    public static void persistInCurrentTransaction(final ManagedObject managedObject) {
+    public void persistInCurrentTransaction(final ManagedObject managedObject) {
         requiresEntity(managedObject);
         val spec = managedObject.getSpecification();
         val entityFacet = spec.entityFacetElseFail();
         entityFacet.persist(managedObject.getPojo());
     }
 
-    public static void destroyInCurrentTransaction(final ManagedObject managedObject) {
+    public void destroyInCurrentTransaction(final ManagedObject managedObject) {
         requiresEntity(managedObject);
         val spec = managedObject.getSpecification();
         val entityFacet = spec.entityFacetElseFail();
         entityFacet.delete(managedObject.getPojo());
     }
 
-    public static void requiresEntity(final ManagedObject managedObject) {
+    public void requiresEntity(final ManagedObject managedObject) {
         if(ManagedObjects.isNullOrUnspecifiedOrEmpty(managedObject)) {
             throw _Exceptions.illegalArgument("requires an entity object but got null, unspecified or empty");
         }
@@ -87,7 +92,7 @@ public final class MmEntityUtils {
      * As a side-effect transitions a transient entity to a bookmarked one. For bookmarked entities,
      * or any non-entity types acts as a no-op.
      */
-    public static void ifHasNoOidThenFlush(final @Nullable ManagedObject entity) {
+    public void ifHasNoOidThenFlush(final @Nullable ManagedObject entity) {
         if(ManagedObjects.isNullOrUnspecifiedOrEmpty(entity)
                 || !entity.getSpecialization().isEntity()
                 || entity.isBookmarkMemoized()) {
@@ -103,7 +108,7 @@ public final class MmEntityUtils {
     /**
      * Side-effect free check for whether given entity is attached.
      */
-    public static boolean isAttachedEntity(final @Nullable ManagedObject entity) {
+    public boolean isAttachedEntity(final @Nullable ManagedObject entity) {
         return entity!=null
                 ? entity.getSpecialization().isEntity()
                     && entity.isBookmarkMemoized()
@@ -117,7 +122,7 @@ public final class MmEntityUtils {
      * @throws AssertionError if managedObject is a detached entity
      */
     @NonNull
-    public static ManagedObject requiresAttached(final @NonNull ManagedObject managedObject) {
+    public ManagedObject requiresAttached(final @NonNull ManagedObject managedObject) {
         if(managedObject instanceof PackedManagedObject) {
             ((PackedManagedObject)managedObject).unpack().forEach(MmEntityUtils::requiresAttached);
             return managedObject;
@@ -134,7 +139,7 @@ public final class MmEntityUtils {
         return managedObject;
     }
 
-    public static void requiresWhenFirstIsBookmarkableSecondIsAlso(
+    public void requiresWhenFirstIsBookmarkableSecondIsAlso(
             final ManagedObject first,
             final ManagedObject second) {
 
@@ -154,18 +159,38 @@ public final class MmEntityUtils {
         }
     }
 
+    // -- PROPERTY CHANGE PUBLISHING
+
+    public Stream<OneToOneAssociation> streamPropertiesEnabledForChangePublishing(final @NonNull ManagedObject entity) {
+        return entity.getSpecification().streamProperties(MixedIn.EXCLUDED)
+            .filter(property->!EntityPropertyChangePublishingPolicyFacet.isExcludedFromPublishing(property));
+    }
+
+    public Stream<PropertyChangeRecordId> streamPropertyChangeRecordIdsForChangePublishing(final @NonNull ManagedObject entity) {
+        return streamPropertiesEnabledForChangePublishing(entity)
+                .map(property->PropertyChangeRecordId.of(entity, property));
+    }
+
+    public Optional<OneToOneAssociation> lookupPropertyEnabledForChangePublishing(
+            final @NonNull ManagedObject entity, final String propertyName) {
+        return entity
+                .getSpecification()
+                .getProperty(propertyName, MixedIn.EXCLUDED)
+                .filter(property -> !EntityPropertyChangePublishingPolicyFacet.isExcludedFromPublishing(property));
+    }
+
     // -- SHORTCUTS
 
-    public static boolean hasOid(final @Nullable ManagedObject adapter) {
+    public boolean hasOid(final @Nullable ManagedObject adapter) {
         return MmEntityUtils.getEntityState(adapter).hasOid();
     }
 
-    public static boolean isDetachedCannotReattach(final @Nullable ManagedObject adapter) {
+    public boolean isDetachedCannotReattach(final @Nullable ManagedObject adapter) {
         return MmEntityUtils.getEntityState(adapter).isDetachedCannotReattach();
     }
 
     /** TODO very strange logic */
-    public static boolean isDeleted(final @Nullable ManagedObject entity) {
+    public boolean isDeleted(final @Nullable ManagedObject entity) {
         val state = MmEntityUtils.getEntityState(entity);
         return state.isDetached()
                 || state.isRemoved()
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/objectlifecycle/ObjectLifecyclePublisher.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/objectlifecycle/ObjectLifecyclePublisher.java
index 6b83c7becb..811ec87cf2 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/objectlifecycle/ObjectLifecyclePublisher.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/objectlifecycle/ObjectLifecyclePublisher.java
@@ -18,6 +18,8 @@
  */
 package org.apache.causeway.core.metamodel.services.objectlifecycle;
 
+import java.util.function.Function;
+
 import org.springframework.lang.Nullable;
 
 import org.apache.causeway.applib.services.factory.FactoryService;
@@ -92,9 +94,10 @@ public interface ObjectLifecyclePublisher {
      * </p>
      *
      * @param entity
-     * @param changeRecords - optional parameter to provide the pre-computed {@link PropertyChangeRecord}s from the ORM.  JPA does this, JDO does not.
+     * @param changeRecords - optional parameter to provide the pre-computed {@link PropertyChangeRecord}s from the ORM.
+     *      JPA does this, JDO does not.
      */
-    void onPreUpdate(ManagedObject entity, @Nullable Can<PropertyChangeRecord> changeRecords);
+    void onPreUpdate(ManagedObject entity, @Nullable Function<ManagedObject, Can<PropertyChangeRecord>> changeRecordSupplier);
 
     /**
      * Called by both JPA and JDO, after an existing entity has been updated.
@@ -108,7 +111,7 @@ public interface ObjectLifecyclePublisher {
     void onPostUpdate(ManagedObject entity);
 
     /**
-     * Called by both JPA and JDO, just beforean entity is deleted from the database.
+     * Called by both JPA and JDO, just before an entity is deleted from the database.
      *
      * <p>
      *     Default implementation fires callbacks and enlists the entity within <code>EntityChangeTracker</code>
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/ObjectLifecyclePublisherDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/ObjectLifecyclePublisherDefault.java
index ae12fc6bde..b2115fc1ed 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/ObjectLifecyclePublisherDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/ObjectLifecyclePublisherDefault.java
@@ -18,6 +18,9 @@
  */
 package org.apache.causeway.core.runtimeservices.publish;
 
+import java.util.Optional;
+import java.util.function.Function;
+
 import javax.annotation.Priority;
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -55,20 +58,6 @@ public class ObjectLifecyclePublisherDefault implements ObjectLifecyclePublisher
     private final Provider<LifecycleCallbackNotifier> lifecycleCallbackNotifierProvider;
     private final Provider<InteractionService> interactionServiceProvider;
 
-    private InteractionService interactionService() {
-        return interactionServiceProvider.get();
-    }
-
-    private EntityChangeTracker entityChangeTracker() {
-        return interactionService().isInInteraction()
-                ? entityChangeTrackerProvider.get()
-                : EntityChangeTracker.NOOP;
-    }
-
-    LifecycleCallbackNotifier lifecycleCallbackNotifier() {
-        return lifecycleCallbackNotifierProvider.get();
-    }
-
     @Override
     public void onPostCreate(final ManagedObject entity) {
         lifecycleCallbackNotifier().postCreate(entity);
@@ -76,7 +65,8 @@ public class ObjectLifecyclePublisherDefault implements ObjectLifecyclePublisher
 
     @Override
     public void onPostLoad(final ManagedObject entity) {
-        entityChangeTracker().incrementLoaded(entity);
+        entityChangeTracker()
+            .ifPresent(entityChangeTracker->entityChangeTracker.incrementLoaded(entity));
         lifecycleCallbackNotifier().postLoad(entity);
     }
 
@@ -87,19 +77,20 @@ public class ObjectLifecyclePublisherDefault implements ObjectLifecyclePublisher
 
     @Override
     public void onPostPersist(final ManagedObject entity) {
-        entityChangeTracker().enlistCreated(entity);
+        entityChangeTracker()
+            .ifPresent(entityChangeTracker->entityChangeTracker.enlistCreated(entity));
         lifecycleCallbackNotifier().postPersist(entity);
     }
 
     @Override
     public void onPreUpdate(
             final ManagedObject entity,
-            @Nullable final Can<PropertyChangeRecord> changeRecords) {
-        entityChangeTracker().enlistUpdating(entity, changeRecords);
+            final @Nullable Function<ManagedObject, Can<PropertyChangeRecord>> propertyChangeRecordSupplier) {
+        entityChangeTracker()
+            .ifPresent(entityChangeTracker->entityChangeTracker.enlistUpdating(entity, propertyChangeRecordSupplier));
         lifecycleCallbackNotifier().preUpdate(entity);
     }
 
-
     @Override
     public void onPostUpdate(final ManagedObject entity) {
         lifecycleCallbackNotifier().postUpdate(entity);
@@ -108,9 +99,25 @@ public class ObjectLifecyclePublisherDefault implements ObjectLifecyclePublisher
 
     @Override
     public void onPreRemove(final ManagedObject entity) {
-        entityChangeTracker().enlistDeleting(entity);
+        entityChangeTracker()
+            .ifPresent(entityChangeTracker->entityChangeTracker.enlistDeleting(entity));
         lifecycleCallbackNotifier().preRemove(entity);
     }
 
+    // -- HELPER
+
+    private InteractionService interactionService() {
+        return interactionServiceProvider.get();
+    }
+
+    private Optional<EntityChangeTracker> entityChangeTracker() {
+        return interactionService().isInInteraction()
+                ? Optional.of(entityChangeTrackerProvider.get())
+                : Optional.empty();
+    }
+
+    private LifecycleCallbackNotifier lifecycleCallbackNotifier() {
+        return lifecycleCallbackNotifierProvider.get();
+    }
 
 }
diff --git a/core/transaction/src/main/java/org/apache/causeway/core/transaction/changetracking/EntityChangeTracker.java b/core/transaction/src/main/java/org/apache/causeway/core/transaction/changetracking/EntityChangeTracker.java
index 37b9976f2f..d20418a0bb 100644
--- a/core/transaction/src/main/java/org/apache/causeway/core/transaction/changetracking/EntityChangeTracker.java
+++ b/core/transaction/src/main/java/org/apache/causeway/core/transaction/changetracking/EntityChangeTracker.java
@@ -18,6 +18,8 @@
  */
 package org.apache.causeway.core.transaction.changetracking;
 
+import java.util.function.Function;
+
 import org.springframework.beans.factory.DisposableBean;
 import org.springframework.lang.Nullable;
 
@@ -33,18 +35,6 @@ import org.apache.causeway.core.metamodel.services.objectlifecycle.PropertyChang
  */
 public interface EntityChangeTracker extends DisposableBean {
 
-    /**
-     * Provided primarily for testing, but also used in cases where an attempt is made to resolve a bean but
-     * there is no active interaction.
-     */
-    EntityChangeTracker NOOP = new EntityChangeTracker() {
-        @Override public void destroy() throws Exception {}
-        @Override public void enlistCreated(final ManagedObject entity) {}
-        @Override public void enlistUpdating(final ManagedObject entity, final Can<PropertyChangeRecord> propertyChangeRecords) {}
-        @Override public void enlistDeleting(final ManagedObject entity) {}
-        @Override public void incrementLoaded(final ManagedObject entity) {}
-    };
-
     /**
      * Publishing support: for object stores to enlist an object that has just been created,
      * capturing a dummy value <tt>'[NEW]'</tt> for the pre-modification value.
@@ -69,10 +59,10 @@ public interface EntityChangeTracker extends DisposableBean {
      * </p>
      *
      * @param entity
-     * @param propertyChangeRecords - optional parameter (as a performance optimization) to provide the pre-computed {@link PropertyChangeRecord}s from the ORM.  JPA does this, JDO does not.
+     * @param propertyChangeRecords - optional parameter (as a performance optimization)
+     *      to provide the pre-computed {@link PropertyChangeRecord}s from the ORM.  JPA does this, JDO does not.
      */
-    void enlistUpdating(ManagedObject entity, @Nullable Can<PropertyChangeRecord> propertyChangeRecords);
-
+    void enlistUpdating(ManagedObject entity, @Nullable Function<ManagedObject, Can<PropertyChangeRecord>> propertyChangeRecordSupplier);
 
     /**
      * Publishing support: for object stores to enlist an object that is about to be deleted,
@@ -91,8 +81,5 @@ public interface EntityChangeTracker extends DisposableBean {
      */
     void incrementLoaded(ManagedObject entity);
 
-
-
-
 }
 
diff --git a/persistence/commons/src/main/java/org/apache/causeway/persistence/jpa/integration/changetracking/EntityChangeTrackerDefault.java b/persistence/commons/src/main/java/org/apache/causeway/persistence/jpa/integration/changetracking/EntityChangeTrackerDefault.java
index c555e38aa3..08846ba3e2 100644
--- a/persistence/commons/src/main/java/org/apache/causeway/persistence/jpa/integration/changetracking/EntityChangeTrackerDefault.java
+++ b/persistence/commons/src/main/java/org/apache/causeway/persistence/jpa/integration/changetracking/EntityChangeTrackerDefault.java
@@ -25,6 +25,7 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.LongAdder;
+import java.util.function.Function;
 
 import javax.annotation.Priority;
 import javax.inject.Inject;
@@ -56,14 +57,12 @@ import org.apache.causeway.commons.internal.collections._Maps;
 import org.apache.causeway.commons.internal.collections._Sets;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.facets.object.publish.entitychange.EntityChangePublishingFacet;
-import org.apache.causeway.core.metamodel.facets.properties.property.entitychangepublishing.EntityPropertyChangePublishingPolicyFacet;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.ManagedObjects;
 import org.apache.causeway.core.metamodel.object.MmEntityUtils;
 import org.apache.causeway.core.metamodel.services.objectlifecycle.HasEnlistedEntityPropertyChanges;
 import org.apache.causeway.core.metamodel.services.objectlifecycle.PropertyChangeRecord;
 import org.apache.causeway.core.metamodel.services.objectlifecycle.PropertyChangeRecordId;
-import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
 import org.apache.causeway.core.transaction.changetracking.EntityChangeTracker;
 import org.apache.causeway.core.transaction.changetracking.EntityChangesPublisher;
 import org.apache.causeway.core.transaction.changetracking.EntityPropertyChangePublisher;
@@ -341,7 +340,6 @@ implements
         return false;
     }
 
-
     // side-effect free, used by XRay
     long countPotentialPropertyChangeRecords() {
         return enlistedPropertyChangeRecordsById.size();
@@ -361,9 +359,7 @@ implements
         log.debug("enlist entity's property changes for publishing {}", entity);
         enlistForChangeKindPublishing(entity, EntityChangeKind.CREATE);
 
-        entity.getSpecification().streamProperties(MixedIn.EXCLUDED)
-                .filter(property->!EntityPropertyChangePublishingPolicyFacet.isExcludedFromPublishing(property))
-                .map(property -> PropertyChangeRecordId.of(entity, property))
+        MmEntityUtils.streamPropertyChangeRecordIdsForChangePublishing(entity)
                 .filter(pcrId -> ! enlistedPropertyChangeRecordsById.containsKey(pcrId)) // only if not previously seen
                 .forEach(pcrId -> enlistedPropertyChangeRecordsById.put(pcrId, PropertyChangeRecord.ofNew(pcrId)));
     }
@@ -371,7 +367,7 @@ implements
     @Override
     public void enlistUpdating(
             final ManagedObject entity,
-            @Nullable final Can<PropertyChangeRecord> ormPropertyChangeRecords) {
+            final @Nullable Function<ManagedObject, Can<PropertyChangeRecord>> propertyChangeRecordSupplier) {
 
         _Xray.enlistUpdating(entity, interactionProviderProvider);
 
@@ -383,20 +379,21 @@ implements
         // additional properties may now have been changed, and the changeKind for publishing might also be modified
         enlistForChangeKindPublishing(entity, EntityChangeKind.UPDATE);
 
+        final Can<PropertyChangeRecord> ormPropertyChangeRecords = propertyChangeRecordSupplier!=null
+                ? propertyChangeRecordSupplier.apply(entity)
+                : null;
+
         if(ormPropertyChangeRecords != null) {
             // provided by ORM
             ormPropertyChangeRecords
                     .stream()
-                    .filter(pcr -> !EntityPropertyChangePublishingPolicyFacet.isExcludedFromPublishing(pcr.getProperty()))
                     .filter(pcr -> ! enlistedPropertyChangeRecordsById.containsKey(pcr.getId())) // only if not previously seen
-                    .forEach(pcr -> this.enlistedPropertyChangeRecordsById.put(pcr.getId(), pcr));
+                    .forEach(pcr -> enlistedPropertyChangeRecordsById.put(pcr.getId(), pcr));
         } else {
             // home-grown approach
             log.debug("enlist entity's property changes for publishing {}", entity);
 
-            entity.getSpecification().streamProperties(MixedIn.EXCLUDED)
-                    .filter(property->!EntityPropertyChangePublishingPolicyFacet.isExcludedFromPublishing(property))
-                    .map(property -> PropertyChangeRecordId.of(entity, property))
+            MmEntityUtils.streamPropertyChangeRecordIdsForChangePublishing(entity)
                     .filter(pcrId -> ! enlistedPropertyChangeRecordsById.containsKey(pcrId)) // only if not previously seen
                     .map(pcrId -> enlistedPropertyChangeRecordsById.put(pcrId, PropertyChangeRecord.ofCurrent(pcrId)))
                     .filter(Objects::nonNull)   // shouldn't happen, just keeping compiler happy
@@ -404,7 +401,6 @@ implements
         }
     }
 
-
     @Override
     public void enlistDeleting(final ManagedObject entity) {
 
@@ -416,20 +412,14 @@ implements
 
         final boolean enlisted = enlistForChangeKindPublishing(entity, EntityChangeKind.DELETE);
         if(enlisted) {
-
             log.debug("enlist entity's property changes for publishing {}", entity);
 
-            entity.getSpecification()
-                    .streamProperties(MixedIn.EXCLUDED)
-                    .filter(property -> EntityChangePublishingFacet.isPublishingEnabled(entity.getSpecification()))
-                    .filter(property -> !EntityPropertyChangePublishingPolicyFacet.isExcludedFromPublishing(property))
-                    .map(property -> PropertyChangeRecordId.of(entity, property))
-                    .forEach(pcrId -> enlistedPropertyChangeRecordsById.computeIfAbsent(pcrId, id -> PropertyChangeRecord.ofDeleting(id)));
+            MmEntityUtils.streamPropertyChangeRecordIdsForChangePublishing(entity)
+                    .forEach(pcrId -> enlistedPropertyChangeRecordsById
+                            .computeIfAbsent(pcrId, id -> PropertyChangeRecord.ofDeleting(id)));
         }
     }
 
-
-
     /**
      * Used only for the implementation of {@link MetricsService}.
      * @param entity
@@ -440,7 +430,6 @@ implements
         numberEntitiesLoaded.increment();
     }
 
-
     // -- METRICS SERVICE
 
     @Override
@@ -452,6 +441,4 @@ implements
     public int numberEntitiesDirtied() {
         return changeKindByEnlistedAdapter.size();
     }
-
-
 }
diff --git a/persistence/jpa/applib/src/main/java/org/apache/causeway/persistence/jpa/applib/integration/CausewayEntityListener.java b/persistence/jpa/applib/src/main/java/org/apache/causeway/persistence/jpa/applib/integration/CausewayEntityListener.java
index d0baa3e433..effbe3c9ca 100644
--- a/persistence/jpa/applib/src/main/java/org/apache/causeway/persistence/jpa/applib/integration/CausewayEntityListener.java
+++ b/persistence/jpa/applib/src/main/java/org/apache/causeway/persistence/jpa/applib/integration/CausewayEntityListener.java
@@ -34,9 +34,8 @@ import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
 
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.functional.Either;
-import org.apache.causeway.core.metamodel.facets.object.publish.entitychange.EntityChangePublishingFacet;
-import org.apache.causeway.core.metamodel.facets.properties.property.entitychangepublishing.EntityPropertyChangePublishingPolicyFacet;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
+import org.apache.causeway.core.metamodel.object.MmEntityUtils;
 import org.apache.causeway.core.metamodel.objectmanager.ObjectManager;
 import org.apache.causeway.core.metamodel.services.objectlifecycle.ObjectLifecyclePublisher;
 import org.apache.causeway.core.metamodel.services.objectlifecycle.PropertyChangeRecord;
@@ -90,18 +89,10 @@ public class CausewayEntityListener {
         objectLifecyclePublisher.onPostLoad(entity);
     }
 
-
     @PreUpdate void onPreUpdate(final Object entityPojo) {
         log.debug("onPreUpdate: {}", entityPojo);
-
         val entity = objectManager.adapt(entityPojo);
-
-        final Can<PropertyChangeRecord> propertyChangeRecords =
-                EntityChangePublishingFacet.isPublishingEnabled(entity.getSpecification())
-                ? gatherPropertyChangeRecords(entity)
-                : Can.empty();
-
-        objectLifecyclePublisher.onPreUpdate(entity, propertyChangeRecords);
+        objectLifecyclePublisher.onPreUpdate(entity, this::gatherPropertyChangeRecords);
     }
 
     @PreRemove void onPreRemove(final Object entityPojo) {
@@ -150,11 +141,7 @@ public class CausewayEntityListener {
                     .map(DirectToFieldChangeRecord.class::cast)
                     .map(ormChangeRecord -> {
                         final String propertyName = ormChangeRecord.getAttribute();
-                        return entity
-                                .getSpecification()
-                                .getProperty(propertyName)
-                                .filter(property -> !property.isMixedIn())
-                                .filter(property -> !EntityPropertyChangePublishingPolicyFacet.isExcludedFromPublishing(property))
+                        return MmEntityUtils.lookupPropertyEnabledForChangePublishing(entity, propertyName)
                                 .map(property -> PropertyChangeRecord.ofCurrent(
                                         PropertyChangeRecordId.of(entity, property),
                                         ormChangeRecord.getOldValue()))