Compare commits

...
Author SHA1 Message Date
rob bygrave f7035eb228 [maven-release-plugin] prepare release ebean-parent-12.8.1 2021-03-31 00:06:04 +13:00
Robin Bygrave 1bf09ddd38 #2211 - Improve javadoc for queryPlanTTLSeconds
Hopefully people understand it is unrelated. Unfortunately we now have some properties with similar names so maybe we need a better name for this sometime in the future.
2021-03-30 23:49:44 +13:00
Robin Bygrave a8bf27da55 #2211 - Improve query plan capture - Align config property names
collectQueryPlans -> queryPlanEnable
collectQueryPlanThresholdMicros -> queryPlanThresholdMicros

properties keys:
ebean.collectQueryPlans -> ebean.queryPlan.enable
ebean.collectQueryPlanThresholdMicros -> ebean.queryPlan.thresholdMicros

This then aligns all the queryPlan related properties together which is important in order to avoid confusion.
2021-03-30 23:44:30 +13:00
Robin Bygrave ee8bb5ef75 #2211 - Improve query plan capture - Add default mechanism to log captured query plans 2021-03-30 23:19:21 +13:00
Robin Bygrave bcfd9cab1d #2210 - Improve query plan capture - bump threshold multiplier to 1.5 2021-03-30 16:05:42 +13:00
Robin Bygrave 2a5fd7f3a9 #2210 - Improve query plan capture - ability to change default threshold micros 2021-03-30 16:02:27 +13:00
Robin Bygrave f8c37772d4 Tidy internals of DefaultServer with @Nonnull, <>, Collections.singleton(), remove unused queryBatchSize 2021-03-30 10:23:28 +13:00
Robin Bygrave d988dbe69d #2209 - Registering [default] as the default server but [default] is already registered as the default exception with hot-reload 2021-03-30 09:56:48 +13:00
rob bygrave 5cb9b9b237 #2208 - When using fetchCache() with a single id / findOne ... doesn't hit L2 cache
Plus #2207 ENH: Query beans add support for nested FetchGroup
2021-03-29 23:21:07 +13:00
rob bygrave 819d391495 Bump agent maven plugin to 12.8.1 2021-03-26 20:31:57 +13:00
rob bygrave 1f0ed629a7 Bump java8-oss to 3.1 with removal of --illegal-access=permit argLine 2021-03-26 20:31:03 +13:00
rob bygrave 91044d0a75 javadoc only - improve javadoc for DatabaseConfig.loadFromProperties() 2021-03-25 23:13:51 +13:00
rob bygrave 57736185d0 #2206 - Postgres DDL generation - for create index use "if not exists" clause 2021-03-25 23:13:15 +13:00
Robin Bygrave 8992980689 #2206 - Postgres DDL generation - for create index use "if not exists" clause 2021-03-25 16:45:44 +13:00
rob bygrave f341ff0167 #2205 - [oracle] Incorrect DDL generated for alter table add column 2021-03-23 23:58:28 +13:00
rob bygrave abfa841ed7 #2205 - [oracle] Incorrect DDL generated for alter table add column 2021-03-23 23:51:07 +13:00
Rob BygraveandGitHub e68adf53bc Merge pull request #2204 from ebean-orm/feature/Honor-setLazyLoadBatchSize
Honor setLazyLoadBatchSize() when using FetchConfig.ofLazy() of unspecified size
2021-03-23 23:25:08 +13:00
Robin Bygrave 562d175de1 With FetchConfig.ofLazy() honor query.setLazyLoadBatchSize()
Use 0 for default lazy batch such that it honors a value set via query.setLazyLoadBatchSize()
2021-03-23 23:17:07 +13:00
Robin Bygrave b5cef354ad Refactor simplify LoadContext.getSecondaryQueriesMinBatchSize()
No functional change here. Just move the constant into DLoadContext.
2021-03-23 14:53:30 +13:00
Rob Bygrave 09a887aeb7 #2203 - Support use of Java Record type with @Entity, @Embeddable @IdClass
Add tests for IdClass use of record type
2021-03-23 00:19:52 +13:00
Rob Bygrave 53c903fa48 #2203 - Support use of Java Record type with @Entity, @Embeddable @IdClass
Add tests for EmbeddedId use of record type
2021-03-23 00:10:28 +13:00
Rob Bygrave 548454c90d #2203 - Support use of Java Record type with @Entity, @Embeddable @IdClass
Add tests for embedded use of record type
2021-03-22 23:57:14 +13:00
Rob Bygrave 94bab5f83b #2203 - Support use of Java Record type with @Entity, @Embeddable @IdClass
Add tests for entity use of record type
2021-03-22 23:44:10 +13:00
Rob Bygrave 78965c1ac4 #2203 - Support use of Java Record type with @Entity, @Embeddable @IdClass
Allow java.lang.Record to be a root level parent for entity beans etc
2021-03-22 23:43:35 +13:00
Rob Bygrave 00b0fcaeb3 #2202 - kotlin-maven-plugin issue with JDK 16
Temporary modify tests to not invoke the kotlin-maven-plugin while adding JDK 16 specific tests.

