This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch past-M2 in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit b5be7b6e99f70cd69827b98da84f1c29c0fda259 Author: Andrus Adamchik <[email protected]> AuthorDate: Thu Jun 11 17:57:10 2026 -0400 CAY-2958 Empty join is saved --- RELEASE-NOTES.txt | 3 +- .../org/apache/cayenne/map/DbRelationship.java | 5 +- .../org/apache/cayenne/map/DbRelationshipTest.java | 96 ++++++++++++++++++++++ .../ui/dbrelationship/DbJoinTableModel.java | 5 +- .../ui/dbrelationship/DbRelationshipDialog.java | 4 +- 5 files changed, 109 insertions(+), 4 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 42834e397..ede153c1b 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -14,10 +14,11 @@ Date: Changes/New Features: CAY-2956 Get rid of a dedicated adapter for Oracle 8 -CAY-2957 Get rid of adaper for legacy HSQLDB <= 1.8 +CAY-2957 Get rid of adapter for legacy HSQLDB <= 1.8 Bug Fixes: +CAY-2958 Empty join is saved ---------------------------------- Release: 5.0-M2 diff --git a/cayenne/src/main/java/org/apache/cayenne/map/DbRelationship.java b/cayenne/src/main/java/org/apache/cayenne/map/DbRelationship.java index 5db9010cb..1549afae0 100644 --- a/cayenne/src/main/java/org/apache/cayenne/map/DbRelationship.java +++ b/cayenne/src/main/java/org/apache/cayenne/map/DbRelationship.java @@ -80,7 +80,10 @@ public class DbRelationship extends Relationship<DbEntity, DbAttribute, DbRelati encoder.attribute("toDependentPK", isToDependentPK() && isValidForDepPk()); encoder.attribute("toMany", isToMany()); - encoder.nested(getJoins(), delegate); + // skip empty joins that would otherwise be saved as useless "<db-attribute-pair/>" tags + encoder.nested(getJoins().stream() + .filter(j -> j.getSourceName() != null || j.getTargetName() != null) + .toList(), delegate); delegate.visitDbRelationship(this); encoder.end(); diff --git a/cayenne/src/test/java/org/apache/cayenne/map/DbRelationshipTest.java b/cayenne/src/test/java/org/apache/cayenne/map/DbRelationshipTest.java new file mode 100644 index 000000000..b981bf1ce --- /dev/null +++ b/cayenne/src/test/java/org/apache/cayenne/map/DbRelationshipTest.java @@ -0,0 +1,96 @@ +/***************************************************************** + * 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.map; + +import org.apache.cayenne.configuration.BaseConfigurationNodeVisitor; +import org.apache.cayenne.util.XMLEncoder; +import org.junit.jupiter.api.Test; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DbRelationshipTest { + + @Test + public void encodeAsXMLSkipsEmptyJoins() { + DbRelationship relationship = relationship(); + relationship.addJoin(new DbJoin(relationship, "A_ID", "B_ID")); + + // empty join, e.g. added in the Modeler join table and never filled in; must not be saved + relationship.addJoin(new DbJoin(relationship)); + + String ls = System.lineSeparator(); + assertEquals("<db-relationship name=\"r1\" source=\"A\" target=\"B\">" + ls + + "<db-attribute-pair source=\"A_ID\" target=\"B_ID\"/>" + ls + + "</db-relationship>" + ls, encode(relationship)); + } + + @Test + public void encodeAsXMLKeepsHalfFilledJoins() { + DbRelationship relationship = relationship(); + + // a join with only one side set still carries user data and must be saved + relationship.addJoin(new DbJoin(relationship, "A_ID", null)); + + String ls = System.lineSeparator(); + assertEquals("<db-relationship name=\"r1\" source=\"A\" target=\"B\">" + ls + + "<db-attribute-pair source=\"A_ID\"/>" + ls + + "</db-relationship>" + ls, encode(relationship)); + } + + @Test + public void encodeAsXMLWithOnlyEmptyJoins() { + DbRelationship relationship = relationship(); + relationship.addJoin(new DbJoin(relationship)); + + String ls = System.lineSeparator(); + assertEquals("<db-relationship name=\"r1\" source=\"A\" target=\"B\"/>" + ls, encode(relationship)); + } + + private DbRelationship relationship() { + DataMap map = new DataMap("M"); + DbEntity source = new DbEntity("A"); + DbEntity target = new DbEntity("B"); + map.addDbEntity(source); + map.addDbEntity(target); + + DbRelationship relationship = new DbRelationship("r1"); + relationship.setSourceEntity(source); + relationship.setTargetEntityName("B"); + source.addRelationship(relationship); + + return relationship; + } + + private String encode(DbRelationship relationship) { + StringWriter out = new StringWriter(); + relationship.encodeAsXML(new XMLEncoder(new PrintWriter(out)), new EncoderDummyVisitor()); + return out.toString(); + } + + private static class EncoderDummyVisitor extends BaseConfigurationNodeVisitor<Object> { + + @Override + public Object visitDbRelationship(DbRelationship relationship) { + return null; + } + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/dbrelationship/DbJoinTableModel.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/dbrelationship/DbJoinTableModel.java index fbef69781..1a8ab08b0 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/dbrelationship/DbJoinTableModel.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/dbrelationship/DbJoinTableModel.java @@ -73,7 +73,10 @@ public class DbJoinTableModel extends CMTableModel<DbJoin> { } public void commit() { - relationship.setJoins(objectList); + // drop empty rows added by the user but never filled in + relationship.setJoins(objectList.stream() + .filter(j -> j.getSourceName() != null || j.getTargetName() != null) + .toList()); } @Override diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/dbrelationship/DbRelationshipDialog.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/dbrelationship/DbRelationshipDialog.java index b7e6de15c..804a716a8 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/dbrelationship/DbRelationshipDialog.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/dbrelationship/DbRelationshipDialog.java @@ -344,12 +344,14 @@ public class DbRelationshipDialog extends ProjectDialog { stopEditing(); DbJoinTableModel model = (DbJoinTableModel) table.getModel(); - boolean updatingReverse = !model.getObjectList().isEmpty(); handleNameUpdate(relationship, name.getText().trim()); model.commit(); + // check after commit, as it filters out empty joins + boolean updatingReverse = !relationship.getJoins().isEmpty(); + relationship.setToMany(toMany.isSelected()); ObjectInfo.putToMetaData(app.getMetaData(), relationship, ObjectInfo.COMMENT, comment.getText());
