This is an automated email from the ASF dual-hosted git repository. ntimofeev pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/master by this push: new 16d61ac CAY-2704 Vertical inheritance with Embeddedables looses ObjAttribute information 16d61ac is described below commit 16d61acf01fefd2e1a6f2d31440c10863406510f Author: Nikita Timofeev <stari...@gmail.com> AuthorDate: Fri Mar 26 17:54:35 2021 +0300 CAY-2704 Vertical inheritance with Embeddedables looses ObjAttribute information --- .../org/apache/cayenne/map/EmbeddedAttribute.java | 24 ++++-- .../java/org/apache/cayenne/map/ObjEntity.java | 27 +++++-- .../org/apache/cayenne/access/EmbeddingIT.java | 62 +++++++++++++++ .../cayenne/testdo/embeddable/EmbedChild.java | 9 +++ .../cayenne/testdo/embeddable/EmbedRoot.java | 9 +++ .../testdo/embeddable/auto/_EmbedChild.java | 87 ++++++++++++++++++++++ .../testdo/embeddable/auto/_EmbedEntity1.java | 2 +- .../testdo/embeddable/auto/_EmbedEntity2.java | 2 +- .../auto/{_EmbedEntity2.java => _EmbedRoot.java} | 35 ++++----- .../src/test/resources/embeddable.map.xml | 27 +++++++ 10 files changed, 252 insertions(+), 32 deletions(-) diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddedAttribute.java b/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddedAttribute.java index a3d99ef..6acad57 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddedAttribute.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddedAttribute.java @@ -54,6 +54,21 @@ public class EmbeddedAttribute extends ObjAttribute { setEntity(entity); } + /** + * Copying constructor + * @param other attribute to copy + * @since 4.2 + */ + public EmbeddedAttribute(EmbeddedAttribute other) { + setName(other.getName()); + setType(other.getType()); + setEntity(other.getEntity()); + setDbAttributePath(other.getDbAttributePath()); + setUsedForLocking(other.isUsedForLocking()); + setLazy(other.isLazy()); + attributeOverrides = other.getAttributeOverrides(); + } + @Override public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) { encoder.start("embedded-attribute") @@ -91,15 +106,10 @@ public class EmbeddedAttribute extends ObjAttribute { return makeObjAttribute(embeddableAttribute, dbPath); } - private ObjAttribute makeObjAttribute( - EmbeddableAttribute embeddableAttribute, - String dbPath) { + private ObjAttribute makeObjAttribute(EmbeddableAttribute embeddableAttribute, String dbPath) { String fullName = getName() + "." + embeddableAttribute.getName(); - ObjAttribute oa = new ObjAttribute( - fullName, - embeddableAttribute.getType(), - (ObjEntity) getEntity()); + ObjAttribute oa = new ObjAttribute(fullName, embeddableAttribute.getType(), getEntity()); oa.setDbAttributePath(dbPath); return oa; } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java index 47ea965..a59065c 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java @@ -32,6 +32,8 @@ import org.apache.cayenne.map.event.ObjEntityListener; import org.apache.cayenne.util.CayenneMapEntry; import org.apache.cayenne.util.Util; import org.apache.cayenne.util.XMLEncoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; @@ -52,11 +54,12 @@ import java.util.function.Function; */ public class ObjEntity extends Entity implements ObjEntityListener, ConfigurationNode { + private static final Logger LOGGER = LoggerFactory.getLogger(ObjEntity.class); + public static final int LOCK_TYPE_NONE = 0; public static final int LOCK_TYPE_OPTIMISTIC = 1; - // do not import CayenneDataObject as it introduces unneeded client - // dependency + // do not import CayenneDataObject as it introduces unneeded client dependency private static final String CAYENNE_DATA_OBJECT_CLASS = "org.apache.cayenne.CayenneDataObject"; /** * A collection of default "generic" entity classes excluded from class @@ -641,11 +644,23 @@ public class ObjEntity extends Entity implements ObjEntityListener, Configuratio String overridedDbPath = attributeOverrides.get(attributeName); - ObjAttribute attribute = new ObjAttribute(attributeMap.get(attributeName)); - attribute.setEntity(this); - if (overridedDbPath != null) { - attribute.setDbAttributePath(overridedDbPath); + ObjAttribute superAttribute = attributeMap.get(attributeName); + ObjAttribute attribute; + + if(superAttribute instanceof EmbeddedAttribute) { + EmbeddedAttribute embeddedAttribute = new EmbeddedAttribute((EmbeddedAttribute)superAttribute); + if(overridedDbPath != null) { + LOGGER.warn("'{}.{}': DB path override for an embedded attribute is not supported.", + getName(), attributeName); + } + attribute = embeddedAttribute; + } else { + attribute = new ObjAttribute(superAttribute); + if (overridedDbPath != null) { + attribute.setDbAttributePath(overridedDbPath); + } } + attribute.setEntity(this); map.put(attributeName, attribute); } } diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/EmbeddingIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/EmbeddingIT.java index 0f3f291..c01f363 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/access/EmbeddingIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/access/EmbeddingIT.java @@ -25,8 +25,10 @@ import org.apache.cayenne.di.Inject; import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.test.jdbc.DBHelper; import org.apache.cayenne.test.jdbc.TableHelper; +import org.apache.cayenne.testdo.embeddable.EmbedChild; import org.apache.cayenne.testdo.embeddable.EmbedEntity1; import org.apache.cayenne.testdo.embeddable.EmbedEntity2; +import org.apache.cayenne.testdo.embeddable.EmbedRoot; import org.apache.cayenne.testdo.embeddable.Embeddable1; import org.apache.cayenne.unit.di.server.CayenneProjects; import org.apache.cayenne.unit.di.server.ServerCase; @@ -38,6 +40,7 @@ import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; @UseServerRuntime(CayenneProjects.EMBEDDABLE_PROJECT) public class EmbeddingIT extends ServerCase { @@ -50,6 +53,8 @@ public class EmbeddingIT extends ServerCase { protected TableHelper tEmbedEntity1; protected TableHelper tEmbedEntity2; + protected TableHelper tEmbedRoot; + protected TableHelper tEmbedChild; @Before public void setUp() throws Exception { @@ -58,6 +63,12 @@ public class EmbeddingIT extends ServerCase { tEmbedEntity2 = new TableHelper(dbHelper, "EMBED_ENTITY2"); tEmbedEntity2.setColumns("ID", "NAME", "ENTITY1_ID", "EMBEDDED10", "EMBEDDED20"); + + tEmbedRoot = new TableHelper(dbHelper, "EMBED_ROOT"); + tEmbedRoot.setColumns("ID", "NAME", "EMBEDDED10", "EMBEDDED20", "TYPE"); + + tEmbedChild = new TableHelper(dbHelper, "EMBED_CHILD"); + tEmbedChild.setColumns("ID", "CHILD_ATTR"); } protected void createSelectDataSet() throws Exception { @@ -71,6 +82,12 @@ public class EmbeddingIT extends ServerCase { tEmbedEntity2.insert(2, "n2-1", 2, "e1", "e2"); } + protected void createSelectDataSetInheritance() throws Exception { + tEmbedRoot.insert(1, "root1", "e1-1", "e2-1", 0); + tEmbedRoot.insert(2, "root2", "e1-2", "e2-2", 1); + tEmbedChild.insert(2, "child-attr1"); + } + protected void createUpdateDataSet() throws Exception { tEmbedEntity1.insert(1, "n1", "e1", "e2", "e3", "e4"); } @@ -298,4 +315,49 @@ public class EmbeddingIT extends ServerCase { .select(context); assertEquals(1, result.size()); } + + @Test + public void testSelectWithInheritance() throws Exception { + createSelectDataSetInheritance(); + + List<EmbedRoot> roots = ObjectSelect.query(EmbedRoot.class) + .orderBy(EmbedRoot.NAME.asc()) + .select(context); + + assertEquals(2, roots.size()); + + EmbedRoot root = roots.get(0); + EmbedRoot child = roots.get(1); + assertTrue(child instanceof EmbedChild); + + assertEquals("e1-1", root.getEmbedded().getEmbedded10()); + assertEquals("e2-1", root.getEmbedded().getEmbedded20()); + + assertEquals("e1-2", child.getEmbedded().getEmbedded10()); + assertEquals("e2-2", child.getEmbedded().getEmbedded20()); + assertEquals("child-attr1", ((EmbedChild) child).getChildAttr()); + } + + @Test + public void testInsertWithInheritance() { + { + EmbedRoot root = context.newObject(EmbedRoot.class); + root.setName("root"); + Embeddable1 embeddable1 = new Embeddable1(); + embeddable1.setEmbedded10("root-10"); + embeddable1.setEmbedded20("root-20"); + root.setEmbedded(embeddable1); + } + + { + EmbedChild child = context.newObject(EmbedChild.class); + child.setName("child"); + Embeddable1 embeddable1 = new Embeddable1(); + embeddable1.setEmbedded10("child-10"); + embeddable1.setEmbedded20("child-20"); + child.setEmbedded(embeddable1); + } + + context.commitChanges(); + } } diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/EmbedChild.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/EmbedChild.java new file mode 100644 index 0000000..f90058f --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/EmbedChild.java @@ -0,0 +1,9 @@ +package org.apache.cayenne.testdo.embeddable; + +import org.apache.cayenne.testdo.embeddable.auto._EmbedChild; + +public class EmbedChild extends _EmbedChild { + + private static final long serialVersionUID = 1L; + +} diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/EmbedRoot.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/EmbedRoot.java new file mode 100644 index 0000000..a7c5dd3 --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/EmbedRoot.java @@ -0,0 +1,9 @@ +package org.apache.cayenne.testdo.embeddable; + +import org.apache.cayenne.testdo.embeddable.auto._EmbedRoot; + +public class EmbedRoot extends _EmbedRoot { + + private static final long serialVersionUID = 1L; + +} diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedChild.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedChild.java new file mode 100644 index 0000000..a43726a --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedChild.java @@ -0,0 +1,87 @@ +package org.apache.cayenne.testdo.embeddable.auto; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.apache.cayenne.exp.property.PropertyFactory; +import org.apache.cayenne.exp.property.StringProperty; +import org.apache.cayenne.testdo.embeddable.EmbedRoot; + +/** + * Class _EmbedChild 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 _EmbedChild extends EmbedRoot { + + private static final long serialVersionUID = 1L; + + public static final String ID_PK_COLUMN = "ID"; + + public static final StringProperty<String> CHILD_ATTR = PropertyFactory.createString("childAttr", String.class); + + protected String childAttr; + + + public void setChildAttr(String childAttr) { + beforePropertyWrite("childAttr", this.childAttr, childAttr); + this.childAttr = childAttr; + } + + public String getChildAttr() { + beforePropertyRead("childAttr"); + return this.childAttr; + } + + @Override + public Object readPropertyDirectly(String propName) { + if(propName == null) { + throw new IllegalArgumentException(); + } + + switch(propName) { + case "childAttr": + return this.childAttr; + default: + return super.readPropertyDirectly(propName); + } + } + + @Override + public void writePropertyDirectly(String propName, Object val) { + if(propName == null) { + throw new IllegalArgumentException(); + } + + switch (propName) { + case "childAttr": + this.childAttr = (String)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.childAttr); + } + + @Override + protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException { + super.readState(in); + this.childAttr = (String)in.readObject(); + } + +} diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java index e5d29cb..82b8a1d 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java @@ -21,7 +21,7 @@ import org.apache.cayenne.testdo.embeddable.Embeddable1; */ public abstract class _EmbedEntity1 extends BaseDataObject { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; public static final String ID_PK_COLUMN = "ID"; diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity2.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity2.java index 5d867b2..c4875eb 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity2.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity2.java @@ -20,7 +20,7 @@ import org.apache.cayenne.testdo.embeddable.Embeddable1; */ public abstract class _EmbedEntity2 extends BaseDataObject { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; public static final String ID_PK_COLUMN = "ID"; diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity2.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedRoot.java similarity index 78% copy from cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity2.java copy to cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedRoot.java index 5d867b2..0d12bae 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity2.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedRoot.java @@ -6,32 +6,31 @@ import java.io.ObjectOutputStream; import org.apache.cayenne.BaseDataObject; import org.apache.cayenne.exp.property.EmbeddableProperty; -import org.apache.cayenne.exp.property.EntityProperty; +import org.apache.cayenne.exp.property.NumericProperty; import org.apache.cayenne.exp.property.PropertyFactory; import org.apache.cayenne.exp.property.StringProperty; -import org.apache.cayenne.testdo.embeddable.EmbedEntity1; import org.apache.cayenne.testdo.embeddable.Embeddable1; /** - * Class _EmbedEntity2 was generated by Cayenne. + * Class _EmbedRoot 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 _EmbedEntity2 extends BaseDataObject { +public abstract class _EmbedRoot extends BaseDataObject { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; public static final String ID_PK_COLUMN = "ID"; public static final EmbeddableProperty<Embeddable1> EMBEDDED = PropertyFactory.createEmbeddable("embedded", Embeddable1.class); public static final StringProperty<String> NAME = PropertyFactory.createString("name", String.class); - public static final EntityProperty<EmbedEntity1> ENTITY1 = PropertyFactory.createEntity("entity1", EmbedEntity1.class); + public static final NumericProperty<Integer> TYPE = PropertyFactory.createNumeric("type", Integer.class); protected Embeddable1 embedded; protected String name; + protected int type; - protected Object entity1; public void setEmbedded(Embeddable1 embedded) { beforePropertyWrite("embedded", this.embedded, embedded); @@ -53,12 +52,14 @@ public abstract class _EmbedEntity2 extends BaseDataObject { return this.name; } - public void setEntity1(EmbedEntity1 entity1) { - setToOneTarget("entity1", entity1, true); + public void setType(int type) { + beforePropertyWrite("type", this.type, type); + this.type = type; } - public EmbedEntity1 getEntity1() { - return (EmbedEntity1)readProperty("entity1"); + public int getType() { + beforePropertyRead("type"); + return this.type; } @Override @@ -72,8 +73,8 @@ public abstract class _EmbedEntity2 extends BaseDataObject { return this.embedded; case "name": return this.name; - case "entity1": - return this.entity1; + case "type": + return this.type; default: return super.readPropertyDirectly(propName); } @@ -92,8 +93,8 @@ public abstract class _EmbedEntity2 extends BaseDataObject { case "name": this.name = (String)val; break; - case "entity1": - this.entity1 = val; + case "type": + this.type = val == null ? 0 : (int)val; break; default: super.writePropertyDirectly(propName, val); @@ -113,7 +114,7 @@ public abstract class _EmbedEntity2 extends BaseDataObject { super.writeState(out); out.writeObject(this.embedded); out.writeObject(this.name); - out.writeObject(this.entity1); + out.writeInt(this.type); } @Override @@ -121,7 +122,7 @@ public abstract class _EmbedEntity2 extends BaseDataObject { super.readState(in); this.embedded = (Embeddable1)in.readObject(); this.name = (String)in.readObject(); - this.entity1 = in.readObject(); + this.type = in.readInt(); } } diff --git a/cayenne-server/src/test/resources/embeddable.map.xml b/cayenne-server/src/test/resources/embeddable.map.xml index dbcae05..c01ed73 100644 --- a/cayenne-server/src/test/resources/embeddable.map.xml +++ b/cayenne-server/src/test/resources/embeddable.map.xml @@ -8,6 +8,10 @@ <embeddable-attribute name="embedded20" type="java.lang.String" db-attribute-name="EMBEDDED20"/> <embeddable-attribute name="embedded10" type="java.lang.String" db-attribute-name="EMBEDDED10"/> </embeddable> + <db-entity name="EMBED_CHILD"> + <db-attribute name="CHILD_ATTR" type="VARCHAR" length="100"/> + <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/> + </db-entity> <db-entity name="EMBED_ENTITY1"> <db-attribute name="EMBEDDED10" type="VARCHAR" length="100"/> <db-attribute name="EMBEDDED20" type="VARCHAR" length="100"/> @@ -23,6 +27,17 @@ <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/> <db-attribute name="NAME" type="VARCHAR" length="100"/> </db-entity> + <db-entity name="EMBED_ROOT"> + <db-attribute name="EMBEDDED10" type="VARCHAR" length="100"/> + <db-attribute name="EMBEDDED20" type="VARCHAR" length="100"/> + <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/> + <db-attribute name="NAME" type="VARCHAR" isMandatory="true" length="100"/> + <db-attribute name="TYPE" type="INTEGER" isMandatory="true"/> + </db-entity> + <obj-entity name="EmbedChild" superEntityName="EmbedRoot" className="org.apache.cayenne.testdo.embeddable.EmbedChild"> + <qualifier><![CDATA[type = 1]]></qualifier> + <obj-attribute name="childAttr" type="java.lang.String" db-attribute-path="child.CHILD_ATTR"/> + </obj-entity> <obj-entity name="EmbedEntity1" className="org.apache.cayenne.testdo.embeddable.EmbedEntity1" dbEntityName="EMBED_ENTITY1"> <embedded-attribute name="embedded1" type="org.apache.cayenne.testdo.embeddable.Embeddable1"/> <embedded-attribute name="embedded2" type="org.apache.cayenne.testdo.embeddable.Embeddable1"> @@ -35,12 +50,24 @@ <embedded-attribute name="embedded" type="org.apache.cayenne.testdo.embeddable.Embeddable1"/> <obj-attribute name="name" type="java.lang.String" db-attribute-path="NAME"/> </obj-entity> + <obj-entity name="EmbedRoot" className="org.apache.cayenne.testdo.embeddable.EmbedRoot" dbEntityName="EMBED_ROOT"> + <qualifier><![CDATA[type = 0]]></qualifier> + <embedded-attribute name="embedded" type="org.apache.cayenne.testdo.embeddable.Embeddable1"/> + <obj-attribute name="name" type="java.lang.String" db-attribute-path="NAME"/> + <obj-attribute name="type" type="int" db-attribute-path="TYPE"/> + </obj-entity> + <db-relationship name="root" source="EMBED_CHILD" target="EMBED_ROOT"> + <db-attribute-pair source="ID" target="ID"/> + </db-relationship> <db-relationship name="untitledRel" source="EMBED_ENTITY1" target="EMBED_ENTITY2" toMany="true"> <db-attribute-pair source="ID" target="ENTITY1_ID"/> </db-relationship> <db-relationship name="untitledRel" source="EMBED_ENTITY2" target="EMBED_ENTITY1"> <db-attribute-pair source="ENTITY1_ID" target="ID"/> </db-relationship> + <db-relationship name="child" source="EMBED_ROOT" target="EMBED_CHILD" toDependentPK="true"> + <db-attribute-pair source="ID" target="ID"/> + </db-relationship> <obj-relationship name="embedEntity2s" source="EmbedEntity1" target="EmbedEntity2" deleteRule="Deny" db-relationship-path="untitledRel"/> <obj-relationship name="entity1" source="EmbedEntity2" target="EmbedEntity1" deleteRule="Nullify" db-relationship-path="untitledRel"/> </data-map>