This is an automated email from the ASF dual-hosted git repository. ntimofeev pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit cd8f404f6227e4b49b19503d5491152bdc521043 Author: Nikita Timofeev <stari...@gmail.com> AuthorDate: Fri Nov 19 16:36:13 2021 +0300 CAY-2723 Phantom update of a to-dependent-pk relationship --- .../cayenne/access/flush/DbRowOpFactory.java | 4 ++ .../cayenne/access/flush/operation/Qualifier.java | 2 +- .../java/org/apache/cayenne/access/CAY2723IT.java | 58 ++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DbRowOpFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DbRowOpFactory.java index 28fa93d..accf329 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DbRowOpFactory.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DbRowOpFactory.java @@ -94,6 +94,10 @@ class DbRowOpFactory { } private DbRowOp createRow(DbEntity entity, ObjectId id, DbRowOpType type) { + // skip phantom nodes, this could be a created and immediately deleted relationship + if(store.getNode(id) == null && !id.getEntityName().startsWith("db:")) { + return null; + } switch (type) { case INSERT: return new InsertDbRowOp(object, entity, id); diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/Qualifier.java b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/Qualifier.java index 5f17172..0589e0a 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/Qualifier.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/Qualifier.java @@ -50,7 +50,7 @@ public class Qualifier { public Map<String, Object> getSnapshot() { Map<String, Object> idSnapshot = row.getChangeId().getIdSnapshot(); if(additionalQualifier == null || additionalQualifier.isEmpty()) { - return idSnapshot; + return new HashMap<>(idSnapshot); } Map<String, Object> qualifier = new HashMap<>(additionalQualifier.size() + idSnapshot.size()); diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/CAY2723IT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/CAY2723IT.java new file mode 100644 index 0000000..b337792 --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/access/CAY2723IT.java @@ -0,0 +1,58 @@ +/***************************************************************** + * 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.access; + +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.testdo.testmap.Painting; +import org.apache.cayenne.testdo.testmap.PaintingInfo; +import org.apache.cayenne.unit.di.DataChannelInterceptor; +import org.apache.cayenne.unit.di.server.CayenneProjects; +import org.apache.cayenne.unit.di.server.ServerCase; +import org.apache.cayenne.unit.di.server.UseServerRuntime; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) +public class CAY2723IT extends ServerCase { + @Inject + private DataContext context; + + @Inject + private DataChannelInterceptor queryInterceptor; + + @Test + public void phantomToDepPKUpdate() { + Painting painting = context.newObject(Painting.class); + painting.setPaintingTitle("test_p_123"); + + PaintingInfo paintingInfo = context.newObject(PaintingInfo.class); + paintingInfo.setTextReview("test_a_123"); + + painting.setToPaintingInfo(paintingInfo); + painting.setToPaintingInfo(null); + + context.deleteObject(paintingInfo); + + // here should be only single insert of the painting object, but there will be 3 queries in total + // (2 for the PK generation + insert) + int queryCounter = queryInterceptor.runWithQueryCounter(() -> context.commitChanges()); + assertEquals(3, queryCounter); + } +}