From 80fc0723c8c8cd47cf2611c5196d9c2b980147fe Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 10 Oct 2023 21:24:35 +1300 Subject: [PATCH 1/4] Failing test for OneToOne with SoftDelete join missing deleted predicate --- .../model/softdelete/ESoftDelOneBOwner.java | 59 +++++++++++++++++++ .../softdelete/TestSoftDeleteOtoImported.java | 47 +++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 ebean-test/src/test/java/org/tests/model/softdelete/ESoftDelOneBOwner.java create mode 100644 ebean-test/src/test/java/org/tests/softdelete/TestSoftDeleteOtoImported.java diff --git a/ebean-test/src/test/java/org/tests/model/softdelete/ESoftDelOneBOwner.java b/ebean-test/src/test/java/org/tests/model/softdelete/ESoftDelOneBOwner.java new file mode 100644 index 000000000..e2303d559 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/softdelete/ESoftDelOneBOwner.java @@ -0,0 +1,59 @@ +package org.tests.model.softdelete; + +import io.ebean.annotation.SoftDelete; +import jakarta.persistence.*; + +@SuppressWarnings("unused") +@Entity +public class ESoftDelOneBOwner { + + @Id + long id; + + String name; + + @ManyToOne + ESoftDelOneB oneb; + + @SoftDelete + boolean deleted; + + @Version + long version; + + public ESoftDelOneBOwner(String name) { + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ESoftDelOneB oneb() { + return oneb; + } + + public void setOneb(ESoftDelOneB oneb) { + this.oneb = oneb; + } + + public long getVersion() { + return version; + } + + public void setVersion(long version) { + this.version = version; + } +} diff --git a/ebean-test/src/test/java/org/tests/softdelete/TestSoftDeleteOtoImported.java b/ebean-test/src/test/java/org/tests/softdelete/TestSoftDeleteOtoImported.java new file mode 100644 index 000000000..f420f2bb2 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/softdelete/TestSoftDeleteOtoImported.java @@ -0,0 +1,47 @@ +package org.tests.softdelete; + +import io.ebean.DB; +import io.ebean.test.LoggedSql; +import io.ebean.xtest.BaseTestCase; +import org.junit.jupiter.api.Test; +import org.tests.model.softdelete.ESoftDelOneA; +import org.tests.model.softdelete.ESoftDelOneB; +import org.tests.model.softdelete.ESoftDelOneBOwner; + +import static org.assertj.core.api.Assertions.assertThat; + +class TestSoftDeleteOtoImported extends BaseTestCase { + + @Test + void extraJoinToOtoImported_expect_softDeletePredicate() { + ESoftDelOneB b = new ESoftDelOneB("xbImported"); + DB.save(b); + + ESoftDelOneA a = new ESoftDelOneA("xaImported"); + a.setOneb(b); + DB.save(a); + + ESoftDelOneBOwner co = new ESoftDelOneBOwner("xoImport"); + co.setOneb(b); + DB.save(co); + + LoggedSql.start(); + var listResult = DB.find(ESoftDelOneBOwner.class) + .where() + .eq("oneb.onea.name", "xaImported") + .findList(); + + var countResult = DB.find(ESoftDelOneBOwner.class) + .where() + .eq("oneb.onea.name", "xaImported") + .findCount(); + + var sql = LoggedSql.stop(); + assertThat(sql).hasSize(2); + + assertThat(listResult).hasSize(1); + assertThat(countResult).isEqualTo(1); + assertThat(sql.get(0)).contains("and t2.deleted = "); + assertThat(sql.get(1)).contains("and t2.deleted = "); + } +} From 5684919a9fb7e781a4bf82e7691df53ef9ac7e57 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 10 Oct 2023 21:27:26 +1300 Subject: [PATCH 2/4] Fix for OneToOne with SoftDelete join missing deleted predicate --- .../io/ebeaninternal/server/deploy/DbSqlContext.java | 6 ++++++ .../ebeaninternal/server/query/DefaultDbSqlContext.java | 9 ++++++++- .../ebeaninternal/server/query/SqlTreeNodeExtraJoin.java | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java index be515b634..522aff847 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java @@ -148,4 +148,10 @@ public interface DbSqlContext { * Add extra joins *IF* required to support inheritance discriminator in projection. */ void flushExtraJoins(); + + /** + * Return true if the last join was added and false means the join was suppressed + * as it was already added to the query. + */ + boolean joinAdded(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java index 9488ad118..7e5df0f3f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java @@ -38,6 +38,7 @@ final class DefaultDbSqlContext implements DbSqlContext { private final CQueryDraftSupport draftSupport; private final CQueryHistorySupport historySupport; private final boolean historyQuery; + private boolean joinSuppressed; DefaultDbSqlContext(SqlTreeAlias alias, String columnAliasPrefix, CQueryHistorySupport historySupport, CQueryDraftSupport draftSupport, String fromForUpdate) { @@ -50,6 +51,11 @@ final class DefaultDbSqlContext implements DbSqlContext { this.fromForUpdate = fromForUpdate; } + @Override + public boolean joinAdded() { + return !joinSuppressed; + } + @Override public boolean isIncludeSoftDelete() { return alias.isIncludeSoftDelete(); @@ -118,9 +124,10 @@ final class DefaultDbSqlContext implements DbSqlContext { } String joinKey = table + "-" + a1 + "-" + a2; if (tableJoins.contains(joinKey)) { + joinSuppressed = true; return; } - + joinSuppressed = false; tableJoins.add(joinKey); sb.append(' ').append(type); boolean addAsOfOnClause = false; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java index 9b8806406..43a3953cb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java @@ -145,7 +145,7 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode { assocBeanProperty.appendFrom(ctx, joinType, null); } joinType = assocBeanProperty.addJoin(joinType, prefix, ctx); - if (!oneToOneExported && assocBeanProperty.isTargetSoftDelete() && temporalMode != SpiQuery.TemporalMode.SOFT_DELETED) { + if (ctx.joinAdded() && assocBeanProperty.isTargetSoftDelete() && temporalMode != SpiQuery.TemporalMode.SOFT_DELETED) { ctx.append(" and ").append(assocBeanProperty.softDeletePredicate(ctx.tableAlias(prefix))); } } From 131d531922a7516b685d399e300118d5cc25ef42 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 10 Oct 2023 21:32:28 +1300 Subject: [PATCH 3/4] #3240 followup no functional change, tidy use of ctx.joinAdded() --- .../io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java | 4 +++- .../ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java index 43a3953cb..6c5a2ead1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java @@ -10,6 +10,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import static io.ebeaninternal.api.SpiQuery.TemporalMode.SOFT_DELETED; + /** * The purpose is to add an extra join to the query. *

@@ -145,7 +147,7 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode { assocBeanProperty.appendFrom(ctx, joinType, null); } joinType = assocBeanProperty.addJoin(joinType, prefix, ctx); - if (ctx.joinAdded() && assocBeanProperty.isTargetSoftDelete() && temporalMode != SpiQuery.TemporalMode.SOFT_DELETED) { + if (temporalMode != SOFT_DELETED && ctx.joinAdded() && assocBeanProperty.isTargetSoftDelete()) { ctx.append(" and ").append(assocBeanProperty.softDeletePredicate(ctx.tableAlias(prefix))); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java index 325f58cea..ada05c4c9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java @@ -88,14 +88,14 @@ final class SqlTreeNodeManyWhereJoin implements SqlTreeNode { if (nodeBeanProp instanceof STreePropertyAssocOne) { nodeBeanProp.addJoin(joinType, parentAlias, alias, ctx); - if (softDelete) { + if (softDelete && ctx.joinAdded()) { ctx.append(" and ").append(target.softDeletePredicate(alias)); } } else { STreePropertyAssocMany manyProp = (STreePropertyAssocMany) nodeBeanProp; if (!manyProp.hasJoinTable()) { manyProp.addJoin(joinType, parentAlias, alias, ctx); - if (softDelete) { + if (softDelete && ctx.joinAdded()) { ctx.append(" and ").append(target.softDeletePredicate(alias)); } } else { From 90a6b71b7e8fe5546ee4c9a86c5b4d576e1bd6bb Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 10 Oct 2023 21:33:23 +1300 Subject: [PATCH 4/4] #3240 followup no functional change, tidy use of ctx.joinAdded() --- ebean-test/src/test/java/org/tests/merge/TestMergeCustomer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/ebean-test/src/test/java/org/tests/merge/TestMergeCustomer.java b/ebean-test/src/test/java/org/tests/merge/TestMergeCustomer.java index e48d2be8e..e18f2983e 100644 --- a/ebean-test/src/test/java/org/tests/merge/TestMergeCustomer.java +++ b/ebean-test/src/test/java/org/tests/merge/TestMergeCustomer.java @@ -331,7 +331,6 @@ public class TestMergeCustomer extends BaseTestCase { assertThat(sql.get(17)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?"); assertSqlBind(sql, 18, 21); assertThat(sql.get(23)).contains("update mcontact_message set title=?, subject=?, notes=?, version=?, contact_id=? where id=? and version=?"); - assertSqlBind(sql, 24, 29); } }