mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4509c6b1ed | ||
|
|
d32559baf4 | ||
|
|
2639e799c5 | ||
|
|
46c66b7169 | ||
|
|
30ae435f33 | ||
|
|
4f4743807c | ||
|
|
7b7a492241 | ||
|
|
4af8e53315 | ||
|
|
1c6fa575f2 | ||
|
|
2430404e1f | ||
|
|
d433e0ea46 | ||
|
|
42591a5a87 | ||
|
|
65a62bf683 | ||
|
|
3e3f3c35e7 | ||
|
|
f4db7ca0bb | ||
|
|
b0e487cfbd | ||
|
|
23fd29ddf1 | ||
|
|
f21cac5d4f | ||
|
|
c2e5619f70 | ||
|
|
18e0ff7067 | ||
|
|
4119c02f0d | ||
|
|
8985d37aed | ||
|
|
23e035894f | ||
|
|
32b8fd0e26 | ||
|
|
4971db54c8 | ||
|
|
e417b3cde2 | ||
|
|
d23ca31e42 | ||
|
|
c9b71ae373 | ||
|
|
f06f21cf5e | ||
|
|
f8b97ff397 | ||
|
|
fc032c632b | ||
|
|
54534ea7f3 | ||
|
|
812dcbb4bb | ||
|
|
a3d4b57b94 | ||
|
|
61479d365a | ||
|
|
3ba9a52b0e | ||
|
|
518ba37fb2 | ||
|
|
fd6a7f64eb | ||
|
|
8b073ef10a | ||
|
|
6a7555055d | ||
|
|
eb31f1bfce | ||
|
|
2e4f99318a | ||
|
|
409c1d06b2 | ||
|
|
01f2881940 | ||
|
|
aef512a851 | ||
|
|
435e1f61fe | ||
|
|
0230e3e253 | ||
|
|
770364fd1a |
@@ -1,6 +1,29 @@
|
||||
[](https://travis-ci.org/ebean-orm/ebean)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean)
|
||||
|
||||
# Sponsors
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.foconis.de/" target="_blank">
|
||||
<img width="222px" src="https://www.foconis.de/templates/yootheme/cache/foconis_logo_322-709da1de.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.payintech.com/" target="_blank">
|
||||
<img width="222px" src="https://www.payintech.com/wp-content/uploads/2015/10/PayinTech-logo-noir.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.premium-minds.com" target="_blank">
|
||||
<img width="222px" src="https://ebean.io/images/logo-premium-minds.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
# Need help?
|
||||
Post questions or issues to the Ebean google group - https://groups.google.com/forum/#!forum/ebean
|
||||
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.36.1</version>
|
||||
<version>11.37.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
<url>http://ebean-orm.github.io/</url>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.9.5</jackson-core.version>
|
||||
<jackson-core.version>2.9.8</jackson-core.version>
|
||||
<snakeyaml.version>1.21</snakeyaml.version>
|
||||
</properties>
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.36.1</tag>
|
||||
<tag>ebean-11.37.1</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -111,13 +111,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>persistence-api</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<version>2.2.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>4.6</version>
|
||||
<version>4.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -250,7 +250,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-agent</artifactId>
|
||||
<version>11.27.1</version>
|
||||
<version>11.37.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -330,7 +330,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>11.27.1</version>
|
||||
<version>11.37.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -109,6 +109,12 @@ public interface DtoQuery<T> {
|
||||
*/
|
||||
DtoQuery<T> setLabel(String label);
|
||||
|
||||
/**
|
||||
* Set the profile location of this query. This is used to relate query execution metrics
|
||||
* back to a location like a specific line of code.
|
||||
*/
|
||||
DtoQuery<T> setProfileLocation(ProfileLocation profileLocation);
|
||||
|
||||
/**
|
||||
* Set a timeout on this query.
|
||||
* <p>
|
||||
|
||||
@@ -40,6 +40,11 @@ public interface ProfileLocation {
|
||||
*/
|
||||
String shortDescription();
|
||||
|
||||
/**
|
||||
* Return the short label.
|
||||
*/
|
||||
String label();
|
||||
|
||||
/**
|
||||
* Add execution time.
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,7 @@ package io.ebean.config;
|
||||
*/
|
||||
public class AutoTuneConfig {
|
||||
|
||||
private AutoTuneMode mode = AutoTuneMode.DEFAULT_ON;
|
||||
private AutoTuneMode mode = AutoTuneMode.DEFAULT_OFF;
|
||||
|
||||
private String queryTuningFile = "ebean-autotune.xml";
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.migration.MigrationRunner;
|
||||
import io.ebean.util.StringHelper;
|
||||
|
||||
import javax.persistence.EnumType;
|
||||
import javax.sql.DataSource;
|
||||
import java.time.Clock;
|
||||
import java.util.ArrayList;
|
||||
@@ -229,6 +230,8 @@ public class ServerConfig {
|
||||
|
||||
private int persistBatchSize = 20;
|
||||
|
||||
private EnumType defaultEnumType = EnumType.ORDINAL;
|
||||
|
||||
private boolean disableLazyLoading;
|
||||
|
||||
/**
|
||||
@@ -521,6 +524,10 @@ public class ServerConfig {
|
||||
*/
|
||||
private boolean idGeneratorAutomatic = true;
|
||||
|
||||
private boolean dumpMetricsOnShutdown;
|
||||
|
||||
private String dumpMetricsOptions;
|
||||
|
||||
/**
|
||||
* Construct a Database Configuration for programmatically creating an Database.
|
||||
*/
|
||||
@@ -978,6 +985,14 @@ public class ServerConfig {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
}
|
||||
|
||||
public EnumType getDefaultEnumType() {
|
||||
return defaultEnumType;
|
||||
}
|
||||
|
||||
public void setDefaultEnumType(EnumType defaultEnumType) {
|
||||
this.defaultEnumType = defaultEnumType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if lazy loading is disabled on queries by default.
|
||||
*/
|
||||
@@ -2892,6 +2907,8 @@ public class ServerConfig {
|
||||
}
|
||||
loadDocStoreSettings(p);
|
||||
|
||||
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
|
||||
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
|
||||
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
|
||||
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
|
||||
collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans);
|
||||
@@ -2963,6 +2980,7 @@ public class ServerConfig {
|
||||
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
|
||||
jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);
|
||||
|
||||
defaultEnumType = p.getEnum(EnumType.class, "defaultEnumType", defaultEnumType);
|
||||
disableLazyLoading = p.getBoolean("disableLazyLoading", disableLazyLoading);
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
|
||||
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
|
||||
@@ -3275,6 +3293,36 @@ public class ServerConfig {
|
||||
this.collectQueryPlans = collectQueryPlans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if metrics should be dumped when the server is shutdown.
|
||||
*/
|
||||
public boolean isDumpMetricsOnShutdown() {
|
||||
return dumpMetricsOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if metrics should be dumped when the server is shutdown.
|
||||
*/
|
||||
public void setDumpMetricsOnShutdown(boolean dumpMetricsOnShutdown) {
|
||||
this.dumpMetricsOnShutdown = dumpMetricsOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the options for dumping metrics.
|
||||
*/
|
||||
public String getDumpMetricsOptions() {
|
||||
return dumpMetricsOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include 'sql' or 'hash' in options such that they are included in the output.
|
||||
*
|
||||
* @param dumpMetricsOptions Example "sql,hash", "sql"
|
||||
*/
|
||||
public void setDumpMetricsOptions(String dumpMetricsOptions) {
|
||||
this.dumpMetricsOptions = dumpMetricsOptions;
|
||||
}
|
||||
|
||||
public enum UuidVersion {
|
||||
VERSION4,
|
||||
VERSION1,
|
||||
|
||||
@@ -49,6 +49,8 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected boolean useExtraTransactionOnIterateSecondaryQueries;
|
||||
|
||||
protected boolean supportsDeleteTableAlias;
|
||||
|
||||
/**
|
||||
* The behaviour used when ending a read only transaction at read committed isolation level.
|
||||
*/
|
||||
@@ -176,10 +178,10 @@ public class DatabasePlatform {
|
||||
* findIterate() and findVisit().
|
||||
*/
|
||||
protected boolean forwardOnlyHintOnFindIterate;
|
||||
|
||||
|
||||
/**
|
||||
* If set then use the CONCUR_UPDATABLE hint when creating ResultSets.
|
||||
*
|
||||
*
|
||||
* This is {@code false} for HANA
|
||||
*/
|
||||
protected boolean supportsResultSetConcurrencyModeUpdatable = true;
|
||||
@@ -306,6 +308,13 @@ public class DatabasePlatform {
|
||||
return supportsNativeIlike;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the platform supports delete statements with table alias.
|
||||
*/
|
||||
public boolean isSupportsDeleteTableAlias() {
|
||||
return supportsDeleteTableAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum table name length.
|
||||
* <p>
|
||||
@@ -524,7 +533,7 @@ public class DatabasePlatform {
|
||||
public void setForwardOnlyHintOnFindIterate(boolean forwardOnlyHintOnFindIterate) {
|
||||
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the ResultSet CONCUR_UPDATABLE Hint should be used on
|
||||
* createNativeSqlTree() PreparedStatements.
|
||||
@@ -535,7 +544,7 @@ public class DatabasePlatform {
|
||||
public boolean isSupportsResultSetConcurrencyModeUpdatable() {
|
||||
return supportsResultSetConcurrencyModeUpdatable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to true if the ResultSet CONCUR_UPDATABLE Hint should be used by default on createNativeSqlTree() PreparedStatements.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.ebean.config.dbplatform.clickhouse;
|
||||
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
public class ClickHousePlatform extends DatabasePlatform {
|
||||
|
||||
public ClickHousePlatform() {
|
||||
super();
|
||||
this.platform = Platform.CLICKHOUSE;
|
||||
//this.dbEncrypt =
|
||||
//this.historySupport =
|
||||
//this.exceptionTranslator =
|
||||
|
||||
this.nativeUuidType = true;
|
||||
this.dbDefaultValue.setNow("now()");
|
||||
this.columnAliasPrefix = null;
|
||||
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
this.dbIdentity.setSupportsSequence(false);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
|
||||
this.booleanDbType = Types.INTEGER;
|
||||
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("UInt8"));
|
||||
|
||||
// using unsigned as default types ...
|
||||
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("UInt8", false));
|
||||
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("UInt16", false));
|
||||
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("UInt32", false));
|
||||
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("UInt64", false));
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("Decimal", 16,4));
|
||||
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("Float64", false));
|
||||
|
||||
dbTypeMap.put(DbType.DATE, new DbPlatformType("Date", false));
|
||||
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("DateTime", false));
|
||||
|
||||
dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.CLOB, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.JSONVARCHAR, new DbPlatformType("String", false));
|
||||
|
||||
dbTypeMap.put(DbType.UUID, new DbPlatformType("UUID", false));
|
||||
dbTypeMap.put(DbType.INET, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.CDIR, new DbPlatformType("String", false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNativeArrayType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public class H2Platform extends DatabasePlatform {
|
||||
this.dbEncrypt = new H2DbEncrypt();
|
||||
this.historySupport = new H2HistorySupport();
|
||||
this.nativeUuidType = true;
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.dbDefaultValue.setNow("now()");
|
||||
this.columnAliasPrefix = null;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
public OraclePlatform() {
|
||||
super();
|
||||
this.platform = Platform.ORACLE;
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.maxTableNameLength = 30;
|
||||
this.maxConstraintNameLength = 30;
|
||||
this.dbEncrypt = new OracleDbEncrypt();
|
||||
|
||||
@@ -30,6 +30,7 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
super();
|
||||
this.platform = Platform.POSTGRES;
|
||||
this.supportsNativeIlike = true;
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.selectCountWithAlias = true;
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
this.clobDbType = Types.VARCHAR;
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.util.List;
|
||||
/**
|
||||
* A simple MetricVisitor that can collect the desired metrics into lists.
|
||||
*/
|
||||
public class BasicMetricVisitor extends AbstractMetricVisitor {
|
||||
public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerMetrics {
|
||||
|
||||
private final List<MetaTimedMetric> timed = new ArrayList<>();
|
||||
private final List<MetaQueryMetric> dtoQuery = new ArrayList<>();
|
||||
@@ -29,6 +29,7 @@ public class BasicMetricVisitor extends AbstractMetricVisitor {
|
||||
/**
|
||||
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
|
||||
*/
|
||||
@Override
|
||||
public List<MetaTimedMetric> getTimedMetrics() {
|
||||
return timed;
|
||||
}
|
||||
@@ -36,6 +37,7 @@ public class BasicMetricVisitor extends AbstractMetricVisitor {
|
||||
/**
|
||||
* Return the DTO query metrics.
|
||||
*/
|
||||
@Override
|
||||
public List<MetaQueryMetric> getDtoQueryMetrics() {
|
||||
return dtoQuery;
|
||||
}
|
||||
@@ -43,6 +45,7 @@ public class BasicMetricVisitor extends AbstractMetricVisitor {
|
||||
/**
|
||||
* Return the ORM query metrics.
|
||||
*/
|
||||
@Override
|
||||
public List<MetaOrmQueryMetric> getOrmQueryMetrics() {
|
||||
return ormQuery;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@ import java.util.List;
|
||||
*/
|
||||
public interface MetaInfoManager {
|
||||
|
||||
/**
|
||||
* Return the metrics for the database instance.
|
||||
* <p>
|
||||
* This will reset the metrics (reset counters back to zero etc) and
|
||||
* will only return the non-empty metrics.
|
||||
* </p>
|
||||
*/
|
||||
ServerMetrics collectMetrics();
|
||||
|
||||
/**
|
||||
* Collect query plans.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -9,11 +7,6 @@ import java.util.List;
|
||||
*/
|
||||
public interface MetaOrmQueryMetric extends MetaQueryMetric {
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Return true if this query plan was tuned by AutoTune.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
/**
|
||||
* Query execution metrics.
|
||||
*/
|
||||
@@ -15,6 +17,11 @@ public interface MetaQueryMetric extends MetaTimedMetric {
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* The actual SQL of the query.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Metrics of the Database instance.
|
||||
*/
|
||||
public interface ServerMetrics {
|
||||
|
||||
/**
|
||||
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
|
||||
*/
|
||||
List<MetaTimedMetric> getTimedMetrics();
|
||||
|
||||
/**
|
||||
* Return the DTO query metrics.
|
||||
*/
|
||||
List<MetaQueryMetric> getDtoQueryMetrics();
|
||||
|
||||
/**
|
||||
* Return the ORM query metrics.
|
||||
*/
|
||||
List<MetaOrmQueryMetric> getOrmQueryMetrics();
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Comparator for timed metrics sorted by name and then count.
|
||||
*/
|
||||
public class SortMetric {
|
||||
|
||||
public static final Comparator<MetaTimedMetric> NAME = new Name();
|
||||
public static final Comparator<MetaTimedMetric> COUNT = new Count();
|
||||
public static final Comparator<MetaTimedMetric> TOTAL = new Total();
|
||||
public static final Comparator<MetaTimedMetric> MEAN = new Mean();
|
||||
public static final Comparator<MetaTimedMetric> MAX = new Max();
|
||||
|
||||
/**
|
||||
* Sort by name.
|
||||
*/
|
||||
public static class Name implements Comparator<MetaTimedMetric> {
|
||||
|
||||
@Override
|
||||
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
|
||||
String name = o1.getName();
|
||||
String name2 = o2.getName();
|
||||
if (name == null) {
|
||||
return name2 == null ? 0 : -1;
|
||||
}
|
||||
if (name2 == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int i = name.compareTo(name2);
|
||||
return i != 0 ? i : Long.compare(o1.getCount(), o2.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by count desc.
|
||||
*/
|
||||
public static class Count implements Comparator<MetaTimedMetric> {
|
||||
|
||||
@Override
|
||||
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
|
||||
return Long.compare(o2.getCount(), o1.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by total time desc.
|
||||
*/
|
||||
public static class Total implements Comparator<MetaTimedMetric> {
|
||||
|
||||
@Override
|
||||
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
|
||||
return Long.compare(o2.getTotal(), o1.getTotal());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by mean desc.
|
||||
*/
|
||||
public static class Mean implements Comparator<MetaTimedMetric> {
|
||||
|
||||
@Override
|
||||
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
|
||||
return Long.compare(o2.getMean(), o1.getMean());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by max desc.
|
||||
*/
|
||||
public static class Max implements Comparator<MetaTimedMetric> {
|
||||
|
||||
@Override
|
||||
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
|
||||
return Long.compare(o2.getMax(), o1.getMax());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebean.metric;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetricType;
|
||||
|
||||
/**
|
||||
@@ -27,6 +28,6 @@ public interface MetricFactory {
|
||||
/**
|
||||
* Create a Timed metric.
|
||||
*/
|
||||
QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, String sql);
|
||||
QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, ProfileLocation profileLocation, String sql);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.DtoQuery;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebeaninternal.server.dto.DtoMappingRequest;
|
||||
import io.ebeaninternal.server.dto.DtoQueryPlan;
|
||||
|
||||
@@ -37,9 +38,23 @@ public interface SpiDtoQuery<T> extends DtoQuery<T>, SpiSqlBinding {
|
||||
/**
|
||||
* Return the label for the query.
|
||||
*/
|
||||
@Override
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the label with fallback to profile location label.
|
||||
*/
|
||||
String getPlanLabel();
|
||||
|
||||
/**
|
||||
* Obtain the location if necessary.
|
||||
*/
|
||||
void obtainLocation();
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Return the associated DTO bean type.
|
||||
*/
|
||||
|
||||
@@ -54,67 +54,69 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
/**
|
||||
* Find by Id or unique returning a single bean.
|
||||
*/
|
||||
BEAN(FIND_ONE),
|
||||
BEAN(FIND_ONE, "byId"),
|
||||
|
||||
/**
|
||||
* Find returning a List.
|
||||
*/
|
||||
LIST(FIND_MANY),
|
||||
LIST(FIND_MANY, "findList"),
|
||||
|
||||
/**
|
||||
* Find returning a Set.
|
||||
*/
|
||||
SET(FIND_MANY),
|
||||
SET(FIND_MANY, "findSet"),
|
||||
|
||||
/**
|
||||
* Find returning a Map.
|
||||
*/
|
||||
MAP(FIND_MANY),
|
||||
MAP(FIND_MANY, "findMap"),
|
||||
|
||||
/**
|
||||
* Find iterate type query - findEach(), findIterate() etc.
|
||||
*/
|
||||
ITERATE(FIND_ITERATE),
|
||||
ITERATE(FIND_ITERATE, "findEach"),
|
||||
|
||||
/**
|
||||
* Find the Id's.
|
||||
*/
|
||||
ID_LIST(FIND_ID_LIST),
|
||||
ID_LIST(FIND_ID_LIST, "findIds"),
|
||||
|
||||
/**
|
||||
* Find single attribute.
|
||||
*/
|
||||
ATTRIBUTE(FIND_ATTRIBUTE),
|
||||
ATTRIBUTE(FIND_ATTRIBUTE, "findAttribute"),
|
||||
|
||||
/**
|
||||
* Find rowCount.
|
||||
*/
|
||||
COUNT(FIND_COUNT),
|
||||
COUNT(FIND_COUNT, "findCount"),
|
||||
|
||||
/**
|
||||
* A subquery used as part of a where clause.
|
||||
*/
|
||||
SUBQUERY(FIND_SUBQUERY),
|
||||
SUBQUERY(FIND_SUBQUERY, "subquery"),
|
||||
|
||||
/**
|
||||
* Delete query.
|
||||
*/
|
||||
DELETE(FIND_DELETE, true),
|
||||
DELETE(FIND_DELETE, "delete", true),
|
||||
|
||||
/**
|
||||
* Update query.
|
||||
*/
|
||||
UPDATE(FIND_UPDATE, true);
|
||||
UPDATE(FIND_UPDATE, "update", true);
|
||||
|
||||
boolean update;
|
||||
String profileEventId;
|
||||
private boolean update;
|
||||
private String profileEventId;
|
||||
private String label;
|
||||
|
||||
Type(String profileEventId) {
|
||||
this(profileEventId, false);
|
||||
Type(String profileEventId, String label) {
|
||||
this(profileEventId, label, false);
|
||||
}
|
||||
|
||||
Type(String profileEventId, boolean update) {
|
||||
Type(String profileEventId, String label, boolean update) {
|
||||
this.profileEventId = profileEventId;
|
||||
this.label = label;
|
||||
this.update = update;
|
||||
}
|
||||
|
||||
@@ -128,6 +130,10 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
public String profileEventId() {
|
||||
return profileEventId;
|
||||
}
|
||||
|
||||
public String label() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
enum TemporalMode {
|
||||
@@ -197,6 +203,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the label manually set on the query or from the profile location.
|
||||
*/
|
||||
String getPlanLabel();
|
||||
|
||||
/**
|
||||
* Return true if this is a "find by id" query. This includes a check for a single "equal to" expression for the Id.
|
||||
*/
|
||||
|
||||
@@ -69,11 +69,6 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
void registerDeleteBean(Integer hash);
|
||||
|
||||
/**
|
||||
* Unregister the hash of the bean.
|
||||
*/
|
||||
void unregisterDeleteBean(Integer hash);
|
||||
|
||||
/**
|
||||
* Return true if this is a bean that has already been saved/deleted.
|
||||
*/
|
||||
|
||||
@@ -183,11 +183,6 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
transaction.registerDeleteBean(hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterDeleteBean(Integer hash) {
|
||||
transaction.unregisterDeleteBean(hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegisteredDeleteBean(Integer hash) {
|
||||
return transaction.isRegisteredDeleteBean(hash);
|
||||
|
||||
@@ -189,7 +189,7 @@ public class DdlGenerator {
|
||||
runScript(connection, false, createAllContent, getCreateFileName());
|
||||
|
||||
if (extraDdl && jaxbPresent) {
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
if (currentModel().isTablePartitioning()) {
|
||||
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
|
||||
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
|
||||
runScript(connection, false, extraPartitioning, "builtin-partitioning-ddl");
|
||||
@@ -201,7 +201,7 @@ public class DdlGenerator {
|
||||
runScript(connection, false, extraApply, "extra-ddl");
|
||||
}
|
||||
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
if (currentModel().isTablePartitioning()) {
|
||||
checkInitialTablePartitions(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import io.ebean.config.DbMigrationConfig;
|
||||
import io.ebean.config.PlatformConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform;
|
||||
import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
|
||||
import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.config.dbplatform.hana.HanaPlatform;
|
||||
@@ -802,6 +804,11 @@ public class DefaultDbMigration implements DbMigration {
|
||||
return new SQLitePlatform();
|
||||
case HANA:
|
||||
return new HanaPlatform();
|
||||
case COCKROACH:
|
||||
return new CockroachPlatform();
|
||||
case CLICKHOUSE:
|
||||
return new ClickHousePlatform();
|
||||
|
||||
case GENERIC:
|
||||
return new DatabasePlatform();
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ public interface DdlBuffer {
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Append a statement allowing for null or empty statements.
|
||||
*/
|
||||
DdlBuffer appendStatement(String content) throws IOException;
|
||||
|
||||
/**
|
||||
* Append DDL content to the buffer.
|
||||
*/
|
||||
|
||||
+12
-1
@@ -38,6 +38,15 @@ public class BaseDdlBuffer implements DdlBuffer {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DdlBuffer appendStatement(String content) throws IOException {
|
||||
if (content != null && !content.isEmpty()) {
|
||||
writer.append(content);
|
||||
endOfStatement();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DdlBuffer append(String content) throws IOException {
|
||||
writer.append(content);
|
||||
@@ -73,7 +82,9 @@ public class BaseDdlBuffer implements DdlBuffer {
|
||||
*/
|
||||
@Override
|
||||
public DdlBuffer end() throws IOException {
|
||||
writer.append("\n");
|
||||
if (!isEmpty()) {
|
||||
writer.append("\n");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
+61
-143
@@ -37,6 +37,8 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns.split;
|
||||
|
||||
/**
|
||||
* Base implementation for 'create table' and 'alter table' statements.
|
||||
*/
|
||||
@@ -148,8 +150,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
buffer.append("-- NOTE: table has @History - special migration may be necessary").newLine();
|
||||
}
|
||||
for (String ddlScript : before) {
|
||||
buffer.append(translate(ddlScript, tableName, columnName, this.defaultValue));
|
||||
buffer.endOfStatement();
|
||||
buffer.appendStatement(translate(ddlScript, tableName, columnName, this.defaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +160,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
// here we run post migration scripts
|
||||
for (String ddlScript : after) {
|
||||
buffer.append(translate(ddlScript, tableName, columnName, defaultValue));
|
||||
buffer.endOfStatement();
|
||||
buffer.appendStatement(translate(ddlScript, tableName, columnName, defaultValue));
|
||||
}
|
||||
if (!after.isEmpty()) {
|
||||
buffer.end();
|
||||
@@ -262,7 +262,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
DdlBuffer apply = writer.apply();
|
||||
apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(tableName).append(" (");
|
||||
writeTableColumns(apply, columns, useIdentity);
|
||||
writeCheckConstraints(apply, createTable);
|
||||
writeUniqueConstraints(apply, createTable);
|
||||
writeCompoundUniqueConstraints(apply, createTable);
|
||||
if (!pk.isEmpty()) {
|
||||
@@ -276,6 +275,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
|
||||
apply.newLine().append(")");
|
||||
addTableStorageEngine(apply, createTable);
|
||||
addTableCommentInline(apply, createTable);
|
||||
if (partitionMode != null) {
|
||||
platformDdl.addTablePartition(apply, partitionMode, createTable.getPartitionColumn());
|
||||
@@ -316,12 +316,11 @@ public class BaseTableDdl implements TableDdl {
|
||||
private void addComments(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
if (!platformDdl.isInlineComments()) {
|
||||
String tableComment = createTable.getComment();
|
||||
if (!StringHelper.isNull(tableComment)) {
|
||||
if (hasValue(tableComment)) {
|
||||
platformDdl.addTableComment(apply, createTable.getName(), tableComment);
|
||||
}
|
||||
|
||||
List<Column> columns = createTable.getColumn();
|
||||
for (Column column : columns) {
|
||||
for (Column column : createTable.getColumn()) {
|
||||
if (!StringHelper.isNull(column.getComment())) {
|
||||
platformDdl.addColumnComment(apply, createTable.getName(), column.getName(), column.getComment());
|
||||
}
|
||||
@@ -329,6 +328,15 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the table storage engine clause.
|
||||
*/
|
||||
private void addTableStorageEngine(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
if (platformDdl.isIncludeStorageEngine()) {
|
||||
platformDdl.tableStorageEngine(apply, createTable.getStorageEngine());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the table comment inline with the create table statement.
|
||||
*/
|
||||
@@ -358,26 +366,17 @@ public class BaseTableDdl implements TableDdl {
|
||||
uqName = col.getUnique();
|
||||
}
|
||||
String[] columnNames = {col.getName()};
|
||||
write.apply()
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, Boolean.TRUE.equals(col.isNotnull()) ? null : columnNames))
|
||||
.endOfStatement();
|
||||
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, tableName))
|
||||
.endOfStatement();
|
||||
write.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, Boolean.TRUE.equals(col.isNotnull()) ? null : columnNames));
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName));
|
||||
}
|
||||
|
||||
for (UniqueConstraint constraint : externalCompoundUnique) {
|
||||
String uqName = constraint.getName();
|
||||
String[] columnNames = SplitColumns.split(constraint.getColumnNames());
|
||||
String[] nullableColumns = SplitColumns.split(constraint.getNullableColumns());
|
||||
write.apply()
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns))
|
||||
.endOfStatement();
|
||||
String[] columnNames = split(constraint.getColumnNames());
|
||||
String[] nullableColumns = split(constraint.getNullableColumns());
|
||||
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, tableName))
|
||||
.endOfStatement();
|
||||
write.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns));
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,9 +393,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
|
||||
String createSeq = platformDdl.createSequence(seqName, initial, allocate);
|
||||
if (createSeq != null) {
|
||||
if (hasValue(createSeq)) {
|
||||
writer.apply().append(createSeq).newLine();
|
||||
writer.dropAll().append(platformDdl.dropSequence(seqName)).endOfStatement();
|
||||
writer.dropAll().appendStatement(platformDdl.dropSequence(seqName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,8 +424,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeInlineCompoundForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
|
||||
|
||||
List<ForeignKey> foreignKey = createTable.getForeignKey();
|
||||
for (ForeignKey key : foreignKey) {
|
||||
for (ForeignKey key : createTable.getForeignKey()) {
|
||||
String fkConstraint = platformDdl.tableInlineForeignKey(new WriteForeignKey(null, key));
|
||||
write.apply().append(",").newLine().append(" ").append(fkConstraint);
|
||||
}
|
||||
@@ -434,12 +432,10 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeAddForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
|
||||
|
||||
String tableName = createTable.getName();
|
||||
List<Column> columns = createTable.getColumn();
|
||||
for (Column column : columns) {
|
||||
for (Column column : createTable.getColumn()) {
|
||||
String references = column.getReferences();
|
||||
if (hasValue(references)) {
|
||||
writeForeignKey(write, tableName, column);
|
||||
writeForeignKey(write, createTable.getName(), column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,11 +444,8 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeAddCompoundForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
|
||||
|
||||
String tableName = createTable.getName();
|
||||
|
||||
List<ForeignKey> foreignKey = createTable.getForeignKey();
|
||||
for (ForeignKey key : foreignKey) {
|
||||
writeForeignKey(write, new WriteForeignKey(tableName, key));
|
||||
for (ForeignKey key : createTable.getForeignKey()) {
|
||||
writeForeignKey(write, new WriteForeignKey(createTable.getName(), key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,19 +459,15 @@ public class BaseTableDdl implements TableDdl {
|
||||
String tableName = lowerTableName(request.table());
|
||||
if (request.indexName() != null) {
|
||||
// no matching unique constraint so add the index
|
||||
fkeyBuffer.append(platformDdl.createIndex(request.indexName(), tableName, request.cols())).endOfStatement();
|
||||
fkeyBuffer.appendStatement(platformDdl.createIndex(request.indexName(), tableName, request.cols()));
|
||||
}
|
||||
|
||||
alterTableAddForeignKey(fkeyBuffer, request);
|
||||
|
||||
fkeyBuffer.end();
|
||||
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.alterTableDropForeignKey(tableName, request.fkName())).endOfStatement();
|
||||
|
||||
if (request.indexName() != null) {
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(request.indexName(), tableName)).endOfStatement();
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.alterTableDropForeignKey(tableName, request.fkName()));
|
||||
if (hasValue(request.indexName())) {
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(request.indexName(), tableName));
|
||||
}
|
||||
|
||||
write.dropAllForeignKeys().end();
|
||||
@@ -486,10 +475,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void alterTableAddForeignKey(DdlBuffer buffer, WriteForeignKey request) throws IOException {
|
||||
|
||||
String fkConstraint = platformDdl.alterTableAddForeignKey(request);
|
||||
if (fkConstraint != null && !fkConstraint.isEmpty()) {
|
||||
buffer.append(fkConstraint).endOfStatement();
|
||||
}
|
||||
buffer.appendStatement(platformDdl.alterTableAddForeignKey(request));
|
||||
}
|
||||
|
||||
protected void appendColumns(String[] columns, DdlBuffer buffer) throws IOException {
|
||||
@@ -503,13 +489,12 @@ public class BaseTableDdl implements TableDdl {
|
||||
buffer.append(")");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add 'drop table' statement to the buffer.
|
||||
*/
|
||||
protected void dropTable(DdlBuffer buffer, String tableName) throws IOException {
|
||||
|
||||
buffer.append(platformDdl.dropTable(tableName)).endOfStatement();
|
||||
buffer.appendStatement(platformDdl.dropTable(tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -517,43 +502,16 @@ public class BaseTableDdl implements TableDdl {
|
||||
*/
|
||||
protected void dropSequence(DdlBuffer buffer, String sequenceName) throws IOException {
|
||||
|
||||
buffer.append(platformDdl.dropSequence(sequenceName)).endOfStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write all the check constraints.
|
||||
*/
|
||||
protected void writeCheckConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
|
||||
List<Column> columns = createTable.getColumn();
|
||||
for (Column column : columns) {
|
||||
String checkConstraint = column.getCheckConstraint();
|
||||
if (hasValue(checkConstraint)) {
|
||||
writeCheckConstraint(apply, column, checkConstraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a check constraint.
|
||||
*/
|
||||
protected void writeCheckConstraint(DdlBuffer buffer, Column column, String checkConstraint) throws IOException {
|
||||
|
||||
String ckName = column.getCheckConstraintName();
|
||||
|
||||
buffer.append(",").newLine();
|
||||
buffer.append(" constraint ").append(ckName);
|
||||
buffer.append(" ").append(checkConstraint);
|
||||
buffer.appendStatement(platformDdl.dropSequence(sequenceName));
|
||||
}
|
||||
|
||||
protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
|
||||
List<UniqueConstraint> uniqueConstraints = createTable.getUniqueConstraint();
|
||||
boolean inlineUniqueWhenNull = platformDdl.isInlineUniqueWhenNullable();
|
||||
for (UniqueConstraint uniqueConstraint : uniqueConstraints) {
|
||||
for (UniqueConstraint uniqueConstraint : createTable.getUniqueConstraint()) {
|
||||
if (inlineUniqueWhenNull) {
|
||||
String uqName = uniqueConstraint.getName();
|
||||
String[] columns = SplitColumns.split(uniqueConstraint.getColumnNames());
|
||||
String[] columns = split(uniqueConstraint.getColumnNames());
|
||||
apply.append(",").newLine();
|
||||
apply.append(" constraint ").append(uqName).append(" unique");
|
||||
appendColumns(columns, apply);
|
||||
@@ -650,54 +608,34 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, CreateIndex createIndex) throws IOException {
|
||||
|
||||
String[] cols = SplitColumns.split(createIndex.getColumns());
|
||||
writer.apply()
|
||||
.append(platformDdl.createIndex(createIndex.getIndexName(), createIndex.getTableName(), cols))
|
||||
.endOfStatement();
|
||||
|
||||
writer.dropAll()
|
||||
.append(platformDdl.dropIndex(createIndex.getIndexName(), createIndex.getTableName()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.createIndex(createIndex.getIndexName(), createIndex.getTableName(), split(createIndex.getColumns())));
|
||||
writer.dropAll().appendStatement(platformDdl.dropIndex(createIndex.getIndexName(), createIndex.getTableName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, DropIndex dropIndex) throws IOException {
|
||||
|
||||
writer.apply()
|
||||
.append(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, AddUniqueConstraint constraint) throws IOException {
|
||||
|
||||
if (DdlHelp.isDropConstraint(constraint.getColumnNames())) {
|
||||
String ddl = platformDdl.alterTableDropUniqueConstraint(constraint.getTableName(), constraint.getConstraintName());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(constraint.getTableName(), constraint.getConstraintName()));
|
||||
|
||||
} else {
|
||||
String[] cols = SplitColumns.split(constraint.getColumnNames());
|
||||
String[] nullableColumns = SplitColumns.split(constraint.getNullableColumns());
|
||||
String ddl = platformDdl.alterTableAddUniqueConstraint(constraint.getTableName(), constraint.getConstraintName(), cols, nullableColumns);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
String[] cols = split(constraint.getColumnNames());
|
||||
String[] nullableColumns = split(constraint.getNullableColumns());
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(constraint.getTableName(), constraint.getConstraintName(), cols, nullableColumns));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, AlterForeignKey alterForeignKey) throws IOException {
|
||||
if (DdlHelp.isDropForeignKey(alterForeignKey.getColumnNames())) {
|
||||
String ddl = platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName()));
|
||||
} else {
|
||||
String ddl = platformDdl.alterTableAddForeignKey(new WriteForeignKey(alterForeignKey));
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddForeignKey(new WriteForeignKey(alterForeignKey)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -907,7 +845,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
String ddl = platformDdl.alterColumnBaseAttributes(alter);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
writer.apply().appendStatement(ddl);
|
||||
|
||||
if (isTrue(alter.isWithHistory()) && alter.getType() != null && historySupport == HistorySupport.TRIGGER_BASED) {
|
||||
// mysql and sql server column type change allowing nulls in the history table column
|
||||
@@ -919,53 +857,41 @@ public class BaseTableDdl implements TableDdl {
|
||||
String histColumnDdl = platformDdl.alterColumnBaseAttributes(alterHistoryColumn);
|
||||
|
||||
// write the apply to history table
|
||||
writer.apply().append(histColumnDdl).endOfStatement();
|
||||
writer.apply().appendStatement(histColumnDdl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void alterColumnDefaultValue(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue()));
|
||||
}
|
||||
|
||||
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter, String constraintName) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName));
|
||||
}
|
||||
|
||||
protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint()));
|
||||
}
|
||||
|
||||
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull()));
|
||||
}
|
||||
|
||||
protected void alterColumnType(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
writer.apply().appendStatement(ddl);
|
||||
if (isTrue(alter.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
|
||||
regenerateHistoryTriggers(alter.getTableName(), HistoryTableUpdate.Change.ALTER, alter.getColumnName());
|
||||
// apply same type change to matching column in the history table
|
||||
ddl = platformDdl.alterColumnType(historyTable(alter.getTableName()), alter.getColumnName(), alter.getType());
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
writer.apply().appendStatement(ddl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -977,17 +903,13 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply()
|
||||
.append(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()));
|
||||
}
|
||||
|
||||
|
||||
protected void alterColumnDropUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply()
|
||||
.append(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()));
|
||||
}
|
||||
|
||||
protected void alterColumnAddUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
@@ -1004,13 +926,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
String[] cols = {alter.getColumnName()};
|
||||
boolean notNull = alter.isNotnull() != null ? alter.isNotnull() : Boolean.TRUE.equals(alter.isNotnull());
|
||||
writer.apply()
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols));
|
||||
|
||||
writer.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, alter.getTableName()))
|
||||
.endOfStatement();
|
||||
writer.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, alter.getTableName()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Logical array type to ClickHouse array type conversion.
|
||||
*/
|
||||
class ClickHouseDbArray {
|
||||
|
||||
private static final Map<String, String> mapping = new HashMap<>();
|
||||
static {
|
||||
mapping.put("uuid[]", "Array(UUID)");
|
||||
mapping.put("varchar[]", "Array(String)");
|
||||
mapping.put("integer[]", "Array(UInt32)");
|
||||
mapping.put("bigint[]", "Array(UInt64)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Covert the 'logical' array type to a native one (for Postgres and Cockroach).
|
||||
*/
|
||||
static String logicalToNative(String logicalArrayType) {
|
||||
int colonPos = logicalArrayType.indexOf(':');
|
||||
if (colonPos > -1) {
|
||||
logicalArrayType = logicalArrayType.substring(0, colonPos);
|
||||
}
|
||||
String clickHouseType = mapping.get(logicalArrayType);
|
||||
if (clickHouseType == null) {
|
||||
throw new IllegalStateException("No mapping for logical array type " + logicalArrayType);
|
||||
}
|
||||
return clickHouseType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ClickHouseDdl extends PlatformDdl {
|
||||
|
||||
private static final String LOG_TABLE = "ENGINE = Log()";
|
||||
|
||||
public ClickHouseDdl(DatabasePlatform platform) {
|
||||
super(platform);
|
||||
this.includeStorageEngine = true;
|
||||
this.identitySuffix = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DdlHandler createDdlHandler(ServerConfig serverConfig) {
|
||||
return new ClickHouseDdlHandler(serverConfig, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String convertArrayType(String logicalArrayType) {
|
||||
return ClickHouseDbArray.logicalToNative(logicalArrayType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an table storage engine to the create table statement.
|
||||
*/
|
||||
@Override
|
||||
public void tableStorageEngine(DdlBuffer apply, String storageEngine) throws IOException {
|
||||
if (storageEngine == null) {
|
||||
// default to Log() table but really should all be explicit (need arguments for MergeTree etc)
|
||||
storageEngine = LOG_TABLE;
|
||||
}
|
||||
apply.append(" ").append(storageEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableAddForeignKey(WriteForeignKey request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableDropForeignKey(String tableName, String fkName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tableInlineForeignKey(WriteForeignKey request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dropIndex(String indexName, String tableName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createIndex(String indexName, String tableName, String[] columns) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createCheckConstraint(String ckName, String checkConstraint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeColumnNotNull(DdlBuffer buffer) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInlineComments() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
|
||||
|
||||
public class ClickHouseDdlHandler extends BaseDdlHandler {
|
||||
|
||||
public ClickHouseDdlHandler(ServerConfig serverConfig, PlatformDdl platformDdl) {
|
||||
super(serverConfig, platformDdl, new ClickHouseTableDdl(serverConfig, platformDdl));
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.migration.CreateTable;
|
||||
|
||||
public class ClickHouseTableDdl extends BaseTableDdl {
|
||||
|
||||
public ClickHouseTableDdl(ServerConfig serverConfig, PlatformDdl platformDdl) {
|
||||
super(serverConfig, platformDdl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writePrimaryKeyConstraint(DdlBuffer buffer, String pkName, String[] pkColumns) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,10 @@ import java.util.Collection;
|
||||
*/
|
||||
public class MySqlDdl extends PlatformDdl {
|
||||
|
||||
// check constraint support is disabled by default. See https://groups.google.com/forum/#!topic/ebean/luFN-2xBkUw
|
||||
// this flag is for compatibility. Use it with care.
|
||||
private static final boolean USE_CHECK_CONSTRAINT = Boolean.getBoolean("ebean.mysql.useCheckConstraint");
|
||||
|
||||
public MySqlDdl(DatabasePlatform platform) {
|
||||
super(platform);
|
||||
this.alterColumn = "modify";
|
||||
@@ -48,11 +52,40 @@ public class MySqlDdl extends PlatformDdl {
|
||||
buffer.append("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')").endOfStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createCheckConstraint(String ckName, String checkConstraint) {
|
||||
if (USE_CHECK_CONSTRAINT) {
|
||||
return super.createCheckConstraint(ckName, checkConstraint);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
|
||||
if (USE_CHECK_CONSTRAINT) {
|
||||
return super.alterTableAddCheckConstraint(tableName, checkConstraintName, checkConstraint);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableDropConstraint(String tableName, String constraintName) {
|
||||
// drop constraint not supported in MySQL 5.7 and 8.0 but starting with MariaDB 10.2.1 CHECK is evaluated
|
||||
// TODO: Implement for MariaDB >= 10.2.1
|
||||
return null;
|
||||
// drop constraint not supported in MySQL 5.7 and 8.0 but starting with MariaDB
|
||||
// 10.2.1 CHECK is evaluated
|
||||
if (USE_CHECK_CONSTRAINT) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// statement for MySQL >= 8.0.16
|
||||
sb.append("/*!80016 alter table ").append(tableName);
|
||||
sb.append(" drop check ").append(constraintName).append(" */;\n");
|
||||
// statement for MariaDB >= 10.2.1
|
||||
sb.append("/*M!100201 ");
|
||||
sb.append(super.alterTableDropConstraint(tableName, constraintName));
|
||||
sb.append(" */");
|
||||
return sb.toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,7 +137,6 @@ public class MySqlDdl extends PlatformDdl {
|
||||
}
|
||||
buffer.append(String.format(" comment '%s'", comment));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+71
-18
@@ -118,6 +118,8 @@ public class PlatformDdl {
|
||||
*/
|
||||
protected boolean inlineForeignKeys;
|
||||
|
||||
protected boolean includeStorageEngine;
|
||||
|
||||
protected final DbDefaultValue dbDefaultValue;
|
||||
|
||||
protected String fallbackArrayType = "varchar(1000)";
|
||||
@@ -191,6 +193,13 @@ public class PlatformDdl {
|
||||
return inlineComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the platform includes storage engine clause.
|
||||
*/
|
||||
public boolean isIncludeStorageEngine() {
|
||||
return includeStorageEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if foreign key reference constraints need to inlined with create table.
|
||||
* Ideally we don't do this as then the constraints are not named. Do this for SQLite.
|
||||
@@ -204,10 +213,21 @@ public class PlatformDdl {
|
||||
*/
|
||||
public void writeTableColumns(DdlBuffer apply, List<Column> columns, boolean useIdentity) throws IOException {
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
if (i > 0) {
|
||||
apply.append(",");
|
||||
}
|
||||
apply.newLine();
|
||||
writeColumnDefinition(apply, columns.get(i), useIdentity);
|
||||
if (i < columns.size() - 1) {
|
||||
apply.append(",");
|
||||
}
|
||||
|
||||
for (Column column : columns) {
|
||||
String checkConstraint = column.getCheckConstraint();
|
||||
if (hasValue(checkConstraint)) {
|
||||
checkConstraint = createCheckConstraint(maxConstraintName(column.getCheckConstraintName()), checkConstraint);
|
||||
if (hasValue(checkConstraint)) {
|
||||
apply.append(",").newLine();
|
||||
apply.append(checkConstraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,13 +250,27 @@ public class PlatformDdl {
|
||||
}
|
||||
}
|
||||
if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) {
|
||||
buffer.append(" not null");
|
||||
writeColumnNotNull(buffer);
|
||||
}
|
||||
|
||||
// add check constraints later as we really want to give them a nice name
|
||||
// so that the database can potentially provide a nice SQL error
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow for platform overriding (e.g. ClickHouse).
|
||||
*/
|
||||
protected void writeColumnNotNull(DdlBuffer buffer) throws IOException {
|
||||
buffer.append(" not null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the check constraint.
|
||||
*/
|
||||
public String createCheckConstraint(String ckName, String checkConstraint) {
|
||||
return " constraint " + ckName + " " + checkConstraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the DB column default literal to platform specific.
|
||||
*/
|
||||
@@ -409,13 +443,17 @@ public class PlatformDdl {
|
||||
}
|
||||
|
||||
protected String translate(ConstraintMode mode) {
|
||||
switch(mode) {
|
||||
case SET_NULL: return "set null";
|
||||
case SET_DEFAULT: return "set default";
|
||||
case RESTRICT: return "restrict";
|
||||
case CASCADE: return "cascade";
|
||||
switch (mode) {
|
||||
case SET_NULL:
|
||||
return "set null";
|
||||
case SET_DEFAULT:
|
||||
return "set default";
|
||||
case RESTRICT:
|
||||
return "restrict";
|
||||
case CASCADE:
|
||||
return "cascade";
|
||||
default:
|
||||
throw new IllegalStateException("Unknown mode "+mode);
|
||||
throw new IllegalStateException("Unknown mode " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,16 +503,17 @@ public class PlatformDdl {
|
||||
|
||||
if (!onHistoryTable) {
|
||||
if (isTrue(column.isNotnull())) {
|
||||
buffer.append(" not null");
|
||||
writeColumnNotNull(buffer);
|
||||
}
|
||||
buffer.append(addColumnSuffix);
|
||||
buffer.endOfStatement();
|
||||
|
||||
// check constraints cannot be added in one statement for h2
|
||||
if (!StringHelper.isNull(column.getCheckConstraint())) {
|
||||
String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(),
|
||||
column.getCheckConstraint());
|
||||
buffer.append(ddl).endOfStatement();
|
||||
String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(), column.getCheckConstraint());
|
||||
if (hasValue(ddl)) {
|
||||
buffer.append(ddl).endOfStatement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buffer.append(addColumnSuffix);
|
||||
@@ -485,7 +524,7 @@ public class PlatformDdl {
|
||||
|
||||
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
|
||||
buffer.append("alter table ").append(tableName).append(" ").append(dropColumn).append(" ").append(columnName)
|
||||
.append(dropColumnSuffix).endOfStatement();
|
||||
.append(dropColumnSuffix).endOfStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -560,7 +599,7 @@ public class PlatformDdl {
|
||||
}
|
||||
|
||||
protected void appendWithSpace(String content, StringBuilder buffer) {
|
||||
if (content != null && !content.isEmpty()) {
|
||||
if (hasValue(content)) {
|
||||
buffer.append(" ").append(content);
|
||||
}
|
||||
}
|
||||
@@ -593,6 +632,13 @@ public class PlatformDdl {
|
||||
return updateNullWithDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if null or trimmed string is empty.
|
||||
*/
|
||||
protected boolean hasValue(String value) {
|
||||
return value != null && !value.trim().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Null safe Boolean true test.
|
||||
*/
|
||||
@@ -607,6 +653,13 @@ public class PlatformDdl {
|
||||
// do nothing by default (MySql only)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an table storage engine to the create table statement.
|
||||
*/
|
||||
public void tableStorageEngine(DdlBuffer apply, String storageEngine) throws IOException {
|
||||
// do nothing by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Add table comment as a separate statement (from the create table statement).
|
||||
*/
|
||||
@@ -643,10 +696,10 @@ public class PlatformDdl {
|
||||
|
||||
/**
|
||||
* Shortens the given name to the maximum constraint name length of the platform in a deterministic way.
|
||||
*
|
||||
* <p>
|
||||
* First, all vowels are removed, If the string is still to long, 31 bits are taken from the hash code
|
||||
* of the string and base36 encoded (10 digits and 26 chars) string.
|
||||
*
|
||||
* <p>
|
||||
* As 36^6 > 31^2, the resulting string is never longer as 6 chars.
|
||||
*/
|
||||
protected String maxConstraintName(String name) {
|
||||
@@ -654,7 +707,7 @@ public class PlatformDdl {
|
||||
int hash = name.hashCode() & 0x7FFFFFFF;
|
||||
name = VowelRemover.trim(name, 4);
|
||||
if (name.length() > platform.getMaxConstraintNameLength()) {
|
||||
return name.substring(0, platform.getMaxConstraintNameLength()-7) + "_" + Integer.toString(hash, 36);
|
||||
return name.substring(0, platform.getMaxConstraintNameLength() - 7) + "_" + Integer.toString(hash, 36);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
|
||||
@@ -77,6 +77,8 @@ public class CreateTable {
|
||||
protected BigInteger sequenceAllocate;
|
||||
@XmlAttribute(name = "pkName")
|
||||
protected String pkName;
|
||||
@XmlAttribute(name = "storageEngine")
|
||||
protected String storageEngine;
|
||||
@XmlAttribute(name = "tablespace")
|
||||
protected String tablespace;
|
||||
@XmlAttribute(name = "indexTablespace")
|
||||
@@ -365,6 +367,26 @@ public class CreateTable {
|
||||
this.pkName = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the storageEngine property.
|
||||
*
|
||||
* @return possible object is
|
||||
* {@link String }
|
||||
*/
|
||||
public String getStorageEngine() {
|
||||
return storageEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the storageEngine property.
|
||||
*
|
||||
* @param value allowed object is
|
||||
* {@link String }
|
||||
*/
|
||||
public void setStorageEngine(String value) {
|
||||
this.storageEngine = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the tablespace property.
|
||||
*
|
||||
|
||||
@@ -67,14 +67,14 @@ public class CurrentModel {
|
||||
* Return true if the model contains tables that are partitioned.
|
||||
*/
|
||||
public boolean isTablePartitioning() {
|
||||
return model.isTablePartitioning();
|
||||
return read().isTablePartitioning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tables that have partitioning.
|
||||
*/
|
||||
public List<MTable> getPartitionedTables() {
|
||||
return model.getPartitionedTables();
|
||||
return read().getPartitionedTables();
|
||||
}
|
||||
|
||||
private static DbConstraintNaming.MaxLength maxLength(SpiEbeanServer server, DbConstraintNaming naming) {
|
||||
|
||||
@@ -79,6 +79,8 @@ public class MTable {
|
||||
*/
|
||||
private String tablespace;
|
||||
|
||||
private String storageEngine;
|
||||
|
||||
/**
|
||||
* Tablespace to use for indexes on this table.
|
||||
*/
|
||||
@@ -159,6 +161,7 @@ public class MTable {
|
||||
this.name = createTable.getName();
|
||||
this.pkName = createTable.getPkName();
|
||||
this.comment = createTable.getComment();
|
||||
this.storageEngine = createTable.getStorageEngine();
|
||||
this.tablespace = createTable.getTablespace();
|
||||
this.indexTablespace = createTable.getIndexTablespace();
|
||||
this.withHistory = Boolean.TRUE.equals(createTable.isWithHistory());
|
||||
@@ -244,6 +247,7 @@ public class MTable {
|
||||
createTable.setPartitionMode(partitionMeta.getMode().name());
|
||||
createTable.setPartitionColumn(partitionMeta.getProperty());
|
||||
}
|
||||
createTable.setStorageEngine(storageEngine);
|
||||
createTable.setTablespace(tablespace);
|
||||
createTable.setIndexTablespace(indexTablespace);
|
||||
createTable.setSequenceName(sequenceName);
|
||||
@@ -452,6 +456,10 @@ public class MTable {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public void setStorageEngine(String storageEngine) {
|
||||
this.storageEngine = storageEngine;
|
||||
}
|
||||
|
||||
public String getTablespace() {
|
||||
return tablespace;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
|
||||
}
|
||||
|
||||
MTable table = new MTable(descriptor.getBaseTable());
|
||||
table.setStorageEngine(descriptor.getStorageEngine());
|
||||
table.setPartitionMeta(descriptor.getPartitionMeta());
|
||||
table.setComment(descriptor.getDbComment());
|
||||
if (descriptor.isHistorySupport()) {
|
||||
|
||||
@@ -41,7 +41,7 @@ public class BaseQueryTuner {
|
||||
*/
|
||||
private final boolean skipAll;
|
||||
|
||||
public BaseQueryTuner(AutoTuneConfig config, SpiEbeanServer server, ProfilingListener profilingListener) {
|
||||
BaseQueryTuner(AutoTuneConfig config, SpiEbeanServer server, ProfilingListener profilingListener) {
|
||||
this.server = server;
|
||||
this.profilingListener = profilingListener;
|
||||
this.mode = config.getMode();
|
||||
@@ -84,12 +84,17 @@ public class BaseQueryTuner {
|
||||
/**
|
||||
* Auto tune the query and enable profiling.
|
||||
*/
|
||||
public boolean tuneQuery(SpiQuery<?> query) {
|
||||
boolean tuneQuery(SpiQuery<?> query) {
|
||||
|
||||
if (skipAll || !tunableQuery(query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (query.getProfilingListener() != null) {
|
||||
// profiling secondary query
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!useTuning(query)) {
|
||||
if (profiling) {
|
||||
profiling(query, server.createCallStack());
|
||||
@@ -138,6 +143,7 @@ public class BaseQueryTuner {
|
||||
switch (type) {
|
||||
case COUNT:
|
||||
case ID_LIST:
|
||||
case UPDATE:
|
||||
case DELETE:
|
||||
case SUBQUERY:
|
||||
return false;
|
||||
|
||||
@@ -103,8 +103,7 @@ public class ProfileManager implements ProfilingListener {
|
||||
|
||||
private ProfileOrigin getProfileOrigin(ObjectGraphOrigin originQueryPoint) {
|
||||
synchronized (monitor) {
|
||||
ProfileOrigin stats = profileMap.computeIfAbsent(originQueryPoint.getKey(), k -> new ProfileOrigin(originQueryPoint, queryTuningAddVersion, profilingBase, profilingRate));
|
||||
return stats;
|
||||
return profileMap.computeIfAbsent(originQueryPoint.getKey(), k -> new ProfileOrigin(originQueryPoint, queryTuningAddVersion, profilingBase, profilingRate));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public class ProfileOrigin {
|
||||
}
|
||||
}
|
||||
|
||||
private OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
|
||||
OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
|
||||
|
||||
PathProperties pathProps = new PathProperties();
|
||||
for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) {
|
||||
@@ -107,8 +107,7 @@ public class ProfileOrigin {
|
||||
|
||||
OrmQueryDetail detail = new OrmQueryDetail();
|
||||
|
||||
Collection<Props> pathProperties = pathProps.getPathProps();
|
||||
for (Props props : pathProperties) {
|
||||
for (Props props : pathProps.getPathProps()) {
|
||||
if (!props.isEmpty()) {
|
||||
detail.fetch(props.getPath(), props.getPropertiesAsString(), null);
|
||||
}
|
||||
@@ -151,12 +150,8 @@ public class ProfileOrigin {
|
||||
* Collect the usage information for from a instance for this node.
|
||||
*/
|
||||
public void collectUsageInfo(NodeUsageCollector profile) {
|
||||
|
||||
//logger.info("COLLECT USAGE {}", profile.toString());
|
||||
|
||||
if (!profile.isEmpty()) {
|
||||
ProfileOriginNodeUsage nodeStats = getNodeStats(profile.getNode().getPath());
|
||||
nodeStats.collectUsageInfo(profile);
|
||||
getNodeStats(profile.getNode().getPath()).collectUsageInfo(profile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,23 +55,27 @@ public class ProfileOriginNodeUsage {
|
||||
}
|
||||
}
|
||||
|
||||
BeanProperty toOneIdProperty = null;
|
||||
boolean addedToPath = false;
|
||||
|
||||
for (String propName : aggregateUsed) {
|
||||
BeanProperty beanProp = desc.findPropertyFromPath(propName);
|
||||
if (beanProp == null) {
|
||||
logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName());
|
||||
|
||||
} else {
|
||||
if (beanProp instanceof BeanPropertyAssoc<?>) {
|
||||
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProp;
|
||||
String targetIdProp = assocProp.getTargetIdProperty();
|
||||
String manyPath = SplitName.add(path, assocProp.getName());
|
||||
pathProps.addToPath(manyPath, targetIdProp);
|
||||
if (beanProp.isId()) {
|
||||
// remember and maybe add ToOne property to parent path
|
||||
toOneIdProperty = beanProp;
|
||||
} else if (beanProp instanceof BeanPropertyAssoc<?>) {
|
||||
// intentionally skip
|
||||
} else {
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (beanProp.isLob() && !beanProp.isFetchEager()) {
|
||||
// AutoTune will not include Lob's marked FetchLazy
|
||||
// (which is the default for Lob's so typical).
|
||||
} else {
|
||||
addedToPath = true;
|
||||
pathProps.addToPath(path, beanProp.getName());
|
||||
}
|
||||
}
|
||||
@@ -81,9 +85,16 @@ public class ProfileOriginNodeUsage {
|
||||
if ((modified || addVersionProperty) && desc != null) {
|
||||
BeanProperty versionProp = desc.getVersionProperty();
|
||||
if (versionProp != null) {
|
||||
addedToPath = true;
|
||||
pathProps.addToPath(path, versionProp.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (toOneIdProperty != null && !addedToPath) {
|
||||
// add ToOne property to parent path
|
||||
ElPropertyValue assocOne = rootDesc.getElGetValue(path);
|
||||
pathProps.addToPath(SplitName.parent(path), assocOne.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,15 +144,13 @@ public abstract class AbstractSqlQueryRequest {
|
||||
* Prepare and execute the SQL using the Binder.
|
||||
*/
|
||||
public void executeSql(Binder binder, SpiQuery.Type type) throws SQLException {
|
||||
startNano = System.nanoTime();
|
||||
executeAsSql(binder);
|
||||
}
|
||||
|
||||
protected void executeAsSql(Binder binder) throws SQLException {
|
||||
|
||||
startNano = System.nanoTime();
|
||||
|
||||
prepareSql();
|
||||
|
||||
Connection conn = trans.getInternalConnection();
|
||||
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform;
|
||||
import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
|
||||
import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
@@ -104,6 +105,9 @@ public class DatabasePlatformFactory {
|
||||
if (dbName.equals("db2")) {
|
||||
return new DB2Platform();
|
||||
}
|
||||
if (dbName.equals("clickhouse")) {
|
||||
return new ClickHousePlatform();
|
||||
}
|
||||
if (dbName.equals("sqlite")) {
|
||||
return new SQLitePlatform();
|
||||
}
|
||||
@@ -162,6 +166,8 @@ public class DatabasePlatformFactory {
|
||||
return new SqlAnywherePlatform();
|
||||
} else if (dbProductName.contains("hdb")) {
|
||||
return new HanaPlatform();
|
||||
} else if (dbProductName.contains("clickhouse")) {
|
||||
return new ClickHousePlatform();
|
||||
}
|
||||
|
||||
// use the standard one
|
||||
|
||||
@@ -28,7 +28,7 @@ public class DefaultCallStackFactory implements CallStackFactory {
|
||||
|
||||
// find the first non-avaje stackElement
|
||||
for (; startIndex < stackTrace.length; startIndex++) {
|
||||
if (!stackTrace[startIndex].getClassName().startsWith(IO_EBEAN)) {
|
||||
if (!ignore(stackTrace[startIndex])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,16 @@ public class DefaultCallStackFactory implements CallStackFactory {
|
||||
return createCallStack(finalTrace);
|
||||
}
|
||||
|
||||
private boolean ignore(StackTraceElement element) {
|
||||
if (element.getClassName().startsWith(IO_EBEAN)) {
|
||||
return true;
|
||||
}
|
||||
if (element.getMethodName().startsWith("_ebean_")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private CallStack createCallStack(StackTraceElement[] finalTrace) {
|
||||
return new CallStack(finalTrace, finalTrace[0].hashCode(), pathHash(finalTrace));
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -35,6 +36,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
server.visitMetrics(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerMetrics collectMetrics() {
|
||||
return visitBasic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicMetricVisitor visitBasic() {
|
||||
BasicMetricVisitor basic = new BasicMetricVisitor();
|
||||
|
||||
@@ -505,6 +505,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private void shutdownPlugins() {
|
||||
|
||||
if (serverConfig.isDumpMetricsOnShutdown()) {
|
||||
new DumpMetrics(this, serverConfig.getDumpMetricsOptions()).dump();
|
||||
}
|
||||
|
||||
for (Plugin plugin : serverPlugins) {
|
||||
try {
|
||||
plugin.shutdown();
|
||||
@@ -1234,7 +1238,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(spiQuery, t);
|
||||
request.profileLocationById();
|
||||
if (request.isUseDocStore()) {
|
||||
return docStore().find(request);
|
||||
}
|
||||
@@ -1588,7 +1591,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
private <T> List<T> findList(Query<T> query, Transaction t, boolean findOne) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
|
||||
request.profileLocationAll();
|
||||
request.resetBeanCacheAutoMode(findOne);
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
|
||||
@@ -37,6 +37,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
super(server, query, null);
|
||||
this.queryEngine = engine;
|
||||
this.query = query;
|
||||
query.obtainLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,6 +45,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
*/
|
||||
@Override
|
||||
public void executeSql(Binder binder, SpiQuery.Type type) throws SQLException {
|
||||
startNano = System.nanoTime();
|
||||
SpiQuery<?> ormQuery = query.getOrmQuery();
|
||||
if (ormQuery != null) {
|
||||
ormQuery.setType(type);
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetaOrmQueryMetric;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
import io.ebean.meta.SortMetric;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
class DumpMetrics {
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
private final String options;
|
||||
|
||||
private final String nameFormat;
|
||||
private final String nameFormatTimed;
|
||||
|
||||
private boolean dumpHash;
|
||||
private boolean dumpSql;
|
||||
private boolean dumpLoc;
|
||||
|
||||
private Comparator<MetaTimedMetric> sortBy = SortMetric.NAME;
|
||||
|
||||
DumpMetrics(SpiEbeanServer server, String options) {
|
||||
this.server = server;
|
||||
this.options = options;
|
||||
|
||||
int width = 0;
|
||||
|
||||
if (options != null) {
|
||||
dumpLoc = options.contains("loc");
|
||||
dumpSql = options.contains("sql");
|
||||
dumpHash = options.contains("hash");
|
||||
for (int i = 5; i < 10; i++) {
|
||||
width = Math.max(width, optionWidth(i * 10));
|
||||
}
|
||||
for (String option : new String[]{"Total", "Count", "Mean", "Max"}) {
|
||||
sortOption(option);
|
||||
}
|
||||
}
|
||||
if (width == 0) {
|
||||
width = 80;
|
||||
}
|
||||
|
||||
nameFormat = "%1$-" + width + "s";
|
||||
nameFormatTimed = "%1$-" + (width + 6) + "s";
|
||||
}
|
||||
|
||||
private int optionWidth(int check) {
|
||||
return options.contains("w" + check) ? check : 0;
|
||||
}
|
||||
|
||||
private void sortOption(String option) {
|
||||
if (options.contains("sort" + option)) {
|
||||
sortBy = setSortOption(option);
|
||||
}
|
||||
}
|
||||
|
||||
private Comparator<MetaTimedMetric> setSortOption(String option) {
|
||||
switch (option.toUpperCase()) {
|
||||
case "TOTAL":
|
||||
return SortMetric.TOTAL;
|
||||
case "COUNT":
|
||||
return SortMetric.COUNT;
|
||||
case "MEAN":
|
||||
return SortMetric.MEAN;
|
||||
case "MAX":
|
||||
return SortMetric.MAX;
|
||||
}
|
||||
return SortMetric.NAME;
|
||||
}
|
||||
|
||||
void dump() {
|
||||
|
||||
out("-- Dumping metrics for " + server.getName() + " -- ");
|
||||
ServerMetrics serverMetrics = server.getMetaInfoManager().collectMetrics();
|
||||
|
||||
for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
|
||||
log(metric);
|
||||
}
|
||||
|
||||
List<MetaOrmQueryMetric> ormQueryMetrics = serverMetrics.getOrmQueryMetrics();
|
||||
if (!ormQueryMetrics.isEmpty()) {
|
||||
out("\n-- ORM queries --");
|
||||
ormQueryMetrics.sort(sortBy);
|
||||
for (MetaOrmQueryMetric metric : ormQueryMetrics) {
|
||||
logQuery(metric);
|
||||
}
|
||||
}
|
||||
|
||||
List<MetaQueryMetric> dtoQueryMetrics = serverMetrics.getDtoQueryMetrics();
|
||||
if (!dtoQueryMetrics.isEmpty()) {
|
||||
out("\n-- DTO queries --");
|
||||
dtoQueryMetrics.sort(sortBy);
|
||||
for (MetaQueryMetric metric : dtoQueryMetrics) {
|
||||
logDtoQuery(metric);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void out(String sb) {
|
||||
System.out.println(sb);
|
||||
}
|
||||
|
||||
private void logQuery(MetaOrmQueryMetric metric) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
appendQueryName(metric, sb);
|
||||
appendCounters(metric, sb);
|
||||
if (dumpHash) {
|
||||
sb.append("\n hash:").append(metric.getQueryPlanHash());
|
||||
}
|
||||
appendProfileAndSql(metric, sb);
|
||||
out(sb.toString());
|
||||
}
|
||||
|
||||
|
||||
private void logDtoQuery(MetaQueryMetric metric) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
appendQueryName(metric, sb);
|
||||
appendCounters(metric, sb);
|
||||
appendProfileAndSql(metric, sb);
|
||||
out(sb.toString());
|
||||
}
|
||||
|
||||
private void appendQueryName(MetaQueryMetric metric, StringBuilder sb) {
|
||||
sb.append("query:").append(padName(metric.getName())).append(" ");
|
||||
}
|
||||
|
||||
private void appendProfileAndSql(MetaQueryMetric metric, StringBuilder sb) {
|
||||
ProfileLocation profileLocation = metric.getProfileLocation();
|
||||
if (dumpLoc && profileLocation != null) {
|
||||
sb.append("\n loc:").append(profileLocation.shortDescription());
|
||||
}
|
||||
|
||||
if (dumpSql) {
|
||||
sb.append(" \n\n sql:").append(metric.getSql()).append("\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void log(MetaTimedMetric metric) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(padNameTimed(metric.getName())).append(" ");
|
||||
appendCounters(metric, sb);
|
||||
out(sb.toString());
|
||||
}
|
||||
|
||||
private void appendCounters(MetaTimedMetric timedMetric, StringBuilder sb) {
|
||||
sb.append(" count:").append(pad(timedMetric.getCount()))
|
||||
.append(" total:").append(pad(timedMetric.getTotal()))
|
||||
.append(" mean:").append(pad(timedMetric.getMean()))
|
||||
.append(" max:").append(pad(timedMetric.getMax()));
|
||||
}
|
||||
|
||||
private String padName(String name) {
|
||||
return String.format(nameFormat, name);
|
||||
}
|
||||
|
||||
private String padNameTimed(String name) {
|
||||
return String.format(nameFormatTimed, name);
|
||||
}
|
||||
|
||||
private String pad(long value) {
|
||||
return String.format("%1$-8s", value);
|
||||
}
|
||||
}
|
||||
@@ -102,20 +102,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
return queryEngine.translate(this, bindLog, sql, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profileLocationById() {
|
||||
if (query.getProfileLocation() == null) {
|
||||
query.setProfileLocation(beanDescriptor.profileLocationById());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profileLocationAll() {
|
||||
if (query.getProfileLocation() == null && query.isFindAll()) {
|
||||
query.setProfileLocation(beanDescriptor.profileLocationAll());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleteByStatement() {
|
||||
if (!transaction.isPersistCascade() || beanDescriptor.isDeleteByStatement()) {
|
||||
|
||||
@@ -607,11 +607,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
transaction.registerDeleteBean(hash);
|
||||
}
|
||||
|
||||
public void unregisterDeleteBean() {
|
||||
Integer hash = getBeanHash();
|
||||
transaction.unregisterDeleteBean(hash);
|
||||
}
|
||||
|
||||
public boolean isRegisteredForDeleteBean() {
|
||||
if (transaction == null) {
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.ClickHouseDdl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.CockroachDdl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DB2Ddl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.H2Ddl;
|
||||
@@ -50,6 +51,8 @@ public class PlatformDdlBuilder {
|
||||
return new SqlServerDdl(platform);
|
||||
case HANA:
|
||||
return new HanaColumnStoreDdl(platform);
|
||||
case CLICKHOUSE:
|
||||
return new ClickHouseDdl(platform);
|
||||
default:
|
||||
return new PlatformDdl(platform);
|
||||
}
|
||||
|
||||
@@ -162,16 +162,6 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
|
||||
*/
|
||||
boolean isUseDocStore();
|
||||
|
||||
/**
|
||||
* Set profile location for "find by id" if not set.
|
||||
*/
|
||||
void profileLocationById();
|
||||
|
||||
/**
|
||||
* Set profile location for "find all" if not set.
|
||||
*/
|
||||
void profileLocationAll();
|
||||
|
||||
/**
|
||||
* Return true if delete by statement is allowed for this type given cascade rules etc.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
@@ -136,8 +135,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
private final Map<String, String> namedQuery;
|
||||
|
||||
private final short profileBeanId;
|
||||
private final ProfileLocation locationById;
|
||||
private final ProfileLocation locationAll;
|
||||
|
||||
private final boolean multiValueSupported;
|
||||
|
||||
@@ -211,6 +208,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
private final String draftTable;
|
||||
|
||||
private final PartitionMeta partitionMeta;
|
||||
private final String storageEngine;
|
||||
|
||||
/**
|
||||
* DB table comment.
|
||||
@@ -447,8 +445,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.name = InternString.intern(deploy.getName());
|
||||
this.baseTableAlias = "t0";
|
||||
this.fullName = InternString.intern(deploy.getFullName());
|
||||
this.locationById = ProfileLocation.createAt(fullName + ".byId");
|
||||
this.locationAll = ProfileLocation.createAt(fullName + ".all");
|
||||
this.profileBeanId = deploy.getProfileId();
|
||||
this.beanType = deploy.getBeanType();
|
||||
this.rootBeanType = PersistenceContextUtil.root(beanType);
|
||||
@@ -492,6 +488,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.dependentTables = deploy.getDependentTables();
|
||||
this.dbComment = deploy.getDbComment();
|
||||
this.partitionMeta = deploy.getPartitionMeta();
|
||||
this.storageEngine = deploy.getStorageEngine();
|
||||
this.autoTunable = EntityType.ORM == entityType && (beanFinder == null);
|
||||
|
||||
// helper object used to derive lists of properties
|
||||
@@ -580,20 +577,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a location for "find by id".
|
||||
*/
|
||||
public ProfileLocation profileLocationById() {
|
||||
return locationById;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a location for "find all".
|
||||
*/
|
||||
public ProfileLocation profileLocationAll() {
|
||||
return locationAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the id used in profiling to identify the bean type.
|
||||
*/
|
||||
@@ -1970,10 +1953,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
DefaultOrmQuery<T> query = new DefaultOrmQuery<>(this, ebeanServer, ebeanServer.getExpressionFactory());
|
||||
query.setPersistenceContext(pc);
|
||||
return query
|
||||
// .select(getIdProperty().getName())
|
||||
// we do not select the id because we
|
||||
// probably have to load the entire bean
|
||||
.setId(id).findOne();
|
||||
// .select(getIdProperty().getName())
|
||||
// we do not select the id because we
|
||||
// probably have to load the entire bean
|
||||
.setId(id).findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2874,6 +2857,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return partitionMeta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the storage engine.
|
||||
*/
|
||||
public String getStorageEngine() {
|
||||
return storageEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dependent tables for a view based entity.
|
||||
* <p>
|
||||
|
||||
@@ -433,7 +433,13 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
} else {
|
||||
for (XmRawSql sql : entityDeploy.getRawSql()) {
|
||||
RawSqlBuilder builder = RawSqlBuilder.parse(sql.getQuery().getValue());
|
||||
RawSqlBuilder builder;
|
||||
try {
|
||||
builder = RawSqlBuilder.parse(sql.getQuery().getValue());
|
||||
} catch (RuntimeException e) {
|
||||
builder = RawSqlBuilder.unparsed(sql.getQuery().getValue());
|
||||
}
|
||||
|
||||
for (XmColumnMapping columnMapping : sql.getColumnMapping()) {
|
||||
builder.columnMapping(columnMapping.getColumn(), columnMapping.getProperty());
|
||||
}
|
||||
|
||||
@@ -136,6 +136,8 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private List<IndexDefinition> indexDefinitions;
|
||||
|
||||
private String storageEngine;
|
||||
|
||||
/**
|
||||
* The base database table.
|
||||
*/
|
||||
@@ -275,6 +277,14 @@ public class DeployBeanDescriptor<T> {
|
||||
return Modifier.isAbstract(beanType.getModifiers());
|
||||
}
|
||||
|
||||
public void setStorageEngine(String storageEngine) {
|
||||
this.storageEngine = storageEngine;
|
||||
}
|
||||
|
||||
public String getStorageEngine() {
|
||||
return storageEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true for @History entity beans that have history.
|
||||
*/
|
||||
@@ -315,7 +325,7 @@ public class DeployBeanDescriptor<T> {
|
||||
this.partitionMeta = partitionMeta;
|
||||
}
|
||||
|
||||
public PartitionMeta getPartitionMeta() {
|
||||
public PartitionMeta getPartitionMeta() {
|
||||
if (partitionMeta != null) {
|
||||
DeployBeanProperty beanProperty = getBeanProperty(partitionMeta.getProperty());
|
||||
if (beanProperty != null) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.ebean.annotation.History;
|
||||
import io.ebean.annotation.Index;
|
||||
import io.ebean.annotation.InvalidateQueryCache;
|
||||
import io.ebean.annotation.ReadAudit;
|
||||
import io.ebean.annotation.StorageEngine;
|
||||
import io.ebean.annotation.UpdateMode;
|
||||
import io.ebean.annotation.View;
|
||||
import io.ebean.config.TableName;
|
||||
@@ -154,6 +155,11 @@ public class AnnotationClass extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
StorageEngine storage = AnnotationUtil.findAnnotationRecursive(cls, StorageEngine.class);
|
||||
if (storage != null) {
|
||||
descriptor.setStorageEngine(storage.value());
|
||||
}
|
||||
|
||||
DbPartition partition = AnnotationUtil.findAnnotationRecursive(cls, DbPartition.class);
|
||||
if (partition != null) {
|
||||
descriptor.setPartitionMeta(new PartitionMeta(partition.mode(), partition.property()));
|
||||
|
||||
@@ -64,7 +64,7 @@ public class DeployCreateProperties {
|
||||
* </p>
|
||||
*/
|
||||
private boolean ignoreFieldByName(String fieldName) {
|
||||
if (fieldName.startsWith("_ebean_")) {
|
||||
if (fieldName.startsWith("_ebean_") || fieldName.equals("_$targetDatabase")) {
|
||||
// ignore Ebean internal fields
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.metric.MetricFactory;
|
||||
import io.ebean.metric.QueryPlanMetric;
|
||||
@@ -14,6 +15,8 @@ public class DtoMappingRequest {
|
||||
|
||||
private final String label;
|
||||
|
||||
private final ProfileLocation profileLocation;
|
||||
|
||||
private final String sql;
|
||||
|
||||
private final boolean relaxedMode;
|
||||
@@ -22,7 +25,8 @@ public class DtoMappingRequest {
|
||||
|
||||
public DtoMappingRequest(SpiDtoQuery query, String sql, DtoColumn[] columnMeta) {
|
||||
this.type = query.getType();
|
||||
this.label = query.getLabel();
|
||||
this.label = query.getPlanLabel();
|
||||
this.profileLocation = query.getProfileLocation();
|
||||
this.sql = sql;
|
||||
this.relaxedMode = query.isRelaxedMode();
|
||||
this.columnMeta = columnMeta;
|
||||
@@ -45,6 +49,6 @@ public class DtoMappingRequest {
|
||||
}
|
||||
|
||||
public QueryPlanMetric createMetric() {
|
||||
return MetricFactory.get().createQueryPlanMetric(MetricType.DTO, type, label, sql);
|
||||
return MetricFactory.get().createQueryPlanMetric(MetricType.DTO, type, label, profileLocation, sql);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.loadcontext;
|
||||
import io.ebean.FetchConfig;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
|
||||
@@ -77,6 +78,20 @@ public abstract class DLoadBaseContext {
|
||||
return (lazyBatchSize > 1) ? lazyBatchSize : defaultBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the parent has a query plan label then extend it with the path and
|
||||
* set onto the secondary query.
|
||||
*/
|
||||
void setLabel(SpiQuery<?> query) {
|
||||
|
||||
String label = parent.getPlanLabel();
|
||||
if (label != null) {
|
||||
label += "_" + fullPath;
|
||||
query.setLabel(label);
|
||||
query.setProfileLocation(parent.getProfileLocation());
|
||||
}
|
||||
}
|
||||
|
||||
protected PersistenceContext getPersistenceContext() {
|
||||
return parent.getPersistenceContext();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
|
||||
protected void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
|
||||
|
||||
setLabel(query);
|
||||
parent.propagateQueryState(query, desc.isDocStoreMapped());
|
||||
query.setParentNode(objectGraphNode);
|
||||
query.setLazyLoadProperty(lazyLoadProperty);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.loadcontext;
|
||||
|
||||
import io.ebean.CacheMode;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.CallStack;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
@@ -12,6 +13,7 @@ import io.ebeaninternal.api.LoadSecondaryQuery;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuerySecondary;
|
||||
import io.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -54,7 +56,9 @@ public class DLoadContext implements LoadContext {
|
||||
*/
|
||||
private final String relativePath;
|
||||
private final ObjectGraphOrigin origin;
|
||||
private final boolean useProfiling;
|
||||
private final String planLabel;
|
||||
private final ProfileLocation profileLocation;
|
||||
private final ProfilingListener profilingListener;
|
||||
|
||||
private final Map<String, ObjectGraphNode> nodePathMap = new HashMap<>();
|
||||
|
||||
@@ -83,7 +87,9 @@ public class DLoadContext implements LoadContext {
|
||||
this.disableReadAudit = false;
|
||||
this.includeSoftDeletes = false;
|
||||
this.relativePath = null;
|
||||
this.useProfiling = false;
|
||||
this.planLabel = null;
|
||||
this.profileLocation = null;
|
||||
this.profilingListener = null;
|
||||
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
|
||||
}
|
||||
|
||||
@@ -109,7 +115,9 @@ public class DLoadContext implements LoadContext {
|
||||
this.disableReadAudit = query.isDisableReadAudit();
|
||||
this.disableLazyLoading = query.isDisableLazyLoading();
|
||||
this.useBeanCache = query.getUseBeanCache();
|
||||
this.useProfiling = query.getProfilingListener() != null;
|
||||
this.profilingListener = query.getProfilingListener();
|
||||
this.planLabel = query.getPlanLabel();
|
||||
this.profileLocation = query.getProfileLocation();
|
||||
|
||||
ObjectGraphNode parentNode = query.getParentNode();
|
||||
if (parentNode != null) {
|
||||
@@ -126,6 +134,19 @@ public class DLoadContext implements LoadContext {
|
||||
registerSecondaryQueries(secondaryQueries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan label of the origin query.
|
||||
*/
|
||||
public String getPlanLabel() {
|
||||
return planLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the profile location of the origin query.
|
||||
*/
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return profileLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the +query and +lazy secondary queries with their appropriate LoadBeanContext or LoadManyContext.
|
||||
@@ -211,10 +232,7 @@ public class DLoadContext implements LoadContext {
|
||||
|
||||
@Override
|
||||
public ObjectGraphNode getObjectGraphNode(String path) {
|
||||
|
||||
ObjectGraphNode node = nodePathMap.computeIfAbsent(path, this::createObjectGraphNode);
|
||||
|
||||
return node;
|
||||
return nodePathMap.computeIfAbsent(path, this::createObjectGraphNode);
|
||||
}
|
||||
|
||||
private ObjectGraphNode createObjectGraphNode(String path) {
|
||||
@@ -351,8 +369,8 @@ public class DLoadContext implements LoadContext {
|
||||
if (disableReadAudit) {
|
||||
query.setDisableReadAuditing();
|
||||
}
|
||||
if (useProfiling) {
|
||||
query.setAutoTune(true);
|
||||
if (profilingListener != null) {
|
||||
query.setProfilingListener(profilingListener);
|
||||
}
|
||||
if (tenantId != null) {
|
||||
query.setTenantId(tenantId);
|
||||
|
||||
@@ -59,6 +59,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
|
||||
public void configureQuery(SpiQuery<?> query) {
|
||||
|
||||
setLabel(query);
|
||||
parent.propagateQueryState(query, docStoreMapped);
|
||||
query.setParentNode(objectGraphNode);
|
||||
if (queryProps != null) {
|
||||
|
||||
@@ -882,7 +882,6 @@ public final class DefaultPersister implements Persister {
|
||||
// bean to handle bi-directional cascading
|
||||
request.registerDeleteBean();
|
||||
deleteAssocMany(request);
|
||||
request.unregisterDeleteBean();
|
||||
|
||||
unloadedForeignKeys = getDeleteUnloadedForeignKeys(request);
|
||||
if (unloadedForeignKeys != null) {
|
||||
|
||||
@@ -5,14 +5,16 @@ import io.ebean.ProfileLocation;
|
||||
/**
|
||||
* Fixed / given location. Used internally for find by id and find all.
|
||||
*/
|
||||
class BasicProfileLocation implements ProfileLocation {
|
||||
final class BasicProfileLocation implements ProfileLocation {
|
||||
|
||||
private final String location;
|
||||
private final String shortDescription;
|
||||
private final String label;
|
||||
|
||||
BasicProfileLocation(String location) {
|
||||
this.location = location;
|
||||
this.shortDescription = shortDesc(location);
|
||||
this.label = UtilLocation.label(shortDescription);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -30,6 +32,11 @@ class BasicProfileLocation implements ProfileLocation {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String label() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shortDescription() {
|
||||
return shortDescription;
|
||||
@@ -38,9 +45,9 @@ class BasicProfileLocation implements ProfileLocation {
|
||||
private String shortDesc(String location) {
|
||||
int lastPer = location.lastIndexOf('.');
|
||||
if (lastPer > -1) {
|
||||
lastPer = location.lastIndexOf('.', lastPer-1);
|
||||
lastPer = location.lastIndexOf('.', lastPer - 1);
|
||||
if (lastPer > -1) {
|
||||
return location.substring(lastPer+1);
|
||||
return location.substring(lastPer + 1);
|
||||
}
|
||||
}
|
||||
return location;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.metric.MetricFactory;
|
||||
import io.ebean.metric.QueryPlanMetric;
|
||||
@@ -22,8 +23,8 @@ public class DMetricFactory implements MetricFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, String sql) {
|
||||
return new DQueryPlanMetric(new DQueryPlanMeta(type, label, sql), new DTimedMetric(metricType, label));
|
||||
public QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, ProfileLocation profileLocation, String sql) {
|
||||
return new DQueryPlanMetric(new DQueryPlanMeta(type, label, profileLocation, sql), new DTimedMetric(metricType, label));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ class DProfileLocation implements ProfileLocation {
|
||||
|
||||
private String shortDescription;
|
||||
|
||||
private String label;
|
||||
|
||||
private final int lineNumber;
|
||||
|
||||
DProfileLocation() {
|
||||
@@ -40,20 +42,29 @@ class DProfileLocation implements ProfileLocation {
|
||||
|
||||
@Override
|
||||
public String obtain() {
|
||||
// atomic assignment so happy with this
|
||||
// atomic assignments so happy enough with this (racing but atomic)
|
||||
if (location == null) {
|
||||
location = create();
|
||||
shortDescription = shortDesc(location);
|
||||
final String loc = create();
|
||||
final String shortDesc = shortDesc(loc);
|
||||
label = UtilLocation.label(shortDesc);
|
||||
shortDescription = shortDesc;
|
||||
location = loc;
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String label() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shortDescription() {
|
||||
return shortDescription;
|
||||
}
|
||||
|
||||
private String create() {
|
||||
// relatively expensive but we only do it once per profile location
|
||||
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
|
||||
for (int i = 3; i < trace.length; i++) {
|
||||
if (!trace[i].getClassName().startsWith(IO_EBEAN)) {
|
||||
|
||||
@@ -1,25 +1,43 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
class DQueryPlanMeta {
|
||||
|
||||
private final Class<?> type;
|
||||
private final String label;
|
||||
private final ProfileLocation profileLocation;
|
||||
private final String name;
|
||||
private final String sql;
|
||||
|
||||
DQueryPlanMeta(Class<?> type, String label, String sql) {
|
||||
DQueryPlanMeta(Class<?> type, String label, ProfileLocation profileLocation, String sql) {
|
||||
this.type = type;
|
||||
this.label = label;
|
||||
this.profileLocation = profileLocation;
|
||||
this.sql = sql;
|
||||
String name = type.getSimpleName();
|
||||
if (label != null) {
|
||||
name += "_" + label;
|
||||
}
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return profileLocation;
|
||||
}
|
||||
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
@@ -60,6 +61,11 @@ class DQueryPlanMetric implements QueryPlanMetric {
|
||||
return meta.getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return meta.getProfileLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
return meta.getSql();
|
||||
@@ -67,7 +73,7 @@ class DQueryPlanMetric implements QueryPlanMetric {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return stats.getName();
|
||||
return meta.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
final class UtilLocation {
|
||||
|
||||
static String label(String shortDescription) {
|
||||
int pos = shortDescription.indexOf("(");
|
||||
if (pos == -1) {
|
||||
return shortDescription;
|
||||
} else {
|
||||
return trimInit(shortDescription.substring(0, pos));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim constructor init to be "safe" without greater than or less than chars.
|
||||
*/
|
||||
private static String trimInit(String desc) {
|
||||
if (desc.endsWith("<init>")) {
|
||||
// trim to make a safe label
|
||||
return desc.substring(0, desc.length() - 6) + "init";
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -135,14 +135,22 @@ class CQueryBuilder {
|
||||
|
||||
private <T> String buildDeleteSql(OrmQueryRequest<T> request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) {
|
||||
|
||||
String alias = alias(rootTableAlias);
|
||||
if (!sqlTree.isIncludeJoins()) {
|
||||
// simple - delete from table ...
|
||||
return aliasStrip(buildSql("delete", request, predicates, sqlTree).getSql());
|
||||
if (dbPlatform.isSupportsDeleteTableAlias()) {
|
||||
// delete from table <alias> ...
|
||||
return aliasReplace(buildSql("delete", request, predicates, sqlTree).getSql(), alias);
|
||||
} else if (dbPlatform.getPlatform() == Platform.MYSQL) {
|
||||
return aliasReplace(buildSql("delete " + alias, request, predicates, sqlTree).getSql(), alias);
|
||||
} else {
|
||||
// simple - delete from table ...
|
||||
return aliasStrip(buildSql("delete", request, predicates, sqlTree).getSql());
|
||||
}
|
||||
}
|
||||
// wrap as - delete from table where id in (select id ...)
|
||||
String sql = buildSql(null, request, predicates, sqlTree).getSql();
|
||||
sql = request.getBeanDescriptor().getDeleteByIdInSql() + "in (" + sql + ")";
|
||||
sql = aliasReplace(sql, alias(rootTableAlias));
|
||||
sql = aliasReplace(sql, alias);
|
||||
return sql;
|
||||
}
|
||||
|
||||
@@ -425,7 +433,7 @@ class CQueryBuilder {
|
||||
try {
|
||||
// For SqlServer we need either "selectMethod=cursor" in the connection string or fetch explicitly a cursorable
|
||||
// statement here by specifying ResultSet.CONCUR_UPDATABLE
|
||||
PreparedStatement statement = connection.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY, dbPlatform.isSupportsResultSetConcurrencyModeUpdatable() ? ResultSet.CONCUR_UPDATABLE : ResultSet.CONCUR_READ_ONLY);
|
||||
PreparedStatement statement = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, dbPlatform.isSupportsResultSetConcurrencyModeUpdatable() ? ResultSet.CONCUR_UPDATABLE : ResultSet.CONCUR_READ_ONLY);
|
||||
predicates.bind(statement, connection);
|
||||
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
@@ -702,21 +710,21 @@ class CQueryBuilder {
|
||||
}
|
||||
|
||||
private String toSql(CountDistinctOrder orderBy) {
|
||||
switch(orderBy) {
|
||||
case ATTR_ASC:
|
||||
return " order by r1.attribute_";
|
||||
case ATTR_DESC:
|
||||
return " order by r1.attribute_ desc";
|
||||
case COUNT_ASC_ATTR_ASC:
|
||||
return " order by count(*), r1.attribute_";
|
||||
case COUNT_ASC_ATTR_DESC:
|
||||
return " order by count(*), r1.attribute_ desc";
|
||||
case COUNT_DESC_ATTR_ASC:
|
||||
return " order by count(*) desc, r1.attribute_";
|
||||
case COUNT_DESC_ATTR_DESC:
|
||||
return " order by count(*) desc, r1.attribute_ desc";
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal enum: "+ orderBy);
|
||||
switch (orderBy) {
|
||||
case ATTR_ASC:
|
||||
return " order by r1.attribute_";
|
||||
case ATTR_DESC:
|
||||
return " order by r1.attribute_ desc";
|
||||
case COUNT_ASC_ATTR_ASC:
|
||||
return " order by count(*), r1.attribute_";
|
||||
case COUNT_ASC_ATTR_DESC:
|
||||
return " order by count(*), r1.attribute_ desc";
|
||||
case COUNT_DESC_ATTR_ASC:
|
||||
return " order by count(*) desc, r1.attribute_";
|
||||
case COUNT_DESC_ATTR_DESC:
|
||||
return " order by count(*) desc, r1.attribute_ desc";
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal enum: " + orderBy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ public class CQueryPlan {
|
||||
|
||||
private final String label;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final CQueryPlanKey planKey;
|
||||
|
||||
private final boolean rawSql;
|
||||
@@ -72,6 +74,7 @@ public class CQueryPlan {
|
||||
private final boolean rowNumberIncluded;
|
||||
|
||||
private final String sql;
|
||||
private final String sqlHash;
|
||||
|
||||
private final String logWhereSql;
|
||||
|
||||
@@ -110,11 +113,13 @@ public class CQueryPlan {
|
||||
this.planKey = request.getQueryPlanKey();
|
||||
SpiQuery<?> query = request.getQuery();
|
||||
this.profileLocation = query.getProfileLocation();
|
||||
this.label = query.getLabel();
|
||||
this.label = query.getPlanLabel();
|
||||
this.name = deriveName(label, query.getType());
|
||||
this.location = location();
|
||||
this.autoTuned = query.isAutoTuned();
|
||||
this.asOfTableCount = query.getAsOfTableCount();
|
||||
this.sql = sqlRes.getSql();
|
||||
this.sqlHash = md5Hash(sql);
|
||||
this.rowNumberIncluded = sqlRes.isIncludesRowNumberColumn();
|
||||
this.sqlTree = sqlTree;
|
||||
this.rawSql = rawSql;
|
||||
@@ -135,12 +140,14 @@ public class CQueryPlan {
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
SpiQuery<?> query = request.getQuery();
|
||||
this.profileLocation = query.getProfileLocation();
|
||||
this.label = query.getLabel();
|
||||
this.label = query.getPlanLabel();
|
||||
this.name = deriveName(label, query.getType());
|
||||
this.location = location();
|
||||
this.planKey = buildPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
|
||||
this.autoTuned = false;
|
||||
this.asOfTableCount = 0;
|
||||
this.sql = sql;
|
||||
this.sqlHash = md5Hash(sql);
|
||||
this.sqlTree = sqlTree;
|
||||
this.rawSql = rawSql;
|
||||
this.rowNumberIncluded = rowNumberIncluded;
|
||||
@@ -151,6 +158,16 @@ public class CQueryPlan {
|
||||
this.bindCapture = initBindCapture(server.getServerConfig(), query);
|
||||
}
|
||||
|
||||
private String deriveName(String label, SpiQuery.Type type) {
|
||||
if (label == null) {
|
||||
return beanType.getSimpleName() + "." + type.label();
|
||||
}
|
||||
if (label.startsWith(beanType.getSimpleName())) {
|
||||
return label;
|
||||
}
|
||||
return beanType.getSimpleName() + "_" + label;
|
||||
}
|
||||
|
||||
private CQueryBindCapture initBindCapture(ServerConfig serverConfig, SpiQuery<?> query) {
|
||||
if (serverConfig.isCollectQueryPlans() && !query.getType().isUpdate()) {
|
||||
return new CQueryBindCapture(this, PlatformQueryPlan.getLogger(serverConfig.getDatabasePlatform().getPlatform()));
|
||||
@@ -189,6 +206,10 @@ public class CQueryPlan {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
@@ -245,21 +266,25 @@ public class CQueryPlan {
|
||||
|
||||
private String calcAuditQueryKey() {
|
||||
// rawSql needs to include the MD5 hash of the sql
|
||||
return rawSql ? planKey.getPartialKey() + "_" + getSqlMd5Hash() : planKey.getPartialKey();
|
||||
return rawSql ? planKey.getPartialKey() + "_" + sqlHash : planKey.getPartialKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MD5 hash of the underlying sql.
|
||||
* Return the MD5 hash of the sql.
|
||||
*/
|
||||
private String getSqlMd5Hash() {
|
||||
private String md5Hash(String sql) {
|
||||
try {
|
||||
return Md5.hash(sql);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to MD5 hash the rawSql query", e);
|
||||
logger.error("Failed to MD5 hash the query", e);
|
||||
return "error";
|
||||
}
|
||||
}
|
||||
|
||||
public String getSqlHash() {
|
||||
return sqlHash;
|
||||
}
|
||||
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ public final class CQueryPlanStats {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "location:" + getLocation() + " metrics:" + metrics + " sql:" + getSql();
|
||||
return "label:" + getLabel() + " location:" + getLocation() + " metrics:" + metrics + " sql:" + getSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,7 +184,7 @@ public final class CQueryPlanStats {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return queryPlan.getLabel();
|
||||
return queryPlan.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,7 +239,7 @@ public final class CQueryPlanStats {
|
||||
|
||||
@Override
|
||||
public String getQueryPlanHash() {
|
||||
return queryPlan.getPlanKey().toString();
|
||||
return queryPlan.getSqlHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,7 +26,7 @@ public abstract class QueryPlanLogger {
|
||||
}
|
||||
|
||||
protected DQueryPlanOutput createPlan(CQueryPlan plan, String bind, String planString) {
|
||||
return new DQueryPlanOutput(plan.getBeanType(), plan.getLabel(), plan.getSql(), bind, planString);
|
||||
return new DQueryPlanOutput(plan.getBeanType(), plan.getName(), plan.getSql(), bind, planString);
|
||||
}
|
||||
|
||||
DQueryPlanOutput readQueryPlanBasic(CQueryPlan plan, BindCapture bind, ResultSet rset) throws SQLException {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.DtoQuery;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiDtoQuery;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
@@ -39,6 +40,8 @@ public class DefaultDtoQuery<T> implements SpiDtoQuery<T> {
|
||||
|
||||
private String label;
|
||||
|
||||
private ProfileLocation profileLocation;
|
||||
|
||||
/**
|
||||
* Bind parameters when using the query language.
|
||||
*/
|
||||
@@ -52,6 +55,7 @@ public class DefaultDtoQuery<T> implements SpiDtoQuery<T> {
|
||||
this.descriptor = descriptor;
|
||||
this.ormQuery = ormQuery;
|
||||
this.label = ormQuery.getLabel();
|
||||
this.profileLocation = ormQuery.getProfileLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,6 +170,35 @@ public class DefaultDtoQuery<T> implements SpiDtoQuery<T> {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlanLabel() {
|
||||
if (label != null) {
|
||||
return label;
|
||||
}
|
||||
if (profileLocation != null) {
|
||||
return profileLocation.label();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void obtainLocation() {
|
||||
if (profileLocation != null) {
|
||||
profileLocation.obtain();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DtoQuery<T> setProfileLocation(ProfileLocation profileLocation) {
|
||||
this.profileLocation = profileLocation;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return profileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstRow() {
|
||||
return firstRow;
|
||||
|
||||
@@ -361,6 +361,17 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlanLabel() {
|
||||
if (label != null) {
|
||||
return label;
|
||||
}
|
||||
if (profileLocation != null) {
|
||||
return profileLocation.label();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setLabel(String label) {
|
||||
this.label = label;
|
||||
|
||||
@@ -121,7 +121,10 @@ public class OrmQueryDetail implements Serializable {
|
||||
}
|
||||
if (fetchPaths != null) {
|
||||
for (OrmQueryProperties join : fetchPaths.values()) {
|
||||
join.append(" fetch ", sb);
|
||||
if (sb.length() > 0) {
|
||||
sb.append(" ");
|
||||
}
|
||||
join.append("fetch ", sb);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
|
||||
@@ -294,7 +294,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
sb.append(path).append(" ");
|
||||
}
|
||||
if (!isEmpty()) {
|
||||
sb.append("(").append(rawProperties).append(") ");
|
||||
sb.append("(").append(rawProperties).append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -194,11 +194,6 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
throw new IllegalStateException(notExpectedMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterDeleteBean(Integer persistedBean) {
|
||||
throw new IllegalStateException(notExpectedMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a bean that has already been saved/deleted.
|
||||
*/
|
||||
|
||||
@@ -436,16 +436,6 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
deletingBeansHash.add(persistingBean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the persisted bean.
|
||||
*/
|
||||
@Override
|
||||
public void unregisterDeleteBean(Integer persistedBean) {
|
||||
if (deletingBeansHash != null) {
|
||||
deletingBeansHash.remove(persistedBean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a bean that has already been saved/deleted.
|
||||
*/
|
||||
|
||||
@@ -123,11 +123,6 @@ class NoTransaction implements SpiTransaction {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterDeleteBean(Integer hash) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegisteredDeleteBean(Integer hash) {
|
||||
return false;
|
||||
|
||||
@@ -154,6 +154,8 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
private final boolean offlineMigrationGeneration;
|
||||
|
||||
private final EnumType defaultEnumType;
|
||||
|
||||
// OPTIONAL ScalarTypes registered if Jackson/JsonNode is in the classpath
|
||||
|
||||
/**
|
||||
@@ -201,6 +203,8 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
this.offlineMigrationGeneration = DbOffline.isGenerateMigration();
|
||||
|
||||
this.defaultEnumType = config.getDefaultEnumType();
|
||||
|
||||
initialiseStandard(jsonDateTime, config);
|
||||
initialiseJavaTimeTypes(jsonDateTime, config);
|
||||
initialiseJodaTypes(jsonDateTime, config);
|
||||
@@ -645,8 +649,13 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
private ScalarTypeEnum<?> createEnumScalarTypePerSpec(Class<?> enumType, EnumType type) {
|
||||
if (type == null) {
|
||||
// default as per spec is ORDINAL
|
||||
return new ScalarTypeEnumStandard.OrdinalEnum(enumType);
|
||||
|
||||
if(defaultEnumType == EnumType.ORDINAL) {
|
||||
return new ScalarTypeEnumStandard.OrdinalEnum(enumType);
|
||||
|
||||
} else {
|
||||
return new ScalarTypeEnumStandard.StringEnum(enumType);
|
||||
}
|
||||
|
||||
} else if (type == EnumType.ORDINAL) {
|
||||
return new ScalarTypeEnumStandard.OrdinalEnum(enumType);
|
||||
|
||||
@@ -18,13 +18,53 @@ abstract class ScalarTypeArrayBase<T> extends ScalarTypeJsonCollection<T> {
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
return fromArray((Object[]) array.getArray());
|
||||
return fromArray(convertArray(array.getArray()));
|
||||
} finally {
|
||||
array.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] convertArray(Object array) {
|
||||
if (array instanceof Object[]) {
|
||||
return (Object[]) array;
|
||||
}
|
||||
if (array instanceof long[]) {
|
||||
return convertLongs((long[]) array);
|
||||
}
|
||||
if (array instanceof int[]) {
|
||||
return convertInts((int[]) array);
|
||||
}
|
||||
if (array instanceof double[]) {
|
||||
return convertDoubles((double[]) array);
|
||||
}
|
||||
throw new IllegalArgumentException("Unable to convert array " + array);
|
||||
}
|
||||
|
||||
private Object[] convertLongs(long[] o) {
|
||||
Long[] list = new Long[o.length];
|
||||
for (int i = 0; i < o.length; i++) {
|
||||
list[i] = o[i];
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private Object[] convertInts(int[] o) {
|
||||
Integer[] list = new Integer[o.length];
|
||||
for (int i = 0; i < o.length; i++) {
|
||||
list[i] = o[i];
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private Object[] convertDoubles(double[] o) {
|
||||
Double[] list = new Double[o.length];
|
||||
for (int i = 0; i < o.length; i++) {
|
||||
list[i] = o[i];
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
protected abstract T fromArray(Object[] array1);
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
|
||||
|
||||
@Override
|
||||
public LocalDate convertFromDate(Date date) {
|
||||
return new LocalDate(date.getTime());
|
||||
return LocalDate.fromDateFields(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.ebeaninternal.server.util;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class Md5 {
|
||||
public final class Md5 {
|
||||
|
||||
/**
|
||||
* Return the MD5 hash of the underlying sql.
|
||||
@@ -12,8 +12,7 @@ public class Md5 {
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] digest = md.digest(content.getBytes(StandardCharsets.UTF_8));
|
||||
return digestToHex(digest);
|
||||
return digestToHex(md.digest(content.getBytes(StandardCharsets.UTF_8)));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("MD5 hashing failed", e);
|
||||
}
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
<xsd:attribute name="sequenceInitial" type="xsd:positiveInteger"/>
|
||||
<xsd:attribute name="sequenceAllocate" type="xsd:positiveInteger"/>
|
||||
<xsd:attribute name="pkName" type="xsd:string"/>
|
||||
<xsd:attribute name="storageEngine" type="xsd:string"/>
|
||||
<xsd:attributeGroup ref="tablespaceAttributes"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
@@ -2,9 +2,9 @@ package io.ebean;
|
||||
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
@@ -64,12 +64,12 @@ public abstract class BaseTestCase {
|
||||
server().getMetaInfoManager().resetAllMetrics();
|
||||
}
|
||||
|
||||
protected BasicMetricVisitor visitMetricsBasic() {
|
||||
return server().getMetaInfoManager().visitBasic();
|
||||
protected ServerMetrics collectMetrics() {
|
||||
return server().getMetaInfoManager().collectMetrics();
|
||||
}
|
||||
|
||||
protected List<MetaTimedMetric> visitTimedMetrics() {
|
||||
return visitMetricsBasic().getTimedMetrics();
|
||||
return collectMetrics().getTimedMetrics();
|
||||
}
|
||||
|
||||
protected List<MetaTimedMetric> sqlMetrics() {
|
||||
@@ -170,6 +170,10 @@ public abstract class BaseTestCase {
|
||||
return isH2() || isPostgres();
|
||||
}
|
||||
|
||||
public boolean isPlatformSupportsDeleteTableAlias() {
|
||||
return spiEbeanServer().getDatabasePlatform().isSupportsDeleteTableAlias();
|
||||
}
|
||||
|
||||
public boolean isPersistBatchOnCascade() {
|
||||
return spiEbeanServer().getDatabasePlatform().getPersistBatchOnCascade() != PersistBatch.NONE;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.annotation.ForPlatform;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Contact;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
@@ -19,23 +20,25 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
@BeforeClass
|
||||
public static void resetStats() {
|
||||
Ebean.getDefaultServer().getMetaInfoManager().resetAllMetrics();
|
||||
DB.getDefault().getMetaInfoManager().resetAllMetrics();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void reportStats() {
|
||||
BasicMetricVisitor basic = DB.getDefault().getMetaInfoManager().visitBasic();
|
||||
for (MetaQueryMetric metric : basic.getDtoQueryMetrics()) {
|
||||
ServerMetrics metrics = DB.getDefault().getMetaInfoManager().collectMetrics();
|
||||
for (MetaQueryMetric metric : metrics.getDtoQueryMetrics()) {
|
||||
System.out.println(metric);
|
||||
}
|
||||
|
||||
System.out.println("-- transaction metrics --");
|
||||
for (MetaTimedMetric metric : basic.getTimedMetrics()) {
|
||||
for (MetaTimedMetric metric : metrics.getTimedMetrics()) {
|
||||
System.out.println(metric);
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore
|
||||
private static final ProfileLocation loc0 = ProfileLocation.create();
|
||||
|
||||
@ForPlatform(Platform.H2)
|
||||
@Test
|
||||
public void testPlanHits() {
|
||||
|
||||
@@ -43,27 +46,27 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
resetAllMetrics();
|
||||
|
||||
String[] prefix = { "Bl", "B", "Red", "jim" };
|
||||
String[] prefix = {"Bl", "B", "Red", "jim"};
|
||||
|
||||
for (String val : prefix) {
|
||||
List<ContactDto> list = Ebean.find(Contact.class)
|
||||
.select("email, " + concat("lastName", ", ", "firstName") + " as fullName").where()
|
||||
.istartsWith(concat("lastName", ", ", "firstName"), val).orderBy().asc("lastName").setMaxRows(10)
|
||||
.asDto(ContactDto.class).setLabel("prefixLoop").findList();
|
||||
|
||||
System.out.println("List:" + list);
|
||||
DB.find(Contact.class)
|
||||
.setProfileLocation(loc0)
|
||||
.select("email, " + concat("lastName", ", ", "firstName") + " as fullName").where()
|
||||
.istartsWith(concat("lastName", ", ", "firstName"), val).orderBy().asc("lastName").setMaxRows(10)
|
||||
.asDto(ContactDto.class).setLabel("prefixLoop").findList();
|
||||
}
|
||||
|
||||
BasicMetricVisitor basic = visitMetricsBasic();
|
||||
ServerMetrics metrics = collectMetrics();
|
||||
|
||||
List<MetaQueryMetric> stats = basic.getDtoQueryMetrics();
|
||||
List<MetaQueryMetric> stats = metrics.getDtoQueryMetrics();
|
||||
for (MetaQueryMetric stat : stats) {
|
||||
System.out.println(stat);
|
||||
long meanMicros = stat.getMean();
|
||||
assertThat(meanMicros).isLessThan(900_000);
|
||||
assertThat(stat.getProfileLocation()).isSameAs(loc0);
|
||||
}
|
||||
|
||||
assertThat(stats).hasSize(1);
|
||||
assertThat(stats.get(0).getCount()).isEqualTo(3);
|
||||
|
||||
assertThat(stats.get(0).getCount()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,11 +76,11 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
DtoQuery<ContactDto> query = Ebean.find(Contact.class)
|
||||
// we must explicitly add the id property for DTO query (if we want it)
|
||||
.select("id, email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email")
|
||||
.isNotNull("lastName").orderBy().asc("lastName").asDto(ContactDto.class).setLabel("explicitId")
|
||||
.setRelaxedMode();
|
||||
DtoQuery<ContactDto> query = DB.find(Contact.class)
|
||||
// we must explicitly add the id property for DTO query (if we want it)
|
||||
.select("id, email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email")
|
||||
.isNotNull("lastName").orderBy().asc("lastName").asDto(ContactDto.class).setLabel("explicitId")
|
||||
.setRelaxedMode();
|
||||
|
||||
List<ContactDto> dtos = query.findList();
|
||||
|
||||
@@ -86,12 +89,11 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
for (ContactDto dto : dtos) {
|
||||
assertThat(dto.getEmail()).isNotNull();
|
||||
assertThat(dto.getFullName()).isNotNull();
|
||||
System.out.println(dto);
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql.get(0)).contains("select t0.id, t0.email, " + concat("t0.last_name", ", ", "t0.first_name")
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -101,9 +103,9 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
DtoQuery<ContactDto> query = Ebean.find(Contact.class)
|
||||
.select("email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email")
|
||||
.isNotNull("lastName").orderBy().asc("lastName").asDto(ContactDto.class);
|
||||
DtoQuery<ContactDto> query = DB.find(Contact.class)
|
||||
.select("email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email")
|
||||
.isNotNull("lastName").orderBy().asc("lastName").asDto(ContactDto.class);
|
||||
|
||||
List<ContactDto> dtos = query.findList();
|
||||
|
||||
@@ -112,12 +114,11 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
for (ContactDto dto : dtos) {
|
||||
assertThat(dto.getEmail()).isNotNull();
|
||||
assertThat(dto.getFullName()).isNotNull();
|
||||
System.out.println(dto);
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql.get(0)).contains("select t0.email, " + concat("t0.last_name", ", ", "t0.first_name")
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -127,27 +128,26 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
List<ContactDto> contactDtos = Ebean.find(Contact.class).setLabel("emailFullName")
|
||||
.select("email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email")
|
||||
.isNotNull("lastName").orderBy().asc("lastName").setMaxRows(10).asDto(ContactDto.class).findList();
|
||||
List<ContactDto> contactDtos = DB.find(Contact.class).setLabel("emailFullName")
|
||||
.select("email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email")
|
||||
.isNotNull("lastName").orderBy().asc("lastName").setMaxRows(10).asDto(ContactDto.class).findList();
|
||||
|
||||
assertThat(contactDtos).isNotEmpty();
|
||||
|
||||
for (ContactDto dto : contactDtos) {
|
||||
assertThat(dto.getEmail()).isNotNull();
|
||||
assertThat(dto.getFullName()).isNotNull();
|
||||
System.out.println(dto);
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
if (isSqlServer()) {
|
||||
assertThat(sql.get(0)).contains("select top 10 t0.email, " + concat("t0.last_name", ", ", "t0.first_name")
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
|
||||
} else {
|
||||
assertThat(sql.get(0)).contains("select t0.email, " + concat("t0.last_name", ", ", "t0.first_name")
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,14 +158,13 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
List<ContactDto> contactDtos = Ebean.find(Contact.class)
|
||||
.select("id, email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email")
|
||||
.isNotNull("lastName").orderBy().asc("lastName").setMaxRows(10).asDto(ContactDto.class).findList();
|
||||
List<ContactDto> contactDtos = DB.find(Contact.class)
|
||||
.select("id, email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email")
|
||||
.isNotNull("lastName").orderBy().asc("lastName").setMaxRows(10).asDto(ContactDto.class).findList();
|
||||
|
||||
assertThat(contactDtos).isNotEmpty();
|
||||
|
||||
for (ContactDto dto : contactDtos) {
|
||||
System.out.println(dto);
|
||||
assertThat(dto.getId()).isNotNull();
|
||||
assertThat(dto.getFullName()).isNotNull();
|
||||
assertThat(dto.getEmail()).isNotNull();
|
||||
@@ -174,11 +173,11 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
if (isSqlServer()) {
|
||||
assertThat(sql.get(0)).contains("select top 10 t0.id, t0.email, "
|
||||
+ concat("t0.last_name", ", ", "t0.first_name")
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
+ concat("t0.last_name", ", ", "t0.first_name")
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
} else {
|
||||
assertThat(sql.get(0)).contains("select t0.id, t0.email, " + concat("t0.last_name", ", ", "t0.first_name")
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,22 +188,20 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
List<ContactDto> contactDtos = Ebean.find(Contact.class)
|
||||
.select(concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("lastName").orderBy()
|
||||
.asc("lastName").asDto(ContactDto.class).setFirstRow(2).setMaxRows(5).findList();
|
||||
List<ContactDto> contactDtos = DB.find(Contact.class)
|
||||
.select(concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("lastName").orderBy()
|
||||
.asc("lastName").asDto(ContactDto.class).setFirstRow(2).setMaxRows(5).findList();
|
||||
|
||||
assertThat(contactDtos).isNotEmpty();
|
||||
|
||||
for (ContactDto dto : contactDtos) {
|
||||
System.out.println(dto);
|
||||
assertThat(dto.getFullName()).isNotNull();
|
||||
assertThat(dto.getId()).isNull();
|
||||
assertThat(dto.getEmail()).isNull();
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql.get(0))
|
||||
.contains("select " + concat("t0.last_name", ", ", "t0.first_name") + " fullName from contact t0 where");
|
||||
assertThat(sql.get(0)).contains("select " + concat("t0.last_name", ", ", "t0.first_name") + " fullName from contact t0 where");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -214,9 +211,9 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
List<ContactTotals> contactDtos = Ebean.find(Contact.class).select("lastName, count(*) as totalCount").where()
|
||||
.isNotNull("lastName").having().gt("count(*)", 1).orderBy().desc("count(*)").asDto(ContactTotals.class)
|
||||
.findList();
|
||||
List<ContactTotals> contactDtos = DB.find(Contact.class).select("lastName, count(*) as totalCount").where()
|
||||
.isNotNull("lastName").having().gt("count(*)", 1).orderBy().desc("count(*)").asDto(ContactTotals.class)
|
||||
.findList();
|
||||
|
||||
assertThat(contactDtos).isNotEmpty();
|
||||
|
||||
@@ -226,8 +223,7 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql.get(0)).contains(
|
||||
"select t0.last_name, count(*) totalCount from contact t0 where t0.last_name is not null group by t0.last_name having count(*) > ?");
|
||||
assertThat(sql.get(0)).contains("select t0.last_name, count(*) totalCount from contact t0 where t0.last_name is not null group by t0.last_name having count(*) > ?");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -235,8 +231,8 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
List<ContactTotals> contactDtos = Ebean.find(Contact.class).select("lastName, count(*) as totalCount").where()
|
||||
.isNotNull("lastName").asDto(ContactTotals.class).findList();
|
||||
List<ContactTotals> contactDtos = DB.find(Contact.class).select("lastName, count(*) as totalCount").where()
|
||||
.isNotNull("lastName").asDto(ContactTotals.class).findList();
|
||||
|
||||
assertThat(contactDtos).isNotEmpty();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.ebean.annotation.ForPlatform;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
@@ -24,6 +25,7 @@ public class DtoQueryTest extends BaseTestCase {
|
||||
public void dto_findList_constructorMatch() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
resetAllMetrics();
|
||||
|
||||
DtoQuery<DCust> dtoQuery = server().findDto(DCust.class, "select id, name from o_customer");
|
||||
|
||||
@@ -31,6 +33,17 @@ public class DtoQueryTest extends BaseTestCase {
|
||||
|
||||
log.info(list.toString());
|
||||
assertThat(list).isNotEmpty();
|
||||
|
||||
ServerMetrics metrics = collectMetrics();
|
||||
|
||||
List<MetaQueryMetric> stats = metrics.getDtoQueryMetrics();
|
||||
for (MetaQueryMetric stat : stats) {
|
||||
long meanMicros = stat.getMean();
|
||||
assertThat(meanMicros).isLessThan(900_000);
|
||||
}
|
||||
|
||||
assertThat(stats).hasSize(1);
|
||||
assertThat(stats.get(0).getCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -188,7 +201,7 @@ public class DtoQueryTest extends BaseTestCase {
|
||||
MetaQueryMetric queryMetric = stats.get(0);
|
||||
assertThat(queryMetric.getLabel()).isEqualTo("basic");
|
||||
assertThat(queryMetric.getCount()).isEqualTo(3);
|
||||
assertThat(queryMetric.getName()).isEqualTo("basic");
|
||||
assertThat(queryMetric.getName()).isEqualTo("DCust_basic");
|
||||
|
||||
|
||||
server().findDto(DCust.class, "select c4.id, c4.name from o_customer c4 where lower(c4.name) = :name")
|
||||
@@ -196,7 +209,7 @@ public class DtoQueryTest extends BaseTestCase {
|
||||
.setParameter("name", "rob")
|
||||
.findList();
|
||||
|
||||
BasicMetricVisitor metric2 = server().getMetaInfoManager().visitBasic();
|
||||
ServerMetrics metric2 = server().getMetaInfoManager().collectMetrics();
|
||||
|
||||
stats = metric2.getDtoQueryMetrics();
|
||||
assertThat(stats).hasSize(2);
|
||||
|
||||
@@ -127,7 +127,7 @@ public class NamedDtoQueryTest extends BaseTestCase {
|
||||
MetaQueryMetric queryMetric = stats.get(0);
|
||||
assertThat(queryMetric.getLabel()).isEqualTo("basic");
|
||||
assertThat(queryMetric.getCount()).isEqualTo(3);
|
||||
assertThat(queryMetric.getName()).isEqualTo("basic");
|
||||
assertThat(queryMetric.getName()).isEqualTo("DCust_basic");
|
||||
|
||||
|
||||
server().createNamedDtoQuery(DCust.class, "findByName_c4")
|
||||
|
||||
@@ -2,8 +2,8 @@ package io.ebean;
|
||||
|
||||
import io.ebean.annotation.IgnorePlatform;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.meta.MetaOrmQueryMetric;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Country;
|
||||
@@ -38,8 +38,8 @@ public class UpdateQueryTest extends BaseTestCase {
|
||||
|
||||
assertThat(query.getGeneratedSql()).contains("update o_customer set status=?, updtime=? where status = ? and id > ?");
|
||||
|
||||
BasicMetricVisitor basic = visitMetricsBasic();
|
||||
List<MetaOrmQueryMetric> ormQueryMetrics = basic.getOrmQueryMetrics();
|
||||
ServerMetrics metrics = collectMetrics();
|
||||
List<MetaOrmQueryMetric> ormQueryMetrics = metrics.getOrmQueryMetrics();
|
||||
assertThat(ormQueryMetrics).hasSize(1);
|
||||
assertThat(ormQueryMetrics.get(0).getType()).isEqualTo(Customer.class);
|
||||
assertThat(ormQueryMetrics.get(0).getLabel()).isEqualTo("updateActive");
|
||||
@@ -68,8 +68,8 @@ public class UpdateQueryTest extends BaseTestCase {
|
||||
|
||||
assertThat(sql.get(0)).contains("update o_customer set status = status");
|
||||
|
||||
BasicMetricVisitor basic = visitMetricsBasic();
|
||||
List<MetaOrmQueryMetric> ormQueryMetrics = basic.getOrmQueryMetrics();
|
||||
ServerMetrics metrics = collectMetrics();
|
||||
List<MetaOrmQueryMetric> ormQueryMetrics = metrics.getOrmQueryMetrics();
|
||||
assertThat(ormQueryMetrics).hasSize(1);
|
||||
assertThat(ormQueryMetrics.get(0).getType()).isEqualTo(Customer.class);
|
||||
assertThat(ormQueryMetrics.get(0).getLabel()).isEqualTo("updateAll");
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package io.ebean.server.type;
|
||||
|
||||
public enum MySex {
|
||||
MALE, FEMALE
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import io.ebeaninternal.server.core.bootup.BootupClasses;
|
||||
import io.ebeaninternal.server.type.DefaultTypeManager;
|
||||
import io.ebeaninternal.server.type.RsetDataReader;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarTypeEnumStandard;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.ivo.Money;
|
||||
@@ -95,6 +96,21 @@ public class TestTypeManager extends BaseTestCase {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithConfig() {
|
||||
DefaultTypeManager typeManager1 = createTypeManager();
|
||||
ScalarType<?> type1 = typeManager1.createEnumScalarType(MySex.class, null);
|
||||
assertThat(type1 instanceof ScalarTypeEnumStandard.OrdinalEnum);
|
||||
//
|
||||
DefaultTypeManager typeManager2 = createTypeManagerDefaultEnumTypeString();
|
||||
ScalarType<?> type2 = typeManager2.createEnumScalarType(MySex.class, null);
|
||||
assertThat(type2 instanceof ScalarTypeEnumStandard.StringEnum);
|
||||
//
|
||||
DefaultTypeManager typeManager3 = createTypeManagerDefaultEnumTypeString();
|
||||
ScalarType<?> type3 = typeManager3.createEnumScalarType(MySex.class, EnumType.ORDINAL);
|
||||
assertThat(type3 instanceof ScalarTypeEnumStandard.OrdinalEnum);
|
||||
}
|
||||
|
||||
private DefaultTypeManager createTypeManager() {
|
||||
|
||||
ServerConfig serverConfig = new ServerConfig();
|
||||
@@ -106,6 +122,18 @@ public class TestTypeManager extends BaseTestCase {
|
||||
return new DefaultTypeManager(serverConfig, bootupClasses);
|
||||
}
|
||||
|
||||
private DefaultTypeManager createTypeManagerDefaultEnumTypeString() {
|
||||
|
||||
ServerConfig serverConfig = new ServerConfig();
|
||||
serverConfig.setDatabasePlatform(new H2Platform());
|
||||
serverConfig.setDefaultEnumType(EnumType.STRING);
|
||||
|
||||
BootupClasses bootupClasses = new BootupClasses();
|
||||
bootupClasses.getAttributeConverters().add(MoneyTypeConverter.class);
|
||||
|
||||
return new DefaultTypeManager(serverConfig, bootupClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test double DataReader implementation.
|
||||
*/
|
||||
|
||||
+18
@@ -2,6 +2,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
|
||||
import io.ebean.config.dbplatform.oracle.OraclePlatform;
|
||||
@@ -62,6 +63,23 @@ public class BaseTableDdlTest {
|
||||
assertThat(ddl).contains("alter table mytable add column col_name varchar2(20)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddColumn_withTypeConversion_clickHouseVarchar() throws IOException {
|
||||
|
||||
ClickHouseTableDdl ddlGen = new ClickHouseTableDdl(serverConfig, PlatformDdlBuilder.create(new ClickHousePlatform()));
|
||||
|
||||
DdlWrite write = new DdlWrite();
|
||||
|
||||
Column column = new Column();
|
||||
column.setName("col_name");
|
||||
column.setType("varchar(20)");
|
||||
|
||||
ddlGen.alterTableAddColumn(write.apply(), "mytable", column, false, false);
|
||||
|
||||
String ddl = write.apply().getBuffer();
|
||||
assertThat(ddl).contains("alter table mytable add column col_name String");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlterColumnComment() throws IOException {
|
||||
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ClickHouseDbArrayTest {
|
||||
|
||||
@Test
|
||||
public void logicalToNative() {
|
||||
|
||||
assertThat(ClickHouseDbArray.logicalToNative("uuid[]")).isEqualTo("Array(UUID)");
|
||||
assertThat(ClickHouseDbArray.logicalToNative("varchar[]")).isEqualTo("Array(String)");
|
||||
assertThat(ClickHouseDbArray.logicalToNative("integer[]")).isEqualTo("Array(UInt32)");
|
||||
assertThat(ClickHouseDbArray.logicalToNative("bigint[]")).isEqualTo("Array(UInt64)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logicalToNative_withFallbackDefined() {
|
||||
|
||||
assertThat(ClickHouseDbArray.logicalToNative("uuid[]:(1000)")).isEqualTo("Array(UUID)");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package io.ebeaninternal.server.autotune.service;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.bean.NodeUsageCollector;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.bean.ObjectGraphOrigin;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Order;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
|
||||
private BeanDescriptor<Order> desc = getBeanDescriptor(Order.class);
|
||||
|
||||
@Test
|
||||
public void buildDetail() {
|
||||
|
||||
NodeUsageCollector c = node("customer");
|
||||
c.addUsed("id");
|
||||
c.addUsed("name");
|
||||
|
||||
ProfileOrigin po = new ProfileOrigin(null, false, 1, 1);
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString().trim()).isEqualTo("fetch customer (name)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildDetail_selectFetch() {
|
||||
|
||||
NodeUsageCollector c = node("customer");
|
||||
c.addUsed("id");
|
||||
c.addUsed("name");
|
||||
|
||||
ProfileOrigin po = new ProfileOrigin(null, false, 1, 1);
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
c = node(null);
|
||||
c.addUsed("orderDate");
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString()).isEqualTo("select (orderDate) fetch customer (name)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildDetail_expect_mergeFetchToSelect() {
|
||||
|
||||
NodeUsageCollector c = node("customer");
|
||||
c.addUsed("id");
|
||||
|
||||
ProfileOrigin po = new ProfileOrigin(null, false, 1, 1);
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
c = node(null);
|
||||
c.addUsed("orderDate");
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString().trim()).isEqualTo("select (orderDate,customer)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildDetail_expect_mergeFetchToParentFetch() {
|
||||
|
||||
ProfileOrigin po = new ProfileOrigin(null, false, 1, 1);
|
||||
|
||||
NodeUsageCollector c = node(null);
|
||||
c.addUsed("orderDate");
|
||||
c.addUsed("customer");
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
c = node("customer");
|
||||
c.addUsed("billingAddress");
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
c = node("customer.billingAddress");
|
||||
c.addUsed("id");
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString()).isEqualTo("select (orderDate) fetch customer (billingAddress)");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void buildDetail_expect_mergeMulit() {
|
||||
|
||||
ProfileOrigin po = new ProfileOrigin(null, false, 1, 1);
|
||||
|
||||
//fetch details (id,orderQty,shipQty,unitPrice)
|
||||
|
||||
NodeUsageCollector c = node(null);
|
||||
c.addUsed("details");
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
c = node("details");
|
||||
c.addUsed("id");
|
||||
c.addUsed("orderQty");
|
||||
c.addUsed("shipQty");
|
||||
c.addUsed("unitPrice");
|
||||
c.addUsed("product");
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
//fetch details.product (id,name)
|
||||
c = node("details.product");
|
||||
c.addUsed("id");
|
||||
c.addUsed("name");
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
assertThat(detail.asString()).isEqualTo("fetch details (orderQty,shipQty,unitPrice) fetch details.product (name)");
|
||||
}
|
||||
|
||||
private NodeUsageCollector node(String path) {
|
||||
ObjectGraphNode node = new ObjectGraphNode((ObjectGraphOrigin)null, path);
|
||||
return new NodeUsageCollector(node, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueries() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
//DB.createQuery(Order.class, "select (orderDate) fetch customer (billingAddress)").findList();
|
||||
|
||||
// we prefer this first query other the second one
|
||||
DB.createQuery(Order.class, "select (orderDate,customer)").findList();
|
||||
DB.createQuery(Order.class, "select (orderDate) fetch customer (id)").findList();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,14 @@ package io.ebeaninternal.server.expression;
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Query;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
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.Test;
|
||||
import org.tests.model.nofk.EUserNoFk;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -26,6 +30,29 @@ public class IsEmptyExpressionQueryTest extends BaseTestCase {
|
||||
assertThat(sqlOf(query)).contains("select t0.id from o_customer t0 where not exists (select 1 from contact x where x.customer_id = t0.id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteQuery_isEmpty() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.find(EUserNoFk.class)
|
||||
.where().isEmpty("files")
|
||||
.delete();
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
|
||||
if (isPlatformSupportsDeleteTableAlias()) {
|
||||
assertThat(sql.get(0)).contains("delete from euser_no_fk t0 where not exists (select 1 from efile_no_fk x where x.owner_user_id = t0.user_id)");
|
||||
} else if (isMySql()) {
|
||||
assertThat(sql.get(0)).contains("delete t0 from euser_no_fk t0 where not exists (select 1 from efile_no_fk x where x.owner_user_id = t0.user_id)");
|
||||
} else {
|
||||
assertThat(sql.get(0)).contains("delete from euser_no_fk where not exists (select 1 from efile_no_fk x where x.owner_user_id = user_id)");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotEmpty() {
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ public class BasicProfileLocationTest {
|
||||
|
||||
assertThat(loc.obtain()).endsWith(":12)");
|
||||
assertThat(loc.shortDescription()).isEqualTo("NativeMethodAccessorImpl.invoke0(Native Method:12)");
|
||||
assertThat(loc.label()).isEqualTo("NativeMethodAccessorImpl.invoke0");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -23,6 +25,7 @@ public class BasicProfileLocationTest {
|
||||
BasicProfileLocation loc = new BasicProfileLocation("com.foo.Bar.all");
|
||||
assertThat(loc.obtain()).isEqualTo("com.foo.Bar.all");
|
||||
assertThat(loc.shortDescription()).isEqualTo("Bar.all");
|
||||
assertThat(loc.label()).isEqualTo("Bar.all");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -31,5 +34,6 @@ public class BasicProfileLocationTest {
|
||||
BasicProfileLocation loc = new BasicProfileLocation("foo.Bar.all");
|
||||
assertThat(loc.obtain()).isEqualTo("foo.Bar.all");
|
||||
assertThat(loc.shortDescription()).isEqualTo("Bar.all");
|
||||
assertThat(loc.label()).isEqualTo("Bar.all");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.SortMetric;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class SortMetricTest {
|
||||
|
||||
private Comparator<MetaTimedMetric> sortMetric = SortMetric.NAME;
|
||||
|
||||
@Test
|
||||
public void compare_list() {
|
||||
|
||||
List<DTimeMetricStats> list = new ArrayList<>();
|
||||
list.add(create("d"));
|
||||
list.add(create("b"));
|
||||
list.add(create("c"));
|
||||
list.add(create(null));
|
||||
list.add(create("a"));
|
||||
list.sort(sortMetric);
|
||||
|
||||
String names = list.stream().map(DTimeMetricStats::getName).collect(Collectors.joining());
|
||||
|
||||
assertEquals("nullabcd", names);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compare_when_same() {
|
||||
|
||||
assertEquals(0, sortMetric.compare(create("foo"), create("foo")));
|
||||
assertEquals(0, sortMetric.compare(create(null), create(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compare_when_less() {
|
||||
|
||||
assertEquals(-1, sortMetric.compare(create("a"), create("b")));
|
||||
assertEquals(-1, sortMetric.compare(create("foo"), create("goo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compare_when_more() {
|
||||
|
||||
assertEquals(1, sortMetric.compare(create("b"), create("a")));
|
||||
assertEquals(1, sortMetric.compare(create("goo"), create("foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compare_when_nulls() {
|
||||
|
||||
assertEquals(0, sortMetric.compare(create(null), create(null)));
|
||||
assertEquals(1, sortMetric.compare(create("foo"), create(null)));
|
||||
assertEquals(-1, sortMetric.compare(create(null), create("foo")));
|
||||
}
|
||||
|
||||
private DTimeMetricStats create(String name) {
|
||||
return new DTimeMetricStats(MetricType.L2, name, 0, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class UtilLocationTest {
|
||||
|
||||
@Test
|
||||
public void label() {
|
||||
|
||||
assertThat(UtilLocation.label("foo")).isEqualTo("foo");
|
||||
assertThat(UtilLocation.label("ProfileLocationTest$Other.<init>(ProfileLocationTest.java:47)")).isEqualTo("ProfileLocationTest$Other.init");
|
||||
}
|
||||
}
|
||||
@@ -62,14 +62,14 @@ public class OrmQueryPropertiesTest {
|
||||
public void append_when_someProperties() {
|
||||
|
||||
OrmQueryProperties p1 = new OrmQueryProperties(null, "id,name");
|
||||
assertThat(append("select ", p1)).isEqualTo("select (id,name) ");
|
||||
assertThat(append("select ", p1)).isEqualTo("select (id,name)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void append_when_somePropertiesWithOptions() {
|
||||
|
||||
OrmQueryProperties p1 = new OrmQueryProperties(null, "id,name +cache");
|
||||
assertThat(append("select ", p1)).isEqualTo("select (id,name +cache) ");
|
||||
assertThat(append("select ", p1)).isEqualTo("select (id,name +cache)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -83,7 +83,7 @@ public class OrmQueryPropertiesTest {
|
||||
public void append_when_path_and_somePropertiesWithOptions() {
|
||||
|
||||
OrmQueryProperties p1 = new OrmQueryProperties("customer", "id,name +cache");
|
||||
assertThat(append("fetch ", p1)).isEqualTo("fetch customer (id,name +cache) ");
|
||||
assertThat(append("fetch ", p1)).isEqualTo("fetch customer (id,name +cache)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user