Author: aadamchik
Date: Sun Feb 5 20:26:58 2012
New Revision: 1240798
URL: http://svn.apache.org/viewvc?rev=1240798&view=rev
Log:
CAY-1662 @Auditable and @AuditableChild annotations should support
'ignoredProperties' parameter and exclude changes to runtime relationships
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableEntityDescriptor.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilter_InRuntime_Test.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable1.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable2.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild1.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild2.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild3.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable1.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable2.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild1.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild2.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild3.java
Modified:
cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/Auditable.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableChild.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableFilter.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableProcessor.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilterTest.java
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml
Modified: cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
--- cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt (original)
+++ cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt Sun Feb 5
20:26:58 2012
@@ -33,6 +33,7 @@ CAY-1653 Improving DefaultEventManager c
CAY-1654 Can't switch DataSource Factory on certain projects
CAY-1656 cayenne generate extra data in xml model when change DataNode factory
CAY-1657 Expression: in-memory comparison requires persistent to be in the
same context as exp parameters to match
+CAY-1662 @Auditable and @AuditableChild annotations should support
'ignoredProperties' parameter and exclude changes to runtime relationships
Bug Fixes Since 3.1M3:
Modified:
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/Auditable.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/Auditable.java?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/Auditable.java
(original)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/Auditable.java
Sun Feb 5 20:26:58 2012
@@ -36,4 +36,5 @@ import java.lang.annotation.Target;
@Inherited
public @interface Auditable {
+ String[] ignoredProperties() default {};
}
Modified:
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableChild.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableChild.java?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableChild.java
(original)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableChild.java
Sun Feb 5 20:26:58 2012
@@ -43,4 +43,6 @@ public @interface AuditableChild {
* object that should be audited when annotated object is changed.
*/
String value();
+
+ String[] ignoredProperties() default {};
}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableEntityDescriptor.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableEntityDescriptor.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableEntityDescriptor.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableEntityDescriptor.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,81 @@
+/*****************************************************************
+ * 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
+ *
+ * http://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.lifecycle.audit;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.lifecycle.changeset.ChangeSet;
+import org.apache.cayenne.lifecycle.changeset.ChangeSetFilter;
+import org.apache.cayenne.lifecycle.changeset.PropertyChange;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+
+class AuditableEntityDescriptor {
+
+ private Collection<String> ignoredProperties;
+
+ AuditableEntityDescriptor(ObjEntity auditableEntity, String[]
ignoredProperties) {
+
+ this.ignoredProperties = new HashSet<String>();
+
+ // ignore runtime relationships
+ for (ObjRelationship relationship :
auditableEntity.getRelationships()) {
+ if (relationship.isRuntime()) {
+ this.ignoredProperties.add(relationship.getName());
+ }
+ }
+
+ // ignore explicitly specified properties
+ if (ignoredProperties != null) {
+ for (String property : ignoredProperties) {
+ this.ignoredProperties.add(property);
+ }
+ }
+ }
+
+ boolean auditableChange(Persistent object) {
+ if (ignoredProperties.isEmpty()) {
+ return true;
+ }
+
+ ChangeSet changeSet = ChangeSetFilter.preCommitChangeSet();
+ if (changeSet == null) {
+ throw new CayenneRuntimeException(
+ "Required ChangeSetFilter is not installed, or is in the
wrong place in the filter chain.");
+ }
+
+ Map<String, PropertyChange> changes = changeSet.getChanges(object);
+
+ if (changes.size() > ignoredProperties.size()) {
+ return true;
+ }
+
+ for (String key : changes.keySet()) {
+ if (!ignoredProperties.contains(key)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
Modified:
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableFilter.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableFilter.java?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableFilter.java
(original)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableFilter.java
Sun Feb 5 20:26:58 2012
@@ -18,31 +18,44 @@
****************************************************************/
package org.apache.cayenne.lifecycle.audit;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
import org.apache.cayenne.DataChannel;
import org.apache.cayenne.DataChannelFilter;
import org.apache.cayenne.DataChannelFilterChain;
import org.apache.cayenne.DataObject;
import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.Persistent;
import org.apache.cayenne.QueryResponse;
import org.apache.cayenne.annotation.PostPersist;
import org.apache.cayenne.annotation.PostRemove;
import org.apache.cayenne.annotation.PostUpdate;
import org.apache.cayenne.graph.GraphDiff;
+import org.apache.cayenne.lifecycle.changeset.ChangeSetFilter;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.query.Query;
/**
* A {@link DataChannelFilter} that enables audit of entities annotated with
- * {@link Auditable} and {@link AuditableChild}.
+ * {@link Auditable} and {@link AuditableChild}. Note that this filter relies
on
+ * {@link ChangeSetFilter} presence in the DataDomain filter chain to be able
to analyze
+ * ignored properties.
*
* @since 3.1
*/
public class AuditableFilter implements DataChannelFilter {
private ThreadLocal<AuditableAggregator> threadAggregator;
+ private ConcurrentMap<String, AuditableEntityDescriptor> entityDescriptors;
protected AuditableProcessor processor;
+ protected EntityResolver entityResolver;
- public AuditableFilter(AuditableProcessor processor) {
+ public AuditableFilter(EntityResolver entityResolver, AuditableProcessor
processor) {
this.processor = processor;
+ this.entityResolver = entityResolver;
+ this.entityDescriptors = new ConcurrentHashMap<String,
AuditableEntityDescriptor>();
this.threadAggregator = new ThreadLocal<AuditableAggregator>();
}
@@ -122,7 +135,9 @@ public class AuditableFilter implements
@PostUpdate(entityAnnotations = Auditable.class)
void updateAudit(Object object) {
- getAggregator().audit(object, AuditableOperation.UPDATE);
+ if (isAuditableUpdate(object, false)) {
+ getAggregator().audit(object, AuditableOperation.UPDATE);
+ }
}
// only catching child updates... child insert/delete presumably causes an
event on
@@ -131,15 +146,20 @@ public class AuditableFilter implements
@PostUpdate(entityAnnotations = AuditableChild.class)
void updateAuditChild(Object object) {
- Object parent = getParent(object);
+ if (isAuditableUpdate(object, true)) {
- if (parent != null) {
- updateAudit(parent);
- }
- else {
- // TODO: maybe log this fact... shouldn't normally happen, but I
can imagine
- // certain combinations of object graphs, disconnected
relationships, delete
- // rules, etc. may cause this
+ Object parent = getParent(object);
+
+ if (parent != null) {
+ // not calling 'updateAudit' to skip checking
'isAuditableUpdate' on
+ // parent
+ getAggregator().audit(parent, AuditableOperation.UPDATE);
+ }
+ else {
+ // TODO: maybe log this fact... shouldn't normally happen, but
I can
+ // imagine certain combinations of object graphs, disconnected
+ // relationships, delete rules, etc. may cause this
+ }
}
}
@@ -164,4 +184,47 @@ public class AuditableFilter implements
return dataObject.readNestedProperty(annotation.value());
}
+
+ protected boolean isAuditableUpdate(Object object, boolean child) {
+ AuditableEntityDescriptor descriptor = getEntityDescriptor(object,
child);
+ return descriptor.auditableChange((Persistent) object);
+ }
+
+ private AuditableEntityDescriptor getEntityDescriptor(Object object,
boolean child) {
+
+ ObjEntity entity = entityResolver.lookupObjEntity(object);
+
+ AuditableEntityDescriptor descriptor =
entityDescriptors.get(entity.getName());
+ if (descriptor == null) {
+
+ String[] ignoredProperties;
+
+ if (child) {
+ AuditableChild annotation = object.getClass().getAnnotation(
+ AuditableChild.class);
+ ignoredProperties = annotation != null
+ ? annotation.ignoredProperties()
+ : null;
+ }
+ else {
+ Auditable annotation =
object.getClass().getAnnotation(Auditable.class);
+ ignoredProperties = annotation != null
+ ? annotation.ignoredProperties()
+ : null;
+ }
+
+ descriptor = new AuditableEntityDescriptor(entity,
ignoredProperties);
+
+ AuditableEntityDescriptor existingDescriptor =
entityDescriptors.putIfAbsent(
+ entity.getName(),
+ descriptor);
+
+ if (existingDescriptor != null) {
+ descriptor = existingDescriptor;
+ }
+ }
+
+ return descriptor;
+
+ }
}
Modified:
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableProcessor.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableProcessor.java?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableProcessor.java
(original)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableProcessor.java
Sun Feb 5 20:26:58 2012
@@ -19,7 +19,7 @@
package org.apache.cayenne.lifecycle.audit;
/**
- * A superclass of application specific handlers of the {@link Auditable}
mixin that
+ * A superclass of application specific handlers of the {@link Auditable}
annotation, that
* provides basic needed callbacks.
*
* @since 3.1
Modified:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilterTest.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilterTest.java?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilterTest.java
(original)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilterTest.java
Sun Feb 5 20:26:58 2012
@@ -18,25 +18,41 @@
****************************************************************/
package org.apache.cayenne.lifecycle.audit;
+import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import junit.framework.TestCase;
+import org.apache.cayenne.CayenneDataObject;
import org.apache.cayenne.DataChannel;
import org.apache.cayenne.DataChannelFilterChain;
import org.apache.cayenne.DataObject;
import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ObjectId;
import org.apache.cayenne.graph.GraphDiff;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjEntity;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class AuditableFilterTest extends TestCase {
+ private AuditableProcessor processor;
+ private EntityResolver resolver;
+
+ @Override
+ protected void setUp() throws Exception {
+ processor = mock(AuditableProcessor.class);
+ resolver = mock(EntityResolver.class);
+
+ ObjEntity objectEntity = new ObjEntity("CayenneDataObject");
+
when(resolver.lookupObjEntity(any(Object.class))).thenReturn(objectEntity);
+ }
+
public void testInsertAudit() {
- AuditableProcessor processor = mock(AuditableProcessor.class);
- AuditableFilter filter = new AuditableFilter(processor);
+ AuditableFilter filter = new AuditableFilter(resolver, processor);
Object audited = new Object();
filter.insertAudit(audited);
filter.postSync();
@@ -45,9 +61,8 @@ public class AuditableFilterTest extends
}
public void testDeleteAudit() {
- AuditableProcessor processor = mock(AuditableProcessor.class);
- AuditableFilter filter = new AuditableFilter(processor);
+ AuditableFilter filter = new AuditableFilter(resolver, processor);
Object audited = new Object();
filter.deleteAudit(audited);
filter.postSync();
@@ -56,10 +71,9 @@ public class AuditableFilterTest extends
}
public void testUpdateAudit() {
- AuditableProcessor processor = mock(AuditableProcessor.class);
- AuditableFilter filter = new AuditableFilter(processor);
- Object audited = new Object();
+ AuditableFilter filter = new AuditableFilter(resolver, processor);
+ Object audited = new CayenneDataObject();
filter.updateAudit(audited);
filter.postSync();
@@ -67,12 +81,12 @@ public class AuditableFilterTest extends
}
public void testUpdateAuditChild() {
- AuditableProcessor processor = mock(AuditableProcessor.class);
- AuditableFilter filter = new AuditableFilter(processor);
+ AuditableFilter filter = new AuditableFilter(resolver, processor);
Object auditedParent = new Object();
DataObject audited = new MockAuditableChild();
+ audited.setObjectId(new ObjectId("MockAuditableChild", "a", 1));
audited.writeProperty("parent", auditedParent);
filter.updateAuditChild(audited);
filter.postSync();
@@ -81,9 +95,8 @@ public class AuditableFilterTest extends
}
public void testOnSyncPassThrough() {
- AuditableProcessor processor = mock(AuditableProcessor.class);
- AuditableFilter filter = new AuditableFilter(processor);
+ AuditableFilter filter = new AuditableFilter(resolver, processor);
ObjectContext context = mock(ObjectContext.class);
GraphDiff changes = mock(GraphDiff.class);
@@ -97,13 +110,12 @@ public class AuditableFilterTest extends
}
public void testOnSyncAuditEventsCollapse() {
- AuditableProcessor processor = mock(AuditableProcessor.class);
- final AuditableFilter filter = new AuditableFilter(processor);
+ final AuditableFilter filter = new AuditableFilter(resolver,
processor);
ObjectContext context = mock(ObjectContext.class);
GraphDiff changes = mock(GraphDiff.class);
- final Object auditedParent1 = new Object();
+ final Object auditedParent1 = new CayenneDataObject();
final DataObject audited11 = new MockAuditableChild();
audited11.writeProperty("parent", auditedParent1);
final DataObject audited12 = new MockAuditableChild();
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilter_InRuntime_Test.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilter_InRuntime_Test.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilter_InRuntime_Test.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilter_InRuntime_Test.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,271 @@
+/*****************************************************************
+ * 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
+ *
+ * http://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.lifecycle.audit;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.cayenne.Cayenne;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.lifecycle.changeset.ChangeSetFilter;
+import org.apache.cayenne.lifecycle.db.Auditable1;
+import org.apache.cayenne.lifecycle.db.Auditable2;
+import org.apache.cayenne.lifecycle.db.AuditableChild1;
+import org.apache.cayenne.lifecycle.db.AuditableChild2;
+import org.apache.cayenne.lifecycle.db.AuditableChild3;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+
+public class AuditableFilter_InRuntime_Test extends TestCase {
+
+ private ServerRuntime runtime;
+
+ private TableHelper auditable1;
+ private TableHelper auditableChild1;
+ private TableHelper auditableChild2;
+
+ private TableHelper auditable2;
+ private TableHelper auditableChild3;
+
+ @Override
+ protected void setUp() throws Exception {
+ runtime = new ServerRuntime("cayenne-lifecycle.xml");
+
+ DBHelper dbHelper = new DBHelper(runtime.getDataSource(null));
+
+ auditable1 = new TableHelper(dbHelper, "AUDITABLE1").setColumns(
+ "ID",
+ "CHAR_PROPERTY1");
+
+ auditableChild1 = new TableHelper(dbHelper,
"AUDITABLE_CHILD1").setColumns(
+ "ID",
+ "AUDITABLE1_ID",
+ "CHAR_PROPERTY1");
+
+ auditableChild2 = new TableHelper(dbHelper,
"AUDITABLE_CHILD2").setColumns(
+ "ID",
+ "AUDITABLE1_ID",
+ "CHAR_PROPERTY1");
+
+ auditable2 = new TableHelper(dbHelper, "AUDITABLE2").setColumns(
+ "ID",
+ "CHAR_PROPERTY1",
+ "CHAR_PROPERTY2");
+
+ auditableChild3 = new TableHelper(dbHelper,
"AUDITABLE_CHILD3").setColumns(
+ "ID",
+ "AUDITABLE2_ID",
+ "CHAR_PROPERTY1",
+ "CHAR_PROPERTY2");
+
+ auditableChild1.deleteAll();
+ auditableChild2.deleteAll();
+ auditable1.deleteAll();
+
+ auditableChild3.deleteAll();
+ auditable2.deleteAll();
+ }
+
+ public void testAudit_IgnoreRuntimeRelationships() throws Exception {
+
+ auditable1.insert(1, "xx");
+ auditable1.insert(2, "yy");
+ auditable1.insert(3, "aa");
+ auditableChild2.insert(1, 1, "zz");
+
+ DataDomain domain = runtime.getDataDomain();
+
+ Processor processor = new Processor();
+
+ AuditableFilter filter = new AuditableFilter(
+ domain.getEntityResolver(),
+ processor);
+ domain.addFilter(filter);
+ domain.getEntityResolver().getCallbackRegistry().addListener(filter);
+
+ // prerequisite for BaseAuditableProcessor use
+ ChangeSetFilter changeSetFilter = new ChangeSetFilter();
+ domain.addFilter(changeSetFilter);
+
domain.getEntityResolver().getCallbackRegistry().addListener(changeSetFilter);
+
+ ObjectContext context = runtime.getContext();
+
+ Auditable1 a2 = Cayenne.objectForPK(context, Auditable1.class, 2);
+ AuditableChild2 a21 = Cayenne.objectForPK(context,
AuditableChild2.class, 1);
+
+ a21.setParent(a2);
+ a21.setCharProperty1("XYZA");
+ context.commitChanges();
+
+ assertEquals(0, processor.size);
+
+ processor.reset();
+
+ Auditable1 a3 = Cayenne.objectForPK(context, Auditable1.class, 3);
+ a21.setParent(a3);
+ a3.setCharProperty1("12");
+
+ context.commitChanges();
+ assertEquals(1, processor.size);
+
assertTrue(processor.audited.get(AuditableOperation.UPDATE).contains(a3));
+ }
+
+ public void testAudit_IncludeToManyRelationships() throws Exception {
+
+ auditable1.insert(1, "xx");
+ auditable1.insert(2, "yy");
+ auditableChild1.insert(1, 1, "zz");
+
+ DataDomain domain = runtime.getDataDomain();
+
+ Processor processor = new Processor();
+
+ AuditableFilter filter = new AuditableFilter(
+ domain.getEntityResolver(),
+ processor);
+ domain.addFilter(filter);
+ domain.getEntityResolver().getCallbackRegistry().addListener(filter);
+
+ // prerequisite for BaseAuditableProcessor use
+ ChangeSetFilter changeSetFilter = new ChangeSetFilter();
+ domain.addFilter(changeSetFilter);
+
domain.getEntityResolver().getCallbackRegistry().addListener(changeSetFilter);
+
+ ObjectContext context = runtime.getContext();
+
+ Auditable1 a2 = Cayenne.objectForPK(context, Auditable1.class, 2);
+ AuditableChild1 a21 = Cayenne.objectForPK(context,
AuditableChild1.class, 1);
+
+ a21.setParent(a2);
+ context.commitChanges();
+
+ assertEquals(2, processor.size);
+
+
assertTrue(processor.audited.get(AuditableOperation.UPDATE).contains(a2));
+ assertTrue(processor.audited.get(AuditableOperation.UPDATE).contains(
+ Cayenne.objectForPK(context, Auditable1.class, 1)));
+ }
+
+ public void testAudit_IgnoreProperties() throws Exception {
+
+ auditable2.insert(1, "P1_1", "P2_1");
+ auditable2.insert(2, "P1_2", "P2_2");
+ auditable2.insert(3, "P1_3", "P2_3");
+
+ DataDomain domain = runtime.getDataDomain();
+
+ Processor processor = new Processor();
+
+ AuditableFilter filter = new AuditableFilter(
+ domain.getEntityResolver(),
+ processor);
+ domain.addFilter(filter);
+ domain.getEntityResolver().getCallbackRegistry().addListener(filter);
+
+ // prerequisite for BaseAuditableProcessor use
+ ChangeSetFilter changeSetFilter = new ChangeSetFilter();
+ domain.addFilter(changeSetFilter);
+
domain.getEntityResolver().getCallbackRegistry().addListener(changeSetFilter);
+
+ ObjectContext context = runtime.getContext();
+
+ Auditable2 a1 = Cayenne.objectForPK(context, Auditable2.class, 1);
+ Auditable2 a2 = Cayenne.objectForPK(context, Auditable2.class, 2);
+ Auditable2 a3 = Cayenne.objectForPK(context, Auditable2.class, 3);
+
+ a1.setCharProperty1("__");
+ a2.setCharProperty2("__");
+ a3.setCharProperty1("__");
+ a3.setCharProperty2("__");
+
+ context.commitChanges();
+
+ assertEquals(2, processor.size);
+
assertTrue(processor.audited.get(AuditableOperation.UPDATE).contains(a2));
+
assertTrue(processor.audited.get(AuditableOperation.UPDATE).contains(a3));
+ }
+
+ public void testAuditableChild_IgnoreProperties() throws Exception {
+
+ auditable2.insert(1, "P1_1", "P2_1");
+ auditable2.insert(2, "P1_2", "P2_2");
+ auditableChild3.insert(1, 1, "C", "D");
+
+ DataDomain domain = runtime.getDataDomain();
+
+ Processor processor = new Processor();
+
+ AuditableFilter filter = new AuditableFilter(
+ domain.getEntityResolver(),
+ processor);
+ domain.addFilter(filter);
+ domain.getEntityResolver().getCallbackRegistry().addListener(filter);
+
+ // prerequisite for BaseAuditableProcessor use
+ ChangeSetFilter changeSetFilter = new ChangeSetFilter();
+ domain.addFilter(changeSetFilter);
+
domain.getEntityResolver().getCallbackRegistry().addListener(changeSetFilter);
+
+ ObjectContext context = runtime.getContext();
+
+ AuditableChild3 ac1 = Cayenne.objectForPK(context,
AuditableChild3.class, 1);
+
+ // a change to ignored property should not cause an audit event
+ ac1.setCharProperty1("X_X");
+
+ context.commitChanges();
+ assertEquals(0, processor.size);
+
+ processor.reset();
+ ac1.setCharProperty2("XXXXX");
+ context.commitChanges();
+ assertEquals(1, processor.size);
+ }
+
+ private final class Processor implements AuditableProcessor {
+
+ Map<AuditableOperation, Collection<Object>> audited;
+ int size;
+
+ Processor() {
+ reset();
+ }
+
+ void reset() {
+
+ audited = new EnumMap<AuditableOperation, Collection<Object>>(
+ AuditableOperation.class);
+
+ for (AuditableOperation op : AuditableOperation.values()) {
+ audited.put(op, new ArrayList<Object>());
+ }
+ }
+
+ public void audit(Object object, AuditableOperation operation) {
+ audited.get(operation).add(object);
+ size++;
+ }
+ }
+}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable1.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable1.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable1.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable1.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,9 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.audit.Auditable;
+import org.apache.cayenne.lifecycle.db.auto._Auditable1;
+
+@Auditable
+public class Auditable1 extends _Auditable1 {
+
+}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable2.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable2.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable2.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable2.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,9 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.audit.Auditable;
+import org.apache.cayenne.lifecycle.db.auto._Auditable2;
+
+@Auditable(ignoredProperties = _Auditable2.CHAR_PROPERTY1_PROPERTY)
+public class Auditable2 extends _Auditable2 {
+
+}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild1.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild1.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild1.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild1.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,7 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.db.auto._AuditableChild1;
+
+public class AuditableChild1 extends _AuditableChild1 {
+
+}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild2.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild2.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild2.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild2.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,7 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.db.auto._AuditableChild2;
+
+public class AuditableChild2 extends _AuditableChild2 {
+
+}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild3.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild3.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild3.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild3.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,9 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.audit.AuditableChild;
+import org.apache.cayenne.lifecycle.db.auto._AuditableChild3;
+
+@AuditableChild(value = _AuditableChild3.PARENT_PROPERTY, ignoredProperties =
_AuditableChild3.CHAR_PROPERTY1_PROPERTY)
+public class AuditableChild3 extends _AuditableChild3 {
+
+}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable1.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable1.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable1.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable1.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,40 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import java.util.List;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.lifecycle.db.AuditableChild1;
+
+/**
+ * Class _Auditable1 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 _Auditable1 extends CayenneDataObject {
+
+ public static final String CHAR_PROPERTY1_PROPERTY = "charProperty1";
+ public static final String CHILDREN1_PROPERTY = "children1";
+
+ public static final String ID_PK_COLUMN = "ID";
+
+ public void setCharProperty1(String charProperty1) {
+ writeProperty(CHAR_PROPERTY1_PROPERTY, charProperty1);
+ }
+ public String getCharProperty1() {
+ return (String)readProperty(CHAR_PROPERTY1_PROPERTY);
+ }
+
+ public void addToChildren1(AuditableChild1 obj) {
+ addToManyTarget(CHILDREN1_PROPERTY, obj, true);
+ }
+ public void removeFromChildren1(AuditableChild1 obj) {
+ removeToManyTarget(CHILDREN1_PROPERTY, obj, true);
+ }
+ @SuppressWarnings("unchecked")
+ public List<AuditableChild1> getChildren1() {
+ return (List<AuditableChild1>)readProperty(CHILDREN1_PROPERTY);
+ }
+
+
+}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable2.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable2.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable2.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable2.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,48 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import java.util.List;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.lifecycle.db.AuditableChild3;
+
+/**
+ * Class _Auditable2 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 _Auditable2 extends CayenneDataObject {
+
+ public static final String CHAR_PROPERTY1_PROPERTY = "charProperty1";
+ public static final String CHAR_PROPERTY2_PROPERTY = "charProperty2";
+ public static final String CHILDREN_PROPERTY = "children";
+
+ public static final String ID_PK_COLUMN = "ID";
+
+ public void setCharProperty1(String charProperty1) {
+ writeProperty(CHAR_PROPERTY1_PROPERTY, charProperty1);
+ }
+ public String getCharProperty1() {
+ return (String)readProperty(CHAR_PROPERTY1_PROPERTY);
+ }
+
+ public void setCharProperty2(String charProperty2) {
+ writeProperty(CHAR_PROPERTY2_PROPERTY, charProperty2);
+ }
+ public String getCharProperty2() {
+ return (String)readProperty(CHAR_PROPERTY2_PROPERTY);
+ }
+
+ public void addToChildren(AuditableChild3 obj) {
+ addToManyTarget(CHILDREN_PROPERTY, obj, true);
+ }
+ public void removeFromChildren(AuditableChild3 obj) {
+ removeToManyTarget(CHILDREN_PROPERTY, obj, true);
+ }
+ @SuppressWarnings("unchecked")
+ public List<AuditableChild3> getChildren() {
+ return (List<AuditableChild3>)readProperty(CHILDREN_PROPERTY);
+ }
+
+
+}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild1.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild1.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild1.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild1.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,35 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.lifecycle.db.Auditable1;
+
+/**
+ * Class _AuditableChild1 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 _AuditableChild1 extends CayenneDataObject {
+
+ public static final String CHAR_PROPERTY1_PROPERTY = "charProperty1";
+ public static final String PARENT_PROPERTY = "parent";
+
+ public static final String ID_PK_COLUMN = "ID";
+
+ public void setCharProperty1(String charProperty1) {
+ writeProperty(CHAR_PROPERTY1_PROPERTY, charProperty1);
+ }
+ public String getCharProperty1() {
+ return (String)readProperty(CHAR_PROPERTY1_PROPERTY);
+ }
+
+ public void setParent(Auditable1 parent) {
+ setToOneTarget(PARENT_PROPERTY, parent, true);
+ }
+
+ public Auditable1 getParent() {
+ return (Auditable1)readProperty(PARENT_PROPERTY);
+ }
+
+
+}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild2.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild2.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild2.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild2.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,35 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.lifecycle.db.Auditable1;
+
+/**
+ * Class _AuditableChild2 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 _AuditableChild2 extends CayenneDataObject {
+
+ public static final String CHAR_PROPERTY1_PROPERTY = "charProperty1";
+ public static final String PARENT_PROPERTY = "parent";
+
+ public static final String ID_PK_COLUMN = "ID";
+
+ public void setCharProperty1(String charProperty1) {
+ writeProperty(CHAR_PROPERTY1_PROPERTY, charProperty1);
+ }
+ public String getCharProperty1() {
+ return (String)readProperty(CHAR_PROPERTY1_PROPERTY);
+ }
+
+ public void setParent(Auditable1 parent) {
+ setToOneTarget(PARENT_PROPERTY, parent, true);
+ }
+
+ public Auditable1 getParent() {
+ return (Auditable1)readProperty(PARENT_PROPERTY);
+ }
+
+
+}
Added:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild3.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild3.java?rev=1240798&view=auto
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild3.java
(added)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild3.java
Sun Feb 5 20:26:58 2012
@@ -0,0 +1,43 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.lifecycle.db.Auditable2;
+
+/**
+ * Class _AuditableChild3 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 _AuditableChild3 extends CayenneDataObject {
+
+ public static final String CHAR_PROPERTY1_PROPERTY = "charProperty1";
+ public static final String CHAR_PROPERTY2_PROPERTY = "charProperty2";
+ public static final String PARENT_PROPERTY = "parent";
+
+ public static final String ID_PK_COLUMN = "ID";
+
+ public void setCharProperty1(String charProperty1) {
+ writeProperty(CHAR_PROPERTY1_PROPERTY, charProperty1);
+ }
+ public String getCharProperty1() {
+ return (String)readProperty(CHAR_PROPERTY1_PROPERTY);
+ }
+
+ public void setCharProperty2(String charProperty2) {
+ writeProperty(CHAR_PROPERTY2_PROPERTY, charProperty2);
+ }
+ public String getCharProperty2() {
+ return (String)readProperty(CHAR_PROPERTY2_PROPERTY);
+ }
+
+ public void setParent(Auditable2 parent) {
+ setToOneTarget(PARENT_PROPERTY, parent, true);
+ }
+
+ public Auditable2 getParent() {
+ return (Auditable2)readProperty(PARENT_PROPERTY);
+ }
+
+
+}
Modified:
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
---
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml
(original)
+++
cayenne/main/trunk/framework/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml
Sun Feb 5 20:26:58 2012
@@ -4,6 +4,31 @@
xsi:schemaLocation="http://cayenne.apache.org/schema/3.0/modelMap
http://cayenne.apache.org/schema/3.0/modelMap.xsd"
project-version="6">
<property name="defaultPackage"
value="org.apache.cayenne.lifecycle.db"/>
+ <db-entity name="AUDITABLE1">
+ <db-attribute name="CHAR_PROPERTY1" type="VARCHAR"
length="200"/>
+ <db-attribute name="ID" type="INTEGER" isPrimaryKey="true"
isMandatory="true"/>
+ </db-entity>
+ <db-entity name="AUDITABLE2">
+ <db-attribute name="CHAR_PROPERTY1" type="VARCHAR"
length="200"/>
+ <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_CHILD1">
+ <db-attribute name="AUDITABLE1_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="AUDITABLE_CHILD2">
+ <db-attribute name="AUDITABLE1_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="AUDITABLE_CHILD3">
+ <db-attribute name="AUDITABLE2_ID" type="INTEGER"/>
+ <db-attribute name="CHAR_PROPERTY1" type="VARCHAR"
length="200"/>
+ <db-attribute name="CHAR_PROPERTY2" type="VARCHAR"
length="200"/>
+ <db-attribute name="ID" type="INTEGER" isPrimaryKey="true"
isMandatory="true"/>
+ </db-entity>
<db-entity name="E1">
<db-attribute name="ID" type="BIGINT" isPrimaryKey="true"
isMandatory="true"/>
</db-entity>
@@ -21,6 +46,23 @@
<db-attribute name="ID" type="INTEGER" isPrimaryKey="true"
isMandatory="true"/>
<db-attribute name="UUID" type="VARCHAR" length="200"/>
</db-entity>
+ <obj-entity name="Auditable1"
className="org.apache.cayenne.lifecycle.db.Auditable1"
dbEntityName="AUDITABLE1">
+ <obj-attribute name="charProperty1" type="java.lang.String"
db-attribute-path="CHAR_PROPERTY1"/>
+ </obj-entity>
+ <obj-entity name="Auditable2"
className="org.apache.cayenne.lifecycle.db.Auditable2"
dbEntityName="AUDITABLE2">
+ <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="AuditableChild1"
className="org.apache.cayenne.lifecycle.db.AuditableChild1"
dbEntityName="AUDITABLE_CHILD1">
+ <obj-attribute name="charProperty1" type="java.lang.String"
db-attribute-path="CHAR_PROPERTY1"/>
+ </obj-entity>
+ <obj-entity name="AuditableChild2"
className="org.apache.cayenne.lifecycle.db.AuditableChild2"
dbEntityName="AUDITABLE_CHILD2">
+ <obj-attribute name="charProperty1" type="java.lang.String"
db-attribute-path="CHAR_PROPERTY1"/>
+ </obj-entity>
+ <obj-entity name="AuditableChild3"
className="org.apache.cayenne.lifecycle.db.AuditableChild3"
dbEntityName="AUDITABLE_CHILD3">
+ <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="E1" className="org.apache.cayenne.lifecycle.db.E1"
dbEntityName="E1">
</obj-entity>
<obj-entity name="E2" className="org.apache.cayenne.lifecycle.db.E2"
dbEntityName="E2">
@@ -32,12 +74,35 @@
<obj-entity name="UuidRoot1"
className="org.apache.cayenne.lifecycle.db.UuidRoot1" dbEntityName="UUID_ROOT1">
<obj-attribute name="uuid" type="java.lang.String"
db-attribute-path="UUID"/>
</obj-entity>
+ <db-relationship name="children1" source="AUDITABLE1"
target="AUDITABLE_CHILD1" toMany="true">
+ <db-attribute-pair source="ID" target="AUDITABLE1_ID"/>
+ </db-relationship>
+ <db-relationship name="children2" source="AUDITABLE1"
target="AUDITABLE_CHILD2" toMany="true">
+ <db-attribute-pair source="ID" target="AUDITABLE1_ID"/>
+ </db-relationship>
+ <db-relationship name="children" source="AUDITABLE2"
target="AUDITABLE_CHILD3" toMany="true">
+ <db-attribute-pair source="ID" target="AUDITABLE2_ID"/>
+ </db-relationship>
+ <db-relationship name="parent" source="AUDITABLE_CHILD1"
target="AUDITABLE1" toMany="false">
+ <db-attribute-pair source="AUDITABLE1_ID" target="ID"/>
+ </db-relationship>
+ <db-relationship name="parent" source="AUDITABLE_CHILD2"
target="AUDITABLE1" toMany="false">
+ <db-attribute-pair source="AUDITABLE1_ID" target="ID"/>
+ </db-relationship>
+ <db-relationship name="parent" source="AUDITABLE_CHILD3"
target="AUDITABLE2" toMany="false">
+ <db-attribute-pair source="AUDITABLE2_ID" target="ID"/>
+ </db-relationship>
<db-relationship name="root" source="SORT_DEP" target="SORT_ROOT"
toMany="false">
<db-attribute-pair source="ROOT_ID" target="ID"/>
</db-relationship>
<db-relationship name="deps" source="SORT_ROOT" target="SORT_DEP"
toMany="true">
<db-attribute-pair source="ID" target="ROOT_ID"/>
</db-relationship>
+ <obj-relationship name="children1" source="Auditable1"
target="AuditableChild1" deleteRule="Deny" db-relationship-path="children1"/>
+ <obj-relationship name="children" source="Auditable2"
target="AuditableChild3" 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="AuditableChild2"
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="root" source="SortDep" target="SortRoot"
deleteRule="Nullify" db-relationship-path="root"/>
<obj-relationship name="deps" source="SortRoot" target="SortDep"
deleteRule="Deny" db-relationship-path="deps"/>
</data-map>