diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataContextObjectCreator.java b/cayenne/src/main/java/org/apache/cayenne/access/DataContextObjectCreator.java
index de7a5ebfa7..f7d9ec3cb8 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataContextObjectCreator.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataContextObjectCreator.java
@@ -209,7 +209,7 @@ protected void injectInitialValue(Object obj) {
ObjEntity entity;
try {
- entity = context.getEntityResolver().getObjEntity(object.getClass());
+ entity = context.getEntityResolver().getObjEntity(object.getObjectId().getEntityName());
} catch (CayenneRuntimeException ex) {
// ObjEntity cannot be fetched, ignored
entity = null;
diff --git a/cayenne/src/main/java/org/apache/cayenne/reflect/ArcProperty.java b/cayenne/src/main/java/org/apache/cayenne/reflect/ArcProperty.java
index 97e2499ff8..0bab4ca7d9 100644
--- a/cayenne/src/main/java/org/apache/cayenne/reflect/ArcProperty.java
+++ b/cayenne/src/main/java/org/apache/cayenne/reflect/ArcProperty.java
@@ -19,7 +19,6 @@
package org.apache.cayenne.reflect;
-import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.ObjRelationship;
/**
@@ -55,7 +54,7 @@ public interface ArcProperty extends PropertyDescriptor {
* Returns a ClassDescriptor for the type of graph nodes pointed to by this
* arc property. Note that considering that a target object may be a
* subclass of the class handled by the descriptor, users of this method may
- * need to call {@link ClassDescriptor#getSubclassDescriptor(Class)} before
+ * need to call {@link ClassDescriptor#getSubclassDescriptor(String)} before
* using the descriptor to access objects.
*/
ClassDescriptor getTargetDescriptor();
diff --git a/cayenne/src/main/java/org/apache/cayenne/reflect/ClassDescriptor.java b/cayenne/src/main/java/org/apache/cayenne/reflect/ClassDescriptor.java
index 13833f8e3c..51b4ca5a03 100644
--- a/cayenne/src/main/java/org/apache/cayenne/reflect/ClassDescriptor.java
+++ b/cayenne/src/main/java/org/apache/cayenne/reflect/ClassDescriptor.java
@@ -88,14 +88,25 @@ public interface ClassDescriptor {
ClassDescriptor getSuperclassDescriptor();
/**
- * Returns the most "specialized" descriptor for a given class. This method assumes
- * that the following is true:
+ * Returns the most "specialized" descriptor for a given class.
+ * This method assumes that the following is true:
*
*
* this.getObjectClass().isAssignableFrom(objectClass)
*
+ * @deprecated since 5.0, will throw UnsupportedOperationException on invocation,
+ * use {@link #getSubclassDescriptor(String)}
*/
- ClassDescriptor getSubclassDescriptor(Class> objectClass);
+ @Deprecated(since = "5.0", forRemoval = true)
+ default ClassDescriptor getSubclassDescriptor(Class> unused) {
+ throw new UnsupportedOperationException("This method is deprecated, use getSubclassDescriptor(entityName) instead");
+ }
+
+ /**
+ * Returns the most "specialized" descriptor for a given entity name.
+ * @since 5.0
+ */
+ ClassDescriptor getSubclassDescriptor(String entityName);
/**
* Creates a new instance of a class described by this object.
diff --git a/cayenne/src/main/java/org/apache/cayenne/reflect/LazyClassDescriptorDecorator.java b/cayenne/src/main/java/org/apache/cayenne/reflect/LazyClassDescriptorDecorator.java
index 6c7810f327..228394a5e8 100644
--- a/cayenne/src/main/java/org/apache/cayenne/reflect/LazyClassDescriptorDecorator.java
+++ b/cayenne/src/main/java/org/apache/cayenne/reflect/LazyClassDescriptorDecorator.java
@@ -147,9 +147,10 @@ public PropertyDescriptor getProperty(String propertyName) {
return descriptor.getProperty(propertyName);
}
- public ClassDescriptor getSubclassDescriptor(Class> objectClass) {
+ @Override
+ public ClassDescriptor getSubclassDescriptor(String entityName) {
checkDescriptorInitialized();
- return descriptor.getSubclassDescriptor(objectClass);
+ return descriptor.getSubclassDescriptor(entityName);
}
public ClassDescriptor getSuperclassDescriptor() {
diff --git a/cayenne/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java b/cayenne/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
index 8842c3e499..9f981765e4 100644
--- a/cayenne/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
+++ b/cayenne/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
@@ -46,9 +46,7 @@
*/
public class PersistentDescriptor implements ClassDescriptor {
- static final Integer TRANSIENT_STATE = PersistenceState.TRANSIENT;
static final Integer HOLLOW_STATE = PersistenceState.HOLLOW;
- static final Integer COMMITTED_STATE = PersistenceState.COMMITTED;
protected ClassDescriptor superclassDescriptor;
@@ -85,7 +83,7 @@ public PersistentDescriptor() {
this.subclassDescriptors = new HashMap<>();
// must be a set as duplicate addition attempts are expected...
- this.rootDbEntities = new HashSet(1);
+ this.rootDbEntities = new HashSet<>(1);
}
public void setDiscriminatorColumns(Collection columns) {
@@ -213,14 +211,11 @@ public void removeDeclaredProperty(String propertyName) {
/**
* Adds a subclass descriptor that maps to a given class name.
*/
- public void addSubclassDescriptor(String className, ClassDescriptor subclassDescriptor) {
- // note that 'className' should be used instead of
- // "subclassDescriptor.getEntity().getClassName()", as this method is
- // called in
- // the early phases of descriptor initialization and we do not want to
- // trigger
- // subclassDescriptor resolution just yet to prevent stack overflow.
- subclassDescriptors.put(className, subclassDescriptor);
+ public void addSubclassDescriptor(String entityName, ClassDescriptor subclassDescriptor) {
+ // NOTE: 'entityName' should be used instead of "subclassDescriptor.getEntity().getName()",
+ // as this method is called in the early phases of descriptor initialization, and we do not want to
+ // trigger subclassDescriptor resolution just yet to prevent stack overflow.
+ subclassDescriptors.put(entityName, subclassDescriptor);
}
public ObjEntity getEntity() {
@@ -259,8 +254,8 @@ void setObjectClass(Class> objectClass) {
this.objectClass = objectClass;
}
- public ClassDescriptor getSubclassDescriptor(Class> objectClass) {
- if (objectClass == null) {
+ public ClassDescriptor getSubclassDescriptor(String entityName) {
+ if (entityName == null) {
throw new IllegalArgumentException("Null objectClass");
}
@@ -268,22 +263,12 @@ public ClassDescriptor getSubclassDescriptor(Class> objectClass) {
return this;
}
- ClassDescriptor subclassDescriptor = subclassDescriptors.get(objectClass.getName());
-
- // ascend via the class hierarchy (only doing it if there are multiple
- // choices)
- if (subclassDescriptor == null) {
- Class> currentClass = objectClass;
- while (subclassDescriptor == null && (currentClass = currentClass.getSuperclass()) != null) {
- subclassDescriptor = subclassDescriptors.get(currentClass.getName());
- }
- }
-
+ ClassDescriptor subclassDescriptor = subclassDescriptors.get(entityName);
return subclassDescriptor != null ? subclassDescriptor : this;
}
public Collection getDiscriminatorColumns() {
- return allDiscriminatorColumns != null ? allDiscriminatorColumns : Collections. emptyList();
+ return allDiscriminatorColumns != null ? allDiscriminatorColumns : Collections.emptyList();
}
public Collection getIdProperties() {
diff --git a/cayenne/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java b/cayenne/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
index 3e22bf5b74..e573f45012 100644
--- a/cayenne/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
+++ b/cayenne/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
@@ -65,7 +65,8 @@ public ClassDescriptor getDescriptor(String entityName) {
protected ClassDescriptor getDescriptor(ObjEntity entity, Class> entityClass) {
String superEntityName = entity.getSuperEntityName();
- ClassDescriptor superDescriptor = (superEntityName != null) ? descriptorMap.getDescriptor(superEntityName)
+ ClassDescriptor superDescriptor = (superEntityName != null)
+ ? descriptorMap.getDescriptor(superEntityName)
: null;
PersistentDescriptor descriptor = createDescriptor();
@@ -175,7 +176,7 @@ protected void indexSubclassDescriptors(PersistentDescriptor descriptor, EntityI
for (EntityInheritanceTree child : inheritanceTree.getChildren()) {
ObjEntity childEntity = child.getEntity();
- descriptor.addSubclassDescriptor(childEntity.getClassName(),
+ descriptor.addSubclassDescriptor(childEntity.getName(),
descriptorMap.getDescriptor(childEntity.getName()));
indexSubclassDescriptors(descriptor, child);
@@ -200,8 +201,7 @@ private void appendDeclaredRootDbEntity(PersistentDescriptor descriptor, ObjEnti
DbEntity dbEntity = entity.getDbEntity();
if (dbEntity != null) {
// descriptor takes care of weeding off duplicates, which are likely
- // in cases
- // of non-horizontal inheritance
+ // in cases of non-horizontal inheritance
descriptor.addRootDbEntity(dbEntity);
}
}
diff --git a/cayenne/src/main/java/org/apache/cayenne/util/DeepMergeOperation.java b/cayenne/src/main/java/org/apache/cayenne/util/DeepMergeOperation.java
index a3e1c24a90..414475baa3 100644
--- a/cayenne/src/main/java/org/apache/cayenne/util/DeepMergeOperation.java
+++ b/cayenne/src/main/java/org/apache/cayenne/util/DeepMergeOperation.java
@@ -83,7 +83,7 @@ private T merge(
final T target = shallowMergeOperation.merge(peerInParentContext);
seen.put(id, target);
- descriptor = descriptor.getSubclassDescriptor(peerInParentContext.getClass());
+ descriptor = descriptor.getSubclassDescriptor(id.getEntityName());
descriptor.visitProperties(new PropertyVisitor() {
public boolean visitToOne(ToOneProperty property) {
diff --git a/cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java b/cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
index 74f37a4392..c49c8ab2cb 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
@@ -20,6 +20,7 @@
import org.apache.cayenne.Cayenne;
import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.Persistent;
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.query.ColumnSelect;
import org.apache.cayenne.query.EJBQLQuery;
@@ -1234,4 +1235,37 @@ public void testColumnSelectVerticalInheritance_Sub1Sub1() throws SQLException {
assertEquals("mDA", result.getSub1Name());
assertEquals("3DQa", result.getSub1Sub1Name());
}
+
+ @Test
+ public void testInsertTwoGenericVerticalInheritanceObjects() {
+ // Generic DataObjects play nicer with a DataContext
+ final DataContext dataContext = (DataContext) context;
+
+ final Persistent girlEmma = dataContext.newObject("GenGirl");
+ final Persistent boyLuke = dataContext.newObject("GenBoy");
+
+ assertEquals("Girl is type G", girlEmma.readProperty("type"), "G");
+ assertEquals("Boy is type B", boyLuke.readProperty("type"), "B");
+
+ girlEmma.writeProperty("reference", "g1");
+ girlEmma.writeProperty("name", "Emma");
+ girlEmma.writeProperty("toyDolls", 5);
+
+ boyLuke.writeProperty("reference", "b1");
+ boyLuke.writeProperty("name", "Luke");
+ boyLuke.writeProperty("toyTrucks", 12);
+
+ context.commitChanges();
+
+ assertEquals(2, ObjectSelect.query(Persistent.class, "GenStudent").selectCount(context));
+
+ final List students = ObjectSelect.query(Persistent.class, "GenStudent").select(context);
+ assertTrue(students.contains(girlEmma));
+ assertTrue(students.contains(boyLuke));
+
+ final List girls = ObjectSelect.query(Persistent.class, "GenGirl").select(context);
+ assertEquals(1, girls.size());
+ final List boys = ObjectSelect.query(Persistent.class, "GenBoy").select(context);
+ assertEquals(1, boys.size());
+ }
}
diff --git a/cayenne/src/test/java/org/apache/cayenne/reflect/generic/PersistentObjectDescriptorFactory_VerticalInheritanceIT.java b/cayenne/src/test/java/org/apache/cayenne/reflect/generic/PersistentObjectDescriptorFactory_VerticalInheritanceIT.java
new file mode 100644
index 0000000000..c3f83ac144
--- /dev/null
+++ b/cayenne/src/test/java/org/apache/cayenne/reflect/generic/PersistentObjectDescriptorFactory_VerticalInheritanceIT.java
@@ -0,0 +1,65 @@
+/*****************************************************************
+ * 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.reflect.generic;
+
+import org.apache.cayenne.GenericPersistentObject;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.SingletonFaultFactory;
+import org.apache.cayenne.unit.di.runtime.CayenneProjects;
+import org.apache.cayenne.unit.di.runtime.RuntimeCase;
+import org.apache.cayenne.unit.di.runtime.UseCayenneRuntime;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+
+@UseCayenneRuntime(CayenneProjects.INHERITANCE_VERTICAL_PROJECT)
+public class PersistentObjectDescriptorFactory_VerticalInheritanceIT extends RuntimeCase {
+
+ @Inject
+ private EntityResolver resolver;
+
+ @Test
+ public void testVisitProperties_IterationOrder() {
+
+ PersistentObjectDescriptorFactory factory = new PersistentObjectDescriptorFactory(
+ resolver.getClassDescriptorMap(),
+ new SingletonFaultFactory(),
+ new DefaultValueComparisonStrategyFactory(mock(ValueObjectTypeRegistry.class))
+ );
+
+ ClassDescriptor genStudent = factory.getDescriptor("GenStudent");
+ assertNotNull(genStudent);
+ ClassDescriptor genBoy = genStudent.getSubclassDescriptor("GenBoy");
+ assertNotNull(genBoy);
+ ClassDescriptor genGirl = genStudent.getSubclassDescriptor("GenGirl");
+ assertNotNull(genGirl);
+
+ assertNotSame(genStudent, genBoy);
+ assertNotSame(genStudent, genGirl);
+ assertNotSame(genBoy, genGirl);
+
+ assertEquals(GenericPersistentObject.class, genBoy.getObjectClass());
+ assertEquals(GenericPersistentObject.class, genGirl.getObjectClass());
+ assertEquals(GenericPersistentObject.class, genStudent.getObjectClass());
+ }
+}
diff --git a/cayenne/src/test/resources/inheritance-vertical.map.xml b/cayenne/src/test/resources/inheritance-vertical.map.xml
index 142f7ee13a..f5b8509f06 100644
--- a/cayenne/src/test/resources/inheritance-vertical.map.xml
+++ b/cayenne/src/test/resources/inheritance-vertical.map.xml
@@ -83,6 +83,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -176,6 +190,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -290,6 +317,18 @@
+
+
+
+
+
+
+
+
+
+
+
+