This is an automated email from the ASF dual-hosted git repository. ntimofeev pushed a commit to branch STABLE-4.2 in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/STABLE-4.2 by this push: new 105a8e2bc CAY-2840 Vertical Inheritance: Missing subclass attributes with joint prefetch 105a8e2bc is described below commit 105a8e2bc9b60a5b0db37191dc70a4a898d58120 Author: Jadon Hansell <130694311+jghans...@users.noreply.github.com> AuthorDate: Mon Feb 5 22:58:28 2024 +0400 CAY-2840 Vertical Inheritance: Missing subclass attributes with joint prefetch (cherry picked from commit 2358f52a9c7728751ddc5aea68f973a84f1c8f35) --- RELEASE-NOTES.txt | 1 + .../select/DescriptorColumnExtractor.java | 6 ++++- .../cayenne/access/VerticalInheritanceIT.java | 30 ++++++++++++++++++++++ .../testdo/inheritance_vertical/auto/_IvBase.java | 25 ++++++++++++++++++ .../testdo/inheritance_vertical/auto/_IvOther.java | 19 ++++++++++++++ .../test/resources/inheritance-vertical.map.xml | 9 +++++++ 6 files changed, 89 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index ab27b4664..cf9643a37 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -20,6 +20,7 @@ CAY-2809 Cayenne Expression grammar doesn't allow custom function as an argument CAY-2810 Can't use custom operator expression with aggregate functions CAY-2813 Regression: Constants.CI_PROPERTY flag is no longer working for MySQL CAY-2815 Incorrect translation of aliased expression +CAY-2840 Vertical Inheritance: Missing subclass attributes with joint prefetch ---------------------------------- Release: 4.2 diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DescriptorColumnExtractor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DescriptorColumnExtractor.java index 1cb72ff1a..ad4ee573f 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DescriptorColumnExtractor.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DescriptorColumnExtractor.java @@ -128,7 +128,11 @@ class DescriptorColumnExtractor extends BaseColumnExtractor implements PropertyV } if (count > 1) { // it was a flattened attribute, so need to keep full path info - String dataRowKey = result.getAttributePaths().get(i) + "." + dbAttribute.getName(); + String attributePath = result.getAttributePaths().get(i); + if (attributePath.startsWith(PREFETCH_PREFIX)) { + attributePath = attributePath.substring(PREFETCH_PREFIX.length()); + } + String dataRowKey = attributePath + "." + dbAttribute.getName(); resultNodeDescriptor.setDataRowKey(dataRowKey); addEntityResultField(dataRowKey); } else { diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java index a0cf075f7..320f22051 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java @@ -682,6 +682,36 @@ public class VerticalInheritanceIT extends ServerCase { assertEquals(2, ObjectSelect.query(IvImpl.class).selectCount(context)); } + /** + * @link https://issues.apache.org/jira/browse/CAY-2840 + */ + @Test + public void testJointPrefetchBelongsTo() throws SQLException { + TableHelper ivOtherTable = new TableHelper(dbHelper, "IV_OTHER"); + ivOtherTable.setColumns("ID", "NAME", "BASE_ID").setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.INTEGER); + + TableHelper ivBaseTable = new TableHelper(dbHelper, "IV_BASE"); + ivBaseTable.setColumns("ID", "NAME", "TYPE").setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.CHAR); + + TableHelper ivImplTable = new TableHelper(dbHelper, "IV_IMPL"); + ivImplTable.setColumns("ID", "ATTR1").setColumnTypes(Types.INTEGER, Types.VARCHAR); + + ivBaseTable.insert(1, "Impl 1", "I"); + ivImplTable.insert(1, "attr1"); + ivOtherTable.insert(1, "other1", 1); + + IvOther other = ObjectSelect.query(IvOther.class).prefetch(IvOther.BASE.joint()).selectOne(context); + assertNotNull(other); + assertNotNull(other.getBase()); + assertTrue(IvImpl.class.isAssignableFrom(other.getBase().getClass())); + + IvImpl impl = (IvImpl)other.getBase(); + // Ensure that base attributes were prefetched correctly + assertEquals("Impl 1", impl.getName()); + // Ensure that subclass attributes were prefetched correctly + assertEquals("attr1", impl.getAttr1()); + } + /** * @link https://issues.apache.org/jira/browse/CAY-2282 */ diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBase.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBase.java index 97c2e182f..dbc850c03 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBase.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBase.java @@ -4,9 +4,12 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.util.List; import org.apache.cayenne.BaseDataObject; +import org.apache.cayenne.exp.property.ListProperty; import org.apache.cayenne.exp.property.PropertyFactory; import org.apache.cayenne.exp.property.StringProperty; +import org.apache.cayenne.testdo.inheritance_vertical.IvOther; /** * Class _IvBase was generated by Cayenne. @@ -22,10 +25,12 @@ public abstract class _IvBase extends BaseDataObject { public static final StringProperty<String> NAME = PropertyFactory.createString("name", String.class); public static final StringProperty<String> TYPE = PropertyFactory.createString("type", String.class); + public static final ListProperty<IvOther> OTHERS = PropertyFactory.createList("others", IvOther.class); protected String name; protected String type; + protected Object others; public void setName(String name) { beforePropertyWrite("name", this.name, name); @@ -47,6 +52,19 @@ public abstract class _IvBase extends BaseDataObject { return this.type; } + public void addToOthers(IvOther obj) { + addToManyTarget("others", obj, true); + } + + public void removeFromOthers(IvOther obj) { + removeToManyTarget("others", obj, true); + } + + @SuppressWarnings("unchecked") + public List<IvOther> getOthers() { + return (List<IvOther>)readProperty("others"); + } + @Override public Object readPropertyDirectly(String propName) { if(propName == null) { @@ -58,6 +76,8 @@ public abstract class _IvBase extends BaseDataObject { return this.name; case "type": return this.type; + case "others": + return this.others; default: return super.readPropertyDirectly(propName); } @@ -76,6 +96,9 @@ public abstract class _IvBase extends BaseDataObject { case "type": this.type = (String)val; break; + case "others": + this.others = val; + break; default: super.writePropertyDirectly(propName, val); } @@ -94,6 +117,7 @@ public abstract class _IvBase extends BaseDataObject { super.writeState(out); out.writeObject(this.name); out.writeObject(this.type); + out.writeObject(this.others); } @Override @@ -101,6 +125,7 @@ public abstract class _IvBase extends BaseDataObject { super.readState(in); this.name = (String)in.readObject(); this.type = (String)in.readObject(); + this.others = in.readObject(); } } diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java index 075309868..1d681139d 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java @@ -6,9 +6,11 @@ import java.io.ObjectOutputStream; import java.util.List; import org.apache.cayenne.BaseDataObject; +import org.apache.cayenne.exp.property.EntityProperty; import org.apache.cayenne.exp.property.ListProperty; import org.apache.cayenne.exp.property.PropertyFactory; import org.apache.cayenne.exp.property.StringProperty; +import org.apache.cayenne.testdo.inheritance_vertical.IvBase; import org.apache.cayenne.testdo.inheritance_vertical.IvImpl; import org.apache.cayenne.testdo.inheritance_vertical.IvImplWithLock; @@ -25,11 +27,13 @@ public abstract class _IvOther extends BaseDataObject { public static final String ID_PK_COLUMN = "ID"; public static final StringProperty<String> NAME = PropertyFactory.createString("name", String.class); + public static final EntityProperty<IvBase> BASE = PropertyFactory.createEntity("base", IvBase.class); public static final ListProperty<IvImpl> IMPLS = PropertyFactory.createList("impls", IvImpl.class); public static final ListProperty<IvImplWithLock> IMPLS_WITH_LOCK = PropertyFactory.createList("implsWithLock", IvImplWithLock.class); protected String name; + protected Object base; protected Object impls; protected Object implsWithLock; @@ -43,6 +47,14 @@ public abstract class _IvOther extends BaseDataObject { return this.name; } + public void setBase(IvBase base) { + setToOneTarget("base", base, true); + } + + public IvBase getBase() { + return (IvBase)readProperty("base"); + } + public void addToImpls(IvImpl obj) { addToManyTarget("impls", obj, true); } @@ -78,6 +90,8 @@ public abstract class _IvOther extends BaseDataObject { switch(propName) { case "name": return this.name; + case "base": + return this.base; case "impls": return this.impls; case "implsWithLock": @@ -97,6 +111,9 @@ public abstract class _IvOther extends BaseDataObject { case "name": this.name = (String)val; break; + case "base": + this.base = val; + break; case "impls": this.impls = val; break; @@ -120,6 +137,7 @@ public abstract class _IvOther extends BaseDataObject { protected void writeState(ObjectOutputStream out) throws IOException { super.writeState(out); out.writeObject(this.name); + out.writeObject(this.base); out.writeObject(this.impls); out.writeObject(this.implsWithLock); } @@ -128,6 +146,7 @@ public abstract class _IvOther extends BaseDataObject { protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException { super.readState(in); this.name = (String)in.readObject(); + this.base = in.readObject(); this.impls = in.readObject(); this.implsWithLock = in.readObject(); } diff --git a/cayenne-server/src/test/resources/inheritance-vertical.map.xml b/cayenne-server/src/test/resources/inheritance-vertical.map.xml index 9c2939727..4fa04258a 100644 --- a/cayenne-server/src/test/resources/inheritance-vertical.map.xml +++ b/cayenne-server/src/test/resources/inheritance-vertical.map.xml @@ -66,6 +66,7 @@ <db-attribute name="OTHER1_ID" type="INTEGER" isMandatory="true"/> </db-entity> <db-entity name="IV_OTHER"> + <db-attribute name="BASE_ID" type="INTEGER"/> <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/> <db-attribute name="NAME" type="VARCHAR" length="100"/> </db-entity> @@ -202,6 +203,9 @@ <db-relationship name="impl" source="IV_BASE" target="IV_IMPL" toDependentPK="true"> <db-attribute-pair source="ID" target="ID"/> </db-relationship> + <db-relationship name="others" source="IV_BASE" target="IV_OTHER" toMany="true"> + <db-attribute-pair source="ID" target="BASE_ID"/> + </db-relationship> <db-relationship name="impl" source="IV_BASE_WITH_LOCK" target="IV_IMPL_WITH_LOCK" toDependentPK="true"> <db-attribute-pair source="ID" target="ID"/> </db-relationship> @@ -229,6 +233,9 @@ <db-relationship name="other1" source="IV_IMPL_WITH_LOCK" target="IV_OTHER"> <db-attribute-pair source="OTHER1_ID" target="ID"/> </db-relationship> + <db-relationship name="base" source="IV_OTHER" target="IV_BASE"> + <db-attribute-pair source="BASE_ID" target="ID"/> + </db-relationship> <db-relationship name="impls" source="IV_OTHER" target="IV_IMPL" toMany="true"> <db-attribute-pair source="ID" target="OTHER_ID"/> </db-relationship> @@ -263,11 +270,13 @@ <db-attribute-pair source="IV_ROOT_ID" target="ID"/> </db-relationship> <obj-relationship name="x" source="Iv2Sub1" target="Iv2X" deleteRule="Nullify" db-relationship-path="sub1.x"/> + <obj-relationship name="others" source="IvBase" target="IvOther" deleteRule="Deny" db-relationship-path="others"/> <obj-relationship name="children" source="IvConcrete" target="IvConcrete" deleteRule="Deny" db-relationship-path="children"/> <obj-relationship name="parent" source="IvConcrete" target="IvConcrete" deleteRule="Nullify" db-relationship-path="parent"/> <obj-relationship name="other1" source="IvImpl" target="IvOther" deleteRule="Nullify" db-relationship-path="impl.other1"/> <obj-relationship name="other2" source="IvImpl" target="IvOther" deleteRule="Nullify" db-relationship-path="impl.other2"/> <obj-relationship name="other1" source="IvImplWithLock" target="IvOther" deleteRule="Nullify" db-relationship-path="impl.other1"/> + <obj-relationship name="base" source="IvOther" target="IvBase" deleteRule="Nullify" db-relationship-path="base"/> <obj-relationship name="impls" source="IvOther" target="IvImpl" deleteRule="Deny" db-relationship-path="impls.base"/> <obj-relationship name="implsWithLock" source="IvOther" target="IvImplWithLock" deleteRule="Deny" db-relationship-path="impls.base"/> <obj-relationship name="ivRoot" source="IvSub3" target="IvRoot" deleteRule="Nullify" db-relationship-path="sub3.ivRoot1"/>