CAY-2899 CommitLog: missing lifecycle-induced changes in `excludeFromTransaction` mode
diff --git a/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/CommitLogModuleExtender.java b/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/CommitLogModuleExtender.java
index 39d0b1d..a959219 100644
--- a/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/CommitLogModuleExtender.java
+++ b/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/CommitLogModuleExtender.java
@@ -18,12 +18,14 @@
  ****************************************************************/
 package org.apache.cayenne.commitlog;
 
+import org.apache.cayenne.DataChannelSyncFilter;
 import org.apache.cayenne.commitlog.meta.AnnotationCommitLogEntityFactory;
 import org.apache.cayenne.commitlog.meta.CommitLogEntity;
 import org.apache.cayenne.commitlog.meta.CommitLogEntityFactory;
 import org.apache.cayenne.configuration.runtime.CoreModule;
 import org.apache.cayenne.di.Binder;
 import org.apache.cayenne.di.ListBuilder;
+import org.apache.cayenne.graph.GraphChangeHandler;
 
 /**
  * A builder of a custom extensions module for {@link CommitLogModule} that customizes its services and installs
@@ -60,6 +62,7 @@
      * within the transaction, so listeners can commit their code together with the main commit.
      */
     public CommitLogModuleExtender excludeFromTransaction() {
+        CoreModule.extend(binder).addSyncFilter(createDiffInitFilter(), true);
         return registerFilter(false);
     }
 
@@ -100,4 +103,17 @@
         }
         return commitLogListeners;
     }
+
+    /**
+     * @return the filter that just initializes incoming Diff
+     */
+    private static DataChannelSyncFilter createDiffInitFilter() {
+        GraphChangeHandler noopHandler = new GraphChangeHandler() {};
+        return (originatingContext, changes, syncType, filterChain)
+                -> {
+            // see ObjectStoreGraphDiff.resolveDiff()
+            changes.apply(noopHandler);
+            return filterChain.onSync(originatingContext, changes, syncType);
+        };
+    }
 }
