Skip to content

Commit 855e560

Browse files
authored
Merge pull request #645 from swarmbox/CAY-2968
CAY-2968 Vertical Inheritance: INSERT instead of UPDATE after updatin…
2 parents 3505d42 + 3268a0c commit 855e560

5 files changed

Lines changed: 119 additions & 4 deletions

File tree

RELEASE-NOTES.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ CAY-2956 Get rid of a dedicated adapter for Oracle 8
1919
CAY-2957 Get rid of adapter for legacy HSQLDB <= 1.8
2020
CAY-2962 Allow unconstrained VARCHAR
2121
CAY-2963 Replace TypesHandler / types.xml with hardcoded map
22-
CAY-2969 Extender API for "soft" delete
22+
CAY-2969 Extender API for "soft" delete
2323
CAY-2970 Tighten deferred value resolution contract on commit
2424
CAY-2971 Remove extra spaces within SQL parenthesis
2525
CAY-2972 Fewer parentheses in generated SQL
@@ -28,7 +28,7 @@ CAY-2975 Mnemonic table aliases in generated SQL
2828

2929
Bug Fixes:
3030

31-
CAY-2892 Modeler: Exception when deleting a callback
31+
CAY-2892 Modeler: Exception when deleting a callback
3232
CAY-2958 Empty join is saved
3333
CAY-2959 Modeler: DbRelationship dialog "Cancel" doesn't cancel
3434
CAY-2960 Undoing renamed relationship change throws
@@ -38,6 +38,7 @@ CAY-2965 MCP Cgen should not fail on an absent "<cgen>" tag
3838
CAY-2966 "comment" field is lost when upgrading from v10 to v12
3939
CAY-2967 SQLTemplate/SQLSelect broken pagination
4040
CAY-2973 Exception trying to copy/paste a callback
41+
CAY-2968 Vertical Inheritance: INSERT instead of UPDATE after updating flattened attribute
4142

4243
----------------------------------
4344
Release: 5.0-M2

cayenne/src/main/java/org/apache/cayenne/access/DataContextSnapshotBuilder.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
import org.apache.cayenne.CayenneRuntimeException;
2323
import org.apache.cayenne.DataRow;
2424
import org.apache.cayenne.Fault;
25+
import org.apache.cayenne.ObjectId;
2526
import org.apache.cayenne.PersistenceState;
2627
import org.apache.cayenne.Persistent;
28+
import org.apache.cayenne.exp.path.CayennePath;
2729
import org.apache.cayenne.map.DbJoin;
2830
import org.apache.cayenne.map.DbRelationship;
2931
import org.apache.cayenne.map.EntityResolver;
@@ -80,6 +82,16 @@ public DataRow build() {
8082
}
8183
}
8284

85+
// Ensure primary keys of additional entities are also included in the snapshot
86+
for (CayennePath path : descriptor.getAdditionalDbEntities().keySet()) {
87+
ObjectId flattenedId = objectStore.getFlattenedId(object.getObjectId(), path);
88+
if (flattenedId != null) {
89+
for (Map.Entry<String, Object> idEntry : flattenedId.getIdSnapshot().entrySet()) {
90+
snapshot.putIfAbsent(path.dot(idEntry.getKey()).value(), idEntry.getValue());
91+
}
92+
}
93+
}
94+
8395
return snapshot;
8496
}
8597

