mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
57
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9eb54da2dc | ||
|
|
bb6ff866c7 | ||
|
|
2d719c5b37 | ||
|
|
9d54b264ad | ||
|
|
65b282a056 | ||
|
|
fc174c8988 | ||
|
|
06802896ef | ||
|
|
7f13ac9e5e | ||
|
|
4a1374d22d | ||
|
|
4ee8d647b7 | ||
|
|
59793f3c01 | ||
|
|
f7f383d8f3 | ||
|
|
562a3959b6 | ||
|
|
bb1275f4f9 | ||
|
|
7fde6b9331 | ||
|
|
3e97a4005a | ||
|
|
c271760230 | ||
|
|
f7c1845f0c | ||
|
|
9b04f20ed3 | ||
|
|
62b6b6293b | ||
|
|
6d8a1c9398 | ||
|
|
303039735f | ||
|
|
6bcf5c6881 | ||
|
|
0527f45f5a | ||
|
|
ba397bbb0c | ||
|
|
a617d58902 | ||
|
|
7fc4a229ae | ||
|
|
f53d31aa2e | ||
|
|
b889b6a6b0 | ||
|
|
f9b55a06b8 | ||
|
|
09d772a5e5 | ||
|
|
f7035eb228 | ||
|
|
1bf09ddd38 | ||
|
|
a8bf27da55 | ||
|
|
ee8bb5ef75 | ||
|
|
bcfd9cab1d | ||
|
|
2a5fd7f3a9 | ||
|
|
f8c37772d4 | ||
|
|
d988dbe69d | ||
|
|
5cb9b9b237 | ||
|
|
819d391495 | ||
|
|
1f0ed629a7 | ||
|
|
91044d0a75 | ||
|
|
57736185d0 | ||
|
|
8992980689 | ||
|
|
f341ff0167 | ||
|
|
abfa841ed7 | ||
|
|
e68adf53bc | ||
|
|
562d175de1 | ||
|
|
b5cef354ad | ||
|
|
09a887aeb7 | ||
|
|
53c903fa48 | ||
|
|
548454c90d | ||
|
|
94bab5f83b | ||
|
|
78965c1ac4 | ||
|
|
00b0fcaeb3 | ||
|
|
f7905c8e94 |
+2
-2
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
@@ -50,7 +50,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>6.15</version>
|
||||
<version>7.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1671,7 +1671,7 @@ public interface ExpressionList<T> {
|
||||
ExpressionList<T> endAnd();
|
||||
|
||||
/**
|
||||
* End a AND junction - synonym for endJunction().
|
||||
* End a OR junction - synonym for endJunction().
|
||||
*/
|
||||
ExpressionList<T> endOr();
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -59,6 +59,14 @@ public interface Transaction extends AutoCloseable {
|
||||
*/
|
||||
void register(TransactionCallback callback);
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL - turn on automatic persistence of dirty beans and batchMode true.
|
||||
* <p>
|
||||
* With this turned on beans that are dirty in the persistence context
|
||||
* are automatically persisted on flush() and commit().
|
||||
*/
|
||||
void setAutoPersistUpdates(boolean autoPersistUpdates);
|
||||
|
||||
/**
|
||||
* Set a label on the transaction.
|
||||
* <p>
|
||||
@@ -339,15 +347,12 @@ public interface Transaction extends AutoCloseable {
|
||||
* The batch is automatically flushed when it hits the batch size and also when we
|
||||
* execute queries or when we mix UpdateSql and CallableSql with save and delete of
|
||||
* beans.
|
||||
* </p>
|
||||
* <p>
|
||||
* We use {@link #flush()} to explicitly flush the batch and we can use
|
||||
* {@link #setFlushOnQuery(boolean)} and {@link #setFlushOnMixed(boolean)}
|
||||
* to control the automatic flushing behaviour.
|
||||
* </p>
|
||||
* <p>
|
||||
* Example: batch processing of CallableSql executing every 10 rows
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -392,14 +397,11 @@ public interface Transaction extends AutoCloseable {
|
||||
* <p>
|
||||
* This only takes effect when batch mode on the transaction has not already meant that
|
||||
* JDBC batch mode is being used.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades
|
||||
* and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is effectively already turned on for all platforms apart from older Sql Server.
|
||||
* </p>
|
||||
*
|
||||
* @param batchMode the batch mode to use per save(), insert(), update() or delete()
|
||||
* @see io.ebean.config.DatabaseConfig#setPersistBatchOnCascade(PersistBatch)
|
||||
@@ -422,15 +424,19 @@ public interface Transaction extends AutoCloseable {
|
||||
int getBatchSize();
|
||||
|
||||
/**
|
||||
* Specify if you want batched inserts to use getGeneratedKeys.
|
||||
* Specify if we want batched inserts to use getGeneratedKeys.
|
||||
* <p>
|
||||
* By default batched inserts will try to use getGeneratedKeys if it is
|
||||
* supported by the underlying jdbc driver and database.
|
||||
* </p>
|
||||
* <p>
|
||||
* You may want to turn getGeneratedKeys off when you are inserting a large
|
||||
* number of objects and you don't care about getting back the ids.
|
||||
* </p>
|
||||
* We want to turn off getGeneratedKeys when we are inserting a large
|
||||
* number of objects and we don't care about getting back the ids. In this
|
||||
* way we avoid the extra cost of getting back the generated id values
|
||||
* from the database.
|
||||
* <p>
|
||||
* Note that when we do turn off getGeneratedKeys then we have the limitation
|
||||
* that after a bean has been inserted we are unable to then mutate the bean
|
||||
* and update it in the same transaction as we have not obtained it's id value.
|
||||
*/
|
||||
void setGetGeneratedKeys(boolean getGeneratedKeys);
|
||||
|
||||
@@ -449,13 +455,11 @@ public interface Transaction extends AutoCloseable {
|
||||
* <p>
|
||||
* If you want to execute both WITHOUT having the batch automatically flush
|
||||
* you need to call this with batchFlushOnMixed = false.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that UpdateSql and CallableSql are ALWAYS executed first (before the
|
||||
* beans are executed). This is because the UpdateSql and CallableSql have
|
||||
* already been bound to their PreparedStatements. The beans on the other hand
|
||||
* have a 2 step process (delayed binding).
|
||||
* </p>
|
||||
*/
|
||||
void setFlushOnMixed(boolean batchFlushOnMixed);
|
||||
|
||||
@@ -473,7 +477,6 @@ public interface Transaction extends AutoCloseable {
|
||||
* <p>
|
||||
* Calling this method with batchFlushOnQuery = false means that you can
|
||||
* execute a query and the batch will not be automatically flushed.
|
||||
* </p>
|
||||
*/
|
||||
void setFlushOnQuery(boolean batchFlushOnQuery);
|
||||
|
||||
@@ -490,7 +493,6 @@ public interface Transaction extends AutoCloseable {
|
||||
* should be flushed prior to executing a query.
|
||||
* <p>
|
||||
* The default is for this to be true.
|
||||
* </p>
|
||||
*/
|
||||
boolean isFlushOnQuery();
|
||||
|
||||
@@ -507,7 +509,6 @@ public interface Transaction extends AutoCloseable {
|
||||
* flush the batch if you like.
|
||||
* <p>
|
||||
* Flushing occurs automatically when:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>the batch size is reached</li>
|
||||
* <li>A query is executed on the same transaction</li>
|
||||
@@ -533,11 +534,9 @@ public interface Transaction extends AutoCloseable {
|
||||
* commit() rollback() and end() methods on the Transaction should still be
|
||||
* used. Calling these methods on the Connection would be a big no no unless
|
||||
* you know what you are doing.
|
||||
* </p>
|
||||
* <p>
|
||||
* Examples of when a developer may wish to use the connection directly are:
|
||||
* Savepoints, advanced CLOB BLOB use and advanced stored procedure calls.
|
||||
* </p>
|
||||
*/
|
||||
Connection getConnection();
|
||||
|
||||
@@ -545,17 +544,14 @@ public interface Transaction extends AutoCloseable {
|
||||
* Add table modification information to the TransactionEvent.
|
||||
* <p>
|
||||
* Use this in conjunction with getConnection() and raw JDBC.
|
||||
* </p>
|
||||
* <p>
|
||||
* This effectively informs Ebean of the data that has been changed by the
|
||||
* transaction and this information is normally automatically handled by Ebean
|
||||
* when you save entity beans or use UpdateSql etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you use raw JDBC then you can use this method to inform Ebean for the
|
||||
* tables that have been modified. Ebean uses this information to keep its
|
||||
* caches in synch and maintain text indexes.
|
||||
* </p>
|
||||
*/
|
||||
void addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebean;
|
||||
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.annotation.TxIsolation;
|
||||
import io.ebean.annotation.TxOption;
|
||||
import io.ebean.annotation.TxType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -33,6 +34,8 @@ public final class TxScope {
|
||||
|
||||
private TxIsolation isolation;
|
||||
|
||||
private TxOption autoPersistUpdates;
|
||||
|
||||
private PersistBatch batch;
|
||||
|
||||
private PersistBatch batchOnCascade;
|
||||
@@ -123,6 +126,13 @@ public final class TxScope {
|
||||
+ "] serverName[" + serverName + "] rollbackFor[" + rollbackFor + "] noRollbackFor[" + noRollbackFor + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the AutoPersistUpdates mode as a nullable Boolean.
|
||||
*/
|
||||
public Boolean getAutoPersistUpdates() {
|
||||
return autoPersistUpdates == null ? null : autoPersistUpdates.asBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if PersistBatch has been set.
|
||||
*/
|
||||
@@ -176,6 +186,14 @@ public final class TxScope {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the autoPersistUpdates mode.
|
||||
*/
|
||||
public TxScope setAutoPersistUpdates(TxOption autoPersistUpdates) {
|
||||
this.autoPersistUpdates = autoPersistUpdates;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction profile id.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebean.bean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds entity beans by there type and id.
|
||||
* <p>
|
||||
@@ -77,6 +79,11 @@ public interface PersistenceContext {
|
||||
*/
|
||||
boolean resetLimit();
|
||||
|
||||
/**
|
||||
* Return the list of dirty beans held by this persistence context.
|
||||
*/
|
||||
List<Object> dirtyBeans();
|
||||
|
||||
/**
|
||||
* Wrapper on a bean to also indicate if a bean has been deleted.
|
||||
* <p>
|
||||
|
||||
@@ -232,6 +232,12 @@ public class DatabaseConfig {
|
||||
*/
|
||||
private String historyTableSuffix = "_history";
|
||||
|
||||
/**
|
||||
* When true explicit transactions beans that have been made dirty will be
|
||||
* automatically persisted via update on flush.
|
||||
*/
|
||||
private boolean autoPersistUpdates;
|
||||
|
||||
/**
|
||||
* Use for transaction scoped batch mode.
|
||||
*/
|
||||
@@ -498,14 +504,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.
|
||||
@@ -896,6 +911,20 @@ public class DatabaseConfig {
|
||||
this.tenantCatalogProvider = tenantCatalogProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if dirty beans are automatically persisted.
|
||||
*/
|
||||
public boolean isAutoPersistUpdates() {
|
||||
return autoPersistUpdates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if dirty beans are automatically persisted.
|
||||
*/
|
||||
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
|
||||
this.autoPersistUpdates = autoPersistUpdates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PersistBatch mode to use by default at the transaction level.
|
||||
* <p>
|
||||
@@ -1045,7 +1074,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 +2721,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();
|
||||
@@ -2796,14 +2827,19 @@ public class DatabaseConfig {
|
||||
loadDocStoreSettings(p);
|
||||
|
||||
defaultServer = p.getBoolean("defaultServer", defaultServer);
|
||||
autoPersistUpdates = p.getBoolean("autoPersistUpdates", autoPersistUpdates);
|
||||
loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
|
||||
maxCallStack = p.getInt("maxCallStack", maxCallStack);
|
||||
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
|
||||
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
|
||||
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
|
||||
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
|
||||
collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans);
|
||||
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 +3129,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 +3212,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3249,7 +3369,7 @@ public class DatabaseConfig {
|
||||
this.loadModuleInfo = loadModuleInfo;
|
||||
}
|
||||
|
||||
public enum UuidVersion {
|
||||
public enum UuidVersion {
|
||||
VERSION4,
|
||||
VERSION1,
|
||||
VERSION1RND
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</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.3</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.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
+17
-17
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</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.2</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.8.2</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.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-xml</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-autotune</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</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.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
+7
-7
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</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.3</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -72,7 +72,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.4a</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -87,19 +87,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</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.2</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.
|
||||
*/
|
||||
|
||||
@@ -73,6 +73,10 @@ public class ScopeTrans {
|
||||
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
|
||||
restoreBatchFlushOnQuery = transaction.isFlushOnQuery();
|
||||
}
|
||||
Boolean autoPersistUpdates = txScope.getAutoPersistUpdates();
|
||||
if (autoPersistUpdates != null) {
|
||||
transaction.setAutoPersistUpdates(autoPersistUpdates);
|
||||
}
|
||||
if (txScope.isBatchSet()) {
|
||||
transaction.setBatchMode(txScope.isBatchMode());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -147,6 +147,11 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
int depth();
|
||||
|
||||
/**
|
||||
* Return true if dirty beans are automatically persisted.
|
||||
*/
|
||||
boolean isAutoPersistUpdates();
|
||||
|
||||
/**
|
||||
* Return true if this transaction was created explicitly via
|
||||
* <code>Ebean.beginTransaction()</code>.
|
||||
|
||||
@@ -43,6 +43,16 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
return transaction.getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
|
||||
transaction.setAutoPersistUpdates(autoPersistUpdates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoPersistUpdates() {
|
||||
return transaction.isAutoPersistUpdates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitAndContinue() {
|
||||
transaction.commitAndContinue();
|
||||
|
||||
@@ -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();
|
||||
@@ -242,7 +236,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.clockService = config.getClockService();
|
||||
|
||||
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
|
||||
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
|
||||
this.transactionManager = config.createTransactionManager(this, docStoreComponents.updateProcessor());
|
||||
this.documentStore = docStoreComponents.documentStore();
|
||||
this.queryPlanManager = config.initQueryPlanManager(transactionManager);
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this);
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ import java.util.function.Predicate;
|
||||
*/
|
||||
public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
|
||||
private static final String ENC_PREFIX = EncryptAlias.PREFIX;
|
||||
private static final String ENC_PREFIX_UPPER = EncryptAlias.PREFIX.toUpperCase();
|
||||
|
||||
private final SpiDtoQuery<T> query;
|
||||
|
||||
private final DtoQueryEngine queryEngine;
|
||||
@@ -133,9 +136,9 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
static String parseColumn(String columnLabel) {
|
||||
if (columnLabel.startsWith("_e_") || columnLabel.startsWith("_E_")) {
|
||||
if (columnLabel.startsWith(ENC_PREFIX) || columnLabel.startsWith(ENC_PREFIX_UPPER)) {
|
||||
// encrypted column alias in the form _e_<tableAlias>_<column>
|
||||
final int pos = columnLabel.indexOf("_", 3);
|
||||
final int pos = columnLabel.indexOf("_", 4);
|
||||
if (pos > -1) {
|
||||
return columnLabel.substring(pos + 1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
/**
|
||||
* Used to create column alias for encrypted columns.
|
||||
*/
|
||||
public interface EncryptAlias {
|
||||
String PREFIX = "zx__";
|
||||
}
|
||||
@@ -423,13 +423,13 @@ public class InternalConfiguration {
|
||||
/**
|
||||
* Create the TransactionManager taking into account autoCommit mode.
|
||||
*/
|
||||
TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) {
|
||||
TransactionManager createTransactionManager(SpiServer server, DocStoreUpdateProcessor indexUpdateProcessor) {
|
||||
|
||||
TransactionScopeManager scopeManager = createTransactionScopeManager();
|
||||
boolean notifyL2CacheInForeground = cacheManager.isLocalL2Caching() || config.isNotifyL2CacheInForeground();
|
||||
|
||||
TransactionManagerOptions options =
|
||||
new TransactionManagerOptions(notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
|
||||
new TransactionManagerOptions(server, notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager,
|
||||
tableModState, cacheNotify, clockService);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -887,6 +887,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove deleted beans from the persistence context early.
|
||||
*/
|
||||
public void removeFromPersistenceContext() {
|
||||
idValue = beanDescriptor.getId(entityBean);
|
||||
beanDescriptor.contextDeleted(transaction.getPersistenceContext(), idValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggressive L1 and L2 cache cleanup for deletes.
|
||||
*/
|
||||
@@ -1030,6 +1038,17 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (!publish) {
|
||||
beanDescriptor.setDraft(entityBean);
|
||||
}
|
||||
if (transaction.isAutoPersistUpdates() && idValue != null) {
|
||||
// with getGeneratedKeys off we will not have a idValue
|
||||
beanDescriptor.contextPut(transaction.getPersistenceContext(), idValue, entityBean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if persist can be skipped on the reference only bean.
|
||||
*/
|
||||
public boolean isSkipReference() {
|
||||
return intercept.isReference() || (Flags.isRecurse(flags) && beanDescriptor.referenceIdPropertyOnly(intercept));
|
||||
}
|
||||
|
||||
public boolean isReference() {
|
||||
|
||||
@@ -2473,7 +2473,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Return the property path given the db table and column.
|
||||
*/
|
||||
public String findBeanPath(String schemaName, String tableName, String columnName) {
|
||||
if (matchBaseTable(schemaName, tableName)) {
|
||||
if (matchBaseTable(tableName)) {
|
||||
return columnPath.get(columnName);
|
||||
}
|
||||
BeanPropertyAssoc<?> assocProperty = tablePath.get(tableName);
|
||||
@@ -2489,10 +2489,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean matchBaseTable(String schemaName, String tableName) {
|
||||
boolean matchBaseTable(String tableName) {
|
||||
return tableName.isEmpty()
|
||||
|| baseTable.equalsIgnoreCase(tableName)
|
||||
|| baseTable.equalsIgnoreCase(schemaName + "." + tableName);
|
||||
|| baseTable.endsWith("." + tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3134,7 +3134,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return ebi.isReference() || referenceIdPropertyOnly(ebi);
|
||||
}
|
||||
|
||||
boolean referenceIdPropertyOnly(EntityBeanIntercept ebi) {
|
||||
public boolean referenceIdPropertyOnly(EntityBeanIntercept ebi) {
|
||||
return idOnlyReference && ebi.hasIdOnly(idPropertyIndex);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,6 +18,7 @@ import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.core.EncryptAlias;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedWhenCreated;
|
||||
@@ -65,6 +66,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
|
||||
|
||||
private static final String ENC_PREFIX = " " + EncryptAlias.PREFIX;
|
||||
|
||||
/**
|
||||
* Flag to mark this is the id property.
|
||||
*/
|
||||
@@ -531,7 +534,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
* Return the SQL for the column including decryption function and column alias.
|
||||
*/
|
||||
private String getDecryptSqlWithColumnAlias(String tableAlias) {
|
||||
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()) + " _e_" + tableAlias + "_" + this.getDbColumn();
|
||||
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()) + ENC_PREFIX + tableAlias + "_" + this.getDbColumn();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -81,18 +81,18 @@ class BeanPropertyAssocManySqlHelp<T> {
|
||||
|
||||
@Override
|
||||
public void visitOneImported(BeanPropertyAssocOne<?> p) {
|
||||
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
public void visitScalar(BeanProperty p, boolean allowNonNull) {
|
||||
sb.append(",").append(p.getDbColumn());
|
||||
colCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -44,9 +44,11 @@ public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor {
|
||||
public abstract void visitOneImported(BeanPropertyAssocOne<?> p);
|
||||
|
||||
/**
|
||||
* Override this method for normal scalar property.
|
||||
* Override this method for normal scalar property. With allowNonNull
|
||||
* used with properties of Embedded beans that can only be Nonnull when
|
||||
* the Embedded bean is Nonnull.
|
||||
*/
|
||||
@Override
|
||||
public abstract void visitScalar(BeanProperty p);
|
||||
public abstract void visitScalar(BeanProperty p, boolean allowNonNull);
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,8 +40,8 @@ public interface BeanPropertyVisitor {
|
||||
void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded);
|
||||
|
||||
/**
|
||||
* Visit a scalar property.
|
||||
* Visit a scalar property specify allowing non-null (e.g. embedded bean scalar properties).
|
||||
*/
|
||||
void visitScalar(BeanProperty p);
|
||||
void visitScalar(BeanProperty p, boolean allowNonNull);
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ public class VisitProperties {
|
||||
|
||||
} else {
|
||||
// simple scalar type
|
||||
pv.visitScalar(p);
|
||||
pv.visitScalar(p, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -81,7 +81,7 @@ public final class BatchControl {
|
||||
*/
|
||||
private int bufferMax;
|
||||
|
||||
private Queue[] queues = new Queue[3];
|
||||
private final Queue[] queues = new Queue[3];
|
||||
|
||||
/**
|
||||
* Create for a given transaction, PersistExecute, default size and getGeneratedKeys.
|
||||
|
||||
@@ -447,7 +447,7 @@ public final class DefaultPersister implements Persister {
|
||||
public void insert(EntityBean bean, Transaction t) {
|
||||
|
||||
PersistRequestBean<?> req = createRequest(bean, t, PersistRequest.Type.INSERT);
|
||||
if (req.isReference()) {
|
||||
if (req.isSkipReference()) {
|
||||
// skip insert on reference bean
|
||||
return;
|
||||
}
|
||||
@@ -881,6 +881,7 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
|
||||
int count = request.executeOrQueue();
|
||||
request.removeFromPersistenceContext();
|
||||
|
||||
if (request.isPersistCascade()) {
|
||||
deleteAssocOne(request);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+27
-4
@@ -1,11 +1,9 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
@@ -200,6 +198,20 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> dirtyBeans() {
|
||||
lock.lock();
|
||||
try {
|
||||
List<Object> list = new ArrayList<>();
|
||||
for (ClassContext classContext : typeCache.values()) {
|
||||
classContext.dirtyBeans(list);
|
||||
}
|
||||
return list;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
lock.lock();
|
||||
@@ -318,6 +330,17 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
deleteSet.add(id);
|
||||
map.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the dirty beans to the list.
|
||||
*/
|
||||
void dirtyBeans(List<Object> list) {
|
||||
for (Object value : map.values()) {
|
||||
if (((EntityBean) value)._ebean_getIntercept().isDirty()) {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
@@ -95,6 +95,16 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
return startNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoPersistUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
// do nothing
|
||||
|
||||
@@ -187,6 +187,8 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
|
||||
private final long startNanos;
|
||||
|
||||
private boolean autoPersistUpdates;
|
||||
|
||||
/**
|
||||
* Create a new JdbcTransaction.
|
||||
*/
|
||||
@@ -209,11 +211,12 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
this.batchOnCascadeMode = false;
|
||||
this.onQueryOnly = OnQueryOnly.ROLLBACK;
|
||||
} else {
|
||||
this.autoPersistUpdates = explicit && manager.isAutoPersistUpdates();
|
||||
this.logSql = manager.isLogSql();
|
||||
this.logSummary = manager.isLogSummary();
|
||||
this.skipCacheAfterWrite = manager.isSkipCacheAfterWrite();
|
||||
this.batchMode = manager.getPersistBatch();
|
||||
this.batchOnCascadeMode = manager.getPersistBatchOnCascade();
|
||||
this.batchMode = manager.isPersistBatch();
|
||||
this.batchOnCascadeMode = manager.isPersistBatchOnCascade();
|
||||
this.onQueryOnly = manager.getOnQueryOnly();
|
||||
}
|
||||
|
||||
@@ -294,6 +297,17 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
|
||||
this.autoPersistUpdates = autoPersistUpdates;
|
||||
this.batchMode = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoPersistUpdates() {
|
||||
return autoPersistUpdates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCacheExplicit() {
|
||||
return (skipCache != null && !skipCache);
|
||||
@@ -774,6 +788,10 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
* Flush the JDBC batch and execute derived relationship statements if necessary.
|
||||
*/
|
||||
private void internalBatchFlush() {
|
||||
if (autoPersistUpdates) {
|
||||
// Experimental - flush dirty beans held by the persistence context
|
||||
manager.flushTransparent(persistenceContext, this);
|
||||
}
|
||||
batchFlush();
|
||||
if (deferredList != null) {
|
||||
for (PersistDeferredRelationship deferred : deferredList) {
|
||||
@@ -1042,7 +1060,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
throw new IllegalStateException(illegalStateMessage);
|
||||
}
|
||||
try {
|
||||
if (queryOnly) {
|
||||
if (queryOnly && !autoPersistUpdates) {
|
||||
connectionEndForQueryOnly();
|
||||
} else {
|
||||
flushCommitAndNotify();
|
||||
|
||||
@@ -25,6 +25,16 @@ class NoTransaction implements SpiTransaction {
|
||||
|
||||
static final NoTransaction INSTANCE = new NoTransaction();
|
||||
|
||||
@Override
|
||||
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoPersistUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
// do nothing
|
||||
|
||||
+24
-3
@@ -5,6 +5,7 @@ import io.ebean.ProfileLocation;
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.annotation.TxType;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.cache.ServerCacheNotification;
|
||||
import io.ebean.cache.ServerCacheNotify;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
@@ -17,6 +18,7 @@ import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.metric.MetricFactory;
|
||||
import io.ebean.metric.TimedMetric;
|
||||
import io.ebean.metric.TimedMetricMap;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebeaninternal.api.ScopeTrans;
|
||||
import io.ebeaninternal.api.ScopedTransaction;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
@@ -59,6 +61,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
|
||||
private static final Logger clusterLogger = LoggerFactory.getLogger("io.ebean.Cluster");
|
||||
|
||||
private final SpiServer server;
|
||||
|
||||
private final BeanDescriptorManager beanDescriptorManager;
|
||||
|
||||
/**
|
||||
@@ -100,6 +104,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
*/
|
||||
final DocStoreUpdateProcessor docStoreUpdateProcessor;
|
||||
|
||||
private final boolean autoPersistUpdates;
|
||||
|
||||
private final boolean persistBatch;
|
||||
|
||||
private final boolean persistBatchOnCascade;
|
||||
@@ -153,7 +159,7 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
* Create the TransactionManager
|
||||
*/
|
||||
public TransactionManager(TransactionManagerOptions options) {
|
||||
|
||||
this.server = options.server;
|
||||
this.logManager = options.logManager;
|
||||
this.txnLogger = logManager.txn();
|
||||
this.txnDebug = txnLogger.isDebug();
|
||||
@@ -161,6 +167,7 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
this.supportsSavepointId = databasePlatform.isSupportsSavepointId();
|
||||
this.skipCacheAfterWrite = options.config.isSkipCacheAfterWrite();
|
||||
this.notifyL2CacheInForeground = options.notifyL2CacheInForeground;
|
||||
this.autoPersistUpdates = options.config.isAutoPersistUpdates();
|
||||
this.persistBatch = PersistBatch.ALL == options.config.getPersistBatch();
|
||||
this.persistBatchOnCascade = PersistBatch.ALL == options.config.appliedPersistBatchOnCascade();
|
||||
this.rollbackOnChecked = options.config.isTransactionRollbackOnChecked();
|
||||
@@ -279,11 +286,15 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
return bulkEventListenerMap;
|
||||
}
|
||||
|
||||
boolean getPersistBatch() {
|
||||
boolean isAutoPersistUpdates() {
|
||||
return autoPersistUpdates;
|
||||
}
|
||||
|
||||
boolean isPersistBatch() {
|
||||
return persistBatch;
|
||||
}
|
||||
|
||||
public boolean getPersistBatchOnCascade() {
|
||||
boolean isPersistBatchOnCascade() {
|
||||
return persistBatchOnCascade;
|
||||
}
|
||||
|
||||
@@ -786,4 +797,14 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
public boolean isLogSummary() {
|
||||
return logManager.sum().isDebug();
|
||||
}
|
||||
|
||||
/**
|
||||
* Experimental - find dirty beans in the persistence context and persist them.
|
||||
*/
|
||||
public void flushTransparent(PersistenceContext persistenceContext, SpiTransaction transaction) {
|
||||
List<Object> dirtyBeans = persistenceContext.dirtyBeans();
|
||||
if (!dirtyBeans.isEmpty()) {
|
||||
server.updateAll(dirtyBeans, transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.transaction;
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.cache.ServerCacheNotify;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
@@ -15,6 +16,7 @@ import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
*/
|
||||
public class TransactionManagerOptions {
|
||||
|
||||
final SpiServer server;
|
||||
final boolean notifyL2CacheInForeground;
|
||||
final DatabaseConfig config;
|
||||
final ClusterManager clusterManager;
|
||||
@@ -31,11 +33,11 @@ public class TransactionManagerOptions {
|
||||
final ClockService clockService;
|
||||
|
||||
|
||||
public TransactionManagerOptions(boolean notifyL2CacheInForeground, DatabaseConfig config, TransactionScopeManager scopeManager, ClusterManager clusterManager,
|
||||
public TransactionManagerOptions(SpiServer server, boolean notifyL2CacheInForeground, DatabaseConfig config, TransactionScopeManager scopeManager, ClusterManager clusterManager,
|
||||
BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor docStoreUpdateProcessor,
|
||||
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler,
|
||||
SpiLogManager logManager, TableModState tableModState, ServerCacheNotify cacheNotify, ClockService clockService) {
|
||||
|
||||
this.server = server;
|
||||
this.notifyL2CacheInForeground = notifyL2CacheInForeground;
|
||||
this.config = config;
|
||||
this.scopeManager = scopeManager;
|
||||
|
||||
@@ -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);
|
||||
@@ -137,6 +149,7 @@ public class ServerConfigTest {
|
||||
ServerConfig serverConfig = new ServerConfig();
|
||||
assertTrue(serverConfig.isIdGeneratorAutomatic());
|
||||
assertTrue(serverConfig.isDefaultServer());
|
||||
assertFalse(serverConfig.isAutoPersistUpdates());
|
||||
|
||||
serverConfig.setIdGeneratorAutomatic(false);
|
||||
assertFalse(serverConfig.isIdGeneratorAutomatic());
|
||||
@@ -144,10 +157,18 @@ 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());
|
||||
serverConfig.setAutoPersistUpdates(true);
|
||||
assertTrue(serverConfig.isAutoPersistUpdates());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -11,10 +11,10 @@ public class DtoQueryRequestTest {
|
||||
public void testParse() {
|
||||
|
||||
assertEquals("foo", DtoQueryRequest.parseColumn("foo"));
|
||||
assertEquals("bar", DtoQueryRequest.parseColumn("_e_t0_bar"));
|
||||
assertEquals("BAR", DtoQueryRequest.parseColumn("_E_T0_BAR"));
|
||||
assertEquals("baz", DtoQueryRequest.parseColumn("_e_t42_baz"));
|
||||
assertEquals("BAZ", DtoQueryRequest.parseColumn("_E_T42_BAZ"));
|
||||
assertEquals("bar", DtoQueryRequest.parseColumn("zx__t0_bar"));
|
||||
assertEquals("BAR", DtoQueryRequest.parseColumn("ZX__T0_BAR"));
|
||||
assertEquals("baz", DtoQueryRequest.parseColumn("zx__t42_baz"));
|
||||
assertEquals("BAZ", DtoQueryRequest.parseColumn("ZX__T42_BAZ"));
|
||||
|
||||
assertEquals("e_t42_nope", DtoQueryRequest.parseColumn("e_t42_nope"));
|
||||
assertEquals("_f_t42_nope", DtoQueryRequest.parseColumn("_f_t42_nope"));
|
||||
|
||||
@@ -4,21 +4,22 @@ import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.plugin.Property;
|
||||
import io.ebeaninternal.server.core.CacheOptions;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployIdentityMode;
|
||||
import io.ebeanservice.docstore.api.DocStoreBeanAdapter;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Animal;
|
||||
import org.tests.model.basic.AnimalShelter;
|
||||
import org.tests.model.basic.Cat;
|
||||
import org.tests.model.basic.Contact;
|
||||
import org.tests.model.basic.Country;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.Dog;
|
||||
import org.tests.model.basic.Order;
|
||||
import org.tests.model.basic.*;
|
||||
import org.tests.model.bridge.BSite;
|
||||
import org.tests.model.bridge.BUser;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BeanDescriptorTest extends BaseTestCase {
|
||||
|
||||
@@ -88,6 +89,42 @@ public class BeanDescriptorTest extends BaseTestCase {
|
||||
assertThat(props).extracting("name").contains("id", "status", "orderDate", "shipDate");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchBaseTable() {
|
||||
BeanDescriptor<Customer> desc = getBeanDescriptor(Customer.class);
|
||||
assertTrue(desc.matchBaseTable("o_customer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchBaseTable_whenTableHasSchema_expect_matchRegardlessOfSchema() {
|
||||
|
||||
DeployBeanDescriptor<Customer> deploy = mockDeployCustomer();
|
||||
|
||||
when(deploy.getBaseTable()).thenReturn("foo.o_customer");
|
||||
BeanDescriptor<?> desc1 = new BeanDescriptor<>(mockOwner(), deploy);
|
||||
assertTrue(desc1.matchBaseTable("o_customer"));
|
||||
|
||||
when(deploy.getBaseTable()).thenReturn("bar.o_customer");
|
||||
BeanDescriptor<?> desc2 = new BeanDescriptor<>(mockOwner(), deploy);
|
||||
assertTrue(desc2.matchBaseTable("o_customer"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private DeployBeanDescriptor<Customer> mockDeployCustomer() {
|
||||
DeployBeanDescriptor<Customer> deploy = mock(DeployBeanDescriptor.class);
|
||||
when(deploy.getBeanType()).thenReturn(Customer.class);
|
||||
when(deploy.getIdentityMode()).thenReturn(DeployIdentityMode.auto());
|
||||
when(deploy.buildIdentityMode()).thenReturn(IdentityMode.NONE);
|
||||
when(deploy.getCacheOptions()).thenReturn(CacheOptions.NO_CACHING);
|
||||
return deploy;
|
||||
}
|
||||
|
||||
private BeanDescriptorMap mockOwner() {
|
||||
BeanDescriptorMap owner = mock(BeanDescriptorMap.class);
|
||||
when(owner.createDocStoreBeanAdapter(any(), any())).thenReturn(mock(DocStoreBeanAdapter.class));
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void merge_when_empty() {
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -25,6 +25,10 @@ public class PersonCacheEmail {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public PersonCacheEmail(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.tests.cache.personinfo;
|
||||
|
||||
import io.ebean.annotation.WhenCreated;
|
||||
import io.ebean.annotation.WhenModified;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.time.Instant;
|
||||
|
||||
@Entity
|
||||
public class PersonOther {
|
||||
|
||||
@Id
|
||||
@Size(max=128)
|
||||
private String id;
|
||||
|
||||
private String email;
|
||||
|
||||
@WhenCreated
|
||||
private Instant whenCreated;
|
||||
|
||||
@WhenModified
|
||||
private Instant whenModified;
|
||||
|
||||
@Version
|
||||
private long version;
|
||||
|
||||
public PersonOther(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Instant getWhenCreated() {
|
||||
return whenCreated;
|
||||
}
|
||||
|
||||
public void setWhenCreated(Instant whenCreated) {
|
||||
this.whenCreated = whenCreated;
|
||||
}
|
||||
|
||||
public Instant getWhenModified() {
|
||||
return whenModified;
|
||||
}
|
||||
|
||||
public void setWhenModified(Instant whenModified) {
|
||||
this.whenModified = whenModified;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.tests.cache.personinfo;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestStringIdOnly extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void insert() {
|
||||
|
||||
PersonCacheEmail b0 = new PersonCacheEmail("IdOnly");
|
||||
DB.save(b0);
|
||||
|
||||
PersonCacheEmail found = DB.find(PersonCacheEmail.class, b0.getId());
|
||||
assertThat(found).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insert_whenIdOnly() {
|
||||
|
||||
PersonOther b0 = new PersonOther("IdOnly");
|
||||
DB.save(b0);
|
||||
|
||||
PersonOther found = DB.find(PersonOther.class, b0.getId());
|
||||
assertThat(found).isNotNull();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
package org.tests.model.embedded;
|
||||
|
||||
import io.ebean.annotation.NotNull;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import java.util.Date;
|
||||
|
||||
@Embeddable
|
||||
public class EEmbDatePeriod {
|
||||
|
||||
@NotNull
|
||||
Date date1;
|
||||
@NotNull
|
||||
Date date2;
|
||||
|
||||
public Date getDate1() {
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -31,7 +31,7 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
runQueries();
|
||||
|
||||
StringBuilder buffer0 = new StringBuilder();
|
||||
server().getMetaInfoManager()
|
||||
DB.getDefault().getMetaInfoManager()
|
||||
.collectMetricsAsJson()
|
||||
.withHeader(false)
|
||||
.write(buffer0);
|
||||
@@ -45,7 +45,7 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
runQueries();
|
||||
|
||||
StringBuilder buffer1 = new StringBuilder();
|
||||
server().getMetaInfoManager()
|
||||
DB.getDefault().getMetaInfoManager()
|
||||
.collectMetricsAsJson()
|
||||
.withHeader(false)
|
||||
.write(buffer1);
|
||||
@@ -55,7 +55,6 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
assertThat(json1).contains("\"name\":\"txn.main\"");
|
||||
assertThat(json1).contains("\"name\":\"orm.Customer.findList\"");
|
||||
assertThat(json1).doesNotContain("\"sql\":\"select t0.id, t0.status, t0.name");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -85,7 +84,6 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
assertThat(customer.getName()).isEqualTo(customer1.getName());
|
||||
|
||||
assertThat(Customer.find.db().getName()).isEqualTo(DB.getDefault().getName());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -175,20 +173,27 @@ 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(2);
|
||||
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);
|
||||
init.setThresholdMicros(1);
|
||||
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();
|
||||
ServerMetrics metrics = DB.getDefault().getMetaInfoManager().collectMetrics();
|
||||
|
||||
List<MetaQueryMetric> planStats = metrics.getQueryMetrics();
|
||||
assertThat(planStats.size()).isGreaterThan(4);
|
||||
@@ -203,12 +208,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
|
||||
|
||||
@@ -1,38 +1,42 @@
|
||||
package org.tests.rawsql;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.RawSql;
|
||||
import io.ebean.RawSqlBuilder;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestRawSqlPositionedParams extends BaseTestCase {
|
||||
|
||||
private static final RawSql RAWSQL_1 = RawSqlBuilder
|
||||
.parse("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
|
||||
.create();
|
||||
|
||||
private static final RawSql RAW_SQL_2 = RawSqlBuilder
|
||||
.unparsed("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
|
||||
.columnMapping("r.id", "id")
|
||||
.columnMapping("r.name", "name")
|
||||
.create();
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
RawSql rawSql = RawSqlBuilder
|
||||
.parse("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
|
||||
.create();
|
||||
List<Customer> list = DB.find(Customer.class)
|
||||
.setRawSql(RAWSQL_1)
|
||||
.setParameter(1)
|
||||
.setParameter("R%")
|
||||
.where().lt("id", 2001)
|
||||
.findList();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class);
|
||||
query.setRawSql(rawSql);
|
||||
query.setParameter(1, 1);
|
||||
query.setParameter(2, "R%");
|
||||
query.where().lt("id", 2001);
|
||||
|
||||
List<Customer> list = query.findList();
|
||||
|
||||
assertNotNull(list);
|
||||
assertThat(list).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -40,18 +44,12 @@ public class TestRawSqlPositionedParams extends BaseTestCase {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
RawSql rawSql = RawSqlBuilder
|
||||
.unparsed("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
|
||||
.columnMapping("r.id", "id")
|
||||
.columnMapping("r.name", "name").create();
|
||||
List<Customer> list = DB.find(Customer.class)
|
||||
.setRawSql(RAW_SQL_2)
|
||||
.setParameter(1)
|
||||
.setParameter("R%")
|
||||
.findList();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class);
|
||||
query.setRawSql(rawSql);
|
||||
query.setParameter(1, 1);
|
||||
query.setParameter(2, "R%");
|
||||
|
||||
List<Customer> list = query.findList();
|
||||
|
||||
assertNotNull(list);
|
||||
assertThat(list).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
package org.tests.rawsql;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.RawSql;
|
||||
import io.ebean.RawSqlBuilder;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestRawSqlUnparsedQuery extends BaseTestCase {
|
||||
|
||||
private static final RawSql rawSql = RawSqlBuilder
|
||||
.unparsed("select r.id, r.name from o_customer r where r.id >= :a and r.name like :b")
|
||||
.columnMapping("r.id", "id")
|
||||
.columnMapping("r.name", "name")
|
||||
.create();
|
||||
|
||||
@Test
|
||||
public void testDoubleUnparsedQuery() {
|
||||
|
||||
@@ -25,17 +31,14 @@ public class TestRawSqlUnparsedQuery extends BaseTestCase {
|
||||
}
|
||||
|
||||
private static void test() {
|
||||
RawSql rawSql = RawSqlBuilder
|
||||
.unparsed("select r.id, r.name from o_customer r where r.id >= :a and r.name like :b")
|
||||
.columnMapping("r.id", "id").columnMapping("r.name", "name").create();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class);
|
||||
query.setRawSql(rawSql);
|
||||
query.setParameter("a", 1);
|
||||
query.setParameter("b", "R%");
|
||||
List<Customer> list = DB.find(Customer.class)
|
||||
.setRawSql(rawSql)
|
||||
.setParameter("a", 1)
|
||||
.setParameter("b", "R%")
|
||||
.findList();
|
||||
|
||||
List<Customer> list = query.findList();
|
||||
Assert.assertNotNull(list);
|
||||
assertThat(list).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
package org.tests.transparentpersist;
|
||||
|
||||
import io.ebean.*;
|
||||
import io.ebean.annotation.*;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeantest.LoggedSql;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.EBasicVer;
|
||||
import org.tests.model.basic.Order;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestTransparentPersist extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void insertFlush_mutateFlush_expect_update() {
|
||||
|
||||
LoggedSql.start();
|
||||
|
||||
EBasicVer newBean;
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
|
||||
|
||||
newBean = new EBasicVer("insertMe");
|
||||
DB.save(newBean);
|
||||
|
||||
// flush - new bean needs to get registered into persistence context
|
||||
transaction.flush();
|
||||
|
||||
// make it dirty, we expect it to execute an update on flush()
|
||||
newBean.setName("make it dirty - auto save");
|
||||
|
||||
// flush again, auto persist dirty bean in persistence context
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSql.stop();
|
||||
|
||||
EBasicVer found = DB.find(EBasicVer.class, newBean.getId());
|
||||
assertThat(found.getName()).isEqualTo("make it dirty - auto save");
|
||||
|
||||
assertThat(sql).hasSize(4);
|
||||
assertThat(sql.get(0)).contains("insert into e_basicver");
|
||||
assertThat(sql.get(1)).contains(" -- bind(");
|
||||
assertThat(sql.get(2)).contains("update e_basicver set name=?, last_update=? where id=? and last_update=?");
|
||||
assertThat(sql.get(3)).contains(" -- bind(");
|
||||
|
||||
DB.delete(found);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete_expect_beanRemovedFromPersistenceContext() {
|
||||
|
||||
EBasicVer b0 = new EBasicVer("simpleDelete");
|
||||
DB.save(b0);
|
||||
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
|
||||
|
||||
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
|
||||
// make it dirty
|
||||
found.setName("make it dirty");
|
||||
|
||||
// delete it, should remove it from the "live" part of persistence context
|
||||
// with the expectation that no update is executed (no dirty in PC update)
|
||||
DB.delete(found);
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
|
||||
assertThat(after).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleInsertUpdateDelete_experimental() {
|
||||
|
||||
EBasicVer b0 = new EBasicVer("simpleIUD_0");
|
||||
b0.save();
|
||||
EBasicVer b1 = new EBasicVer("simpleIUD_1");
|
||||
b1.save();
|
||||
EBasicVer b2 = new EBasicVer("simpleIUD_2");
|
||||
b2.save();
|
||||
|
||||
EBasicVer newBean;
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
|
||||
|
||||
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
|
||||
found.setName("auto dirty");
|
||||
|
||||
// delete by id
|
||||
DB.delete(EBasicVer.class, b1.getId());
|
||||
// find and delete, note the delete is batched up to execute later
|
||||
DB.delete(DB.find(EBasicVer.class, b2.getId()));
|
||||
|
||||
// insert is batched up to execute later
|
||||
newBean = new EBasicVer("simpleIUD_New1");
|
||||
DB.save(newBean);
|
||||
// can still mutate newBean before flush (but not after flush yet as new bean isn't put into Persistence context)
|
||||
newBean.setName("simpleIUD_New2");
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
|
||||
assertThat(after.getName()).isEqualTo("auto dirty");
|
||||
|
||||
EBasicVer wasInserted = DB.find(EBasicVer.class, newBean.getId());
|
||||
assertThat(wasInserted.getName()).isEqualTo("simpleIUD_New2");
|
||||
|
||||
assertThat(DB.find(EBasicVer.class, b1.getId())).isNull();
|
||||
assertThat(DB.find(EBasicVer.class, b2.getId())).isNull();
|
||||
|
||||
DB.delete(after);
|
||||
DB.delete(wasInserted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleUpdate_experimental() {
|
||||
|
||||
EBasicVer transPersist = new EBasicVer("simulate_simpleUpdate");
|
||||
transPersist.save();
|
||||
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
|
||||
|
||||
EBasicVer found = DB.find(EBasicVer.class, transPersist.getId());
|
||||
found.setName("Persisted automatically as dirty");
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
EBasicVer after = DB.find(EBasicVer.class,transPersist.getId());
|
||||
assertThat(after.getName()).isEqualTo("Persisted automatically as dirty");
|
||||
DB.delete(after);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWithPersistCascadeInsert() {
|
||||
|
||||
// setup data
|
||||
Customer c0 = new Customer();
|
||||
c0.setName("firstCust");
|
||||
Order order = new Order();
|
||||
order.setStatus(Order.Status.NEW);
|
||||
order.setCustomer(c0);
|
||||
DB.save(order);
|
||||
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
|
||||
|
||||
Order foundOrder = DB.find(Order.class, order.getId());
|
||||
foundOrder.setStatus(Order.Status.APPROVED);
|
||||
// cascade persist will insert this customer (even though it isn't in the persistence context)
|
||||
Customer c1 = new Customer();
|
||||
c1.setName("newCust CascadePersist");
|
||||
foundOrder.setCustomer(c1);
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
Order checkOrder = DB.find(Order.class, order.getId());
|
||||
|
||||
assertThat(checkOrder.getStatus()).isEqualTo(Order.Status.APPROVED);
|
||||
assertThat(checkOrder.getCustomer().getName()).isEqualTo("newCust CascadePersist");
|
||||
|
||||
DB.delete(checkOrder);
|
||||
DB.delete(Customer.class, checkOrder.getCustomer().getId());
|
||||
DB.delete(Customer.class, c0.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateReferenceOnlyWithPersistCascade_Insert_andUpdateForeignKey() {
|
||||
|
||||
// setup data
|
||||
Customer c0 = new Customer();
|
||||
c0.setName("firstCust");
|
||||
Order order = new Order();
|
||||
order.setStatus(Order.Status.NEW);
|
||||
order.setCustomer(c0);
|
||||
DB.save(order);
|
||||
|
||||
LoggedSql.start();
|
||||
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
|
||||
|
||||
Order foundOrder = DB.find(Order.class, order.getId());
|
||||
// we ONLY mutate the foreign key
|
||||
// cascade persist will insert this customer (even though it isn't in the persistence context)
|
||||
Customer c1 = new Customer();
|
||||
c1.setName("newCust CascadePersist");
|
||||
foundOrder.setCustomer(c1);
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSql.stop();
|
||||
|
||||
Order checkOrder = DB.find(Order.class, order.getId());
|
||||
|
||||
assertThat(checkOrder.getStatus()).isEqualTo(Order.Status.NEW);
|
||||
assertThat(checkOrder.getCustomer().getName()).isEqualTo("newCust CascadePersist");
|
||||
|
||||
assertThat(sql).hasSize(5);
|
||||
assertThat(sql.get(0)).contains("select t0.id, t0.status, t0.order_date");
|
||||
assertThat(sql.get(1)).contains("insert into o_customer");
|
||||
assertThat(sql.get(2)).contains(" -- bind(");
|
||||
assertThat(sql.get(3)).contains("update o_order set updtime=?, kcustomer_id=? where id=? and updtime=?");
|
||||
assertThat(sql.get(4)).contains(" -- bind(");
|
||||
|
||||
DB.delete(checkOrder);
|
||||
DB.delete(Customer.class, checkOrder.getCustomer().getId());
|
||||
DB.delete(Customer.class, c0.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void txScope_setAutoPersistUpdates() {
|
||||
|
||||
EBasicVer b0 = new EBasicVer("txScopeUpdate");
|
||||
b0.save();
|
||||
|
||||
DB.execute(TxScope.required().setAutoPersistUpdates(TxOption.ON), () -> {
|
||||
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
|
||||
found.setName("mutable txScope bean");
|
||||
});
|
||||
|
||||
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
|
||||
assertThat(after.getName()).isEqualTo("mutable txScope bean");
|
||||
|
||||
DB.delete(after);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void txn_setAutoPersistUpdates() {
|
||||
|
||||
EBasicVer b0 = new EBasicVer("txn autoPersistUpdates ON");
|
||||
b0.save();
|
||||
|
||||
performUpdate(b0);
|
||||
|
||||
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
|
||||
assertThat(after.getName()).isEqualTo("mutated with autoPersistUpdates ON");
|
||||
|
||||
DB.delete(after);
|
||||
}
|
||||
|
||||
@Transactional(autoPersistUpdates = TxOption.ON)
|
||||
private void performUpdate(EBasicVer b0) {
|
||||
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
|
||||
found.setName("mutated with autoPersistUpdates ON");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simulate_transparentPersistence_forSimpleUpdate() {
|
||||
|
||||
EBasicVer transPersist = new EBasicVer("simulate_simpleUpdate");
|
||||
transPersist.save();
|
||||
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
transaction.setBatchMode(true);
|
||||
transaction.setBatchSize(10);
|
||||
|
||||
EBasicVer found = DB.find(EBasicVer.class, transPersist.getId());
|
||||
found.setName("Changed");
|
||||
|
||||
// simulate transparent persistence
|
||||
List<Object> dirtyBeans = simulateTransparentPersist(transaction);
|
||||
assertThat(dirtyBeans).hasSize(1);
|
||||
assertThat(dirtyBeans).contains(found);
|
||||
|
||||
// would occur as first part of flush
|
||||
transaction.flush();
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
EBasicVer after = DB.find(EBasicVer.class,transPersist.getId());
|
||||
assertThat(after.getName()).isEqualTo("Changed");
|
||||
DB.delete(after);
|
||||
}
|
||||
|
||||
private List<Object> simulateTransparentPersist(Transaction transaction) {
|
||||
Database db = DB.getDefault();
|
||||
List<Object> dirtyBeans = getDirtyBeansFromPersistenceContext(transaction);
|
||||
for (Object dirtyBean : dirtyBeans) {
|
||||
db.update(dirtyBean, transaction);
|
||||
}
|
||||
return dirtyBeans;
|
||||
}
|
||||
|
||||
private List<Object> getDirtyBeansFromPersistenceContext(Transaction transaction) {
|
||||
return ((SpiTransaction)transaction).getPersistenceContext().dirtyBeans();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,13 +4,12 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</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.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</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.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
+1
@@ -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";
|
||||
|
||||
+4
-1
@@ -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();
|
||||
|
||||
+1
@@ -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 ";
|
||||
|
||||
+13
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -175,7 +175,8 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
if (p instanceof BeanPropertyAssocOne) {
|
||||
visitOneImported((BeanPropertyAssocOne<?>)p);
|
||||
} else {
|
||||
visitScalar(p);
|
||||
// only allow Nonnull if embedded is Nonnull
|
||||
visitScalar(p, !embedded.isNullable());
|
||||
}
|
||||
if (embedded.isId()) {
|
||||
// compound primary key
|
||||
@@ -259,8 +260,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
|
||||
public void visitScalar(BeanProperty p, boolean allowNonNull) {
|
||||
if (p.isSecondaryTable()) {
|
||||
lastColumn = null;
|
||||
return;
|
||||
@@ -292,7 +292,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
}
|
||||
} else {
|
||||
col.setDefaultValue(p.getDbColumnDefault());
|
||||
if (!p.isNullable() || p.isDDLNotNull()) {
|
||||
if (allowNonNull && (!p.isNullable() || p.isDDLNotNull())) {
|
||||
col.setNotnull(true);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+20
@@ -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);
|
||||
|
||||
+98
@@ -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;
|
||||
|
||||
+4
-4
@@ -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);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean external mapping api</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</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.3</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.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -59,14 +59,14 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</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.3</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.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</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.3</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.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.8.0</version>
|
||||
<version>12.8.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -24,4 +24,70 @@ public class PInteger<R> extends PBaseNumber<R,Integer> {
|
||||
super(name, root, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Flags Bulk + Size = Size
|
||||
* // ... meaning Bulk is not set and Size is set
|
||||
*
|
||||
* int selectedFlags = BwFlags.HAS_BULK + BwFlags.HAS_SIZE;
|
||||
* int mask = BwFlags.HAS_SIZE; // Only Size flag set
|
||||
*
|
||||
* bitwiseAnd(selectedFlags, mask)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseAnd(int flags, int mask) {
|
||||
expr().bitwiseAnd(_name, flags, mask);
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add expression for ALL of the given bit flags to be set.
|
||||
* <pre>{@code
|
||||
*
|
||||
* bitwiseAll(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseAll(int flags) {
|
||||
expr().bitwiseAll(_name, flags);
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add expression for ANY of the given bit flags to be set.
|
||||
* <pre>{@code
|
||||
*
|
||||
* bitwiseAny(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseAny(int flags) {
|
||||
expr().bitwiseAny(_name, flags);
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add expression for the given bit flags to be NOT set.
|
||||
* <pre>{@code
|
||||
*
|
||||
* bitwiseNot(BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseNot(int flags) {
|
||||
expr().bitwiseNot(_name, flags);
|
||||
return _root;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package io.ebean.typequery;
|
||||
*
|
||||
* @param <R> the root query bean type
|
||||
*/
|
||||
public class PLong<R> extends PBaseNumber<R,Long> {
|
||||
public class PLong<R> extends PBaseNumber<R, Long> {
|
||||
|
||||
/**
|
||||
* Construct with a property name and root instance.
|
||||
@@ -14,7 +14,7 @@ public class PLong<R> extends PBaseNumber<R,Long> {
|
||||
* @param root the root query bean instance
|
||||
*/
|
||||
public PLong(String name, R root) {
|
||||
super(name , root);
|
||||
super(name, root);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,4 +24,70 @@ public class PLong<R> extends PBaseNumber<R,Long> {
|
||||
super(name, root, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Flags Bulk + Size = Size
|
||||
* // ... meaning Bulk is not set and Size is set
|
||||
*
|
||||
* long selectedFlags = BwFlags.HAS_BULK + BwFlags.HAS_SIZE;
|
||||
* long mask = BwFlags.HAS_SIZE; // Only Size flag set
|
||||
*
|
||||
* bitwiseAnd(selectedFlags, mask)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseAnd(long flags, long mask) {
|
||||
expr().bitwiseAnd(_name, flags, mask);
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add expression for ALL of the given bit flags to be set.
|
||||
* <pre>{@code
|
||||
*
|
||||
* bitwiseAll(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseAll(long flags) {
|
||||
expr().bitwiseAll(_name, flags);
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add expression for ANY of the given bit flags to be set.
|
||||
* <pre>{@code
|
||||
*
|
||||
* bitwiseAny(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseAny(long flags) {
|
||||
expr().bitwiseAny(_name, flags);
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add expression for the given bit flags to be NOT set.
|
||||
* <pre>{@code
|
||||
*
|
||||
* bitwiseNot(BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseNot(long flags) {
|
||||
expr().bitwiseNot(_name, flags);
|
||||
return _root;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package io.ebean.typequery;
|
||||
*
|
||||
* @param <R> the root query bean type
|
||||
*/
|
||||
public class PShort<R> extends PBaseNumber<R,Short> {
|
||||
public class PShort<R> extends PBaseNumber<R, Short> {
|
||||
|
||||
/**
|
||||
* Construct with a property name and root instance.
|
||||
@@ -24,4 +24,70 @@ public class PShort<R> extends PBaseNumber<R,Short> {
|
||||
super(name, root, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Flags Bulk + Size = Size
|
||||
* // ... meaning Bulk is not set and Size is set
|
||||
*
|
||||
* short selectedFlags = BwFlags.HAS_BULK + BwFlags.HAS_SIZE;
|
||||
* short mask = BwFlags.HAS_SIZE; // Only Size flag set
|
||||
*
|
||||
* bitwiseAnd(selectedFlags, mask)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseAnd(short flags, short mask) {
|
||||
expr().bitwiseAnd(_name, flags, mask);
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add expression for ALL of the given bit flags to be set.
|
||||
* <pre>{@code
|
||||
*
|
||||
* bitwiseAll(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseAll(short flags) {
|
||||
expr().bitwiseAll(_name, flags);
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add expression for ANY of the given bit flags to be set.
|
||||
* <pre>{@code
|
||||
*
|
||||
* bitwiseAny(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseAny(short flags) {
|
||||
expr().bitwiseAny(_name, flags);
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add expression for the given bit flags to be NOT set.
|
||||
* <pre>{@code
|
||||
*
|
||||
* bitwiseNot(BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
public R bitwiseNot(short flags) {
|
||||
expr().bitwiseNot(_name, flags);
|
||||
return _root;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package io.ebean.typequery;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* ZonedDateTime property.
|
||||
*
|
||||
* @param <R> the root query bean type
|
||||
*/
|
||||
public class PZonedDateTime<R> extends PBaseCompareable<R, ZonedDateTime> {
|
||||
|
||||
/**
|
||||
* Construct with a property name and root instance.
|
||||
*
|
||||
* @param name property name
|
||||
* @param root the root query bean instance
|
||||
*/
|
||||
public PZonedDateTime(String name, R root) {
|
||||
super(name, root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with additional path prefix.
|
||||
*/
|
||||
public PZonedDateTime(String name, R root, String prefix) {
|
||||
super(name, root, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as greater than.
|
||||
*
|
||||
* @param value the equal to bind value
|
||||
* @return the root query bean instance
|
||||
*/
|
||||
public R after(ZonedDateTime value) {
|
||||
expr().gt(_name, value);
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as less than.
|
||||
*
|
||||
* @param value the equal to bind value
|
||||
* @return the root query bean instance
|
||||
*/
|
||||
public R before(ZonedDateTime value) {
|
||||
expr().lt(_name, value);
|
||||
return _root;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -679,7 +679,7 @@ public abstract class TQRootBean<T, R> {
|
||||
* Provides us with the ability to explicitly use Postgres
|
||||
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
|
||||
*/
|
||||
R withLock(Query.LockType lockType) {
|
||||
public R withLock(Query.LockType lockType) {
|
||||
query.withLock(lockType);
|
||||
return root;
|
||||
}
|
||||
@@ -693,7 +693,7 @@ public abstract class TQRootBean<T, R> {
|
||||
* Provides us with the ability to explicitly use Postgres
|
||||
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
|
||||
*/
|
||||
R withLock(Query.LockType lockType, Query.LockWait lockWait) {
|
||||
public R withLock(Query.LockType lockType, Query.LockWait lockWait) {
|
||||
query.withLock(lockType, lockWait);
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.example.domain;
|
||||
import io.ebean.annotation.DbArray;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -11,33 +12,35 @@ import java.util.Map;
|
||||
* Contact entity bean.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name="be_contact")
|
||||
@Table(name = "be_contact")
|
||||
public class Contact extends BaseModel {
|
||||
|
||||
@DbArray
|
||||
List<String> phoneNumbers = new ArrayList();
|
||||
List<String> phoneNumbers = new ArrayList<>();
|
||||
|
||||
@Column(length=50)
|
||||
@Column(length = 50)
|
||||
String firstName;
|
||||
|
||||
@Column(length=50)
|
||||
@Column(length = 50)
|
||||
String lastName;
|
||||
|
||||
@Column(length=200)
|
||||
@Column(length = 200)
|
||||
String email;
|
||||
|
||||
@Column(length=20)
|
||||
@Column(length = 20)
|
||||
String phone;
|
||||
|
||||
@ManyToOne(optional=false)
|
||||
ZonedDateTime zoneDateTime;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
Customer customer;
|
||||
|
||||
@OneToMany(mappedBy = "contact")
|
||||
List<ContactNote> notes;
|
||||
|
||||
@OneToMany(cascade = CascadeType.PERSIST)
|
||||
@MapKey(name="key")
|
||||
Map<String,ContactOther> others;
|
||||
@MapKey(name = "key")
|
||||
Map<String, ContactOther> others;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
@@ -85,6 +88,14 @@ public class Contact extends BaseModel {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public ZonedDateTime getZoneDateTime() {
|
||||
return zoneDateTime;
|
||||
}
|
||||
|
||||
public void setZoneDateTime(ZonedDateTime zoneDateTime) {
|
||||
this.zoneDateTime = zoneDateTime;
|
||||
}
|
||||
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,15 @@ package org.querytest;
|
||||
import org.example.domain.query.QContact;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
public class QContactTest {
|
||||
|
||||
@Test
|
||||
public void test_oneToManyMap() {
|
||||
|
||||
new QContact()
|
||||
.others.fetch()
|
||||
.zoneDateTime.before(ZonedDateTime.now())
|
||||
.findList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ public class QCustomerTest {
|
||||
// not found using other transaction
|
||||
final Customer foundNot = new QCustomer()
|
||||
.name.eq("explicitTransaction")
|
||||
.withLock(Query.LockType.SHARE)
|
||||
.findOne();
|
||||
assertThat(foundNot).isNull();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user