diff --git a/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_PreUpdate_IT.java b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_PreUpdate_IT.java
new file mode 100644
index 0000000..5646c4c
--- /dev/null
+++ b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_PreUpdate_IT.java
@@ -0,0 +1,101 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.commitlog;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.annotation.PreUpdate;
+import org.apache.cayenne.commitlog.db.AuditLog;
+import org.apache.cayenne.commitlog.db.AuditableChild5;
+import org.apache.cayenne.commitlog.model.ObjectChange;
+import org.apache.cayenne.commitlog.unit.AuditableRuntimeCase;
+import org.apache.cayenne.configuration.runtime.CoreModule;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.runtime.CayenneRuntimeBuilder;
+import org.apache.cayenne.tx.BaseTransaction;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class CommitLogFilter_PreUpdate_IT extends AuditableRuntimeCase {
+
+    protected ObjectContext context;
+    protected CommitLogListener listener;
+
+    @Override
+    protected CayenneRuntimeBuilder configureCayenne() {
+        this.listener = (originatingContext, changes) -> {
+            // assert we are not inside a transaction
+            assertNotNull(BaseTransaction.getThreadTransaction());
+
+            for (ObjectChange c : changes.getUniqueChanges()) {
+                AuditLog log = runtime.newContext().newObject(AuditLog.class);
+                log.setLog("DONE: " + c.getPostCommitId());
+                log.getObjectContext().commitChanges();
+            }
+        };
+
+        return super.configureCayenne()
+                .addModule(binder ->
+                        CoreModule.extend(binder)
+                                .addListenerType(MyPreUpdateListener.class)
+                )
+                .addModule(binder ->
+                        CommitLogModule.extend(binder)
+                                .commitLogAnnotationEntitiesOnly()
+                                .addListener(listener)
+                );
+    }
+
+    @Before
+    public void before() throws Exception {
+        context = runtime.newContext();
+        auditable5.insert(1, "yy");
+        auditableChild5.insert(1, 1, "zz");
+    }
+
+    @Test
+    public void testCommitLog() throws SQLException {
+
+        AuditableChild5 auditableChild = ObjectSelect.query(AuditableChild5.class)
+                .selectOne(context);
+        assertEquals("zz", auditableChild.getCharProperty1());
+
+        auditableChild.setCharProperty1("xx");
+
+        context.commitChanges();
+
+        List<Object[]> logs = auditLog.selectAll();
+        assertEquals(2, logs.size());
+    }
+
+    public static class MyPreUpdateListener {
+
+        @PreUpdate({ AuditableChild5.class })
+        public void preUpdate( AuditableChild5 child ) {
+            child.getParent().setCharProperty1("zz");
+        }
+    }
+
+
+}
diff --git a/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_PreUpdate_OutsideTxIT.java b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_PreUpdate_OutsideTxIT.java
new file mode 100644
index 0000000..ac7d7be
--- /dev/null
+++ b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_PreUpdate_OutsideTxIT.java
@@ -0,0 +1,101 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.commitlog;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.annotation.PreUpdate;
+import org.apache.cayenne.commitlog.db.AuditLog;
+import org.apache.cayenne.commitlog.db.AuditableChild5;
+import org.apache.cayenne.commitlog.model.ObjectChange;
+import org.apache.cayenne.commitlog.unit.AuditableRuntimeCase;
+import org.apache.cayenne.configuration.runtime.CoreModule;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.runtime.CayenneRuntimeBuilder;
+import org.apache.cayenne.tx.BaseTransaction;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class CommitLogFilter_PreUpdate_OutsideTxIT extends AuditableRuntimeCase {
+
+    protected ObjectContext context;
+    protected CommitLogListener listener;
+
+    @Override
+    protected CayenneRuntimeBuilder configureCayenne() {
+        this.listener = (originatingContext, changes) -> {
+            // assert we are not inside a transaction
+            assertNull(BaseTransaction.getThreadTransaction());
+
+            for (ObjectChange c : changes.getUniqueChanges()) {
+                AuditLog log = runtime.newContext().newObject(AuditLog.class);
+                log.setLog("DONE: " + c.getPostCommitId());
+                log.getObjectContext().commitChanges();
+            }
+        };
+
+        return super.configureCayenne()
+                .addModule(binder ->
+                        CoreModule.extend(binder)
+                                .addListenerType(MyPreUpdateListener.class)
+                )
+                .addModule(
+                        b -> CommitLogModule.extend(b)
+                                .commitLogAnnotationEntitiesOnly()
+                                .excludeFromTransaction()
+                                .addListener(listener)
+                );
+    }
+
+    @Before
+    public void before() throws Exception {
+        context = runtime.newContext();
+        auditable5.insert(1, "yy");
+        auditableChild5.insert(1, 1, "zz");
+    }
+
+    @Test
+    public void testCommitLog() throws SQLException {
+
+        AuditableChild5 auditableChild = ObjectSelect.query(AuditableChild5.class)
+                .selectOne(context);
+        assertEquals("zz", auditableChild.getCharProperty1());
+
+        auditableChild.setCharProperty1("xx");
+
+        context.commitChanges();
+
+        List<Object[]> logs = auditLog.selectAll();
+        assertEquals(2, logs.size());
+    }
+
+    public static class MyPreUpdateListener {
+
+        @PreUpdate({ AuditableChild5.class })
+        public void preUpdate( AuditableChild5 child ) {
+            child.getParent().setCharProperty1("zz");
+        }
+    }
+
+
+}
diff --git a/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/Auditable5.java b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/Auditable5.java
new file mode 100644
index 0000000..ed13e0b
--- /dev/null
+++ b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/Auditable5.java
@@ -0,0 +1,30 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.commitlog.db;
+
+import org.apache.cayenne.commitlog.CommitLog;
+import org.apache.cayenne.commitlog.db.auto._Auditable5;
+
+@CommitLog
+public class Auditable5 extends _Auditable5 {
+
+    private static final long serialVersionUID = 1L;
+
+}
diff --git a/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/AuditableChild5.java b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/AuditableChild5.java
new file mode 100644
index 0000000..5e8c445
--- /dev/null
+++ b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/AuditableChild5.java
@@ -0,0 +1,30 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.commitlog.db;
+
+import org.apache.cayenne.commitlog.CommitLog;
+import org.apache.cayenne.commitlog.db.auto._AuditableChild5;
+
+@CommitLog
+public class AuditableChild5 extends _AuditableChild5 {
+
+    private static final long serialVersionUID = 1L;
+
+}
diff --git a/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/auto/_Auditable5.java b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/auto/_Auditable5.java
new file mode 100644
index 0000000..242db2a
--- /dev/null
+++ b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/auto/_Auditable5.java
@@ -0,0 +1,116 @@
+package org.apache.cayenne.commitlog.db.auto;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.List;
+
+import org.apache.cayenne.PersistentObject;
+import org.apache.cayenne.commitlog.db.Auditable5;
+import org.apache.cayenne.commitlog.db.AuditableChild5;
+import org.apache.cayenne.exp.property.ListProperty;
+import org.apache.cayenne.exp.property.PropertyFactory;
+import org.apache.cayenne.exp.property.SelfProperty;
+import org.apache.cayenne.exp.property.StringProperty;
+
+/**
+ * Class _Auditable5 was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _Auditable5 extends PersistentObject {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final SelfProperty<Auditable5> SELF = PropertyFactory.createSelf(Auditable5.class);
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final StringProperty<String> CHAR_PROPERTY1 = PropertyFactory.createString("charProperty1", String.class);
+    public static final ListProperty<AuditableChild5> CHILDREN = PropertyFactory.createList("children", AuditableChild5.class);
+
+    protected String charProperty1;
+
+    protected Object children;
+
+    public void setCharProperty1(String charProperty1) {
+        beforePropertyWrite("charProperty1", this.charProperty1, charProperty1);
+        this.charProperty1 = charProperty1;
+    }
+
+    public String getCharProperty1() {
+        beforePropertyRead("charProperty1");
+        return this.charProperty1;
+    }
+
+    public void addToChildren(AuditableChild5 obj) {
+        addToManyTarget("children", obj, true);
+    }
+
+    public void removeFromChildren(AuditableChild5 obj) {
+        removeToManyTarget("children", obj, true);
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<AuditableChild5> getChildren() {
+        return (List<AuditableChild5>)readProperty("children");
+    }
+
+    @Override
+    public Object readPropertyDirectly(String propName) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch(propName) {
+            case "charProperty1":
+                return this.charProperty1;
+            case "children":
+                return this.children;
+            default:
+                return super.readPropertyDirectly(propName);
+        }
+    }
+
+    @Override
+    public void writePropertyDirectly(String propName, Object val) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch (propName) {
+            case "charProperty1":
+                this.charProperty1 = (String)val;
+                break;
+            case "children":
+                this.children = val;
+                break;
+            default:
+                super.writePropertyDirectly(propName, val);
+        }
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        writeSerialized(out);
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        readSerialized(in);
+    }
+
+    @Override
+    protected void writeState(ObjectOutputStream out) throws IOException {
+        super.writeState(out);
+        out.writeObject(this.charProperty1);
+        out.writeObject(this.children);
+    }
+
+    @Override
+    protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        super.readState(in);
+        this.charProperty1 = (String)in.readObject();
+        this.children = in.readObject();
+    }
+
+}
diff --git a/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/auto/_AuditableChild5.java b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/auto/_AuditableChild5.java
new file mode 100644
index 0000000..26e66b2
--- /dev/null
+++ b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/db/auto/_AuditableChild5.java
@@ -0,0 +1,110 @@
+package org.apache.cayenne.commitlog.db.auto;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.apache.cayenne.PersistentObject;
+import org.apache.cayenne.commitlog.db.Auditable5;
+import org.apache.cayenne.commitlog.db.AuditableChild5;
+import org.apache.cayenne.exp.property.EntityProperty;
+import org.apache.cayenne.exp.property.PropertyFactory;
+import org.apache.cayenne.exp.property.SelfProperty;
+import org.apache.cayenne.exp.property.StringProperty;
+
+/**
+ * Class _AuditableChild5 was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _AuditableChild5 extends PersistentObject {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final SelfProperty<AuditableChild5> SELF = PropertyFactory.createSelf(AuditableChild5.class);
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final StringProperty<String> CHAR_PROPERTY1 = PropertyFactory.createString("charProperty1", String.class);
+    public static final EntityProperty<Auditable5> PARENT = PropertyFactory.createEntity("parent", Auditable5.class);
+
+    protected String charProperty1;
+
+    protected Object parent;
+
+    public void setCharProperty1(String charProperty1) {
+        beforePropertyWrite("charProperty1", this.charProperty1, charProperty1);
+        this.charProperty1 = charProperty1;
+    }
+
+    public String getCharProperty1() {
+        beforePropertyRead("charProperty1");
+        return this.charProperty1;
+    }
+
+    public void setParent(Auditable5 parent) {
+        setToOneTarget("parent", parent, true);
+    }
+
+    public Auditable5 getParent() {
+        return (Auditable5)readProperty("parent");
+    }
+
+    @Override
+    public Object readPropertyDirectly(String propName) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch(propName) {
+            case "charProperty1":
+                return this.charProperty1;
+            case "parent":
+                return this.parent;
+            default:
+                return super.readPropertyDirectly(propName);
+        }
+    }
+
+    @Override
+    public void writePropertyDirectly(String propName, Object val) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch (propName) {
+            case "charProperty1":
+                this.charProperty1 = (String)val;
+                break;
+            case "parent":
+                this.parent = val;
+                break;
+            default:
+                super.writePropertyDirectly(propName, val);
+        }
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        writeSerialized(out);
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        readSerialized(in);
+    }
+
+    @Override
+    protected void writeState(ObjectOutputStream out) throws IOException {
+        super.writeState(out);
+        out.writeObject(this.charProperty1);
+        out.writeObject(this.parent);
+    }
+
+    @Override
+    protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        super.readState(in);
+        this.charProperty1 = (String)in.readObject();
+        this.parent = in.readObject();
+    }
+
+}
diff --git a/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/unit/AuditableRuntimeCase.java b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/unit/AuditableRuntimeCase.java
index c86b7e1..cbb3e1b 100644
--- a/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/unit/AuditableRuntimeCase.java
+++ b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/unit/AuditableRuntimeCase.java
@@ -42,6 +42,9 @@
 	protected TableHelper auditable3;
 	protected TableHelper auditable4;
 
+	protected TableHelper auditable5;
+	protected TableHelper auditableChild5;
+
 	protected TableHelper auditLog;
 
 	@Before
@@ -68,6 +71,10 @@
 		this.auditable4 = new TableHelper(dbHelper, "AUDITABLE4").setColumns("ID", "CHAR_PROPERTY1", "CHAR_PROPERTY2",
 				"AUDITABLE3_ID");
 
+		this.auditable5 = new TableHelper(dbHelper, "AUDITABLE5").setColumns("ID", "CHAR_PROPERTY1");
+		this.auditableChild5 = new TableHelper(dbHelper, "AUDITABLE_CHILD5").setColumns("ID", "AUDITABLE5_ID",
+				"CHAR_PROPERTY1");
+
 		this.auditableChild1.deleteAll();
 		this.auditableChild1x.deleteAll();
 		this.auditable1.deleteAll();
@@ -75,6 +82,8 @@
 		this.auditable2.deleteAll();
 		this.auditable4.deleteAll();
 		this.auditable3.deleteAll();
+		this.auditableChild5.deleteAll();
+		this.auditable5.deleteAll();
 
 		this.auditLog.deleteAll();
 	}
diff --git a/cayenne-commitlog/src/test/resources/lifecycle-map.map.xml b/cayenne-commitlog/src/test/resources/lifecycle-map.map.xml
index cbcba1c..179bbd8 100644
--- a/cayenne-commitlog/src/test/resources/lifecycle-map.map.xml
+++ b/cayenne-commitlog/src/test/resources/lifecycle-map.map.xml
@@ -24,6 +24,10 @@
 		<db-attribute name="CHAR_PROPERTY2" type="VARCHAR" length="200"/>
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 	</db-entity>
+	<db-entity name="AUDITABLE5">
+		<db-attribute name="CHAR_PROPERTY1" type="VARCHAR" length="200"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+	</db-entity>
 	<db-entity name="AUDITABLE_CHILD1">
 		<db-attribute name="AUDITABLE1_ID" type="INTEGER"/>
 		<db-attribute name="CHAR_PROPERTY1" type="VARCHAR" length="200"/>
@@ -40,6 +44,11 @@
 		<db-attribute name="CHAR_PROPERTY2" type="VARCHAR" length="200"/>
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 	</db-entity>
+	<db-entity name="AUDITABLE_CHILD5">
+		<db-attribute name="AUDITABLE5_ID" type="INTEGER"/>
+		<db-attribute name="CHAR_PROPERTY1" type="VARCHAR" length="200"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+	</db-entity>
 	<db-entity name="AUDIT_LOG">
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 		<db-attribute name="LOG" type="CLOB"/>
@@ -78,6 +87,9 @@
 		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
 		<obj-attribute name="charProperty2" type="java.lang.String" db-attribute-path="CHAR_PROPERTY2"/>
 	</obj-entity>
+	<obj-entity name="Auditable5" className="org.apache.cayenne.commitlog.db.Auditable5" dbEntityName="AUDITABLE5">
+		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
+	</obj-entity>
 	<obj-entity name="AuditableChild1" className="org.apache.cayenne.commitlog.db.AuditableChild1" dbEntityName="AUDITABLE_CHILD1">
 		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
 	</obj-entity>
@@ -88,6 +100,9 @@
 		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
 		<obj-attribute name="charProperty2" type="java.lang.String" db-attribute-path="CHAR_PROPERTY2"/>
 	</obj-entity>
+	<obj-entity name="AuditableChild5" className="org.apache.cayenne.commitlog.db.AuditableChild5" dbEntityName="AUDITABLE_CHILD5">
+		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
+	</obj-entity>
 	<obj-entity name="E1" className="org.apache.cayenne.commitlog.db.E1" dbEntityName="E1"/>
 	<obj-entity name="E2" className="org.apache.cayenne.commitlog.db.E2" dbEntityName="E2"/>
 	<obj-entity name="E3" className="org.apache.cayenne.commitlog.db.E3" dbEntityName="E3"/>
@@ -107,6 +122,9 @@
 	<db-relationship name="auditable3" source="AUDITABLE4" target="AUDITABLE3">
 		<db-attribute-pair source="AUDITABLE3_ID" target="ID"/>
 	</db-relationship>
+	<db-relationship name="children" source="AUDITABLE5" target="AUDITABLE_CHILD5" toMany="true">
+		<db-attribute-pair source="ID" target="AUDITABLE5_ID"/>
+	</db-relationship>
 	<db-relationship name="parent" source="AUDITABLE_CHILD1" target="AUDITABLE1">
 		<db-attribute-pair source="AUDITABLE1_ID" target="ID"/>
 	</db-relationship>
@@ -116,6 +134,9 @@
 	<db-relationship name="parent" source="AUDITABLE_CHILD3" target="AUDITABLE2">
 		<db-attribute-pair source="AUDITABLE2_ID" target="ID"/>
 	</db-relationship>
+	<db-relationship name="parent" source="AUDITABLE_CHILD5" target="AUDITABLE5">
+		<db-attribute-pair source="AUDITABLE5_ID" target="ID"/>
+	</db-relationship>
 	<db-relationship name="e34s" source="E3" target="E34" toDependentPK="true" toMany="true">
 		<db-attribute-pair source="ID" target="E3_ID"/>
 	</db-relationship>
@@ -132,9 +153,11 @@
 	<obj-relationship name="children" source="Auditable2" target="AuditableChild3" deleteRule="Deny" db-relationship-path="children"/>
 	<obj-relationship name="auditable4s" source="Auditable3" target="Auditable4" deleteRule="Deny" db-relationship-path="auditable4s"/>
 	<obj-relationship name="auditable3" source="Auditable4" target="Auditable3" deleteRule="Nullify" db-relationship-path="auditable3"/>
+	<obj-relationship name="children" source="Auditable5" target="AuditableChild5" deleteRule="Deny" db-relationship-path="children"/>
 	<obj-relationship name="parent" source="AuditableChild1" target="Auditable1" deleteRule="Nullify" db-relationship-path="parent"/>
 	<obj-relationship name="parent" source="AuditableChild1x" target="Auditable1" deleteRule="Nullify" db-relationship-path="parent"/>
 	<obj-relationship name="parent" source="AuditableChild3" target="Auditable2" deleteRule="Nullify" db-relationship-path="parent"/>
+	<obj-relationship name="parent" source="AuditableChild5" target="Auditable5" deleteRule="Nullify" db-relationship-path="parent"/>
 	<obj-relationship name="e4s" source="E3" target="E4" deleteRule="Deny" db-relationship-path="e34s.e4"/>
 	<obj-relationship name="e3s" source="E4" target="E3" deleteRule="Deny" db-relationship-path="e34s.e3"/>
 	<cgen xmlns="http://cayenne.apache.org/schema/11/cgen">
@@ -142,6 +165,10 @@
 		<mode>entity</mode>
 		<template>templates/v4_1/subclass.vm</template>
 		<superTemplate>templates/v4_1/superclass.vm</superTemplate>
+		<embeddableTemplate>templates/v4_1/embeddable-subclass.vm</embeddableTemplate>
+		<embeddableSuperTemplate>templates/v4_1/embeddable-superclass.vm</embeddableSuperTemplate>
+		<queryTemplate>templates/v4_1/datamap-subclass.vm</queryTemplate>
+		<querySuperTemplate>templates/v4_1/datamap-superclass.vm</querySuperTemplate>
 		<outputPattern>*.java</outputPattern>
 		<makePairs>true</makePairs>
 		<usePkgPath>true</usePkgPath>