cayenne/src/main/java/org/apache/cayenne/access/ObjectResolver.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,7 @@ private void resolveAdditionalIds(DataRow row, Persistent object, ClassDescripto
186186
for(Map.Entry<CayennePath, AdditionalDbEntityDescriptor> entry : classDescriptor.getAdditionalDbEntities().entrySet()) {
187187
DbEntity dbEntity = entry.getValue().getDbEntity();
188188
CayennePath path = entry.getKey();
189-
CayennePath prefix = path.length() == 1 ? path : path.tail(path.length() - 1);
190-
ObjectId objectId = createObjectId(row, "db:" + dbEntity.getName(), dbEntity.getPrimaryKeys(), prefix, false);
189+
ObjectId objectId = createObjectId(row, "db:" + dbEntity.getName(), dbEntity.getPrimaryKeys(), path, false);
191190
if(objectId != null) {
192191
context.getObjectStore().markFlattenedPath(object.getObjectId(), path, objectId);
193192
}

cayenne/src/main/java/org/apache/cayenne/access/PrefetchProcessorJointNode.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,14 @@ private boolean visitRelationship(ArcProperty arc) {
228228
appendColumn(targetSource, pkName, prefix + pkName);
229229
}
230230

231+
// append id columns of additional entities...
232+
descriptor.getAdditionalDbEntities().forEach((additionalPath, additionalDescriptor) -> {
233+
for (DbAttribute pk : additionalDescriptor.getDbEntity().getPrimaryKeys()) {
234+
String name = additionalPath.dot(pk.getName()).value();
235+
appendColumn(targetSource, name, prefix + name);
236+
}
237+
});
238+
231239
// append inheritance discriminator columns...
232240
for (ObjAttribute column : descriptor.getDiscriminatorColumns()) {
233241
CayennePath target = column.getDbAttributePath();

cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.apache.cayenne.query.ColumnSelect;
2727
import org.apache.cayenne.query.EJBQLQuery;
2828
import org.apache.cayenne.query.ObjectSelect;
29+
import org.apache.cayenne.query.PrefetchTreeNode;
2930
import org.apache.cayenne.query.SelectById;
3031
import org.apache.cayenne.runtime.CayenneRuntime;
3132
import org.apache.cayenne.test.jdbc.TableHelper;
@@ -1194,4 +1195,98 @@ public void insertTwoGenericVerticalInheritanceObjects() {
11941195
final List<Persistent> boys = ObjectSelect.query(Persistent.class, "GenBoy").select(env.context());
11951196
assertEquals(1, boys.size());
11961197
}
1198+
1199+
private void updateFlattenedAttributeOfPrefetchedInheritedChild(int prefetchSemantics) throws SQLException {
1200+
TableHelper ivOtherTable = env.table("IV_OTHER", "ID");
1201+
TableHelper ivBaseTable = env.table("IV_BASE", "ID", "NAME", "TYPE");
1202+
TableHelper ivImplTable = env.table("IV_IMPL", "ID", "ATTR1", "OTHER3_ID");
1203+
1204+
ivOtherTable.insert(1);
1205+
ivBaseTable.insert(1, "name", "I");
1206+
ivImplTable.insert(1, "attr1", 1);
1207+
1208+
IvOther other = ObjectSelect.query(IvOther.class)
1209+
.prefetch(IvOther.IMPLS_WITH_INVERSE.getName(), prefetchSemantics)
1210+
.selectOne(env.context());
1211+
1212+
IvImpl impl = other.getImplsWithInverse().getFirst();
1213+
assertEquals("attr1", impl.getAttr1());
1214+
1215+
impl.setAttr1("attr1-updated");
1216+
env.context().commitChanges();
1217+
1218+
assertEquals(1, ivImplTable.getRowCount());
1219+
ObjectContext cleanContext = runtime.newContext();
1220+
IvImpl reread = SelectById.queryId(IvImpl.class, 1).selectOne(cleanContext);
1221+
assertEquals("attr1-updated", reread.getAttr1());
1222+
}
1223+
1224+
@Test
1225+
public void updateFlattenedAttributeOfInheritedChildJointPrefetch() throws SQLException {
1226+
updateFlattenedAttributeOfPrefetchedInheritedChild(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
1227+
}
1228+
1229+
@Test
1230+
public void updateFlattenedAttributeOfInheritedChildDisjointPrefetch() throws SQLException {
1231+
updateFlattenedAttributeOfPrefetchedInheritedChild(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
1232+
}
1233+
1234+
@Test
1235+
public void updateFlattenedAttributeOfInheritedChildDisjointByIdPrefetch() throws SQLException {
1236+
updateFlattenedAttributeOfPrefetchedInheritedChild(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
1237+
}
1238+
1239+
@Test
1240+
public void updateFlattenedAttributeOfInheritedChildRepeatedSaveResolvedFromCachedSnapshot() throws SQLException {
1241+
TableHelper ivBaseTable = env.table("IV_BASE", "ID", "NAME", "TYPE");
1242+
TableHelper ivImplTable = env.table("IV_IMPL", "ID", "ATTR1");
1243+
1244+
ivBaseTable.insert(1, "name", "I");
1245+
ivImplTable.insert(1, "attr1");
1246+
1247+
// First request: loads the child fresh (cold cache)
1248+
// This rebuilds the shared cached snapshot for the child.
1249+
{
1250+
ObjectContext freshContext = runtime.newContext();
1251+
IvImpl impl = Cayenne.objectForPK(freshContext, IvImpl.class, 1);
1252+
impl.setAttr1("attr1-first");
1253+
freshContext.commitChanges();
1254+
}
1255+
1256+
// Second request: (new context, shared snapshot cache)
1257+
// objectForPK is served from the cached snapshot
1258+
{
1259+
ObjectContext freshContext = runtime.newContext();
1260+
IvImpl impl = Cayenne.objectForPK(freshContext, IvImpl.class, 1);
1261+
impl.setAttr1("attr1-second");
1262+
freshContext.commitChanges();
1263+
}
1264+
1265+
assertEquals(1, ivImplTable.getRowCount());
1266+
ObjectContext cleanContext = runtime.newContext();
1267+
IvImpl reread = SelectById.queryId(IvImpl.class, 1).selectOne(cleanContext);
1268+
assertEquals("attr1-second", reread.getAttr1());
1269+
}
1270+
1271+
@Test
1272+
public void updateFlattenedAttributeOfThreeLevelInheritanceChild() throws SQLException {
1273+
TableHelper ivRootTable = env.table("IV_ROOT", "ID", "DISCRIMINATOR");
1274+
TableHelper ivSub1Table = env.table("IV_SUB1", "ID");
1275+
TableHelper ivSub1Sub1Table = env.table("IV_SUB1_SUB1", "ID", "SUB1_SUB1_NAME");
1276+
1277+
ivRootTable.insert(1, "IvSub1Sub1");
1278+
ivSub1Table.insert(1);
1279+
ivSub1Sub1Table.insert(1, "sub1sub1name");
1280+
1281+
IvSub1Sub1 sub1Sub1 = SelectById.queryId(IvSub1Sub1.class, 1).selectOne(env.context());
1282+
assertEquals("sub1sub1name", sub1Sub1.getSub1Sub1Name());
1283+
1284+
sub1Sub1.setSub1Sub1Name("sub1sub1name-updated");
1285+
env.context().commitChanges();
1286+
1287+
assertEquals(1, ivSub1Sub1Table.getRowCount());
1288+
ObjectContext cleanContext = runtime.newContext();
1289+
IvSub1Sub1 reread = SelectById.queryId(IvSub1Sub1.class, 1).selectOne(cleanContext);
1290+
assertEquals("sub1sub1name-updated", reread.getSub1Sub1Name());
1291+
}
11971292
}

0 commit comments

Comments
 (0)