diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 8575f4580..357f8aefb 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,9 +1,3 @@
-
-GITHUB ISSUES ARE STRICTLY CONTROLLED FOR THIS PROJECT.
-
-Refer to http://ebean-orm.github.io/support for the policies controlling the use of github issues.
-Please post issues to the Ebean group https://groups.google.com/forum/#!forum/ebean first.
-
## Expected behavior
## Actual behavior
diff --git a/.gitignore b/.gitignore
index 1b88dd830..861a33064 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,4 @@
*.autofetch
-*create-all.sql
-*drop-all.sql
*.orig
.classpath
.project
@@ -12,7 +10,6 @@ ebean-autotune.xml
ebean-profiling*.xml
/db
/mydb.db
-!src/test/ddl-review/*.sql
profiling/
# Intellij project files
@@ -20,4 +17,4 @@ profiling/
*.ipr
*.iws
.idea/
-*uuid.state
\ No newline at end of file
+*uuid.state
diff --git a/CONFIGURATION.md b/CONFIGURATION.md
index 16fa6d698..46be98f0b 100644
--- a/CONFIGURATION.md
+++ b/CONFIGURATION.md
@@ -293,6 +293,6 @@ ebean.tenant.schemaProvider
ebean.updateAllPropertiesInBatch
ebean.updateChangesOnly
ebean.updatesDeleteMissingChildren
-ebean.useJavaxValidationNotNull
+ebean.useValidationNotNull
ebean.useJtaTransactionManager
diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml
index 472d240f9..9bb40a671 100644
--- a/ebean-api/pom.xml
+++ b/ebean-api/pom.xml
@@ -4,18 +4,13 @@
ebean-parentio.ebean
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTebean apiebean apiebean-api
-
- 2.11.3
- 2.11.3
-
-
-
- javax.validation
- validation-api
- 1.1.0.Final
- true
-
-
javax.servletjavax.servlet-api
diff --git a/ebean-api/src/main/java/io/ebean/BackgroundExecutor.java b/ebean-api/src/main/java/io/ebean/BackgroundExecutor.java
index 9ffa4b62b..537fb7f36 100644
--- a/ebean-api/src/main/java/io/ebean/BackgroundExecutor.java
+++ b/ebean-api/src/main/java/io/ebean/BackgroundExecutor.java
@@ -1,33 +1,43 @@
package io.ebean;
import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
- * Background thread pool service for executing of tasks asynchronously.
+ * Background executor service for executing of tasks asynchronously.
*
- * This service is used internally by Ebean for executing background tasks such
- * as the {@link Query#findFutureList()} and also for executing background tasks
- * periodically.
- *
+ * This service can be used to execute tasks in the background.
*
- * This service has been made available so you can use it for your application
- * code if you want. It can be useful for some server caching implementations
- * (background population and trimming of the cache etc).
- *
- *
- * @author rbygrave
+ * This service is managed by Ebean and will perform a clean shutdown
+ * waiting for background tasks to complete with a default 30 second
+ * timeout. Shutdown occurs prior to DataSource shutdown.
+ *
+ * This also propagates MDC context from the current thread to the
+ * background task if defined.
*/
public interface BackgroundExecutor {
/**
- * Execute a task in the background.
+ * Execute a callable task in the background returning the Future.
*/
- void execute(Runnable r);
+ Future submit(Callable task);
/**
+ * Execute a runnable task in the background returning the Future.
+ */
+ Future> submit(Runnable task);
+
+ /**
+ * Execute a task in the background. Effectively the same as
+ * {@link BackgroundExecutor#submit(Runnable)} but returns void.
+ */
+ void execute(Runnable task);
+
+ /**
+ * Deprecated - migrate to scheduleWithFixedDelay().
* Execute a task periodically with a fixed delay between each execution.
*
* For example, execute a runnable every minute.
@@ -36,27 +46,64 @@ public interface BackgroundExecutor {
* That is, this method has the same behaviour characteristics as
* {@link ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)}
*/
- void executePeriodically(Runnable r, long delay, TimeUnit unit);
+ @Deprecated
+ void executePeriodically(Runnable task, long delay, TimeUnit unit);
/**
+ * Deprecated - migrate to scheduleWithFixedDelay().
* Execute a task periodically additionally with an initial delay different from delay.
*/
- void executePeriodically(Runnable r, long initialDelay, long delay, TimeUnit unit);
+ @Deprecated
+ void executePeriodically(Runnable task, long initialDelay, long delay, TimeUnit unit);
+
+ /**
+ * Execute a task periodically with a given delay.
+ *
+ * @param task the task to execute
+ * @param initialDelay the time to delay first execution
+ * @param delay the delay between the termination of one
+ * execution and the commencement of the next
+ * @param unit the time unit of the initialDelay and delay parameters
+ * @return a ScheduledFuture representing pending completion of
+ * the series of repeated tasks. The future's {@link
+ * Future#get() get()} method will never return normally,
+ * and will throw an exception upon task cancellation or
+ * abnormal termination of a task execution.
+ */
+ ScheduledFuture> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit);
+
+ /**
+ * Execute a task periodically with a given period.
+ *
+ *
If any execution of this task takes longer than its period, then
+ * subsequent executions may start late, but will not concurrently
+ * execute.
+ *
+ * @param task the task to execute
+ * @param initialDelay the time to delay first execution
+ * @param period the period between successive executions
+ * @param unit the time unit of the initialDelay and period parameters
+ * @return a ScheduledFuture representing pending completion of
+ * the series of repeated tasks. The future's {@link
+ * Future#get() get()} method will never return normally,
+ * and will throw an exception upon task cancellation or
+ * abnormal termination of a task execution.
+ */
+ ScheduledFuture> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit);
/**
* Schedules a Runnable for one-shot action that becomes enabled after the given delay.
*
* @return a ScheduledFuture representing pending completion of the task and
- * whose get() method will return null upon completion
+ * whose get() method will return null upon completion
*/
- ScheduledFuture> schedule(Runnable r, long delay, TimeUnit unit);
+ ScheduledFuture> schedule(Runnable task, long delay, TimeUnit unit);
/**
* Schedules a Callable for one-shot action that becomes enabled after the given delay.
*
* @return a ScheduledFuture that can be used to extract result or cancel
*/
- ScheduledFuture schedule(Callable c, long delay, TimeUnit unit);
-
+ ScheduledFuture schedule(Callable task, long delay, TimeUnit unit);
}
diff --git a/ebean-api/src/main/java/io/ebean/BeanFinder.java b/ebean-api/src/main/java/io/ebean/BeanFinder.java
index 00aec8de5..2c8e04eec 100644
--- a/ebean-api/src/main/java/io/ebean/BeanFinder.java
+++ b/ebean-api/src/main/java/io/ebean/BeanFinder.java
@@ -14,7 +14,7 @@ import java.util.Optional;
*
* public class CustomerFinder extends BeanFinder {
*
- * @Inject
+ * @Inject
* public CustomerFinder(Database database) {
* super(Customer.class, database);
* }
diff --git a/ebean-api/src/main/java/io/ebean/BeanRepository.java b/ebean-api/src/main/java/io/ebean/BeanRepository.java
index 19f390582..b89a49b39 100644
--- a/ebean-api/src/main/java/io/ebean/BeanRepository.java
+++ b/ebean-api/src/main/java/io/ebean/BeanRepository.java
@@ -9,10 +9,10 @@ import java.util.Collection;
*
*
{@code
*
- * @Repository
+ * @Repository
* public class CustomerRepository extends BeanRepository {
*
- * @Inject
+ * @Inject
* public CustomerRepository(Database server) {
* super(Customer.class, server);
* }
diff --git a/ebean-api/src/main/java/io/ebean/BeanState.java b/ebean-api/src/main/java/io/ebean/BeanState.java
index da8c24d81..daf163cc7 100644
--- a/ebean-api/src/main/java/io/ebean/BeanState.java
+++ b/ebean-api/src/main/java/io/ebean/BeanState.java
@@ -118,4 +118,9 @@ public interface BeanState {
*/
@Nullable
Map getLoadErrors();
+
+ /**
+ * Return the sort order value for an order column.
+ */
+ int getSortOrder();
}
diff --git a/ebean-api/src/main/java/io/ebean/CancelableQuery.java b/ebean-api/src/main/java/io/ebean/CancelableQuery.java
new file mode 100644
index 000000000..fb3100bd2
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/CancelableQuery.java
@@ -0,0 +1,19 @@
+package io.ebean;
+
+/**
+ * Defines a cancelable query.
+ *
+ * Typically holds a representation of the PreparedStatement to perform the
+ * actual cancel.
+ *
{@code
* public class Order { ...
*
- * @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
+ * @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* List details;
* ...
* }
diff --git a/ebean-api/src/main/java/io/ebean/DatabaseFactory.java b/ebean-api/src/main/java/io/ebean/DatabaseFactory.java
index 13a0c4125..588f48c24 100644
--- a/ebean-api/src/main/java/io/ebean/DatabaseFactory.java
+++ b/ebean-api/src/main/java/io/ebean/DatabaseFactory.java
@@ -16,23 +16,21 @@ import java.util.concurrent.locks.ReentrantLock;
*
* This uses either DatabaseConfig or properties in the application.properties file to
* configure and create a Database instance.
- *
*
* The Database instance can either be registered with the DB singleton or
* not. The DB singleton effectively holds a map of Database by a name.
* If the Database is registered with the DB singleton you can retrieve it
* later via {@link DB#byName(String)}.
- *
*
* One Database can be nominated as the 'default/primary' Database. Many
* methods on the DB singleton such as {@link DB#find(Class)} are just a
* convenient way of using the 'default/primary' Database.
- *
*/
public class DatabaseFactory {
- private static final ReentrantLock lock = new ReentrantLock(false);
+ private static final ReentrantLock lock = new ReentrantLock();
private static SpiContainer container;
+ private static String defaultServerName;
static {
EbeanVersion.getVersion();
@@ -47,7 +45,7 @@ public class DatabaseFactory {
public static void initialiseContainer(ContainerConfig containerConfig) {
lock.lock();
try {
- getContainer(containerConfig);
+ container(containerConfig);
} finally {
lock.unlock();
}
@@ -59,7 +57,7 @@ public class DatabaseFactory {
public static Database create(String name) {
lock.lock();
try {
- return getContainer(null).createServer(name);
+ return container(null).createServer(name);
} finally {
lock.unlock();
}
@@ -67,6 +65,16 @@ public class DatabaseFactory {
/**
* Create using the DatabaseConfig object to configure the database.
+ *
+ *
*/
public static Database create(DatabaseConfig config) {
lock.lock();
@@ -76,6 +84,12 @@ public class DatabaseFactory {
}
Database server = createInternal(config);
if (config.isRegister()) {
+ if (config.isDefaultServer()) {
+ 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();
+ }
DbPrimary.setSkip(true);
DbContext.getInstance().register(server, config.isDefaultServer());
}
@@ -108,7 +122,6 @@ public class DatabaseFactory {
* Shutdown gracefully all Database instances cleaning up any resources as required.
*
* This is typically invoked via JVM shutdown hook and not explicitly called.
- *
*/
public static void shutdown() {
lock.lock();
@@ -120,15 +133,15 @@ public class DatabaseFactory {
}
private static Database createInternal(DatabaseConfig config) {
- return getContainer(config.getContainerConfig()).createServer(config);
+ return container(config.getContainerConfig()).createServer(config);
}
/**
- * Get the EbeanContainer initialising it if necessary.
+ * Return the SpiContainer initialising it if necessary.
*
* @param containerConfig the configuration controlling clustering communication
*/
- private static SpiContainer getContainer(ContainerConfig containerConfig) {
+ private static SpiContainer container(ContainerConfig containerConfig) {
// thread safe in that all calling methods hold lock
if (container != null) {
return container;
diff --git a/ebean-api/src/main/java/io/ebean/DbContext.java b/ebean-api/src/main/java/io/ebean/DbContext.java
index cf09dc7fb..18df2169e 100644
--- a/ebean-api/src/main/java/io/ebean/DbContext.java
+++ b/ebean-api/src/main/java/io/ebean/DbContext.java
@@ -27,7 +27,7 @@ final class DbContext {
private final HashMap syncMap = new HashMap<>();
- private final ReentrantLock lock = new ReentrantLock(false);
+ private final ReentrantLock lock = new ReentrantLock();
/**
* The 'default' Database.
diff --git a/ebean-api/src/main/java/io/ebean/DbPrimary.java b/ebean-api/src/main/java/io/ebean/DbPrimary.java
index 9cbdc2eba..ffec8b114 100644
--- a/ebean-api/src/main/java/io/ebean/DbPrimary.java
+++ b/ebean-api/src/main/java/io/ebean/DbPrimary.java
@@ -12,7 +12,7 @@ import java.util.concurrent.locks.ReentrantLock;
*/
class DbPrimary {
- private static final ReentrantLock lock = new ReentrantLock(false);
+ private static final ReentrantLock lock = new ReentrantLock();
private static String defaultServerName;
private static boolean skip;
diff --git a/ebean-api/src/main/java/io/ebean/DocumentStore.java b/ebean-api/src/main/java/io/ebean/DocumentStore.java
index a4f75132d..0a2353ff0 100644
--- a/ebean-api/src/main/java/io/ebean/DocumentStore.java
+++ b/ebean-api/src/main/java/io/ebean/DocumentStore.java
@@ -149,7 +149,7 @@ public interface DocumentStore {
* .setUseDocStore(true)
* .where()... // perhaps add predicates
* .findEachWhile(new Predicate() {
- * @Override
+ * @Override
* public void accept(Order bean) {
* // process the bean
*
diff --git a/ebean-api/src/main/java/io/ebean/DtoQuery.java b/ebean-api/src/main/java/io/ebean/DtoQuery.java
index bb2fa43ab..69ed034ef 100644
--- a/ebean-api/src/main/java/io/ebean/DtoQuery.java
+++ b/ebean-api/src/main/java/io/ebean/DtoQuery.java
@@ -6,6 +6,7 @@ import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.stream.Stream;
/**
* Query for performing native SQL queries that return DTO Bean's.
@@ -37,7 +38,7 @@ import java.util.function.Predicate;
*
* }
*/
-public interface DtoQuery {
+public interface DtoQuery extends CancelableQuery {
/**
* Execute the query returning a list.
@@ -45,6 +46,26 @@ public interface DtoQuery {
@Nonnull
List findList();
+ /**
+ * Execute the query iterating a row at a time.
+ *
+ * Note that the QueryIterator holds resources related to the underlying
+ * resultSet and potentially connection and MUST be closed. We should use
+ * QueryIterator in a try with resource block.
+ */
+ @Nonnull
+ QueryIterator findIterate();
+
+ /**
+ * Execute the query returning a Stream.
+ *
+ * Note that the Stream holds resources related to the underlying
+ * resultSet and potentially connection and MUST be closed. We should use
+ * the Stream in a try with resource block.
+ */
+ @Nonnull
+ Stream findStream();
+
/**
* Execute the query iterating a row at a time.
*
@@ -53,6 +74,17 @@ public interface DtoQuery {
*/
void findEach(Consumer consumer);
+ /**
+ * Execute the query iterating the results and batching them for the consumer.
+ *
+ * This runs like findEach streaming results from the database but just collects the results
+ * into batches to pass to the consumer.
+ *
+ * @param batch The number of dto beans to collect before given them to the consumer
+ * @param consumer The consumer to process the batch of DTO beans
+ */
+ void findEach(int batch, Consumer> consumer);
+
/**
* Execute the query iterating a row at a time with the ability to stop consuming part way through.
*
diff --git a/ebean-api/src/main/java/io/ebean/Ebean.java b/ebean-api/src/main/java/io/ebean/Ebean.java
index f68a0666b..fb0e34db3 100644
--- a/ebean-api/src/main/java/io/ebean/Ebean.java
+++ b/ebean-api/src/main/java/io/ebean/Ebean.java
@@ -346,7 +346,7 @@ public final class Ebean {
*
{@code
* public class Order { ...
*
- * @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
+ * @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* List details;
* ...
* }
diff --git a/ebean-api/src/main/java/io/ebean/ExpressionFactory.java b/ebean-api/src/main/java/io/ebean/ExpressionFactory.java
index 1e5c4ebf5..3bcd5cb39 100644
--- a/ebean-api/src/main/java/io/ebean/ExpressionFactory.java
+++ b/ebean-api/src/main/java/io/ebean/ExpressionFactory.java
@@ -200,7 +200,7 @@ public interface ExpressionFactory {
Expression gtOrNull(String propertyName, Object value);
/**
- * Greater than or Equal to OR Null >= or null
+ * Greater than or Equal to OR Null ({@code >= or null })
*
* A convenient expression combining GE and Is Null. Most often useful for range
* expressions where the top range value is nullable.
@@ -227,7 +227,7 @@ public interface ExpressionFactory {
Expression ltOrNull(String propertyName, Object value);
/**
- * Less Than or Equal to OR Null <= or null
+ * Less Than or Equal to OR Null ({@code <= or null })
*
* A convenient expression combining LE and Is Null. Most often useful for range
* expressions where the bottom range value is nullable.
diff --git a/ebean-api/src/main/java/io/ebean/ExpressionList.java b/ebean-api/src/main/java/io/ebean/ExpressionList.java
index 5ac3d7de8..e91846e08 100644
--- a/ebean-api/src/main/java/io/ebean/ExpressionList.java
+++ b/ebean-api/src/main/java/io/ebean/ExpressionList.java
@@ -169,16 +169,33 @@ public interface ExpressionList {
*/
UpdateQuery asUpdate();
+ /**
+ * Execute the query with the given lock type and WAIT.
+ *
+ * Note that forUpdate() is the same as
+ * withLock(LockType.UPDATE).
+ *
+ * Provides us with the ability to explicitly use Postgres
+ * SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
+ */
+ Query withLock(Query.LockType lockType);
+
+ /**
+ * Execute the query with the given lock type and lock wait.
+ *
+ * Note that forUpdateNoWait() is the same as
+ * withLock(LockType.UPDATE, LockWait.NOWAIT).
+ *
+ * Provides us with the ability to explicitly use Postgres
+ * SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
+ */
+ Query withLock(Query.LockType lockType, Query.LockWait lockWait);
+
/**
* Execute using "for update" clause which results in the DB locking the record.
*/
Query forUpdate();
- /**
- * Execute using "for update" with given lock type (currently Postgres only).
- */
- Query forUpdate(Query.LockType lockType);
-
/**
* Execute using "for update" clause with No Wait option.
*
@@ -187,11 +204,6 @@ public interface ExpressionList {
*/
Query forUpdateNoWait();
- /**
- * Execute using "for update nowait" with given lock type (currently Postgres only).
- */
- Query forUpdateNoWait(Query.LockType lockType);
-
/**
* Execute using "for update" clause with Skip Locked option.
*
@@ -200,11 +212,6 @@ public interface ExpressionList {
*/
Query forUpdateSkipLocked();
- /**
- * Execute using "for update skip locked" with given lock type (currently Postgres only).
- */
- Query forUpdateSkipLocked(Query.LockType lockType);
-
/**
* Execute the query including soft deleted rows.
*/
@@ -300,6 +307,13 @@ public interface ExpressionList {
*/
void findEach(Consumer consumer);
+ /**
+ * Execute findEach with a batch consumer.
+ *
+ * @see Query#findEach(int, Consumer)
+ */
+ void findEach(int batch, Consumer> consumer);
+
/**
* Execute the query processing the beans one at a time with the ability to
* stop processing before reading all the beans.
@@ -902,7 +916,7 @@ public interface ExpressionList {
ExpressionList gtOrNull(String propertyName, Object value);
/**
- * Greater Than or Equal to OR Null - >= or null .
+ * Greater Than or Equal to OR Null - ({@code >= or null }).
*/
ExpressionList geOrNull(String propertyName, Object value);
@@ -923,7 +937,7 @@ public interface ExpressionList {
ExpressionList ltOrNull(String propertyName, Object value);
/**
- * Less Than or Equal to OR Null - <= or null .
+ * Less Than or Equal to OR Null - ({@code <= or null }).
*/
ExpressionList leOrNull(String propertyName, Object value);
@@ -1657,7 +1671,7 @@ public interface ExpressionList {
ExpressionList endAnd();
/**
- * End a AND junction - synonym for endJunction().
+ * End a OR junction - synonym for endJunction().
*/
ExpressionList endOr();
diff --git a/ebean-api/src/main/java/io/ebean/ExtendedServer.java b/ebean-api/src/main/java/io/ebean/ExtendedServer.java
index 06ee85c2b..59b1c678e 100644
--- a/ebean-api/src/main/java/io/ebean/ExtendedServer.java
+++ b/ebean-api/src/main/java/io/ebean/ExtendedServer.java
@@ -67,7 +67,7 @@ public interface ExtendedServer {
*
* @return True if the query finds a matching row in the database
*/
- boolean exists(Query> ormQuery, Transaction transaction);
+ boolean exists(Query ormQuery, Transaction transaction);
/**
* Return the number of 'top level' or 'root' entities this query should return.
@@ -159,6 +159,13 @@ public interface ExtendedServer {
*/
void findEach(Query query, Consumer consumer, Transaction transaction);
+ /**
+ * Execute findEach with batch consumer.
+ *
+ * @see Query#findEach(int, Consumer)
+ */
+ void findEach(Query query, int batch, Consumer> consumer, Transaction t);
+
/**
* Execute the query visiting the each bean one at a time.
*
diff --git a/ebean-api/src/main/java/io/ebean/FetchConfig.java b/ebean-api/src/main/java/io/ebean/FetchConfig.java
index 39823b2c7..e2d4fbe35 100644
--- a/ebean-api/src/main/java/io/ebean/FetchConfig.java
+++ b/ebean-api/src/main/java/io/ebean/FetchConfig.java
@@ -3,30 +3,13 @@ package io.ebean;
import java.io.Serializable;
/**
- * Defines the configuration options for a "query fetch" or a
- * "lazy loading fetch". This gives you the ability to use multiple smaller
- * queries to populate an object graph as opposed to a single large query.
- *
- * The primary goal is to provide efficient ways of loading complex object
- * graphs avoiding SQL Cartesian product and issues around populating object
- * graphs that have multiple *ToMany relationships.
- *
- *
- * It also provides the ability to control the lazy loading queries (batch size,
- * selected properties and fetches) to avoid N+1 queries etc.
- *
- * There can also be cases loading across a single OneToMany where 2 SQL queries
- * using Ebean FetchConfig.query() can be more efficient than one SQL query.
- * When the "One" side is wide (lots of columns) and the cardinality difference
- * is high (a lot of "Many" beans per "One" bean) then this can be more
- * efficient loaded as 2 SQL queries.
- *
+ * Defines how a relationship is fetched via either normal SQL join,
+ * a eager secondary query, via lazy loading or via eagerly hitting L2 cache.
*
*
{@code
* // Normal fetch join results in a single SQL query
* List list = DB.find(Order.class).fetch("details").findList();
*
- * // Find Orders join details using a single SQL query
* }
*
* Example: Using a "query join" instead of a "fetch join" we instead use 2 SQL queries
@@ -37,103 +20,13 @@ import java.io.Serializable;
* // This will use 2 SQL queries to build this object graph
* List list =
* DB.find(Order.class)
- * .fetch("details", new FetchConfig().query())
+ * .fetch("details", FetchConfig.ofQuery())
* .findList();
*
* // query 1) find order
* // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
*
* }
- *
- * Example: Using 2 "query joins"
- *
- *
- *
{@code
- *
- * // This will use 3 SQL queries to build this object graph
- * List list =
- * DB.find(Order.class)
- * .fetch("details", new FetchConfig().query())
- * .fetch("customer", new FetchConfig().queryFirst(5))
- * .findList();
- *
- * // query 1) find order
- * // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
- * // query 3) find customer where id in (?,?,?,?,?) // first 5 customers
- *
- * }
- *
- * Example: Using "query joins" and partial objects
- *
- *
- *
- *
{@code
- * // This will use 3 SQL queries to build this object graph
- * List list =
- * DB.find(Order.class)
- * .select("status, shipDate")
- * .fetch("details", "quantity, price", new FetchConfig().query())
- * .fetch("details.product", "sku, name")
- * .fetch("customer", "name", new FetchConfig().queryFirst(5))
- * .fetch("customer.contacts")
- * .fetch("customer.shippingAddress")
- * .findList();
- *
- * // query 1) find order (status, shipDate)
- * // query 2) find orderDetail (quantity, price) fetch product (sku, name) where
- * // order.id in (?,? ...)
- * // query 3) find customer (name) fetch contacts (*) fetch shippingAddress (*)
- * // where id in (?,?,?,?,?)
- *
- * // Note: the fetch of "details.product" is automatically included into the
- * // fetch of "details"
- * //
- * // Note: the fetch of "customer.contacts" and "customer.shippingAddress"
- * // are automatically included in the fetch of "customer"
- * }
- *
- * You can use query() and lazy together on a single join. The query is executed
- * immediately and the lazy defines the batch size to use for further lazy
- * loading (if lazy loading is invoked).
- *
- *
- *
{@code
- *
- * List list =
- * DB.find(Order.class)
- * .fetch("customer", new FetchConfig().query(10).lazy(5))
- * .findList();
- *
- * // query 1) find order
- * // query 2) find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers
- * // .. then if lazy loading of customers is invoked
- * // .. use a batch size of 5 to load the customers
- *
- * }
- *
- *
- * Example of controlling the lazy loading query:
- *
- *
- * This gives us the ability to optimise the lazy loading query for a given use
- * case.
- *
- *
- *
{@code
- *
- * List list = DB.find(Order.class)
- * .fetch("customer","name", new FetchConfig().lazy(5))
- * .fetch("customer.contacts","contactName, phone, email")
- * .fetch("customer.shippingAddress")
- * .where().eq("status",Order.Status.NEW)
- * .findList();
- *
- * // query 1) find order where status = Order.Status.NEW
- * //
- * // .. if lazy loading of customers is invoked
- * // .. use a batch size of 5 to load the customers
- *
- * }
*
* @author mario
* @author rbygrave
@@ -142,149 +35,209 @@ public class FetchConfig implements Serializable {
private static final long serialVersionUID = 1L;
- private int lazyBatchSize = -1;
+ private static final int JOIN_MODE = 0;
+ private static final int QUERY_MODE = 1;
+ private static final int LAZY_MODE = 2;
+ private static final int CACHE_MODE = 3;
- private int queryBatchSize = -1;
-
- private boolean queryAll;
-
- private boolean cache;
+ private int mode;
+ private int batchSize;
+ private int hashCode;
/**
- * Construct the fetch configuration object.
+ * Deprecated - migrate to one of the static factory methods like {@link FetchConfig#ofQuery()}
+ *
+ * Construct using default JOIN mode.
*/
+ @Deprecated
public FetchConfig() {
+ //this.mode = JOIN_MODE;
+ this.batchSize = 100;
+ this.hashCode = 1000;
+ }
+
+ private FetchConfig(int mode, int batchSize) {
+ this.mode = mode;
+ this.batchSize = batchSize;
+ this.hashCode = mode + 10 * batchSize;
}
/**
- * Specify that this path should be lazy loaded using the default batch load
- * size.
+ * Return FetchConfig to eagerly fetch the relationship using L2 cache.
+ *
+ * Any cache misses will be loaded by secondary query to the database.
*/
+ public static FetchConfig ofCache() {
+ return new FetchConfig(CACHE_MODE, 100);
+ }
+
+ /**
+ * Return FetchConfig to eagerly fetch the relationship using a secondary query.
+ */
+ public static FetchConfig ofQuery() {
+ return new FetchConfig(QUERY_MODE, 100);
+ }
+
+ /**
+ * Return FetchConfig to eagerly fetch the relationship using a secondary with a given batch size.
+ */
+ public static FetchConfig ofQuery(int batchSize) {
+ return new FetchConfig(QUERY_MODE, batchSize);
+ }
+
+ /**
+ * Return FetchConfig to lazily load the relationship.
+ */
+ public static FetchConfig ofLazy() {
+ return new FetchConfig(LAZY_MODE, 0);
+ }
+
+ /**
+ * Return FetchConfig to lazily load the relationship specifying the batch size.
+ */
+ public static FetchConfig ofLazy(int batchSize) {
+ return new FetchConfig(LAZY_MODE, batchSize);
+ }
+
+ /**
+ * Return FetchConfig to fetch the relationship using SQL join.
+ */
+ public static FetchConfig ofDefault() {
+ return new FetchConfig(JOIN_MODE, 100);
+ }
+
+ /**
+ * We want to migrate away from mutating FetchConfig to a fully immutable FetchConfig.
+ */
+ private FetchConfig mutate(int mode, int batchSize) {
+ if (batchSize < 0) {
+ throw new IllegalArgumentException("batch size " + batchSize + " must be > 0");
+ }
+ this.mode = mode;
+ this.batchSize = batchSize;
+ this.hashCode = mode + 10 * batchSize;
+ return this;
+ }
+
+ /**
+ * Deprecated - migrate to FetchConfig.ofLazy().
+ */
+ @Deprecated
public FetchConfig lazy() {
- this.lazyBatchSize = 0;
- this.queryAll = false;
- return this;
+ return mutate(LAZY_MODE, 0);
}
/**
- * Specify that this path should be lazy loaded with a specified batch size.
- *
- * @param lazyBatchSize the batch size for lazy loading
+ * Deprecated - migrate to FetchConfig.ofLazy(batchSize).
*/
- public FetchConfig lazy(int lazyBatchSize) {
- this.lazyBatchSize = lazyBatchSize;
- this.queryAll = false;
- return this;
+ @Deprecated
+ public FetchConfig lazy(int batchSize) {
+ return mutate(LAZY_MODE, batchSize);
}
/**
+ * Deprecated - migrate to FetchConfig.ofQuery().
+ *
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
*
* This will use the default batch size for separate query which is 100.
- *
*/
+ @Deprecated
public FetchConfig query() {
- this.queryBatchSize = 0;
- this.queryAll = true;
- return this;
- }
-
- /**
- * Eagerly fetch the beans fetching the beans from the L2 bean cache
- * and using the DB for beans not in the cache.
- */
- public FetchConfig cache() {
- this.cache = true;
- this.queryBatchSize = 0;
- this.queryAll = true;
- return this;
+ return mutate(QUERY_MODE, 100);
}
/**
+ * Deprecated - migrate to FetchConfig.ofQuery(batchSize).
+ *
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
*
* The queryBatchSize is the number of parent id's that this separate query
* will load per batch.
- *
*
* This will load all beans on this path eagerly unless a {@link #lazy(int)}
* is also used.
- *
*
- * @param queryBatchSize the batch size used to load beans on this path
+ * @param batchSize the batch size used to load beans on this path
*/
- public FetchConfig query(int queryBatchSize) {
- this.queryBatchSize = queryBatchSize;
- // queryAll true as long as a lazy batch size has not already been set
- this.queryAll = (lazyBatchSize == -1);
- return this;
+ @Deprecated
+ public FetchConfig query(int batchSize) {
+ return mutate(QUERY_MODE, batchSize);
}
/**
+ * Deprecated - migrate to FetchConfig.ofQuery(batchSize).
+ *
* Eagerly fetch the first batch of beans on this path.
* This is similar to {@link #query(int)} but only fetches the first batch.
*
* If there are more parent beans than the batch size then they will not be
* loaded eagerly but instead use lazy loading.
- *
*
- * @param queryBatchSize the number of parent beans this path is populated for
+ * @param batchSize the number of parent beans this path is populated for
*/
- public FetchConfig queryFirst(int queryBatchSize) {
- this.queryBatchSize = queryBatchSize;
- this.queryAll = false;
- return this;
+ @Deprecated
+ public FetchConfig queryFirst(int batchSize) {
+ return query(batchSize);
}
/**
- * Return the batch size for lazy loading.
+ * Deprecated - migrate to FetchConfig.ofCache().
+ *
+ * Eagerly fetch the beans fetching the beans from the L2 bean cache
+ * and using the DB for beans not in the cache.
*/
- public int getLazyBatchSize() {
- return lazyBatchSize;
+ @Deprecated
+ public FetchConfig cache() {
+ return mutate(CACHE_MODE, 100);
}
/**
- * Return the batch size for separate query load.
+ * Return the batch size for fetching.
*/
- public int getQueryBatchSize() {
- return queryBatchSize;
+ public int getBatchSize() {
+ return batchSize;
}
/**
- * Return true if the query fetch should fetch 'all' rather than just the
- * 'first' batch.
- */
- public boolean isQueryAll() {
- return queryAll;
- }
-
- /**
- * Return true if this uses L2 bean cache.
+ * Return true if the fetch should use the L2 cache.
*/
public boolean isCache() {
- return cache;
+ return mode == CACHE_MODE;
+ }
+
+ /**
+ * Return true if the fetch should be a eager secondary query.
+ */
+ public boolean isQuery() {
+ return mode == QUERY_MODE;
+ }
+
+ /**
+ * Return true if the fetch should be a lazy query.
+ */
+ public boolean isLazy() {
+ return mode == LAZY_MODE;
+ }
+
+ /**
+ * Return true if the fetch should try to use SQL join.
+ */
+ public boolean isJoin() {
+ return mode == JOIN_MODE;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
-
- FetchConfig that = (FetchConfig) o;
- if (lazyBatchSize != that.lazyBatchSize) return false;
- if (queryBatchSize != that.queryBatchSize) return false;
- if (cache != that.cache) return false;
- return queryAll == that.queryAll;
+ return (hashCode == ((FetchConfig) o).hashCode);
}
@Override
public int hashCode() {
- int result = lazyBatchSize;
- result = 92821 * result + queryBatchSize;
- result = 92821 * result + (queryAll ? 1 : 0);
- result = 92821 * result + (cache ? 1 : 0);
- return result;
+ return hashCode;
}
}
diff --git a/ebean-api/src/main/java/io/ebean/Finder.java b/ebean-api/src/main/java/io/ebean/Finder.java
index e91238344..3c9318e7d 100644
--- a/ebean-api/src/main/java/io/ebean/Finder.java
+++ b/ebean-api/src/main/java/io/ebean/Finder.java
@@ -37,7 +37,7 @@ import java.util.List;
* }
* }
*
- * @Entity
+ * @Entity
* public class Customer extends BaseModel {
*
* public static final CustomerFinder find = new CustomerFinder();
@@ -80,7 +80,7 @@ public class Finder {
* // ... add extra customer specific finder methods
* }
*
- * @Entity
+ * @Entity
* public class Customer extends BaseModel {
*
* public static final CustomerFinder find = new CustomerFinder();
diff --git a/ebean-api/src/main/java/io/ebean/Model.java b/ebean-api/src/main/java/io/ebean/Model.java
index 5fe2205e1..ac2f11ea7 100644
--- a/ebean-api/src/main/java/io/ebean/Model.java
+++ b/ebean-api/src/main/java/io/ebean/Model.java
@@ -32,16 +32,16 @@ import io.ebean.bean.EntityBean;
* // Typically there is a common base model that has some
* // common properties like the ones below
*
- * @MappedSuperclass
+ * @MappedSuperclass
* public class BaseModel extends Model {
*
- * @Id Long id;
+ * @Id Long id;
*
- * @Version Long version;
+ * @Version Long version;
*
- * @WhenCreated Timestamp whenCreated;
+ * @WhenCreated Timestamp whenCreated;
*
- * @WhenUpdated Timestamp whenUpdated;
+ * @WhenUpdated Timestamp whenUpdated;
*
* ...
* }
@@ -52,7 +52,7 @@ import io.ebean.bean.EntityBean;
*
* // Extend the mappedSuperclass
*
- * @Entity @Table(name="o_account")
+ * @Entity @Table(name="o_account")
* public class Customer extends BaseModel {
*
* String name;
diff --git a/ebean-api/src/main/java/io/ebean/OrderBy.java b/ebean-api/src/main/java/io/ebean/OrderBy.java
index 820a76314..8b39fc19e 100644
--- a/ebean-api/src/main/java/io/ebean/OrderBy.java
+++ b/ebean-api/src/main/java/io/ebean/OrderBy.java
@@ -16,7 +16,7 @@ import java.util.Objects;
* on the Query object.
*
*/
-public final class OrderBy implements Serializable {
+public class OrderBy implements Serializable {
private static final long serialVersionUID = 9157089257745730539L;
@@ -69,7 +69,6 @@ public final class OrderBy implements Serializable {
* Add a property with ascending order to this OrderBy.
*/
public Query asc(String propertyName) {
-
list.add(new Property(propertyName, true));
return query;
}
@@ -98,7 +97,6 @@ public final class OrderBy implements Serializable {
return query;
}
-
/**
* Return true if the property is known to be contained in the order by clause.
*/
@@ -207,7 +205,6 @@ public final class OrderBy implements Serializable {
if (!(obj instanceof OrderBy>)) {
return false;
}
-
OrderBy> e = (OrderBy>) obj;
return e.list.equals(list);
}
@@ -249,7 +246,7 @@ public final class OrderBy implements Serializable {
/**
* A property and its ascending descending order.
*/
- public static final class Property implements Serializable {
+ public static class Property implements Serializable {
private static final long serialVersionUID = 1546009780322478077L;
@@ -415,13 +412,10 @@ public final class OrderBy implements Serializable {
}
private void parse(String orderByClause) {
-
if (orderByClause == null) {
return;
}
-
- String[] chunks = orderByClause.split(",");
- for (String chunk : chunks) {
+ for (String chunk : orderByClause.split(",")) {
Property p = parseProperty(chunk);
if (p != null) {
list.add(p);
@@ -467,8 +461,7 @@ public final class OrderBy implements Serializable {
if (s.startsWith("desc")) {
return false;
}
- String m = "Expecting [" + s + "] to be asc or desc?";
- throw new RuntimeException(m);
+ throw new RuntimeException("Expecting [" + s + "] to be asc or desc?");
}
private boolean isEmptyString(String s) {
diff --git a/ebean-api/src/main/java/io/ebean/Pairs.java b/ebean-api/src/main/java/io/ebean/Pairs.java
index 4f609f95a..bd5ee5137 100644
--- a/ebean-api/src/main/java/io/ebean/Pairs.java
+++ b/ebean-api/src/main/java/io/ebean/Pairs.java
@@ -18,7 +18,7 @@ import java.util.List;
*
* // where a bean is annotated with a complex
* // natural key made of several properties
- * @Cache(naturalKey = {"store","code","sku"})
+ * @Cache(naturalKey = {"store","code","sku"})
*
*
* Pairs pairs = new Pairs("sku", "code");
diff --git a/ebean-api/src/main/java/io/ebean/Query.java b/ebean-api/src/main/java/io/ebean/Query.java
index 200704cfd..0dc86905e 100644
--- a/ebean-api/src/main/java/io/ebean/Query.java
+++ b/ebean-api/src/main/java/io/ebean/Query.java
@@ -177,14 +177,15 @@ import java.util.stream.Stream;
*
* @param the type of Entity bean this query will fetch.
*/
-public interface Query {
+public interface Query extends CancelableQuery {
/**
* The lock type (strength) to use with query FOR UPDATE row locking.
*/
enum LockType {
/**
- * The default lock type - See PlatformConfig.forUpdateNoKey option.
+ * The default lock type being either UPDATE or NO_KEY_UPDATE based on
+ * PlatformConfig.forUpdateNoKey configuration (Postgres option).
*/
DEFAULT,
@@ -194,17 +195,17 @@ public interface Query {
UPDATE,
/**
- * FOR NO KEY UPDATE.
+ * FOR NO KEY UPDATE (Postgres only).
*/
NO_KEY_UPDATE,
/**
- * FOR SHARE UPDATE.
+ * FOR SHARE (Postgres only).
*/
SHARE,
/**
- * FOR KEY SHARE UPDATE.
+ * FOR KEY SHARE (Postgres only).
*/
KEY_SHARE
}
@@ -290,15 +291,6 @@ public interface Query {
*/
UpdateQuery asUpdate();
- /**
- * Cancel the query execution if supported by the underlying database and
- * driver.
- *
- * This must be called from a different thread to the query executor.
- *
- */
- void cancel();
-
/**
* Return a copy of the query.
*
@@ -661,7 +653,7 @@ public interface Query {
* // fetch customers (their id, name and status)
* List customers = DB.find(Customer.class)
* // lazy fetch contacts with a batch size of 100
- * .fetch("contacts", new FetchConfig().lazy(100))
+ * .fetch("contacts", FetchConfig.ofLazy(100))
* .findList();
*
* }
@@ -809,7 +801,7 @@ public interface Query {
*
*
* This method is functionally equivalent to findIterate() but instead of using an
- * iterator uses the Consumer interface which is better suited to use with Java8 closures.
+ * iterator uses the Consumer interface which is better suited to use with closures.
*
*
{@code
*
@@ -828,6 +820,21 @@ public interface Query {
*/
void findEach(Consumer consumer);
+ /**
+ * Execute findEach streaming query batching the results for consuming.
+ *
+ * This query execution will stream the results and is suited to consuming
+ * large numbers of results from the database.
+ *
+ * Typically we use this batch consumer when we want to do further processing on
+ * the beans and want to do that processing in batch form, for example - 100 at
+ * a time.
+ *
+ * @param batch The number of beans processed in the batch
+ * @param consumer Process the batch of beans
+ */
+ void findEach(int batch, Consumer> consumer);
+
/**
* Execute the query using callbacks to a visitor to process the resulting
* beans one at a time.
@@ -838,12 +845,12 @@ public interface Query {
*
*
* This method is functionally equivalent to findIterate() but instead of using an
- * iterator uses the Predicate (SAM) interface which is better suited to use with Java8 closures.
+ * iterator uses the Predicate interface which is better suited to use with closures.
*
*
{@code
*
* DB.find(Customer.class)
- * .fetch("contacts", new FetchConfig().query(2))
+ * .fetchQuery("contacts")
* .where().eq("status", Status.NEW)
* .order().asc("id")
* .setMaxRows(2000)
@@ -1644,41 +1651,52 @@ public interface Query {
String getGeneratedSql();
/**
- * Execute using "for update" clause which results in the DB locking the record.
+ * Execute the query with the given lock type and WAIT.
+ *
+ * Note that forUpdate() is the same as
+ * withLock(LockType.UPDATE).
+ *
+ * Provides us with the ability to explicitly use Postgres
+ * SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
*/
- Query forUpdate();
+ Query withLock(LockType lockType);
/**
- * Execute using "for update" with given lock type (currently Postgres only).
+ * Execute the query with the given lock type and lock wait.
+ *
+ * Note that forUpdateNoWait() is the same as
+ * withLock(LockType.UPDATE, LockWait.NOWAIT).
+ *
+ * Provides us with the ability to explicitly use Postgres
+ * SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
*/
- Query forUpdate(LockType lockType);
+ Query withLock(LockType lockType, LockWait lockWait);
+
+ /**
+ * Execute using "for update" clause which results in the DB locking the record.
+ *
+ * The same as withLock(LockType.UPDATE, LockWait.WAIT).
+ */
+ Query forUpdate();
/**
* Execute using "for update" clause with "no wait" option.
*
* This is typically a Postgres and Oracle only option at this stage.
- *
+ *
+ * The same as withLock(LockType.UPDATE, LockWait.NOWAIT).
*/
Query forUpdateNoWait();
- /**
- * Execute using "for update nowait" with given lock type (currently Postgres only).
- */
- Query forUpdateNoWait(LockType lockType);
-
/**
* Execute using "for update" clause with "skip locked" option.
*
* This is typically a Postgres and Oracle only option at this stage.
- *
+ *
+ * The same as withLock(LockType.UPDATE, LockWait.SKIPLOCKED).
*/
Query forUpdateSkipLocked();
- /**
- * Execute using "for update skip locked" with given lock type (currently Postgres only).
- */
- Query forUpdateSkipLocked(LockType lockType);
-
/**
* Return true if this query has forUpdate set.
*/
diff --git a/ebean-api/src/main/java/io/ebean/QueryIterator.java b/ebean-api/src/main/java/io/ebean/QueryIterator.java
index c158e62c6..6ad523151 100644
--- a/ebean-api/src/main/java/io/ebean/QueryIterator.java
+++ b/ebean-api/src/main/java/io/ebean/QueryIterator.java
@@ -56,7 +56,7 @@ import java.util.Iterator;
*
* @param the type of entity bean in the iteration
*/
-public interface QueryIterator extends Iterator, java.io.Closeable {
+public interface QueryIterator extends Iterator, AutoCloseable {
/**
* Returns true if the iteration has more elements.
@@ -70,12 +70,6 @@ public interface QueryIterator extends Iterator, java.io.Closeable {
@Override
T next();
- /**
- * Remove is not allowed.
- */
- @Override
- void remove();
-
/**
* Close the underlying resources held by this iterator.
*/
diff --git a/ebean-api/src/main/java/io/ebean/RawSql.java b/ebean-api/src/main/java/io/ebean/RawSql.java
index 1fe274a1e..901f1a43c 100644
--- a/ebean-api/src/main/java/io/ebean/RawSql.java
+++ b/ebean-api/src/main/java/io/ebean/RawSql.java
@@ -41,14 +41,14 @@ package io.ebean;
*
Example OrderAggregate
*
{@code
* ...
- * // @Sql indicates to that this bean
+ * // @Sql indicates to that this bean
* // is based on RawSql rather than a table
*
- * @Entity
- * @Sql
+ * @Entity
+ * @Sql
* public class OrderAggregate {
*
- * @OneToOne
+ * @OneToOne
* Order order;
*
* Double totalAmount;
@@ -107,7 +107,7 @@ package io.ebean;
*
* List orders = DB.find(OrderAggregate.class)
* .setRawSql(rawSql)
- * .fetch("order", "status,orderDate", new FetchConfig().query())
+ * .fetch("order", "status,orderDate", FetchConfig.ofQuery())
* .fetch("order.customer", "name")
* .where().gt("order.id", 0)
* .having().gt("totalAmount", 20)
diff --git a/ebean-api/src/main/java/io/ebean/RowMapper.java b/ebean-api/src/main/java/io/ebean/RowMapper.java
index 47e96498d..b1d59c984 100644
--- a/ebean-api/src/main/java/io/ebean/RowMapper.java
+++ b/ebean-api/src/main/java/io/ebean/RowMapper.java
@@ -22,7 +22,7 @@ import java.sql.SQLException;
* //
* class CustomerMapper implements RowMapper {
*
- * @Override
+ * @Override
* public CustomerDto map(ResultSet rset, int rowNum) throws SQLException {
*
* long id = rset.getLong(1);
diff --git a/ebean-api/src/main/java/io/ebean/SqlQuery.java b/ebean-api/src/main/java/io/ebean/SqlQuery.java
index 8b5452ee7..9a878f233 100644
--- a/ebean-api/src/main/java/io/ebean/SqlQuery.java
+++ b/ebean-api/src/main/java/io/ebean/SqlQuery.java
@@ -37,7 +37,7 @@ import java.util.function.Predicate;
*
* }
*/
-public interface SqlQuery extends Serializable {
+public interface SqlQuery extends Serializable, CancelableQuery {
/**
* Execute the query returning a list.
@@ -365,5 +365,10 @@ public interface SqlQuery extends Serializable {
* Return the list of values.
*/
List findList();
+
+ /**
+ * Find streaming the result effectively consuming a row at a time.
+ */
+ void findEach(Consumer consumer);
}
}
diff --git a/ebean-api/src/main/java/io/ebean/Transaction.java b/ebean-api/src/main/java/io/ebean/Transaction.java
index b2bce9abf..0856a6e4e 100644
--- a/ebean-api/src/main/java/io/ebean/Transaction.java
+++ b/ebean-api/src/main/java/io/ebean/Transaction.java
@@ -59,6 +59,14 @@ public interface Transaction extends AutoCloseable {
*/
void register(TransactionCallback callback);
+ /**
+ * EXPERIMENTAL - turn on automatic persistence of dirty beans and batchMode true.
+ *
+ * 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.
*
@@ -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.
- *
*
* 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.
- *
*
* Example: batch processing of CallableSql executing every 10 rows
- *
* This only takes effect when batch mode on the transaction has not already meant that
* JDBC batch mode is being used.
- *
*
* 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.
- *
*
* This is effectively already turned on for all platforms apart from older Sql Server.
- *
*
* @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.
*
* By default batched inserts will try to use getGeneratedKeys if it is
* supported by the underlying jdbc driver and database.
- *
*
- * 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.
- *
+ * 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.
+ *
+ * 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 {
*
* If you want to execute both WITHOUT having the batch automatically flush
* you need to call this with batchFlushOnMixed = false.
- *
*
* 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).
- *
* Calling this method with batchFlushOnQuery = false means that you can
* execute a query and the batch will not be automatically flushed.
- *
*/
void setFlushOnQuery(boolean batchFlushOnQuery);
@@ -490,7 +493,6 @@ public interface Transaction extends AutoCloseable {
* should be flushed prior to executing a query.
*
* The default is for this to be true.
- *
*/
boolean isFlushOnQuery();
@@ -507,7 +509,6 @@ public interface Transaction extends AutoCloseable {
* flush the batch if you like.
*
* Flushing occurs automatically when:
- *
*
*
the batch size is reached
*
A query is executed on the same transaction
@@ -519,11 +520,11 @@ public interface Transaction extends AutoCloseable {
void flush() throws PersistenceException;
/**
- * This is a synonym for flush() and will be deprecated.
+ * Deprecated - migrate to flush().
*
* flush() is preferred as it matches the JPA flush() method.
- *
*/
+ @Deprecated
void flushBatch() throws PersistenceException;
/**
@@ -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.
- *
*
* Examples of when a developer may wish to use the connection directly are:
* Savepoints, advanced CLOB BLOB use and advanced stored procedure calls.
- *
*/
Connection getConnection();
@@ -545,17 +544,14 @@ public interface Transaction extends AutoCloseable {
* Add table modification information to the TransactionEvent.
*
* Use this in conjunction with getConnection() and raw JDBC.
- *
*
* 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.
- *
*
* 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.
- *
*/
void addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
diff --git a/ebean-api/src/main/java/io/ebean/TxScope.java b/ebean-api/src/main/java/io/ebean/TxScope.java
index ee9fdca7b..59d9f6971 100644
--- a/ebean-api/src/main/java/io/ebean/TxScope.java
+++ b/ebean-api/src/main/java/io/ebean/TxScope.java
@@ -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.
*/
diff --git a/ebean-api/src/main/java/io/ebean/Update.java b/ebean-api/src/main/java/io/ebean/Update.java
index 25aed9315..ece973392 100644
--- a/ebean-api/src/main/java/io/ebean/Update.java
+++ b/ebean-api/src/main/java/io/ebean/Update.java
@@ -13,23 +13,23 @@ package io.ebean;
*
*
{@code
* ...
- * @NamedUpdates(value = {
- * @NamedUpdate(
+ * @NamedUpdates(value = {
+ * @NamedUpdate(
* name = "setTitle",
* notifyCache = false,
* update = "update topic set title = :title, postCount = :count where id = :id"),
- * @NamedUpdate(
+ * @NamedUpdate(
* name = "setPostCount",
* notifyCache = false,
* update = "update f_topic set post_count = :postCount where id = :id"),
- * @NamedUpdate(
+ * @NamedUpdate(
* name = "incrementPostCount",
* notifyCache = false,
* update = "update Topic set postCount = postCount + 1 where id = :id")
* //update = "update f_topic set post_count = post_count + 1 where id = :id")
* })
- * @Entity
- * @Table(name = "f_topic")
+ * @Entity
+ * @Table(name = "f_topic")
* public class Topic {
* ...
* }
diff --git a/ebean-api/src/main/java/io/ebean/bean/BeanCollection.java b/ebean-api/src/main/java/io/ebean/bean/BeanCollection.java
index 3ec5163e3..6271d4051 100644
--- a/ebean-api/src/main/java/io/ebean/bean/BeanCollection.java
+++ b/ebean-api/src/main/java/io/ebean/bean/BeanCollection.java
@@ -13,12 +13,10 @@ import java.util.Set;
* from the Map Set or List. The purpose of gathering the additions and removals
* is to support persisting ManyToMany objects. The additions and removals
* become inserts and deletes from the intersection table.
- *
*
* Technically this is NOT an extension of
* java.util.Collection. The reason being that java.util.Map is not a
* Collection. I realise this makes this name confusing so I apologise for that.
- *
*/
public interface BeanCollection extends Serializable {
@@ -68,7 +66,6 @@ public interface BeanCollection extends Serializable {
* Return true if the collection is uninitialised or is empty without any held modifications.
*
* Returning true means can safely skip cascade save for this bean collection.
- *
* That is, if the collection was not loaded due to filterMany predicates etc
* then make sure the collection is set to empty.
- *
*/
boolean checkEmptyLazyLoad();
@@ -136,10 +132,7 @@ public interface BeanCollection extends Serializable {
boolean isReadOnly();
/**
- * Add the bean to the collection.
- *
- * This is disallowed for BeanMap.
- *
+ * Add the bean to the collection. This is disallowed for BeanMap.
*/
void internalAdd(Object bean);
@@ -168,7 +161,6 @@ public interface BeanCollection extends Serializable {
* Map.Entry.
*
* For maps this returns the entrySet as we need the keys of the map.
- *
*/
Collection> getActualEntries();
@@ -185,6 +177,11 @@ public interface BeanCollection extends Serializable {
*/
boolean isReference();
+ /**
+ * Return true if the collection is modify listening and has modifications.
+ */
+ boolean hasModifications();
+
/**
* Set modify listening on or off. This is used to keep track of objects that
* have been added to or removed from the list set or map.
@@ -192,7 +189,6 @@ public interface BeanCollection extends Serializable {
* This is required only for ManyToMany collections. The additions and
* deletions are used to insert or delete entries from the intersection table.
* Otherwise modifyListening is false.
- *
* This will potentially end up as an delete from an intersection table for a
* ManyToMany.
- *
*/
void modifyRemoval(Object bean);
diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBean.java b/ebean-api/src/main/java/io/ebean/bean/EntityBean.java
index 03d1195c8..ee54991ad 100644
--- a/ebean-api/src/main/java/io/ebean/bean/EntityBean.java
+++ b/ebean-api/src/main/java/io/ebean/bean/EntityBean.java
@@ -27,17 +27,6 @@ public interface EntityBean extends Serializable {
throw new NotEnhancedException();
}
- /**
- * Return the enhancement marker value.
- *
- * This is the class name of the enhanced class and used to check that all
- * entity classes are enhanced (specifically not just a super class).
- *
- */
- default String _ebean_getMarker() {
- throw new NotEnhancedException();
- }
-
/**
* Create and return a new entity bean instance.
*/
diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java
index 4bb520a32..9924bf4f7 100644
--- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java
+++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java
@@ -32,39 +32,52 @@ public final class EntityBeanIntercept implements Serializable {
private static final int STATE_REFERENCE = 1;
private static final int STATE_LOADED = 2;
- private transient final ReentrantLock lock = new ReentrantLock(false);
+ /**
+ * Used when a bean is partially filled.
+ */
+ private static final byte FLAG_LOADED_PROP = 1;
+ private static final byte FLAG_CHANGED_PROP = 2;
+ private static final byte FLAG_CHANGEDLOADED_PROP = 3;
+ /**
+ * Flags indicating if a property is a dirty embedded bean. Used to distinguish
+ * between an embedded bean being completely overwritten and one of its
+ * embedded properties being made dirty.
+ */
+ private static final byte FLAG_EMBEDDED_DIRTY = 4;
+ /**
+ * Flags indicating if a property is a dirty embedded bean. Used to distinguish
+ * between an embedded bean being completely overwritten and one of its
+ * embedded properties being made dirty.
+ */
+ private static final byte FLAG_ORIG_VALUE_SET = 8;
+ /**
+ * Flags indicating if the mutable hash is set.
+ */
+ private static final byte FLAG_MUTABLE_HASH_SET = 16;
+
+ private transient final ReentrantLock lock = new ReentrantLock();
private transient NodeUsageCollector nodeUsageCollector;
-
private transient PersistenceContext persistenceContext;
-
private transient BeanLoader beanLoader;
-
private transient PreGetterCallback preGetterCallback;
private String ebeanServerName;
-
private boolean deletedFromCollection;
/**
* The actual entity bean that 'owns' this intercept.
*/
private final EntityBean owner;
-
private EntityBean embeddedOwner;
private int embeddedOwnerIndex;
-
/**
* One of NEW, REF, UPD.
*/
private int state;
-
private boolean forceUpdate;
-
private boolean readOnly;
-
private boolean dirty;
-
/**
* Flag set to disable lazy loading - typically for SQL "report" type entity beans.
*/
@@ -74,41 +87,26 @@ public final class EntityBeanIntercept implements Serializable {
* Flag set when lazy loading failed due to the underlying bean being deleted in the DB.
*/
private boolean lazyLoadFailure;
-
- /**
- * Used when a bean is partially filled.
- */
- private static final byte FLAG_LOADED_PROP = 1;
-
- /**
- * Set of changed properties.
- */
- private static final byte FLAG_CHANGED_PROP = 2;
-
- /**
- * Flags indicating if a property is a dirty embedded bean. Used to distinguish
- * between an embedded bean being completely overwritten and one of its
- * embedded properties being made dirty.
- */
- private static final byte FLAG_EMBEDDED_DIRTY = 4;
-
- /**
- * Flags indicating if a property is a dirty embedded bean. Used to distinguish
- * between an embedded bean being completely overwritten and one of its
- * embedded properties being made dirty.
- */
- private static final byte FLAG_ORIG_VALUE_SET = 8;
-
- private final byte[] flags;
-
private boolean fullyLoadedBean;
private boolean loadedFromCache;
+ private final byte[] flags;
private Object[] origValues;
private Exception[] loadErrors;
private int lazyLoadProperty = -1;
private Object ownerId;
private int sortOrder;
+ /**
+ * Holds information of json loaded jackson beans (e.g. the original json or checksum).
+ */
+ private MutableValueInfo[] mutableInfo;
+
+ /**
+ * Holds json content determined at point of dirty check.
+ * Stored here on dirty check such that we only convert to json once.
+ */
+ private MutableValueNext[] mutableNext;
+
/**
* Create a intercept with a given entity.
*/
@@ -246,6 +244,17 @@ public final class EntityBeanIntercept implements Serializable {
* if any embedded beans are either new or dirty (and hence need saving).
*/
public boolean isDirty() {
+ if (dirty) {
+ return true;
+ }
+ if (mutableInfo != null) {
+ for (int i = 0; i < mutableInfo.length; i++) {
+ if (mutableInfo[i] != null && !mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) {
+ dirty = true;
+ break;
+ }
+ }
+ }
return dirty;
}
@@ -386,8 +395,18 @@ public final class EntityBeanIntercept implements Serializable {
this.owner._ebean_setEmbeddedLoaded();
this.lazyLoadProperty = -1;
this.origValues = null;
+ // after save, transfer the mutable next values back to mutable info
+ if (mutableNext != null) {
+ for (int i = 0; i < mutableNext.length; i++) {
+ MutableValueNext next = mutableNext[i];
+ if (next != null) {
+ mutableInfo(i, next.info());
+ }
+ }
+ }
+ this.mutableNext = null;
for (int i = 0; i < flags.length; i++) {
- flags[i] &= ~(FLAG_CHANGED_PROP + FLAG_ORIG_VALUE_SET);
+ flags[i] &= ~(FLAG_CHANGED_PROP | FLAG_ORIG_VALUE_SET);
}
this.dirty = false;
}
@@ -460,6 +479,10 @@ public final class EntityBeanIntercept implements Serializable {
* Return the original value that was changed via an update.
*/
public Object getOrigValue(int propertyIndex) {
+ if ((flags[propertyIndex] & (FLAG_ORIG_VALUE_SET | FLAG_MUTABLE_HASH_SET)) == FLAG_MUTABLE_HASH_SET) {
+ // mutable hash set, but not ORIG_VALUE
+ setOriginalValue(propertyIndex, mutableInfo[propertyIndex].get());
+ }
if (origValues == null) {
return null;
}
@@ -494,7 +517,7 @@ public final class EntityBeanIntercept implements Serializable {
* Return the number of properties.
*/
public int getPropertyLength() {
- return owner._ebean_getPropertyNames().length;
+ return flags.length;
}
/**
@@ -569,6 +592,10 @@ public final class EntityBeanIntercept implements Serializable {
flags[propertyIndex] |= FLAG_CHANGED_PROP;
}
+ private void setChangeLoaded(int propertyIndex) {
+ flags[propertyIndex] |= FLAG_CHANGEDLOADED_PROP;
+ }
+
/**
* Set that an embedded bean has had one of its properties changed.
*/
@@ -578,7 +605,7 @@ public final class EntityBeanIntercept implements Serializable {
private void setOriginalValue(int propertyIndex, Object value) {
if (origValues == null) {
- origValues = new Object[owner._ebean_getPropertyNames().length];
+ origValues = new Object[flags.length];
}
if ((flags[propertyIndex] & FLAG_ORIG_VALUE_SET) == 0) {
flags[propertyIndex] |= FLAG_ORIG_VALUE_SET;
@@ -591,7 +618,7 @@ public final class EntityBeanIntercept implements Serializable {
*/
private void setOriginalValueForce(int propertyIndex, Object value) {
if (origValues == null) {
- origValues = new Object[owner._ebean_getPropertyNames().length];
+ origValues = new Object[flags.length];
}
origValues[propertyIndex] = value;
}
@@ -652,7 +679,7 @@ public final class EntityBeanIntercept implements Serializable {
public void addDirtyPropertyNames(Set props, String prefix) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
- if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
+ if (isChangedProp(i)) {
// the property has been changed on this bean
props.add((prefix == null ? getProperty(i) : prefix + getProperty(i)));
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
@@ -670,7 +697,7 @@ public final class EntityBeanIntercept implements Serializable {
String[] names = owner._ebean_getPropertyNames();
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
- if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
+ if (isChangedProp(i)) {
if (propertyNames.contains(names[i])) {
return true;
}
@@ -698,7 +725,7 @@ public final class EntityBeanIntercept implements Serializable {
public void addDirtyPropertyValues(Map dirtyValues, String prefix) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
- if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
+ if (isChangedProp(i)) {
// the property has been changed on this bean
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
Object newVal = owner._ebean_getField(i);
@@ -720,7 +747,7 @@ public final class EntityBeanIntercept implements Serializable {
public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
- if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
+ if (isChangedProp(i)) {
// the property has been changed on this bean
Object newVal = owner._ebean_getField(i);
Object oldVal = getOrigValue(i);
@@ -755,7 +782,7 @@ public final class EntityBeanIntercept implements Serializable {
}
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
- if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
+ if ((flags[i] & FLAG_CHANGED_PROP) != 0) { // we do not check against mutablecontent here.
sb.append(i).append(',');
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
@@ -931,10 +958,14 @@ public final class EntityBeanIntercept implements Serializable {
* OneToMany and ManyToMany only set loaded state.
*/
public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
- if (readOnly) {
- throw new IllegalStateException("This bean is readOnly");
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else {
+ if (readOnly) {
+ throw new IllegalStateException("This bean is readOnly");
+ }
+ setChangeLoaded(propertyIndex);
}
- setLoadedProperty(propertyIndex);
}
private void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
@@ -973,7 +1004,6 @@ public final class EntityBeanIntercept implements Serializable {
}
}
-
/**
* Check for primitive boolean.
*/
@@ -1123,7 +1153,7 @@ public final class EntityBeanIntercept implements Serializable {
*/
public void setLoadError(int propertyIndex, Exception t) {
if (loadErrors == null) {
- loadErrors = new Exception[owner._ebean_getPropertyNames().length];
+ loadErrors = new Exception[flags.length];
}
loadErrors[propertyIndex] = t;
flags[propertyIndex] |= FLAG_LOADED_PROP;
@@ -1149,4 +1179,61 @@ public final class EntityBeanIntercept implements Serializable {
}
return ret;
}
+
+ private boolean isChangedProp(int i) {
+ if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
+ return true;
+ } else if (mutableInfo == null || mutableInfo[i] == null || mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) {
+ return false;
+ } else {
+ // mark for change
+ flags[i] |= FLAG_CHANGED_PROP;
+ dirty = true; // this makes the bean automatically dirty!
+ return true;
+ }
+ }
+
+ /**
+ * Return the MutableValueInfo for the given property or null.
+ */
+ public MutableValueInfo mutableInfo(int propertyIndex) {
+ return mutableInfo == null ? null : mutableInfo[propertyIndex];
+ }
+
+ /**
+ * Set the MutableValueInfo for the given property.
+ */
+ public void mutableInfo(int propertyIndex, MutableValueInfo info) {
+ if (mutableInfo == null) {
+ mutableInfo = new MutableValueInfo[flags.length];
+ }
+ flags[propertyIndex] |= FLAG_MUTABLE_HASH_SET;
+ mutableInfo[propertyIndex] = info;
+ }
+
+ /**
+ * Dirty detection set the next mutable property content and info .
+ *
+ * Set here as the mutable property dirty detection is based on json content comparison.
+ * We only want to perform the json serialisation once so storing it here as part of
+ * dirty detection so that we can get it back to bind in insert or update etc.
+ */
+ public void mutableNext(int propertyIndex, MutableValueNext next) {
+ if (mutableNext == null) {
+ mutableNext = new MutableValueNext[flags.length];
+ }
+ mutableNext[propertyIndex] = next;
+ }
+
+ /**
+ * Update the 'next' mutable info returning the content that was obtained via dirty detection.
+ */
+ public String mutableNext(int propertyIndex) {
+ if (mutableNext == null) {
+ return null;
+ }
+ final MutableValueNext next = mutableNext[propertyIndex];
+ return next != null ? next.content() : null;
+ }
+
}
diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java
new file mode 100644
index 000000000..27716b13c
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java
@@ -0,0 +1,42 @@
+package io.ebean.bean;
+
+/**
+ * Holds information on mutable values (like plain beans stored as json).
+ *
+ * Used internally in EntityBeanIntercept for dirty detection on mutable values.
+ * Typically, mutation detection is based on a hash/checksum of json content or the
+ * original json content itself.
+ *
+ * Refer to the mapping options {@code @DbJson(mutationDetection)}.
+ */
+public interface MutableValueInfo {
+
+ /**
+ * Compares the given json returning null if deemed unchanged or returning
+ * the MutableValueNext to use if deemed dirty/changed.
+ *
+ * Returning MutableValueNext allows an implementation based on hash/checksum
+ * to only perform that computation once.
+ *
+ * @return Null if deemed unchanged or the MutableValueNext if deemed changed.
+ */
+ MutableValueNext nextDirty(String json);
+
+ /**
+ * Compares the given object to an internal value.
+ *
+ * This is used to support changelog/beanState. The implementation can serialize the
+ * object into json form and compare it against the original json.
+ */
+ boolean isEqualToObject(Object obj);
+
+ /**
+ * Creates a new instance from the internal json string.
+ *
+ * This is used to provide an original/old value for change logging / persist listeners.
+ * This is only available for properties that have {@code @DbJson(keepSource=true)}.
+ */
+ default Object get() {
+ return null;
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableValueNext.java b/ebean-api/src/main/java/io/ebean/bean/MutableValueNext.java
new file mode 100644
index 000000000..401d3c223
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/bean/MutableValueNext.java
@@ -0,0 +1,17 @@
+package io.ebean.bean;
+
+/**
+ * Represents a next value to use for mutable content properties (DbJson with jackson beans).
+ */
+public interface MutableValueNext {
+
+ /**
+ * Return the next content to use. Provided such that we serialise to json once.
+ */
+ String content();
+
+ /**
+ * Return the next MutableValueInfo to use after an update.
+ */
+ MutableValueInfo info();
+}
diff --git a/ebean-api/src/main/java/io/ebean/bean/PersistenceContext.java b/ebean-api/src/main/java/io/ebean/bean/PersistenceContext.java
index a8a42e51b..9d67499d1 100644
--- a/ebean-api/src/main/java/io/ebean/bean/PersistenceContext.java
+++ b/ebean-api/src/main/java/io/ebean/bean/PersistenceContext.java
@@ -5,7 +5,6 @@ package io.ebean.bean;
*
* This is used to ensure only one instance for a given entity type and id is
* used to build object graphs from queries and lazy loading.
- *
*/
public interface PersistenceContext {
@@ -20,7 +19,6 @@ public interface PersistenceContext {
*
* Returns an existing entity bean (if one is already there) and otherwise
* returns null.
- *
* If a bean has been deleted then for the same persistence context is should
* not be able to be fetched from persistence context or L2 cache.
- *
*/
class WithOption {
diff --git a/ebean-api/src/main/java/io/ebean/bean/SingleBeanLoader.java b/ebean-api/src/main/java/io/ebean/bean/SingleBeanLoader.java
index 4d84ada39..ad4a30b95 100644
--- a/ebean-api/src/main/java/io/ebean/bean/SingleBeanLoader.java
+++ b/ebean-api/src/main/java/io/ebean/bean/SingleBeanLoader.java
@@ -10,7 +10,7 @@ import java.util.concurrent.locks.ReentrantLock;
*/
public abstract class SingleBeanLoader implements BeanLoader {
- private final ReentrantLock lock = new ReentrantLock(false);
+ private final ReentrantLock lock = new ReentrantLock();
protected final Database database;
diff --git a/ebean-api/src/main/java/io/ebean/common/AbstractBeanCollection.java b/ebean-api/src/main/java/io/ebean/common/AbstractBeanCollection.java
index 297c6fb9d..809ca13b3 100644
--- a/ebean-api/src/main/java/io/ebean/common/AbstractBeanCollection.java
+++ b/ebean-api/src/main/java/io/ebean/common/AbstractBeanCollection.java
@@ -16,7 +16,7 @@ abstract class AbstractBeanCollection implements BeanCollection {
private static final long serialVersionUID = 3365725236140187588L;
- protected final ReentrantLock lock = new ReentrantLock(false);
+ protected final ReentrantLock lock = new ReentrantLock();
protected boolean readOnly;
@@ -135,6 +135,11 @@ abstract class AbstractBeanCollection implements BeanCollection {
// Support for modify additions deletions etc - ManyToMany
// ---------------------------------------------------------
+ @Override
+ public boolean hasModifications() {
+ return modifyHolder != null && modifyHolder.hasModifications();
+ }
+
@Override
public ModifyListenMode getModifyListening() {
return modifyListenMode;
@@ -145,7 +150,6 @@ abstract class AbstractBeanCollection implements BeanCollection {
*/
@Override
public void setModifyListening(ModifyListenMode mode) {
-
this.modifyListenMode = mode;
this.modifyListening = mode != null && ModifyListenMode.NONE != mode;
if (modifyListening) {
diff --git a/ebean-api/src/main/java/io/ebean/common/BeanList.java b/ebean-api/src/main/java/io/ebean/common/BeanList.java
index 0aee7f211..ba0adca99 100644
--- a/ebean-api/src/main/java/io/ebean/common/BeanList.java
+++ b/ebean-api/src/main/java/io/ebean/common/BeanList.java
@@ -87,7 +87,7 @@ public final class BeanList extends AbstractBeanCollection implements List
@Override
public void internalAddWithCheck(Object bean) {
- if (list == null || !containsInstance(bean)) {
+ if (list == null || bean == null || !containsInstance(bean)) {
internalAdd(bean);
}
}
@@ -198,10 +198,9 @@ public final class BeanList extends AbstractBeanCollection implements List
}
if (list == null) {
sb.append("deferred ");
-
} else {
sb.append("size[").append(list.size()).append("] ");
- sb.append("list").append(list).append("");
+ sb.append("list").append(list);
}
return sb.toString();
}
diff --git a/ebean-api/src/main/java/io/ebean/common/BeanMap.java b/ebean-api/src/main/java/io/ebean/common/BeanMap.java
index b2ded02c9..643febc14 100644
--- a/ebean-api/src/main/java/io/ebean/common/BeanMap.java
+++ b/ebean-api/src/main/java/io/ebean/common/BeanMap.java
@@ -77,7 +77,7 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
}
public void internalPutWithCheck(Object key, Object bean) {
- if (map == null || !map.containsKey(key)) {
+ if (map == null || key == null || !map.containsKey(key)) {
internalPut(key, bean);
}
}
@@ -175,10 +175,6 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
/**
* Returns the map entrySet.
- *
- * This is because the key values may need to be set against the details (so
- * they don't need to be set twice).
- *
*/
@Override
public Collection> getActualEntries() {
@@ -194,7 +190,6 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
}
if (map == null) {
sb.append("deferred ");
-
} else {
sb.append("size[").append(map.size()).append("]");
sb.append(" map").append(map);
@@ -243,17 +238,12 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
}
@Override
- @SuppressWarnings({"unchecked"})
public Set> entrySet() {
init();
if (isReadOnly()) {
return Collections.unmodifiableSet(map.entrySet());
}
- if (modifyListening) {
- Set> s = map.entrySet();
- return new ModifySet(this, s);
- }
- return map.entrySet();
+ return modifyListening ? new ModifyEntrySet<>(this, map.entrySet()) : map.entrySet();
}
@Override
@@ -274,8 +264,7 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
if (isReadOnly()) {
return Collections.unmodifiableSet(map.keySet());
}
- // we don't really care about modifications to the ketSet?
- return map.keySet();
+ return modifyListening ? new ModifyKeySet<>(this, map.keySet()) : map.keySet();
}
@Override
@@ -346,11 +335,7 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
if (isReadOnly()) {
return Collections.unmodifiableCollection(map.values());
}
- if (modifyListening) {
- Collection c = map.values();
- return new ModifyCollection<>(this, c);
- }
- return map.values();
+ return modifyListening ? new ModifyCollection<>(this, map.values()) : map.values();
}
@Override
diff --git a/ebean-api/src/main/java/io/ebean/common/BeanSet.java b/ebean-api/src/main/java/io/ebean/common/BeanSet.java
index ec4dc3829..0c6f0c508 100644
--- a/ebean-api/src/main/java/io/ebean/common/BeanSet.java
+++ b/ebean-api/src/main/java/io/ebean/common/BeanSet.java
@@ -70,9 +70,8 @@ public final class BeanSet extends AbstractBeanCollection implements Set extends AbstractBeanCollection implements Set extends AbstractList implements Li
private static final long serialVersionUID = 1L;
- private final ReentrantLock lock = new ReentrantLock(false);
+ private final ReentrantLock lock = new ReentrantLock();
/**
* The underlying List implementation.
diff --git a/ebean-api/src/main/java/io/ebean/common/ModifyCollection.java b/ebean-api/src/main/java/io/ebean/common/ModifyCollection.java
index 05b203a2f..48f703ba8 100644
--- a/ebean-api/src/main/java/io/ebean/common/ModifyCollection.java
+++ b/ebean-api/src/main/java/io/ebean/common/ModifyCollection.java
@@ -25,7 +25,7 @@ class ModifyCollection implements Collection {
* The owner is notified of the additions and removals.
*
*/
- public ModifyCollection(BeanCollection owner, Collection c) {
+ ModifyCollection(BeanCollection owner, Collection c) {
this.owner = owner;
this.c = c;
}
diff --git a/ebean-api/src/main/java/io/ebean/common/ModifyEntrySet.java b/ebean-api/src/main/java/io/ebean/common/ModifyEntrySet.java
new file mode 100644
index 000000000..174863a74
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/common/ModifyEntrySet.java
@@ -0,0 +1,131 @@
+package io.ebean.common;
+
+import java.util.*;
+
+/**
+ * Handles the Entry Set for BeanMap.
+ */
+class ModifyEntrySet implements Set> {
+
+ private final BeanMap owner;
+ private final Set> entrySet;
+
+ ModifyEntrySet(BeanMap owner, Set> entrySet) {
+ this.owner = owner;
+ this.entrySet = entrySet;
+ }
+
+ @Override
+ public int size() {
+ return entrySet.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return entrySet.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return entrySet.contains(o);
+ }
+
+ @Override
+ public Object[] toArray() {
+ return entrySet.toArray();
+ }
+
+ @Override
+ public T[] toArray(T[] a) {
+ return entrySet.toArray(a);
+ }
+
+ @Override
+ public boolean containsAll(Collection> entries) {
+ return entrySet.containsAll(entries);
+ }
+
+ @Override
+ public void clear() {
+ owner.clear();
+ }
+
+ @Override
+ public boolean add(Map.Entry entry) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection extends Map.Entry> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public boolean remove(Object o) {
+ if (o instanceof Map.Entry) {
+ Map.Entry entry = (Map.Entry) o;
+ final E val = owner.get(entry.getKey());
+ if (Objects.equals(val, entry.getValue())) {
+ owner.remove(entry.getKey());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean retainAll(Collection> entries) {
+ boolean modified = false;
+ final Iterator> it = iterator();
+ while (it.hasNext()) {
+ if (!entries.contains(it.next())) {
+ it.remove();
+ modified = true;
+ }
+ }
+ return modified;
+ }
+
+ @Override
+ public boolean removeAll(Collection> entries) {
+ boolean modified = false;
+ for (Object entry : entries) {
+ modified |= remove(entry);
+ }
+ return modified;
+ }
+
+ @Override
+ public Iterator> iterator() {
+ return new EntrySetIterator(new ArrayList<>(entrySet).iterator());
+ }
+
+ class EntrySetIterator implements Iterator> {
+
+ private final Iterator> iterator;
+ private Map.Entry entry;
+
+ EntrySetIterator(Iterator> iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public Map.Entry next() {
+ entry = iterator.next();
+ return entry;
+ }
+
+ @Override
+ public void remove() {
+ owner.remove(entry.getKey());
+ iterator.remove();
+ }
+ }
+
+}
diff --git a/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java b/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java
index 6da14d3a5..957b0fa88 100644
--- a/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java
+++ b/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java
@@ -3,9 +3,7 @@ package io.ebean.common;
import io.ebean.bean.EntityBean;
import java.io.Serializable;
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.Set;
+import java.util.*;
/**
* Holds sets of additions and deletions from a 'owner' List Set or Map.
@@ -23,19 +21,19 @@ class ModifyHolder implements Serializable {
/**
* Deletions list for manyToMany persistence.
*/
- private Set modifyDeletions = new LinkedHashSet<>();
+ private Map modifyDeletions = new IdentityHashMap<>();
/**
* Additions list for manyToMany persistence.
*/
- private Set modifyAdditions = new LinkedHashSet<>();
+ private Map modifyAdditions = new IdentityHashMap<>();
private boolean touched;
void reset() {
touched = false;
- modifyDeletions = new LinkedHashSet<>();
- modifyAdditions = new LinkedHashSet<>();
+ modifyDeletions = new IdentityHashMap<>();
+ modifyAdditions = new IdentityHashMap<>();
}
/**
@@ -50,51 +48,46 @@ class ModifyHolder implements Serializable {
}
private boolean undoDeletion(E bean) {
- return (bean != null) && modifyDeletions.remove(bean);
+ return (bean != null) && modifyDeletions.remove(bean) != null;
}
void modifyAddition(E bean) {
if (bean != null) {
touched = true;
-
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(false);
}
-
// If it is to delete then just remove the deletion
if (!undoDeletion(bean)) {
- // Insert
- modifyAdditions.add(bean);
+ modifyAdditions.put(bean, bean);
}
}
}
private boolean undoAddition(Object bean) {
- return (bean != null) && modifyAdditions.remove(bean);
+ return (bean != null) && modifyAdditions.remove(bean) != null;
}
@SuppressWarnings("unchecked")
void modifyRemoval(Object bean) {
if (bean != null) {
touched = true;
-
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(true);
}
-
// If it is to be added then just remove the addition
if (!undoAddition(bean)) {
- modifyDeletions.add((E) bean);
+ modifyDeletions.put((E) bean, bean);
}
}
}
Set getModifyAdditions() {
- return modifyAdditions;
+ return modifyAdditions.keySet();
}
Set getModifyRemovals() {
- return modifyDeletions;
+ return modifyDeletions.keySet();
}
/**
diff --git a/ebean-api/src/main/java/io/ebean/common/ModifyKeySet.java b/ebean-api/src/main/java/io/ebean/common/ModifyKeySet.java
new file mode 100644
index 000000000..e9f829b9b
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/common/ModifyKeySet.java
@@ -0,0 +1,126 @@
+package io.ebean.common;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Handle the Key Set for BeanMap.
+ */
+class ModifyKeySet implements Set {
+
+ private final Set keySet;
+ private final BeanMap owner;
+
+ ModifyKeySet(BeanMap owner, Set keySet) {
+ this.owner = owner;
+ this.keySet = keySet;
+ }
+
+ @Override
+ public int size() {
+ return keySet.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return keySet.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return keySet.contains(o);
+ }
+
+ @Override
+ public Object[] toArray() {
+ return keySet.toArray();
+ }
+
+ @Override
+ public T[] toArray(T[] a) {
+ return keySet.toArray(a);
+ }
+
+ @Override
+ public boolean add(E key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection extends E> keys) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return owner.remove(o) != null;
+ }
+
+ @Override
+ public boolean containsAll(Collection> keys) {
+ return keySet.containsAll(keys);
+ }
+
+ @Override
+ public void clear() {
+ owner.clear();
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new KeySetIterator<>(new ArrayList<>(keySet).iterator());
+ }
+
+ @Override
+ public boolean retainAll(Collection> keys) {
+ return keysMatch(keys, false);
+ }
+
+ @Override
+ public boolean removeAll(Collection> keys) {
+ return keysMatch(keys, true);
+ }
+
+ private boolean keysMatch(Collection> keys, boolean containsMatch) {
+ boolean changed = false;
+ final Iterator iterator = iterator();
+ while (iterator.hasNext()) {
+ final E key = iterator.next();
+ if (keys.contains(key) == containsMatch) {
+ iterator.remove();
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+
+ class KeySetIterator implements Iterator {
+
+ private final Iterator iterator;
+ private K key;
+
+ KeySetIterator(Iterator iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public K next() {
+ key = iterator.next();
+ return key;
+ }
+
+ @Override
+ public void remove() {
+ owner.remove(key);
+ iterator.remove();
+ }
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/common/ModifySet.java b/ebean-api/src/main/java/io/ebean/common/ModifySet.java
deleted file mode 100644
index 1a0a2e1bf..000000000
--- a/ebean-api/src/main/java/io/ebean/common/ModifySet.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package io.ebean.common;
-
-import io.ebean.bean.BeanCollection;
-
-import java.util.Set;
-
-/**
- * Wraps a Set for the purposes of notifying removals and additions to the
- * BeanCollection owner.
- *
- * This is required for persisting ManyToMany objects. Additions and removals
- * become inserts and deletes to the intersection table.
- *
- */
-class ModifySet extends ModifyCollection implements Set {
-
- /**
- * Create with an Owner that is notified of any additions or deletions.
- */
- public ModifySet(BeanCollection owner, Set s) {
- super(owner, s);
- }
-
-}
diff --git a/ebean-api/src/main/java/io/ebean/config/AbstractNamingConvention.java b/ebean-api/src/main/java/io/ebean/config/AbstractNamingConvention.java
index 9bb65ccce..33d607e97 100644
--- a/ebean-api/src/main/java/io/ebean/config/AbstractNamingConvention.java
+++ b/ebean-api/src/main/java/io/ebean/config/AbstractNamingConvention.java
@@ -7,6 +7,8 @@ import javax.persistence.DiscriminatorValue;
import javax.persistence.Inheritance;
import javax.persistence.Table;
+import static io.ebean.util.StringHelper.isNull;
+
/**
* Provides some base implementation for NamingConventions.
*
@@ -78,10 +80,16 @@ public abstract class AbstractNamingConvention implements NamingConvention {
@Override
public String getSequenceName(String rawTableName, String pkColumn) {
- final String tableNameUnquoted = databasePlatform.unQuote(rawTableName);
+ TableName tableName = new TableName(rawTableName);
+ String seqName = seqName(pkColumn, tableName.getName());
+ return tableName.withCatalogAndSchema(seqName);
+ }
+
+ private String seqName(String pkColumn, String tableName) {
+ final String tableNameUnquoted = unQuote(tableName);
String seqName = sequenceFormat.replace("{table}", tableNameUnquoted);
- pkColumn = (pkColumn == null) ? "" : databasePlatform.unQuote(pkColumn);
- return seqName.replace("{column}", pkColumn);
+ pkColumn = (pkColumn == null) ? "" : unQuote(pkColumn);
+ return quoteIdentifiers(seqName.replace("{column}", pkColumn));
}
/**
@@ -216,15 +224,13 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|| AnnotationUtil.has(supCls, DiscriminatorValue.class);
}
-
@Override
public TableName getM2MJoinTableName(TableName lhsTable, TableName rhsTable) {
-
StringBuilder buffer = new StringBuilder();
- buffer.append(lhsTable.getName());
+ buffer.append(unQuote(lhsTable.getName()));
buffer.append("_");
- String rhsTableName = rhsTable.getName();
+ String rhsTableName = unQuote(rhsTable.getName());
if (rhsTableName.indexOf('_') < rhsPrefixLength) {
// trim off a xx_ prefix if there is one
rhsTableName = rhsTableName.substring(rhsTableName.indexOf('_') + 1);
@@ -238,7 +244,13 @@ public abstract class AbstractNamingConvention implements NamingConvention {
buffer.setLength(maxTableNameLength);
}
- return new TableName(lhsTable.getCatalog(), lhsTable.getSchema(), buffer.toString());
+ String tableName = quoteIdentifiers(buffer.toString());
+ return new TableName(lhsTable.getCatalog(), lhsTable.getSchema(), tableName);
+ }
+
+ @Override
+ public String deriveM2MColumn(String tableName, String dbColumn) {
+ return quoteIdentifiers(unQuote(tableName) +"_" + unQuote(dbColumn));
}
/**
@@ -255,14 +267,29 @@ public abstract class AbstractNamingConvention implements NamingConvention {
return null;
}
+ @Override
+ public String getTableName(String catalog, String schema, String name) {
+ StringBuilder sb = new StringBuilder();
+ if (!isNull(catalog)) {
+ sb.append(quoteIdentifiers(catalog)).append(".");
+ }
+ if (!isNull(schema)) {
+ sb.append(quoteIdentifiers(schema)).append(".");
+ }
+ return sb.append(quoteIdentifiers(name)).toString();
+ }
+
/**
- * Replace back ticks (if they are used) with database platform specific
- * quoted identifiers.
+ * Replace back ticks (if they are used) with database platform specific quoted identifiers.
*/
protected String quoteIdentifiers(String s) {
return databasePlatform.convertQuotedIdentifiers(s);
}
+ private String unQuote(String val) {
+ return databasePlatform.unQuote(val);
+ }
+
/**
* Checks string is null or empty .
*/
diff --git a/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java b/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java
index 821905405..764fa86a1 100644
--- a/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java
+++ b/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java
@@ -40,6 +40,13 @@ public class ClassLoadConfig {
return isPresent("javax.validation.constraints.NotNull");
}
+ /**
+ * Return true if jakarta validation annotations like Size and NotNull are present.
+ */
+ public boolean isJakartaValidationAnnotationsPresent() {
+ return isPresent("jakarta.validation.constraints.NotNull");
+ }
+
/**
* Return true if javax PostConstruct annotation is present (maybe not in java9).
* If not we don't support PostConstruct lifecycle events.
diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java
index 7ea8e56e9..873304c3a 100644
--- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java
+++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java
@@ -7,9 +7,7 @@ import io.ebean.EbeanVersion;
import io.ebean.PersistenceContextScope;
import io.ebean.Query;
import io.ebean.Transaction;
-import io.ebean.annotation.Encrypted;
-import io.ebean.annotation.PersistBatch;
-import io.ebean.annotation.Platform;
+import io.ebean.annotation.*;
import io.ebean.cache.ServerCachePlugin;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbEncrypt;
@@ -193,6 +191,11 @@ public class DatabaseConfig {
*/
private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL;
+ /**
+ * The default mode used for {@code @DbJson} with Jackson ObjectMapper.
+ */
+ private MutationDetection jsonMutationDetection = MutationDetection.HASH;
+
/**
* The database platform name. Used to imply a DatabasePlatform to use.
*/
@@ -232,6 +235,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.
*/
@@ -310,6 +319,8 @@ public class DatabaseConfig {
*/
private ExternalTransactionManager externalTransactionManager;
+ private boolean skipDataSourceCheck;
+
/**
* The data source (if programmatically provided).
*/
@@ -489,7 +500,7 @@ public class DatabaseConfig {
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
* If set to false, use io.ebean.annotation.NotNull or Column(nullable=true).
*/
- private boolean useJavaxValidationNotNull = true;
+ private boolean useValidationNotNull = true;
/**
* Generally we want to perform L2 cache notification in the background and not impact
@@ -498,14 +509,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.
@@ -722,6 +742,24 @@ public class DatabaseConfig {
this.jsonInclude = jsonInclude;
}
+ /**
+ * Return the default MutableDetection to use with {@code @DbJson} using Jackson.
+ *
+ * @see DbJson#mutationDetection()
+ */
+ public MutationDetection getJsonMutationDetection() {
+ return jsonMutationDetection;
+ }
+
+ /**
+ * Set the default MutableDetection to use with {@code @DbJson} using Jackson.
+ *
+ * @see DbJson#mutationDetection()
+ */
+ public void setJsonMutationDetection(MutationDetection jsonMutationDetection) {
+ this.jsonMutationDetection = jsonMutationDetection;
+ }
+
/**
* Return the name of the Database.
*/
@@ -896,6 +934,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.
*
@@ -1045,7 +1097,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);
@@ -1602,6 +1653,20 @@ public class DatabaseConfig {
this.autoTuneConfig = autoTuneConfig;
}
+ /**
+ * Return true if the startup DataSource check should be skipped.
+ */
+ public boolean skipDataSourceCheck() {
+ return skipDataSourceCheck;
+ }
+
+ /**
+ * Set to true to skip the startup DataSource check.
+ */
+ public void setSkipDataSourceCheck(boolean skipDataSourceCheck) {
+ this.skipDataSourceCheck = skipDataSourceCheck;
+ }
+
/**
* Return the DataSource.
*/
@@ -2693,7 +2758,10 @@ public class DatabaseConfig {
}
/**
- * Load settings from ebean.properties.
+ * Load settings from application.properties, application.yaml and other sources.
+ *
+ * Uses avaje-config 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();
@@ -2795,21 +2863,27 @@ 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);
enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
- useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
+ useValidationNotNull = p.getBoolean("useValidationNotNull", useValidationNotNull);
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
idGeneratorAutomatic = p.getBoolean("idGeneratorAutomatic", idGeneratorAutomatic);
@@ -2872,7 +2946,9 @@ public class DatabaseConfig {
jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude);
jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime);
jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate);
+ jsonMutationDetection = p.getEnum(MutationDetection.class, "jsonMutationDetection", jsonMutationDetection);
+ skipDataSourceCheck = p.getBoolean("skipDataSourceCheck", skipDataSourceCheck);
runMigration = p.getBoolean("migration.run", runMigration);
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
@@ -3056,20 +3132,21 @@ public class DatabaseConfig {
/**
* Returns if we use javax.validation.constraints.NotNull
*/
- public boolean isUseJavaxValidationNotNull() {
- return useJavaxValidationNotNull;
+ public boolean isUseValidationNotNull() {
+ return useValidationNotNull;
}
/**
- * Controls if Ebean should ignore &x64;javax.validation.contstraints.NotNull
+ * Controls if Ebean should ignore &x64;javax.validation.contstraints.NotNull or
+ * &x64;jakarta.validation.contstraints.NotNull
* with respect to generating a NOT NULL column.
*
* Normally when Ebean sees javax NotNull annotation it means that column is defined as NOT NULL.
* Set this to false and the javax NotNull annotation is effectively ignored (and
* we instead use Ebean's own NotNull annotation or JPA Column(nullable=false) annotation.
*/
- public void setUseJavaxValidationNotNull(boolean useJavaxValidationNotNull) {
- this.useJavaxValidationNotNull = useJavaxValidationNotNull;
+ public void setUseValidationNotNull(boolean useValidationNotNull) {
+ this.useValidationNotNull = useValidationNotNull;
}
/**
@@ -3091,14 +3168,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.
+ *
+ * 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;
@@ -3171,29 +3251,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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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;
}
/**
diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfigProvider.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfigProvider.java
index 0a1f3e768..066af5fc4 100644
--- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfigProvider.java
+++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfigProvider.java
@@ -15,7 +15,7 @@ package io.ebean.config;
*
* public class EbeanConfigProvider implements DatabaseConfigProvider {
*
- * @Override
+ * @Override
* public void apply(DatabaseConfig config) {
*
* // register the entity bean classes explicitly
diff --git a/ebean-api/src/main/java/io/ebean/config/MatchingNamingConvention.java b/ebean-api/src/main/java/io/ebean/config/MatchingNamingConvention.java
index 0b741994a..d12cb2e80 100644
--- a/ebean-api/src/main/java/io/ebean/config/MatchingNamingConvention.java
+++ b/ebean-api/src/main/java/io/ebean/config/MatchingNamingConvention.java
@@ -39,11 +39,6 @@ public class MatchingNamingConvention extends AbstractNamingConvention {
return new TableName(quoteIdentifiers(getCatalog()), quoteIdentifiers(getSchema()), quoteIdentifiers(beanClass.getSimpleName()));
}
- @Override
- public String getPropertyFromColumn(Class> beanClass, String dbColumnName) {
- return dbColumnName;
- }
-
@Override
public String getForeignKey(String prefix, String fkProperty) {
prefix = databasePlatform.unQuote(prefix);
diff --git a/ebean-api/src/main/java/io/ebean/config/ModuleInfoLoader.java b/ebean-api/src/main/java/io/ebean/config/ModuleInfoLoader.java
index d32f80630..94d35ab2c 100644
--- a/ebean-api/src/main/java/io/ebean/config/ModuleInfoLoader.java
+++ b/ebean-api/src/main/java/io/ebean/config/ModuleInfoLoader.java
@@ -7,13 +7,8 @@ import java.util.List;
*/
public interface ModuleInfoLoader {
- /**
- * Return the entity classes to register with the default DB.
- */
- List> entityClasses();
-
/**
* Return entity classes to register for a named DB (not default DB).
*/
- List> entityClassesFor(String dbName);
+ List> classesFor(String dbName, boolean defaultServer);
}
diff --git a/ebean-api/src/main/java/io/ebean/config/NamingConvention.java b/ebean-api/src/main/java/io/ebean/config/NamingConvention.java
index c5a0a2b86..470822e5b 100644
--- a/ebean-api/src/main/java/io/ebean/config/NamingConvention.java
+++ b/ebean-api/src/main/java/io/ebean/config/NamingConvention.java
@@ -53,6 +53,16 @@ public interface NamingConvention {
*/
TableName getM2MJoinTableName(TableName lhsTable, TableName rhsTable);
+ /**
+ * Derive a DB Column from a FK table and column.
+ */
+ String deriveM2MColumn(String tableName, String dbColumn);
+
+ /**
+ * Return the full table name taking into account quoted identifiers.
+ */
+ String getTableName(String catalog, String schema, String name);
+
/**
* Return the column name given the property name.
*
@@ -60,18 +70,6 @@ public interface NamingConvention {
*/
String getColumnFromProperty(Class> beanClass, String propertyName);
- /**
- * Return the property name from the column name.
- *
- * This is used to help mapping of raw SQL queries onto bean properties.
- *
- *
- * @param beanClass the bean class
- * @param dbColumnName the db column name
- * @return the property name from the column name
- */
- String getPropertyFromColumn(Class> beanClass, String dbColumnName);
-
/**
* Return the sequence name given the table name (for DB's that use sequences).
*
diff --git a/ebean-api/src/main/java/io/ebean/config/QueryPlanCapture.java b/ebean-api/src/main/java/io/ebean/config/QueryPlanCapture.java
new file mode 100644
index 000000000..6f34097fe
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/QueryPlanCapture.java
@@ -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 plans;
+
+ public QueryPlanCapture(Database database, List 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 getPlans() {
+ return plans;
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/config/QueryPlanListener.java b/ebean-api/src/main/java/io/ebean/config/QueryPlanListener.java
new file mode 100644
index 000000000..8368186cf
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/QueryPlanListener.java
@@ -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);
+}
diff --git a/ebean-api/src/main/java/io/ebean/config/ServerConfigProvider.java b/ebean-api/src/main/java/io/ebean/config/ServerConfigProvider.java
index f992b004d..1ba45907d 100644
--- a/ebean-api/src/main/java/io/ebean/config/ServerConfigProvider.java
+++ b/ebean-api/src/main/java/io/ebean/config/ServerConfigProvider.java
@@ -16,7 +16,7 @@ package io.ebean.config;
*
* public class EbeanConfigProvider implements ServerConfigProvider {
*
- * @Override
+ * @Override
* public void apply(ServerConfig config) {
*
* // register the entity bean classes explicitly
diff --git a/ebean-api/src/main/java/io/ebean/config/TableName.java b/ebean-api/src/main/java/io/ebean/config/TableName.java
index dba8b1d0d..9f55577b3 100644
--- a/ebean-api/src/main/java/io/ebean/config/TableName.java
+++ b/ebean-api/src/main/java/io/ebean/config/TableName.java
@@ -20,7 +20,7 @@ public final class TableName {
/**
* The name.
*/
- private String name;
+ private final String name;
/**
* Construct with the given catalog schema and table name.
@@ -29,7 +29,6 @@ public final class TableName {
*
*/
public TableName(String catalog, String schema, String name) {
- super();
this.catalog = catalog != null ? catalog.trim() : null;
this.schema = schema != null ? schema.trim() : null;
this.name = name != null ? name.trim() : null;
@@ -110,14 +109,11 @@ public final class TableName {
* @return the qualified name
*/
public String getQualifiedName() {
-
StringBuilder buffer = new StringBuilder();
-
// Add catalog
if (catalog != null) {
buffer.append(catalog);
}
-
// Add schema
if (schema != null) {
if (buffer.length() > 0) {
@@ -125,31 +121,27 @@ public final class TableName {
}
buffer.append(schema);
}
-
if (buffer.length() > 0) {
buffer.append(".");
}
- buffer.append(name);
-
- return buffer.toString();
+ return buffer.append(name).toString();
}
/**
* Append a catalog and schema prefix if they exist to the string builder.
*/
- public void appendCatalogAndSchema(StringBuilder buffer) {
- if (catalog != null) {
- buffer.append(catalog).append(".");
- }
+ public String withCatalogAndSchema(String name) {
if (schema != null) {
- buffer.append(schema).append(".");
+ name = schema + "." + name;
}
+ if (catalog != null) {
+ name = catalog + "." + name;
+ }
+ return name;
}
/**
* Checks if is table name is valid i.e. it has at least a name.
- *
- * @return true, if is valid
*/
public boolean isValid() {
return name != null && !name.isEmpty();
diff --git a/ebean-api/src/main/java/io/ebean/config/UnderscoreNamingConvention.java b/ebean-api/src/main/java/io/ebean/config/UnderscoreNamingConvention.java
index 16b685227..603e4e76c 100644
--- a/ebean-api/src/main/java/io/ebean/config/UnderscoreNamingConvention.java
+++ b/ebean-api/src/main/java/io/ebean/config/UnderscoreNamingConvention.java
@@ -60,18 +60,6 @@ public class UnderscoreNamingConvention extends AbstractNamingConvention {
return toUnderscoreFromCamel(propertyName);
}
- /**
- * Converts underscore based column name to Camel case property name.
- *
- * @param beanClass the bean class
- * @param dbColumnName the db column name
- * @return the property from column
- */
- @Override
- public String getPropertyFromColumn(Class> beanClass, String dbColumnName) {
- return toCamelFromUnderscore(dbColumnName);
- }
-
/**
* Return true if the result will be upper case.
*
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/AbstractDbEncrypt.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/AbstractDbEncrypt.java
index be2c0941e..6f8e27556 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/AbstractDbEncrypt.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/AbstractDbEncrypt.java
@@ -9,9 +9,6 @@ import java.sql.Types;
* functions for varchar, date and timestamp. If they are left null then that is
* treated as though that data type can not be encrypted in the DB and will
* instead use java client encryption.
- *
- *
- * @author rbygrave
*/
public abstract class AbstractDbEncrypt implements DbEncrypt {
@@ -47,13 +44,10 @@ public abstract class AbstractDbEncrypt implements DbEncrypt {
case Types.CHAR:
case Types.LONGVARCHAR:
return varcharEncryptFunction;
-
case Types.DATE:
return dateEncryptFunction;
-
case Types.TIMESTAMP:
return timestampEncryptFunction;
-
default:
return null;
}
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/DbDefaultValue.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/DbDefaultValue.java
index b74465f35..851fc2707 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/DbDefaultValue.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/DbDefaultValue.java
@@ -79,7 +79,7 @@ public class DbDefaultValue {
}
/**
- * This method checks & convert the {@link DbDefault#value()} to a valid SQL literal.
+ * This method checks and converts the {@link DbDefault#value()} to a valid SQL literal.
*
* This is mainly to quote string literals and verify integer/dates for correctness.
*
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java
index 351d1e8b8..7ddcef6f3 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java
@@ -26,9 +26,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
protected static final Logger logger = LoggerFactory.getLogger("io.ebean.SEQ");
- private final ReentrantLock lock = new ReentrantLock(false);
-
- private final ReentrantLock loadLock = new ReentrantLock(false);
+ private final ReentrantLock lock = new ReentrantLock();
/**
* The actual sequence name.
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2DbEncrypt.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2DbEncrypt.java
index ec7b1d9a7..f3eeeffa0 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2DbEncrypt.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2DbEncrypt.java
@@ -5,8 +5,6 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
/**
* H2 encryption support via encrypt decrypt function.
- *
- * @author rbygrave
*/
public class H2DbEncrypt extends AbstractDbEncrypt {
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2HistoryTrigger.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2HistoryTrigger.java
index 785e24a0c..323dd1120 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2HistoryTrigger.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2HistoryTrigger.java
@@ -4,11 +4,7 @@ import org.h2.api.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Timestamp;
+import java.sql.*;
import java.util.Arrays;
/**
@@ -43,7 +39,6 @@ public class H2HistoryTrigger implements Trigger {
@Override
public void init(Connection conn, String schemaName, String triggerName, String tableName, boolean before, int type) throws SQLException {
-
// get the columns for the table
ResultSet rs = conn.getMetaData().getColumns(null, schemaName, tableName, null);
@@ -79,7 +74,6 @@ public class H2HistoryTrigger implements Trigger {
@Override
public void fire(Connection connection, Object[] oldRow, Object[] newRow) throws SQLException {
-
if (oldRow != null) {
// a delete or update event
Timestamp now = new Timestamp(System.currentTimeMillis());
@@ -99,7 +93,6 @@ public class H2HistoryTrigger implements Trigger {
* Insert the data into the history table.
*/
private void insertIntoHistory(Connection connection, Object[] oldRow) throws SQLException {
-
try (PreparedStatement stmt = connection.prepareStatement(insertHistorySql)) {
for (int i = 0; i < oldRow.length; i++) {
stmt.setObject(i + 1, oldRow[i]);
@@ -110,11 +103,11 @@ public class H2HistoryTrigger implements Trigger {
@Override
public void close() throws SQLException {
-
+ // do nothing
}
@Override
public void remove() throws SQLException {
-
+ // do nothing
}
}
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/mysql/MySqlDbEncrypt.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/mysql/MySqlDbEncrypt.java
index 2a22d8ab2..f8717936b 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/mysql/MySqlDbEncrypt.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/mysql/MySqlDbEncrypt.java
@@ -5,8 +5,6 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
/**
* MySql aes_encrypt aes_decrypt based encryption support.
- *
- * @author rbygrave
*/
public class MySqlDbEncrypt extends AbstractDbEncrypt {
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java
index e13155f3f..5934b6dd4 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java
@@ -11,8 +11,8 @@ public class Oracle11Platform extends OraclePlatform {
public Oracle11Platform() {
super();
this.platform = Platform.ORACLE11;
- this.columnAliasPrefix = "c";
this.sqlLimiter = new OracleRownumSqlLimiter();
+ this.basicSqlLimiter = new OracleRownumBasicLimiter();
dbIdentity.setIdType(IdType.SEQUENCE);
}
}
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleDbEncrypt.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleDbEncrypt.java
index e1194ac37..2eae251a9 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleDbEncrypt.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleDbEncrypt.java
@@ -60,7 +60,6 @@ public class OracleDbEncrypt extends AbstractDbEncrypt {
* @param decryptFunction the decrypt stored procedure
*/
public OracleDbEncrypt(String encryptFunction, String decryptFunction) {
-
this.varcharEncryptFunction = new OraVarcharFunction(encryptFunction, decryptFunction);
this.dateEncryptFunction = new OraDateFunction(encryptFunction, decryptFunction);
}
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java
index c12ca8c74..d6896ad3e 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java
@@ -22,6 +22,7 @@ public class OraclePlatform extends DatabasePlatform {
public OraclePlatform() {
super();
this.platform = Platform.ORACLE;
+ this.columnAliasPrefix = "c";
this.supportsDeleteTableAlias = true;
this.maxTableNameLength = 30;
this.maxConstraintNameLength = 30;
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumBasicLimiter.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumBasicLimiter.java
new file mode 100644
index 000000000..e51df98cb
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumBasicLimiter.java
@@ -0,0 +1,35 @@
+package io.ebean.config.dbplatform.oracle;
+
+import io.ebean.config.dbplatform.BasicSqlLimiter;
+
+/**
+ * Row limiter for Oracle 9,10,11 using rownum.
+ */
+public class OracleRownumBasicLimiter implements BasicSqlLimiter {
+
+ @Override
+ public String limit(String dbSql, int firstRow, int maxRows) {
+ if (firstRow < 1 && maxRows < 1) {
+ return dbSql;
+ }
+ StringBuilder sb = new StringBuilder(60 + dbSql.length());
+ int lastRow = maxRows;
+ if (lastRow > 0) {
+ lastRow += firstRow;
+ }
+ sb.append("select * from (select ");
+ if (maxRows > 0) {
+ sb.append("/*+ FIRST_ROWS(").append(maxRows).append(") */ ");
+ }
+ sb.append("a.*, rownum rn_ from (");
+ sb.append(dbSql).append(") a ");
+ if (lastRow > 0) {
+ sb.append(" where rownum <= ").append(lastRow);
+ }
+ sb.append(") ");
+ if (firstRow > 0) {
+ sb.append(" where rn_ > ").append(firstRow);
+ }
+ return sb.toString();
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java
index 8a9c5b4be..4490c148d 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java
@@ -25,6 +25,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
this.platform = Platform.SQLSERVER;
// disable persistBatchOnCascade mode for
// SQL Server unless we are using sequences
+ this.dbEncrypt = new SqlServerDbEncrypt();
this.persistBatchOnCascade = PersistBatch.NONE;
this.idInExpandedForm = true;
this.selectCountWithAlias = true;
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerDbEncrypt.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerDbEncrypt.java
new file mode 100644
index 000000000..5647483cd
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerDbEncrypt.java
@@ -0,0 +1,46 @@
+package io.ebean.config.dbplatform.sqlserver;
+
+import io.ebean.config.dbplatform.AbstractDbEncrypt;
+import io.ebean.config.dbplatform.DbEncryptFunction;
+
+/**
+ * SQL Server EncryptByPassPhrase DecryptByPassPhrase based encryption support.
+ */
+public class SqlServerDbEncrypt extends AbstractDbEncrypt {
+
+ public SqlServerDbEncrypt() {
+ this.varcharEncryptFunction = new VarcharFunction();
+ this.dateEncryptFunction = new DateFunction();
+ }
+
+ @Override
+ public boolean isBindEncryptDataFirst() {
+ return false;
+ }
+
+ private static class VarcharFunction implements DbEncryptFunction {
+
+ @Override
+ public String getDecryptSql(String columnWithTableAlias) {
+ return "convert(nvarchar,DecryptByPassPhrase(?," + columnWithTableAlias + "))";
+ }
+
+ @Override
+ public String getEncryptBindSql() {
+ return "EncryptByPassPhrase(?,?)";
+ }
+ }
+
+ private static class DateFunction implements DbEncryptFunction {
+
+ @Override
+ public String getDecryptSql(String columnWithTableAlias) {
+ return "cast(convert(nvarchar,DecryptByPassPhrase(?," + columnWithTableAlias + ")) as date)";
+ }
+
+ @Override
+ public String getEncryptBindSql() {
+ return "EncryptByPassPhrase(?,format(?,'yyyy-MM-dd'))";
+ }
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java b/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java
index f01a22b83..4011683ca 100644
--- a/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java
+++ b/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java
@@ -14,16 +14,15 @@ import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
/**
- * Manages the shutdown of the JVM Runtime.
+ * Manages the shutdown of Ebean.
*
* Makes sure all the resources are shutdown properly and in order.
- *
*/
public final class ShutdownManager {
private static final Logger logger = LoggerFactory.getLogger(ShutdownManager.class);
- private static final ReentrantLock lock = new ReentrantLock(false);
+ private static final ReentrantLock lock = new ReentrantLock();
private static final List databases = new ArrayList<>();
@@ -44,6 +43,9 @@ public final class ShutdownManager {
private ShutdownManager() {
}
+ /**
+ * Registers the container (potentially with cluster management).
+ */
public static void registerContainer(SpiContainer ebeanContainer) {
container = ebeanContainer;
}
@@ -94,7 +96,7 @@ public final class ShutdownManager {
/**
* Register the shutdown hook with the Runtime.
*/
- protected static void registerShutdownHook() {
+ private static void registerShutdownHook() {
lock.lock();
try {
String value = System.getProperty("ebean.registerShutdownHook");
@@ -123,13 +125,10 @@ public final class ShutdownManager {
// Already run shutdown...
return;
}
-
if (logger.isDebugEnabled()) {
logger.debug("Shutting down");
}
-
stopping = true;
-
deregisterShutdownHook();
String shutdownRunner = System.getProperty("ebean.shutdown.runnable");
@@ -147,7 +146,6 @@ public final class ShutdownManager {
// shutdown cluster networking if active
container.shutdown();
}
-
// shutdown any registered servers that have not
// already been shutdown manually
for (Database server : databases) {
@@ -158,7 +156,6 @@ public final class ShutdownManager {
ex.printStackTrace();
}
}
-
if ("true".equalsIgnoreCase(System.getProperty("ebean.datasource.deregisterAllDrivers", "false"))) {
deregisterAllJdbcDrivers();
}
@@ -168,15 +165,15 @@ public final class ShutdownManager {
}
private static void deregisterAllJdbcDrivers() {
- // This manually deregisters all JDBC drivers
+ // This manually de-registers all JDBC drivers
Enumeration drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
- logger.info("Deregistering jdbc driver: " + driver);
+ logger.info("De-registering jdbc driver: " + driver);
DriverManager.deregisterDriver(driver);
} catch (SQLException e) {
- logger.error("Error deregistering driver " + driver, e);
+ logger.error("Error de-registering driver " + driver, e);
}
}
}
@@ -209,6 +206,9 @@ public final class ShutdownManager {
}
private static class ShutdownHook extends Thread {
+ private ShutdownHook() {
+ super("EbeanHook");
+ }
@Override
public void run() {
ShutdownManager.shutdown();
diff --git a/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java b/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java
index 323b572ec..51df586d9 100644
--- a/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java
+++ b/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java
@@ -18,22 +18,22 @@ public abstract class AbstractMetricVisitor implements MetricVisitor {
}
@Override
- public boolean isReset() {
+ public boolean reset() {
return reset;
}
@Override
- public boolean isCollectTransactionMetrics() {
+ public boolean collectTransactionMetrics() {
return collectTransactionMetrics;
}
@Override
- public boolean isCollectQueryMetrics() {
+ public boolean collectQueryMetrics() {
return collectQueryMetrics;
}
@Override
- public boolean isCollectL2Metrics() {
+ public boolean collectL2Metrics() {
return collectL2Metrics;
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java b/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java
index c821c63a7..0db953159 100644
--- a/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java
+++ b/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java
@@ -27,17 +27,17 @@ public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerM
}
@Override
- public List getTimedMetrics() {
+ public List timedMetrics() {
return timed;
}
@Override
- public List getQueryMetrics() {
+ public List queryMetrics() {
return query;
}
@Override
- public List getCountMetrics() {
+ public List countMetrics() {
return count;
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java
index 8b6af3594..cdaa2d7fa 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java
@@ -8,6 +8,13 @@ public interface MetaCountMetric extends MetaMetric {
/**
* Return the total count.
*/
- long getCount();
+ long count();
+ /**
+ * Migrate to count()
+ */
+ @Deprecated
+ default long getCount() {
+ return count();
+ }
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java
index 0d716fe23..671dc05c1 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java
@@ -8,6 +8,13 @@ public interface MetaMetric {
/**
* Return the metric name.
*/
- String getName();
+ String name();
+ /**
+ * Migrate to name().
+ */
+ @Deprecated
+ default String getName() {
+ return name();
+ }
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java
index 514b700dc..1c9b5fe12 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java
@@ -8,21 +8,45 @@ public interface MetaQueryMetric extends MetaTimedMetric {
/**
* The type of entity or DTO bean.
*/
- Class> getType();
+ Class> type();
+
+ /**
+ * Migrate to type().
+ */
+ @Deprecated
+ default Class> getType() {
+ return type();
+ }
/**
* The label for the query (can be null).
*/
- String getLabel();
+ String label();
+
+ /**
+ * Migrate to label().
+ */
+ @Deprecated
+ default String getLabel() {
+ return label();
+ }
/**
* The actual SQL of the query.
*/
- String getSql();
+ String sql();
+
+ /**
+ * Migrate to sql().
+ */
+ @Deprecated
+ default String getSql() {
+ return sql();
+ }
/**
* Return the hash of the plan.
*/
- String getHash();
+ String hash();
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java
index fb2b6d095..c1ae44a82 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java
@@ -10,45 +10,45 @@ public interface MetaQueryPlan {
/**
* Return the bean type for the query.
*/
- Class> getBeanType();
+ Class> beanType();
/**
* Return the label of the query.
*/
- String getLabel();
+ String label();
/**
* Return the profile location for the query.
*/
- ProfileLocation getProfileLocation();
+ ProfileLocation profileLocation();
/**
* Return the sql of the query.
*/
- String getSql();
+ String sql();
/**
* Return the hash of the plan.
*/
- String getHash();
+ String hash();
/**
* Return a description of the bind values.
*/
- String getBind();
+ String bind();
/**
* Return the raw plan.
*/
- String getPlan();
+ String plan();
/**
* Return the query execution time associated with the bind values capture.
*/
- long getQueryTimeMicros();
+ long queryTimeMicros();
/**
* Return the total count of times bind capture has occurred.
*/
- long getCaptureCount();
+ long captureCount();
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java
index c6ba13516..f95f3248d 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java
@@ -9,27 +9,68 @@ public interface MetaTimedMetric extends MetaMetric {
/**
* Return the metric location if defined.
*/
- String getLocation();
+ String location();
+
+ /**
+ * Migrate to location()
+ */
+ @Deprecated
+ default String getLocation() {
+ return location();
+ }
/**
* Return the total count.
*/
- long getCount();
+ long count();
+
+ /**
+ * Migrate to count()
+ */
+ @Deprecated
+ default long getCount() {
+ return count();
+ }
/**
* Return the total execution time in micros.
*/
- long getTotal();
+ long total();
+
+ /**
+ * Migrate to total()
+ */
+ @Deprecated
+ default long getTotal() {
+ return total();
+ }
/**
* Return the max execution time in micros.
*/
- long getMax();
+ long max();
+
+ /**
+ * Migrate to max()
+ */
+ @Deprecated
+ default long getMax() {
+ return max();
+ }
/**
* Return the mean execution time in micros.
*/
- long getMean();
+ long mean();
+
+
+ /**
+ * Migrate to mean()
+ */
+ @Deprecated
+ default long getMean() {
+ return mean();
+ }
/**
* Return true if this is the first metrics collection for this query.
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java b/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java
index 18c7a72dc..fac9f050e 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java
@@ -8,22 +8,22 @@ public interface MetricVisitor {
/**
* Return true if the metrics should be reset.
*/
- boolean isReset();
+ boolean reset();
/**
* Return true if we should visit the transaction metrics.
*/
- boolean isCollectTransactionMetrics();
+ boolean collectTransactionMetrics();
/**
* Return true if we should visit the ORM and SQL query metrics.
*/
- boolean isCollectQueryMetrics();
+ boolean collectQueryMetrics();
/**
* Return true if we should visit the L2 cache metrics.
*/
- boolean isCollectL2Metrics();
+ boolean collectL2Metrics();
/**
* Visit has started.
diff --git a/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java b/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java
index 3c19caa39..5cce68994 100644
--- a/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java
+++ b/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java
@@ -32,7 +32,7 @@ public class QueryPlanInit {
* Return the query execution time threshold which must be exceeded to initiate
* query plan collection.
*/
- public long getThresholdMicros() {
+ public long thresholdMicros() {
return thresholdMicros;
}
@@ -40,7 +40,7 @@ public class QueryPlanInit {
* Set the query execution time threshold which must be exceeded to initiate
* query plan collection.
*/
- public void setThresholdMicros(long thresholdMicros) {
+ public void thresholdMicros(long thresholdMicros) {
this.thresholdMicros = thresholdMicros;
}
@@ -54,14 +54,14 @@ public class QueryPlanInit {
/**
* Return the specific hashes that we want to collect query plans on.
*/
- public Set getHashes() {
+ public Set hashes() {
return hashes;
}
/**
* Set the specific hashes that we want to collect query plans on.
*/
- public void setHashes(Set hashes) {
+ public void hashes(Set hashes) {
this.hashes = hashes;
}
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java b/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java
index 3510b30cc..421e0c151 100644
--- a/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java
+++ b/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java
@@ -18,7 +18,7 @@ public class QueryPlanRequest {
* have been around for a while (e.g. 5 mins) and so reasonably represent
* bind values that match the slowest execution for this query plan.
*/
- public long getSince() {
+ public long since() {
return since;
}
@@ -28,21 +28,24 @@ public class QueryPlanRequest {
*
* @param since The minimum age of the bind values capture.
*/
- public void setSince(long since) {
+ public void since(long since) {
this.since = since;
}
/**
* Return the maximum number of plans to capture.
*/
- public int getMaxCount() {
+ public int maxCount() {
return maxCount;
}
/**
* Set the maximum number of plans to capture.
+ *
+ * Use this to limit how much query plan capturing is done as query
+ * plan capture is actual database load.
*/
- public void setMaxCount(int maxCount) {
+ public void maxCount(int maxCount) {
this.maxCount = maxCount;
}
@@ -51,16 +54,18 @@ public class QueryPlanRequest {
*
* Query plan collection will stop once this time is exceeded.
*/
- public long getMaxTimeMillis() {
+ public long maxTimeMillis() {
return maxTimeMillis;
}
/**
* Set the maximum amount of time we want to use to capture plans.
*
- * 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) {
+ public void maxTimeMillis(long maxTimeMillis) {
this.maxTimeMillis = maxTimeMillis;
}
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java b/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java
index 8f0488d62..b8a50d85d 100644
--- a/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java
+++ b/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java
@@ -10,16 +10,39 @@ public interface ServerMetrics {
/**
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
*/
- List getTimedMetrics();
+ List timedMetrics();
+
+ /**
+ * Migrate to timedMetrics().
+ */
+ @Deprecated
+ default List getTimedMetrics() {
+ return timedMetrics();
+ }
/**
* Return the query metrics.
*/
- List getQueryMetrics();
+ List queryMetrics();
+
+ /**
+ * Migrate to queryMetrics().
+ */
+ @Deprecated
+ default List getQueryMetrics() {
+ return queryMetrics();
+ }
/**
* Return the Counter metrics.
*/
- List getCountMetrics();
+ List countMetrics();
+ /**
+ * Migrate to countMetrics().
+ */
+ @Deprecated
+ default List getCountMetrics() {
+ return countMetrics();
+ }
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java b/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java
index f48ecb5b6..7e42302c8 100644
--- a/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java
+++ b/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java
@@ -8,12 +8,12 @@ import java.util.Comparator;
public interface ServerMetricsAsJson {
/**
- * Set to false to exclude profile location and sql.
+ * Set to false in order to exclude profile location and sql.
*/
ServerMetricsAsJson withExtraAttributes(boolean withLocation);
/**
- * Set to false to exclude SQL hash.
+ * Set to false in order to exclude SQL hash.
*/
ServerMetricsAsJson withHash(boolean withHash);
diff --git a/ebean-api/src/main/java/io/ebean/meta/SortMetric.java b/ebean-api/src/main/java/io/ebean/meta/SortMetric.java
index 9df2c704b..30104f1ff 100644
--- a/ebean-api/src/main/java/io/ebean/meta/SortMetric.java
+++ b/ebean-api/src/main/java/io/ebean/meta/SortMetric.java
@@ -32,7 +32,7 @@ public class SortMetric {
@Override
public int compare(MetaCountMetric o1, MetaCountMetric o2) {
- return stringCompare(o1.getName(), o2.getName());
+ return stringCompare(o1.name(), o2.name());
}
}
@@ -43,8 +43,8 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
- int i = stringCompare(o1.getName(), o2.getName());
- return i != 0 ? i : Long.compare(o1.getCount(), o2.getCount());
+ int i = stringCompare(o1.name(), o2.name());
+ return i != 0 ? i : Long.compare(o1.count(), o2.count());
}
}
@@ -55,7 +55,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
- return Long.compare(o2.getCount(), o1.getCount());
+ return Long.compare(o2.count(), o1.count());
}
}
@@ -66,7 +66,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
- return Long.compare(o2.getTotal(), o1.getTotal());
+ return Long.compare(o2.total(), o1.total());
}
}
@@ -77,7 +77,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
- return Long.compare(o2.getMean(), o1.getMean());
+ return Long.compare(o2.mean(), o1.mean());
}
}
@@ -88,7 +88,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
- return Long.compare(o2.getMax(), o1.getMax());
+ return Long.compare(o2.max(), o1.max());
}
}
}
diff --git a/ebean-api/src/main/java/io/ebean/util/JdbcClose.java b/ebean-api/src/main/java/io/ebean/util/JdbcClose.java
index 92b3f7e28..b72f59c4d 100644
--- a/ebean-api/src/main/java/io/ebean/util/JdbcClose.java
+++ b/ebean-api/src/main/java/io/ebean/util/JdbcClose.java
@@ -66,4 +66,17 @@ public class JdbcClose {
logger.warn("Error on connection rollback", e);
}
}
+
+ /**
+ * Cancels the statement
+ */
+ public static void cancel(Statement stmt) {
+ try {
+ if (stmt != null) {
+ stmt.cancel();
+ }
+ } catch (SQLException e) {
+ logger.warn("Error on cancelling statement", e);
+ }
+ }
}
diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml
index 6ffe3cbce..79101340c 100644
--- a/ebean-autotune/pom.xml
+++ b/ebean-autotune/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOT
@@ -14,7 +14,7 @@
scm:git:git@github.com:ebean-orm/ebean.git
- HEAD
+ ebean-parent-12.8.0ebean autotune
@@ -26,7 +26,7 @@
io.ebeanebean-core
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTprovided
@@ -59,12 +59,12 @@
io.repaint.maventiles-maven-plugin
- 2.18
+ 2.19true
- io.ebean.tile:enhancement:12.5.0
+ io.ebean.tile:enhancement:12.6.0
diff --git a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/AutoTuneDiffCollection.java b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/AutoTuneDiffCollection.java
index 9eb5439f2..e9ebe5af1 100644
--- a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/AutoTuneDiffCollection.java
+++ b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/AutoTuneDiffCollection.java
@@ -113,7 +113,7 @@ public class AutoTuneDiffCollection {
diffCount++;
- Origin origin = createOrigin(entry, point, tuneDetail.toString());
+ Origin origin = createOrigin(entry, point, tuneDetail.asString());
ProfileDiff diff = document.getProfileDiff();
if (diff == null) {
diff = new ProfileDiff();
@@ -147,7 +147,7 @@ public class AutoTuneDiffCollection {
Origin origin = new Origin();
origin.setKey(point.getKey());
origin.setBeanType(point.getBeanType());
- origin.setDetail(entry.getDetail().toString());
+ origin.setDetail(entry.getDetail().asString());
origin.setCallStack(point.getCallOrigin().getFullDescription());
origin.setOriginal(query);
diff --git a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java
index af969fd53..b93be5cc8 100644
--- a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java
+++ b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java
@@ -146,7 +146,8 @@ public class BaseQueryTuner {
case ID_LIST:
case UPDATE:
case DELETE:
- case SUBQUERY:
+ case SQ_EXISTS:
+ case SQ_IN:
return false;
default:
// not using autoTune when explicitly loading the l2 bean cache
diff --git a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/DefaultAutoTuneService.java b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/DefaultAutoTuneService.java
index 11182ec19..be2033695 100644
--- a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/DefaultAutoTuneService.java
+++ b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/DefaultAutoTuneService.java
@@ -23,7 +23,7 @@ public class DefaultAutoTuneService implements AutoTuneService {
private static final Logger logger = LoggerFactory.getLogger(DefaultAutoTuneService.class);
- private final ReentrantLock lock = new ReentrantLock(false);
+ private final ReentrantLock lock = new ReentrantLock();
private final SpiEbeanServer server;
@@ -77,7 +77,7 @@ public class DefaultAutoTuneService implements AutoTuneService {
loadTuningFile();
if (isRuntimeTuningUpdates()) {
// periodically gather and update query tuning
- server.getBackgroundExecutor().executePeriodically(new ProfilingUpdate(), profilingUpdateFrequency, TimeUnit.SECONDS);
+ server.getBackgroundExecutor().scheduleWithFixedDelay(new ProfilingUpdate(), profilingUpdateFrequency, profilingUpdateFrequency, TimeUnit.SECONDS);
}
}
}
diff --git a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileManager.java b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileManager.java
index dfbdc5800..170a3a406 100644
--- a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileManager.java
+++ b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileManager.java
@@ -18,7 +18,7 @@ import java.util.concurrent.locks.ReentrantLock;
*/
public class ProfileManager implements ProfilingListener {
- private final ReentrantLock lock = new ReentrantLock(false);
+ private final ReentrantLock lock = new ReentrantLock();
private final boolean queryTuningAddVersion;
@@ -46,7 +46,6 @@ public class ProfileManager implements ProfilingListener {
@Override
public boolean isProfileRequest(ObjectGraphNode origin, SpiQuery> query) {
-
ProfileOrigin profileOrigin = profileMap.get(origin.getOriginQueryPoint().getKey());
if (profileOrigin == null) {
profileMap.put(origin.getOriginQueryPoint().getKey(), createProfileOrigin(origin, query));
@@ -61,12 +60,11 @@ public class ProfileManager implements ProfilingListener {
*
* For new profiling entries it is useful to compare the profiling against the current
* query detail that is specified in the code (as the query might already be manually optimised).
- *
*/
private ProfileOrigin createProfileOrigin(ObjectGraphNode origin, SpiQuery> query) {
ProfileOrigin profileOrigin = new ProfileOrigin(origin.getOriginQueryPoint(), queryTuningAddVersion, profilingBase, profilingRate);
// set the current query detail (fetch group) so that we can compare against profiling for new entries
- profileOrigin.setOriginalQuery(query.getDetail().toString());
+ profileOrigin.setOriginalQuery(query.getDetail().asString());
return profileOrigin;
}
@@ -77,7 +75,6 @@ public class ProfileManager implements ProfilingListener {
*/
@Override
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) {
-
if (node != null) {
ObjectGraphOrigin origin = node.getOriginQueryPoint();
if (origin != null) {
@@ -92,11 +89,9 @@ public class ProfileManager implements ProfilingListener {
*
* This is sent to use from a EntityBeanIntercept when the finalise method
* is called on the bean.
- *
*/
@Override
public void collectNodeUsage(NodeUsageCollector usageCollector) {
-
ProfileOrigin profileOrigin = getProfileOrigin(usageCollector.getNode().getOriginQueryPoint());
profileOrigin.collectUsageInfo(usageCollector);
}
@@ -114,16 +109,13 @@ public class ProfileManager implements ProfilingListener {
* Collect all the profiling information.
*/
public AutoTuneCollection profilingCollection(boolean reset) {
-
AutoTuneCollection req = new AutoTuneCollection();
-
for (ProfileOrigin origin : profileMap.values()) {
BeanDescriptor> desc = server.getBeanDescriptorById(origin.getOrigin().getBeanType());
if (desc != null) {
origin.profilingCollection(desc, req, reset);
}
}
-
return req;
}
diff --git a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOrigin.java b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOrigin.java
index 62feec130..b9935f558 100644
--- a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOrigin.java
+++ b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOrigin.java
@@ -16,7 +16,7 @@ import java.util.concurrent.locks.ReentrantLock;
public class ProfileOrigin {
- private final ReentrantLock lock = new ReentrantLock(false);
+ private final ReentrantLock lock = new ReentrantLock();
private static final long RESET_COUNT = -1000000000L;
diff --git a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOriginNodeUsage.java b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOriginNodeUsage.java
index dc632e2cd..3ba6afa20 100644
--- a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOriginNodeUsage.java
+++ b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOriginNodeUsage.java
@@ -21,7 +21,7 @@ public class ProfileOriginNodeUsage {
private static final Logger logger = LoggerFactory.getLogger(ProfileOriginNodeUsage.class);
- private final ReentrantLock lock = new ReentrantLock(false);
+ private final ReentrantLock lock = new ReentrantLock();
private final String path;
diff --git a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/TunedQueryInfo.java b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/TunedQueryInfo.java
index 374a8d722..43e66c5af 100644
--- a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/TunedQueryInfo.java
+++ b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/TunedQueryInfo.java
@@ -65,7 +65,7 @@ public class TunedQueryInfo implements Serializable {
@Override
public String toString() {
- return tunedDetail.toString();
+ return tunedDetail.asString();
}
}
diff --git a/ebean-autotune/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java b/ebean-autotune/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java
index 4bc86f6b4..b1f982024 100644
--- a/ebean-autotune/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java
+++ b/ebean-autotune/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java
@@ -11,12 +11,9 @@ import org.tests.model.basic.Order;
import static org.assertj.core.api.Assertions.assertThat;
-//import org.tests.model.basic.ResetBasicData;
-
public class ProfileOriginTest extends BaseTestCase {
-
- private BeanDescriptor desc = getBeanDescriptor(Order.class);
+ private final BeanDescriptor desc = getBeanDescriptor(Order.class);
@Test
public void buildDetail() {
diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml
index aea366e64..5cd61b30c 100644
--- a/ebean-bom/pom.xml
+++ b/ebean-bom/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTebean bom
@@ -12,16 +12,6 @@
ebean-bompom
-
- 1.0
- 1.0
- 12.2.0
- 4.0
- 7.0
- 12.6.0
- 12.6.0
-
-
@@ -81,72 +71,90 @@
io.ebeanebean
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTio.ebeanebean-api
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTio.ebeanebean-core
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOT
+
+
+
+ io.ebean
+ ebean-core-type
+ 12.11.3-SNAPSHOTio.ebeanebean-ddl-generator
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTio.ebeanebean-externalmapping-api
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTio.ebeanebean-externalmapping-xml
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTio.ebeanebean-autotune
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTio.ebeanebean-querybean
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTio.ebeanquerybean-generator
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTprovidedio.ebeankotlin-querybean-generator
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTprovidedio.ebeanebean-test
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTtest
+
+ io.ebean
+ ebean-postgis
+ 12.11.3-SNAPSHOT
+
+
+
+ io.ebean
+ ebean-redis
+ 12.11.3-SNAPSHOT
+
+
diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml
index 6bf92485e..ebe821cc6 100644
--- a/ebean-core-type/pom.xml
+++ b/ebean-core-type/pom.xml
@@ -4,28 +4,25 @@
ebean-parentio.ebean
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTebean-core-type
-
-
- 2.11.3
- 2.11.3
-
+ ebean core type
+ ebean scalar types apiio.ebeanebean-api
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTcom.fasterxml.jackson.corejackson-core
- ${jackson-core.version}
+ ${jackson.version}true
diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java b/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java
index be6a3b559..b08bb529a 100644
--- a/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java
+++ b/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java
@@ -163,4 +163,15 @@ public interface DataBinder {
* Bind an array value.
*/
void setArray(String arrayType, Object[] elements) throws SQLException;
+
+ /**
+ * Push json from dirty detection to be available for binding.
+ */
+ void pushJson(String json);
+
+ /**
+ * Pop json made during dirty detection for scalarType binding.
+ */
+ String popJson();
+
}
diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java b/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java
index 45fba0e0e..ef61b9122 100644
--- a/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java
+++ b/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java
@@ -11,8 +11,6 @@ public interface DataReader {
boolean next() throws SQLException;
- void resetColumnPosition();
-
void incrementPos(int increment);
byte[] getBinaryBytes() throws SQLException;
@@ -50,4 +48,14 @@ public interface DataReader {
Object getObject() throws SQLException;
InputStream getBinaryStream() throws SQLException;
+
+ /**
+ * Push json from dirty detection to be available for binding.
+ */
+ void pushJson(String json);
+
+ /**
+ * Pop json made during dirty detection for scalarType binding.
+ */
+ String popJson();
}
diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java
index a971a0115..6c01811c5 100644
--- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java
+++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java
@@ -2,6 +2,7 @@ package io.ebean.core.type;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
+
import io.ebean.text.StringFormatter;
import io.ebean.text.StringParser;
@@ -34,6 +35,10 @@ import java.sql.SQLException;
*/
public interface ScalarType extends StringParser, StringFormatter, ScalarDataReader {
+ default boolean isJsonMapper() {
+ return false;
+ }
+
/**
* Return true if this is a binary type and can not support parse() and format() from/to string.
* This allows Ebean to optimise marshalling types to string.
diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml
index 4532869d3..3ca1e8a3b 100644
--- a/ebean-core/pom.xml
+++ b/ebean-core/pom.xml
@@ -3,7 +3,7 @@
ebean-parentio.ebean
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTebean-core
@@ -15,32 +15,18 @@
scm:git:git@github.com:ebean-orm/ebean.git
- HEAD
+ ebean-parent-12.8.0
-
- 2.11.3
- 2.11.3
-
-
-
db2
+
- com.ibm.jdbc
- db2jcc4
- 4.23.42
+ com.ibm.db2
+ jcc
+ 11.5.5.0test
@@ -57,27 +43,27 @@
io.avajeclasspath-scanner
- 4.2
+ 6.0io.ebeanebean-migration-auto
- 1.0
+ 1.1io.ebeanebean-migration
- 12.2.0
+ 12.4.0testio.ebeanebean-ddl-generator
- 12.6.1
+ 12.11.0test
@@ -92,19 +78,19 @@
io.ebeanebean-api
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTio.ebeanebean-core-type
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOTio.ebeanebean-externalmapping-api
- 12.6.2-SNAPSHOT
+ 12.11.3-SNAPSHOT
@@ -124,6 +110,15 @@
provided
+
+
+ com.h2database
+ h2
+ 1.4.199
+ provided
+
+
javax.transactionjta
@@ -138,6 +133,12 @@
1.1.0.Finaltrue
+
+ jakarta.validation
+ jakarta.validation-api
+ 3.0.0
+ true
+ joda-time
@@ -157,14 +158,14 @@
com.fasterxml.jackson.corejackson-core
- ${jackson-core.version}
+ ${jackson.version}truecom.fasterxml.jackson.corejackson-databind
- ${jackson-databind.version}
+ ${jackson.version}true
@@ -172,7 +173,7 @@
org.postgresqlpostgresql
- 42.2.10
+ 42.2.20true
@@ -202,7 +203,7 @@
io.ebeanebean-test-docker
- 4.0
+ 4.1test
@@ -213,15 +214,6 @@
test
-
-
- com.h2database
- h2
- 1.4.199
- provided
-
-
org.xerialsqlite-jdbc
@@ -281,7 +273,7 @@
commons-iocommons-io
- 2.5
+ 2.7test
@@ -301,7 +293,7 @@
io.ebeanebean-maven-plugin
- 12.5.0
+ ${ebean-maven-plugin.version}test
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BeanCacheResult.java b/ebean-core/src/main/java/io/ebeaninternal/api/BeanCacheResult.java
index c3b70e2e1..11e6d5f15 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/BeanCacheResult.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/BeanCacheResult.java
@@ -6,7 +6,7 @@ import java.util.List;
/**
* The results of bean cache hit.
*/
-public class BeanCacheResult {
+public final class BeanCacheResult {
private final List> list = new ArrayList<>();
@@ -27,7 +27,7 @@ public class BeanCacheResult {
/**
* Bean and cache key pair.
*/
- static class Entry {
+ static final class Entry {
private final T bean;
private final Object key;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BinaryReadContext.java b/ebean-core/src/main/java/io/ebeaninternal/api/BinaryReadContext.java
index 105a9b81b..bde5c8167 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/BinaryReadContext.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/BinaryReadContext.java
@@ -7,7 +7,7 @@ import java.io.IOException;
/**
* Context used to read binary format messages.
*/
-public class BinaryReadContext {
+public final class BinaryReadContext {
private final DataInputStream in;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BinaryWriteContext.java b/ebean-core/src/main/java/io/ebeaninternal/api/BinaryWriteContext.java
index c36bfebc8..610ad41ea 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/BinaryWriteContext.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/BinaryWriteContext.java
@@ -6,10 +6,9 @@ import java.io.IOException;
/**
* Context used to write binary message (like RemoteTransactionEvent).
*/
-public class BinaryWriteContext {
+public final class BinaryWriteContext {
private final DataOutputStream out;
-
private long counter;
public BinaryWriteContext(DataOutputStream out) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java
index a45d98e6d..7c5264892 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java
@@ -4,11 +4,7 @@ import io.ebeaninternal.server.persist.MultiValueWrapper;
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
/**
@@ -17,35 +13,28 @@ import java.util.Map.Entry;
* Supports ordered or named parameters.
*
*/
-public class BindParams implements Serializable {
+public final class BindParams implements Serializable {
private static final long serialVersionUID = 4541081933302086285L;
private final List positionedParameters = new ArrayList<>();
-
private final Map namedParameters = new LinkedHashMap<>();
-
/**
* This is the sql. For named parameters this is the sql after the named
* parameters have been replaced with question mark place holders and the
* parameters have been ordered by addNamedParamInOrder().
*/
private String preparedSql;
-
/**
* Bind hash and count used to detect when the bind values have changed such
* that the generated SQL (with named parameters) needs to be recalculated.
*/
private String bindHash;
-
/**
* Helper to add positioned parameters in order.
*/
private int addPos;
- public BindParams() {
- }
-
/**
* Reset positioned parameters (usually due to bind parameter expansion).
*/
@@ -54,12 +43,11 @@ public class BindParams implements Serializable {
positionedParameters.clear();
}
- public int queryBindHash() {
- int hc = namedParameters.hashCode();
- for (Param positionedParameter : positionedParameters) {
- hc = hc * 92821 + positionedParameter.hashCode();
+ public void queryBindHash(BindValuesKey key) {
+ key.add(positionedParameters.size());
+ for (Param param : positionedParameters) {
+ param.queryBindHash(key);
}
- return hc;
}
/**
@@ -155,7 +143,6 @@ public class BindParams implements Serializable {
* Set an In Out parameter using position.
*/
public void setParameter(int position, Object value, int outType) {
-
Param p = getParam(position);
p.setInValue(value);
p.setOutType(outType);
@@ -178,8 +165,8 @@ public class BindParams implements Serializable {
* Using position set the In value of a parameter. Note that for nulls you
* must use setNullParameter.
*/
+ @SuppressWarnings("rawtypes")
public void setParameter(int position, Object value) {
-
Param p = getParam(position);
if (value instanceof Collection) {
// use of postgres ANY with positioned parameter
@@ -214,7 +201,6 @@ public class BindParams implements Serializable {
* Set a named In Out parameter.
*/
public void setParameter(String name, Object value, int outType) {
-
Param p = getParam(name);
p.setInValue(value);
p.setOutType(outType);
@@ -232,7 +218,6 @@ public class BindParams implements Serializable {
* Set a named In parameter that is not null.
*/
public Param setParameter(String name, Object value) {
-
Param p = getParam(name);
p.setInValue(value);
return p;
@@ -299,7 +284,6 @@ public class BindParams implements Serializable {
* Return true if the bind hash and count has not changed.
*/
public boolean isSameBindHash() {
-
if (bindHash == null) {
bindHash = calcQueryPlanHash();
return false;
@@ -330,10 +314,6 @@ public class BindParams implements Serializable {
private final StringBuilder preparedSql;
- public OrderedList() {
- this(new ArrayList<>());
- }
-
public OrderedList(List paramList) {
this.paramList = paramList;
this.preparedSql = new StringBuilder();
@@ -432,7 +412,14 @@ public class BindParams implements Serializable {
@Override
public boolean equals(Object o) {
- return o != null && (o == this || (o instanceof Param) && hashCode() == o.hashCode());
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Param param = (Param) o;
+ return isInParam == param.isInParam && isOutParam == param.isOutParam && type == param.type && Objects.equals(inValue, param.inValue);
+ }
+
+ void queryBindHash(BindValuesKey key) {
+ key.add(isInParam).add(isOutParam).add(type).add(inValue);
}
/**
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java
new file mode 100644
index 000000000..062932af2
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java
@@ -0,0 +1,35 @@
+package io.ebeaninternal.api;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * BindValues used for L2 query cache key matching.
+ *
+ * The equals/hashCode implementation must meet the requirement that the query bind values
+ * match for L2 query cache hit (given the query plan hash is already a match).
+ */
+public final class BindValuesKey {
+
+ private final List
*/
private boolean isOnlyIds() {
return onlyIds;
@@ -91,18 +87,16 @@ public class LoadManyRequest extends LoadRequest {
return loadContext.getBatchSize();
}
- private List getParentIdList() {
-
+ private List parentIdList(SpiEbeanServer server) {
List idList = new ArrayList<>();
-
BeanPropertyAssocMany> many = getMany();
for (BeanCollection> bc : batch) {
idList.add(many.getParentId(bc.getOwnerBean()));
+ bc.setLoader(server); // don't use the load buffer again
}
if (many.getTargetDescriptor().isPadInExpression()) {
BindPadding.padIds(idList);
}
-
return idList;
}
@@ -111,9 +105,7 @@ public class LoadManyRequest extends LoadRequest {
}
public SpiQuery> createQuery(SpiEbeanServer server) {
-
BeanPropertyAssocMany> many = getMany();
-
SpiQuery> query = many.newQuery(server);
String orderBy = many.getLazyFetchOrderBy();
if (orderBy != null) {
@@ -124,11 +116,11 @@ public class LoadManyRequest extends LoadRequest {
if (extraWhere != null) {
// replace special ${ta} placeholder with the base table alias
// which is always t0 and add the extra where clause
- query.where().raw(extraWhere.replace("${ta}", "t0"));
+ query.where().raw(extraWhere.replace("${ta}", "t0").replace("${mta}", "int_"));
}
query.setLazyLoadForParents(many);
- many.addWhereParentIdIn(query, getParentIdList(), loadContext.isUseDocStore());
+ many.addWhereParentIdIn(query, parentIdList(server), loadContext.isUseDocStore());
query.setPersistenceContext(loadContext.getPersistenceContext());
String mode = isLazy() ? "+lazy" : "+query";
@@ -146,7 +138,6 @@ public class LoadManyRequest extends LoadRequest {
// override to just select the Id values
query.select(many.getTargetIdProperty());
}
-
return query;
}
@@ -154,10 +145,8 @@ public class LoadManyRequest extends LoadRequest {
* After the query execution check for empty collections and load L2 cache if desired.
*/
public void postLoad() {
-
BeanDescriptor> desc = loadContext.getBeanDescriptor();
BeanPropertyAssocMany> many = getMany();
-
// check for BeanCollection's that where never processed
// in the +query or +lazy load due to no rows (predicates)
for (BeanCollection> bc : batch) {
@@ -172,6 +161,5 @@ public class LoadManyRequest extends LoadRequest {
desc.cacheManyPropPut(many, bc, parentId);
}
}
-
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/ManyWhereJoins.java b/ebean-core/src/main/java/io/ebeaninternal/api/ManyWhereJoins.java
index 0c6b128a2..91fc49d21 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/ManyWhereJoins.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/ManyWhereJoins.java
@@ -17,16 +17,13 @@ import java.util.TreeSet;
* Holds the joins needs to support the many where predicates.
* These joins are independent of any 'fetch' joins on the many.
*/
-public class ManyWhereJoins implements Serializable {
+public final class ManyWhereJoins implements Serializable {
private static final long serialVersionUID = -6490181101871795417L;
private final TreeMap joins = new TreeMap<>();
-
private List formulaJoinProperties;
-
private boolean aggregation;
-
/**
* 'Mode' indicating that joins added while this is true are required to be outer joins.
*/
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/Monitor.java b/ebean-core/src/main/java/io/ebeaninternal/api/Monitor.java
deleted file mode 100644
index e1b930769..000000000
--- a/ebean-core/src/main/java/io/ebeaninternal/api/Monitor.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.ebeaninternal.api;
-
-import java.io.Serializable;
-
-/**
- * Object used as a synchronization monitor that is serializable.
- */
-public class Monitor implements Serializable {
-
- private static final long serialVersionUID = -2741687226680981940L;
-
-}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEntryBasic.java b/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEntryBasic.java
index aff59771b..61514bc66 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEntryBasic.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEntryBasic.java
@@ -11,7 +11,7 @@ import java.util.Map;
/**
* Natural key entry with name value pairs for each of the properties making up the key.
*/
-class NaturalKeyEntryBasic implements NaturalKeyEntry {
+final class NaturalKeyEntryBasic implements NaturalKeyEntry {
private final Map map = new HashMap<>();
private final String key;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEntrySimple.java b/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEntrySimple.java
index 0fa7bef62..0ad6e9343 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEntrySimple.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEntrySimple.java
@@ -1,6 +1,6 @@
package io.ebeaninternal.api;
-class NaturalKeyEntrySimple implements NaturalKeyEntry {
+final class NaturalKeyEntrySimple implements NaturalKeyEntry {
private final String key;
private final Object val;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEq.java b/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEq.java
index e7a6218df..3933c2f4b 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEq.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyEq.java
@@ -3,7 +3,7 @@ package io.ebeaninternal.api;
/**
* A property value pair in a natural key lookup.
*/
-public class NaturalKeyEq {
+public final class NaturalKeyEq {
final String property;
final Object value;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java b/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java
index 6966787d5..a8b4d348d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java
@@ -11,28 +11,22 @@ import java.util.Set;
/**
* Collects the data for processing the natural key cache processing.
*/
-public class NaturalKeyQueryData {
+public final class NaturalKeyQueryData {
private final BeanNaturalKey naturalKey;
-
/**
* Only one of IN or IN PAIRS is allowed.
*/
private boolean hasIn;
-
// IN Pairs clause - only one allowed
private String inProperty0, inProperty1;
private List inPairs;
-
// IN clause - only one allowed
private List inValues;
private String inProperty;
-
// normal EQ expressions
private List eqList;
-
private NaturalKeySet set;
-
private int hitCount;
public NaturalKeyQueryData(BeanNaturalKey naturalKey) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeySet.java b/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeySet.java
index cf31afbf9..89b5ae484 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeySet.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/NaturalKeySet.java
@@ -4,8 +4,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
-public class NaturalKeySet {
-
+public final class NaturalKeySet {
private final Map map = new LinkedHashMap<>();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryBindCapture.java b/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryBindCapture.java
index 2f7162c70..33ee06b54 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryBindCapture.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryBindCapture.java
@@ -2,7 +2,7 @@ package io.ebeaninternal.api;
import io.ebeaninternal.server.type.bindcapture.BindCapture;
-class NoopQueryBindCapture implements SpiQueryBindCapture {
+final class NoopQueryBindCapture implements SpiQueryBindCapture {
@Override
public boolean collectFor(long timeMicros) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryPlanManager.java b/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryPlanManager.java
index e00ad57f1..9c6a54952 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryPlanManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryPlanManager.java
@@ -6,7 +6,12 @@ import io.ebean.meta.QueryPlanRequest;
import java.util.Collections;
import java.util.List;
-class NoopQueryPlanManager implements QueryPlanManager {
+final class NoopQueryPlanManager implements QueryPlanManager {
+
+ @Override
+ public void setDefaultThreshold(long thresholdMicros) {
+ // do nothing
+ }
@Override
public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/PlatformMatch.java b/ebean-core/src/main/java/io/ebeaninternal/api/PlatformMatch.java
index a333008e9..eeb51221e 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/PlatformMatch.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/PlatformMatch.java
@@ -3,7 +3,7 @@ package io.ebeaninternal.api;
import io.ebean.annotation.Platform;
import io.ebean.util.StringHelper;
-public class PlatformMatch {
+public final class PlatformMatch {
/**
* Return true if the script platforms is a match/supported for the given platform.
@@ -15,7 +15,6 @@ public class PlatformMatch {
if (platforms == null || platforms.trim().isEmpty()) {
return true;
}
-
// match on base platform name and platform name
for (String name : StringHelper.splitNames(platforms)) {
if (name.equalsIgnoreCase(platform.base().name()) || name.equalsIgnoreCase(platform.name())) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/PropertyJoin.java b/ebean-core/src/main/java/io/ebeaninternal/api/PropertyJoin.java
index 2b11732b0..ab63e09d4 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/PropertyJoin.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/PropertyJoin.java
@@ -5,16 +5,9 @@ import io.ebeaninternal.server.query.SqlJoinType;
/**
* Represents a join required for a given property and whether than needs to be an outer join.
*/
-public class PropertyJoin {
+public final class PropertyJoin {
- /**
- * The property name.
- */
private final String property;
-
- /**
- * Set to true if the property needs to be an outer join.
- */
private final SqlJoinType joinType;
public PropertyJoin(String property, SqlJoinType joinType) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/QueryPlanManager.java b/ebean-core/src/main/java/io/ebeaninternal/api/QueryPlanManager.java
index f9750c47a..da7ce2b43 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/QueryPlanManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/QueryPlanManager.java
@@ -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.
*/
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/ScopeTrans.java b/ebean-core/src/main/java/io/ebeaninternal/api/ScopeTrans.java
index 5fbb03f25..f3ca6555f 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/ScopeTrans.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/ScopeTrans.java
@@ -7,7 +7,7 @@ import java.util.ArrayList;
/**
* Used internally to handle the scoping of transactions for methods.
*/
-public class ScopeTrans {
+public final class ScopeTrans {
private static final int OPCODE_ATHROW = 191;
@@ -15,43 +15,32 @@ public class ScopeTrans {
* The transaction in scope (can be null).
*/
private final SpiTransaction transaction;
-
/**
* If true by default rollback on Checked exceptions.
*/
private final boolean rollbackOnChecked;
-
/**
* True if the transaction was created and hence should be committed
* on finally if it hasn't already been rolled back.
*/
private final boolean created;
-
/**
* Explicit set of Exceptions that DO NOT cause a rollback to occur.
*/
private final ArrayList> noRollbackFor;
-
/**
* Explicit set of Exceptions that DO cause a rollback to occur.
*/
private final ArrayList> rollbackFor;
-
private Boolean restoreBatch;
-
private Boolean restoreBatchOnCascade;
-
private int restoreBatchSize;
-
private Boolean restoreBatchGeneratedKeys;
-
private boolean restoreBatchFlushOnQuery;
-
/**
* Flag set when a rollback has occurred.
*/
private boolean rolledBack;
-
/**
* Flag set when nested commit has occurred.
*/
@@ -73,6 +62,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());
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/ScopedTransaction.java b/ebean-core/src/main/java/io/ebeaninternal/api/ScopedTransaction.java
index f5a1db07b..383746f13 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/ScopedTransaction.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/ScopedTransaction.java
@@ -10,15 +10,13 @@ import javax.persistence.PersistenceException;
*
* These can be nested and internally they are pushed and popped from a stack.
*/
-public class ScopedTransaction extends SpiTransactionProxy {
+public final class ScopedTransaction extends SpiTransactionProxy {
private final TransactionScopeManager manager;
-
/**
* Stack of 'nested' transactions.
*/
private final ArrayStack stack = new ArrayStack<>();
-
private ScopeTrans current;
/**
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiBeanType.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiBeanType.java
new file mode 100644
index 000000000..9923fff3f
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiBeanType.java
@@ -0,0 +1,17 @@
+package io.ebeaninternal.api;
+
+import io.ebean.bean.EntityBean;
+
+/**
+ * SPI interface for underlying BeanDescriptor.
+ */
+public interface SpiBeanType {
+
+ /**
+ * Return true if the bean contains a many property that has modifications.
+ *
+ * That is a ManyToMany or a OneToMany with orphan removal with additions
+ * or removals from the collection.
+ */
+ boolean isToManyDirty(EntityBean bean);
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiBeanTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiBeanTypeManager.java
new file mode 100644
index 000000000..0a6b79d78
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiBeanTypeManager.java
@@ -0,0 +1,13 @@
+package io.ebeaninternal.api;
+
+/**
+ * Manager of SpiBeanTypes.
+ */
+public interface SpiBeanTypeManager {
+
+ /**
+ * Return the bean type for the given entity class.
+ */
+ SpiBeanType getBeanType(Class> entityType);
+
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiCancelableQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiCancelableQuery.java
new file mode 100644
index 000000000..10a53b9a7
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiCancelableQuery.java
@@ -0,0 +1,26 @@
+package io.ebeaninternal.api;
+
+import javax.persistence.PersistenceException;
+
+import io.ebean.CancelableQuery;
+
+/**
+ * Cancellable query, that has a delegate.
+ *
+ * @author Roland Praml, FOCONIS AG
+ *
+ */
+public interface SpiCancelableQuery extends CancelableQuery {
+
+ /**
+ * Checks if the query was cancelled.
+ * @throws PersistenceException if query was cancelled.
+ */
+ void checkCancelled();
+
+ /**
+ * Set the underlying cancelable query (with the PreparedStatement).
+ */
+ void setCancelableQuery(CancelableQuery cancelableQuery);
+
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java
index e9f26b2b2..fae60f49d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java
@@ -1,14 +1,6 @@
package io.ebeaninternal.api;
-import io.ebean.DtoQuery;
-import io.ebean.EbeanServer;
-import io.ebean.ExtendedServer;
-import io.ebean.PersistenceContextScope;
-import io.ebean.Query;
-import io.ebean.RowConsumer;
-import io.ebean.RowMapper;
-import io.ebean.Transaction;
-import io.ebean.TxScope;
+import io.ebean.*;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.CallOrigin;
import io.ebean.config.DatabaseConfig;
@@ -16,6 +8,7 @@ import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetricVisitor;
+import io.ebeaninternal.api.SpiQuery.Type;
import io.ebeaninternal.server.core.SpiResultSet;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.deploy.BeanDescriptor;
@@ -25,6 +18,7 @@ import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.stream.Stream;
/**
* Service Provider extension to EbeanServer.
@@ -156,7 +150,7 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
/**
* Compile a query.
*/
- CQuery compileQuery(Query query, Transaction t);
+ CQuery compileQuery(Type type, Query query, Transaction t);
/**
* Execute the findId's query but without copying the query.
@@ -234,6 +228,11 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
*/
List findSingleAttributeList(SpiSqlQuery query, Class cls);
+ /**
+ * SqlQuery find single attribute streaming the result to a consumer.
+ */
+ void findSingleAttributeEach(SpiSqlQuery query, Class cls, Consumer consumer);
+
/**
* SqlQuery find one with mapper.
*/
@@ -249,6 +248,16 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
*/
void findEachRow(SpiSqlQuery query, RowConsumer consumer);
+ /**
+ * DTO findIterate query.
+ */
+ QueryIterator findDtoIterate(SpiDtoQuery query);
+
+ /**
+ * DTO findStream query.
+ */
+ Stream findDtoStream(SpiDtoQuery query);
+
/**
* DTO findList query.
*/
@@ -264,6 +273,11 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
*/
void findDtoEach(SpiDtoQuery query, Consumer consumer);
+ /**
+ * DTO findEach batch query.
+ */
+ void findDtoEach(SpiDtoQuery query, int batch, Consumer> consumer);
+
/**
* DTO findEachWhile query.
*/
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java
index cad22c50a..8fe986259 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java
@@ -54,9 +54,9 @@ public interface SpiExpression extends Expression {
void queryPlanHash(StringBuilder builder);
/**
- * Return the hash value for the values that will be bound.
+ * Build the key for bind values of the query.
*/
- int queryBindHash();
+ void queryBindKey(BindValuesKey key);
/**
* Return true if the expression is the same with respect to bind values.
@@ -105,4 +105,9 @@ public interface SpiExpression extends Expression {
* Check for match to a natural key query returning false if it doesn't match.
*/
boolean naturalKey(NaturalKeyQueryData> data);
+
+ /**
+ * Apply property prefix when filterMany expressions included into main query.
+ */
+ void prefixProperty(String path);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpressionList.java
index 3cf5c2e64..c4764b30f 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpressionList.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpressionList.java
@@ -43,4 +43,9 @@ public interface SpiExpressionList extends ExpressionList, SpiExpression {
default void applyRowLimits(SpiQuery> query) {
// do nothing by default
}
+
+ /**
+ * Apply property prefix when filterMany expressions included in main query.
+ */
+ void prefixProperty(String path);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpressionValidation.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpressionValidation.java
index e02397a75..203827820 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpressionValidation.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpressionValidation.java
@@ -8,10 +8,9 @@ import java.util.Set;
/**
* Property expression validation request for a given root bean type.
*/
-public class SpiExpressionValidation {
+public final class SpiExpressionValidation {
private final BeanType> desc;
-
private final LinkedHashSet unknown = new LinkedHashSet<>();
public SpiExpressionValidation(BeanType> desc) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiPersistenceContext.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiPersistenceContext.java
new file mode 100644
index 000000000..a11f9d2f4
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiPersistenceContext.java
@@ -0,0 +1,17 @@
+package io.ebeaninternal.api;
+
+import io.ebean.bean.PersistenceContext;
+
+import java.util.List;
+
+/**
+ * SPI extension to PersistenceContext.
+ */
+public interface SpiPersistenceContext extends PersistenceContext {
+
+ /**
+ * Return the list of dirty beans held by this persistence context.
+ */
+ List dirtyBeans(SpiBeanTypeManager manager);
+
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java
index f3b133bc2..5a6ae2217 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java
@@ -17,9 +17,9 @@ import io.ebeaninternal.server.core.SpiOrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.TableJoin;
-import io.ebeaninternal.server.query.CancelableQuery;
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
+import io.ebeaninternal.server.querydefn.OrmQueryProperties;
import io.ebeaninternal.server.querydefn.OrmUpdateProperties;
import io.ebeaninternal.server.rawsql.SpiRawSql;
@@ -30,7 +30,7 @@ import java.util.Set;
/**
* Object Relational query - Internal extension to Query object.
*/
-public interface SpiQuery extends Query, TxnProfileEventCodes {
+public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCodes, SpiCancelableQuery {
enum Mode {
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
@@ -84,7 +84,7 @@ public interface SpiQuery extends Query, TxnProfileEventCodes {
/**
* Find single attribute.
*/
- ATTRIBUTE(FIND_ATTRIBUTE, "findAttribute"),
+ ATTRIBUTE(FIND_ATTRIBUTE, "findAttribute", false, false),
/**
* Find rowCount.
@@ -92,9 +92,14 @@ public interface SpiQuery extends Query, TxnProfileEventCodes {
COUNT(FIND_COUNT, "findCount"),
/**
- * A subquery used as part of a where clause.
+ * A subquery used as part of an exists where clause.
*/
- SUBQUERY(FIND_SUBQUERY, "subquery"),
+ SQ_EXISTS(FIND_SUBQUERY, "sqExists", false, false),
+
+ /**
+ * A subquery used as part of an in where clause.
+ */
+ SQ_IN(FIND_SUBQUERY, "sqIn", false, false),
/**
* Delete query.
@@ -107,17 +112,21 @@ public interface SpiQuery extends Query, TxnProfileEventCodes {
UPDATE(FIND_UPDATE, "update", true);
private final boolean update;
+ private final boolean defaultSelect;
private final String profileEventId;
private final String label;
Type(String profileEventId, String label) {
- this(profileEventId, label, false);
+ this(profileEventId, label, false, true);
}
-
Type(String profileEventId, String label, boolean update) {
+ this(profileEventId, label, update, true);
+ }
+ Type(String profileEventId, String label, boolean update, boolean defaultSelect) {
this.profileEventId = profileEventId;
this.label = label;
this.update = update;
+ this.defaultSelect = defaultSelect;
}
/**
@@ -127,6 +136,13 @@ public interface SpiQuery extends Query, TxnProfileEventCodes {
return update;
}
+ /**
+ * Return true if this allows default select clause.
+ */
+ public boolean defaultSelect() {
+ return defaultSelect;
+ }
+
public String profileEventId() {
return profileEventId;
}
@@ -289,6 +305,16 @@ public interface SpiQuery extends Query, TxnProfileEventCodes {
*/
boolean selectAllForLazyLoadProperty();
+ /**
+ * Set the select properties.
+ */
+ void selectProperties(OrmQueryProperties other);
+
+ /**
+ * Set the fetch properties for the given path.
+ */
+ void fetchProperties(String path, OrmQueryProperties other);
+
/**
* Set the on a secondary query given the label, relativePath and profile location of the parent query.
*/
@@ -619,13 +645,11 @@ public interface SpiQuery extends Query, TxnProfileEventCodes {
CQueryPlanKey prepare(SpiOrmQueryRequest request);
/**
- * Calculate a hash based on the bind values used in the query.
+ * Build the key for the bind values used in the query (for l2 query cache).
*
- * Combined with queryPlanHash() to return getQueryHash (a unique hash for a
- * query).
- *
+ * Combined with queryPlanHash() to return queryHash (a unique key for a query).
*/
- int queryBindHash();
+ void queryBindKey(BindValuesKey key);
/**
* Identifies queries that are exactly the same including bind variables.
@@ -836,16 +860,6 @@ public interface SpiQuery extends Query, TxnProfileEventCodes {
*/
ReadEvent getFutureFetchAudit();
- /**
- * Set the underlying cancelable query (with the PreparedStatement).
- */
- void setCancelableQuery(CancelableQuery cancelableQuery);
-
- /**
- * Return true if this query has been cancelled.
- */
- boolean isCancelled();
-
/**
* Return the base table to use if user defined on the query.
*/
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryFetch.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryFetch.java
new file mode 100644
index 000000000..716dbc076
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryFetch.java
@@ -0,0 +1,27 @@
+package io.ebeaninternal.api;
+
+import io.ebean.FetchConfig;
+import io.ebeaninternal.server.querydefn.OrmQueryDetail;
+
+import java.util.Set;
+
+/**
+ * Query select and fetch properties (that avoids parsing).
+ */
+public interface SpiQueryFetch {
+
+ /**
+ * Specify the select properties.
+ */
+ void selectProperties(Set properties);
+
+ /**
+ * Specify the fetch properties for the given path.
+ */
+ void fetchProperties(String name, Set properties, FetchConfig config);
+
+ /**
+ * Add a nested fetch graph.
+ */
+ void addNested(String name, OrmQueryDetail nestedDetail, FetchConfig config);
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java
index 96a1eade7..0b4c3ea0a 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java
@@ -18,7 +18,7 @@ public interface SpiQueryPlan {
String getName();
/**
- * The hash for the query plan.
+ * The hash of the sql.
*/
String getHash();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiSqlBinding.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiSqlBinding.java
index 849ad0cb0..62b93d34c 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiSqlBinding.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiSqlBinding.java
@@ -3,7 +3,7 @@ package io.ebeaninternal.api;
/**
* SQL query binding (for SqlQuery and DtoQuery).
*/
-public interface SpiSqlBinding {
+public interface SpiSqlBinding extends SpiCancelableQuery {
/**
* Return the named or positioned parameters.
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransaction.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransaction.java
index 5e75831b5..d26ab3ecc 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransaction.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransaction.java
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
import io.ebean.ProfileLocation;
import io.ebean.Transaction;
import io.ebean.annotation.DocStoreMode;
-import io.ebean.bean.PersistenceContext;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
@@ -147,6 +146,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
* Ebean.beginTransaction().
@@ -191,7 +195,7 @@ public interface SpiTransaction extends Transaction {
* later. This is along the lines of 'extended persistence context'
* behaviour.
*/
- PersistenceContext getPersistenceContext();
+ SpiPersistenceContext getPersistenceContext();
/**
* Set the persistence context to this transaction.
@@ -203,7 +207,7 @@ public interface SpiTransaction extends Transaction {
* and setPersistenceContext() enable a developer to reuse a single
* PersistenceContext with multiple transactions.
*/
- void setPersistenceContext(PersistenceContext context);
+ void setPersistenceContext(SpiPersistenceContext context);
/**
* Return the underlying Connection for internal use.
@@ -318,4 +322,20 @@ public interface SpiTransaction extends Transaction {
* Return true if explicitly set to skip cache (ignores skipOnWrite).
*/
boolean isSkipCacheExplicit();
+
+ /**
+ * Fire pre commit processing/listeners.
+ */
+ void preCommit();
+
+ /**
+ * Fire post commit events and listeners.
+ */
+ void postCommit();
+
+ /**
+ * Fire post rollback events and listeners.
+ */
+ void postRollback(Throwable cause);
+
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java
index 897a32f44..a63c04cc6 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
-import io.ebean.bean.PersistenceContext;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
@@ -43,6 +42,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();
@@ -369,12 +378,12 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
- public PersistenceContext getPersistenceContext() {
+ public SpiPersistenceContext getPersistenceContext() {
return transaction.getPersistenceContext();
}
@Override
- public void setPersistenceContext(PersistenceContext context) {
+ public void setPersistenceContext(SpiPersistenceContext context) {
transaction.setPersistenceContext(context);
}
@@ -418,4 +427,18 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
transaction.flushBatchOnCollection();
}
+ @Override
+ public void preCommit() {
+ transaction.preCommit();
+ }
+
+ @Override
+ public void postCommit() {
+ transaction.postCommit();
+ }
+
+ @Override
+ public void postRollback(Throwable cause) {
+ transaction.postRollback(cause);
+ }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/TransactionEvent.java b/ebean-core/src/main/java/io/ebeaninternal/api/TransactionEvent.java
index 52d87d23e..22a5ba904 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/TransactionEvent.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/TransactionEvent.java
@@ -20,7 +20,7 @@ import java.util.List;
* to the TransactionEventManager.
*
*/
-public class TransactionEvent implements Serializable {
+public final class TransactionEvent implements Serializable {
private static final long serialVersionUID = 7230903304106097120L;
@@ -29,13 +29,9 @@ public class TransactionEvent implements Serializable {
* the cluster).
*/
private final transient boolean local;
-
private TransactionEventTable eventTables;
-
private transient List> listenerNotify;
-
private transient DeleteByIdMap deleteByIdMap;
-
private transient CacheChangeSet changeSet;
/**
@@ -110,11 +106,9 @@ public class TransactionEvent implements Serializable {
* Build and return the cache changeSet.
*/
public CacheChangeSet buildCacheChanges(TransactionManager manager) {
-
if (changeSet == null && deleteByIdMap == null && eventTables == null) {
return null;
}
-
if (changeSet == null) {
changeSet = new CacheChangeSet();
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/TransactionEventTable.java b/ebean-core/src/main/java/io/ebeaninternal/api/TransactionEventTable.java
index 18ea4f5a3..de55cd163 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/TransactionEventTable.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/TransactionEventTable.java
@@ -28,20 +28,17 @@ public final class TransactionEventTable implements Serializable, BinaryWritable
}
public void add(TransactionEventTable table) {
-
for (TableIUD iud : table.values()) {
add(iud);
}
}
public void add(String table, boolean insert, boolean update, boolean delete) {
-
table = table.toUpperCase();
add(new TableIUD(table, insert, update, delete));
}
public void add(TableIUD newTableIUD) {
-
TableIUD existingTableIUD = map.put(newTableIUD.getTableName(), newTableIUD);
if (existingTableIUD != null) {
newTableIUD.add(existingTableIUD);
@@ -56,7 +53,7 @@ public final class TransactionEventTable implements Serializable, BinaryWritable
return map.values();
}
- public static class TableIUD implements Serializable, BulkTableEvent, BinaryWritable {
+ public static final class TableIUD implements Serializable, BulkTableEvent, BinaryWritable {
private static final long serialVersionUID = -1958317571064162089L;
@@ -73,12 +70,10 @@ public final class TransactionEventTable implements Serializable, BinaryWritable
}
public static TableIUD readBinaryMessage(BinaryReadContext dataInput) throws IOException {
-
String table = dataInput.readUTF();
boolean insert = dataInput.readBoolean();
boolean update = dataInput.readBoolean();
boolean delete = dataInput.readBoolean();
-
return new TableIUD(table, insert, update, delete);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/json/DJsonService.java b/ebean-core/src/main/java/io/ebeaninternal/json/DJsonService.java
index d98aa6d7c..aa3443ced 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/json/DJsonService.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/json/DJsonService.java
@@ -17,7 +17,7 @@ import java.util.Set;
/**
* Utility that converts between JSON content and simple java Maps/Lists.
*/
-public class DJsonService implements SpiJsonService {
+public final class DJsonService implements SpiJsonService {
/**
* Write the nested Map/List as json.
diff --git a/ebean-core/src/main/java/io/ebeaninternal/json/EJsonReader.java b/ebean-core/src/main/java/io/ebeaninternal/json/EJsonReader.java
index fa3451f8e..938f7f5a9 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/json/EJsonReader.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/json/EJsonReader.java
@@ -14,7 +14,7 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
-class EJsonReader {
+final class EJsonReader {
static final JsonFactory json = new JsonFactory();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/json/EJsonWriter.java b/ebean-core/src/main/java/io/ebeaninternal/json/EJsonWriter.java
index 1c110c077..2a05f306d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/json/EJsonWriter.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/json/EJsonWriter.java
@@ -14,7 +14,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-class EJsonWriter {
+final class EJsonWriter {
/**
* Base jsonFactory implementation used when it is not passed in.
diff --git a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareFlag.java b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareFlag.java
index 911675605..08cdb273e 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareFlag.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareFlag.java
@@ -7,7 +7,7 @@ import java.io.Serializable;
/**
* Detects when content has been modified and as such needs to be persisted (included in an update).
*/
-public class ModifyAwareFlag implements ModifyAwareType, Serializable {
+public final class ModifyAwareFlag implements ModifyAwareType, Serializable {
private static final long serialVersionUID = 1;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareIterator.java b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareIterator.java
index 7e21e297f..fb57f3447 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareIterator.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareIterator.java
@@ -7,10 +7,9 @@ import java.util.Iterator;
/**
* Wraps an iterator for the purposes of detecting modifications.
*/
-public class ModifyAwareIterator implements Iterator {
+public final class ModifyAwareIterator implements Iterator {
private final ModifyAwareType owner;
-
private final Iterator it;
/**
diff --git a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareList.java b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareList.java
index 7803871df..4743cf27b 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareList.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareList.java
@@ -13,12 +13,11 @@ import java.util.Objects;
/**
* Modify aware wrapper of a list.
*/
-public class ModifyAwareList implements List, ModifyAwareType, Serializable {
+public final class ModifyAwareList implements List, ModifyAwareType, Serializable {
private static final long serialVersionUID = 1;
final List list;
-
final ModifyAwareType owner;
public ModifyAwareList(List list) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareListIterator.java b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareListIterator.java
index 5f6d5f013..974e88700 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareListIterator.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareListIterator.java
@@ -7,10 +7,9 @@ import java.util.ListIterator;
/**
* Modify aware wrapper of a ListIterator.
*/
-public class ModifyAwareListIterator implements ListIterator {
+public final class ModifyAwareListIterator implements ListIterator {
final ModifyAwareType owner;
-
final ListIterator iterator;
public ModifyAwareListIterator(ModifyAwareType owner, ListIterator iterator) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareMap.java b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareMap.java
index 9d5e1fffe..2b2610a2e 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareMap.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareMap.java
@@ -12,15 +12,11 @@ import java.util.Set;
/**
* Map that is wraps an underlying map for the purpose of detecting changes.
*/
-public class ModifyAwareMap implements Map, ModifyAwareType, Serializable {
+public final class ModifyAwareMap implements Map, ModifyAwareType, Serializable {
private static final long serialVersionUID = 1;
final ModifyAwareType owner;
-
- /**
- * The underlying map.
- */
final Map map;
public ModifyAwareMap(Map underlying) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareSet.java b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareSet.java
index f20b39602..06353dd98 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareSet.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/json/ModifyAwareSet.java
@@ -11,13 +11,12 @@ import java.util.Set;
/**
* Wraps a Set for the purposes of detecting modifications.
*/
-public class ModifyAwareSet implements Set, ModifyAwareType, Serializable {
+public final class ModifyAwareSet implements Set, ModifyAwareType, Serializable {
private static final long serialVersionUID = 1;
- protected final ModifyAwareType owner;
-
- protected final Set set;
+ private final ModifyAwareType owner;
+ private final Set set;
/**
* Create as top level with it's own ModifyAwareOwner instance wrapping the given Set.
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/DContainerFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/DContainerFactory.java
index ea9f0b109..77d0a1f72 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/DContainerFactory.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/DContainerFactory.java
@@ -8,7 +8,7 @@ import io.ebeaninternal.server.core.DefaultContainer;
/**
* Default container factory found via service loader.
*/
-public class DContainerFactory implements SpiContainerFactory {
+public final class DContainerFactory implements SpiContainerFactory {
@Override
public SpiContainer create(ContainerConfig containerConfig) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/autotune/NoAutoTuneService.java b/ebean-core/src/main/java/io/ebeaninternal/server/autotune/NoAutoTuneService.java
index b826329bc..b849bbfb7 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/autotune/NoAutoTuneService.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/autotune/NoAutoTuneService.java
@@ -5,7 +5,7 @@ import io.ebeaninternal.api.SpiQuery;
/**
* Noop service when AutoTuneService is not available.
*/
-public class NoAutoTuneService implements AutoTuneService {
+public final class NoAutoTuneService implements AutoTuneService {
@Override
public void startup() {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanRemove.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanRemove.java
index c6f99f194..7c2086c55 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanRemove.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanRemove.java
@@ -8,10 +8,9 @@ import java.util.Collection;
/**
* Change to remove bean from L2 cache.
*/
-class CacheChangeBeanRemove implements CacheChange {
+final class CacheChangeBeanRemove implements CacheChange {
private final BeanDescriptor> descriptor;
-
private final Collection ids;
CacheChangeBeanRemove(Object id, BeanDescriptor> descriptor) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanUpdate.java
index 391e778c2..8203d3a19 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanUpdate.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanUpdate.java
@@ -7,7 +7,7 @@ import java.util.Map;
/**
* Put a new bean entry into the cache.
*/
-class CacheChangeBeanUpdate implements CacheChange {
+final class CacheChangeBeanUpdate implements CacheChange {
private final BeanDescriptor> desc;
private final String key;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeNaturalKeyPut.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeNaturalKeyPut.java
index f90a23749..72f5cfb45 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeNaturalKeyPut.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeNaturalKeyPut.java
@@ -5,7 +5,7 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
/**
* Change the natural key mapping for a bean.
*/
-class CacheChangeNaturalKeyPut implements CacheChange {
+final class CacheChangeNaturalKeyPut implements CacheChange {
private final BeanDescriptor> descriptor;
private final String key;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java
index 20cd7329c..a1393db06 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java
@@ -14,18 +14,13 @@ import java.util.Set;
/**
* List of changes to be applied to L2 cache.
*/
-public class CacheChangeSet {
+public final class CacheChangeSet {
private final List entries = new ArrayList<>();
-
private final Set touchedTables = new HashSet<>();
-
private final Set> queryCaches = new HashSet<>();
-
private final Set> beanCaches = new HashSet<>();
-
private final Map, CacheChangeBeanRemove> beanRemoveMap = new HashMap<>();
-
private final Map manyChangeMap = new HashMap<>();
/**
@@ -173,14 +168,11 @@ public class CacheChangeSet {
/**
* Changes for a specific many property.
*/
- private static class ManyChange implements CacheChange {
+ private static final class ManyChange implements CacheChange {
final ManyKey key;
-
final Set removes = new HashSet<>();
-
final Map puts = new LinkedHashMap<>();
-
boolean clear;
ManyChange(ManyKey key) {
@@ -229,10 +221,9 @@ public class CacheChangeSet {
/**
* Key for changes on a many property.
*/
- private static class ManyKey {
+ private static final class ManyKey {
private final BeanDescriptor> desc;
-
private final String manyProperty;
ManyKey(BeanDescriptor> desc, String manyProperty) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheManagerOptions.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheManagerOptions.java
index 98490702d..ae0c10f4c 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheManagerOptions.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheManagerOptions.java
@@ -10,18 +10,13 @@ import io.ebeaninternal.server.cluster.ClusterManager;
/**
* Configuration options when creating the default cache manager.
*/
-public class CacheManagerOptions {
+public final class CacheManagerOptions {
private final ClusterManager clusterManager;
-
private final DatabaseConfig databaseConfig;
-
private final boolean localL2Caching;
-
private CurrentTenantProvider currentTenantProvider;
-
private QueryCacheEntryValidate queryCacheEntryValidate;
-
private ServerCacheFactory cacheFactory = new DefaultServerCacheFactory();
private ServerCacheOptions beanDefault = new ServerCacheOptions();
private ServerCacheOptions queryDefault = new ServerCacheOptions();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanData.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanData.java
index 930adae21..23ced1d5e 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanData.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanData.java
@@ -11,13 +11,12 @@ import java.util.Map;
/**
* Data held in the bean cache for cached beans.
*/
-public class CachedBeanData implements Externalizable {
+public final class CachedBeanData implements Externalizable {
private long whenCreated;
private long version;
private String discValue;
private Map data;
-
/**
* The sharable bean is effectively transient (near cache only).
*/
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java
index 32ac2ce9a..2fd3fd46b 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java
@@ -9,10 +9,9 @@ import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import java.util.LinkedHashMap;
import java.util.Map;
-public class CachedBeanDataFromBean {
+public final class CachedBeanDataFromBean {
public static CachedBeanData extract(BeanDescriptor> desc, EntityBean bean) {
-
EntityBeanIntercept ebi = bean._ebean_getIntercept();
Map data = new LinkedHashMap<>();
@@ -46,7 +45,6 @@ public class CachedBeanDataFromBean {
}
private static EntityBean createSharableBean(BeanDescriptor> desc, EntityBean bean, EntityBeanIntercept beanEbi) {
-
if (!desc.isCacheSharableBeans() || !beanEbi.isFullyLoadedBean()) {
return null;
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataToBean.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataToBean.java
index 6dbdae1ff..08f3a80fd 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataToBean.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataToBean.java
@@ -7,30 +7,24 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
-public class CachedBeanDataToBean {
-
+public final class CachedBeanDataToBean {
public static void load(BeanDescriptor> desc, EntityBean bean, CachedBeanData cacheBeanData, PersistenceContext context) {
-
EntityBeanIntercept ebi = bean._ebean_getIntercept();
// any future lazy loading skips L2 bean cache
ebi.setLoadedFromCache(true);
-
BeanProperty idProperty = desc.getIdProperty();
if (desc.getInheritInfo() != null) {
desc = desc.getInheritInfo().readType(bean.getClass()).desc();
}
-
if (idProperty != null) {
// load the id property
loadProperty(bean, cacheBeanData, ebi, idProperty, context);
}
-
// load the non-many properties
for (BeanProperty prop : desc.propertiesNonMany()) {
loadProperty(bean, cacheBeanData, ebi, prop, context);
}
-
for (BeanPropertyAssocMany> prop : desc.propertiesMany()) {
if (prop.isElementCollection()) {
loadProperty(bean, cacheBeanData, ebi, prop, context);
@@ -38,12 +32,10 @@ public class CachedBeanDataToBean {
prop.createReferenceIfNull(bean);
}
}
-
ebi.setLoadedLazy();
}
private static void loadProperty(EntityBean bean, CachedBeanData cacheBeanData, EntityBeanIntercept ebi, BeanProperty prop, PersistenceContext context) {
-
if (cacheBeanData.isLoaded(prop.getName())) {
if (!ebi.isLoadedProperty(prop.getPropertyIndex())) {
Object value = cacheBeanData.getData(prop.getName());
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanId.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanId.java
index 45eab630c..6b0fc903b 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanId.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedBeanId.java
@@ -10,7 +10,7 @@ import java.io.ObjectOutput;
*
* Put into L2 cache such that we know the type of a bean with inheritance.
*/
-public class CachedBeanId implements Externalizable {
+public final class CachedBeanId implements Externalizable {
private String discValue;
private Object id;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedManyIds.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedManyIds.java
index 155a43ff5..dd589deea 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedManyIds.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/CachedManyIds.java
@@ -13,7 +13,7 @@ import java.util.List;
* This is effectively just the Id values for each of the beans in the collection.
*
*/
-public class CachedManyIds implements Externalizable {
+public final class CachedManyIds implements Externalizable {
private List idList;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheAdapter.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheAdapter.java
index bd7b0a763..413af049d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheAdapter.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheAdapter.java
@@ -13,7 +13,7 @@ import java.util.List;
* Used to hide the Supplier part of the SpiCacheManager API from public use.
*
*/
-public class DefaultCacheAdapter implements ServerCacheManager {
+public final class DefaultCacheAdapter implements ServerCacheManager {
private final SpiCacheManager cacheManager;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java
index d2d38e7cf..6d632acc3 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java
@@ -23,14 +23,13 @@ import java.util.concurrent.locks.ReentrantLock;
/**
* Manages the construction of caches.
*/
-class DefaultCacheHolder {
+final class DefaultCacheHolder {
private static final Logger log = LoggerFactory.getLogger("io.ebean.cache.ALL");
- private final ReentrantLock lock = new ReentrantLock(false);
+ private final ReentrantLock lock = new ReentrantLock();
private final ConcurrentHashMap allCaches = new ConcurrentHashMap<>();
private final ConcurrentHashMap> collectIdCaches = new ConcurrentHashMap<>();
-
private final ServerCacheFactory cacheFactory;
private final ServerCacheOptions beanDefault;
private final ServerCacheOptions queryDefault;
@@ -76,7 +75,6 @@ class DefaultCacheHolder {
* Return the cache for a given bean type.
*/
private ServerCache getCacheInternal(Class> beanType, ServerCacheType type, String collectionProperty) {
-
String shortName = key(beanType.getSimpleName(), collectionProperty, type);
String fullKey = key(beanType.getName(), collectionProperty, type);
return allCaches.computeIfAbsent(fullKey, s -> createCache(beanType, type, fullKey, shortName));
@@ -143,10 +141,8 @@ class DefaultCacheHolder {
}
private ServerCacheOptions getBeanOptions(Class> cls) {
-
Cache cache = cls.getAnnotation(Cache.class);
boolean nearCache = (cache != null && cache.nearCache());
-
CacheBeanTuning tuning = cls.getAnnotation(CacheBeanTuning.class);
if (tuning != null) {
return new ServerCacheOptions(nearCache, tuning).applyDefaults(beanDefault);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java
index d0c44de1e..ebd086117 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java
@@ -11,6 +11,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
+import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
@@ -37,8 +38,7 @@ public class DefaultServerCache implements ServerCache {
/**
* The underlying map (ConcurrentHashMap or similar)
*/
- protected final Map map;
-
+ protected final Map> map;
protected final CountMetric hitCount;
protected final CountMetric missCount;
protected final CountMetric putCount;
@@ -48,16 +48,11 @@ public class DefaultServerCache implements ServerCache {
protected final String name;
protected final String shortName;
-
- private int maxSize;
-
+ private final int maxSize;
private final int trimFrequency;
-
- private int maxIdleSecs;
-
- private int maxSecsToLive;
-
- private TenantAwareKey tenantAwareKey;
+ private final int maxIdleSecs;
+ private final int maxSecsToLive;
+ private final TenantAwareKey tenantAwareKey;
public DefaultServerCache(DefaultServerCacheConfig config) {
this.name = config.getName();
@@ -70,7 +65,6 @@ public class DefaultServerCache implements ServerCache {
this.trimFrequency = config.determineTrimFrequency();
MetricFactory factory = MetricFactory.get();
-
String prefix = "l2n.";
this.hitCount = factory.createCountMetric(prefix + shortName + ".hit");
this.missCount = factory.createCountMetric(prefix + shortName + ".miss");
@@ -81,12 +75,10 @@ public class DefaultServerCache implements ServerCache {
}
public void periodicTrim(BackgroundExecutor executor) {
-
EvictionRunnable trim = new EvictionRunnable();
-
// default to trimming the cache every 60 seconds
long trimFreqSecs = (trimFrequency == 0) ? 60 : trimFrequency;
- executor.executePeriodically(trim, trimFreqSecs, TimeUnit.SECONDS);
+ executor.scheduleWithFixedDelay(trim, trimFreqSecs, trimFreqSecs, TimeUnit.SECONDS);
}
@Override
@@ -101,11 +93,9 @@ public class DefaultServerCache implements ServerCache {
@Override
public ServerCacheStatistics getStatistics(boolean reset) {
-
ServerCacheStatistics cacheStats = new ServerCacheStatistics();
cacheStats.setCacheName(name);
cacheStats.setMaxSize(maxSize);
-
cacheStats.setSize(size());
cacheStats.setHitCount(hitCount.get(reset));
cacheStats.setMissCount(missCount.get(reset));
@@ -113,7 +103,6 @@ public class DefaultServerCache implements ServerCache {
cacheStats.setRemoveCount(removeCount.get(reset));
cacheStats.setClearCount(clearCount.get(reset));
cacheStats.setEvictCount(evictCount.get(reset));
-
return cacheStats;
}
@@ -133,10 +122,8 @@ public class DefaultServerCache implements ServerCache {
@Override
public int getHitRatio() {
-
long mc = missCount.get(false);
long hc = hitCount.get(false);
-
long totalCount = hc + mc;
if (totalCount == 0) {
return 0;
@@ -177,7 +164,6 @@ public class DefaultServerCache implements ServerCache {
*/
@Override
public Object get(Object id) {
-
CacheEntry entry = getCacheEntry(id);
if (entry == null) {
missCount.increment();
@@ -199,7 +185,8 @@ public class DefaultServerCache implements ServerCache {
* Get the cache entry - override for query cache to validate dependent tables.
*/
protected CacheEntry getCacheEntry(Object id) {
- return map.get(key(id));
+ final SoftReference ref = map.get(key(id));
+ return ref != null ? ref.get() : null;
}
@Override
@@ -213,7 +200,7 @@ public class DefaultServerCache implements ServerCache {
@Override
public void put(Object id, Object value) {
Object key = key(id);
- map.put(key, new CacheEntry(key, value));
+ map.put(key, new SoftReference<>(new CacheEntry(key, value)));
putCount.increment();
}
@@ -222,8 +209,8 @@ public class DefaultServerCache implements ServerCache {
*/
@Override
public void remove(Object id) {
- CacheEntry entry = map.remove(key(id));
- if (entry != null) {
+ SoftReference entry = map.remove(key(id));
+ if (entry != null && entry.get() != null) {
removeCount.increment();
}
}
@@ -250,74 +237,69 @@ public class DefaultServerCache implements ServerCache {
* Run the eviction based on Idle time, Time to live and LRU last access.
*/
public void runEviction() {
-
long trimForMaxSize;
if (maxSize == 0) {
trimForMaxSize = 0;
} else {
trimForMaxSize = size() - maxSize;
}
-
if (maxIdleSecs == 0 && maxSecsToLive == 0 && trimForMaxSize < 0) {
// nothing to trim on this cache
return;
}
-
long startNanos = System.nanoTime();
-
long trimmedByIdle = 0;
+ long trimmedByGC = 0;
long trimmedByTTL = 0;
long trimmedByLRU = 0;
List activeList = new ArrayList<>(map.size());
-
long idleExpireNano = startNanos - TimeUnit.SECONDS.toNanos(maxIdleSecs);
long ttlExpireNano = startNanos - TimeUnit.SECONDS.toNanos(maxSecsToLive);
-
- Iterator it = map.values().iterator();
+ Iterator> it = map.values().iterator();
while (it.hasNext()) {
- CacheEntry cacheEntry = it.next();
- if (maxIdleSecs > 0 && idleExpireNano > cacheEntry.getLastAccessTime()) {
+ SoftReference ref = it.next();
+ final CacheEntry cacheEntry = ref.get();
+ if (cacheEntry == null) {
+ it.remove();
+ trimmedByGC++;
+ } else if (maxIdleSecs > 0 && idleExpireNano > cacheEntry.getLastAccessTime()) {
it.remove();
trimmedByIdle++;
-
} else if (maxSecsToLive > 0 && ttlExpireNano > cacheEntry.getCreateTime()) {
it.remove();
trimmedByTTL++;
-
} else if (trimForMaxSize > 0) {
activeList.add(cacheEntry);
}
}
-
- if (trimForMaxSize > 0) {
- trimmedByLRU = activeList.size() - maxSize;
- if (trimmedByLRU > 0) {
- // sort into last access time ascending
- activeList.sort(BY_LAST_ACCESS);
- int trimSize = getTrimSize();
- for (int i = trimSize; i < activeList.size(); i++) {
- // remove if still in the cache
- map.remove(activeList.get(i).getKey());
+ if (trimForMaxSize > 0 && activeList.size() > maxSize) {
+ // sort into last access time ascending
+ activeList.sort(BY_LAST_ACCESS);
+ int trimSize = getTrimSize();
+ for (int i = trimSize; i < activeList.size(); i++) {
+ // remove if still in the cache
+ if (map.remove(activeList.get(i).getKey()) != null) {
+ trimmedByLRU++;
}
}
}
evictCount.add(trimmedByIdle);
+ evictCount.add(trimmedByGC);
evictCount.add(trimmedByTTL);
evictCount.add(trimmedByLRU);
-
if (logger.isTraceEnabled()) {
long exeMicros = TimeUnit.MICROSECONDS.convert(System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
- logger.trace("Executed trim of cache {} in [{}]millis idle[{}] timeToLive[{}] accessTime[{}]"
- , name, exeMicros, trimmedByIdle, trimmedByTTL, trimmedByLRU);
+ logger.trace("Executed trim of cache {} in [{}]millis idle[{}] timeToLive[{}] accessTime[{}] gc[{}]",
+ name, exeMicros, trimmedByIdle, trimmedByTTL, trimmedByLRU, trimmedByGC);
}
}
/**
* Runnable that calls the eviction routine.
*/
- public class EvictionRunnable implements Runnable {
+ public final class EvictionRunnable implements Runnable {
@Override
public void run() {
@@ -328,7 +310,7 @@ public class DefaultServerCache implements ServerCache {
/**
* Comparator for sorting by last access time.
*/
- public static class CompareByLastAccess implements Comparator, Serializable {
+ public static final class CompareByLastAccess implements Comparator, Serializable {
private static final long serialVersionUID = 1L;
@@ -341,7 +323,7 @@ public class DefaultServerCache implements ServerCache {
/**
* Wraps the value to additionally hold createTime and lastAccessTime and hit counter.
*/
- public static class CacheEntry {
+ public static final class CacheEntry {
private final Object key;
private final Object value;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheConfig.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheConfig.java
index 638f237a2..2c329a62a 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheConfig.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheConfig.java
@@ -4,26 +4,26 @@ import io.ebean.cache.QueryCacheEntryValidate;
import io.ebean.cache.ServerCacheConfig;
import io.ebean.cache.ServerCacheOptions;
import io.ebean.config.CurrentTenantProvider;
+import io.ebeaninternal.server.cache.DefaultServerCache.CacheEntry;
+import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-public class DefaultServerCacheConfig {
+public final class DefaultServerCacheConfig {
private final ServerCacheConfig config;
-
- private int maxSize;
- private int maxIdleSecs;
- private int maxSecsToLive;
- private int trimFrequency;
-
- private Map map;
+ private final int maxSize;
+ private final int maxIdleSecs;
+ private final int maxSecsToLive;
+ private final int trimFrequency;
+ private final Map> map;
public DefaultServerCacheConfig(ServerCacheConfig config) {
this(config, new ConcurrentHashMap<>());
}
- public DefaultServerCacheConfig(ServerCacheConfig config, Map map) {
+ public DefaultServerCacheConfig(ServerCacheConfig config, Map> map) {
this.config = config;
this.map = map;
@@ -50,7 +50,7 @@ public class DefaultServerCacheConfig {
return config.getShortName();
}
- public Map getMap() {
+ public Map> getMap() {
return map;
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheFactory.java
index 1b0476f3c..eab615ed1 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheFactory.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheFactory.java
@@ -11,7 +11,7 @@ import io.ebean.cache.ServerCacheNotify;
/**
* Default implementation of ServerCacheFactory.
*/
-class DefaultServerCacheFactory implements ServerCacheFactory {
+final class DefaultServerCacheFactory implements ServerCacheFactory {
private final BackgroundExecutor executor;
@@ -31,7 +31,6 @@ class DefaultServerCacheFactory implements ServerCacheFactory {
@Override
public ServerCache createCache(ServerCacheConfig config) {
-
DefaultServerCache cache;
if (config.isQueryCache()) {
// use a server cache aware of extra validation and QueryCacheEntry
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java
index 7214437cc..562a98371 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java
@@ -19,18 +19,14 @@ import java.util.Map;
/**
* Manages the bean and query caches.
*/
-public class DefaultServerCacheManager implements SpiCacheManager {
+public final class DefaultServerCacheManager implements SpiCacheManager {
private static final Logger log = LoggerFactory.getLogger("io.ebean.cache.REGION");
private final Map regionMap = new HashMap<>();
-
private final ClusterManager clusterManager;
-
private final DefaultCacheHolder cacheHolder;
-
private final boolean localL2Caching;
-
private final String serverName;
/**
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCachePlugin.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCachePlugin.java
index 262aa8bf2..d4a6a247f 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCachePlugin.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCachePlugin.java
@@ -8,7 +8,7 @@ import io.ebean.config.DatabaseConfig;
/**
* Default implementation of ServerCachePlugin.
*/
-public class DefaultServerCachePlugin implements ServerCachePlugin {
+public final class DefaultServerCachePlugin implements ServerCachePlugin {
/**
* Creates the default ServerCacheFactory.
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerQueryCache.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerQueryCache.java
index afbdb37f7..4f437eafc 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerQueryCache.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerQueryCache.java
@@ -3,6 +3,8 @@ package io.ebeaninternal.server.cache;
import io.ebean.cache.QueryCacheEntry;
import io.ebean.cache.QueryCacheEntryValidate;
+import java.lang.ref.SoftReference;
+
/**
* Server cache for query caching.
*
@@ -27,7 +29,8 @@ public class DefaultServerQueryCache extends DefaultServerCache {
@Override
protected CacheEntry getCacheEntry(Object id) {
Object key = key(id);
- CacheEntry entry = map.get(key);
+ final SoftReference ref = map.get(key);
+ CacheEntry entry = ref != null ? ref.get() : null;
if (entry == null) {
return null;
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/RemoteCacheEvent.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/RemoteCacheEvent.java
index ef327d69e..21f04704d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/RemoteCacheEvent.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/RemoteCacheEvent.java
@@ -12,11 +12,10 @@ import java.util.List;
/**
* Cache events broadcast across the cluster.
*/
-public class RemoteCacheEvent implements BinaryWritable {
+public final class RemoteCacheEvent implements BinaryWritable {
- private boolean clearAll;
-
- private List clearCaches;
+ private final boolean clearAll;
+ private final List clearCaches;
/**
* Clear all the caches.
@@ -57,10 +56,8 @@ public class RemoteCacheEvent implements BinaryWritable {
}
public static RemoteCacheEvent readBinaryMessage(BinaryReadContext dataInput) throws IOException {
-
boolean clearAll = dataInput.readBoolean();
int size = dataInput.readInt();
-
List clearCache = null;
if (size > 0) {
clearCache = new ArrayList<>(size);
@@ -68,7 +65,6 @@ public class RemoteCacheEvent implements BinaryWritable {
clearCache.add(dataInput.readUTF());
}
}
-
return new RemoteCacheEvent(clearAll, clearCache);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java
index 372de9589..f399b78ad 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java
@@ -14,21 +14,14 @@ import java.util.Map;
/**
* Builds JSON document for a bean change.
*/
-class ChangeJsonBuilder {
+final class ChangeJsonBuilder {
- protected final JsonFactory jsonFactory = new JsonFactory();
-
- protected final JsonContext json;
-
- ChangeJsonBuilder(JsonContext json) {
- this.json = json;
- }
+ private final JsonFactory jsonFactory = new JsonFactory();
/**
* Write the bean change as JSON.
*/
void writeBeanJson(Writer writer, BeanChange bean, ChangeSet changeSet) throws IOException {
-
try (JsonGenerator generator = jsonFactory.createGenerator(writer)) {
writeBeanChange(generator, bean, changeSet);
generator.flush();
@@ -39,9 +32,7 @@ class ChangeJsonBuilder {
* Write the bean change as JSON document containing the transaction header details.
*/
private void writeBeanChange(JsonGenerator gen, BeanChange bean, ChangeSet changeSet) throws IOException {
-
gen.writeStartObject();
-
gen.writeNumberField("ts", bean.getEventTime());
gen.writeStringField("change", bean.getEvent().getCode());
gen.writeStringField("type", bean.getType());
@@ -49,9 +40,7 @@ class ChangeJsonBuilder {
if (bean.getTenantId() != null) {
gen.writeStringField("tenantId", bean.getTenantId().toString());
}
-
writeBeanTransactionDetails(gen, changeSet);
-
writeBeanValues(gen, bean);
gen.writeEndObject();
}
@@ -60,7 +49,6 @@ class ChangeJsonBuilder {
* Denormalise by writing the transaction header details.
*/
private void writeBeanTransactionDetails(JsonGenerator gen, ChangeSet changeSet) throws IOException {
-
String source = changeSet.getSource();
if (source != null) {
gen.writeStringField("source", source);
@@ -87,12 +75,10 @@ class ChangeJsonBuilder {
* For insert and update write the new/old values.
*/
private void writeBeanValues(JsonGenerator gen, BeanChange bean) throws IOException {
-
if (bean.getEvent() != ChangeType.DELETE) {
gen.writeFieldName("data");
gen.writeRaw(":");
gen.writeRaw(bean.getData());
-
String oldData = bean.getOldData();
if (oldData != null) {
gen.writeRaw(",\"oldData\":");
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogListener.java b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogListener.java
index 0168fa222..6e12aa31d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogListener.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogListener.java
@@ -15,12 +15,12 @@ import java.util.Properties;
/**
* Simply logs the change sets in JSON form to logger named io.ebean.ChangeLog.
*/
-public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
+public final class DefaultChangeLogListener implements ChangeLogListener, Plugin {
/**
* The usual application specific logger.
*/
- protected static final Logger logger = LoggerFactory.getLogger(DefaultChangeLogListener.class);
+ private static final Logger logger = LoggerFactory.getLogger(DefaultChangeLogListener.class);
/**
* The named logger we send the change set payload to. Can be externally configured as desired.
@@ -45,8 +45,7 @@ public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
*/
@Override
public void configure(SpiServer server) {
- jsonBuilder = new ChangeJsonBuilder(server.json());
-
+ jsonBuilder = new ChangeJsonBuilder();
Properties properties = server.getServerConfig().getProperties();
if (properties != null) {
String bufferSize = properties.getProperty("ebean.changeLog.bufferSize");
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogPrepare.java b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogPrepare.java
index 16af27424..596d4d331 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogPrepare.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogPrepare.java
@@ -11,7 +11,7 @@ import io.ebean.event.changelog.ChangeSet;
* on the changeSet.
*
*/
-public class DefaultChangeLogPrepare implements ChangeLogPrepare {
+public final class DefaultChangeLogPrepare implements ChangeLogPrepare {
/**
* Just return true to send change set through to the logger.
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java
index a6208a703..e7a7bf303 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java
@@ -14,10 +14,9 @@ import java.util.Set;
/**
* Default implementation of ChangeLogRegister.
*/
-public class DefaultChangeLogRegister implements ChangeLogRegister {
+public final class DefaultChangeLogRegister implements ChangeLogRegister {
private static final BasicFilter INCLUDE_INSERTS = new BasicFilter(true);
-
private static final BasicFilter EXCLUDE_INSERTS = new BasicFilter(false);
private final boolean defaultInsertsInclude;
@@ -31,20 +30,16 @@ public class DefaultChangeLogRegister implements ChangeLogRegister {
@Override
public ChangeLogFilter getChangeFilter(Class> beanType) {
-
ChangeLog changeLog = getChangeLog(beanType);
if (changeLog == null) {
return null;
}
-
String[] updatesThatInclude = changeLog.updatesThatInclude();
if (updatesThatInclude.length == 0) {
return insertModeInclude(changeLog.inserts()) ? INCLUDE_INSERTS : EXCLUDE_INSERTS;
}
-
Set updateProps = new HashSet<>();
Collections.addAll(updateProps, updatesThatInclude);
-
return new UpdateFilter(insertModeInclude(changeLog.inserts()), updateProps);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cluster/ClusterManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/cluster/ClusterManager.java
index face12592..d2bdaeda7 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/cluster/ClusterManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/cluster/ClusterManager.java
@@ -18,7 +18,7 @@ public class ClusterManager implements ServerLookup {
private static final Logger clusterLogger = LoggerFactory.getLogger("io.ebean.Cluster");
- private final ReentrantLock lock = new ReentrantLock(false);
+ private final ReentrantLock lock = new ReentrantLock();
private final ConcurrentHashMap serverMap = new ConcurrentHashMap<>();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java
index 606d51b40..a88f1bbe9 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java
@@ -1,14 +1,10 @@
package io.ebeaninternal.server.core;
-import io.ebean.EbeanServer;
+import io.ebean.CancelableQuery;
import io.ebean.Transaction;
import io.ebean.util.JdbcClose;
-import io.ebeaninternal.api.BindParams;
-import io.ebeaninternal.api.SpiEbeanServer;
-import io.ebeaninternal.api.SpiQuery;
-import io.ebeaninternal.api.SpiSqlBinding;
-import io.ebeaninternal.api.SpiTransaction;
-import io.ebeaninternal.server.lib.Str;
+import io.ebeaninternal.api.*;
+import io.ebeaninternal.server.util.Str;
import io.ebeaninternal.server.persist.Binder;
import io.ebeaninternal.server.persist.TrimLogSql;
import io.ebeaninternal.server.util.BindParamsParser;
@@ -17,29 +13,25 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.persistence.PersistenceException;
/**
* Wraps the objects involved in executing a SQL / Relational Query.
*/
-public abstract class AbstractSqlQueryRequest {
+public abstract class AbstractSqlQueryRequest implements CancelableQuery {
protected final SpiSqlBinding query;
-
protected final SpiEbeanServer server;
-
- protected SpiTransaction trans;
-
+ protected SpiTransaction transaction;
private boolean createdTransaction;
-
protected String sql;
-
protected ResultSet resultSet;
-
protected String bindLog = "";
-
protected PreparedStatement pstmt;
-
protected long startNano;
+ private final ReentrantLock lock = new ReentrantLock();
/**
* Create the BeanFindRequest.
@@ -47,18 +39,19 @@ public abstract class AbstractSqlQueryRequest {
AbstractSqlQueryRequest(SpiEbeanServer server, SpiSqlBinding query, Transaction t) {
this.server = server;
this.query = query;
- this.trans = (SpiTransaction) t;
+ this.transaction = (SpiTransaction) t;
+ this.query.setCancelableQuery(this);
}
/**
* Create a transaction if none currently exists.
*/
public void initTransIfRequired() {
- if (trans == null) {
- trans = server.currentServerTransaction();
- if (trans == null || !trans.isActive()) {
+ if (transaction == null) {
+ transaction = server.currentServerTransaction();
+ if (transaction == null || !transaction.isActive()) {
// create a local readOnly transaction
- trans = server.createReadOnlyTransaction(null);
+ transaction = server.createReadOnlyTransaction(null);
createdTransaction = true;
}
}
@@ -69,27 +62,20 @@ public abstract class AbstractSqlQueryRequest {
*/
public void endTransIfRequired() {
if (createdTransaction) {
- trans.commit();
+ transaction.commit();
}
}
- public EbeanServer getServer() {
- return server;
- }
-
- public SpiTransaction getTransaction() {
- return trans;
+ protected void flushJdbcBatchOnQuery() {
+ if (transaction.isFlushOnQuery()) {
+ transaction.flush();
+ }
}
public boolean isLogSql() {
- return trans.isLogSql();
+ return transaction.isLogSql();
}
- /**
- * Set the resultSet and associated query plan if known.
- */
- abstract void setResultSet(ResultSet resultSet, Object queryPlanKey) throws SQLException;
-
/**
* Return the bindLog for this request.
*/
@@ -97,12 +83,15 @@ public abstract class AbstractSqlQueryRequest {
return bindLog;
}
+ /**
+ * Set the resultSet and associated query plan if known.
+ */
+ abstract void setResultSet(ResultSet resultSet, Object queryPlanKey) throws SQLException;
+
/**
* Return true if we can navigate to the next row.
*/
- public boolean next() throws SQLException {
- return resultSet.next();
- }
+ public abstract boolean next() throws SQLException;
protected abstract void requestComplete();
@@ -115,12 +104,10 @@ public abstract class AbstractSqlQueryRequest {
JdbcClose.close(pstmt);
}
-
/**
* Prepare the SQL taking into account named bind parameters.
*/
private void prepareSql() {
-
String sql = query.getQuery();
BindParams bindParams = query.getBindParams();
if (!bindParams.isEmpty()) {
@@ -131,7 +118,6 @@ public abstract class AbstractSqlQueryRequest {
}
private String limitOffset(String sql) {
-
int firstRow = query.getFirstRow();
int maxRows = query.getMaxRows();
if (firstRow > 0 || maxRows > 0) {
@@ -149,28 +135,31 @@ public abstract class AbstractSqlQueryRequest {
}
protected void executeAsSql(Binder binder) throws SQLException {
-
- prepareSql();
- Connection conn = trans.getInternalConnection();
-
- pstmt = conn.prepareStatement(sql);
- if (query.getTimeout() > 0) {
- pstmt.setQueryTimeout(query.getTimeout());
+ lock.lock();
+ try {
+ query.checkCancelled();
+ prepareSql();
+ Connection conn = transaction.getInternalConnection();
+ pstmt = conn.prepareStatement(sql);
+ if (query.getTimeout() > 0) {
+ pstmt.setQueryTimeout(query.getTimeout());
+ }
+ if (query.getBufferFetchSizeHint() > 0) {
+ pstmt.setFetchSize(query.getBufferFetchSizeHint());
+ }
+ BindParams bindParams = query.getBindParams();
+ if (!bindParams.isEmpty()) {
+ this.bindLog = binder.bind(bindParams, pstmt, conn);
+ }
+ if (isLogSql()) {
+ long micros = (System.nanoTime() - startNano) / 1000L;
+ transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ") --micros(", micros + ")"));
+ }
+ } finally {
+ lock.unlock();
}
- if (query.getBufferFetchSizeHint() > 0) {
- pstmt.setFetchSize(query.getBufferFetchSizeHint());
- }
-
- BindParams bindParams = query.getBindParams();
- if (!bindParams.isEmpty()) {
- this.bindLog = binder.bind(bindParams, pstmt, conn);
- }
-
- if (isLogSql()) {
- trans.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
- }
-
setResultSet(pstmt.executeQuery(), null);
+ query.checkCancelled();
}
/**
@@ -180,4 +169,13 @@ public abstract class AbstractSqlQueryRequest {
return sql;
}
+ @Override
+ public void cancel() {
+ lock.lock();
+ try {
+ JdbcClose.cancel(pstmt);
+ } finally {
+ lock.unlock();
+ }
+ }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/BindPadding.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/BindPadding.java
index 75963723e..8659fabfd 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/BindPadding.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/BindPadding.java
@@ -33,7 +33,7 @@ public final class BindPadding {
* Extra padding on binding id's in order to get better hit ratio on DB prepared statements / query plans.
*/
static int padding(int size) {
- if (size == 1) {
+ if (size <= 1) {
return 0;
}
if (size <= 5) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java
index 8bf649438..cb78076bc 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java
@@ -17,7 +17,7 @@ import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
-class DScriptRunner implements ScriptRunner {
+final class DScriptRunner implements ScriptRunner {
private static final String NEWLINE = "\n";
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java
index 18c1051a0..9a7c54034 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java
@@ -36,9 +36,8 @@ import java.sql.SQLException;
/**
* Create a DatabasePlatform from the configuration.
*
- * Will used platform name or use the meta data from the JDBC driver to
+ * Will used platform name or use the metadata from the JDBC driver to
* determine the platform automatically.
- *