Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ CAY-2964 ClassCastException for non-generated meaningful PKs
CAY-2965 MCP Cgen should not fail on an absent "<cgen>" tag
CAY-2966 "comment" field is lost when upgrading from v10 to v12
CAY-2967 SQLTemplate/SQLSelect broken pagination
CAY-2968 Vertical Inheritance: INSERT instead of UPDATE after updating flattened attribute

----------------------------------
Release: 5.0-M2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.DataRow;
import org.apache.cayenne.Fault;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.PersistenceState;
import org.apache.cayenne.Persistent;
import org.apache.cayenne.exp.path.CayennePath;
import org.apache.cayenne.map.DbJoin;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.EntityResolver;
Expand Down Expand Up @@ -80,6 +82,16 @@ public DataRow build() {
}
}

// Ensure primary keys of additional entities are also included in the snapshot
for (CayennePath path : descriptor.getAdditionalDbEntities().keySet()) {
ObjectId flattenedId = objectStore.getFlattenedId(object.getObjectId(), path);
if (flattenedId != null) {
for (Map.Entry<String, Object> idEntry : flattenedId.getIdSnapshot().entrySet()) {
snapshot.putIfAbsent(path.dot(idEntry.getKey()).value(), idEntry.getValue());
}
}
}

return snapshot;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,7 @@ private void resolveAdditionalIds(DataRow row, Persistent object, ClassDescripto
for(Map.Entry<CayennePath, AdditionalDbEntityDescriptor> entry : classDescriptor.getAdditionalDbEntities().entrySet()) {
DbEntity dbEntity = entry.getValue().getDbEntity();
CayennePath path = entry.getKey();
CayennePath prefix = path.length() == 1 ? path : path.tail(path.length() - 1);
ObjectId objectId = createObjectId(row, "db:" + dbEntity.getName(), dbEntity.getPrimaryKeys(), prefix, false);
ObjectId objectId = createObjectId(row, "db:" + dbEntity.getName(), dbEntity.getPrimaryKeys(), path, false);
if(objectId != null) {
context.getObjectStore().markFlattenedPath(object.getObjectId(), path, objectId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,14 @@ private boolean visitRelationship(ArcProperty arc) {
appendColumn(targetSource, pkName, prefix + pkName);
}

// append id columns of additional entities...
descriptor.getAdditionalDbEntities().forEach((additionalPath, additionalDescriptor) -> {
for (DbAttribute pk : additionalDescriptor.getDbEntity().getPrimaryKeys()) {
String name = additionalPath.dot(pk.getName()).value();
appendColumn(targetSource, name, prefix + name);
}
});

// append inheritance discriminator columns...
for (ObjAttribute column : descriptor.getDiscriminatorColumns()) {
CayennePath target = column.getDbAttributePath();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.cayenne.query.ColumnSelect;
import org.apache.cayenne.query.EJBQLQuery;
import org.apache.cayenne.query.ObjectSelect;
import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.query.SelectById;
import org.apache.cayenne.runtime.CayenneRuntime;
import org.apache.cayenne.test.jdbc.TableHelper;
Expand Down Expand Up @@ -1194,4 +1195,98 @@ public void insertTwoGenericVerticalInheritanceObjects() {
final List<Persistent> boys = ObjectSelect.query(Persistent.class, "GenBoy").select(env.context());
assertEquals(1, boys.size());
}

private void updateFlattenedAttributeOfPrefetchedInheritedChild(int prefetchSemantics) throws SQLException {
TableHelper ivOtherTable = env.table("IV_OTHER", "ID");
TableHelper ivBaseTable = env.table("IV_BASE", "ID", "NAME", "TYPE");
TableHelper ivImplTable = env.table("IV_IMPL", "ID", "ATTR1", "OTHER3_ID");

ivOtherTable.insert(1);
ivBaseTable.insert(1, "name", "I");
ivImplTable.insert(1, "attr1", 1);

IvOther other = ObjectSelect.query(IvOther.class)
.prefetch(IvOther.IMPLS_WITH_INVERSE.getName(), prefetchSemantics)
.selectOne(env.context());

IvImpl impl = other.getImplsWithInverse().getFirst();
assertEquals("attr1", impl.getAttr1());

impl.setAttr1("attr1-updated");
env.context().commitChanges();

assertEquals(1, ivImplTable.getRowCount());
ObjectContext cleanContext = runtime.newContext();
IvImpl reread = SelectById.queryId(IvImpl.class, 1).selectOne(cleanContext);
assertEquals("attr1-updated", reread.getAttr1());
}

@Test
public void updateFlattenedAttributeOfInheritedChildJointPrefetch() throws SQLException {
updateFlattenedAttributeOfPrefetchedInheritedChild(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
}

@Test
public void updateFlattenedAttributeOfInheritedChildDisjointPrefetch() throws SQLException {
updateFlattenedAttributeOfPrefetchedInheritedChild(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
}

@Test
public void updateFlattenedAttributeOfInheritedChildDisjointByIdPrefetch() throws SQLException {
updateFlattenedAttributeOfPrefetchedInheritedChild(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
}

@Test
public void updateFlattenedAttributeOfInheritedChildRepeatedSaveResolvedFromCachedSnapshot() throws SQLException {
TableHelper ivBaseTable = env.table("IV_BASE", "ID", "NAME", "TYPE");
TableHelper ivImplTable = env.table("IV_IMPL", "ID", "ATTR1");

ivBaseTable.insert(1, "name", "I");
ivImplTable.insert(1, "attr1");

// First request: loads the child fresh (cold cache)
// This rebuilds the shared cached snapshot for the child.
{
ObjectContext freshContext = runtime.newContext();
IvImpl impl = Cayenne.objectForPK(freshContext, IvImpl.class, 1);
impl.setAttr1("attr1-first");
freshContext.commitChanges();
}

// Second request: (new context, shared snapshot cache)
// objectForPK is served from the cached snapshot
{
ObjectContext freshContext = runtime.newContext();
IvImpl impl = Cayenne.objectForPK(freshContext, IvImpl.class, 1);
impl.setAttr1("attr1-second");
freshContext.commitChanges();
}

assertEquals(1, ivImplTable.getRowCount());
ObjectContext cleanContext = runtime.newContext();
IvImpl reread = SelectById.queryId(IvImpl.class, 1).selectOne(cleanContext);
assertEquals("attr1-second", reread.getAttr1());
}

@Test
public void updateFlattenedAttributeOfThreeLevelInheritanceChild() throws SQLException {
TableHelper ivRootTable = env.table("IV_ROOT", "ID", "DISCRIMINATOR");
TableHelper ivSub1Table = env.table("IV_SUB1", "ID");
TableHelper ivSub1Sub1Table = env.table("IV_SUB1_SUB1", "ID", "SUB1_SUB1_NAME");

ivRootTable.insert(1, "IvSub1Sub1");
ivSub1Table.insert(1);
ivSub1Sub1Table.insert(1, "sub1sub1name");

IvSub1Sub1 sub1Sub1 = SelectById.queryId(IvSub1Sub1.class, 1).selectOne(env.context());
assertEquals("sub1sub1name", sub1Sub1.getSub1Sub1Name());

sub1Sub1.setSub1Sub1Name("sub1sub1name-updated");
env.context().commitChanges();

assertEquals(1, ivSub1Sub1Table.getRowCount());
ObjectContext cleanContext = runtime.newContext();
IvSub1Sub1 reread = SelectById.queryId(IvSub1Sub1.class, 1).selectOne(cleanContext);
assertEquals("sub1sub1name-updated", reread.getSub1Sub1Name());
}
}
Loading