From 26d1288757375f8544ce829a30a03c034c82ffeb Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Tue, 22 Jan 2019 00:05:19 +1300 Subject: [PATCH] #1613 - When using transaction.setPersistCascade(false) ... a Delete query should not cascade --- .../java/io/ebeaninternal/api/CQueryPlanKey.java | 4 ++++ src/main/java/io/ebeaninternal/api/SpiQuery.java | 5 +++++ .../ebeaninternal/server/core/OrmQueryRequest.java | 9 ++++++++- .../io/ebeaninternal/server/query/CQueryBuilder.java | 12 ++++++++---- .../io/ebeaninternal/server/query/CQueryEngine.java | 4 ++-- .../server/query/NativeSqlQueryPlanKey.java | 5 +++++ .../server/query/RawSqlQueryPlanKey.java | 5 +++++ .../server/querydefn/DefaultOrmQuery.java | 7 +++++++ .../server/querydefn/OrmQueryPlanKey.java | 5 +++++ 9 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/ebeaninternal/api/CQueryPlanKey.java b/src/main/java/io/ebeaninternal/api/CQueryPlanKey.java index 54de71025..2d3425573 100644 --- a/src/main/java/io/ebeaninternal/api/CQueryPlanKey.java +++ b/src/main/java/io/ebeaninternal/api/CQueryPlanKey.java @@ -11,4 +11,8 @@ public interface CQueryPlanKey { */ String getPartialKey(); + /** + * Return a query plan key taking into account persist cascading. + */ + CQueryPlanKey withDeleteByIds(); } diff --git a/src/main/java/io/ebeaninternal/api/SpiQuery.java b/src/main/java/io/ebeaninternal/api/SpiQuery.java index fe431b268..c951dae39 100644 --- a/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -439,6 +439,11 @@ public interface SpiQuery extends Query, TxnProfileEventCodes { */ void setDelete(); + /** + * Set the query to be delete by ids due to cascading delete. + */ + CQueryPlanKey setDeleteByIdsPlan(); + /** * Set the query to select the id property only. */ diff --git a/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java index 70bb74d4f..0f09bac26 100644 --- a/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java +++ b/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java @@ -118,7 +118,14 @@ public final class OrmQueryRequest extends BeanRequest implements SpiOrmQuery @Override public boolean isDeleteByStatement() { - return !transaction.isPersistCascade() || beanDescriptor.isDeleteByStatement(); + if (!transaction.isPersistCascade() || beanDescriptor.isDeleteByStatement()) { + // plain delete by query + return true; + } else { + // delete by ids due to cascading delete needs + queryPlanKey = query.setDeleteByIdsPlan(); + return false; + } } @Override diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java index 8b2e52f46..037f53ed1 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java @@ -43,8 +43,11 @@ import java.util.List; */ class CQueryBuilder { - protected final String tableAliasPlaceHolder; - protected final String columnAliasPrefix; + private static final String DELETE = "Delete"; + private static final String UPDATE = "Update"; + + final String tableAliasPlaceHolder; + final String columnAliasPrefix; private final SqlLimiter sqlLimiter; @@ -98,8 +101,9 @@ class CQueryBuilder { /** * Build the delete query. */ - CQueryUpdate buildUpdateQuery(String type, OrmQueryRequest request) { + CQueryUpdate buildUpdateQuery(boolean deleteRequest, OrmQueryRequest request) { + String type = (deleteRequest) ? DELETE : UPDATE; SpiQuery query = request.getQuery(); String rootTableAlias = query.getAlias(); query.setDelete(); @@ -117,7 +121,7 @@ class CQueryBuilder { SqlTree sqlTree = createSqlTree(request, predicates); String sql; - if (type.equals("Delete")) { + if (deleteRequest) { sql = buildDeleteSql(request, rootTableAlias, predicates, sqlTree); } else { sql = buildUpdateSql(request, rootTableAlias, predicates, sqlTree); diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java b/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java index 05e64658a..33f9bf143 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java @@ -68,12 +68,12 @@ public class CQueryEngine { } public int delete(OrmQueryRequest request) { - CQueryUpdate query = queryBuilder.buildUpdateQuery("Delete", request); + CQueryUpdate query = queryBuilder.buildUpdateQuery(true, request); return executeUpdate(request, query); } public int update(OrmQueryRequest request) { - CQueryUpdate query = queryBuilder.buildUpdateQuery("Update", request); + CQueryUpdate query = queryBuilder.buildUpdateQuery(false, request); return executeUpdate(request, query); } diff --git a/src/main/java/io/ebeaninternal/server/query/NativeSqlQueryPlanKey.java b/src/main/java/io/ebeaninternal/server/query/NativeSqlQueryPlanKey.java index ac98be5b5..2a0313ff3 100644 --- a/src/main/java/io/ebeaninternal/server/query/NativeSqlQueryPlanKey.java +++ b/src/main/java/io/ebeaninternal/server/query/NativeSqlQueryPlanKey.java @@ -18,6 +18,11 @@ public class NativeSqlQueryPlanKey implements CQueryPlanKey { return getPartialKey(); } + @Override + public CQueryPlanKey withDeleteByIds() { + throw new IllegalStateException("Not allowed"); + } + /** * Return as a partial key. For rawSql hash the sql is part of the key and as such * needs to be included in order to have a complete key. Typically the MD5 of the sql diff --git a/src/main/java/io/ebeaninternal/server/query/RawSqlQueryPlanKey.java b/src/main/java/io/ebeaninternal/server/query/RawSqlQueryPlanKey.java index 24336fb8f..04b2e282f 100644 --- a/src/main/java/io/ebeaninternal/server/query/RawSqlQueryPlanKey.java +++ b/src/main/java/io/ebeaninternal/server/query/RawSqlQueryPlanKey.java @@ -24,6 +24,11 @@ class RawSqlQueryPlanKey implements CQueryPlanKey { return getPartialKey() + ":r"; } + @Override + public CQueryPlanKey withDeleteByIds() { + throw new IllegalStateException("Not allowed"); + } + /** * Return as a partial key. For rawSql hash the sql is part of the key and as such * needs to be included in order to have a complete key. Typically the MD5 of the sql diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index c113ae3ac..e0645c688 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -679,6 +679,13 @@ public class DefaultOrmQuery implements SpiQuery { setSelectId(); } + @Override + public CQueryPlanKey setDeleteByIdsPlan() { + // re-build plan for cascading via delete by ids + queryPlanKey = queryPlanKey.withDeleteByIds(); + return queryPlanKey; + } + /** * Set the select clause to select the Id property. */ diff --git a/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKey.java b/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKey.java index 2286f3ae2..45bf4c802 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKey.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKey.java @@ -25,6 +25,11 @@ class OrmQueryPlanKey implements CQueryPlanKey { this.planHash = hc; } + @Override + public CQueryPlanKey withDeleteByIds() { + return new OrmQueryPlanKey(description + ":deleteByIds", 0, 0, null); + } + @Override public String getPartialKey() { return description;