diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java index 3ee4568be..e6a978d46 100644 --- a/src/main/java/io/ebean/config/ServerConfig.java +++ b/src/main/java/io/ebean/config/ServerConfig.java @@ -486,6 +486,11 @@ public class ServerConfig { */ private boolean notifyL2CacheInForeground; + /** + * Set to true to support query plan capture. + */ + private boolean collectQueryPlans; + /** * The time in millis used to determine when a query is alerted for being slow. */ @@ -2832,6 +2837,7 @@ public class ServerConfig { queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds); slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis); + collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans); docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly); disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache); notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground); @@ -3197,6 +3203,20 @@ public class ServerConfig { this.idGeneratorAutomatic = idGeneratorAutomatic; } + /** + * Return true if query plan capture is enabled. + */ + public boolean isCollectQueryPlans() { + return collectQueryPlans; + } + + /** + * Set to true to enable query plan capture. + */ + public void setCollectQueryPlans(boolean collectQueryPlans) { + this.collectQueryPlans = collectQueryPlans; + } + public enum UuidVersion { VERSION4, VERSION1, diff --git a/src/main/java/io/ebean/meta/MetaInfoManager.java b/src/main/java/io/ebean/meta/MetaInfoManager.java index 9535de536..74d0e7882 100644 --- a/src/main/java/io/ebean/meta/MetaInfoManager.java +++ b/src/main/java/io/ebean/meta/MetaInfoManager.java @@ -1,12 +1,18 @@ package io.ebean.meta; import java.util.List; +import java.util.function.Consumer; /** * Provides access to the meta data in EbeanServer such as query execution statistics. */ public interface MetaInfoManager { + /** + * Collect query plans. + */ + List collectQueryPlans(QueryPlanRequest request); + /** * Visit the metrics resetting and collecting/reporting as desired. */ diff --git a/src/main/java/io/ebean/meta/MetaQueryPlan.java b/src/main/java/io/ebean/meta/MetaQueryPlan.java new file mode 100644 index 000000000..f24ff94fa --- /dev/null +++ b/src/main/java/io/ebean/meta/MetaQueryPlan.java @@ -0,0 +1,23 @@ +package io.ebean.meta; + +public interface MetaQueryPlan { + + Class getBeanType(); + + /** + * Return a string representation of the query plan hash. + */ + String getQueryPlanHash(); + + String getLabel(); + + String getSql(); + + String getBind(); + + String getPlan(); + + long getQueryTimeMicros(); + + long getCaptureCount(); +} diff --git a/src/main/java/io/ebean/meta/QueryPlanRequest.java b/src/main/java/io/ebean/meta/QueryPlanRequest.java new file mode 100644 index 000000000..20b12c145 --- /dev/null +++ b/src/main/java/io/ebean/meta/QueryPlanRequest.java @@ -0,0 +1,97 @@ +package io.ebean.meta; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Request used to capture query plans. + */ +public class QueryPlanRequest { + + private List plans = new ArrayList<>(); + + private Connection connection; + + private boolean store; + + private long since; + + private Set> includedBeanTypes; + + private Set includedLabels; + + public List getPlans() { + return plans; + } + + /** + * Return the connection to use to capture the query plans. + */ + public Connection getConnection() { + return connection; + } + + /** + * Set the connection to use to capture the query plans. + */ + public void setConnection(Connection connection) { + this.connection = connection; + } + + /** + * Return true if the captured query plan is stored. + */ + public boolean isStore() { + return store; + } + + /** + * Set to true to store the captured query plan. + */ + public void setStore(boolean store) { + this.store = store; + } + + /** + * Return the epoch time after which the query plan was capture (to be included). + */ + public long getSince() { + return since; + } + + /** + * Set the epoch time after which the query plan was captured. + *

+ * This is used to only capture plans that have changed since a given time (like the time of last capture). + *

+ * + * @param since The time after which the query plan was captured to be included + */ + public void setSince(long since) { + this.since = since; + } + + /** + * Process consume the query plan. + */ + public void process(MetaQueryPlan plan) { + plans.add(plan); + } + + /** + * Return true if the bean type should be included in the query plan capture. + */ + public boolean includeType(Class beanType) { + return includedBeanTypes == null || includedBeanTypes.isEmpty() || includedBeanTypes.contains(beanType); + } + + /** + * Return true if the label should be included in the query plan capture. + */ + public boolean includeLabel(String label) { + return includedLabels == null || includedLabels.isEmpty() || includedLabels.contains(label); + } + +} diff --git a/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java b/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java index ddd02d768..37ff3d2f2 100644 --- a/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java +++ b/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java @@ -3,7 +3,6 @@ package io.ebeaninternal.api; import io.ebean.Pairs; import java.util.ArrayList; -import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java index b3b36c3ef..da679d9a5 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java @@ -1,10 +1,10 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; -import java.io.IOException; - import io.ebean.config.dbplatform.DatabasePlatform; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; +import java.io.IOException; + public class HanaColumnStoreDdl extends AbstractHanaDdl { public HanaColumnStoreDdl(DatabasePlatform platform) { diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaTableDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaTableDdl.java index 943dc3ecd..8740db1c4 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaTableDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaTableDdl.java @@ -1,8 +1,5 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; -import java.io.IOException; -import java.util.List; - import io.ebean.config.PropertiesWrapper; import io.ebean.config.ServerConfig; import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; @@ -12,6 +9,9 @@ import io.ebeaninternal.dbmigration.migration.Column; import io.ebeaninternal.dbmigration.migration.DropColumn; import io.ebeaninternal.dbmigration.model.MTable; +import java.io.IOException; +import java.util.List; + public class HanaTableDdl extends BaseTableDdl { private final HanaHistoryDdl historyDdl; diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java index a07dcdfd6..f6a65da81 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -20,7 +20,6 @@ import io.ebeaninternal.dbmigration.migration.Column; import io.ebeaninternal.dbmigration.migration.DropHistoryTable; import io.ebeaninternal.dbmigration.migration.IdentityType; import io.ebeaninternal.dbmigration.model.MTable; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,7 +77,7 @@ public class PlatformDdl { protected String dropIndexIfExists = "drop index if exists "; protected String alterColumn = "alter column"; - + protected String alterColumnSuffix = ""; protected String dropUniqueConstraint = "drop constraint"; @@ -86,7 +85,7 @@ public class PlatformDdl { protected String addConstraint = "add constraint"; protected String addColumn = "add column"; - + protected String addColumnSuffix = ""; protected String columnSetType = ""; @@ -100,11 +99,11 @@ public class PlatformDdl { protected String columnSetNull = "set null"; protected String updateNullWithDefault = "update ${table} set ${column} = ${default} where ${column} is null"; - + protected String createTable = "create table"; - + protected String dropColumn = "drop column"; - + protected String dropColumnSuffix = ""; /** @@ -674,12 +673,12 @@ public class PlatformDdl { public void unlockTables(DdlBuffer buffer, Collection tables) throws IOException { } - + /** * Returns the database-specific "create table" command prefix. For HANA this is * either "create column table" or "create row table", for all other databases * it is "create table". - * + * * @return The "create table" command prefix */ public String getCreateTableCommandPrefix() { diff --git a/src/main/java/io/ebeaninternal/server/core/BasicTypeConverter.java b/src/main/java/io/ebeaninternal/server/core/BasicTypeConverter.java index b87a1c287..dbdf6b110 100644 --- a/src/main/java/io/ebeaninternal/server/core/BasicTypeConverter.java +++ b/src/main/java/io/ebeaninternal/server/core/BasicTypeConverter.java @@ -7,7 +7,6 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Timestamp; import java.sql.Types; -import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java b/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java index 6ebc4152d..377ccb081 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java @@ -6,8 +6,10 @@ import io.ebean.meta.MetaInfoManager; import io.ebean.meta.MetaOrmQueryMetric; import io.ebean.meta.MetaOrmQueryNode; import io.ebean.meta.MetaQueryMetric; +import io.ebean.meta.MetaQueryPlan; import io.ebean.meta.MetaTimedMetric; import io.ebean.meta.MetricVisitor; +import io.ebean.meta.QueryPlanRequest; import java.util.ArrayList; import java.util.List; @@ -23,6 +25,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager { this.server = server; } + @Override + public List collectQueryPlans(QueryPlanRequest request) { + return server.collectQueryPlans(request); + } + @Override public void visitMetrics(MetricVisitor visitor) { server.visitMetrics(visitor); diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 1872c0f5b..5b9e2893c 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -54,13 +54,16 @@ import io.ebean.event.BeanPersistController; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; import io.ebean.meta.MetaInfoManager; +import io.ebean.meta.MetaQueryPlan; import io.ebean.meta.MetricVisitor; +import io.ebean.meta.QueryPlanRequest; import io.ebean.plugin.BeanType; import io.ebean.plugin.Plugin; import io.ebean.plugin.Property; import io.ebean.plugin.SpiServer; import io.ebean.text.csv.CsvReader; import io.ebean.text.json.JsonContext; +import io.ebean.util.JdbcClose; import io.ebeaninternal.api.LoadBeanRequest; import io.ebeaninternal.api.LoadManyRequest; import io.ebeaninternal.api.ScopedTransaction; @@ -120,6 +123,8 @@ import javax.persistence.NonUniqueResultException; import javax.persistence.OptimisticLockException; import javax.persistence.PersistenceException; import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; import java.time.Clock; import java.util.Collection; import java.util.Collections; @@ -2335,10 +2340,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { @Override public void slowQueryCheck(long timeMicros, int rowCount, SpiQuery query) { - if (timeMicros > slowQueryMicros) { - if (slowQueryListener != null) { - slowQueryListener.process(new SlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount, query.getParentNode())); - } + if (timeMicros > slowQueryMicros && slowQueryListener != null) { + slowQueryListener.process(new SlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount, query.getParentNode())); } } @@ -2425,4 +2428,19 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } visitor.visitEnd(); } + + public List collectQueryPlans(QueryPlanRequest request) { + Connection connection = null; + try { + connection = getDataSource().getConnection(); + request.setConnection(connection); + beanDescriptorManager.collectQueryPlans(request); + } catch (SQLException e) { + throw new RuntimeException(e); + + } finally { + JdbcClose.close(connection); + } + return request.getPlans(); + } } diff --git a/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java index c27c530d1..bfe2d756e 100644 --- a/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java @@ -15,6 +15,7 @@ import io.ebean.config.ServerConfig; import io.ebean.config.SlowQueryListener; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.DbHistorySupport; +import io.ebean.datasource.DataSourcePool; import io.ebean.event.changelog.ChangeLogListener; import io.ebean.event.changelog.ChangeLogPrepare; import io.ebean.event.changelog.ChangeLogRegister; @@ -87,7 +88,6 @@ import io.ebeanservice.docstore.api.DocStoreFactory; import io.ebeanservice.docstore.api.DocStoreIntegration; import io.ebeanservice.docstore.api.DocStoreUpdateProcessor; import io.ebeanservice.docstore.none.NoneDocStoreFactory; -import io.ebean.datasource.DataSourcePool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/ebeaninternal/server/core/MultiTenantDbCatalogSupplier.java b/src/main/java/io/ebeaninternal/server/core/MultiTenantDbCatalogSupplier.java index f1dfc07e1..936a79c3b 100644 --- a/src/main/java/io/ebeaninternal/server/core/MultiTenantDbCatalogSupplier.java +++ b/src/main/java/io/ebeaninternal/server/core/MultiTenantDbCatalogSupplier.java @@ -2,8 +2,8 @@ package io.ebeaninternal.server.core; import io.ebean.config.CurrentTenantProvider; import io.ebean.config.TenantCatalogProvider; -import io.ebeaninternal.server.transaction.DataSourceSupplier; import io.ebean.datasource.DataSourcePool; +import io.ebeaninternal.server.transaction.DataSourceSupplier; import javax.sql.DataSource; import java.io.PrintWriter; diff --git a/src/main/java/io/ebeaninternal/server/core/MultiTenantDbSchemaSupplier.java b/src/main/java/io/ebeaninternal/server/core/MultiTenantDbSchemaSupplier.java index a309f4f2f..a31f48896 100644 --- a/src/main/java/io/ebeaninternal/server/core/MultiTenantDbSchemaSupplier.java +++ b/src/main/java/io/ebeaninternal/server/core/MultiTenantDbSchemaSupplier.java @@ -2,8 +2,8 @@ package io.ebeaninternal.server.core; import io.ebean.config.CurrentTenantProvider; import io.ebean.config.TenantSchemaProvider; -import io.ebeaninternal.server.transaction.DataSourceSupplier; import io.ebean.datasource.DataSourcePool; +import io.ebeaninternal.server.transaction.DataSourceSupplier; import javax.sql.DataSource; import java.io.PrintWriter; diff --git a/src/main/java/io/ebeaninternal/server/core/SimpleDataSourceProvider.java b/src/main/java/io/ebeaninternal/server/core/SimpleDataSourceProvider.java index 6d83da262..556a5ab6d 100644 --- a/src/main/java/io/ebeaninternal/server/core/SimpleDataSourceProvider.java +++ b/src/main/java/io/ebeaninternal/server/core/SimpleDataSourceProvider.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.core; -import io.ebeaninternal.server.transaction.DataSourceSupplier; import io.ebean.datasource.DataSourcePool; +import io.ebeaninternal.server.transaction.DataSourceSupplier; import javax.sql.DataSource; import java.sql.Connection; diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 979c4cf0b..5c2f18a76 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -7,7 +7,6 @@ import io.ebean.SqlUpdate; import io.ebean.Transaction; import io.ebean.ValuePair; import io.ebean.annotation.DocStoreMode; -import io.ebean.annotation.Formula; import io.ebean.bean.BeanCollection; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; @@ -30,6 +29,7 @@ import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; import io.ebean.event.readaudit.ReadEvent; import io.ebean.meta.MetricVisitor; +import io.ebean.meta.QueryPlanRequest; import io.ebean.plugin.BeanDocType; import io.ebean.plugin.BeanType; import io.ebean.plugin.ExpressionPath; @@ -1652,6 +1652,14 @@ public class BeanDescriptor implements BeanType, STreeType { return new DeployUpdateParser(this).parse(ormUpdateStatement); } + public void collectQueryPlans(QueryPlanRequest request) { + for (CQueryPlan queryPlan : queryPlanCache.values()) { + if (request.includeLabel(queryPlan.getLabel())) { + queryPlan.collectQueryPlan(request); + } + } + } + /** * Visit all the ORM query plan metrics (includes UpdateQuery with updates and deletes). */ diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index 7a536f687..ee8087a4f 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -21,6 +21,7 @@ import io.ebean.event.changelog.ChangeLogListener; import io.ebean.event.changelog.ChangeLogPrepare; import io.ebean.event.changelog.ChangeLogRegister; import io.ebean.meta.MetricVisitor; +import io.ebean.meta.QueryPlanRequest; import io.ebean.plugin.BeanType; import io.ebean.util.AnnotationUtil; import io.ebeaninternal.api.ConcurrencyMode; @@ -1688,6 +1689,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap { } } + public void collectQueryPlans(QueryPlanRequest request) { + for (BeanDescriptor desc : immutableDescriptorList) { + if (request.includeType(desc.getBeanType())) { + desc.collectQueryPlans(request); + } + } + } + /** * Comparator to sort the BeanDescriptors by name. */ diff --git a/src/main/java/io/ebeaninternal/server/el/ElComparatorProperty.java b/src/main/java/io/ebeaninternal/server/el/ElComparatorProperty.java index d3ffb5aa2..a1093473f 100644 --- a/src/main/java/io/ebeaninternal/server/el/ElComparatorProperty.java +++ b/src/main/java/io/ebeaninternal/server/el/ElComparatorProperty.java @@ -1,9 +1,9 @@ package io.ebeaninternal.server.el; -import java.util.Comparator; - import io.ebean.bean.EntityBean; +import java.util.Comparator; + /** * Comparator based on a ElGetValue. */ diff --git a/src/main/java/io/ebeaninternal/server/el/ElPropertyChain.java b/src/main/java/io/ebeaninternal/server/el/ElPropertyChain.java index a51679b93..ee2732cbb 100644 --- a/src/main/java/io/ebeaninternal/server/el/ElPropertyChain.java +++ b/src/main/java/io/ebeaninternal/server/el/ElPropertyChain.java @@ -3,10 +3,10 @@ package io.ebeaninternal.server.el; import io.ebean.bean.EntityBean; import io.ebean.plugin.Property; import io.ebean.text.StringParser; +import io.ebean.util.SplitName; import io.ebean.util.StringHelper; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.deploy.BeanProperty; -import io.ebean.util.SplitName; import io.ebeaninternal.server.type.ScalarType; import java.util.Arrays; diff --git a/src/main/java/io/ebeaninternal/server/expression/InExpression.java b/src/main/java/io/ebeaninternal/server/expression/InExpression.java index 8e21c112f..ca0ba1730 100644 --- a/src/main/java/io/ebeaninternal/server/expression/InExpression.java +++ b/src/main/java/io/ebeaninternal/server/expression/InExpression.java @@ -2,11 +2,11 @@ package io.ebeaninternal.server.expression; import io.ebean.bean.EntityBean; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; import io.ebeaninternal.server.persist.MultiValueWrapper; -import io.ebeaninternal.api.NaturalKeyQueryData; import java.io.IOException; import java.util.ArrayList; diff --git a/src/main/java/io/ebeaninternal/server/expression/NullExpression.java b/src/main/java/io/ebeaninternal/server/expression/NullExpression.java index ba81c0bef..51ac7b27a 100644 --- a/src/main/java/io/ebeaninternal/server/expression/NullExpression.java +++ b/src/main/java/io/ebeaninternal/server/expression/NullExpression.java @@ -1,11 +1,11 @@ package io.ebeaninternal.server.expression; +import io.ebean.util.SplitName; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.el.ElPropertyValue; -import io.ebean.util.SplitName; import java.io.IOException; diff --git a/src/main/java/io/ebeaninternal/server/grammer/antlr/EQLLexer.java b/src/main/java/io/ebeaninternal/server/grammer/antlr/EQLLexer.java index dc0486a0f..7dc2d8101 100644 --- a/src/main/java/io/ebeaninternal/server/grammer/antlr/EQLLexer.java +++ b/src/main/java/io/ebeaninternal/server/grammer/antlr/EQLLexer.java @@ -1,14 +1,16 @@ // Generated from C:/dev/ebean/ebean/src/test/resources/EQL.g4 by ANTLR 4.7.1 package io.ebeaninternal.server.grammer.antlr; -import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenStream; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.LexerATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; @SuppressWarnings({ "all", "warnings", "unchecked", "unused", "cast" }) public class EQLLexer extends Lexer { @@ -292,4 +294,4 @@ public class EQLLexer extends Lexer { _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); } } -} \ No newline at end of file +} diff --git a/src/main/java/io/ebeaninternal/server/grammer/antlr/EQLParser.java b/src/main/java/io/ebeaninternal/server/grammer/antlr/EQLParser.java index 67d3d0d5b..31e012d79 100644 --- a/src/main/java/io/ebeaninternal/server/grammer/antlr/EQLParser.java +++ b/src/main/java/io/ebeaninternal/server/grammer/antlr/EQLParser.java @@ -1,14 +1,24 @@ // Generated from C:/dev/ebean/ebean/src/test/resources/EQL.g4 by ANTLR 4.7.1 package io.ebeaninternal.server.grammer.antlr; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.NoViableAltException; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ParserATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.misc.*; -import org.antlr.v4.runtime.tree.*; +import org.antlr.v4.runtime.tree.ParseTreeListener; +import org.antlr.v4.runtime.tree.TerminalNode; + import java.util.List; -import java.util.Iterator; -import java.util.ArrayList; @SuppressWarnings({ "all", "warnings", "unchecked", "unused", "cast" }) public class EQLParser extends Parser { @@ -2822,4 +2832,4 @@ public class EQLParser extends Parser { _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); } } -} \ No newline at end of file +} diff --git a/src/main/java/io/ebeaninternal/server/query/CQuery.java b/src/main/java/io/ebeaninternal/server/query/CQuery.java index cdc0a7138..3b04a99a8 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/io/ebeaninternal/server/query/CQuery.java @@ -602,7 +602,9 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran if (autoTuneProfiling) { profilingListener.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros); } - queryPlan.executionTime(loadedBeanCount, executionTimeMicros, objectGraphNode); + if (queryPlan.executionTime(loadedBeanCount, executionTimeMicros, objectGraphNode)) { + queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros); + } getTransaction().profileEvent(this); } catch (Exception e) { logger.error("Error updating execution statistics", e); diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryBindCapture.java b/src/main/java/io/ebeaninternal/server/query/CQueryBindCapture.java new file mode 100644 index 000000000..f3d1990f4 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/query/CQueryBindCapture.java @@ -0,0 +1,73 @@ +package io.ebeaninternal.server.query; + +import io.ebean.config.ServerConfig; +import io.ebean.meta.QueryPlanRequest; +import io.ebeaninternal.server.type.bindcapture.BindCapture; + +class CQueryBindCapture { + + private final double multiplier = 1.3d; + + private final CQueryPlan cQueryPlan; + private final QueryPlanLogger planLogger; + private final boolean enabled; + + private BindCapture bindCapture; + private long queryTimeMicros; + private long thresholdMicros; + private long captureCount; + + private long lastBindCapture; + + + CQueryBindCapture(CQueryPlan cQueryPlan, ServerConfig serverConfig) { + this.cQueryPlan = cQueryPlan; + this.enabled = serverConfig.isCollectQueryPlans(); + this.planLogger = PlatformQueryPlan.getLogger(serverConfig.getDatabasePlatform().getPlatform()); + } + + /** + * Return true if we should capture the bind values for this query. + */ + boolean collectFor(long timeMicros) { + return enabled && (bindCapture == null || timeMicros > thresholdMicros); + } + + /** + * Set the captured bind values that we can use later to collect a query plan. + * + * @param bindCapture The bind values of the query + * @param queryTimeMicros The query execution time + */ + void setBind(BindCapture bindCapture, long queryTimeMicros) { + synchronized (this) { + this.bindCapture = bindCapture; + this.queryTimeMicros = queryTimeMicros; + this.thresholdMicros = Math.round(queryTimeMicros * multiplier); + captureCount++; + lastBindCapture = System.currentTimeMillis(); + } + } + + + /** + * Collect the query plan using already captured bind values. + */ + void collectQueryPlan(QueryPlanRequest request) { + + if (request.getSince() > lastBindCapture) { + // no bind capture since the last capture + return; + } + + final BindCapture last = this.bindCapture; + if (last == null) { + return; + } + + DQueryPlanOutput queryPlan = planLogger.logQueryPlan(request.getConnection(), cQueryPlan, last); + queryPlan.with(queryTimeMicros, captureCount, cQueryPlan.getPlanKey().toString()); + request.process(queryPlan); + } + +} diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryBuilderRawSql.java b/src/main/java/io/ebeaninternal/server/query/CQueryBuilderRawSql.java index 9cc90b5ba..0bbee778d 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryBuilderRawSql.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryBuilderRawSql.java @@ -28,7 +28,7 @@ class CQueryBuilderRawSql { if (rsql == null) { // this is a ResultSet based RawSql query - just use some placeholder for the SQL - return new SqlLimitResponse("--ResultSetBasedRawSql", false); + return new SqlLimitResponse(CQueryPlan.RESULT_SET_BASED_RAW_SQL, false); } if (!rsql.isParsed()) { diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java b/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java index 061a35b71..7f0ea84e5 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java @@ -117,7 +117,9 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent { executionTimeMicros = (System.nanoTime() - startNano) / 1000L; request.slowQueryCheck(executionTimeMicros, rowCount); - queryPlan.executionTime(rowCount, executionTimeMicros, null); + if (queryPlan.executionTime(rowCount, executionTimeMicros, null)) { + queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros); + } getTransaction().profileEvent(this); return result; diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java b/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java index e2ad826d7..36302a8d9 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java @@ -4,6 +4,7 @@ import io.ebean.ProfileLocation; import io.ebean.bean.ObjectGraphNode; import io.ebean.config.dbplatform.SqlLimitResponse; import io.ebean.meta.MetricType; +import io.ebean.meta.QueryPlanRequest; import io.ebean.metric.MetricFactory; import io.ebean.metric.TimedMetric; import io.ebeaninternal.api.CQueryPlanKey; @@ -13,6 +14,7 @@ import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.core.timezone.DataTimeZone; import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot; import io.ebeaninternal.server.type.DataBind; +import io.ebeaninternal.server.type.DataBindCapture; import io.ebeaninternal.server.type.DataReader; import io.ebeaninternal.server.type.RsetDataReader; import io.ebeaninternal.server.type.ScalarDataReader; @@ -50,6 +52,8 @@ public class CQueryPlan { private static final Logger logger = LoggerFactory.getLogger(CQueryPlan.class); + public static final String RESULT_SET_BASED_RAW_SQL = "--ResultSetBasedRawSql"; + private final SpiEbeanServer server; private final boolean autoTuned; @@ -92,6 +96,8 @@ public class CQueryPlan { private final Set dependentTables; + private final CQueryBindCapture bindCapture; + /** * Create a query plan based on a OrmQueryRequest. */ @@ -115,6 +121,7 @@ public class CQueryPlan { this.encryptedProps = sqlTree.getEncryptedProps(); this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins()); this.dependentTables = sqlTree.dependentTables(); + this.bindCapture = new CQueryBindCapture(this, server.getServerConfig()); } /** @@ -140,6 +147,7 @@ public class CQueryPlan { this.encryptedProps = sqlTree.getEncryptedProps(); this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins()); this.dependentTables = (rawSql) ? Collections.emptySet() : sqlTree.dependentTables(); + this.bindCapture = new CQueryBindCapture(this, server.getServerConfig()); } private String location() { @@ -193,6 +201,16 @@ public class CQueryPlan { return dataBind; } + private DataBindCapture bindCapture() throws SQLException { + DataBindCapture dataBind = DataBindCapture.of(dataTimeZone); + if (encryptedProps != null) { + for (STreeProperty encryptedProp : encryptedProps) { + dataBind.setString(encryptedProp.getEncryptKeyAsString()); + } + } + return dataBind; + } + int getAsOfTableCount() { return asOfTableCount; } @@ -263,13 +281,15 @@ public class CQueryPlan { /** * Register an execution time against this query plan; */ - void executionTime(long loadedBeanCount, long timeMicros, ObjectGraphNode objectGraphNode) { + boolean executionTime(long loadedBeanCount, long timeMicros, ObjectGraphNode objectGraphNode) { stats.add(loadedBeanCount, timeMicros, objectGraphNode); if (objectGraphNode != null) { // collect stats based on objectGraphNode for lazy loading reporting server.collectQueryStats(objectGraphNode, loadedBeanCount, timeMicros); } + + return bindCapture.collectFor(timeMicros); } /** @@ -300,4 +320,22 @@ public class CQueryPlan { public TimedMetric createTimedMetric() { return MetricFactory.get().createTimedMetric(MetricType.ORM, label); } + + void captureBindForQueryPlan(CQueryPredicates predicates, long executionTimeMicros) { + try { + DataBindCapture capture = bindCapture(); + predicates.bind(capture); + bindCapture.setBind(capture.bindCapture(), executionTimeMicros); + + } catch (SQLException e) { + logger.error("Error capturing bind values", e); + } + } + + public void collectQueryPlan(QueryPlanRequest request) { + + if (!getSql().equals(RESULT_SET_BASED_RAW_SQL)) { + bindCapture.collectQueryPlan(request); + } + } } diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java b/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java index e0f9c1dbb..173cfc2be 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java @@ -125,8 +125,10 @@ class CQueryRowCount implements SpiProfileTransactionEvent { rowCount = rset.getInt(1); executionTimeMicros = (System.nanoTime() - startNano) / 1000L; - queryPlan.executionTime(rowCount, executionTimeMicros, query.getParentNode()); request.slowQueryCheck(executionTimeMicros, rowCount); + if (queryPlan.executionTime(rowCount, executionTimeMicros, query.getParentNode())) { + queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros); + } getTransaction().profileEvent(this); return rowCount; diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java b/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java index 6331c1cb6..5782ae337 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java @@ -111,7 +111,9 @@ class CQueryUpdate implements SpiProfileTransactionEvent { executionTimeMicros = (System.nanoTime() - startNano) / 1000L; request.slowQueryCheck(executionTimeMicros, rowCount); - queryPlan.executionTime(rowCount, executionTimeMicros, null); + if (queryPlan.executionTime(rowCount, executionTimeMicros, null)) { + queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros); + } getTransaction().profileEvent(this); return rowCount; diff --git a/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java b/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java new file mode 100644 index 000000000..cb68aa7b2 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java @@ -0,0 +1,108 @@ +package io.ebeaninternal.server.query; + +import io.ebean.meta.MetaQueryPlan; + +/** + * Captured query plan details. + */ +class DQueryPlanOutput implements MetaQueryPlan { + + private final Class beanType; + private final String label; + + + private final String sql; + + private final String bind; + + private final String plan; + + private String planHash; + private long queryTimeMicros; + private long captureCount; + + DQueryPlanOutput(Class beanType, String label, String sql, String bind, String plan) { + this.beanType = beanType; + this.label = label; + this.sql = sql; + this.bind = bind; + this.plan = plan; + } + + @Override + public String getQueryPlanHash() { + return planHash; + } + + /** + * Return the associated bean. + */ + @Override + public Class getBeanType() { + return beanType; + } + + /** + * Return the query label if set. + */ + @Override + public String getLabel() { + return label; + } + + /** + * Return the sql of query. + */ + @Override + public String getSql() { + return sql; + } + + /** + * Return a description of the bind values used. + */ + @Override + public String getBind() { + return bind; + } + + /** + * Return the query plan. + */ + @Override + public String getPlan() { + return plan; + } + + /** + * Return the query execution time associated with the capture of bind values used + * to build the query plan. + */ + @Override + public long getQueryTimeMicros() { + return queryTimeMicros; + } + + /** + * Return the total count of times bind capture has occurred. We don't want this to be + * massive as that implies a high overhead. + */ + @Override + public long getCaptureCount() { + return captureCount; + } + + @Override + public String toString() { + return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + planHash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan; + } + + /** + * Additionally set the query execution time and the number of bind captures. + */ + void with(long queryTimeMicros, long captureCount, String planHash) { + this.queryTimeMicros = queryTimeMicros; + this.captureCount = captureCount; + this.planHash = planHash; + } +} diff --git a/src/main/java/io/ebeaninternal/server/query/PlatformQueryPlan.java b/src/main/java/io/ebeaninternal/server/query/PlatformQueryPlan.java new file mode 100644 index 000000000..6f65622d9 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/query/PlatformQueryPlan.java @@ -0,0 +1,36 @@ +package io.ebeaninternal.server.query; + +import io.ebean.annotation.Platform; + +public final class PlatformQueryPlan { + + private static QueryPlanLogger explainLogger = new QueryPlanLoggerExplain(); + + private static QueryPlanLogger postgresLogger = new QueryPlanLoggerPostgres(); + + private static QueryPlanLogger sqlServerLogger = new QueryPlanLoggerSqlServer(); + + private static QueryPlanLogger oracleLogger = new QueryPlanLoggerOracle(); + + /** + * Returns the logger to log query plans for the given platform. + */ + public static QueryPlanLogger getLogger(Platform platform) { + + switch (platform) { + case POSTGRES: + return postgresLogger; + + case SQLSERVER: + case SQLSERVER16: + case SQLSERVER17: + return sqlServerLogger; + + case ORACLE: + return oracleLogger; + + default: + return explainLogger; + } + } +} diff --git a/src/main/java/io/ebeaninternal/server/query/QueryPlanLogger.java b/src/main/java/io/ebeaninternal/server/query/QueryPlanLogger.java new file mode 100644 index 000000000..8f51e0e67 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/query/QueryPlanLogger.java @@ -0,0 +1,47 @@ +package io.ebeaninternal.server.query; + +import io.ebeaninternal.server.type.bindcapture.BindCapture; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; + +public abstract class QueryPlanLogger { + + static final Logger queryPlanLog = LoggerFactory.getLogger(QueryPlanLogger.class); + + public abstract DQueryPlanOutput logQueryPlan(Connection conn, CQueryPlan plan, BindCapture bind); + + DQueryPlanOutput readQueryPlan(CQueryPlan plan, BindCapture bind, ResultSet rset) throws SQLException { + StringBuilder sb = new StringBuilder(); + for (int i = 1; i <= rset.getMetaData().getColumnCount(); i++) { + sb.append(rset.getMetaData().getColumnLabel(i)).append("\t"); + } + sb.setLength(sb.length() - 1); + readPlanData(sb, rset); + + return createPlan(plan, bind.toString(), sb.toString()); + } + + protected DQueryPlanOutput createPlan(CQueryPlan plan, String bind, String planString) { + return new DQueryPlanOutput(plan.getBeanType(), plan.getLabel(), plan.getSql(), bind, planString); + } + + DQueryPlanOutput readQueryPlanBasic(CQueryPlan plan, BindCapture bind, ResultSet rset) throws SQLException { + StringBuilder sb = new StringBuilder(); + readPlanData(sb, rset); + return createPlan(plan, bind.toString(), sb.toString().trim()); + } + + private void readPlanData(StringBuilder sb, ResultSet rset) throws SQLException { + while (rset.next()) { + sb.append('\n'); + for (int i = 1; i <= rset.getMetaData().getColumnCount(); i++) { + sb.append(rset.getString(i)).append("\t"); + } + sb.setLength(sb.length()-1); + } + } +} diff --git a/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerExplain.java b/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerExplain.java new file mode 100644 index 000000000..21ce968de --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerExplain.java @@ -0,0 +1,30 @@ +package io.ebeaninternal.server.query; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import io.ebeaninternal.server.type.bindcapture.BindCapture; + +/** + * A QueryPlanlogger that prefixes "EXPLAIN " to the query. This works for Postgres, H2 and MySql. + */ +public class QueryPlanLoggerExplain extends QueryPlanLogger { + + @Override + public DQueryPlanOutput logQueryPlan(Connection conn, CQueryPlan plan, BindCapture bind) { + + try (PreparedStatement explainStmt = conn.prepareStatement("EXPLAIN " + plan.getSql())) { + bind.prepare(explainStmt, conn); + try (ResultSet rset = explainStmt.executeQuery()) { + return readQueryPlan(plan, bind, rset); + } + + } catch (SQLException e) { + queryPlanLog.error("Could not log query plan", e); + } + return null; + } + +} diff --git a/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerOracle.java b/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerOracle.java new file mode 100644 index 000000000..773724880 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerOracle.java @@ -0,0 +1,37 @@ + +package io.ebeaninternal.server.query; + +import io.ebeaninternal.server.type.bindcapture.BindCapture; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * A QueryPlanlogger for oracle. + * + * @author Roland Praml, FOCONIS AG + */ +public class QueryPlanLoggerOracle extends QueryPlanLogger { + + @Override + public DQueryPlanOutput logQueryPlan(Connection conn, CQueryPlan plan, BindCapture bind) { + + try (Statement stmt = conn.createStatement()) { + try (PreparedStatement explainStmt = conn.prepareStatement("EXPLAIN PLAN FOR " + plan.getSql())) { + bind.prepare(explainStmt, conn); + explainStmt.execute(); + } + try (ResultSet rset = stmt.executeQuery("select plan_table_output from table(dbms_xplan.display())")) { + return readQueryPlan(plan, bind, rset); + } + + } catch (SQLException e) { + queryPlanLog.error("Could not log query plan", e); + } + return null; + } + +} diff --git a/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerPostgres.java b/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerPostgres.java new file mode 100644 index 000000000..bd04e0eb2 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerPostgres.java @@ -0,0 +1,31 @@ +package io.ebeaninternal.server.query; + +import io.ebeaninternal.server.type.bindcapture.BindCapture; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * A QueryPlanlogger that prefixes "EXPLAIN " to the query. This works for Postgres, H2 and MySql. + */ +public class QueryPlanLoggerPostgres extends QueryPlanLogger { + + @Override + public DQueryPlanOutput logQueryPlan(Connection conn, CQueryPlan plan, BindCapture bind) { + + String explain = "EXPLAIN " + plan.getSql(); + try (PreparedStatement explainStmt = conn.prepareStatement(explain)) { + bind.prepare(explainStmt, conn); + try (ResultSet rset = explainStmt.executeQuery()) { + return readQueryPlanBasic(plan, bind, rset); + } + + } catch (SQLException e) { + queryPlanLog.error("Could not log query plan: " + explain, e); + throw new IllegalStateException("Failed to obtain explain plan: " + explain, e); + } + } + +} diff --git a/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerSqlServer.java b/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerSqlServer.java new file mode 100644 index 000000000..9a6b82459 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerSqlServer.java @@ -0,0 +1,52 @@ +package io.ebeaninternal.server.query; + +import io.ebeaninternal.server.type.bindcapture.BindCapture; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * A QueryPlanlogger for sqlserver. It will return the plan as XML, which can be opened in + * Microsoft SQL Server Management Studio. + * + * @author Roland Praml, FOCONIS AG + */ +public class QueryPlanLoggerSqlServer extends QueryPlanLogger { + + @Override + public DQueryPlanOutput logQueryPlan(Connection conn, CQueryPlan plan, BindCapture bind) { + + try (Statement stmt = conn.createStatement()) { + stmt.execute("SET STATISTICS XML ON"); + try (PreparedStatement explainStmt = conn.prepareStatement(plan.getSql())) { + bind.prepare(explainStmt, conn); + + try (ResultSet rset = explainStmt.executeQuery()) { + // unfortunately, this will execute the + } + if (explainStmt.getMoreResults()) { + try (ResultSet rset = explainStmt.getResultSet()) { + StringBuilder sb = new StringBuilder(); + while (rset.next()) { + sb.append("XML: ").append(rset.getString(1)); + } + return createPlan(plan, bind.toString(), sb.toString()); + } + } + + } catch (SQLException e) { + queryPlanLog.error("Could not log query plan", e); + + } finally { + stmt.execute("SET STATISTICS XML OFF"); + } + } catch (SQLException e) { + queryPlanLog.error("Could not log query plan", e); + } + return null; + } + +} diff --git a/src/main/java/io/ebeaninternal/server/type/DataBind.java b/src/main/java/io/ebeaninternal/server/type/DataBind.java index b28be14b7..6c81384af 100644 --- a/src/main/java/io/ebeaninternal/server/type/DataBind.java +++ b/src/main/java/io/ebeaninternal/server/type/DataBind.java @@ -34,7 +34,7 @@ public class DataBind { private List inputStreams; - private int pos; + protected int pos; public DataBind(DataTimeZone dataTimeZone, PreparedStatement pstmt, Connection connection) { this.dataTimeZone = dataTimeZone; diff --git a/src/main/java/io/ebeaninternal/server/type/DataBindCapture.java b/src/main/java/io/ebeaninternal/server/type/DataBindCapture.java new file mode 100644 index 000000000..e87ebf471 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/type/DataBindCapture.java @@ -0,0 +1,38 @@ +package io.ebeaninternal.server.type; + +import io.ebeaninternal.server.core.timezone.DataTimeZone; +import io.ebeaninternal.server.type.bindcapture.BindCapture; +import io.ebeaninternal.server.type.bindcapture.BindCaptureStatement; + +/** + * Special DataBind used to capture bind values for obtaining explain plans. + */ +public class DataBindCapture extends DataBind { + + private final BindCaptureStatement captureStatement; + + /** + * Create given the dataTimeZone in use. + */ + public static DataBindCapture of(DataTimeZone dataTimeZone) { + return new DataBindCapture(dataTimeZone, new BindCaptureStatement()); + } + + private DataBindCapture(DataTimeZone dataTimeZone, BindCaptureStatement pstmt) { + super(dataTimeZone, pstmt, null); + this.captureStatement = pstmt; + } + + /** + * Return the bind values capture used to obtain explain plans. + */ + public BindCapture bindCapture() { + return captureStatement.bindCapture(); + } + + @Override + public void setArray(String arrayType, Object[] elements) { + captureStatement.setArray(++pos, arrayType, elements); + } + +} diff --git a/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index 896391ff5..c29afeaad 100644 --- a/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -3,7 +3,6 @@ package io.ebeaninternal.server.type; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.AnnotatedField; - import io.ebean.annotation.DbArray; import io.ebean.annotation.DbEnumType; import io.ebean.annotation.DbEnumValue; diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 91c1e1c9b..3e6b06952 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -1,11 +1,5 @@ package io.ebeaninternal.server.type; -import io.ebean.config.dbplatform.DbPlatformType; -import io.ebeaninternal.json.ModifyAwareList; -import io.ebeaninternal.json.ModifyAwareMap; -import io.ebeaninternal.json.ModifyAwareOwner; -import io.ebeaninternal.json.ModifyAwareSet; -import io.ebeanservice.docstore.api.mapping.DocPropertyType; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; @@ -17,6 +11,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.introspect.AnnotatedField; +import io.ebean.config.dbplatform.DbPlatformType; +import io.ebeaninternal.json.ModifyAwareList; +import io.ebeaninternal.json.ModifyAwareMap; +import io.ebeaninternal.json.ModifyAwareOwner; +import io.ebeaninternal.json.ModifyAwareSet; +import io.ebeanservice.docstore.api.mapping.DocPropertyType; import javax.persistence.PersistenceException; import java.io.DataInput; diff --git a/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCapture.java b/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCapture.java new file mode 100644 index 000000000..f8e27393c --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCapture.java @@ -0,0 +1,34 @@ +package io.ebeaninternal.server.type.bindcapture; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Holds bind values that can be used to obtain an explain plan. + */ +public class BindCapture { + + private final List entries = new ArrayList<>(); + + public void add(BindCaptureEntry entry) { + this.entries.add(entry); + } + + /** + * Prepare for explain plan statement execution. + */ + public void prepare(PreparedStatement explainStmt, Connection connection) throws SQLException { + for (BindCaptureEntry entry : entries) { + entry.bind(explainStmt, connection); + } + } + + @Override + public String toString() { + return entries.toString(); + } + +} diff --git a/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureEntry.java b/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureEntry.java new file mode 100644 index 000000000..3bb7d3f95 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureEntry.java @@ -0,0 +1,10 @@ +package io.ebeaninternal.server.type.bindcapture; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public interface BindCaptureEntry { + + void bind(PreparedStatement statement, Connection connection) throws SQLException; +} diff --git a/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureStatement.java b/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureStatement.java new file mode 100644 index 000000000..e8cda7b13 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureStatement.java @@ -0,0 +1,120 @@ +package io.ebeaninternal.server.type.bindcapture; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; + +/** + * Special PreparedStatement used to capture bind values used to obtain explain plans. + */ +public class BindCaptureStatement extends BindCaptureStatementBase implements PreparedStatement { + + private final BindCapture capture = new BindCapture(); + + /** + * Return the captured bind values. + */ + public BindCapture bindCapture() { + return capture; + } + + public void setArray(int parameterIndex, String arrayType, Object[] elements) { + capture.add(new BindCaptureTypes.TArray(parameterIndex, arrayType, elements)); + } + + @Override + public void setNull(int parameterIndex, int sqlType) { + capture.add(new BindCaptureTypes.Null(parameterIndex, sqlType)); + } + + @Override + public void setBoolean(int parameterIndex, boolean x) { + capture.add(new BindCaptureTypes.Boolean(parameterIndex, x)); + } + + @Override + public void setByte(int parameterIndex, byte x) { + capture.add(new BindCaptureTypes.Byte(parameterIndex, x)); + } + + @Override + public void setShort(int parameterIndex, short x) { + capture.add(new BindCaptureTypes.TShort(parameterIndex, x)); + } + + @Override + public void setInt(int parameterIndex, int x) { + capture.add(new BindCaptureTypes.TInt(parameterIndex, x)); + } + + @Override + public void setLong(int parameterIndex, long x) { + capture.add(new BindCaptureTypes.TLong(parameterIndex, x)); + } + + @Override + public void setFloat(int parameterIndex, float x) { + capture.add(new BindCaptureTypes.TFloat(parameterIndex, x)); + } + + @Override + public void setDouble(int parameterIndex, double x) { + capture.add(new BindCaptureTypes.TDouble(parameterIndex, x)); + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) { + capture.add(new BindCaptureTypes.TBigDecimal(parameterIndex, x)); + } + + @Override + public void setString(int parameterIndex, String x) { + capture.add(new BindCaptureTypes.TString(parameterIndex, x)); + } + + @Override + public void setBytes(int parameterIndex, byte[] x) { + capture.add(new BindCaptureTypes.Bytes(parameterIndex, x)); + } + + @Override + public void setDate(int parameterIndex, Date x) { + capture.add(new BindCaptureTypes.TDate(parameterIndex, x)); + } + + @Override + public void setTime(int parameterIndex, Time x) { + capture.add(new BindCaptureTypes.TTime(parameterIndex, x)); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x) { + capture.add(new BindCaptureTypes.TTimestamp(parameterIndex, x, null)); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) { + capture.add(new BindCaptureTypes.TTimestamp(parameterIndex, x, cal)); + } + + @Override + public void setObject(int parameterIndex, Object x) { + capture.add(new BindCaptureTypes.TObject(parameterIndex, x)); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) { + capture.add(new BindCaptureTypes.BinaryStream(parameterIndex)); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) { + capture.add(new BindCaptureTypes.CharacterStream(parameterIndex)); + } + +} diff --git a/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureStatementBase.java b/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureStatementBase.java new file mode 100644 index 000000000..ba4e01524 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureStatementBase.java @@ -0,0 +1,441 @@ +package io.ebeaninternal.server.type.bindcapture; + +import java.io.InputStream; +import java.io.Reader; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.Date; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Time; +import java.util.Calendar; + +abstract class BindCaptureStatementBase implements PreparedStatement { + + @Override + public ResultSet executeQuery() { + return null; + } + + @Override + public int executeUpdate() { + return 0; + } + + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) { + + } + + @Override + public void setUnicodeStream(int parameterIndex, InputStream x, int length) { + + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) { + + } + + @Override + public void clearParameters() { + + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) { + + } + + @Override + public boolean execute() { + return false; + } + + @Override + public void addBatch() { + + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) { + + } + + @Override + public void setRef(int parameterIndex, Ref x) { + + } + + @Override + public void setBlob(int parameterIndex, Blob x) { + + } + + @Override + public void setClob(int parameterIndex, Clob x) { + + } + + @Override + public void setArray(int parameterIndex, Array x) { + + } + + @Override + public ResultSetMetaData getMetaData() { + return null; + } + + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) { + + } + + @Override + public void setTime(int parameterIndex, Time x, Calendar cal) { + + } + + @Override + public void setNull(int parameterIndex, int sqlType, String typeName) { + + } + + @Override + public void setURL(int parameterIndex, URL x) { + + } + + @Override + public ParameterMetaData getParameterMetaData() { + return null; + } + + @Override + public void setRowId(int parameterIndex, RowId x) { + + } + + @Override + public void setNString(int parameterIndex, String value) { + + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value, long length) { + + } + + @Override + public void setNClob(int parameterIndex, NClob value) { + + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) { + + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) { + + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) { + + } + + @Override + public void setSQLXML(int parameterIndex, SQLXML xmlObject) { + + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) { + + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) { + + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) { + + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) { + + } + + + @Override + public void setNCharacterStream(int parameterIndex, Reader value) { + + } + + @Override + public void setClob(int parameterIndex, Reader reader) { + + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream) { + + } + + @Override + public void setNClob(int parameterIndex, Reader reader) { + + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x) { + + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x) { + + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) { + + } + + @Override + public ResultSet executeQuery(String sql) { + return null; + } + + @Override + public int executeUpdate(String sql) { + return 0; + } + + @Override + public void close() { + + } + + @Override + public int getMaxFieldSize() { + return 0; + } + + @Override + public void setMaxFieldSize(int max) { + + } + + @Override + public int getMaxRows() { + return 0; + } + + @Override + public void setMaxRows(int max) { + + } + + @Override + public void setEscapeProcessing(boolean enable) { + + } + + @Override + public int getQueryTimeout() { + return 0; + } + + @Override + public void setQueryTimeout(int seconds) { + + } + + @Override + public void cancel() { + + } + + @Override + public SQLWarning getWarnings() { + return null; + } + + @Override + public void clearWarnings() { + + } + + @Override + public void setCursorName(String name) { + + } + + @Override + public boolean execute(String sql) { + return false; + } + + @Override + public ResultSet getResultSet() { + return null; + } + + @Override + public int getUpdateCount() { + return 0; + } + + @Override + public boolean getMoreResults() { + return false; + } + + @Override + public void setFetchDirection(int direction) { + + } + + @Override + public int getFetchDirection() { + return 0; + } + + @Override + public void setFetchSize(int rows) { + + } + + @Override + public int getFetchSize() { + return 0; + } + + @Override + public int getResultSetConcurrency() { + return 0; + } + + @Override + public int getResultSetType() { + return 0; + } + + @Override + public void addBatch(String sql) { + + } + + @Override + public void clearBatch() { + + } + + @Override + public int[] executeBatch() { + return new int[0]; + } + + @Override + public Connection getConnection() { + return null; + } + + @Override + public boolean getMoreResults(int current) { + return false; + } + + @Override + public ResultSet getGeneratedKeys() { + return null; + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) { + return 0; + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) { + return 0; + } + + @Override + public int executeUpdate(String sql, String[] columnNames) { + return 0; + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) { + return false; + } + + @Override + public boolean execute(String sql, int[] columnIndexes) { + return false; + } + + @Override + public boolean execute(String sql, String[] columnNames) { + return false; + } + + @Override + public int getResultSetHoldability() { + return 0; + } + + @Override + public boolean isClosed() { + return false; + } + + @Override + public void setPoolable(boolean poolable) { + + } + + @Override + public boolean isPoolable() { + return false; + } + + @Override + public void closeOnCompletion() { + + } + + @Override + public boolean isCloseOnCompletion() { + return false; + } + + @Override + public T unwrap(Class iface) { + return null; + } + + @Override + public boolean isWrapperFor(Class iface) { + return false; + } +} diff --git a/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureTypes.java b/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureTypes.java new file mode 100644 index 000000000..661047c44 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/type/bindcapture/BindCaptureTypes.java @@ -0,0 +1,406 @@ +package io.ebeaninternal.server.type.bindcapture; + +import java.io.ByteArrayInputStream; +import java.io.StringReader; +import java.math.BigDecimal; +import java.nio.charset.Charset; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Arrays; +import java.util.Calendar; + +class BindCaptureTypes { + + static class Null implements BindCaptureEntry { + + private final int parameterIndex; + private final int sqlType; + + Null(int parameterIndex, int sqlType) { + this.parameterIndex = parameterIndex; + this.sqlType = sqlType; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setNull(parameterIndex, sqlType); + } + + @Override + public String toString() { + return "null"; + } + } + + static class Boolean implements BindCaptureEntry { + + private final int parameterIndex; + private final boolean x; + + Boolean(int parameterIndex, boolean x) { + this.parameterIndex = parameterIndex; + this.x = x; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setBoolean(parameterIndex, x); + } + + @Override + public String toString() { + return String.valueOf(x); + } + } + + static class Byte implements BindCaptureEntry { + + private final int parameterIndex; + private final byte x; + + Byte(int parameterIndex, byte x) { + this.parameterIndex = parameterIndex; + this.x = x; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setByte(parameterIndex, x); + } + + @Override + public String toString() { + return String.valueOf(x); + } + } + + static class Bytes implements BindCaptureEntry { + + private final int parameterIndex; + private final byte[] x; + + Bytes(int parameterIndex, byte[] x) { + this.parameterIndex = parameterIndex; + this.x = x; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setBytes(parameterIndex, x); + } + + @Override + public String toString() { + return String.valueOf(x); + } + } + + static class TShort implements BindCaptureEntry { + + private final int parameterIndex; + private final short value; + + TShort(int parameterIndex, short value) { + this.parameterIndex = parameterIndex; + this.value = value; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setShort(parameterIndex, value); + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + static class TInt implements BindCaptureEntry { + + private final int parameterIndex; + private final int value; + + TInt(int parameterIndex, int value) { + this.parameterIndex = parameterIndex; + this.value = value; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setInt(parameterIndex, value); + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + static class TLong implements BindCaptureEntry { + + private final int parameterIndex; + private final long value; + + TLong(int parameterIndex, long value) { + this.parameterIndex = parameterIndex; + this.value = value; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setLong(parameterIndex, value); + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + static class TFloat implements BindCaptureEntry { + + private final int parameterIndex; + private final float value; + + TFloat(int parameterIndex, float value) { + this.parameterIndex = parameterIndex; + this.value = value; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setFloat(parameterIndex, value); + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + static class TDouble implements BindCaptureEntry { + + private final int parameterIndex; + private final double value; + + TDouble(int parameterIndex, double value) { + this.parameterIndex = parameterIndex; + this.value = value; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setDouble(parameterIndex, value); + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + static class TBigDecimal implements BindCaptureEntry { + + private final int parameterIndex; + private final BigDecimal value; + + TBigDecimal(int parameterIndex, BigDecimal value) { + this.parameterIndex = parameterIndex; + this.value = value; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setBigDecimal(parameterIndex, value); + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + + static class TString implements BindCaptureEntry { + + private final int parameterIndex; + private final String value; + + TString(int parameterIndex, String value) { + this.parameterIndex = parameterIndex; + this.value = value; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setString(parameterIndex, value); + } + + @Override + public String toString() { + return value; + } + } + + static class TDate implements BindCaptureEntry { + + private final int parameterIndex; + private final Date value; + + TDate(int parameterIndex, Date value) { + this.parameterIndex = parameterIndex; + this.value = value; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setDate(parameterIndex, value); + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + static class TTime implements BindCaptureEntry { + + private final int parameterIndex; + private final Time value; + + TTime(int parameterIndex, Time value) { + this.parameterIndex = parameterIndex; + this.value = value; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setTime(parameterIndex, value); + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + static class TTimestamp implements BindCaptureEntry { + + private final int parameterIndex; + private final Timestamp value; + private final Calendar timezone; + + TTimestamp(int parameterIndex, Timestamp value, Calendar timezone) { + this.parameterIndex = parameterIndex; + this.value = value; + this.timezone = timezone; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + if (timezone == null) { + statement.setTimestamp(parameterIndex, value); + } else { + statement.setTimestamp(parameterIndex, value, timezone); + } + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + static class TObject implements BindCaptureEntry { + + private final int parameterIndex; + private final Object value; + + TObject(int parameterIndex, Object value) { + this.parameterIndex = parameterIndex; + this.value = value; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setObject(parameterIndex, value); + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + static class TArray implements BindCaptureEntry { + + private final int parameterIndex; + private final String arrayType; + private final Object[] elements; + + + TArray(int parameterIndex, String arrayType, Object[] elements) { + this.parameterIndex = parameterIndex; + this.arrayType = arrayType; + this.elements = elements; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + java.sql.Array array = connection.createArrayOf(arrayType, elements); + statement.setArray(parameterIndex, array); + } + + @Override + public String toString() { + return "Array{" + arrayType + ": " + Arrays.toString(elements) + "}"; + } + } + + static class CharacterStream implements BindCaptureEntry { + + private static final String dummy = "hi"; + + private final int parameterIndex; + + CharacterStream(int parameterIndex) { + this.parameterIndex = parameterIndex; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setCharacterStream(parameterIndex, new StringReader(dummy), dummy.length()); + } + + @Override + public String toString() { + return "charStream"; + } + } + + static class BinaryStream implements BindCaptureEntry { + + private static final byte[] dummy = "hi".getBytes(Charset.defaultCharset()); + + private final int parameterIndex; + + BinaryStream(int parameterIndex) { + this.parameterIndex = parameterIndex; + } + + @Override + public void bind(PreparedStatement statement, Connection connection) throws SQLException { + statement.setBinaryStream(parameterIndex, new ByteArrayInputStream(dummy), dummy.length); + } + + @Override + public String toString() { + return "binaryStream"; + } + } +} diff --git a/src/test/java/io/ebean/BaseTestCase.java b/src/test/java/io/ebean/BaseTestCase.java index 7fd97fa9e..c0c5bb672 100644 --- a/src/test/java/io/ebean/BaseTestCase.java +++ b/src/test/java/io/ebean/BaseTestCase.java @@ -13,7 +13,6 @@ import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.expression.platform.DbExpressionHandler; import io.ebeaninternal.server.expression.platform.DbExpressionHandlerFactory; - import org.avaje.agentloader.AgentLoader; import org.junit.runner.RunWith; import org.slf4j.Logger; @@ -148,7 +147,7 @@ public abstract class BaseTestCase { public boolean isMySql() { return Platform.MYSQL == platform(); } - + public boolean isHana() { return Platform.HANA == platform(); } @@ -160,7 +159,7 @@ public abstract class BaseTestCase { public boolean isPlatformOrderNullsSupport() { return isH2() || isPostgres(); } - + public boolean isPersistBatchOnCascade() { return spiEbeanServer().getDatabasePlatform().getPersistBatchOnCascade() != PersistBatch.NONE; } @@ -217,7 +216,7 @@ public abstract class BaseTestCase { assertThat(sql).contains(containsIn+" not in "); } } - + /** * Platform specific CONCAT clause. */ diff --git a/src/test/java/io/ebean/DtoQueryFromOrmTest.java b/src/test/java/io/ebean/DtoQueryFromOrmTest.java index 3a1adf02c..0288e4732 100644 --- a/src/test/java/io/ebean/DtoQueryFromOrmTest.java +++ b/src/test/java/io/ebean/DtoQueryFromOrmTest.java @@ -3,7 +3,6 @@ package io.ebean; import io.ebean.meta.BasicMetricVisitor; import io.ebean.meta.MetaQueryMetric; import io.ebean.meta.MetaTimedMetric; - import org.ebeantest.LoggedSqlCollector; import org.junit.AfterClass; import org.junit.BeforeClass; diff --git a/src/test/java/io/ebean/config/dbplatform/HanaHistorySupportTest.java b/src/test/java/io/ebean/config/dbplatform/HanaHistorySupportTest.java index c40ceaf41..9af554f9e 100644 --- a/src/test/java/io/ebean/config/dbplatform/HanaHistorySupportTest.java +++ b/src/test/java/io/ebean/config/dbplatform/HanaHistorySupportTest.java @@ -1,12 +1,11 @@ package io.ebean.config.dbplatform; +import io.ebean.config.dbplatform.hana.HanaHistorySupport; +import org.junit.Test; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import org.junit.Test; - -import io.ebean.config.dbplatform.hana.HanaHistorySupport; - public class HanaHistorySupportTest { private HanaHistorySupport support = new HanaHistorySupport(); @@ -24,7 +23,7 @@ public class HanaHistorySupportTest { String asOfViewSuffix = support.getAsOfViewSuffix("_with_history"); assertEquals(asOfViewSuffix, " for system_time as of ?"); } - + @Test public void getVersionsBetweenSuffix() { diff --git a/src/test/java/io/ebean/plugin/ExpressionPathTest.java b/src/test/java/io/ebean/plugin/ExpressionPathTest.java index e55598744..274cbb846 100644 --- a/src/test/java/io/ebean/plugin/ExpressionPathTest.java +++ b/src/test/java/io/ebean/plugin/ExpressionPathTest.java @@ -2,10 +2,10 @@ package io.ebean.plugin; import io.ebean.Ebean; import io.ebean.EbeanServer; +import org.junit.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.Order; import org.tests.model.basic.ResetBasicData; -import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java b/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java index 00b99161f..f30fc8eba 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java @@ -9,7 +9,6 @@ import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.Platform; import io.ebean.migration.ddl.DdlRunner; import io.ebeaninternal.dbmigration.ddlgeneration.Helper; - import org.junit.Test; import javax.persistence.PersistenceException; diff --git a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java index 911a28964..6fc27e34d 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java @@ -1,14 +1,13 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; -import static org.junit.Assert.assertEquals; - -import java.io.IOException; - -import org.junit.Test; - import io.ebean.config.dbplatform.hana.HanaPlatform; import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; import io.ebeaninternal.dbmigration.migration.Column; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; public class HanaDdlTest { diff --git a/src/test/java/io/ebeaninternal/server/expression/platform/HanaDbExpressionTest.java b/src/test/java/io/ebeaninternal/server/expression/platform/HanaDbExpressionTest.java index c67fe6de5..c2d47bea2 100644 --- a/src/test/java/io/ebeaninternal/server/expression/platform/HanaDbExpressionTest.java +++ b/src/test/java/io/ebeaninternal/server/expression/platform/HanaDbExpressionTest.java @@ -1,12 +1,11 @@ package io.ebeaninternal.server.expression.platform; -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.expression.DefaultExpressionRequest; import io.ebeaninternal.server.expression.Op; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; public class HanaDbExpressionTest { private HanaDbExpression expression = new HanaDbExpression(); @@ -54,13 +53,13 @@ public class HanaDbExpressionTest { String concat = expression.concat("property0", "separator", "property1", "suffix"); assertEquals("concat(property0, 'separator'||property1||'suffix')", concat); } - + @Test public void testConcatNullSuffix() { String concat = expression.concat("property0", "separator", "property1", null); assertEquals("concat(property0, 'separator'||property1)", concat); } - + @Test public void testJson() { SpiExpressionRequest request = new DefaultExpressionRequest(null); diff --git a/src/test/java/io/ebeaninternal/server/query/SqlTreeBuilderTest.java b/src/test/java/io/ebeaninternal/server/query/SqlTreeBuilderTest.java index 2c5beb403..241c218da 100644 --- a/src/test/java/io/ebeaninternal/server/query/SqlTreeBuilderTest.java +++ b/src/test/java/io/ebeaninternal/server/query/SqlTreeBuilderTest.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.query; import org.junit.Test; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; public class SqlTreeBuilderTest { diff --git a/src/test/java/org/tests/autofetch/TunedQueryWithNullFetchedBeanTest.java b/src/test/java/org/tests/autofetch/TunedQueryWithNullFetchedBeanTest.java index 4d20c866b..af082e386 100644 --- a/src/test/java/org/tests/autofetch/TunedQueryWithNullFetchedBeanTest.java +++ b/src/test/java/org/tests/autofetch/TunedQueryWithNullFetchedBeanTest.java @@ -6,12 +6,12 @@ import io.ebean.EbeanServer; import io.ebean.Query; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; -import org.tests.model.basic.Address; -import org.tests.model.basic.Customer; -import org.tests.model.basic.ResetBasicData; import org.ebeantest.LoggedSqlCollector; import org.junit.Assert; import org.junit.Test; +import org.tests.model.basic.Address; +import org.tests.model.basic.Customer; +import org.tests.model.basic.ResetBasicData; import java.util.List; import java.util.Set; diff --git a/src/test/java/org/tests/basic/MainDbBoolean.java b/src/test/java/org/tests/basic/MainDbBoolean.java index 47feddb82..b8d6c6f2c 100644 --- a/src/test/java/org/tests/basic/MainDbBoolean.java +++ b/src/test/java/org/tests/basic/MainDbBoolean.java @@ -6,11 +6,11 @@ import io.ebean.Query; import io.ebean.SqlRow; import io.ebean.config.ServerConfig; import io.ebean.config.dbplatform.postgres.PostgresPlatform; +import io.ebean.datasource.DataSourceConfig; +import org.junit.Assert; import org.tests.model.basic.TOne; import org.tests.model.basic.TSDetail; import org.tests.model.basic.TSMaster; -import io.ebean.datasource.DataSourceConfig; -import org.junit.Assert; import java.util.List; diff --git a/src/test/java/org/tests/basic/TestErrorBindLog.java b/src/test/java/org/tests/basic/TestErrorBindLog.java index 75b1bd7c5..481f385ee 100644 --- a/src/test/java/org/tests/basic/TestErrorBindLog.java +++ b/src/test/java/org/tests/basic/TestErrorBindLog.java @@ -2,9 +2,9 @@ package org.tests.basic; import io.ebean.BaseTestCase; import io.ebean.Ebean; -import org.tests.model.basic.Order; import org.junit.Assert; import org.junit.Test; +import org.tests.model.basic.Order; import javax.persistence.PersistenceException; diff --git a/src/test/java/org/tests/basic/TestLazyLoadEmptyOneToMany.java b/src/test/java/org/tests/basic/TestLazyLoadEmptyOneToMany.java index 0c8c29094..4135d09aa 100644 --- a/src/test/java/org/tests/basic/TestLazyLoadEmptyOneToMany.java +++ b/src/test/java/org/tests/basic/TestLazyLoadEmptyOneToMany.java @@ -2,11 +2,11 @@ package org.tests.basic; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import org.junit.Assert; +import org.junit.Test; import org.tests.model.basic.Contact; import org.tests.model.basic.Customer; import org.tests.model.basic.ResetBasicData; -import org.junit.Assert; -import org.junit.Test; import java.util.List; diff --git a/src/test/java/org/tests/basic/TestTransient.java b/src/test/java/org/tests/basic/TestTransient.java index e6c69752b..3c59ae7fd 100644 --- a/src/test/java/org/tests/basic/TestTransient.java +++ b/src/test/java/org/tests/basic/TestTransient.java @@ -3,10 +3,10 @@ package org.tests.basic; import io.ebean.BaseTestCase; import io.ebean.BeanState; import io.ebean.Ebean; -import org.tests.model.basic.Customer; -import org.tests.model.basic.ResetBasicData; import org.junit.Assert; import org.junit.Test; +import org.tests.model.basic.Customer; +import org.tests.model.basic.ResetBasicData; public class TestTransient extends BaseTestCase { diff --git a/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java b/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java index 2d4cfa3ea..a83aa13ae 100644 --- a/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java +++ b/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java @@ -6,8 +6,8 @@ import io.ebean.EbeanServer; import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.Platform; import io.ebean.annotation.Transactional; -import org.tests.model.basic.Customer; import org.junit.Test; +import org.tests.model.basic.Customer; import static org.junit.Assert.assertNotNull; diff --git a/src/test/java/org/tests/batchload/TestBasicNavOnEmpty.java b/src/test/java/org/tests/batchload/TestBasicNavOnEmpty.java index 61d098ce0..5354c7cf3 100644 --- a/src/test/java/org/tests/batchload/TestBasicNavOnEmpty.java +++ b/src/test/java/org/tests/batchload/TestBasicNavOnEmpty.java @@ -2,11 +2,11 @@ package org.tests.batchload; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import org.junit.Assert; +import org.junit.Test; import org.tests.model.basic.Contact; import org.tests.model.basic.Customer; import org.tests.model.basic.ResetBasicData; -import org.junit.Assert; -import org.junit.Test; import java.util.List; diff --git a/src/test/java/org/tests/batchload/TestEmptyManyLazyLoad.java b/src/test/java/org/tests/batchload/TestEmptyManyLazyLoad.java index 825735cf8..40be470a8 100644 --- a/src/test/java/org/tests/batchload/TestEmptyManyLazyLoad.java +++ b/src/test/java/org/tests/batchload/TestEmptyManyLazyLoad.java @@ -2,11 +2,11 @@ package org.tests.batchload; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import org.junit.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.Order; import org.tests.model.basic.Order.Status; import org.tests.model.basic.ResetBasicData; -import org.junit.Test; public class TestEmptyManyLazyLoad extends BaseTestCase { diff --git a/src/test/java/org/tests/batchload/TestSecondQueryNoRows.java b/src/test/java/org/tests/batchload/TestSecondQueryNoRows.java index 54e8d28bb..3b4151589 100644 --- a/src/test/java/org/tests/batchload/TestSecondQueryNoRows.java +++ b/src/test/java/org/tests/batchload/TestSecondQueryNoRows.java @@ -3,9 +3,9 @@ package org.tests.batchload; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.FetchConfig; +import org.junit.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.ResetBasicData; -import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; diff --git a/src/test/java/org/tests/cache/TestBeanCache.java b/src/test/java/org/tests/cache/TestBeanCache.java index 2419d3b3c..ed9aae7ad 100644 --- a/src/test/java/org/tests/cache/TestBeanCache.java +++ b/src/test/java/org/tests/cache/TestBeanCache.java @@ -2,10 +2,10 @@ package org.tests.cache; import io.ebean.BaseTestCase; import io.ebean.Ebean; -import org.tests.model.basic.Country; -import org.tests.model.basic.OCachedBean; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; +import org.tests.model.basic.Country; +import org.tests.model.basic.OCachedBean; import java.util.List; diff --git a/src/test/java/org/tests/cache/TestCacheInterceptSaveWhenLazyLoaded.java b/src/test/java/org/tests/cache/TestCacheInterceptSaveWhenLazyLoaded.java index 5ad1e93c7..992d3eabc 100644 --- a/src/test/java/org/tests/cache/TestCacheInterceptSaveWhenLazyLoaded.java +++ b/src/test/java/org/tests/cache/TestCacheInterceptSaveWhenLazyLoaded.java @@ -2,10 +2,10 @@ package org.tests.cache; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import org.junit.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.Order; import org.tests.model.basic.ResetBasicData; -import org.junit.Test; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; diff --git a/src/test/java/org/tests/changelog/TestChangeLog.java b/src/test/java/org/tests/changelog/TestChangeLog.java index f430c2769..3be63f361 100644 --- a/src/test/java/org/tests/changelog/TestChangeLog.java +++ b/src/test/java/org/tests/changelog/TestChangeLog.java @@ -1,5 +1,7 @@ package org.tests.changelog; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; import io.ebean.BaseTestCase; import io.ebean.EbeanServerFactory; import io.ebean.annotation.ChangeLog; @@ -13,12 +15,10 @@ import io.ebean.event.changelog.ChangeLogRegister; import io.ebean.event.changelog.ChangeSet; import io.ebean.event.changelog.ChangeType; import io.ebeaninternal.api.SpiEbeanServer; -import org.tests.model.basic.EBasicChangeLog; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.tests.model.basic.EBasicChangeLog; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; diff --git a/src/test/java/org/tests/delete/TestDeleteByIdWithPersistenceContext.java b/src/test/java/org/tests/delete/TestDeleteByIdWithPersistenceContext.java index eb8fb6685..6858e12d4 100644 --- a/src/test/java/org/tests/delete/TestDeleteByIdWithPersistenceContext.java +++ b/src/test/java/org/tests/delete/TestDeleteByIdWithPersistenceContext.java @@ -4,10 +4,10 @@ import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.EbeanServer; import io.ebean.Transaction; -import org.tests.model.basic.Product; -import org.tests.model.basic.ResetBasicData; import org.junit.Assert; import org.junit.Test; +import org.tests.model.basic.Product; +import org.tests.model.basic.ResetBasicData; import java.util.Arrays; diff --git a/src/test/java/org/tests/inheritance/cache/TestInheritanceRefCache.java b/src/test/java/org/tests/inheritance/cache/TestInheritanceRefCache.java index 5b7f37d06..0b39f4f03 100644 --- a/src/test/java/org/tests/inheritance/cache/TestInheritanceRefCache.java +++ b/src/test/java/org/tests/inheritance/cache/TestInheritanceRefCache.java @@ -2,18 +2,17 @@ package org.tests.inheritance.cache; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import org.ebeantest.LoggedSqlCollector; +import org.junit.Test; import org.tests.model.basic.cache.CInhOne; import org.tests.model.basic.cache.CInhRef; import org.tests.model.basic.cache.CInhRoot; import org.tests.model.basic.cache.CInhTwo; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; import java.util.List; -import org.ebeantest.LoggedSqlCollector; +import static org.assertj.core.api.Assertions.assertThat; public class TestInheritanceRefCache extends BaseTestCase { diff --git a/src/test/java/org/tests/json/TestDbJson_Jackson.java b/src/test/java/org/tests/json/TestDbJson_Jackson.java index c281444a7..00b4bfa52 100644 --- a/src/test/java/org/tests/json/TestDbJson_Jackson.java +++ b/src/test/java/org/tests/json/TestDbJson_Jackson.java @@ -1,20 +1,17 @@ package org.tests.json; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.StrictAssertions.assertThat; - -import java.io.IOException; - +import com.fasterxml.jackson.databind.ObjectMapper; +import io.ebean.BaseTestCase; +import io.ebean.Ebean; import org.junit.Test; import org.tests.model.json.EBasicJsonJackson; import org.tests.model.json.EBasicJsonJackson2; import org.tests.model.json.LongJacksonType; import org.tests.model.json.StringJacksonType; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; -import io.ebean.BaseTestCase; -import io.ebean.Ebean; +import static org.assertj.core.api.Assertions.assertThat; public class TestDbJson_Jackson extends BaseTestCase { diff --git a/src/test/java/org/tests/json/TestDbJson_Jackson2.java b/src/test/java/org/tests/json/TestDbJson_Jackson2.java index 866161cf0..f4a9a0d41 100644 --- a/src/test/java/org/tests/json/TestDbJson_Jackson2.java +++ b/src/test/java/org/tests/json/TestDbJson_Jackson2.java @@ -1,19 +1,16 @@ package org.tests.json; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.StrictAssertions.assertThat; - -import java.io.IOException; - +import com.fasterxml.jackson.databind.ObjectMapper; +import io.ebean.BaseTestCase; +import io.ebean.Ebean; import org.junit.Test; import org.tests.model.json.EBasicJsonJackson2; import org.tests.model.json.LongJacksonType; import org.tests.model.json.StringJacksonType; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; -import io.ebean.BaseTestCase; -import io.ebean.Ebean; +import static org.assertj.core.api.Assertions.assertThat; public class TestDbJson_Jackson2 extends BaseTestCase { diff --git a/src/test/java/org/tests/merge/TestMergeM2M.java b/src/test/java/org/tests/merge/TestMergeM2M.java index 784e53025..fe955b713 100644 --- a/src/test/java/org/tests/merge/TestMergeM2M.java +++ b/src/test/java/org/tests/merge/TestMergeM2M.java @@ -28,16 +28,16 @@ public class TestMergeM2M extends BaseTestCase { Ebean.save(group4); Ebean.save(group5); - MMachine machine = new MMachine("mac1"); - machine.getGroups().add(group1); - machine.getGroups().add(group2); - machine.getGroups().add(group3); + MMachine m0 = new MMachine("mac1"); + m0.getGroups().add(group1); + m0.getGroups().add(group2); + m0.getGroups().add(group3); MergeOptions options = new MergeOptionsBuilder().addPath("groups").build(); LoggedSqlCollector.start(); - Ebean.merge(machine, options); + Ebean.merge(m0, options); List sql = LoggedSqlCollector.current(); if (isPersistBatchOnCascade()) { @@ -55,13 +55,15 @@ public class TestMergeM2M extends BaseTestCase { assertThat(sql.get(4)).contains("insert into mmachine_mgroup"); } - machine.setName("mac1-mod"); - machine.getGroups().remove(group2); - machine.getGroups().remove(group3); - machine.getGroups().add(group4); - machine.getGroups().add(group5); + MMachine m1 = new MMachine("mac1"); + m1.setId(m0.getId()); + m1.setName("mac1-mod"); + m1.getGroups().remove(group2); + m1.getGroups().remove(group3); + m1.getGroups().add(group4); + m1.getGroups().add(group5); - Ebean.merge(machine, options); + Ebean.merge(m1, options); sql = LoggedSqlCollector.current(); assertThat(sql).hasSize(4); diff --git a/src/test/java/org/tests/model/array/TestDbArray_asSet.java b/src/test/java/org/tests/model/array/TestDbArray_asSet.java index b87726c53..5501eafe2 100644 --- a/src/test/java/org/tests/model/array/TestDbArray_asSet.java +++ b/src/test/java/org/tests/model/array/TestDbArray_asSet.java @@ -5,7 +5,6 @@ import io.ebean.Ebean; import io.ebean.Query; import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.Platform; - import org.ebeantest.LoggedSqlCollector; import org.junit.Test; diff --git a/src/test/java/org/tests/model/array/TestDbArray_basic.java b/src/test/java/org/tests/model/array/TestDbArray_basic.java index 4da3ada4a..f13bf3516 100644 --- a/src/test/java/org/tests/model/array/TestDbArray_basic.java +++ b/src/test/java/org/tests/model/array/TestDbArray_basic.java @@ -1,7 +1,13 @@ package org.tests.model.array; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; +import io.ebean.BaseTestCase; +import io.ebean.Ebean; +import io.ebean.Query; +import io.ebean.SqlRow; +import io.ebean.annotation.IgnorePlatform; +import io.ebean.annotation.Platform; +import org.ebeantest.LoggedSqlCollector; +import org.junit.Test; import java.sql.SQLException; import java.util.ArrayList; @@ -10,15 +16,8 @@ import java.util.List; import java.util.Set; import java.util.UUID; -import org.ebeantest.LoggedSqlCollector; -import org.junit.Test; - -import io.ebean.BaseTestCase; -import io.ebean.Ebean; -import io.ebean.Query; -import io.ebean.SqlRow; -import io.ebean.annotation.IgnorePlatform; -import io.ebean.annotation.Platform; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; public class TestDbArray_basic extends BaseTestCase { diff --git a/src/test/java/org/tests/model/basic/CKeyParent.java b/src/test/java/org/tests/model/basic/CKeyParent.java index f5e387b09..36ef22ab8 100644 --- a/src/test/java/org/tests/model/basic/CKeyParent.java +++ b/src/test/java/org/tests/model/basic/CKeyParent.java @@ -2,7 +2,6 @@ package org.tests.model.basic; import io.ebean.annotation.Cache; -import javax.persistence.Cacheable; import javax.persistence.CascadeType; import javax.persistence.EmbeddedId; import javax.persistence.Entity; diff --git a/src/test/java/org/tests/model/basic/ResetBasicData.java b/src/test/java/org/tests/model/basic/ResetBasicData.java index c0112d21a..d1b7468a3 100644 --- a/src/test/java/org/tests/model/basic/ResetBasicData.java +++ b/src/test/java/org/tests/model/basic/ResetBasicData.java @@ -5,7 +5,6 @@ import io.ebean.EbeanServer; import org.tests.model.basic.Order.Status; import java.sql.Date; -import java.time.LocalDate; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java b/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java index 1f1073116..b8b0b20e5 100644 --- a/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java +++ b/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java @@ -10,7 +10,6 @@ import io.ebean.cache.ServerCacheStatistics; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java index 13a41e66d..52bdb65d9 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java @@ -3,7 +3,6 @@ package org.tests.model.elementcollection; import io.ebean.Ebean; import io.ebean.annotation.PersistBatch; import io.ebeaninternal.api.SpiEbeanServer; - import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -90,7 +89,7 @@ public class TestElementCollectionBasicCache { LoggedSqlCollector.stop(); } - + public boolean isPersistBatchOnCascade() { return ((SpiEbeanServer) Ebean.getDefaultServer()).getDatabasePlatform().getPersistBatchOnCascade() != PersistBatch.NONE; } diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMap.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMap.java index baf2f6d86..f3c5c49e0 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMap.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMap.java @@ -2,7 +2,6 @@ package org.tests.model.elementcollection; import io.ebean.BaseTestCase; import io.ebean.Ebean; - import org.ebeantest.LoggedSqlCollector; import org.junit.Test; diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java index e92ba9324..1c5317516 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java @@ -3,7 +3,6 @@ package org.tests.model.elementcollection; import io.ebean.Ebean; import io.ebean.annotation.PersistBatch; import io.ebeaninternal.api.SpiEbeanServer; - import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -95,7 +94,7 @@ public class TestElementCollectionBasicMapCache { LoggedSqlCollector.stop(); } - + public boolean isPersistBatchOnCascade() { return ((SpiEbeanServer) Ebean.getDefaultServer()).getDatabasePlatform().getPersistBatchOnCascade() != PersistBatch.NONE; } diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java index b885fe488..b9425e59f 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java @@ -2,7 +2,6 @@ package org.tests.model.elementcollection; import io.ebean.BaseTestCase; import io.ebean.Ebean; - import org.ebeantest.LoggedSqlCollector; import org.junit.Test; diff --git a/src/test/java/org/tests/model/json/EBasicJsonJackson.java b/src/test/java/org/tests/model/json/EBasicJsonJackson.java index 9faa35eee..bdfa43add 100644 --- a/src/test/java/org/tests/model/json/EBasicJsonJackson.java +++ b/src/test/java/org/tests/model/json/EBasicJsonJackson.java @@ -1,14 +1,12 @@ package org.tests.model.json; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.ebean.annotation.DbJson; import io.ebean.annotation.DbJsonB; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Version; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashSet; diff --git a/src/test/java/org/tests/model/json/EBasicJsonJackson2.java b/src/test/java/org/tests/model/json/EBasicJsonJackson2.java index dc8269682..45a305105 100644 --- a/src/test/java/org/tests/model/json/EBasicJsonJackson2.java +++ b/src/test/java/org/tests/model/json/EBasicJsonJackson2.java @@ -1,14 +1,12 @@ package org.tests.model.json; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.ebean.annotation.DbJson; import io.ebean.annotation.DbJsonB; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Version; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashSet; diff --git a/src/test/java/org/tests/model/nofk/EFile2NoFk.java b/src/test/java/org/tests/model/nofk/EFile2NoFk.java index aee08e6fb..5d5ff5c90 100644 --- a/src/test/java/org/tests/model/nofk/EFile2NoFk.java +++ b/src/test/java/org/tests/model/nofk/EFile2NoFk.java @@ -1,17 +1,13 @@ package org.tests.model.nofk; -import java.util.List; +import io.ebean.annotation.Formula; +import io.ebean.annotation.Index; import javax.persistence.Entity; import javax.persistence.Id; -import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.validation.constraints.Size; -import io.ebean.annotation.DbForeignKey; -import io.ebean.annotation.Formula; -import io.ebean.annotation.Index; - @Entity public class EFile2NoFk { diff --git a/src/test/java/org/tests/model/nofk/EFileNoFk.java b/src/test/java/org/tests/model/nofk/EFileNoFk.java index fb48a53b0..c6823500b 100644 --- a/src/test/java/org/tests/model/nofk/EFileNoFk.java +++ b/src/test/java/org/tests/model/nofk/EFileNoFk.java @@ -1,14 +1,13 @@ package org.tests.model.nofk; -import java.util.List; +import io.ebean.annotation.DbForeignKey; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.validation.constraints.Size; - -import io.ebean.annotation.DbForeignKey; +import java.util.List; @Entity public class EFileNoFk { diff --git a/src/test/java/org/tests/model/nofk/EUserNoFk.java b/src/test/java/org/tests/model/nofk/EUserNoFk.java index 8513ad8d5..2b4e57013 100644 --- a/src/test/java/org/tests/model/nofk/EUserNoFk.java +++ b/src/test/java/org/tests/model/nofk/EUserNoFk.java @@ -1,10 +1,9 @@ package org.tests.model.nofk; -import java.util.List; - import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; +import java.util.List; @Entity public class EUserNoFk { diff --git a/src/test/java/org/tests/model/nofk/EUserNoFkSoftDel.java b/src/test/java/org/tests/model/nofk/EUserNoFkSoftDel.java index 792a004af..214dd289c 100644 --- a/src/test/java/org/tests/model/nofk/EUserNoFkSoftDel.java +++ b/src/test/java/org/tests/model/nofk/EUserNoFkSoftDel.java @@ -1,15 +1,14 @@ package org.tests.model.nofk; -import java.util.List; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.OneToMany; - import io.ebean.annotation.Formula; import io.ebean.annotation.Platform; import io.ebean.annotation.SoftDelete; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import java.util.List; + @Entity public class EUserNoFkSoftDel { diff --git a/src/test/java/org/tests/model/nofk/Test2NoFk.java b/src/test/java/org/tests/model/nofk/Test2NoFk.java index 4e963b4f3..a6851bf9b 100644 --- a/src/test/java/org/tests/model/nofk/Test2NoFk.java +++ b/src/test/java/org/tests/model/nofk/Test2NoFk.java @@ -4,13 +4,11 @@ import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; - import org.junit.Before; import org.junit.Test; -import java.util.List; - import javax.persistence.EntityNotFoundException; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/src/test/java/org/tests/model/nofk/TestNoFk.java b/src/test/java/org/tests/model/nofk/TestNoFk.java index 9d49f0666..87110ce14 100644 --- a/src/test/java/org/tests/model/nofk/TestNoFk.java +++ b/src/test/java/org/tests/model/nofk/TestNoFk.java @@ -5,16 +5,12 @@ import io.ebean.Ebean; import io.ebean.SqlRow; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; -import io.ebean.plugin.BeanType; -import io.ebean.plugin.Property; - import org.junit.Before; import org.junit.Ignore; import org.junit.Test; -import java.util.List; - import javax.persistence.EntityNotFoundException; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/src/test/java/org/tests/model/uuidsibling/USibChild.java b/src/test/java/org/tests/model/uuidsibling/USibChild.java index 47edd9fc1..7fa10caea 100644 --- a/src/test/java/org/tests/model/uuidsibling/USibChild.java +++ b/src/test/java/org/tests/model/uuidsibling/USibChild.java @@ -4,13 +4,13 @@ import io.ebean.Finder; import io.ebean.Model; import io.ebean.annotation.SoftDelete; -import java.io.Serializable; -import java.util.UUID; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; +import java.io.Serializable; +import java.util.UUID; @Entity public class USibChild extends Model implements Serializable { diff --git a/src/test/java/org/tests/model/uuidsibling/USibChildSibling.java b/src/test/java/org/tests/model/uuidsibling/USibChildSibling.java index 03e39b5f7..152ba6212 100644 --- a/src/test/java/org/tests/model/uuidsibling/USibChildSibling.java +++ b/src/test/java/org/tests/model/uuidsibling/USibChildSibling.java @@ -4,11 +4,10 @@ import io.ebean.Finder; import io.ebean.Model; import io.ebean.annotation.SoftDelete; -import java.io.Serializable; - import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToOne; +import java.io.Serializable; @Entity public class USibChildSibling extends Model implements Serializable { diff --git a/src/test/java/org/tests/model/uuidsibling/USibParent.java b/src/test/java/org/tests/model/uuidsibling/USibParent.java index 30597f5ad..7f8332c6f 100644 --- a/src/test/java/org/tests/model/uuidsibling/USibParent.java +++ b/src/test/java/org/tests/model/uuidsibling/USibParent.java @@ -4,12 +4,12 @@ import io.ebean.Finder; import io.ebean.Model; import io.ebean.annotation.SoftDelete; -import java.io.Serializable; -import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; +import java.io.Serializable; +import java.util.List; @Entity public class USibParent extends Model implements Serializable { diff --git a/src/test/java/org/tests/model/view/TestViewBaseEntity.java b/src/test/java/org/tests/model/view/TestViewBaseEntity.java index 63c94a244..a0560ff98 100644 --- a/src/test/java/org/tests/model/view/TestViewBaseEntity.java +++ b/src/test/java/org/tests/model/view/TestViewBaseEntity.java @@ -3,10 +3,10 @@ package org.tests.model.view; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Query; +import org.junit.Test; import org.tests.model.basic.Order; import org.tests.model.basic.OrderDetail; import org.tests.model.basic.ResetBasicData; -import org.junit.Test; import java.util.List; diff --git a/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTable.java b/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTable.java index e18c9e9ba..98e405a36 100644 --- a/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTable.java +++ b/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTable.java @@ -2,7 +2,6 @@ package org.tests.o2m.jointable; import io.ebean.BaseTestCase; import io.ebean.Ebean; - import org.ebeantest.LoggedSqlCollector; import org.junit.Test; diff --git a/src/test/java/org/tests/query/TestOuterJoin.java b/src/test/java/org/tests/query/TestOuterJoin.java index b2f8e6ae1..5256bcc1b 100644 --- a/src/test/java/org/tests/query/TestOuterJoin.java +++ b/src/test/java/org/tests/query/TestOuterJoin.java @@ -1,19 +1,17 @@ package org.tests.query; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.*; - -import java.util.List; - +import io.ebean.BaseTestCase; +import io.ebean.Ebean; +import io.ebeantest.LoggedSql; import org.junit.BeforeClass; import org.junit.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.Order; import org.tests.model.basic.ResetBasicData; -import io.ebean.BaseTestCase; -import io.ebean.Ebean; -import io.ebeantest.LoggedSql; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; /** * Tests if outer joins are correctly used. diff --git a/src/test/java/org/tests/query/TestQueryFilterCaseInsensitive.java b/src/test/java/org/tests/query/TestQueryFilterCaseInsensitive.java index 8ddf3f181..8d606cc4b 100644 --- a/src/test/java/org/tests/query/TestQueryFilterCaseInsensitive.java +++ b/src/test/java/org/tests/query/TestQueryFilterCaseInsensitive.java @@ -2,10 +2,10 @@ package org.tests.query; import io.ebean.BaseTestCase; import io.ebean.Ebean; -import org.tests.model.basic.Customer; -import org.tests.model.basic.ResetBasicData; import org.junit.BeforeClass; import org.junit.Test; +import org.tests.model.basic.Customer; +import org.tests.model.basic.ResetBasicData; import java.util.List; diff --git a/src/test/java/org/tests/query/TestQueryFindIterate.java b/src/test/java/org/tests/query/TestQueryFindIterate.java index 47c20eb73..ccca68cdf 100644 --- a/src/test/java/org/tests/query/TestQueryFindIterate.java +++ b/src/test/java/org/tests/query/TestQueryFindIterate.java @@ -5,8 +5,8 @@ import io.ebean.Ebean; import io.ebean.EbeanServer; import io.ebean.Query; import io.ebean.QueryIterator; -import io.ebean.plugin.SpiServer; import io.ebean.datasource.DataSourcePool; +import io.ebean.plugin.SpiServer; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; import org.tests.model.basic.Customer; diff --git a/src/test/java/org/tests/query/TestQueryFindPagedList.java b/src/test/java/org/tests/query/TestQueryFindPagedList.java index 97775405a..293b4d519 100644 --- a/src/test/java/org/tests/query/TestQueryFindPagedList.java +++ b/src/test/java/org/tests/query/TestQueryFindPagedList.java @@ -3,10 +3,10 @@ package org.tests.query; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.PagedList; -import org.tests.model.basic.Order; -import org.tests.model.basic.ResetBasicData; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; +import org.tests.model.basic.Order; +import org.tests.model.basic.ResetBasicData; import javax.persistence.PersistenceException; import java.util.List; @@ -16,7 +16,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class TestQueryFindPagedList extends BaseTestCase { diff --git a/src/test/java/org/tests/query/aggregation/TestAggregationCount.java b/src/test/java/org/tests/query/aggregation/TestAggregationCount.java index fc578780e..65231e8d1 100644 --- a/src/test/java/org/tests/query/aggregation/TestAggregationCount.java +++ b/src/test/java/org/tests/query/aggregation/TestAggregationCount.java @@ -1,12 +1,8 @@ package org.tests.query.aggregation; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.StrictAssertions.assertThat; - -import java.sql.Timestamp; -import java.time.Instant; -import java.util.List; - +import io.ebean.BaseTestCase; +import io.ebean.Ebean; +import io.ebean.Query; import org.ebeantest.LoggedSqlCollector; import org.junit.BeforeClass; import org.junit.Test; @@ -17,9 +13,11 @@ import org.tests.model.basic.ResetBasicData; import org.tests.model.tevent.TEventMany; import org.tests.model.tevent.TEventOne; -import io.ebean.BaseTestCase; -import io.ebean.Ebean; -import io.ebean.Query; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; public class TestAggregationCount extends BaseTestCase { diff --git a/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/src/test/java/org/tests/query/finder/TestCustomerFinder.java index 63e41a954..26c2f1b4c 100644 --- a/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -5,7 +5,9 @@ import io.ebean.Ebean; import io.ebean.Transaction; import io.ebean.meta.BasicMetricVisitor; import io.ebean.meta.MetaOrmQueryMetric; +import io.ebean.meta.MetaQueryPlan; import io.ebean.meta.MetaTimedMetric; +import io.ebean.meta.QueryPlanRequest; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; import org.tests.model.basic.Customer; @@ -163,6 +165,14 @@ public class TestCustomerFinder extends BaseTestCase { for (MetaTimedMetric txnTimed : basic.getTimedMetrics()) { System.out.println(txnTimed); } + + QueryPlanRequest request = new QueryPlanRequest(); + + List plans = server().getMetaInfoManager().collectQueryPlans(request); + for (MetaQueryPlan plan : plans) { + System.out.println(plan); + } + } } diff --git a/src/test/java/org/tests/query/orderby/TestOrderByWithDistinct.java b/src/test/java/org/tests/query/orderby/TestOrderByWithDistinct.java index 9d2ece465..a1c9d7767 100644 --- a/src/test/java/org/tests/query/orderby/TestOrderByWithDistinct.java +++ b/src/test/java/org/tests/query/orderby/TestOrderByWithDistinct.java @@ -3,13 +3,13 @@ package org.tests.query.orderby; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Query; +import org.junit.Assert; +import org.junit.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.MRole; import org.tests.model.basic.MUser; import org.tests.model.basic.MUserType; import org.tests.model.basic.ResetBasicData; -import org.junit.Assert; -import org.junit.Test; import java.util.List; import java.util.Set; diff --git a/src/test/java/org/tests/query/other/TestFindWhereUsingEnumerated.java b/src/test/java/org/tests/query/other/TestFindWhereUsingEnumerated.java index 45a97264f..274b5eeb4 100644 --- a/src/test/java/org/tests/query/other/TestFindWhereUsingEnumerated.java +++ b/src/test/java/org/tests/query/other/TestFindWhereUsingEnumerated.java @@ -2,10 +2,9 @@ package org.tests.query.other; import io.ebean.Ebean; import io.ebean.TransactionalTestCase; - +import org.junit.Test; import org.tests.model.basic.MNonEnum; import org.tests.model.basic.MNonUpdPropEntity; -import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/java/org/tests/text/csv/TestCsvReader.java b/src/test/java/org/tests/text/csv/TestCsvReader.java index de4a0d4c5..73df33c30 100644 --- a/src/test/java/org/tests/text/csv/TestCsvReader.java +++ b/src/test/java/org/tests/text/csv/TestCsvReader.java @@ -3,9 +3,9 @@ package org.tests.text.csv; import io.ebean.Ebean; import io.ebean.TransactionalTestCase; import io.ebean.text.csv.CsvReader; +import org.junit.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.ResetBasicData; -import org.junit.Test; import java.io.File; import java.io.FileReader; diff --git a/src/test/java/org/tests/text/csv/TestCsvReaderWithCallback.java b/src/test/java/org/tests/text/csv/TestCsvReaderWithCallback.java index 45cae1f70..0d8a6bd76 100644 --- a/src/test/java/org/tests/text/csv/TestCsvReaderWithCallback.java +++ b/src/test/java/org/tests/text/csv/TestCsvReaderWithCallback.java @@ -5,16 +5,16 @@ import io.ebean.EbeanServer; import io.ebean.TransactionalTestCase; import io.ebean.text.csv.CsvReader; import io.ebean.text.csv.DefaultCsvCallback; -import org.tests.model.basic.Customer; import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; +import org.tests.model.basic.Customer; import java.io.File; import java.io.FileReader; import java.net.URL; import java.util.Locale; +import static org.assertj.core.api.Assertions.assertThat; + public class TestCsvReaderWithCallback extends TransactionalTestCase { @Test diff --git a/src/test/java/org/tests/text/json/TestTextJsonInsertUpdate.java b/src/test/java/org/tests/text/json/TestTextJsonInsertUpdate.java index 06bfc9c11..6f510b7e2 100644 --- a/src/test/java/org/tests/text/json/TestTextJsonInsertUpdate.java +++ b/src/test/java/org/tests/text/json/TestTextJsonInsertUpdate.java @@ -4,9 +4,9 @@ import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.text.json.JsonContext; import io.ebean.util.StringHelper; +import org.junit.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.ResetBasicData; -import org.junit.Test; import java.io.IOException; diff --git a/src/test/java/org/tests/types/TestPeriodType.java b/src/test/java/org/tests/types/TestPeriodType.java index ef753550e..7d7dc995a 100644 --- a/src/test/java/org/tests/types/TestPeriodType.java +++ b/src/test/java/org/tests/types/TestPeriodType.java @@ -2,8 +2,8 @@ package org.tests.types; import io.ebean.BaseTestCase; import io.ebean.Ebean; -import org.tests.model.types.SomePeriodBean; import org.junit.Test; +import org.tests.model.types.SomePeriodBean; import java.sql.Date; import java.time.LocalDate; diff --git a/src/test/java/org/tests/unitinternal/HelloMain.java b/src/test/java/org/tests/unitinternal/HelloMain.java index 5efeba5ea..ca43af917 100644 --- a/src/test/java/org/tests/unitinternal/HelloMain.java +++ b/src/test/java/org/tests/unitinternal/HelloMain.java @@ -3,11 +3,11 @@ package org.tests.unitinternal; import io.ebean.EbeanServer; import io.ebean.EbeanServerFactory; import io.ebean.config.ServerConfig; -import org.tests.model.basic.TOne; -import org.avaje.agentloader.AgentLoader; import io.ebean.datasource.DataSourceConfig; +import org.avaje.agentloader.AgentLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.tests.model.basic.TOne; import java.io.File; diff --git a/src/test/java/org/tests/update/TestUpdatePartial.java b/src/test/java/org/tests/update/TestUpdatePartial.java index 48624e35a..2d0c4466c 100644 --- a/src/test/java/org/tests/update/TestUpdatePartial.java +++ b/src/test/java/org/tests/update/TestUpdatePartial.java @@ -4,8 +4,8 @@ import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.SqlQuery; import io.ebean.SqlRow; -import org.tests.model.basic.Customer; import org.junit.Test; +import org.tests.model.basic.Customer; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index d7a9263cc..cd6b5c07c 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -22,6 +22,8 @@ ebean.ddl.header=-- Generated by ebean ${version} at ${timestamp} ebean.packages=org.tests datasource.default=h2 +ebean.collectQueryPlans=true + ebean.autoReadOnlyDataSource=true #ebean.persistBatch=NONE