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
5 changes: 3 additions & 2 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ CAY-2956 Get rid of a dedicated adapter for Oracle 8
CAY-2957 Get rid of adapter for legacy HSQLDB <= 1.8
CAY-2962 Allow unconstrained VARCHAR
CAY-2963 Replace TypesHandler / types.xml with hardcoded map
CAY-2969 Extender API for "soft" delete
CAY-2969 Extender API for "soft" delete
CAY-2970 Tighten deferred value resolution contract on commit
CAY-2971 Remove extra spaces within SQL parenthesis
CAY-2972 Fewer parentheses in generated SQL
Expand All @@ -28,7 +28,7 @@ CAY-2975 Mnemonic table aliases in generated SQL

Bug Fixes:

CAY-2892 Modeler: Exception when deleting a callback
CAY-2892 Modeler: Exception when deleting a callback
CAY-2958 Empty join is saved
CAY-2959 Modeler: DbRelationship dialog "Cancel" doesn't cancel
CAY-2960 Undoing renamed relationship change throws
Expand All @@ -38,6 +38,7 @@ 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-2973 Exception trying to copy/paste a callback
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());
}
}