diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index e031946fc..8a4974cfb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -2276,7 +2276,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { if (initRequest.isAll()) { queryPlanManager.setDefaultThreshold(initRequest.thresholdMicros()); } - return descriptorManager.queryPlanInit(initRequest); + List list = descriptorManager.queryPlanInit(initRequest); + dtoBeanManager.queryPlanInit(initRequest, list); + return list; } List queryPlanCollectNow(QueryPlanRequest request) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoBeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoBeanDescriptor.java index c9720308a..c4bbe7642 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoBeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoBeanDescriptor.java @@ -1,7 +1,10 @@ package io.ebeaninternal.server.dto; +import io.ebean.meta.MetaQueryPlan; import io.ebean.meta.MetricVisitor; +import io.ebean.meta.QueryPlanInit; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -43,6 +46,18 @@ public final class DtoBeanDescriptor { } } + /** + * Arm bind capture for matching (native SQL) DTO query plans. + */ + public void queryPlanInit(QueryPlanInit request, List list) { + for (DtoQueryPlan plan : plans.values()) { + if (request.includeHash(plan.hash())) { + plan.queryPlanInit(request.thresholdMicros(plan.hash())); + list.add(plan.createMeta(null, null)); + } + } + } + /** * Return the named RawSql query. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoBeanManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoBeanManager.java index c95ad1176..1d2975866 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoBeanManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoBeanManager.java @@ -1,9 +1,12 @@ package io.ebeaninternal.server.dto; +import io.ebean.meta.MetaQueryPlan; import io.ebean.meta.MetricVisitor; +import io.ebean.meta.QueryPlanInit; import io.ebeaninternal.server.type.TypeManager; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -51,4 +54,14 @@ public final class DtoBeanManager { value.visit(visitor); } } + + /** + * Arm bind capture for matching (native SQL) DTO query plans. + */ + @SuppressWarnings("unchecked") + public void queryPlanInit(QueryPlanInit request, List list) { + for (DtoBeanDescriptor value : descriptorMap.values()) { + value.queryPlanInit(request, list); + } + } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlan.java index 15f9325fe..58526545e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlan.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlan.java @@ -2,6 +2,7 @@ package io.ebeaninternal.server.dto; import io.ebean.core.type.DataReader; import io.ebean.meta.MetricVisitor; +import io.ebeaninternal.api.SpiQueryPlan; import io.ebeaninternal.server.bind.capture.BindCapture; import java.sql.SQLException; @@ -9,7 +10,7 @@ import java.sql.SQLException; /** * Knows how to read and map rows into a Bean. */ -public interface DtoQueryPlan { +public interface DtoQueryPlan extends SpiQueryPlan { /** * Read the row data and return the DTO bean. diff --git a/ebean-test/src/test/java/io/ebean/xtest/base/DtoQueryPlanCaptureTest.java b/ebean-test/src/test/java/io/ebean/xtest/base/DtoQueryPlanCaptureTest.java index ef49b1fe4..fc749b3f4 100644 --- a/ebean-test/src/test/java/io/ebean/xtest/base/DtoQueryPlanCaptureTest.java +++ b/ebean-test/src/test/java/io/ebean/xtest/base/DtoQueryPlanCaptureTest.java @@ -30,25 +30,32 @@ class DtoQueryPlanCaptureTest extends BaseTestCase { } } + private List runQuery() { + return DB.getDefault() + .findDto(DCustPlanCapture.class, "select id, name from o_customer where id > ?") + .setParameter(0) + .setLabel("custDtoPlan") + .findList(); + } + @Test void nativeDtoQuery_capturesQueryPlan() { ResetBasicData.reset(); - // arm bind capture with a very low threshold so new (DTO) plans capture + // build the DTO plan first (default threshold -> no capture yet) + assertThat(runQuery()).isNotEmpty(); + + // arming an already-built DTO plan should return its meta QueryPlanInit init = new QueryPlanInit(); init.setAll(true); init.thresholdMicros(1); - DB.getDefault().metaInfo().queryPlanInit(init); + List armed = DB.getDefault().metaInfo().queryPlanInit(init); + assertThat(armed) + .as("queryPlanInit includes the (already built) DTO plan") + .anyMatch(p -> "dto.DCustPlanCapture.custDtoPlan".equals(p.label())); - // native SQL DtoQuery - creates the DTO plan with the low threshold and captures bind values - for (int i = 0; i < 3; i++) { - List list = DB.getDefault() - .findDto(DCustPlanCapture.class, "select id, name from o_customer where id > ?") - .setParameter(0) - .setLabel("custDtoPlan") - .findList(); - assertThat(list).isNotEmpty(); - } + // run again now that the plan is armed -> bind values captured + assertThat(runQuery()).isNotEmpty(); QueryPlanRequest request = new QueryPlanRequest(); request.maxCount(1000);