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 2361f20a7 CAY-2838 Vertical Inheritance: Problem setting db attribute to null via flattened path 2361f20a7 is described below commit 2361f20a74bc6178f9dab40f0920e2a4f2aa6f00 Author: Nikita Timofeev <stari...@gmail.com> AuthorDate: Tue Feb 27 12:42:59 2024 +0400 CAY-2838 Vertical Inheritance: Problem setting db attribute to null via flattened path (cherry picked from commit 2bbe6cedd189c56f6e93c41eb8158c2eb840539f) --- RELEASE-NOTES.txt | 1 + .../access/flush/ArcValuesCreationHandler.java | 5 +- .../cayenne/access/VerticalInheritanceIT.java | 124 +++++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index cf9643a37..6044843b1 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-2838 Vertical Inheritance: Problem setting db attribute to null via flattened path CAY-2840 Vertical Inheritance: Missing subclass attributes with joint prefetch ---------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/ArcValuesCreationHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/ArcValuesCreationHandler.java index 91f86bd5d..1ca012aa6 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/ArcValuesCreationHandler.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/ArcValuesCreationHandler.java @@ -151,8 +151,9 @@ class ArcValuesCreationHandler implements GraphChangeHandler { // should update existing DB row factory.getOrCreate(target, targetId, add ? DbRowOpType.UPDATE : defaultType); } - processRelationship(relationship, srcId, targetId, add); - srcId = targetId; // use target as next source.. + // should always add data from the intermediate relationship + processRelationship(relationship, srcId, targetId, dbPathIterator.hasNext() || add); + srcId = targetId; // use target as next source } } 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 320f22051..e826fa112 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 @@ -619,6 +619,130 @@ public class VerticalInheritanceIT extends ServerCase { context.commitChanges(); } + /** + * @link https://issues.apache.org/jira/browse/CAY-2838 + */ + @Test + public void testNullifyFlattenedAttribute() { + IvConcrete concrete = context.newObject(IvConcrete.class); + concrete.setName("Concrete"); + context.commitChanges(); + + concrete.setName(null); + context.commitChanges(); + + assertNull(concrete.getName()); + + long id = Cayenne.longPKForObject(concrete); + { + ObjectContext cleanContext = runtime.newContext(); + IvConcrete concreteFetched = SelectById.query(IvConcrete.class, id).selectOne(cleanContext); + assertNull(concreteFetched.getName()); + } + } + + @Test + public void testNullifyFlattenedRelationship() { + IvOther other = context.newObject(IvOther.class); + other.setName("other"); + + IvImpl impl = context.newObject(IvImpl.class); + impl.setName("Impl 1"); + impl.setOther1(other); + context.commitChanges(); + + impl.setOther1(null); + context.commitChanges(); + + assertNull(impl.getOther1()); + + long id = Cayenne.longPKForObject(impl); + { + ObjectContext cleanContext = runtime.newContext(); + IvImpl implFetched = SelectById.query(IvImpl.class, id).selectOne(cleanContext); + assertEquals("Impl 1", implFetched.getName()); + assertNull(implFetched.getOther1()); + } + } + + @Test + public void testDeleteFlattenedNoValues() throws SQLException { + TableHelper ivAbstractTable = new TableHelper(dbHelper, "IV_ABSTRACT"); + ivAbstractTable.setColumns("ID", "PARENT_ID", "TYPE") + .setColumnTypes(Types.INTEGER, Types.INTEGER, Types.CHAR); + + TableHelper ivConcreteTable = new TableHelper(dbHelper, "IV_CONCRETE"); + ivConcreteTable.setColumns("ID", "NAME") + .setColumnTypes(Types.INTEGER, Types.VARCHAR); + + ivAbstractTable.insert(1, null, "S"); + + IvConcrete concrete = SelectById.query(IvConcrete.class, 1).selectOne(context); + assertNotNull(concrete); + assertNull(concrete.getName()); + + context.deleteObject(concrete); + context.commitChanges(); + + assertEquals(0, ivAbstractTable.getRowCount()); + assertEquals(0, ivConcreteTable.getRowCount()); + } + + @Test + public void testDeleteFlattenedNullValues() throws SQLException { + TableHelper ivAbstractTable = new TableHelper(dbHelper, "IV_ABSTRACT"); + ivAbstractTable.setColumns("ID", "PARENT_ID", "TYPE") + .setColumnTypes(Types.INTEGER, Types.INTEGER, Types.CHAR); + + TableHelper ivConcreteTable = new TableHelper(dbHelper, "IV_CONCRETE"); + ivConcreteTable.setColumns("ID", "NAME") + .setColumnTypes(Types.INTEGER, Types.VARCHAR); + + ivAbstractTable.insert(1, null, "S"); + ivConcreteTable.insert(1, null); + + IvConcrete concrete = SelectById.query(IvConcrete.class, 1).selectOne(context); + assertNotNull(concrete); + assertNull(concrete.getName()); + + context.deleteObject(concrete); + context.commitChanges(); + + assertEquals(0, ivAbstractTable.getRowCount()); + assertEquals(0, ivConcreteTable.getRowCount()); + } + + @Test + public void testDeleteFlattenedNullifyValues() throws SQLException { + TableHelper ivAbstractTable = new TableHelper(dbHelper, "IV_ABSTRACT"); + ivAbstractTable.setColumns("ID", "PARENT_ID", "TYPE") + .setColumnTypes(Types.INTEGER, Types.INTEGER, Types.CHAR); + + TableHelper ivConcreteTable = new TableHelper(dbHelper, "IV_CONCRETE"); + ivConcreteTable.setColumns("ID", "NAME") + .setColumnTypes(Types.INTEGER, Types.VARCHAR); + + ivAbstractTable.insert(1, null, "S"); + ivConcreteTable.insert(1, "test"); + + IvConcrete concrete = SelectById.query(IvConcrete.class, 1).selectOne(context); + assertNotNull(concrete); + assertEquals("test", concrete.getName()); + + concrete.setName(null); + context.commitChanges(); + assertNull(concrete.getName()); + + assertEquals(1, ivAbstractTable.getRowCount()); + assertEquals(1, ivConcreteTable.getRowCount()); + + context.deleteObject(concrete); + context.commitChanges(); + + assertEquals(0, ivAbstractTable.getRowCount()); + assertEquals(0, ivConcreteTable.getRowCount()); + } + @Test//(expected = ValidationException.class) // other2 is not mandatory for now public void testInsertWithAttributeAndRelationship() { IvOther other = context.newObject(IvOther.class);