diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java index f5f3a8a4a..a2a091ef6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java @@ -262,10 +262,16 @@ public abstract class BeanPropertyAssoc extends BeanProperty implements STree /** * Return true if the target side has soft delete. */ + @Override public boolean isTargetSoftDelete() { return targetDescriptor.isSoftDelete(); } + @Override + public String getSoftDeletePredicate(String tableAlias) { + return targetDescriptor.getSoftDeletePredicate(tableAlias); + } + /** * Return true if REFRESH should cascade. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/STreePropertyAssoc.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/STreePropertyAssoc.java index 1213d0b55..4f28bf803 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/STreePropertyAssoc.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/STreePropertyAssoc.java @@ -36,4 +36,13 @@ public interface STreePropertyAssoc extends STreeProperty { */ void setValue(EntityBean parentBean, Object contextBean); + /** + * Return true if the associated type has soft delete. + */ + boolean isTargetSoftDelete(); + + /** + * Return the soft delete predicate. + */ + String getSoftDeletePredicate(String tableAlias); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index bcd8b70a4..bae70396f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -362,7 +362,7 @@ public final class SqlTreeBuilder { // look for predicateIncludes that are not in selectIncludes and add // them as extra joins to the query - IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes); + IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes, temporalMode); Collection extraJoins = extraJoinDistill.getExtraJoinRootNodes(); if (!extraJoins.isEmpty()) { @@ -592,26 +592,20 @@ public final class SqlTreeBuilder { */ private static class IncludesDistiller { + private final STreeType desc; private final Set selectIncludes; private final Set predicateIncludes; + private final SpiQuery.TemporalMode temporalMode; - /** - * Contains the 'root' extra joins. We only return the roots back. - */ private final Map joinRegister = new HashMap<>(); - - /** - * Register of all the extra join nodes. - */ private final Map rootRegister = new HashMap<>(); - private final STreeType desc; - private IncludesDistiller(STreeType desc, Set selectIncludes, - Set predicateIncludes) { + Set predicateIncludes, SpiQuery.TemporalMode temporalMode) { this.desc = desc; this.selectIncludes = selectIncludes; this.predicateIncludes = predicateIncludes; + this.temporalMode = temporalMode; } /** @@ -666,7 +660,7 @@ public final class SqlTreeBuilder { if (extra == null) { return null; } else { - SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.getProperty(), extra.isContainsMany()); + SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.getProperty(), extra.isContainsMany(), temporalMode); joinRegister.put(propertyName, extraJoin); return extraJoin; } 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 08e5601fc..de1cff32b 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 @@ -24,19 +24,17 @@ import java.util.Set; class SqlTreeNodeExtraJoin implements SqlTreeNode { private final STreePropertyAssoc assocBeanProperty; - + private final SpiQuery.TemporalMode temporalMode; private final String prefix; - private final boolean manyJoin; - private final boolean pathContainsMany; - private List children; - SqlTreeNodeExtraJoin(String prefix, STreePropertyAssoc assocBeanProperty, boolean pathContainsMany) { + SqlTreeNodeExtraJoin(String prefix, STreePropertyAssoc assocBeanProperty, boolean pathContainsMany, SpiQuery.TemporalMode temporalMode) { this.prefix = prefix; this.assocBeanProperty = assocBeanProperty; this.pathContainsMany = pathContainsMany; + this.temporalMode = temporalMode; this.manyJoin = assocBeanProperty instanceof STreePropertyAssocMany; } @@ -144,15 +142,16 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode { assocBeanProperty.appendFrom(ctx, joinType); } joinType = assocBeanProperty.addJoin(joinType, prefix, ctx); + if (assocBeanProperty.isTargetSoftDelete() && temporalMode != SpiQuery.TemporalMode.SOFT_DELETED) { + ctx.append(" and ").append(assocBeanProperty.getSoftDeletePredicate(ctx.getTableAlias(prefix))); + } } if (children != null) { - if (manyJoin || pathContainsMany) { // if AUTO then make all descendants use OUTER JOIN joinType = joinType.autoToOuter(); } - for (SqlTreeNodeExtraJoin child : children) { child.appendFrom(ctx, joinType); } diff --git a/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteBasic.java b/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteBasic.java index 590585404..1dfec06c4 100644 --- a/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteBasic.java +++ b/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteBasic.java @@ -137,6 +137,27 @@ public class TestSoftDeleteBasic extends BaseTestCase { query.delete(); } + @Test + public void testFindChild_joinParent() { + EBasicSoftDelete bean = new EBasicSoftDelete(); + bean.setName("softDelParent_withChild"); + bean.addChild("child1", 10); + + DB.save(bean); + + Query query = DB.find(EBasicSDChild.class) + .where() + .eq("owner.name", "softDelParent_withChild") + .query(); + + List list = query.findList(); + assertSql(query).contains("join ebasic_soft_delete t1 on t1.id = t0.owner_id and t1.deleted ="); + assertThat(list).hasSize(1); + + // Cleanup created entity + DB.deletePermanent(bean); + } + @Test public void testFindSoftDeletedList() { EBasicSoftDelete bean = new EBasicSoftDelete();