Hitting error InaccessibleObjectException: Unable to make protected void java.util.ResourceBundle.setParent(java.util.ResourceBundle) accessible: module java.base does not "opens java.util" to unnamed module @62732be7
2021-03-22 23:24:16 +13:00
rob bygrave f7905c8e94 [maven-release-plugin] prepare for next development iteration 2021-03-18 22:30:17 +13:00
83 changed files with 1526 additions and 307 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<name>ebean api</name>
@@ -78,7 +78,7 @@ public class DatabaseFactory {
Database server = createInternal(config);
if (config.isRegister()) {
if (config.isDefaultServer()) {
if (defaultServerName != null) {
if (defaultServerName != null && !defaultServerName.equals(config.getName())) {
throw new IllegalStateException("Registering [" + config.getName() + "] as the default server but [" + defaultServerName + "] is already registered as the default");
}
defaultServerName = config.getName();
@@ -46,7 +46,7 @@ public class FetchConfig implements Serializable {
/**
* Deprecated - migrate to one of the static factory methods like {@link FetchConfig#ofQuery()}
*
* <p>
* Construct using default JOIN mode.
*/
@Deprecated
@@ -89,7 +89,7 @@ public class FetchConfig implements Serializable {
* Return FetchConfig to lazily load the relationship.
*/
public static FetchConfig ofLazy() {
return new FetchConfig(LAZY_MODE, 10);
return new FetchConfig(LAZY_MODE, 0);
}
/**
@@ -110,8 +110,8 @@ public class FetchConfig implements Serializable {
* We want to migrate away from mutating FetchConfig to a fully immutable FetchConfig.
*/
private FetchConfig mutate(int mode, int batchSize) {
if (batchSize < 1) {
throw new IllegalArgumentException("batch size "+batchSize+" must be > 0");
if (batchSize < 0) {
throw new IllegalArgumentException("batch size " + batchSize + " must be > 0");
}
this.mode = mode;
this.batchSize = batchSize;
@@ -124,7 +124,7 @@ public class FetchConfig implements Serializable {
*/
@Deprecated
public FetchConfig lazy() {
return mutate(LAZY_MODE, 10);
return mutate(LAZY_MODE, 0);
}
/**
@@ -137,7 +137,7 @@ public class FetchConfig implements Serializable {
/**
* Deprecated - migrate to FetchConfig.ofQuery().
*
* <p>
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
* <p>
@@ -150,17 +150,15 @@ public class FetchConfig implements Serializable {
/**
* Deprecated - migrate to FetchConfig.ofQuery(batchSize).
*
* <p>
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
* <p>
* The queryBatchSize is the number of parent id's that this separate query
* will load per batch.
* </p>
* <p>
* This will load all beans on this path eagerly unless a {@link #lazy(int)}
* is also used.
* </p>
*
* @param batchSize the batch size used to load beans on this path
*/
@@ -171,13 +169,12 @@ public class FetchConfig implements Serializable {
/**
* Deprecated - migrate to FetchConfig.ofQuery(batchSize).
*
* <p>
* Eagerly fetch the first batch of beans on this path.
* This is similar to {@link #query(int)} but only fetches the first batch.
* <p>
* If there are more parent beans than the batch size then they will not be
* loaded eagerly but instead use lazy loading.
* </p>
*
* @param batchSize the number of parent beans this path is populated for
*/
@@ -188,7 +185,7 @@ public class FetchConfig implements Serializable {
/**
* Deprecated - migrate to FetchConfig.ofCache().
*
* <p>
* Eagerly fetch the beans fetching the beans from the L2 bean cache
* and using the DB for beans not in the cache.
*/
@@ -498,14 +498,23 @@ public class DatabaseConfig {
private boolean notifyL2CacheInForeground;
/**
* Set to true to support query plan capture.
* Set to true to enable bind capture required for query plan capture.
*/
private boolean collectQueryPlans;
private boolean queryPlanEnable;
/**
* The default threshold in micros for collecting query plans.
*/
private long collectQueryPlanThresholdMicros = Long.MAX_VALUE;
private long queryPlanThresholdMicros = Long.MAX_VALUE;
/**
* Set to true to enable automatic periodic query plan capture.
*/
private boolean queryPlanCapture;
private long queryPlanCapturePeriodSecs = 60 * 10; // 10 minutes
private long queryPlanCaptureMaxTimeMillis = 10_000; // 10 seconds
private int queryPlanCaptureMaxCount = 10;
private QueryPlanListener queryPlanListener;
/**
* The time in millis used to determine when a query is alerted for being slow.
@@ -1045,7 +1054,6 @@ public class DatabaseConfig {
* This is a performance optimisation to reduce the number times Ebean
* requests a sequence to be used as an Id for a bean (aka reduce network
* chatter).
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
@@ -2693,7 +2701,10 @@ public class DatabaseConfig {
}
/**
* Load settings from ebean.properties.
* Load settings from application.properties, application.yaml and other sources.
* <p>
* Uses <code>avaje-config</code> to load configuration properties. Goto https://avaje.io/config
* for detail on how and where properties are loaded from.
*/
public void loadFromProperties() {
this.properties = Config.asProperties();
@@ -2802,8 +2813,12 @@ public class DatabaseConfig {
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans);
collectQueryPlanThresholdMicros = p.getLong("collectQueryPlanThresholdMicros", collectQueryPlanThresholdMicros);
queryPlanEnable = p.getBoolean("queryPlan.enable", queryPlanEnable);
queryPlanThresholdMicros = p.getLong("queryPlan.thresholdMicros", queryPlanThresholdMicros);
queryPlanCapture = p.getBoolean("queryPlan.capture", queryPlanCapture);
queryPlanCapturePeriodSecs = p.getLong("queryPlan.capturePeriodSecs", queryPlanCapturePeriodSecs);
queryPlanCaptureMaxTimeMillis = p.getLong("queryPlan.captureMaxTimeMillis", queryPlanCaptureMaxTimeMillis);
queryPlanCaptureMaxCount = p.getInt("queryPlan.captureMaxCount", queryPlanCaptureMaxCount);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
localOnlyL2Cache = p.getBoolean("localOnlyL2Cache", localOnlyL2Cache);
@@ -3093,14 +3108,17 @@ public class DatabaseConfig {
}
/**
* Return the query plan time to live.
* Return the time to live for ebean's internal query plan.
*/
public int getQueryPlanTTLSeconds() {
return queryPlanTTLSeconds;
}
/**
* Set the query plan time to live.
* Set the time to live for ebean's internal query plan.
* <p>
* This is the plan that knows how to execute the query, read the result
* and collects execution metrics. By default this is set to 5 mins.
*/
public void setQueryPlanTTLSeconds(int queryPlanTTLSeconds) {
this.queryPlanTTLSeconds = queryPlanTTLSeconds;
@@ -3173,29 +3191,110 @@ public class DatabaseConfig {
/**
* Return true if query plan capture is enabled.
*/
public boolean isCollectQueryPlans() {
return collectQueryPlans;
public boolean isQueryPlanEnable() {
return queryPlanEnable;
}
/**
* Set to true to enable query plan capture.
*/
public void setCollectQueryPlans(boolean collectQueryPlans) {
this.collectQueryPlans = collectQueryPlans;
public void setQueryPlanEnable(boolean queryPlanEnable) {
this.queryPlanEnable = queryPlanEnable;
}
/**
* Return the query plan collection threshold in microseconds.
*/
public long getCollectQueryPlanThresholdMicros() {
return collectQueryPlanThresholdMicros;
public long getQueryPlanThresholdMicros() {
return queryPlanThresholdMicros;
}
/**
* Set the query plan collection threshold in microseconds.
* <p>
* Queries executing slower than this will have bind values captured such that later
* the query plan can be captured and reported.
*/
public void setCollectQueryPlanThresholdMicros(long collectQueryPlanThresholdMicros) {
this.collectQueryPlanThresholdMicros = collectQueryPlanThresholdMicros;
public void setQueryPlanThresholdMicros(long queryPlanThresholdMicros) {
this.queryPlanThresholdMicros = queryPlanThresholdMicros;
}
/**
* Return true if periodic capture of query plans is enabled.
*/
public boolean isQueryPlanCapture() {
return queryPlanCapture;
}
/**
* Set to true to turn on periodic capture of query plans.
*/
public void setQueryPlanCapture(boolean queryPlanCapture) {
this.queryPlanCapture = queryPlanCapture;
}
/**
* Return the frequency to capture query plans.
*/
public long getQueryPlanCapturePeriodSecs() {
return queryPlanCapturePeriodSecs;
}
/**
* Set the frequency in seconds to capture query plans.
*/
public void setQueryPlanCapturePeriodSecs(long queryPlanCapturePeriodSecs) {
this.queryPlanCapturePeriodSecs = queryPlanCapturePeriodSecs;
}
/**
* Return the time after which a capture query plans request will
* stop capturing more query plans.
* <p>
* Effectively this controls the amount of load/time we want to
* allow for query plan capture.
*/
public long getQueryPlanCaptureMaxTimeMillis() {
return queryPlanCaptureMaxTimeMillis;
}
/**
* Set the time after which a capture query plans request will
* stop capturing more query plans.
* <p>
* Effectively this controls the amount of load/time we want to
* allow for query plan capture.
*/
public void setQueryPlanCaptureMaxTimeMillis(long queryPlanCaptureMaxTimeMillis) {
this.queryPlanCaptureMaxTimeMillis = queryPlanCaptureMaxTimeMillis;
}
/**
* Return the max number of query plans captured per request.
*/
public int getQueryPlanCaptureMaxCount() {
return queryPlanCaptureMaxCount;
}
/**
* Set the max number of query plans captured per request.
*/
public void setQueryPlanCaptureMaxCount(int queryPlanCaptureMaxCount) {
this.queryPlanCaptureMaxCount = queryPlanCaptureMaxCount;
}
/**
* Return the listener used to process captured query plans.
*/
public QueryPlanListener getQueryPlanListener() {
return queryPlanListener;
}
/**
* Set the listener used to process captured query plans.
*/
public void setQueryPlanListener(QueryPlanListener queryPlanListener) {
this.queryPlanListener = queryPlanListener;
}
/**
@@ -0,0 +1,34 @@
package io.ebean.config;
import io.ebean.Database;
import io.ebean.meta.MetaQueryPlan;
import java.util.List;
/**
* The captured query plans.
*/
public class QueryPlanCapture {
private final Database database;
private final List<MetaQueryPlan> plans;
public QueryPlanCapture(Database database, List<MetaQueryPlan> plans) {
this.database = database;
this.plans = plans;
}
/**
* Return the database the plans were captured for.
*/
public Database getDatabase() {
return database;
}
/**
* Return the captured query plans.
*/
public List<MetaQueryPlan> getPlans() {
return plans;
}
}
@@ -0,0 +1,13 @@
package io.ebean.config;
/**
* EXPERIMENTAL: Listener for captured query plans.
*/
@FunctionalInterface
public interface QueryPlanListener {
/**
* Process the captured query plans.
*/
void process(QueryPlanCapture capture);
}
@@ -41,6 +41,9 @@ public class QueryPlanRequest {
/**
* Set the maximum number of plans to capture.
* <p>
* Use this to limit how much query plan capturing is done as query
* plan capture is actual database load.
*/
public void setMaxCount(int maxCount) {
this.maxCount = maxCount;
@@ -58,7 +61,9 @@ public class QueryPlanRequest {
/**
* Set the maximum amount of time we want to use to capture plans.
* <p>
* Query plan collection will stop once this time is exceeded.
* Query plan collection will stop once this time is exceeded. We use
* this to ensure the query plan capture does not use excessive amount
* of time - put too much load on the database.
*/
public void setMaxTimeMillis(long maxTimeMillis) {
this.maxTimeMillis = maxTimeMillis;
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<!-- <parent>-->
<!-- <groupId>org.avaje</groupId>-->
@@ -14,7 +14,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.8.0</tag>
<tag>ebean-parent-12.8.1</tag>
</scm>
<name>ebean autotune</name>
@@ -26,7 +26,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>provided</scope>
</dependency>
+17 -17
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<name>ebean bom</name>
@@ -18,8 +18,8 @@
<ebean-migration.version>12.4.0</ebean-migration.version>
<ebean-test-docker.version>4.1</ebean-test-docker.version>
<ebean-datasource.version>7.0</ebean-datasource.version>
<ebean-agent.version>12.6.6</ebean-agent.version>
<ebean-maven-plugin.version>12.6.6</ebean-maven-plugin.version>
<ebean-agent.version>12.8.1</ebean-agent.version>
<ebean-maven-plugin.version>12.8.1</ebean-maven-plugin.version>
</properties>
<dependencyManagement>
@@ -81,88 +81,88 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-xml</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-autotune</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
+6 -6
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -15,7 +15,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.8.0</tag>
<tag>ebean-parent-12.8.1</tag>
</scm>
<profiles>
@@ -87,19 +87,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
@@ -302,7 +302,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.6.6</version>
<version>12.8.1</version>
<executions>
<execution>
<id>test</id>
@@ -1,52 +1,25 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.expression.IdInExpression;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Used for bean cache lookup with where ids in expression.
* Process Cache lookup by Id(s).
*/
public class CacheIdLookup<T> {
private final IdInExpression idInExpression;
private int remaining;
public CacheIdLookup(IdInExpression idInExpression) {
this.idInExpression = idInExpression;
}
public interface CacheIdLookup<T> {
/**
* Return the Id values for the in expression.
* Return the Id values to lookup against the L2 cache.
*/
public Collection<?> idValues() {
return idInExpression.idValues();
}
Collection<?> idValues();
/**
* Process the hits returning the beans fetched from cache and
* adjusting the in expression (to not fetch the hits).
* Remove the hits returning the beans fetched from L2 cache.
*/
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
List<T> removeHits(BeanCacheResult<T> cacheResult);
Set<Object> hitIds = new HashSet<>();
List<T> beans = new ArrayList<>(hitIds.size());
for (BeanCacheResult.Entry<T> hit : cacheResult.hits()) {
hitIds.add(hit.getKey());
beans.add(hit.getBean());
}
this.remaining = idInExpression.removeIds(hitIds);
return beans;
}
public boolean allHits() {
return remaining == 0;
}
/**
* Return true if all beans where found in L2 cache.
*/
boolean allHits();
}
@@ -0,0 +1,55 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.expression.IdInExpression;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Used for bean cache lookup with where ids in expression.
*/
public class CacheIdLookupMany<T> implements CacheIdLookup<T> {
private final IdInExpression idInExpression;
private int remaining;
public CacheIdLookupMany(IdInExpression idInExpression) {
this.idInExpression = idInExpression;
}
/**
* Return the Id values for the in expression.
*/
@Override
public Collection<?> idValues() {
return idInExpression.idValues();
}
/**
* Process the hits returning the beans fetched from cache and
* adjusting the in expression (to not fetch the hits).
*/
@Override
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
Set<Object> hitIds = new HashSet<>();
List<T> beans = new ArrayList<>(hitIds.size());
for (BeanCacheResult.Entry<T> hit : cacheResult.hits()) {
hitIds.add(hit.getKey());
beans.add(hit.getBean());
}
this.remaining = idInExpression.removeIds(hitIds);
return beans;
}
@Override
public boolean allHits() {
return remaining == 0;
}
}
@@ -0,0 +1,38 @@
package io.ebeaninternal.api;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Used for bean cache lookup with a single id value.
*/
public class CacheIdLookupSingle<T> implements CacheIdLookup<T> {
private final Object idValue;
private boolean found;
public CacheIdLookupSingle(Object idValue) {
this.idValue = idValue;
}
@Override
public Collection<?> idValues() {
return Collections.singleton(idValue);
}
@Override
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
final List<BeanCacheResult.Entry<T>> hits = cacheResult.hits();
if (hits.size() == 1) {
found = true;
return Collections.singletonList(hits.get(0).getBean());
}
return Collections.emptyList();
}
@Override
public boolean allHits() {
return found;
}
}
@@ -16,7 +16,7 @@ public interface LoadContext {
/**
* Return the minimum batch size when using QueryIterator with query joins.
*/
int getSecondaryQueriesMinBatchSize(int defaultQueryBatch);
int getSecondaryQueriesMinBatchSize();
/**
* Execute any secondary (+query) queries if there are any defined.
@@ -8,6 +8,11 @@ import java.util.List;
class NoopQueryPlanManager implements QueryPlanManager {
@Override
public void setDefaultThreshold(long thresholdMicros) {
// do nothing
}
@Override
public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) {
return SpiQueryBindCapture.NOOP;
@@ -12,6 +12,11 @@ public interface QueryPlanManager {
QueryPlanManager NOOP = new NoopQueryPlanManager();
/**
* Update the global default threshold used when new query plans are created.
*/
void setDefaultThreshold(long thresholdMicros);
/**
* Create the bind capture for the given query plan.
*/
@@ -1,6 +1,7 @@
package io.ebeaninternal.api;
import io.ebean.FetchConfig;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import java.util.Set;
@@ -19,4 +20,8 @@ public interface SpiQueryFetch {
*/
void fetchProperties(String name, Set<String> properties, FetchConfig config);
/**
* Add a nested fetch graph.
*/
void addNested(String name, OrmQueryDetail nestedDetail, FetchConfig config);
}
@@ -0,0 +1,25 @@
package io.ebeaninternal.server.core;
import io.ebean.config.QueryPlanCapture;
import io.ebean.config.QueryPlanListener;
import io.ebean.meta.MetaQueryPlan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class DefaultQueryPlanListener implements QueryPlanListener {
static final QueryPlanListener INSTANT = new DefaultQueryPlanListener();
private static final Logger log = LoggerFactory.getLogger("io.ebean.QUERYPLAN");
@Override
public void process(QueryPlanCapture capture) {
// better to log this in JSON form?
String dbName = capture.getDatabase().getName();
for (MetaQueryPlan plan : capture.getPlans()) {
log.info("queryPlan db:{} label:{} queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
dbName, plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
plan.getSql(), plan.getBind(), plan.getPlan());
}
}
}
@@ -45,12 +45,7 @@ import io.ebean.bean.PersistenceContext.WithOption;
import io.ebean.bean.SingleBeanLoader;
import io.ebean.cache.ServerCacheManager;
import io.ebean.common.CopyOnFirstWriteList;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.EncryptKeyManager;
import io.ebean.config.SlowQueryEvent;
import io.ebean.config.SlowQueryListener;
import io.ebean.config.TenantMode;
import io.ebean.config.*;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.BeanPersistController;
import io.ebean.event.ShutdownManager;
@@ -125,6 +120,7 @@ import io.ebeanservice.docstore.api.DocStoreIntegration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
@@ -137,7 +133,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -145,6 +140,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -196,7 +192,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final SpiLogManager logManager;
private final PersistenceContextScope defaultPersistenceContextScope;
private final int lazyLoadBatchSize;
private final int queryBatchSize;
private final boolean updateAllPropertiesInBatch;
private final long slowQueryMicros;
private final SlowQueryListener slowQueryListener;
@@ -217,7 +212,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.extraMetrics = config.getExtraMetrics();
this.serverName = this.config.getName();
this.lazyLoadBatchSize = this.config.getLazyLoadBatchSize();
this.queryBatchSize = this.config.getQueryBatchSize();
this.cqueryEngine = config.getCQueryEngine();
this.expressionFactory = config.getExpressionFactory();
this.encryptKeyManager = this.config.getEncryptKeyManager();
@@ -305,10 +299,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return lazyLoadBatchSize;
}
public int getQueryBatchSize() {
return queryBatchSize;
}
@Override
public Object currentTenantId() {
return currentTenantProvider == null ? null : currentTenantProvider.currentId();
@@ -410,6 +400,31 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
migrationRunner.loadProperties(config.getProperties());
migrationRunner.run(config.getDataSource());
}
startQueryPlanCapture();
}
private void startQueryPlanCapture() {
if (config.isQueryPlanCapture()) {
long secs = config.getQueryPlanCapturePeriodSecs();
if (secs > 10) {
logger.info("capture query plan enabled, every {}secs", secs);
backgroundExecutor.scheduleWithFixedDelay(this::collectQueryPlans, secs, secs, TimeUnit.SECONDS);
}
}
}
private void collectQueryPlans() {
QueryPlanRequest request = new QueryPlanRequest();
request.setMaxCount(config.getQueryPlanCaptureMaxCount());
request.setMaxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
// obtains query explain plans ...
List<MetaQueryPlan> plans = metaInfoManager.queryPlanCollectNow(request);
QueryPlanListener listener = config.getQueryPlanListener();
if (listener == null) {
listener = DefaultQueryPlanListener.INSTANT;
}
listener.process(new QueryPlanCapture(this, plans));
}
@Override
@@ -1150,6 +1165,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction) {
return Optional.ofNullable(findOne(query, transaction));
@@ -1180,6 +1196,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> Set<T> findSet(Query<T> query, Transaction t) {
@@ -1196,6 +1213,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public <K, T> Map<K, T> findMap(Query<T> query, Transaction t) {
@@ -1217,6 +1235,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
@SuppressWarnings("unchecked")
public <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction t) {
@@ -1227,7 +1246,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
try {
request.initTransIfRequired();
return (List<A>) request.findSingleAttributeList();
return request.findSingleAttributeList();
} finally {
request.endTransIfRequired();
}
@@ -1277,6 +1296,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public <A, T> List<A> findIds(Query<T> query, Transaction t) {
return findIdsWithCopy(((SpiQuery<T>) query).copy(), t);
@@ -1337,26 +1357,29 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public <T> FutureRowCount<T> findFutureCount(Query<T> q, Transaction t) {
SpiQuery<T> copy = ((SpiQuery<T>) q).copy();
copy.setFutureFetch(true);
Transaction newTxn = createTransaction();
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<T>(this, copy, newTxn));
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<>(this, copy, newTxn));
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
@Nonnull
@Override
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t) {
SpiQuery<T> copy = ((SpiQuery<T>) query).copy();
copy.setFutureFetch(true);
Transaction newTxn = createTransaction();
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<T>(this, copy, newTxn));
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<>(this, copy, newTxn));
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
@Nonnull
@Override
public <T> FutureList<T> findFutureList(Query<T> query, Transaction t) {
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
@@ -1369,11 +1392,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
// Create a new transaction solely to execute the findList() at some future time
Transaction newTxn = createTransaction();
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<T>(this, spiQuery, newTxn));
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<>(this, spiQuery, newTxn));
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
@Nonnull
@Override
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction) {
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
@@ -1387,6 +1411,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return new LimitOffsetPagedList<>(this, spiQuery);
}
@Nonnull
@Override
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
@@ -1399,11 +1424,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public <T> Stream<T> findLargeStream(Query<T> query, Transaction transaction) {
return findStream(query, transaction);
}
@Nonnull
@Override
public <T> Stream<T> findStream(Query<T> query, Transaction transaction) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, transaction);
@@ -1470,6 +1497,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
// no try finally - findEachWhile guarantee's cleanup of the transaction if required
}
@Nonnull
@Override
public <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, transaction);
@@ -1481,6 +1509,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public <T> List<T> findList(Query<T> query, Transaction t) {
return findList(query, t, false);
@@ -1540,6 +1569,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public List<SqlRow> findList(SqlQuery query, Transaction t) {
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
@@ -2241,6 +2271,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return checkUniqueness(bean, null);
}
@Nonnull
@Override
public Set<Property> checkUniqueness(Object bean, Transaction transaction) {
EntityBean entityBean = checkEntityBean(bean);
@@ -2252,14 +2283,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
Object id = idProperty.getVal(entityBean);
if (entityBean._ebean_getIntercept().isNew() && id != null) {
// Primary Key is changeable only on new models - so skip check if we are not
// new.
// Primary Key is changeable only on new models - so skip check if we are not new
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
query.setId(id);
if (findCount(query, transaction) > 0) {
Set<Property> ret = new HashSet<>();
ret.add(idProperty);
return ret;
return Collections.singleton(idProperty);
}
}
for (BeanProperty[] props : beanDesc.getUniqueProps()) {
@@ -2322,6 +2350,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
List<MetaQueryPlan> queryPlanInit(QueryPlanInit initRequest) {
if (initRequest.isAll()) {
queryPlanManager.setDefaultThreshold(initRequest.getThresholdMicros());
}
return beanDescriptorManager.queryPlanInit(initRequest);
}
@@ -617,10 +617,10 @@ public class InternalConfiguration {
}
public QueryPlanManager initQueryPlanManager(TransactionManager transactionManager) {
if (!config.isCollectQueryPlans()) {
if (!config.isQueryPlanEnable()) {
return QueryPlanManager.NOOP;
}
long threshold = config.getCollectQueryPlanThresholdMicros();
long threshold = config.getQueryPlanThresholdMicros();
return new CQueryPlanManager(transactionManager, threshold, queryPlanLogger(databasePlatform.getPlatform()), extraMetrics);
}
@@ -173,8 +173,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
* iteration is fine.
* </p>
*/
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
return loadContext.getSecondaryQueriesMinBatchSize(defaultQueryBatch);
public int getSecondaryQueriesMinBatchSize() {
return loadContext.getSecondaryQueriesMinBatchSize();
}
/**
@@ -93,6 +93,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorManager.class);
private static final BeanDescComparator beanDescComparator = new BeanDescComparator();
public static final String JAVA_LANG_RECORD = "java.lang.Record";
private final ReadAnnotations readAnnotations;
private final TransientProperties transientProperties;
@@ -1418,14 +1419,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void checkInheritedClasses(Class<?> beanClass) {
Class<?> superclass = beanClass.getSuperclass();
if (Object.class.equals(superclass)) {
if (Object.class.equals(superclass) || Model.class.equals(superclass) || JAVA_LANG_RECORD.equals(superclass.getName())) {
// we got to the top of the inheritance
return;
}
if (Model.class.equals(superclass)) {
// top of the inheritance. Not enhancing Model at this stage
return;
}
if (!EntityBean.class.isAssignableFrom(superclass)) {
if (isMappedSuperWithNoProperties(superclass)) {
// ok to stop and treat just the same as Object.class
@@ -33,7 +33,7 @@ abstract class DLoadBaseContext {
final boolean queryFetch;
DLoadBaseContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
DLoadBaseContext(DLoadContext parent, BeanDescriptor<?> desc, String path, OrmQueryProperties queryProps) {
this.parent = parent;
this.serverName = parent.getEbeanServer().getName();
this.desc = desc;
@@ -42,11 +42,7 @@ abstract class DLoadBaseContext {
this.hitCache = parent.isBeanCacheGet() && desc.isBeanCaching();
this.objectGraphNode = parent.getObjectGraphNode(path);
this.queryFetch = queryProps != null && queryProps.isQueryFetch();
this.batchSize = initBatchSize(defaultBatchSize, queryProps);
}
private int initBatchSize(int batchSize, OrmQueryProperties queryProps) {
return queryProps == null ? batchSize : queryProps.getBatchSize();
this.batchSize = parent.batchSize(queryProps);
}
/**
@@ -31,8 +31,8 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
private LoadBuffer currentBuffer;
DLoadBeanContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
super(parent, desc, path, defaultBatchSize, queryProps);
DLoadBeanContext(DLoadContext parent, BeanDescriptor<?> desc, String path, OrmQueryProperties queryProps) {
super(parent, desc, path, queryProps);
// bufferList only required when using query joins (queryFetch)
this.bufferList = (!queryFetch) ? null : new ArrayList<>();
this.currentBuffer = createBuffer(batchSize);
@@ -90,7 +90,7 @@ public class DLoadContext implements LoadContext {
this.planLabel = null;
this.profileLocation = null;
this.profilingListener = null;
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, null);
}
private ObjectGraphOrigin initOrigin() {
@@ -128,7 +128,7 @@ public class DLoadContext implements LoadContext {
}
// initialise rootBeanContext after origin and relativePath have been set
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, null);
registerSecondaryQueries(secondaryQueries);
}
@@ -182,7 +182,7 @@ public class DLoadContext implements LoadContext {
* Return the minimum batch size when using QueryIterator with query joins.
*/
@Override
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
public int getSecondaryQueriesMinBatchSize() {
if (secQuery == null) {
return -1;
}
@@ -190,7 +190,7 @@ public class DLoadContext implements LoadContext {
for (OrmQueryProperties aSecQuery : secQuery) {
int batchSize = aSecQuery.getBatchSize();
if (batchSize == 0) {
batchSize = defaultQueryBatch;
batchSize = 100;
}
maxBatch = Math.max(maxBatch, batchSize);
}
@@ -287,51 +287,55 @@ public class DLoadContext implements LoadContext {
getManyContext(path, many).register(bc);
}
int batchSize(OrmQueryProperties props) {
if (props == null) {
return defaultBatchSize;
}
int batchSize = props.getBatchSize();
return batchSize == 0 ? defaultBatchSize : batchSize;
}
DLoadBeanContext getBeanContext(String path) {
if (path == null) {
return rootBeanContext;
}
return beanMap.computeIfAbsent(path, p -> createBeanContext(p, defaultBatchSize, null));
return beanMap.computeIfAbsent(path, p -> createBeanContext(p, null));
}
DLoadBeanContext getBeanContextWithInherit(String path, BeanPropertyAssocOne<?> property) {
String key = path + ":" + property.getTargetDescriptor().getName();
return beanMap.computeIfAbsent(key, p -> createBeanContext(property, path, defaultBatchSize, null));
return beanMap.computeIfAbsent(key, p -> createBeanContext(property, path, null));
}
private void registerSecondaryNode(boolean many, OrmQueryProperties props) {
int batchSize = props.getBatchSize();
if (batchSize == 0) {
batchSize = defaultBatchSize;
}
String path = props.getPath();
if (many) {
manyMap.put(path, createManyContext(path, batchSize, props));
manyMap.put(path, createManyContext(path, props));
} else {
beanMap.put(path, createBeanContext(path, batchSize, props));
beanMap.put(path, createBeanContext(path, props));
}
}
DLoadManyContext getManyContext(String path, BeanPropertyAssocMany<?> many) {
return manyMap.computeIfAbsent(path, p -> createManyContext(p, many, defaultBatchSize));
return manyMap.computeIfAbsent(path, p -> createManyContext(p, many));
}
private DLoadManyContext createManyContext(String path, BeanPropertyAssocMany<?> many, int batchSize) {
return new DLoadManyContext(this, many, path, batchSize, null);
private DLoadManyContext createManyContext(String path, BeanPropertyAssocMany<?> many) {
return new DLoadManyContext(this, many, path, null);
}
private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) {
private DLoadManyContext createManyContext(String path, OrmQueryProperties queryProps) {
BeanPropertyAssocMany<?> p = (BeanPropertyAssocMany<?>) getBeanProperty(rootDescriptor, path);
return new DLoadManyContext(this, p, path, batchSize, queryProps);
return new DLoadManyContext(this, p, path, queryProps);
}
private DLoadBeanContext createBeanContext(String path, int batchSize, OrmQueryProperties queryProps) {
private DLoadBeanContext createBeanContext(String path, OrmQueryProperties queryProps) {
BeanPropertyAssoc<?> p = (BeanPropertyAssoc<?>) getBeanProperty(rootDescriptor, path);
return new DLoadBeanContext(this, p.getTargetDescriptor(), path, batchSize, queryProps);
return new DLoadBeanContext(this, p.getTargetDescriptor(), path, queryProps);
}
private DLoadBeanContext createBeanContext(BeanPropertyAssoc<?> property, String path, int batchSize, OrmQueryProperties queryProps) {
return new DLoadBeanContext(this, property.getTargetDescriptor(), path, batchSize, queryProps);
private DLoadBeanContext createBeanContext(BeanPropertyAssoc<?> property, String path, OrmQueryProperties queryProps) {
return new DLoadBeanContext(this, property.getTargetDescriptor(), path, queryProps);
}
private BeanProperty getBeanProperty(BeanDescriptor<?> desc, String path) {
@@ -31,11 +31,8 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
private LoadBuffer currentBuffer;
DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> property,
String path, int defaultBatchSize, OrmQueryProperties queryProps) {
super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> property, String path, OrmQueryProperties queryProps) {
super(parent, property.getBeanDescriptor(), path, queryProps);
this.property = property;
this.docStoreMapped = property.isTargetDocStoreMapped();
// bufferList only required when using query joins (queryFetch)
@@ -9,7 +9,7 @@ import java.util.concurrent.locks.ReentrantLock;
class CQueryBindCapture implements SpiQueryBindCapture {
private static final double multiplier = 1.3d;
private static final double multiplier = 1.5d;
private final ReentrantLock lock = new ReentrantLock();
private final CQueryPlanManager manager;
@@ -37,8 +37,6 @@ public class CQueryEngine {
private static final Logger logger = LoggerFactory.getLogger(CQueryEngine.class);
private static final int defaultSecondaryQueryBatchSize = 100;
private static final String T0 = "t0";
private final int defaultFetchSizeFindList;
@@ -214,7 +212,7 @@ public class CQueryEngine {
logSql(cquery);
}
// first check batch sizes set on query joins
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize);
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize();
if (iterateBufferSize < 1) {
// not set on query joins so check if batch size set on query itself
int queryBatch = request.getQuery().getLazyLoadBatchSize();
@@ -30,14 +30,14 @@ public class CQueryPlanManager implements QueryPlanManager {
private final TransactionManager transactionManager;
private final long defaultThreshold;
private final QueryPlanLogger planLogger;
private final TimedMetric timeCollection;
private final TimedMetric timeBindCapture;
private long defaultThreshold;
public CQueryPlanManager(TransactionManager transactionManager, long defaultThreshold, QueryPlanLogger planLogger, ExtraMetrics extraMetrics) {
this.transactionManager = transactionManager;
this.defaultThreshold = defaultThreshold;
@@ -46,6 +46,11 @@ public class CQueryPlanManager implements QueryPlanManager {
this.timeBindCapture = extraMetrics.getBindCapture();
}
@Override
public void setDefaultThreshold(long thresholdMicros) {
this.defaultThreshold = thresholdMicros;
}
@Override
public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) {
return new CQueryBindCapture(this, queryPlan, defaultThreshold);
@@ -644,4 +644,9 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQueryFetch
public void fetchProperties(String property, Set<String> columns, FetchConfig config) {
detail.fetchProperties(property, columns, config);
}
@Override
public void addNested(String name, OrmQueryDetail nestedDetail, FetchConfig config) {
detail.addNested(name, nestedDetail, config);
}
}
@@ -35,6 +35,8 @@ import io.ebean.plugin.BeanType;
import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.api.CQueryPlanKey;
import io.ebeaninternal.api.CacheIdLookup;
import io.ebeaninternal.api.CacheIdLookupMany;
import io.ebeaninternal.api.CacheIdLookupSingle;
import io.ebeaninternal.api.HashQuery;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
@@ -740,10 +742,14 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return null;
}
List<SpiExpression> underlyingList = whereExpressions.getUnderlyingList();
if (underlyingList.size() == 1) {
if (underlyingList.isEmpty()) {
if (id != null) {
return new CacheIdLookupSingle<>(id);
}
} else if (underlyingList.size() == 1) {
SpiExpression singleExpression = underlyingList.get(0);
if (singleExpression instanceof IdInExpression) {
return new CacheIdLookup<>((IdInExpression) singleExpression);
return new CacheIdLookupMany<>((IdInExpression) singleExpression);
}
}
return null;
@@ -1412,6 +1418,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
detail.fetchProperties(path, other);
}
@Override
public void addNested(String name, OrmQueryDetail nestedDetail, FetchConfig config) {
detail.addNested(name, nestedDetail, config);
}
@Override
public Query<T> select(String columns) {
detail.select(columns);
@@ -120,8 +120,8 @@ public class OrmQueryProperties implements Serializable {
this.parentPath = SplitName.parent(path);
this.allProperties = other.allProperties;
this.included = other.included;
this.cache = other.cache;
this.fetchConfig = fetchConfig;
this.cache = fetchConfig.isCache();
}
/**
@@ -1,6 +1,5 @@
package io.ebean;
import io.ebean.FetchConfig;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -10,7 +9,7 @@ public class FetchConfigTest {
@Test
public void testLazy() {
FetchConfig config = new FetchConfig().lazy();
assertThat(config.getBatchSize()).isEqualTo(10);
assertThat(config.getBatchSize()).isEqualTo(0);
}
@Test
@@ -72,10 +72,16 @@ public class ServerConfigTest {
props.setProperty("enabledL2Regions", "r0,users,orgs");
props.setProperty("caseSensitiveCollation", "false");
props.setProperty("loadModuleInfo", "true");
props.setProperty("collectQueryPlanThresholdMicros", "10000");
props.setProperty("forUpdateNoKey", "true");
props.setProperty("defaultServer", "false");
props.setProperty("queryPlan.enable", "true");
props.setProperty("queryPlan.thresholdMicros", "10000");
props.setProperty("queryPlan.capture", "true");
props.setProperty("queryPlan.capturePeriodSecs", "42");
props.setProperty("queryPlan.captureMaxTimeMillis", "560");
props.setProperty("queryPlan.captureMaxCount", "7");
serverConfig.loadFromProperties(props);
assertFalse(serverConfig.isDefaultServer());
@@ -97,7 +103,6 @@ public class ServerConfigTest {
assertEquals(PlatformConfig.DbUuid.BINARY, serverConfig.getPlatformConfig().getDbUuid());
assertEquals(JsonConfig.DateTime.MILLIS, serverConfig.getJsonDateTime());
assertEquals(JsonConfig.Date.MILLIS, serverConfig.getJsonDate());
assertEquals(10000, serverConfig.getCollectQueryPlanThresholdMicros());
assertEquals("r0,users,orgs", serverConfig.getEnabledL2Regions());
@@ -106,6 +111,13 @@ public class ServerConfigTest {
assertEquals(4, serverConfig.getBackgroundExecutorSchedulePoolSize());
assertEquals(98, serverConfig.getBackgroundExecutorShutdownSecs());
assertTrue(serverConfig.isQueryPlanEnable());
assertEquals(10000, serverConfig.getQueryPlanThresholdMicros());
assertTrue(serverConfig.isQueryPlanCapture());
assertEquals(42, serverConfig.getQueryPlanCapturePeriodSecs());
assertEquals(560, serverConfig.getQueryPlanCaptureMaxTimeMillis());
assertEquals(7, serverConfig.getQueryPlanCaptureMaxCount());
assertThat(serverConfig.getMappingLocations()).containsExactly("classpath:/foo","bar");
serverConfig.setPersistBatch(PersistBatch.NONE);
@@ -144,7 +156,13 @@ public class ServerConfigTest {
assertEquals(JsonConfig.Date.ISO8601, serverConfig.getJsonDate());
assertTrue(serverConfig.getPlatformConfig().isCaseSensitiveCollation());
assertTrue(serverConfig.isAutoLoadModuleInfo());
assertEquals(Long.MAX_VALUE, serverConfig.getCollectQueryPlanThresholdMicros());
assertFalse(serverConfig.isQueryPlanEnable());
assertEquals(Long.MAX_VALUE, serverConfig.getQueryPlanThresholdMicros());
assertFalse(serverConfig.isQueryPlanCapture());
assertEquals(600, serverConfig.getQueryPlanCapturePeriodSecs());
assertEquals(10000L, serverConfig.getQueryPlanCaptureMaxTimeMillis());
assertEquals(10, serverConfig.getQueryPlanCaptureMaxCount());
serverConfig.setLoadModuleInfo(false);
assertFalse(serverConfig.isAutoLoadModuleInfo());
@@ -19,7 +19,7 @@ public class ParseFetchConfigTest {
@Test
public void parseLazy() {
FetchConfig lazy = ParseFetchConfig.parse("lazy");
assertThat(lazy.getBatchSize()).isEqualTo(10);
assertThat(lazy.getBatchSize()).isEqualTo(0);
}
@Test
@@ -46,7 +46,12 @@ public class MainMemoryLeak {
config.addClass(ECachedBean.class);
config.loadFromProperties();
Database server1 = DatabaseFactory.create(config);
assert server1 != null;
// create again for #2193
Database server = DatabaseFactory.create(config);
assert server != null;
// create a string with 10k chars
char[] c = new char[10_000];
@@ -169,22 +169,40 @@ public class TestQueryFindEach extends BaseTestCase {
DB.find(OmBasicParent.class).delete();
insertData();
test_setLazyLoadBatchSize_withFetchLazy();
LoggedSqlCollector.start();
try (final Transaction transaction = DB.beginTransaction()) {
// DB.find(OmBasicParent.class).findList();
DB.find(OmBasicChild.class)
.setLazyLoadBatchSize(100)
//.fetchQuery("parent","name")
//.fetch("parent","name")
.findEach(child -> {
assertNotNull(child.getParent().getName());
});
}
DB.find(OmBasicChild.class)
.setLazyLoadBatchSize(100)
.findEach(child -> {
assertNotNull(child.getParent().getName());
});
final List<String> sql = LoggedSqlCollector.stop();
assertThat(sql.size()).isLessThan(50);
}
private void test_setLazyLoadBatchSize_withFetchLazy() {
LoggedSqlCollector.start();
DB.find(OmBasicParent.class)
.setLazyLoadBatchSize(5)
.fetchLazy("children")
.setMaxRows(50)
.findEach(it -> {
it.getChildren().size();
});
final List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(11);
assertThat(sql.get(0)).contains(" from om_basic_parent ");
for (int i = 1; i < 11; i++) {
assertThat(sql.get(i)).contains(" --bind(Array[5]");
}
}
@Transactional(batchSize = 40)
private void insertData() {
for (int i = 0; i < 150; i++) {
@@ -175,17 +175,24 @@ public class TestCustomerFinder extends BaseTestCase {
ResetBasicData.reset();
// change default collect query plan threshold to 200 micros
QueryPlanInit init0 = new QueryPlanInit();
init0.setAll(true);
init0.setThresholdMicros(200);
final List<MetaQueryPlan> plans = server().getMetaInfoManager().queryPlanInit(init0);
assertThat(plans.size()).isGreaterThan(1);
// the server has some plans
runQueries();
// enable query plan bind capture on all plans threshold 100 micros
// change query plan threshold to 100 micros
QueryPlanInit init = new QueryPlanInit();
init.setAll(true);
init.setThresholdMicros(100);
final List<MetaQueryPlan> appliedToPlans = server().getMetaInfoManager().queryPlanInit(init);
assertThat(appliedToPlans.size()).isGreaterThan(4);
// will collect bind captures
// run queries again
runQueries();
ServerMetrics metrics = server().getMetaInfoManager().collectMetrics();
@@ -203,12 +210,21 @@ public class TestCustomerFinder extends BaseTestCase {
// obtains db query plans ...
QueryPlanRequest request = new QueryPlanRequest();
List<MetaQueryPlan> plans = server().getMetaInfoManager().queryPlanCollectNow(request);
assertThat(plans).isNotEmpty();
// collect max 1000 plans (use something more like 10)
request.setMaxCount(1_000);
// don't collect any more plans if used 10 secs
request.setMaxTimeMillis(10_000);
List<MetaQueryPlan> plans0 = server().getMetaInfoManager().queryPlanCollectNow(request);
assertThat(plans0).isNotEmpty();
for (MetaQueryPlan plan : plans) {
logger.info("queryplan label:{}, queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
plan.getSql(), plan.getBind(), plan.getPlan());
System.out.println(plan);
}
//DB.getBackgroundExecutor().scheduleWithFixedDelay(...)
}
@Test
@@ -27,8 +27,8 @@ datasource.default=h2
ebean.dumpMetricsOnShutdown=true
ebean.dumpMetricsOptions=sql,hash
#ebean.collectQueryPlanThresholdMicros=1000
ebean.collectQueryPlans=true
#ebean.queryPlan.thresholdMicros=1000
ebean.queryPlan.enable=true
ebean.autoReadOnlyDataSource=true
#datasource.h2-ro.url=jdbc:h2:mem:tests
+4 -5
View File
@@ -4,13 +4,12 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<name>ebean ddl generation</name>
<description>DDL and DB Migration generation</description>
<artifactId>ebean-ddl-generator</artifactId>
<!-- <version>12.6.1.PreRelease.0</version>-->
<dependencies>
@@ -29,14 +28,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>provided</scope>
</dependency>
@@ -77,7 +76,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.6.6</version>
<version>12.8.1</version>
<executions>
<execution>
<id>test</id>
@@ -15,6 +15,7 @@ public class OracleDdl extends PlatformDdl {
this.dropConstraintIfExists = "drop constraint";
this.dropIndexIfExists = "drop index ";
this.dropTableCascade = " cascade constraints purge";
this.addColumn = "add";
this.alterColumn = "modify";
this.columnSetNotnull = "not null";
this.columnSetNull = "null";
@@ -3,7 +3,6 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.ConstraintMode;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbDefaultValue;
import io.ebean.config.dbplatform.DbIdentity;
@@ -113,6 +112,7 @@ public class PlatformDdl {
protected String uniqueIndex = "unique";
protected String indexConcurrent = "";
protected String createIndexIfNotExists = "";
/**
* Set false for MsSqlServer to allow multiple nulls for OneToOne mapping.
@@ -414,6 +414,9 @@ public class PlatformDdl {
if (create.isConcurrent()) {
buffer.append(indexConcurrent);
}
if (create.isNotExistsCheck()) {
buffer.append(createIndexIfNotExists);
}
buffer.append(maxConstraintName(create.getIndexName())).append(" on ").append(create.getTableName());
appendColumns(create.getColumns(), buffer);
return buffer.toString();
@@ -18,6 +18,7 @@ public class PostgresDdl extends PlatformDdl {
this.dropTableCascade = " cascade";
this.columnSetType = "type ";
this.alterTableIfExists = "if exists ";
this.createIndexIfNotExists = "if not exists ";
this.columnSetNull = "drop not null";
this.addForeignKeySkipCheck = " not valid";
this.indexConcurrent = "concurrently ";
@@ -12,7 +12,11 @@ class WriteCreateIndex {
private final boolean unique;
private final boolean concurrent;
private final String definition;
private final boolean notExistsCheck;
/**
* Create index for foreign key.
*/
WriteCreateIndex(String indexName, String tableName, String[] columns, boolean unique) {
this.indexName = indexName;
this.tableName = tableName;
@@ -20,8 +24,12 @@ class WriteCreateIndex {
this.unique = unique;
this.concurrent = false;
this.definition = null;
this.notExistsCheck = false;
}
/**
* Create non-foreign key index.
*/
public WriteCreateIndex(CreateIndex index) {
this.indexName = index.getIndexName();
this.tableName = index.getTableName();
@@ -29,6 +37,7 @@ class WriteCreateIndex {
this.unique = Boolean.TRUE.equals(index.isUnique());
this.concurrent = Boolean.TRUE.equals(index.isConcurrent());
this.definition = index.getDefinition();
this.notExistsCheck = true;
}
public String getIndexName() {
@@ -58,4 +67,8 @@ class WriteCreateIndex {
public boolean useDefinition() {
return definition != null && !definition.isEmpty();
}
public boolean isNotExistsCheck() {
return notExistsCheck;
}
}
@@ -61,7 +61,7 @@ public class BaseTableDdlTest {
ddlGen.alterTableAddColumn(write.apply(), "mytable", column, false, false);
String ddl = write.apply().getBuffer();
assertThat(ddl).contains("alter table mytable add column col_name varchar2(20)");
assertThat(ddl).contains("alter table mytable add col_name varchar2(20)");
}
@Test
@@ -9,11 +9,15 @@ import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.ddlgeneration.PlatformDdlBuilder;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.AlterForeignKey;
import io.ebeaninternal.dbmigration.migration.Column;
import org.junit.Test;
import java.io.IOException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -238,6 +242,22 @@ public class PlatformDdl_AlterColumnTest {
assertTrue(exceptionCaught);
}
@Test
public void oracle_alterTableAddColumn() throws IOException {
DdlWrite write = new DdlWrite();
oraDdl.alterTableAddColumn(write.apply(), "my_table", simpleColumn(), false, "1");
assertThat(write.apply().getBuffer()).isEqualTo("alter table my_table add my_column int default 1 not null;\n");
}
private Column simpleColumn() {
Column column = new Column();
column.setName("my_column");
column.setType("int");
column.setNotnull(true);
column.setDefaultValue("1");
return column;
}
@Test
public void useIdentityType_h2() {
assertEquals(h2Ddl.useIdentityType(null), IdType.IDENTITY);
@@ -0,0 +1,98 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.DB;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform;
import io.ebeaninternal.dbmigration.ddlgeneration.PlatformDdlBuilder;
import io.ebeaninternal.dbmigration.migration.CreateIndex;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
public class PlatformDdl_CreateIndexTest {
private final PlatformDdl h2Ddl = PlatformDdlBuilder.create(new H2Platform());
private final PlatformDdl pgDdl = PlatformDdlBuilder.create(new PostgresPlatform());
private final PlatformDdl mysqlDdl = PlatformDdlBuilder.create(new MySqlPlatform());
private final PlatformDdl oraDdl = PlatformDdlBuilder.create(new OraclePlatform());
private final PlatformDdl sqlServerDdl = PlatformDdlBuilder.create(new SqlServer17Platform());
private final PlatformDdl hanaDdl = PlatformDdlBuilder.create(new HanaPlatform());
{
DatabaseConfig config = DB.getDefault().getPluginApi().getServerConfig();
h2Ddl.configure(config);
pgDdl.configure(config);
mysqlDdl.configure(config);
oraDdl.configure(config);
sqlServerDdl.configure(config);
hanaDdl.configure(config);
}
WriteCreateIndex writeCreateIndex() {
return writeCreateIndex(true, true);
}
WriteCreateIndex writeCreateIndex(boolean unique, boolean concurrent) {
CreateIndex createIndex = new CreateIndex();
createIndex.setIndexName("ix_mytab_acol");
createIndex.setTableName("mytab");
createIndex.setColumns("acol");
createIndex.setUnique(unique);
createIndex.setConcurrent(concurrent);
return new WriteCreateIndex(createIndex);
}
WriteCreateIndex fkeyCreateIndex(boolean unique) {
return new WriteCreateIndex("ix_mytab_acol", "mytab", new String[]{"acol"}, unique);
}
@Test
public void createUniqueIndex() {
WriteCreateIndex createIndex = writeCreateIndex();
String sql = h2Ddl.createIndex(createIndex);
assertEquals("create unique index ix_mytab_acol on mytab (acol)", sql);
sql = pgDdl.createIndex(createIndex);
assertEquals("create unique index concurrently if not exists ix_mytab_acol on mytab (acol)", sql);
sql = mysqlDdl.createIndex(createIndex);
assertEquals("create unique index ix_mytab_acol on mytab (acol)", sql);
sql = sqlServerDdl.createIndex(createIndex);
assertEquals("create unique index ix_mytab_acol on mytab (acol)", sql);
sql = oraDdl.createIndex(createIndex);
assertEquals("create unique index ix_mytab_acol on mytab (acol)", sql);
sql = hanaDdl.createIndex(createIndex);
assertThat(sql).isEqualTo("-- explicit index \"ix_mytab_acol\" for single column \"acol\" of table \"mytab\" is not necessary");
}
@Test
public void postgres_createIndex() {
String sql = pgDdl.createIndex(writeCreateIndex(true, true));
assertEquals("create unique index concurrently if not exists ix_mytab_acol on mytab (acol)", sql);
sql = pgDdl.createIndex(writeCreateIndex(false, false));
assertEquals("create index if not exists ix_mytab_acol on mytab (acol)", sql);
sql = pgDdl.createIndex(writeCreateIndex(true, false));
assertEquals("create unique index if not exists ix_mytab_acol on mytab (acol)", sql);
sql = pgDdl.createIndex(writeCreateIndex(false, true));
assertEquals("create index concurrently if not exists ix_mytab_acol on mytab (acol)", sql);
}
@Test
public void postgres_fkeyCreateIndex() {
String sql = pgDdl.createIndex(fkeyCreateIndex(true));
assertEquals("create unique index ix_mytab_acol on mytab (acol)", sql);
sql = pgDdl.createIndex(fkeyCreateIndex(false));
assertEquals("create index ix_mytab_acol on mytab (acol)", sql);
}
}
@@ -17,11 +17,11 @@ create table migtest_mtm_m_migtest_mtm_c (
constraint pk_migtest_mtm_m_migtest_mtm_c primary key (migtest_mtm_m_id,migtest_mtm_c_id)
);
alter table migtest_ckey_detail add column one_key number(10);
alter table migtest_ckey_detail add column two_key varchar2(127);
alter table migtest_ckey_detail add one_key number(10);
alter table migtest_ckey_detail add two_key varchar2(127);
alter table migtest_ckey_detail add constraint fk_migtest_ckey_detail_parent foreign key (one_key,two_key) references migtest_ckey_parent (one_key,two_key);
alter table migtest_ckey_parent add column assoc_id number(10);
alter table migtest_ckey_parent add assoc_id number(10);
alter table migtest_fk_cascade drop constraint fk_migtest_fk_cascade_one_id;
alter table migtest_fk_cascade add constraint fk_migtest_fk_cascade_one_id foreign key (one_id) references migtest_fk_cascade_one (id);
@@ -46,14 +46,14 @@ alter table migtest_e_basic modify status2 null;
insert into migtest_e_user (id) select distinct user_id from migtest_e_basic;
alter table migtest_e_basic add constraint fk_migtest_e_basic_user_id foreign key (user_id) references migtest_e_user (id);
alter table migtest_e_basic modify user_id null;
alter table migtest_e_basic add column new_string_field varchar2(255) default 'foo''bar' not null;
alter table migtest_e_basic add column new_boolean_field number(1) default 1 not null;
alter table migtest_e_basic add new_string_field varchar2(255) default 'foo''bar' not null;
alter table migtest_e_basic add new_boolean_field number(1) default 1 not null;
update migtest_e_basic set new_boolean_field = old_boolean;
alter table migtest_e_basic add column new_boolean_field2 number(1) default 1 not null;
alter table migtest_e_basic add column progress number(10) default 0 not null;
alter table migtest_e_basic add new_boolean_field2 number(1) default 1 not null;
alter table migtest_e_basic add progress number(10) default 0 not null;
alter table migtest_e_basic add constraint ck_migtest_e_basic_progress check ( progress in (0,1,2));
alter table migtest_e_basic add column new_integer number(10) default 42 not null;
alter table migtest_e_basic add new_integer number(10) default 42 not null;
alter table migtest_e_basic drop constraint uq_migtest_e_basic_indextest2;
alter table migtest_e_basic drop constraint uq_migtest_e_basic_indextest6;
@@ -70,12 +70,12 @@ comment on table migtest_e_history is 'We have history now';
update migtest_e_history2 set test_string = 'unknown' where test_string is null;
alter table migtest_e_history2 modify test_string default 'unknown';
alter table migtest_e_history2 modify test_string not null;
alter table migtest_e_history2 add column test_string2 varchar2(255);
alter table migtest_e_history2 add column test_string3 varchar2(255) default 'unknown' not null;
alter table migtest_e_history2 add column new_column varchar2(20);
alter table migtest_e_history2 add test_string2 varchar2(255);
alter table migtest_e_history2 add test_string3 varchar2(255) default 'unknown' not null;
alter table migtest_e_history2 add new_column varchar2(20);
alter table migtest_e_history4 modify test_number number(19);
alter table migtest_e_history5 add column test_boolean number(1) default 0 not null;
alter table migtest_e_history5 add test_boolean number(1) default 0 not null;
-- NOTE: table has @History - special migration may be necessary
@@ -83,9 +83,9 @@ update migtest_e_history6 set test_number1 = 42 where test_number1 is null;
alter table migtest_e_history6 modify test_number1 default 42;
alter table migtest_e_history6 modify test_number1 not null;
alter table migtest_e_history6 modify test_number2 null;
alter table migtest_e_softdelete add column deleted number(1) default 0 not null;
alter table migtest_e_softdelete add deleted number(1) default 0 not null;
alter table migtest_oto_child add column master_id number(19);
alter table migtest_oto_child add master_id number(19);
create index ix_migtest_e_basic_indextest3 on migtest_e_basic (indextest3);
create index ix_migtest_e_basic_indextest6 on migtest_e_basic (indextest6);
@@ -31,9 +31,9 @@ update migtest_e_basic set user_id = 23 where user_id is null;
alter table migtest_e_basic drop constraint fk_migtest_e_basic_user_id;
alter table migtest_e_basic modify user_id default 23;
alter table migtest_e_basic modify user_id not null;
alter table migtest_e_basic add column old_boolean number(1) default 0 not null;
alter table migtest_e_basic add column old_boolean2 number(1);
alter table migtest_e_basic add column eref_id number(10);
alter table migtest_e_basic add old_boolean number(1) default 0 not null;
alter table migtest_e_basic add old_boolean2 number(1);
alter table migtest_e_basic add eref_id number(10);
alter table migtest_e_basic drop constraint uq_mgtst__bsc_stts_ndxtst1;
alter table migtest_e_basic drop constraint uq_migtest_e_basic_name;
@@ -47,8 +47,8 @@ comment on column migtest_e_history.test_string is '';
comment on table migtest_e_history is '';
alter table migtest_e_history2 modify test_string drop default;
alter table migtest_e_history2 modify test_string null;
alter table migtest_e_history2 add column obsolete_string1 varchar2(255);
alter table migtest_e_history2 add column obsolete_string2 varchar2(255);
alter table migtest_e_history2 add obsolete_string1 varchar2(255);
alter table migtest_e_history2 add obsolete_string2 varchar2(255);
alter table migtest_e_history4 modify test_number number(10);
alter table migtest_e_history6 modify test_number1 drop default;
@@ -161,11 +161,11 @@ create table migtest_oto_master (
constraint pk_migtest_oto_master primary key (id)
);
create index ix_migtest_e_basic_indextest1 on migtest_e_basic (indextest1);
create index ix_migtest_e_basic_indextest5 on migtest_e_basic (indextest5);
create index if not exists ix_migtest_e_basic_indextest1 on migtest_e_basic (indextest1);
create index if not exists ix_migtest_e_basic_indextest5 on migtest_e_basic (indextest5);
create index idxd_migtest_0 on migtest_oto_child using hash (upper(name)) where upper(name) = 'JIM';
create index concurrently ix_migtest_oto_child_lowername_id on migtest_oto_child (lower(name),id);
create index ix_migtest_oto_child_lowername on migtest_oto_child (lower(name));
create index concurrently if not exists ix_migtest_oto_child_lowername_id on migtest_oto_child (lower(name),id);
create index if not exists ix_migtest_oto_child_lowername on migtest_oto_child (lower(name));
create index ix_migtest_fk_cascade_one_id on migtest_fk_cascade (one_id);
alter table migtest_fk_cascade add constraint fk_migtest_fk_cascade_one_id foreign key (one_id) references migtest_fk_cascade_one (id) on delete cascade on update restrict;
@@ -100,9 +100,9 @@ alter table migtest_e_softdelete add column deleted boolean default false not nu
alter table migtest_oto_child add column master_id bigint;
create index ix_migtest_e_basic_indextest3 on migtest_e_basic (indextest3);
create index ix_migtest_e_basic_indextest6 on migtest_e_basic (indextest6);
create index ix_migtest_oto_child_name on migtest_oto_child (name);
create index if not exists ix_migtest_e_basic_indextest3 on migtest_e_basic (indextest3);
create index if not exists ix_migtest_e_basic_indextest6 on migtest_e_basic (indextest6);
create index if not exists ix_migtest_oto_child_name on migtest_oto_child (name);
drop index if exists ix_migtest_e_basic_indextest1;
drop index if exists ix_migtest_e_basic_indextest5;
drop index if exists idxd_migtest_0;
@@ -66,11 +66,11 @@ alter table migtest_e_history6 alter column test_number1 drop not null;
update migtest_e_history6 set test_number2 = 7 where test_number2 is null;
alter table migtest_e_history6 alter column test_number2 set default 7;
alter table migtest_e_history6 alter column test_number2 set not null;
create index ix_migtest_e_basic_indextest1 on migtest_e_basic (indextest1);
create index ix_migtest_e_basic_indextest5 on migtest_e_basic (indextest5);
create index ix_m12_otoc71 on migtest_oto_child (name);
create unique index uq_m12_otoc71 on migtest_oto_child (lower(name));
create unique index ix_migtest_oto_master_lowername on migtest_oto_master (lower(name));
create index if not exists ix_migtest_e_basic_indextest1 on migtest_e_basic (indextest1);
create index if not exists ix_migtest_e_basic_indextest5 on migtest_e_basic (indextest5);
create index if not exists ix_m12_otoc71 on migtest_oto_child (name);
create unique index if not exists uq_m12_otoc71 on migtest_oto_child (lower(name));
create unique index if not exists ix_migtest_oto_master_lowername on migtest_oto_master (lower(name));
drop index if exists ix_migtest_e_basic_indextest3;
drop index if exists ix_migtest_e_basic_indextest6;
create index ix_migtest_e_basic_eref_id on migtest_e_basic (eref_id);
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<name>ebean external mapping api</name>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<!-- <parent>-->
<!-- <groupId>org.avaje</groupId>-->
@@ -14,7 +14,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.8.0</tag>
<tag>ebean-parent-12.8.1</tag>
</scm>
<name>ebean external mapping xml</name>
@@ -33,7 +33,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
@@ -59,14 +59,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<name>ebean postgis</name>
@@ -23,7 +23,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>provided</scope>
</dependency>
@@ -74,7 +74,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<name>ebean querybean</name>
@@ -17,7 +17,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>provided</scope>
</dependency>
@@ -57,21 +57,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
@@ -2,7 +2,10 @@ package io.ebean.typequery;
import io.ebean.ExpressionList;
import io.ebean.FetchConfig;
import io.ebean.FetchGroup;
import io.ebeaninternal.api.SpiQueryFetch;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.querydefn.SpiFetchGroup;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -142,7 +145,34 @@ public abstract class TQAssocBean<T, R> extends TQProperty<R> {
return _root;
}
private final SpiQueryFetch spiQuery() {
/**
* Fetch using the nested FetchGroup.
*/
public R fetch(FetchGroup<T> nestedGroup) {
return fetchNested(nestedGroup, FETCH_DEFAULT);
}
/**
* Fetch query using the nested FetchGroup.
*/
public R fetchQuery(FetchGroup<T> nestedGroup) {
return fetchNested(nestedGroup, FETCH_QUERY);
}
/**
* Fetch cache using the nested FetchGroup.
*/
public R fetchCache(FetchGroup<T> nestedGroup) {
return fetchNested(nestedGroup, FETCH_CACHE);
}
private R fetchNested(FetchGroup<T> nestedGroup, FetchConfig fetchConfig) {
OrmQueryDetail nestedDetail = ((SpiFetchGroup) nestedGroup).underlying();
spiQuery().addNested(_name, nestedDetail, fetchConfig);
return _root;
}
private SpiQueryFetch spiQuery() {
return (SpiQueryFetch)((TQRootBean) _root).query();
}
@@ -3,9 +3,13 @@ package org.querytest;
import io.ebean.DB;
import io.ebean.FetchGroup;
import io.ebean.test.LoggedSql;
import org.example.domain.Customer;
import org.example.domain.Order;
import org.example.domain.otherpackage.PhoneNumber;
import org.example.domain.query.QCustomer;
import org.example.domain.query.QOrder;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
@@ -18,6 +22,26 @@ public class QOrderTest {
private static final QOrder or = QOrder.alias();
private static final FetchGroup<Customer> fgC = QCustomer.forFetchGroup()
.select(cu.name, cu.phoneNumber)
.buildFetchGroup();
private static final FetchGroup<Order> fgNested1 = QOrder.forFetchGroup()
.select(or.status, or.shipDate)
.customer.fetch(fgC)
.buildFetchGroup();
private static final FetchGroup<Order> fgNested_fetchQuery = QOrder.forFetchGroup()
.select(or.status)
.customer.fetchQuery(fgC)
.buildFetchGroup();
private static final FetchGroup<Order> fgNested_fetchCache = QOrder.forFetchGroup()
.select(or.status)
.customer.fetchCache(fgC)
.buildFetchGroup();
private static final FetchGroup<Order> fg = QOrder.forFetchGroup()
.select(or.status, or.shipDate)
.customer.fetchCache(cu.name, cu.status, cu.registered, cu.comments)
@@ -28,6 +52,20 @@ public class QOrderTest {
.customer.fetch(cu.name)
.buildFetchGroup();
private static Order order;
private static Customer customer;
@BeforeClass
public static void before() {
setupData();
}
@AfterClass
public static void after() {
DB.delete(order);
DB.delete(customer);
}
@Test
public void fetchCache() {
@@ -76,6 +114,69 @@ public class QOrderTest {
assertThat(sql.get(0)).contains("select t0.id, t0.status, t1.id, t1.name from o_order t0 join be_customer t1 on t1.id = t0.customer_id where");
}
@Test
public void viaFetchGraph_withNested() {
DB.getDefault();
LoggedSql.start();
new QOrder()
.status.eq(Order.Status.NEW)
.select(fgNested1)
.findList();
final List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("select t0.id, t0.status, t0.ship_date, t1.id, t1.name, t1.phone_number from o_order t0 join be_customer t1 on t1.id = t0.customer_id where");
}
@Test
public void viaFetchGraph_withNested_fetchQuery() {
DB.getDefault();
LoggedSql.start();
final Order found = new QOrder()
.id.eq(order.getId())
.select(fgNested_fetchQuery)
.findOne();
final List<String> sql = LoggedSql.stop();
// assert fetching customer via fetchQuery
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("select t0.id, t0.status, t0.customer_id from o_order t0 where t0.id = ?");
assertThat(sql.get(1)).contains("select t0.id, t0.name, t0.phone_number from be_customer t0 where t0.id = ?");
assertThat(found.getCustomer().getPhoneNumber().getMsisdn()).isEqualTo("Ph1");
}
@Test
public void viaFetchGraph_withNested_fetchCache() {
DB.getDefault();
// ensure the customer is loaded in the L2 cache
new QCustomer().id.eq(customer.getId()).findOne();
LoggedSql.start();
final Order found = new QOrder()
.id.eq(order.getId())
.select(fgNested_fetchCache) // cache hit for customer
.findOne();
final String msisdn = found.getCustomer().getPhoneNumber().getMsisdn();
assertThat(msisdn).isEqualTo("Ph1");
final List<String> sql = LoggedSql.stop();
// assert we only hit DB for order
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("select t0.id, t0.status, t0.customer_id from o_order t0 where t0.id = ?");
}
@Test
public void select_partial() {
@@ -113,4 +214,15 @@ public class QOrderTest {
}
private static void setupData() {
customer = new Customer();
customer.setName("Fred");
customer.setPhoneNumber(new PhoneNumber("Ph1"));
customer.save();
order = new Order();
order.setCustomer(customer);
order.save();
}
}
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<artifactId>ebean-redis</artifactId>
@@ -22,35 +22,35 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<name>ebean test</name>
@@ -29,14 +29,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<name>ebean composite</name>
@@ -22,20 +22,20 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
<!-- Technically optional but most expected to use query beans -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
</dependency>
</dependencies>
+44 -43
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<name>kotlin querybean generator</name>
@@ -12,7 +12,7 @@
<artifactId>kotlin-querybean-generator</artifactId>
<properties>
<kotlin.version>1.4.21</kotlin.version>
<kotlin.version>1.4.31</kotlin.version>
</properties>
<dependencies>
@@ -29,7 +29,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
@@ -43,7 +43,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
@@ -64,7 +64,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<scope>test</scope>
</dependency>
@@ -81,46 +81,47 @@
<build>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
<execution>
<id>test-kapt</id>
<goals>
<goal>test-kapt</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>src/test/kotlin</sourceDir>
</sourceDirs>
<!-- <annotationProcessorPaths>-->
<!-- <annotationProcessorPath>-->
<!-- <groupId>io.ebean</groupId>-->
<!-- <artifactId>kotlin-querybean-generator</artifactId>-->
<!--&lt;!&ndash; <version>12.7.1</version>&ndash;&gt;-->
<!-- <version>12.8.0-SNAPSHOT</version>-->
<!-- </annotationProcessorPath>-->
<!-- </annotationProcessorPaths>-->
</configuration>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
<!-- <plugin>-->
<!-- <groupId>org.jetbrains.kotlin</groupId>-->
<!-- <artifactId>kotlin-maven-plugin</artifactId>-->
<!-- <version>${kotlin.version}</version>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <id>test-compile</id>-->
<!-- <phase>test-compile</phase>-->
<!-- <goals>-->
<!-- <goal>test-compile</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- <execution>-->
<!-- <id>test-kapt</id>-->
<!-- <goals>-->
<!-- <goal>test-kapt</goal>-->
<!-- </goals>-->
<!-- <configuration>-->
<!-- <sourceDirs>-->
<!-- <sourceDir>src/test/kotlin</sourceDir>-->
<!-- </sourceDirs>-->
<!--&lt;!&ndash; <annotationProcessorPaths>&ndash;&gt;-->
<!--&lt;!&ndash; <annotationProcessorPath>&ndash;&gt;-->
<!--&lt;!&ndash; <groupId>io.ebean</groupId>&ndash;&gt;-->
<!--&lt;!&ndash; <artifactId>kotlin-querybean-generator</artifactId>&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash; <version>12.7.1</version>&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash; <version>12.8.0-SNAPSHOT</version>&ndash;&gt;-->
<!--&lt;!&ndash; </annotationProcessorPath>&ndash;&gt;-->
<!--&lt;!&ndash; </annotationProcessorPaths>&ndash;&gt;-->
<!-- </configuration>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- <configuration>-->
<!-- <jvmTarget>1.8</jvmTarget>-->
<!--&lt;!&ndash; <args></args>&ndash;&gt;-->
<!-- </configuration>-->
<!-- </plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<version>3.8.1</version>
<executions>
<execution>
<id>default-testCompile</id>
@@ -145,7 +146,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.6.6</version>
<version>12.8.1</version>
<executions>
<execution>
<id>test</id>
+3 -3
View File
@@ -4,12 +4,12 @@
<parent>
<groupId>org.avaje</groupId>
<artifactId>java8-oss</artifactId>
<version>2.3</version>
<version>3.1</version>
</parent>
<groupId>io.ebean</groupId>
<artifactId>ebean-parent</artifactId>
<version>12.8.0</version>
<version>12.8.1</version>
<packaging>pom</packaging>
<name>ebean parent</name>
@@ -18,7 +18,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.8.0</tag>
<tag>ebean-parent-12.8.1</tag>
</scm>
<licenses>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.0</version>
<version>12.8.1</version>
</parent>
<name>querybean generator</name>
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.avaje</groupId>
<artifactId>java8-oss</artifactId>
<version>2.3</version>
</parent>
<groupId>io.ebean</groupId>
<artifactId>tests</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>tests</name>
<description>test modules</description>
<modules>
<module>test-java16</module>
</modules>
</project>
+69
View File
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- <parent>-->
<!-- <groupId>org.avaje</groupId>-->
<!-- <artifactId>java11-oss</artifactId>-->
<!-- <version>2.3</version>-->
<!-- </parent>-->
<groupId>io.ebean</groupId>
<artifactId>test-java16</artifactId>
<version>1.0</version>
<properties>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.8.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.8.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.avaje.composite</groupId>
<artifactId>junit</artifactId>
<version>5.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<release>16</release>
<source>16</source>
<target>16</target>
<annotationProcessorPaths>
<path>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.8.1-SNAPSHOT</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,7 @@
package org.example.records;
import javax.persistence.Embeddable;
@Embeddable
public record Address (String line1, String line2, String city) {
}
@@ -0,0 +1,35 @@
package org.example.records;
import io.ebean.Model;
import io.ebean.annotation.Identity;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
@Identity(start = 1000, cache = 100)
@MappedSuperclass
public class BaseModel extends Model {
@Id
long id;
@Version
long version;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,58 @@
package org.example.records;
import io.ebean.Model;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class Contact extends Model {
@Id
private long id;
@Version
private long version;
private final String name;
@Embedded(prefix = "home_")
private Address homeAddress;
@Embedded(prefix = "work_")
private Address workAddress;
public Contact(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Address getHomeAddress() {
return homeAddress;
}
public void setHomeAddress(Address homeAddress) {
this.homeAddress = homeAddress;
}
public Address getWorkAddress() {
return workAddress;
}
public void setWorkAddress(Address workAddress) {
this.workAddress = workAddress;
}
public long getId() {
return id;
}
public long getVersion() {
return version;
}
}
@@ -0,0 +1,33 @@
package org.example.records;
import io.ebean.annotation.Length;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name = "course")
public class Course extends BaseModel {
@Length(200)
final String name;
@Length(400)
String summary;
public Course(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
}
@@ -0,0 +1,13 @@
package org.example.records;
import io.ebean.annotation.Identity;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Identity(start = 1000)
@Entity
@Table(name="course_rec")
public record CourseRecordEntity(@Id long id, String name, String notes) {
}
@@ -0,0 +1,36 @@
package org.example.records;
import io.ebean.Model;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Version;
@Entity
public class UserRole extends Model {
@EmbeddedId
final UserRoleId id;
String note;
@Version
long version;
public UserRole(UserRoleId id, String note) {
this.id = id;
this.note = note;
}
public UserRoleId getId() {
return id;
}
public String getNote() {
return note;
}
public long getVersion() {
return version;
}
}
@@ -0,0 +1,7 @@
package org.example.records;
import javax.persistence.Embeddable;
@Embeddable
public record UserRoleId(Integer userId, String roleId) {
}
@@ -0,0 +1,51 @@
package org.example.records;
import io.ebean.Model;
import javax.persistence.*;
import java.util.UUID;
@IdClass(UserSiteId.class)
@Entity
public class UserSite extends Model {
@Id
final UUID userId;
@Id
final UUID siteId;
String note;
@Version
long version;
public UserSite(UUID userId, UUID siteId) {
this.userId = userId;
this.siteId = siteId;
}
public UUID getUserId() {
return userId;
}
public UUID getSiteId() {
return siteId;
}
public void setNote(String note) {
this.note = note;
}
public void setVersion(long version) {
this.version = version;
}
public String getNote() {
return note;
}
public long getVersion() {
return version;
}
}
@@ -0,0 +1,8 @@
package org.example.records;
import javax.persistence.Embeddable;
import java.util.UUID;
@Embeddable
public record UserSiteId(UUID userId, UUID siteId) {
}
@@ -0,0 +1,40 @@
package org.example.records;
import io.ebean.DB;
import org.example.records.query.QCourseRecordEntity;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class CourseRecordEntityTest {
@Test
void record_insert_withGivenIdValue() {
var course = new CourseRecordEntity(42, "SuppliedId", "Record");
DB.save(course);
assertThat(course.id()).isEqualTo(42);
var found = new QCourseRecordEntity()
.name.startsWith("SuppliedId")
.findOneOrEmpty();
assertThat(found).isPresent();
assertThat(found.get().id()).isEqualTo(42L);
assertThat(found.get().notes()).isEqualTo("Record");
}
@Test
void record_insert_usingGeneratedId() {
var course2 = new CourseRecordEntity(0, "Second", "Record with generated id");
DB.save(course2);
// as using @Identity(start = 1000)
assertThat(course2.id()).isEqualTo(1000);
var second = new QCourseRecordEntity()
.name.startsWith("Second")
.findOneOrEmpty();
assertThat(second).isPresent();
assertThat(second.get().notes()).isEqualTo("Record with generated id");
}
}
@@ -0,0 +1,59 @@
package org.example.records;
import io.ebean.DB;
import org.example.records.query.QCourse;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.example.records.query.QCourse.Alias.id;
import static org.example.records.query.QCourse.Alias.name;
class DtoQueryUsingRecordsTest {
@Test
void dtoQuery_projectRecords() {
var course = new Course("Calculus");
course.setSummary("Something here");
course.save();
performOrmQuery();
records_via_SqlDtoQuery(course);
records_via_OrmQueryToDtoQuery(course);
course.delete();
}
private void performOrmQuery() {
List<Course> courses = new QCourse()
.name.startsWith("Calc")
.findList();
assertThat(courses).hasSize(1);
}
private void records_via_SqlDtoQuery(Course course) {
List<Foo> records = DB.findDto(Foo.class, "select id, name from course where name like ?")
.setParameter("Calc%")
.findList();
assertThat(records).hasSize(1);
assertThat(records.get(0).name()).isEqualTo(course.getName());
}
private void records_via_OrmQueryToDtoQuery(Course course) {
List<Foo> records2 = new QCourse()
.select(id, name )
.name.startsWith("Calc")
.asDto(Foo.class)
.findList();
assertThat(records2).hasSize(1);
assertThat(records2.get(0).name()).isEqualTo(course.getName());
}
public record Foo(long id, String name){}
}
@@ -0,0 +1,48 @@
package org.example.records;
import org.example.records.query.QContact;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.example.records.query.QContact.Alias.homeAddress;
import static org.example.records.query.QContact.Alias.name;
public class RecordAsEmbeddedTest {
@Test
void insert_query() {
var contact = new Contact("Rob");
contact.setWorkAddress(new Address("45 work", "workling", "wo"));
contact.setHomeAddress(new Address("94 home st", "homeline", "wo"));
contact.save();
Contact found = new QContact()
.id.eq(contact.getId())
.findOne();
assertThat(found.getWorkAddress().toString()).isEqualTo("Address[line1=45 work, line2=workling, city=wo]");
assertThat(found.getHomeAddress().toString()).isEqualTo("Address[line1=94 home st, line2=homeline, city=wo]");
Contact foundPartial = new QContact()
.select(name, homeAddress)
.id.eq(contact.getId())
.findOne();
// invoke lazy loading on getWorkAddress
assertThat(foundPartial.getWorkAddress().toString()).isEqualTo("Address[line1=45 work, line2=workling, city=wo]");
assertThat(foundPartial.getHomeAddress().toString()).isEqualTo("Address[line1=94 home st, line2=homeline, city=wo]");
Contact foundNoLazyLoading = new QContact()
.select(name, homeAddress)
.setDisableLazyLoading(true)
.id.eq(contact.getId())
.findOne();
// no lazy loading on getWorkAddress this time
assertThat(foundNoLazyLoading.getWorkAddress()).isNull();
assertThat(foundNoLazyLoading.getHomeAddress().toString()).isEqualTo("Address[line1=94 home st, line2=homeline, city=wo]");
}
}
@@ -0,0 +1,34 @@
package org.example.records;
import org.example.records.query.QUserSite;
import org.junit.jupiter.api.Test;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
class RecordIdClassTest {
@Test
void insert_query() {
UUID userId = UUID.randomUUID();
UUID siteId = UUID.randomUUID();
var userSite = new UserSite(userId, siteId);
userSite.setNote("HelloIdClass");
userSite.save();
var id = new UserSiteId(userId, siteId);
UserSite found = new QUserSite()
.setId(id)
.findOne();
assertThat(found.getUserId()).isEqualTo(userId);
assertThat(found.getSiteId()).isEqualTo(siteId);
assertThat(found.getNote()).isEqualTo("HelloIdClass");
}
}
@@ -0,0 +1,38 @@
package org.example.records;
import org.example.records.query.QUserRole;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class UserRoleTest {
final UserRoleId id = new UserRoleId(42, "R7");
@Test
void id_equals() {
assertThat(id).isEqualTo(new UserRoleId(42, "R7"));
}
@Test
void id_notEquals() {
assertThat(id).isNotEqualTo(new UserRoleId(43, "R7"));
assertThat(id).isNotEqualTo(new UserRoleId(42, "R8"));
}
@Test
void insert_query() {
var userRole = new UserRole(id, "hello");
userRole.save();
UserRole found = new QUserRole()
.id.eq(new UserRoleId(42, "R7"))
.findOne();
UserRoleId id1 = found.getId();
assertThat(id1).isEqualTo(id);
assertThat(id1.userId()).isEqualTo(42);
assertThat(id1.roleId()).isEqualTo("R7");
}
}
@@ -0,0 +1,6 @@
ebean:
test:
platform: h2
ddlMode: dropCreate
dbName: foo
@@ -0,0 +1,22 @@
<configuration scan="true" scanPeriod="10 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
<logger name="org.avaje" level="TRACE"/>
<logger name="io.ebean" level="INFO"/>
<logger name="io.ebean.docker" level="TRACE"/>
<logger name="io.ebean.DDL" level="DEBUG"/>
<logger name="io.ebean.SQL" level="TRACE"/>
<logger name="io.ebean.TXN" level="TRACE"/>
<logger name="io.ebean.SUM" level="TRACE"/>
</configuration>