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 b625eaea1 CAY-2855 Vertical Inheritance: Fix Disjoint By Id Prefetch
On Sub-Entity
b625eaea1 is described below
commit b625eaea19417f9ed7787450a253712e969bed3c
Author: Nikita Timofeev <[email protected]>
AuthorDate: Mon Jun 10 16:36:01 2024 +0400
CAY-2855 Vertical Inheritance: Fix Disjoint By Id Prefetch On Sub-Entity
---
.../cayenne/access/HierarchicalObjectResolver.java | 59 +++++---
...tyWithMeaningfulPKAndCustomDbRowOpSorterIT.java | 12 +-
.../apache/cayenne/access/GraphSorterModule.java | 35 +++++
.../cayenne/access/VerticalInheritanceIT.java | 156 ++++++++++++++++++++-
.../testdo/inheritance_vertical/auto/_IvImpl.java | 24 ++++
.../testdo/inheritance_vertical/auto/_IvOther.java | 17 +++
.../test/resources/inheritance-vertical.map.xml | 9 ++
7 files changed, 284 insertions(+), 28 deletions(-)
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
b/cayenne/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
index b87baf6ba..c57c4ba11 100644
---
a/cayenne/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
+++
b/cayenne/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
@@ -33,8 +33,10 @@ import org.apache.cayenne.query.PrefetchSelectQuery;
import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.query.QueryMetadata;
import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.util.SingleEntryMap;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -117,21 +119,12 @@ class HierarchicalObjectResolver {
ObjRelationship relationship =
processorNode.getIncoming().getRelationship();
List<DbRelationship> dbRelationships =
relationship.getDbRelationships();
- DbRelationship lastDbRelationship = dbRelationships.get(0);
-
- String pathPrefix = "";
+ CayennePath pathPrefix = CayennePath.EMPTY_PATH;
if (dbRelationships.size() > 1) {
// we need path prefix for flattened relationships
- StringBuilder buffer = new StringBuilder();
for (int i = dbRelationships.size() - 1; i >= 1; i--) {
- if (buffer.length() > 0) {
- buffer.append(".");
- }
-
-
buffer.append(dbRelationships.get(i).getReverseRelationship().getName());
+ pathPrefix =
pathPrefix.dot(dbRelationships.get(i).getReverseRelationship().getName());
}
-
- pathPrefix = buffer.append(".").toString();
}
List<DataRow> parentDataRows;
@@ -145,7 +138,8 @@ class HierarchicalObjectResolver {
}
int maxIdQualifierSize =
context.getParentDataDomain().getMaxIdQualifierSize();
- List<DbJoin> joins = lastDbRelationship.getJoins();
+ List<DbJoin> joins = getDbJoins(relationship);
+ Map<DbJoin, String> joinToDataRowKey = getDataRowKeys(joins,
pathPrefix);
List<PrefetchSelectQuery<DataRow>> queries = new ArrayList<>();
PrefetchSelectQuery<DataRow> currentQuery = null;
@@ -168,7 +162,8 @@ class HierarchicalObjectResolver {
List<Object> joinValues = new ArrayList<>(joins.size());
for (DbJoin join : joins) {
- Object targetValue = dataRow.get(join.getSourceName());
+ String dataRowKey = joinToDataRowKey.get(join);
+ Object targetValue = dataRow.get(dataRowKey);
joinValues.add(targetValue);
}
@@ -205,7 +200,39 @@ class HierarchicalObjectResolver {
return true;
}
- private void createDisjointByIdPrefetchQualifier(String pathPrefix,
PrefetchSelectQuery<?> currentQuery,
+ private List<DbJoin> getDbJoins(ObjRelationship relationship) {
+ // we get the part of the relationship path that contains FK
+ List<DbRelationship> dbRelationships =
relationship.getDbRelationships();
+ if(relationship.isToMany() || !relationship.isToPK()) {
+ return dbRelationships.get(0).getJoins();
+ } else {
+ return dbRelationships.get(dbRelationships.size() -
1).getJoins();
+ }
+ }
+
+ /**
+ * precalculate join to a DataRow key
+ * @param joins to get key from
+ * @param pathPrefix for the flattened path
+ * @return mapping of DbJoin to DataRow key to use
+ */
+ private Map<DbJoin, String> getDataRowKeys(List<DbJoin> joins,
CayennePath pathPrefix) {
+ Map<DbJoin, String> joinToDataRowKey = joins.size() == 1
+ ? new SingleEntryMap<>(joins.get(0))
+ : new HashMap<>(joins.size());
+ for (DbJoin join : joins) {
+ String dataRowKey;
+ if(join.getRelationship().isToMany() ||
!join.getRelationship().isToPK()) {
+ dataRowKey = join.getSourceName();
+ } else {
+ dataRowKey = pathPrefix.dot(join.getSourceName()).value();
+ }
+ joinToDataRowKey.put(join, dataRowKey);
+ }
+ return joinToDataRowKey;
+ }
+
+ private void createDisjointByIdPrefetchQualifier(CayennePath
pathPrefix, PrefetchSelectQuery<?> currentQuery,
List<DbJoin> joins,
Set<List<Object>> values) {
if (currentQuery == null) return;
@@ -214,7 +241,7 @@ class HierarchicalObjectResolver {
// Results in SQL: ... targetField IN ( ?, ?, ?, .... )
if (joins.size() == 1 && values.size() > 1) {
currentQuery.and( ExpressionFactory.inDbExp(
- pathPrefix + joins.get(0).getTargetName(),
+ pathPrefix.dot(joins.get(0).getTargetName()).value(),
values.stream().flatMap(List::stream).collect(Collectors.toSet())
));
} else { // Handle a single value or compound prefetch ID
predicates
@@ -226,7 +253,7 @@ class HierarchicalObjectResolver {
allJoinsQualifier = null;
for(int j=0; j<joins.size(); j++) {
Expression joinQualifier = ExpressionFactory
- .matchDbExp(pathPrefix +
joins.get(j).getTargetName(), joinValues.get(j));
+
.matchDbExp(pathPrefix.dot(joins.get(j).getTargetName()).value(),
joinValues.get(j));
if (allJoinsQualifier == null) {
allJoinsQualifier = joinQualifier;
} else {
diff --git
a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKAndCustomDbRowOpSorterIT.java
b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKAndCustomDbRowOpSorterIT.java
index 519144a23..09bfeaf34 100644
---
a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKAndCustomDbRowOpSorterIT.java
+++
b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKAndCustomDbRowOpSorterIT.java
@@ -19,11 +19,7 @@
package org.apache.cayenne.access;
-import org.apache.cayenne.access.flush.operation.DbRowOpSorter;
-import org.apache.cayenne.access.flush.operation.GraphBasedDbRowOpSorter;
-import org.apache.cayenne.di.Binder;
import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.di.Module;
import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPKDep;
import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPKTest1;
import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPk;
@@ -34,7 +30,7 @@ import org.apache.cayenne.unit.di.runtime.UseCayenneRuntime;
import org.junit.Test;
@UseCayenneRuntime(CayenneProjects.MEANINGFUL_PK_PROJECT)
-@ExtraModules(DataContextEntityWithMeaningfulPKAndCustomDbRowOpSorterIT.GraphSorterModule.class)
+@ExtraModules(GraphSorterModule.class)
public class DataContextEntityWithMeaningfulPKAndCustomDbRowOpSorterIT extends
RuntimeCase {
@Inject
@@ -80,10 +76,4 @@ public class
DataContextEntityWithMeaningfulPKAndCustomDbRowOpSorterIT extends R
context.commitChanges();
}
- public static class GraphSorterModule implements Module {
- @Override
- public void configure(Binder binder) {
- binder.bind(DbRowOpSorter.class).to(GraphBasedDbRowOpSorter.class);
- }
- }
}
diff --git
a/cayenne/src/test/java/org/apache/cayenne/access/GraphSorterModule.java
b/cayenne/src/test/java/org/apache/cayenne/access/GraphSorterModule.java
new file mode 100644
index 000000000..d833d590b
--- /dev/null
+++ b/cayenne/src/test/java/org/apache/cayenne/access/GraphSorterModule.java
@@ -0,0 +1,35 @@
+/*****************************************************************
+ * 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.access;
+
+import org.apache.cayenne.access.flush.operation.DbRowOpSorter;
+import org.apache.cayenne.access.flush.operation.GraphBasedDbRowOpSorter;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.Module;
+
+/**
+ * Test module that sets up {@link GraphBasedDbRowOpSorter} instead of default
one
+ */
+public class GraphSorterModule implements Module {
+ @Override
+ public void configure(Binder binder) {
+ binder.bind(DbRowOpSorter.class).to(GraphBasedDbRowOpSorter.class);
+ }
+}
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 1fd162bbb..6257e6bdb 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
@@ -30,6 +30,7 @@ import org.apache.cayenne.test.jdbc.DBHelper;
import org.apache.cayenne.test.jdbc.TableHelper;
import org.apache.cayenne.testdo.inheritance_vertical.*;
import org.apache.cayenne.unit.di.runtime.CayenneProjects;
+import org.apache.cayenne.unit.di.runtime.ExtraModules;
import org.apache.cayenne.unit.di.runtime.RuntimeCase;
import org.apache.cayenne.unit.di.runtime.UseCayenneRuntime;
import org.junit.After;
@@ -47,6 +48,8 @@ import java.util.Map;
import static org.junit.Assert.*;
@UseCayenneRuntime(CayenneProjects.INHERITANCE_VERTICAL_PROJECT)
+// Default sorter fails to properly sort all the relationships in the test
schema used
+@ExtraModules(GraphSorterModule.class)
public class VerticalInheritanceIT extends RuntimeCase {
@Inject
@@ -865,7 +868,7 @@ public class VerticalInheritanceIT extends RuntimeCase {
* @link https://issues.apache.org/jira/browse/CAY-2840
*/
@Test
- public void testJointPrefetchBelongsTo() throws SQLException {
+ public void testBaseJointPrefetchBelongsTo() throws SQLException {
TableHelper ivOtherTable = new TableHelper(dbHelper,
"IV_OTHER");
ivOtherTable.setColumns("ID", "NAME",
"BASE_ID").setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.INTEGER);
@@ -891,6 +894,157 @@ public class VerticalInheritanceIT extends RuntimeCase {
assertEquals("attr1", impl.getAttr1());
}
+ /**
+ * @link https://issues.apache.org/jira/browse/CAY-2840
+ */
+ @Test
+ public void testBaseDisjointPrefetchBelongsTo() 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.disjoint()).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-2840
+ */
+ @Test
+ public void testBaseDisjointByIdPrefetchBelongsTo() 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.disjointById()).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-2855
+ */
+ @Test
+ public void testImplJointPrefetchBelongsTo() throws SQLException {
+ TableHelper ivOtherTable = new TableHelper(dbHelper,
"IV_OTHER");
+ ivOtherTable.setColumns("ID", "NAME",
"IMPL_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.IMPL.joint()).limit(1).selectOne(context);
+ assertNotNull(other);
+
+ IvImpl impl = other.getImpl();
+ assertNotNull(other.getImpl());
+ // 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-2855
+ */
+ @Test
+ public void testImplDisjointPrefetchBelongsTo() throws SQLException {
+ TableHelper ivOtherTable = new TableHelper(dbHelper,
"IV_OTHER");
+ ivOtherTable.setColumns("ID", "NAME",
"IMPL_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.IMPL.disjoint())
+ .selectOne(context);
+ assertNotNull(other);
+
+ IvImpl impl = other.getImpl();
+ assertNotNull(other.getImpl());
+ // 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-2855
+ */
+ @Test
+ public void testImplDisjointByIdPrefetchBelongsTo() throws SQLException
{
+ TableHelper ivOtherTable = new TableHelper(dbHelper,
"IV_OTHER");
+ ivOtherTable.setColumns("ID", "NAME",
"IMPL_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.IMPL.disjointById())
+ .selectOne(context);
+ assertNotNull(other);
+
+ IvImpl impl = other.getImpl();
+ assertNotNull(other.getImpl());
+ // 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/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImpl.java
b/cayenne/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImpl.java
index f7b998530..4f16d7fb3 100644
---
a/cayenne/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImpl.java
+++
b/cayenne/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImpl.java
@@ -4,9 +4,11 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
+import java.util.List;
import org.apache.cayenne.exp.property.DateProperty;
import org.apache.cayenne.exp.property.EntityProperty;
+import org.apache.cayenne.exp.property.ListProperty;
import org.apache.cayenne.exp.property.NumericIdProperty;
import org.apache.cayenne.exp.property.PropertyFactory;
import org.apache.cayenne.exp.property.SelfProperty;
@@ -33,6 +35,7 @@ public abstract class _IvImpl extends IvBase {
public static final DateProperty<Date> ATTR0 =
PropertyFactory.createDate("attr0", Date.class);
public static final StringProperty<String> ATTR1 =
PropertyFactory.createString("attr1", String.class);
public static final StringProperty<String> ATTR2 =
PropertyFactory.createString("attr2", String.class);
+ public static final ListProperty<IvOther> IMPL_OTHERS =
PropertyFactory.createList("implOthers", IvOther.class);
public static final EntityProperty<IvOther> OTHER1 =
PropertyFactory.createEntity("other1", IvOther.class);
public static final EntityProperty<IvOther> OTHER2 =
PropertyFactory.createEntity("other2", IvOther.class);
public static final EntityProperty<IvOther> OTHER3 =
PropertyFactory.createEntity("other3", IvOther.class);
@@ -41,6 +44,7 @@ public abstract class _IvImpl extends IvBase {
protected String attr1;
protected String attr2;
+ protected Object implOthers;
protected Object other1;
protected Object other2;
protected Object other3;
@@ -75,6 +79,19 @@ public abstract class _IvImpl extends IvBase {
return this.attr2;
}
+ public void addToImplOthers(IvOther obj) {
+ addToManyTarget("implOthers", obj, true);
+ }
+
+ public void removeFromImplOthers(IvOther obj) {
+ removeToManyTarget("implOthers", obj, true);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<IvOther> getImplOthers() {
+ return (List<IvOther>)readProperty("implOthers");
+ }
+
public void setOther1(IvOther other1) {
setToOneTarget("other1", other1, true);
}
@@ -112,6 +129,8 @@ public abstract class _IvImpl extends IvBase {
return this.attr1;
case "attr2":
return this.attr2;
+ case "implOthers":
+ return this.implOthers;
case "other1":
return this.other1;
case "other2":
@@ -139,6 +158,9 @@ public abstract class _IvImpl extends IvBase {
case "attr2":
this.attr2 = (String)val;
break;
+ case "implOthers":
+ this.implOthers = val;
+ break;
case "other1":
this.other1 = val;
break;
@@ -167,6 +189,7 @@ public abstract class _IvImpl extends IvBase {
out.writeObject(this.attr0);
out.writeObject(this.attr1);
out.writeObject(this.attr2);
+ out.writeObject(this.implOthers);
out.writeObject(this.other1);
out.writeObject(this.other2);
out.writeObject(this.other3);
@@ -178,6 +201,7 @@ public abstract class _IvImpl extends IvBase {
this.attr0 = (Date)in.readObject();
this.attr1 = (String)in.readObject();
this.attr2 = (String)in.readObject();
+ this.implOthers = in.readObject();
this.other1 = in.readObject();
this.other2 = in.readObject();
this.other3 = in.readObject();
diff --git
a/cayenne/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java
b/cayenne/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java
index fc18cbc55..655a3b3bf 100644
---
a/cayenne/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java
+++
b/cayenne/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java
@@ -34,6 +34,7 @@ public abstract class _IvOther extends PersistentObject {
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 EntityProperty<IvImpl> IMPL =
PropertyFactory.createEntity("impl", IvImpl.class);
public static final ListProperty<IvImpl> IMPLS =
PropertyFactory.createList("impls", IvImpl.class);
public static final ListProperty<IvImpl> IMPLS_WITH_INVERSE =
PropertyFactory.createList("implsWithInverse", IvImpl.class);
public static final ListProperty<IvImplWithLock> IMPLS_WITH_LOCK =
PropertyFactory.createList("implsWithLock", IvImplWithLock.class);
@@ -41,6 +42,7 @@ public abstract class _IvOther extends PersistentObject {
protected String name;
protected Object base;
+ protected Object impl;
protected Object impls;
protected Object implsWithInverse;
protected Object implsWithLock;
@@ -63,6 +65,14 @@ public abstract class _IvOther extends PersistentObject {
return (IvBase)readProperty("base");
}
+ public void setImpl(IvImpl impl) {
+ setToOneTarget("impl", impl, true);
+ }
+
+ public IvImpl getImpl() {
+ return (IvImpl)readProperty("impl");
+ }
+
public void addToImpls(IvImpl obj) {
addToManyTarget("impls", obj, true);
}
@@ -113,6 +123,8 @@ public abstract class _IvOther extends PersistentObject {
return this.name;
case "base":
return this.base;
+ case "impl":
+ return this.impl;
case "impls":
return this.impls;
case "implsWithInverse":
@@ -137,6 +149,9 @@ public abstract class _IvOther extends PersistentObject {
case "base":
this.base = val;
break;
+ case "impl":
+ this.impl = val;
+ break;
case "impls":
this.impls = val;
break;
@@ -164,6 +179,7 @@ public abstract class _IvOther extends PersistentObject {
super.writeState(out);
out.writeObject(this.name);
out.writeObject(this.base);
+ out.writeObject(this.impl);
out.writeObject(this.impls);
out.writeObject(this.implsWithInverse);
out.writeObject(this.implsWithLock);
@@ -174,6 +190,7 @@ public abstract class _IvOther extends PersistentObject {
super.readState(in);
this.name = (String)in.readObject();
this.base = in.readObject();
+ this.impl = in.readObject();
this.impls = in.readObject();
this.implsWithInverse = in.readObject();
this.implsWithLock = in.readObject();
diff --git a/cayenne/src/test/resources/inheritance-vertical.map.xml
b/cayenne/src/test/resources/inheritance-vertical.map.xml
index 368ef9ee8..142f7ee13 100644
--- a/cayenne/src/test/resources/inheritance-vertical.map.xml
+++ b/cayenne/src/test/resources/inheritance-vertical.map.xml
@@ -70,6 +70,7 @@
<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="IMPL_ID" type="INTEGER"/>
<db-attribute name="NAME" type="VARCHAR" length="100"/>
</db-entity>
<db-entity name="IV_ROOT">
@@ -238,6 +239,9 @@
<db-relationship name="other3" source="IV_IMPL" target="IV_OTHER">
<db-attribute-pair source="OTHER3_ID" target="ID"/>
</db-relationship>
+ <db-relationship name="others" source="IV_IMPL" target="IV_OTHER"
toMany="true">
+ <db-attribute-pair source="ID" target="IMPL_ID"/>
+ </db-relationship>
<db-relationship name="base" source="IV_IMPL_WITH_LOCK"
target="IV_BASE_WITH_LOCK">
<db-attribute-pair source="ID" target="ID"/>
</db-relationship>
@@ -247,6 +251,9 @@
<db-relationship name="base" source="IV_OTHER" target="IV_BASE">
<db-attribute-pair source="BASE_ID" target="ID"/>
</db-relationship>
+ <db-relationship name="impl" source="IV_OTHER" target="IV_IMPL">
+ <db-attribute-pair source="IMPL_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>
@@ -289,11 +296,13 @@
<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="relatedAbstract" source="IvConcrete"
target="IvAbstract" deleteRule="Nullify"
db-relationship-path="concrete.relatedAbstract"/>
+ <obj-relationship name="implOthers" source="IvImpl" target="IvOther"
deleteRule="Deny" db-relationship-path="impl.others"/>
<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="other3" source="IvImpl" target="IvOther"
deleteRule="Nullify" db-relationship-path="impl.other3"/>
<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="impl" source="IvOther" target="IvImpl"
deleteRule="Nullify" db-relationship-path="impl.base"/>
<obj-relationship name="impls" source="IvOther" target="IvImpl"
deleteRule="Deny" db-relationship-path="impls.base"/>
<obj-relationship name="implsWithInverse" source="IvOther"
target="IvImpl" deleteRule="Deny" db-relationship-path="implsWithInverse.base"/>
<obj-relationship name="implsWithLock" source="IvOther"
target="IvImplWithLock" deleteRule="Deny" db-relationship-path="impls.base"/>