mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
205
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7035eb228 | ||
|
|
1bf09ddd38 | ||
|
|
a8bf27da55 | ||
|
|
ee8bb5ef75 | ||
|
|
bcfd9cab1d | ||
|
|
2a5fd7f3a9 | ||
|
|
f8c37772d4 | ||
|
|
d988dbe69d | ||
|
|
5cb9b9b237 | ||
|
|
819d391495 | ||
|
|
1f0ed629a7 | ||
|
|
91044d0a75 | ||
|
|
57736185d0 | ||
|
|
8992980689 | ||
|
|
f341ff0167 | ||
|
|
abfa841ed7 | ||
|
|
e68adf53bc | ||
|
|
562d175de1 | ||
|
|
b5cef354ad | ||
|
|
09a887aeb7 | ||
|
|
53c903fa48 | ||
|
|
548454c90d | ||
|
|
94bab5f83b | ||
|
|
78965c1ac4 | ||
|
|
00b0fcaeb3 | ||
|
|
f7905c8e94 | ||
|
|
7ab7b612f6 | ||
|
|
ce1d2803ce | ||
|
|
9b066e4eac | ||
|
|
e1f9d106f0 | ||
|
|
e40a539406 | ||
|
|
bb73c30bb7 | ||
|
|
2e16f05a70 | ||
|
|
1837c3443d | ||
|
|
bd1060ff28 | ||
|
|
c9968a9662 | ||
|
|
e3001e8bdd | ||
|
|
82bb2b4567 | ||
|
|
f483002e39 | ||
|
|
c2a87a0c88 | ||
|
|
733886c70b | ||
|
|
899fdca995 | ||
|
|
4731ee034e | ||
|
|
e6faf9efd1 | ||
|
|
d75d1fce43 | ||
|
|
ef3980a3ee | ||
|
|
2a1c170aa4 | ||
|
|
d65e769dd7 | ||
|
|
2bb5a85bd6 | ||
|
|
98aee9ca62 | ||
|
|
c86f965d36 | ||
|
|
81e445e386 | ||
|
|
2cbbbc4419 | ||
|
|
632f43016e | ||
|
|
fe62ad2ea2 | ||
|
|
f416309863 | ||
|
|
dcf5e7a15c | ||
|
|
af4f70e3a7 | ||
|
|
79dde23ec6 | ||
|
|
e4d0341e67 | ||
|
|
907b182931 | ||
|
|
1a26cd717f | ||
|
|
7bfacb095b | ||
|
|
6315edfc21 | ||
|
|
464f48d717 | ||
|
|
eacfaf3403 | ||
|
|
36d8028045 | ||
|
|
297c1d7abb | ||
|
|
5a73c85e6e | ||
|
|
68138bc009 | ||
|
|
8c7df3212a | ||
|
|
5ebad53072 | ||
|
|
640824ae31 | ||
|
|
6acf93c978 | ||
|
|
6897e06dcd | ||
|
|
d036af8d1a | ||
|
|
b77c62e0db | ||
|
|
bbf46bb0ec | ||
|
|
6aa4337b27 | ||
|
|
99d7a4e4b4 | ||
|
|
b7da2cbd97 | ||
|
|
b4ae721998 | ||
|
|
3bb4126e26 | ||
|
|
de439e7695 | ||
|
|
e5ec72df5c | ||
|
|
98a89156bf | ||
|
|
dc65475dbb | ||
|
|
bab637667d | ||
|
|
cc64ff5f10 | ||
|
|
ab87174e5b | ||
|
|
a6fdc42934 | ||
|
|
4c16b85e85 | ||
|
|
9e3d6c6a08 | ||
|
|
d567872813 | ||
|
|
7069d3f600 | ||
|
|
b2f8b7c711 | ||
|
|
4092ea307e | ||
|
|
01f64ef9f9 | ||
|
|
f1a48b182e | ||
|
|
8ac5467856 | ||
|
|
758f84f573 | ||
|
|
2073fefc92 | ||
|
|
5b98d8d9c2 | ||
|
|
8e59e72983 | ||
|
|
b56d68dac7 | ||
|
|
e0c3a92b60 | ||
|
|
213562fcf1 | ||
|
|
d3a09b93c0 | ||
|
|
536ea57369 | ||
|
|
ac1dd4cbab | ||
|
|
bc33e05906 | ||
|
|
8ecc407cb0 | ||
|
|
2672dff512 | ||
|
|
74d907bbce | ||
|
|
53aacabce7 | ||
|
|
ab9cdd8f5b | ||
|
|
b3d439ed11 | ||
|
|
d57657c209 | ||
|
|
dd080c5933 | ||
|
|
0be98d7989 | ||
|
|
fca54dbbe3 | ||
|
|
7ec5fe6a85 | ||
|
|
9ea5505214 | ||
|
|
75d3b566a7 | ||
|
|
e38059f52c | ||
|
|
72331aa945 | ||
|
|
e497c54f82 | ||
|
|
b44091ff3a | ||
|
|
2ab889f889 | ||
|
|
23541e661e | ||
|
|
6778e37652 | ||
|
|
5eac31283d | ||
|
|
38016773d4 | ||
|
|
ad80eb267f | ||
|
|
449e0832e0 | ||
|
|
f28f5c9a01 | ||
|
|
9dd28d8879 | ||
|
|
b47eec99a5 | ||
|
|
99bb55efa0 | ||
|
|
4408eb618a | ||
|
|
9ccd1b1b27 | ||
|
|
0c1657fef5 | ||
|
|
ee393c6485 | ||
|
|
7f68b9d846 | ||
|
|
2035a3344e | ||
|
|
ade36582a0 | ||
|
|
a4b8be61e3 | ||
|
|
b7feb62ecd | ||
|
|
7e48f7c8eb | ||
|
|
501f8111fe | ||
|
|
d8f4503d98 | ||
|
|
0d83978c58 | ||
|
|
69e56be459 | ||
|
|
35a721039d | ||
|
|
98b4ac5029 | ||
|
|
fe874c946d | ||
|
|
53386e9bf4 | ||
|
|
5435055f5c | ||
|
|
583e10f5c6 | ||
|
|
5393d709a3 | ||
|
|
fc1c9392aa | ||
|
|
6b2c48d4f6 | ||
|
|
400ae3cf4b | ||
|
|
f22a322104 | ||
|
|
e98ff952bf | ||
|
|
573e1b553c | ||
|
|
a674d0d696 | ||
|
|
5393161c16 | ||
|
|
1bdc6d8f7f | ||
|
|
e9081a176f | ||
|
|
3e7db2887f | ||
|
|
fcf14ac222 | ||
|
|
55324653a5 | ||
|
|
a132f09b03 | ||
|
|
48eac84930 | ||
|
|
948619a125 | ||
|
|
0cd0741f18 | ||
|
|
01a3455ecb | ||
|
|
610f7db1cb | ||
|
|
373ef6d1eb | ||
|
|
f1b1cee2fd | ||
|
|
a73e4f23ff | ||
|
|
9a5d3498c1 | ||
|
|
0b24d635ad | ||
|
|
b623e19dae | ||
|
|
7389a8b344 | ||
|
|
d68f477e8e | ||
|
|
dbe273bb56 | ||
|
|
1a4377da29 | ||
|
|
c3cdecaa60 | ||
|
|
9d706056c6 | ||
|
|
cb0100f02a | ||
|
|
9c6dd0989a | ||
|
|
a23a179bbf | ||
|
|
365b7c4600 | ||
|
|
ed2455f460 | ||
|
|
046033627a | ||
|
|
786444a6b9 | ||
|
|
9ab26cfb9d | ||
|
|
aa1e3ea1bc | ||
|
|
c66875449b | ||
|
|
f6c4024ae5 | ||
|
|
2c8cc793ab | ||
|
|
66454d2e61 | ||
|
|
e7b13cf2d9 |
+1
-4
@@ -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
|
||||
*uuid.state
|
||||
|
||||
+1
-1
@@ -293,6 +293,6 @@ ebean.tenant.schemaProvider
|
||||
ebean.updateAllPropertiesInBatch
|
||||
ebean.updateChangesOnly
|
||||
ebean.updatesDeleteMissingChildren
|
||||
ebean.useJavaxValidationNotNull
|
||||
ebean.useValidationNotNull
|
||||
ebean.useJtaTransactionManager
|
||||
|
||||
|
||||
+5
-18
@@ -4,18 +4,13 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
<description>ebean api</description>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.11.3</jackson-core.version>
|
||||
<jackson-databind.version>2.11.3</jackson-databind.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--
|
||||
@@ -32,7 +27,7 @@
|
||||
<dependency>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>avaje-config</artifactId>
|
||||
<version>1.2</version>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
@@ -55,7 +50,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>6.13</version>
|
||||
<version>6.15</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -74,7 +69,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -82,7 +77,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -102,14 +97,6 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope to read validation annotations Size etc -->
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>1.1.0.Final</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* This service is used internally by Ebean for executing background tasks such
|
||||
* as the {@link Query#findFutureList()} and also for executing background tasks
|
||||
* periodically.
|
||||
* </p>
|
||||
* This service can be used to execute tasks in the background.
|
||||
* <p>
|
||||
* 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).
|
||||
* </p>
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* 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);
|
||||
<T> Future<T> submit(Callable<T> 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.
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>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
|
||||
*/
|
||||
<V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit);
|
||||
|
||||
<V> ScheduledFuture<V> schedule(Callable<V> task, long delay, TimeUnit unit);
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.Optional;
|
||||
*
|
||||
* public class CustomerFinder extends BeanFinder<Long,Customer> {
|
||||
*
|
||||
* @Inject
|
||||
* @Inject
|
||||
* public CustomerFinder(Database database) {
|
||||
* super(Customer.class, database);
|
||||
* }
|
||||
|
||||
@@ -9,10 +9,10 @@ import java.util.Collection;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* @Repository
|
||||
* @Repository
|
||||
* public class CustomerRepository extends BeanRepository<Long,Customer> {
|
||||
*
|
||||
* @Inject
|
||||
* @Inject
|
||||
* public CustomerRepository(Database server) {
|
||||
* super(Customer.class, server);
|
||||
* }
|
||||
|
||||
@@ -897,7 +897,7 @@ public interface Database {
|
||||
* <pre>{@code
|
||||
* public class Order { ...
|
||||
*
|
||||
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
|
||||
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
|
||||
* List<OrderDetail> details;
|
||||
* ...
|
||||
* }
|
||||
|
||||
@@ -31,8 +31,9 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
*/
|
||||
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();
|
||||
@@ -76,6 +77,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());
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ final class DbContext {
|
||||
|
||||
private final HashMap<String, Database> syncMap = new HashMap<>();
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock(false);
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* The 'default' Database.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ public interface DocumentStore {
|
||||
* .setUseDocStore(true)
|
||||
* .where()... // perhaps add predicates
|
||||
* .findEachWhile(new Predicate<Order>() {
|
||||
* @Override
|
||||
* @Override
|
||||
* public void accept(Order bean) {
|
||||
* // process the bean
|
||||
*
|
||||
|
||||
@@ -53,6 +53,17 @@ public interface DtoQuery<T> {
|
||||
*/
|
||||
void findEach(Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Execute the query iterating the results and batching them for the consumer.
|
||||
* <p>
|
||||
* 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<List<T>> consumer);
|
||||
|
||||
/**
|
||||
* Execute the query iterating a row at a time with the ability to stop consuming part way through.
|
||||
* <p>
|
||||
|
||||
@@ -346,7 +346,7 @@ public final class Ebean {
|
||||
* <pre>{@code
|
||||
* public class Order { ...
|
||||
*
|
||||
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
|
||||
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
|
||||
* List<OrderDetail> details;
|
||||
* ...
|
||||
* }
|
||||
|
||||
@@ -200,7 +200,7 @@ public interface ExpressionFactory {
|
||||
Expression gtOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater than or Equal to OR Null <code> >= or null </code>
|
||||
* Greater than or Equal to OR Null ({@code >= or null })
|
||||
* <p>
|
||||
* 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 <code> <= or null </code>
|
||||
* Less Than or Equal to OR Null ({@code <= or null })
|
||||
* <p>
|
||||
* A convenient expression combining LE and Is Null. Most often useful for range
|
||||
* expressions where the bottom range value is nullable.
|
||||
|
||||
@@ -169,16 +169,33 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
UpdateQuery<T> asUpdate();
|
||||
|
||||
/**
|
||||
* Execute the query with the given lock type and WAIT.
|
||||
* <p>
|
||||
* Note that <code>forUpdate()</code> is the same as
|
||||
* <code>withLock(LockType.UPDATE)</code>.
|
||||
* <p>
|
||||
* Provides us with the ability to explicitly use Postgres
|
||||
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
|
||||
*/
|
||||
Query<T> withLock(Query.LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute the query with the given lock type and lock wait.
|
||||
* <p>
|
||||
* Note that <code>forUpdateNoWait()</code> is the same as
|
||||
* <code>withLock(LockType.UPDATE, LockWait.NOWAIT)</code>.
|
||||
* <p>
|
||||
* Provides us with the ability to explicitly use Postgres
|
||||
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
|
||||
*/
|
||||
Query<T> withLock(Query.LockType lockType, Query.LockWait lockWait);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause which results in the DB locking the record.
|
||||
*/
|
||||
Query<T> forUpdate();
|
||||
|
||||
/**
|
||||
* Execute using "for update" with given lock type (currently Postgres only).
|
||||
*/
|
||||
Query<T> forUpdate(Query.LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with No Wait option.
|
||||
* <p>
|
||||
@@ -187,11 +204,6 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> forUpdateNoWait();
|
||||
|
||||
/**
|
||||
* Execute using "for update nowait" with given lock type (currently Postgres only).
|
||||
*/
|
||||
Query<T> forUpdateNoWait(Query.LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with Skip Locked option.
|
||||
* <p>
|
||||
@@ -200,11 +212,6 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> forUpdateSkipLocked();
|
||||
|
||||
/**
|
||||
* Execute using "for update skip locked" with given lock type (currently Postgres only).
|
||||
*/
|
||||
Query<T> forUpdateSkipLocked(Query.LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute the query including soft deleted rows.
|
||||
*/
|
||||
@@ -300,6 +307,13 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
void findEach(Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Execute findEach with a batch consumer.
|
||||
*
|
||||
* @see Query#findEach(int, Consumer)
|
||||
*/
|
||||
void findEach(int batch, Consumer<List<T>> 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<T> {
|
||||
ExpressionList<T> gtOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than or Equal to OR Null - <code> >= or null </code>.
|
||||
* Greater Than or Equal to OR Null - ({@code >= or null }).
|
||||
*/
|
||||
ExpressionList<T> geOrNull(String propertyName, Object value);
|
||||
|
||||
@@ -923,7 +937,7 @@ public interface ExpressionList<T> {
|
||||
ExpressionList<T> ltOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Equal to OR Null - <code> <= or null </code>.
|
||||
* Less Than or Equal to OR Null - ({@code <= or null }).
|
||||
*/
|
||||
ExpressionList<T> leOrNull(String propertyName, Object value);
|
||||
|
||||
|
||||
@@ -159,6 +159,13 @@ public interface ExtendedServer {
|
||||
*/
|
||||
<T> void findEach(Query<T> query, Consumer<T> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute findEach with batch consumer.
|
||||
*
|
||||
* @see Query#findEach(int, Consumer)
|
||||
*/
|
||||
<T> void findEach(Query<T> query, int batch, Consumer<List<T>> consumer, Transaction t);
|
||||
|
||||
/**
|
||||
* Execute the query visiting the each bean one at a time.
|
||||
* <p>
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* It also provides the ability to control the lazy loading queries (batch size,
|
||||
* selected properties and fetches) to avoid N+1 queries etc.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* Defines how a relationship is fetched via either normal SQL join,
|
||||
* a eager secondary query, via lazy loading or via eagerly hitting L2 cache.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
* // Normal fetch join results in a single SQL query
|
||||
* List<Order> list = DB.find(Order.class).fetch("details").findList();
|
||||
*
|
||||
* // Find Orders join details using a single SQL query
|
||||
* }</pre>
|
||||
* <p>
|
||||
* 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<Order> 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
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Example: Using 2 "query joins"
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // This will use 3 SQL queries to build this object graph
|
||||
* List<Order> 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
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Example: Using "query joins" and partial objects
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* <pre>{@code
|
||||
* // This will use 3 SQL queries to build this object graph
|
||||
* List<Order> 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"
|
||||
* }</pre>
|
||||
* <p>
|
||||
* 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).
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> 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
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <p>
|
||||
* Example of controlling the lazy loading query:
|
||||
* </p>
|
||||
* <p>
|
||||
* This gives us the ability to optimise the lazy loading query for a given use
|
||||
* case.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> 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
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @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()}
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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().
|
||||
* <p>
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
* This will use the default batch size for separate query which is 100.
|
||||
* </p>
|
||||
*/
|
||||
@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).
|
||||
* <p>
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
* The queryBatchSize is the number of parent id's that this separate query
|
||||
* will load per batch.
|
||||
* </p>
|
||||
* <p>
|
||||
* This will load all beans on this path eagerly unless a {@link #lazy(int)}
|
||||
* is also used.
|
||||
* </p>
|
||||
*
|
||||
* @param 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).
|
||||
* <p>
|
||||
* Eagerly fetch the first batch of beans on this path.
|
||||
* This is similar to {@link #query(int)} but only fetches the first batch.
|
||||
* <p>
|
||||
* If there are more parent beans than the batch size then they will not be
|
||||
* loaded eagerly but instead use lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @param 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().
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<I, T> {
|
||||
* // ... add extra customer specific finder methods
|
||||
* }
|
||||
*
|
||||
* @Entity
|
||||
* @Entity
|
||||
* public class Customer extends BaseModel {
|
||||
*
|
||||
* public static final CustomerFinder find = new CustomerFinder();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -184,7 +184,8 @@ public interface Query<T> {
|
||||
*/
|
||||
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<T> {
|
||||
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
|
||||
}
|
||||
@@ -513,7 +514,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, fetchProperties, new FetchConfig().query())
|
||||
* fetch(path, fetchProperties, FetchConfig.ofQuery())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -547,7 +548,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, fetchProperties, new FetchConfig().lazy())
|
||||
* fetch(path, fetchProperties, FetchConfig.ofLazy())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -572,7 +573,7 @@ public interface Query<T> {
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName,lastName,email", new FetchConfig().lazy(10))
|
||||
* .fetch("contacts", "firstName,lastName,email", FetchConfig.ofLazy(10))
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
@@ -609,7 +610,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, new FetchConfig().query())
|
||||
* fetch(path, FetchConfig.ofQuery())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -638,7 +639,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, new FetchConfig().lazy())
|
||||
* fetch(path, FetchConfig.ofLazy())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -661,7 +662,7 @@ public interface Query<T> {
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> 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();
|
||||
*
|
||||
* }</pre>
|
||||
@@ -809,7 +810,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -828,6 +829,21 @@ public interface Query<T> {
|
||||
*/
|
||||
void findEach(Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Execute findEach streaming query batching the results for consuming.
|
||||
* <p>
|
||||
* This query execution will stream the results and is suited to consuming
|
||||
* large numbers of results from the database.
|
||||
* <p>
|
||||
* 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<List<T>> consumer);
|
||||
|
||||
/**
|
||||
* Execute the query using callbacks to a visitor to process the resulting
|
||||
* beans one at a time.
|
||||
@@ -838,12 +854,12 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <pre>{@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 +1660,52 @@ public interface Query<T> {
|
||||
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.
|
||||
* <p>
|
||||
* Note that <code>forUpdate()</code> is the same as
|
||||
* <code>withLock(LockType.UPDATE)</code>.
|
||||
* <p>
|
||||
* Provides us with the ability to explicitly use Postgres
|
||||
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
|
||||
*/
|
||||
Query<T> forUpdate();
|
||||
Query<T> 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.
|
||||
* <p>
|
||||
* Note that <code>forUpdateNoWait()</code> is the same as
|
||||
* <code>withLock(LockType.UPDATE, LockWait.NOWAIT)</code>.
|
||||
* <p>
|
||||
* Provides us with the ability to explicitly use Postgres
|
||||
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
|
||||
*/
|
||||
Query<T> forUpdate(LockType lockType);
|
||||
Query<T> withLock(LockType lockType, LockWait lockWait);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause which results in the DB locking the record.
|
||||
* <p>
|
||||
* The same as <code>withLock(LockType.UPDATE, LockWait.WAIT)</code>.
|
||||
*/
|
||||
Query<T> forUpdate();
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with "no wait" option.
|
||||
* <p>
|
||||
* This is typically a Postgres and Oracle only option at this stage.
|
||||
* </p>
|
||||
* <p>
|
||||
* The same as <code>withLock(LockType.UPDATE, LockWait.NOWAIT)</code>.
|
||||
*/
|
||||
Query<T> forUpdateNoWait();
|
||||
|
||||
/**
|
||||
* Execute using "for update nowait" with given lock type (currently Postgres only).
|
||||
*/
|
||||
Query<T> forUpdateNoWait(LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with "skip locked" option.
|
||||
* <p>
|
||||
* This is typically a Postgres and Oracle only option at this stage.
|
||||
* </p>
|
||||
* <p>
|
||||
* The same as <code>withLock(LockType.UPDATE, LockWait.SKIPLOCKED)</code>.
|
||||
*/
|
||||
Query<T> forUpdateSkipLocked();
|
||||
|
||||
/**
|
||||
* Execute using "for update skip locked" with given lock type (currently Postgres only).
|
||||
*/
|
||||
Query<T> forUpdateSkipLocked(LockType lockType);
|
||||
|
||||
/**
|
||||
* Return true if this query has forUpdate set.
|
||||
*/
|
||||
|
||||
@@ -41,14 +41,14 @@ package io.ebean;
|
||||
* <h3>Example OrderAggregate</h3>
|
||||
* <pre>{@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<OrderAggregate> 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)
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.sql.SQLException;
|
||||
* //
|
||||
* class CustomerMapper implements RowMapper<CustomerDto> {
|
||||
*
|
||||
* @Override
|
||||
* @Override
|
||||
* public CustomerDto map(ResultSet rset, int rowNum) throws SQLException {
|
||||
*
|
||||
* long id = rset.getLong(1);
|
||||
|
||||
@@ -519,11 +519,11 @@ public interface Transaction extends AutoCloseable {
|
||||
void flush() throws PersistenceException;
|
||||
|
||||
/**
|
||||
* This is a synonym for flush() and will be deprecated.
|
||||
* Deprecated - migrate to flush().
|
||||
* <p>
|
||||
* flush() is preferred as it matches the JPA flush() method.
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
void flushBatch() throws PersistenceException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,23 +13,23 @@ package io.ebean;
|
||||
* </p>
|
||||
* <pre>{@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 {
|
||||
* ...
|
||||
* }
|
||||
|
||||
@@ -32,7 +32,7 @@ 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);
|
||||
private transient final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
private transient NodeUsageCollector nodeUsageCollector;
|
||||
|
||||
@@ -931,10 +931,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");
|
||||
}
|
||||
setChangedProperty(propertyIndex);
|
||||
}
|
||||
setLoadedProperty(propertyIndex);
|
||||
}
|
||||
|
||||
private void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
|
||||
private static final long serialVersionUID = 3365725236140187588L;
|
||||
|
||||
protected final ReentrantLock lock = new ReentrantLock(false);
|
||||
protected final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
protected boolean readOnly;
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
|
||||
@Override
|
||||
public void internalAddWithCheck(Object bean) {
|
||||
if (list == null || !containsInstance(bean)) {
|
||||
if (list == null || bean == null || !containsInstance(bean)) {
|
||||
internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,9 +70,8 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
|
||||
@Override
|
||||
public void internalAddWithCheck(Object bean) {
|
||||
if (set == null || !set.contains(bean)) {
|
||||
internalAdd(bean);
|
||||
}
|
||||
// set add() already de-dups so just add it
|
||||
internalAdd(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,7 +20,7 @@ public final class CopyOnFirstWriteList<E> extends AbstractList<E> 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.
|
||||
|
||||
@@ -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 .
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -489,7 +489,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 +498,23 @@ public class DatabaseConfig {
|
||||
private boolean notifyL2CacheInForeground;
|
||||
|
||||
/**
|
||||
* Set to true to support query plan capture.
|
||||
* Set to true to enable bind capture required for query plan capture.
|
||||
*/
|
||||
private boolean collectQueryPlans;
|
||||
private boolean queryPlanEnable;
|
||||
|
||||
/**
|
||||
* The default threshold in micros for collecting query plans.
|
||||
*/
|
||||
private long collectQueryPlanThresholdMicros = Long.MAX_VALUE;
|
||||
private long queryPlanThresholdMicros = Long.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Set to true to enable automatic periodic query plan capture.
|
||||
*/
|
||||
private boolean queryPlanCapture;
|
||||
private long queryPlanCapturePeriodSecs = 60 * 10; // 10 minutes
|
||||
private long queryPlanCaptureMaxTimeMillis = 10_000; // 10 seconds
|
||||
private int queryPlanCaptureMaxCount = 10;
|
||||
private QueryPlanListener queryPlanListener;
|
||||
|
||||
/**
|
||||
* The time in millis used to determine when a query is alerted for being slow.
|
||||
@@ -1045,7 +1054,6 @@ public class DatabaseConfig {
|
||||
* This is a performance optimisation to reduce the number times Ebean
|
||||
* requests a sequence to be used as an Id for a bean (aka reduce network
|
||||
* chatter).
|
||||
|
||||
*/
|
||||
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
|
||||
platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
|
||||
@@ -2693,7 +2701,10 @@ public class DatabaseConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from ebean.properties.
|
||||
* Load settings from application.properties, application.yaml and other sources.
|
||||
* <p>
|
||||
* Uses <code>avaje-config</code> to load configuration properties. Goto https://avaje.io/config
|
||||
* for detail on how and where properties are loaded from.
|
||||
*/
|
||||
public void loadFromProperties() {
|
||||
this.properties = Config.asProperties();
|
||||
@@ -2795,21 +2806,26 @@ public class DatabaseConfig {
|
||||
}
|
||||
loadDocStoreSettings(p);
|
||||
|
||||
defaultServer = p.getBoolean("defaultServer", defaultServer);
|
||||
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);
|
||||
|
||||
@@ -3056,20 +3072,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 <code>&x64;javax.validation.contstraints.NotNull</code>
|
||||
* Controls if Ebean should ignore <code>&x64;javax.validation.contstraints.NotNull</code> or
|
||||
* <code>&x64;jakarta.validation.contstraints.NotNull</code>
|
||||
* with respect to generating a <code>NOT NULL</code> column.
|
||||
* <p>
|
||||
* Normally when Ebean sees javax NotNull annotation it means that column is defined as NOT NULL.
|
||||
* Set this to <code>false</code> 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 +3108,17 @@ public class DatabaseConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan time to live.
|
||||
* Return the time to live for ebean's internal query plan.
|
||||
*/
|
||||
public int getQueryPlanTTLSeconds() {
|
||||
return queryPlanTTLSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query plan time to live.
|
||||
* Set the time to live for ebean's internal query plan.
|
||||
* <p>
|
||||
* This is the plan that knows how to execute the query, read the result
|
||||
* and collects execution metrics. By default this is set to 5 mins.
|
||||
*/
|
||||
public void setQueryPlanTTLSeconds(int queryPlanTTLSeconds) {
|
||||
this.queryPlanTTLSeconds = queryPlanTTLSeconds;
|
||||
@@ -3171,29 +3191,110 @@ public class DatabaseConfig {
|
||||
/**
|
||||
* Return true if query plan capture is enabled.
|
||||
*/
|
||||
public boolean isCollectQueryPlans() {
|
||||
return collectQueryPlans;
|
||||
public boolean isQueryPlanEnable() {
|
||||
return queryPlanEnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to enable query plan capture.
|
||||
*/
|
||||
public void setCollectQueryPlans(boolean collectQueryPlans) {
|
||||
this.collectQueryPlans = collectQueryPlans;
|
||||
public void setQueryPlanEnable(boolean queryPlanEnable) {
|
||||
this.queryPlanEnable = queryPlanEnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan collection threshold in microseconds.
|
||||
*/
|
||||
public long getCollectQueryPlanThresholdMicros() {
|
||||
return collectQueryPlanThresholdMicros;
|
||||
public long getQueryPlanThresholdMicros() {
|
||||
return queryPlanThresholdMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query plan collection threshold in microseconds.
|
||||
* <p>
|
||||
* Queries executing slower than this will have bind values captured such that later
|
||||
* the query plan can be captured and reported.
|
||||
*/
|
||||
public void setCollectQueryPlanThresholdMicros(long collectQueryPlanThresholdMicros) {
|
||||
this.collectQueryPlanThresholdMicros = collectQueryPlanThresholdMicros;
|
||||
public void setQueryPlanThresholdMicros(long queryPlanThresholdMicros) {
|
||||
this.queryPlanThresholdMicros = queryPlanThresholdMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if periodic capture of query plans is enabled.
|
||||
*/
|
||||
public boolean isQueryPlanCapture() {
|
||||
return queryPlanCapture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to turn on periodic capture of query plans.
|
||||
*/
|
||||
public void setQueryPlanCapture(boolean queryPlanCapture) {
|
||||
this.queryPlanCapture = queryPlanCapture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the frequency to capture query plans.
|
||||
*/
|
||||
public long getQueryPlanCapturePeriodSecs() {
|
||||
return queryPlanCapturePeriodSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frequency in seconds to capture query plans.
|
||||
*/
|
||||
public void setQueryPlanCapturePeriodSecs(long queryPlanCapturePeriodSecs) {
|
||||
this.queryPlanCapturePeriodSecs = queryPlanCapturePeriodSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time after which a capture query plans request will
|
||||
* stop capturing more query plans.
|
||||
* <p>
|
||||
* Effectively this controls the amount of load/time we want to
|
||||
* allow for query plan capture.
|
||||
*/
|
||||
public long getQueryPlanCaptureMaxTimeMillis() {
|
||||
return queryPlanCaptureMaxTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time after which a capture query plans request will
|
||||
* stop capturing more query plans.
|
||||
* <p>
|
||||
* Effectively this controls the amount of load/time we want to
|
||||
* allow for query plan capture.
|
||||
*/
|
||||
public void setQueryPlanCaptureMaxTimeMillis(long queryPlanCaptureMaxTimeMillis) {
|
||||
this.queryPlanCaptureMaxTimeMillis = queryPlanCaptureMaxTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max number of query plans captured per request.
|
||||
*/
|
||||
public int getQueryPlanCaptureMaxCount() {
|
||||
return queryPlanCaptureMaxCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max number of query plans captured per request.
|
||||
*/
|
||||
public void setQueryPlanCaptureMaxCount(int queryPlanCaptureMaxCount) {
|
||||
this.queryPlanCaptureMaxCount = queryPlanCaptureMaxCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listener used to process captured query plans.
|
||||
*/
|
||||
public QueryPlanListener getQueryPlanListener() {
|
||||
return queryPlanListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener used to process captured query plans.
|
||||
*/
|
||||
public void setQueryPlanListener(QueryPlanListener queryPlanListener) {
|
||||
this.queryPlanListener = queryPlanListener;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -7,13 +7,8 @@ import java.util.List;
|
||||
*/
|
||||
public interface ModuleInfoLoader {
|
||||
|
||||
/**
|
||||
* Return the entity classes to register with the default DB.
|
||||
*/
|
||||
List<Class<?>> entityClasses();
|
||||
|
||||
/**
|
||||
* Return entity classes to register for a named DB (not default DB).
|
||||
*/
|
||||
List<Class<?>> entityClassesFor(String dbName);
|
||||
List<Class<?>> classesFor(String dbName, boolean defaultServer);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* This is used to help mapping of raw SQL queries onto bean properties.
|
||||
* </p>
|
||||
*
|
||||
* @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).
|
||||
* <p>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import io.ebean.Database;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The captured query plans.
|
||||
*/
|
||||
public class QueryPlanCapture {
|
||||
|
||||
private final Database database;
|
||||
private final List<MetaQueryPlan> plans;
|
||||
|
||||
public QueryPlanCapture(Database database, List<MetaQueryPlan> plans) {
|
||||
this.database = database;
|
||||
this.plans = plans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the database the plans were captured for.
|
||||
*/
|
||||
public Database getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the captured query plans.
|
||||
*/
|
||||
public List<MetaQueryPlan> getPlans() {
|
||||
return plans;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL: Listener for captured query plans.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface QueryPlanListener {
|
||||
|
||||
/**
|
||||
* Process the captured query plans.
|
||||
*/
|
||||
void process(QueryPlanCapture capture);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
* </p>
|
||||
*/
|
||||
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();
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -13,6 +13,7 @@ public class Oracle11Platform extends OraclePlatform {
|
||||
this.platform = Platform.ORACLE11;
|
||||
this.columnAliasPrefix = "c";
|
||||
this.sqlLimiter = new OracleRownumSqlLimiter();
|
||||
this.basicSqlLimiter = new OracleRownumBasicLimiter();
|
||||
dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ 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<Database> databases = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -41,6 +41,9 @@ public class QueryPlanRequest {
|
||||
|
||||
/**
|
||||
* Set the maximum number of plans to capture.
|
||||
* <p>
|
||||
* Use this to limit how much query plan capturing is done as query
|
||||
* plan capture is actual database load.
|
||||
*/
|
||||
public void setMaxCount(int maxCount) {
|
||||
this.maxCount = maxCount;
|
||||
@@ -58,7 +61,9 @@ public class QueryPlanRequest {
|
||||
/**
|
||||
* Set the maximum amount of time we want to use to capture plans.
|
||||
* <p>
|
||||
* Query plan collection will stop once this time is exceeded.
|
||||
* Query plan collection will stop once this time is exceeded. We use
|
||||
* this to ensure the query plan capture does not use excessive amount
|
||||
* of time - put too much load on the database.
|
||||
*/
|
||||
public void setMaxTimeMillis(long maxTimeMillis) {
|
||||
this.maxTimeMillis = maxTimeMillis;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</parent>
|
||||
<!-- <parent>-->
|
||||
<!-- <groupId>org.avaje</groupId>-->
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.6.1</tag>
|
||||
<tag>ebean-parent-12.8.1</tag>
|
||||
</scm>
|
||||
|
||||
<name>ebean autotune</name>
|
||||
@@ -26,7 +26,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -59,12 +59,12 @@
|
||||
<plugin>
|
||||
<groupId>io.repaint.maven</groupId>
|
||||
<artifactId>tiles-maven-plugin</artifactId>
|
||||
<version>2.18</version>
|
||||
<version>2.19</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<tiles>
|
||||
<!-- other tiles ... -->
|
||||
<tile>io.ebean.tile:enhancement:12.5.0</tile>
|
||||
<tile>io.ebean.tile:enhancement:12.6.0</tile>
|
||||
</tiles>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
|
||||
+6
-9
@@ -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<Order> desc = getBeanDescriptor(Order.class);
|
||||
private final BeanDescriptor<Order> desc = getBeanDescriptor(Order.class);
|
||||
|
||||
@Test
|
||||
public void buildDetail() {
|
||||
@@ -30,7 +27,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString().trim()).isEqualTo("fetch customer (name)");
|
||||
assertThat(detail.asStringDebug().trim()).isEqualTo("fetch customer (name)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -49,7 +46,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString()).isEqualTo("select (orderDate) fetch customer (name)");
|
||||
assertThat(detail.asStringDebug()).isEqualTo("select (orderDate) fetch customer (name)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -67,7 +64,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString().trim()).isEqualTo("select (orderDate,customer)");
|
||||
assertThat(detail.asStringDebug().trim()).isEqualTo("select (orderDate,customer)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,7 +87,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString()).isEqualTo("select (orderDate) fetch customer (billingAddress)");
|
||||
assertThat(detail.asStringDebug()).isEqualTo("select (orderDate) fetch customer (billingAddress)");
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +116,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
assertThat(detail.asString()).isEqualTo("fetch customer (name,note) fetch customer.billingAddress (line1)");
|
||||
assertThat(detail.asStringDebug()).isEqualTo("fetch customer (name,note) fetch customer.billingAddress (line1)");
|
||||
}
|
||||
|
||||
private NodeUsageCollector node(String path) {
|
||||
|
||||
+34
-16
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean bom</name>
|
||||
@@ -15,11 +15,11 @@
|
||||
<properties>
|
||||
<ebean-ddl-runner.version>1.0</ebean-ddl-runner.version>
|
||||
<ebean-migration-auto.version>1.0</ebean-migration-auto.version>
|
||||
<ebean-migration.version>12.2.0</ebean-migration.version>
|
||||
<ebean-test-docker.version>4.0</ebean-test-docker.version>
|
||||
<ebean-migration.version>12.4.0</ebean-migration.version>
|
||||
<ebean-test-docker.version>4.1</ebean-test-docker.version>
|
||||
<ebean-datasource.version>7.0</ebean-datasource.version>
|
||||
<ebean-agent.version>12.6.0</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.6.0</ebean-maven-plugin.version>
|
||||
<ebean-agent.version>12.8.1</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.8.1</ebean-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -81,72 +81,90 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-xml</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-autotune</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -4,28 +4,25 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.11.3</jackson-core.version>
|
||||
<jackson-databind.version>2.11.3</jackson-databind.version>
|
||||
</properties>
|
||||
<name>ebean core type</name>
|
||||
<description>ebean scalar types api</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
+17
-16
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core</artifactId>
|
||||
@@ -15,14 +15,9 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.6.1</tag>
|
||||
<tag>ebean-parent-12.8.1</tag>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.11.3</jackson-core.version>
|
||||
<jackson-databind.version>2.11.3</jackson-databind.version>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<!-- Note: to use this profile, you need to download manually the db2jcc4 driver.
|
||||
@@ -70,14 +65,14 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>12.2.0</version>
|
||||
<version>12.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.6.1.PreRelease.0</version>
|
||||
<version>12.6.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -92,19 +87,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.6.1</version>
|
||||
<version>12.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -138,6 +133,12 @@
|
||||
<version>1.1.0.Final</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
@@ -157,14 +158,14 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -202,7 +203,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test-docker</artifactId>
|
||||
<version>4.0</version>
|
||||
<version>4.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -301,7 +302,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>12.5.0</version>
|
||||
<version>12.8.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -1,52 +1,25 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.expression.IdInExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used for bean cache lookup with where ids in expression.
|
||||
* Process Cache lookup by Id(s).
|
||||
*/
|
||||
public class CacheIdLookup<T> {
|
||||
|
||||
private final IdInExpression idInExpression;
|
||||
|
||||
private int remaining;
|
||||
|
||||
public CacheIdLookup(IdInExpression idInExpression) {
|
||||
this.idInExpression = idInExpression;
|
||||
}
|
||||
public interface CacheIdLookup<T> {
|
||||
|
||||
/**
|
||||
* Return the Id values for the in expression.
|
||||
* Return the Id values to lookup against the L2 cache.
|
||||
*/
|
||||
public Collection<?> idValues() {
|
||||
return idInExpression.idValues();
|
||||
}
|
||||
Collection<?> idValues();
|
||||
|
||||
/**
|
||||
* Process the hits returning the beans fetched from cache and
|
||||
* adjusting the in expression (to not fetch the hits).
|
||||
* Remove the hits returning the beans fetched from L2 cache.
|
||||
*/
|
||||
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
|
||||
List<T> removeHits(BeanCacheResult<T> cacheResult);
|
||||
|
||||
Set<Object> hitIds = new HashSet<>();
|
||||
List<T> beans = new ArrayList<>(hitIds.size());
|
||||
|
||||
for (BeanCacheResult.Entry<T> hit : cacheResult.hits()) {
|
||||
hitIds.add(hit.getKey());
|
||||
beans.add(hit.getBean());
|
||||
}
|
||||
|
||||
this.remaining = idInExpression.removeIds(hitIds);
|
||||
return beans;
|
||||
}
|
||||
|
||||
public boolean allHits() {
|
||||
return remaining == 0;
|
||||
}
|
||||
/**
|
||||
* Return true if all beans where found in L2 cache.
|
||||
*/
|
||||
boolean allHits();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.expression.IdInExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used for bean cache lookup with where ids in expression.
|
||||
*/
|
||||
public class CacheIdLookupMany<T> implements CacheIdLookup<T> {
|
||||
|
||||
private final IdInExpression idInExpression;
|
||||
|
||||
private int remaining;
|
||||
|
||||
public CacheIdLookupMany(IdInExpression idInExpression) {
|
||||
this.idInExpression = idInExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Id values for the in expression.
|
||||
*/
|
||||
@Override
|
||||
public Collection<?> idValues() {
|
||||
return idInExpression.idValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the hits returning the beans fetched from cache and
|
||||
* adjusting the in expression (to not fetch the hits).
|
||||
*/
|
||||
@Override
|
||||
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
|
||||
|
||||
Set<Object> hitIds = new HashSet<>();
|
||||
List<T> beans = new ArrayList<>(hitIds.size());
|
||||
|
||||
for (BeanCacheResult.Entry<T> hit : cacheResult.hits()) {
|
||||
hitIds.add(hit.getKey());
|
||||
beans.add(hit.getBean());
|
||||
}
|
||||
|
||||
this.remaining = idInExpression.removeIds(hitIds);
|
||||
return beans;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allHits() {
|
||||
return remaining == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used for bean cache lookup with a single id value.
|
||||
*/
|
||||
public class CacheIdLookupSingle<T> implements CacheIdLookup<T> {
|
||||
|
||||
private final Object idValue;
|
||||
private boolean found;
|
||||
|
||||
public CacheIdLookupSingle(Object idValue) {
|
||||
this.idValue = idValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> idValues() {
|
||||
return Collections.singleton(idValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
|
||||
final List<BeanCacheResult.Entry<T>> hits = cacheResult.hits();
|
||||
if (hits.size() == 1) {
|
||||
found = true;
|
||||
return Collections.singletonList(hits.get(0).getBean());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allHits() {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public interface LoadContext {
|
||||
/**
|
||||
* Return the minimum batch size when using QueryIterator with query joins.
|
||||
*/
|
||||
int getSecondaryQueriesMinBatchSize(int defaultQueryBatch);
|
||||
int getSecondaryQueriesMinBatchSize();
|
||||
|
||||
/**
|
||||
* Execute any secondary (+query) queries if there are any defined.
|
||||
|
||||
@@ -8,6 +8,11 @@ import java.util.List;
|
||||
|
||||
class NoopQueryPlanManager implements QueryPlanManager {
|
||||
|
||||
@Override
|
||||
public void setDefaultThreshold(long thresholdMicros) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) {
|
||||
return SpiQueryBindCapture.NOOP;
|
||||
|
||||
@@ -12,6 +12,11 @@ public interface QueryPlanManager {
|
||||
|
||||
QueryPlanManager NOOP = new NoopQueryPlanManager();
|
||||
|
||||
/**
|
||||
* Update the global default threshold used when new query plans are created.
|
||||
*/
|
||||
void setDefaultThreshold(long thresholdMicros);
|
||||
|
||||
/**
|
||||
* Create the bind capture for the given query plan.
|
||||
*/
|
||||
|
||||
@@ -264,6 +264,11 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
|
||||
*/
|
||||
<T> void findDtoEach(SpiDtoQuery<T> query, Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* DTO findEach batch query.
|
||||
*/
|
||||
<T> void findDtoEach(SpiDtoQuery<T> query, int batch, Consumer<List<T>> consumer);
|
||||
|
||||
/**
|
||||
* DTO findEachWhile query.
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,7 @@ 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 +31,7 @@ import java.util.Set;
|
||||
/**
|
||||
* Object Relational query - Internal extension to Query object.
|
||||
*/
|
||||
public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCodes {
|
||||
|
||||
enum Mode {
|
||||
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
|
||||
@@ -289,6 +290,16 @@ public interface SpiQuery<T> extends Query<T>, 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.
|
||||
*/
|
||||
|
||||
@@ -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<String> properties);
|
||||
|
||||
/**
|
||||
* Specify the fetch properties for the given path.
|
||||
*/
|
||||
void fetchProperties(String name, Set<String> properties, FetchConfig config);
|
||||
|
||||
/**
|
||||
* Add a nested fetch graph.
|
||||
*/
|
||||
void addNested(String name, OrmQueryDetail nestedDetail, FetchConfig config);
|
||||
}
|
||||
@@ -318,4 +318,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);
|
||||
|
||||
}
|
||||
|
||||
@@ -418,4 +418,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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ 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<String, ServerCache> allCaches = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, Set<String>> collectIdCaches = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ public class DefaultServerCache implements ServerCache {
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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<String, EbeanServer> serverMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
+14
-22
@@ -27,7 +27,7 @@ public abstract class AbstractSqlQueryRequest {
|
||||
|
||||
protected final SpiEbeanServer server;
|
||||
|
||||
protected SpiTransaction trans;
|
||||
protected SpiTransaction transaction;
|
||||
|
||||
private boolean createdTransaction;
|
||||
|
||||
@@ -47,18 +47,18 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,20 +69,18 @@ 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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +118,6 @@ public abstract class AbstractSqlQueryRequest {
|
||||
* 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 +128,6 @@ public abstract class AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
private String limitOffset(String sql) {
|
||||
|
||||
int firstRow = query.getFirstRow();
|
||||
int maxRows = query.getMaxRows();
|
||||
if (firstRow > 0 || maxRows > 0) {
|
||||
@@ -149,10 +145,8 @@ public abstract class AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
protected void executeAsSql(Binder binder) throws SQLException {
|
||||
|
||||
prepareSql();
|
||||
Connection conn = trans.getInternalConnection();
|
||||
|
||||
Connection conn = transaction.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
@@ -160,14 +154,12 @@ public abstract class AbstractSqlQueryRequest {
|
||||
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, ")"));
|
||||
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
|
||||
}
|
||||
|
||||
setResultSet(pstmt.executeQuery(), null);
|
||||
|
||||
@@ -131,19 +131,10 @@ public class DatabasePlatformFactory {
|
||||
* Use JDBC DatabaseMetaData to determine the platform.
|
||||
*/
|
||||
private DatabasePlatform byDataSource(DataSource dataSource) {
|
||||
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = dataSource.getConnection();
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
|
||||
return byDatabaseMeta(metaData, connection);
|
||||
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
return byDatabaseMeta(connection.getMetaData(), connection);
|
||||
} catch (SQLException ex) {
|
||||
throw new PersistenceException(ex);
|
||||
|
||||
} finally {
|
||||
JdbcClose.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +148,7 @@ public class DatabasePlatformFactory {
|
||||
final int minorVersion = metaData.getDatabaseMinorVersion();
|
||||
|
||||
if (dbProductName.contains("oracle")) {
|
||||
return new OraclePlatform();
|
||||
return oracleVersion(majorVersion);
|
||||
} else if (dbProductName.contains("microsoft")) {
|
||||
throw new IllegalArgumentException("For SqlServer please explicitly choose either sqlserver16 or sqlserver17 as the platform via DatabaseConfig.setDatabasePlatformName. Refer to issue #1340 for more details");
|
||||
} else if (dbProductName.contains("h2")) {
|
||||
@@ -188,6 +179,10 @@ public class DatabasePlatformFactory {
|
||||
return new DatabasePlatform();
|
||||
}
|
||||
|
||||
private DatabasePlatform oracleVersion(int majorVersion) {
|
||||
return majorVersion < 12 ? new Oracle11Platform() : new OraclePlatform();
|
||||
}
|
||||
|
||||
private DatabasePlatform mysqlVersion(int majorVersion, int minorVersion) {
|
||||
if (majorVersion <= 5 && minorVersion <= 5) {
|
||||
return new MySql55Platform();
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.server.lib.DaemonExecutorService;
|
||||
import io.ebeaninternal.server.lib.DaemonScheduleThreadPool;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* The default implementation of the BackgroundExecutor.
|
||||
*/
|
||||
public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
|
||||
|
||||
private final DaemonScheduleThreadPool schedulePool;
|
||||
|
||||
private final DaemonExecutorService pool;
|
||||
|
||||
/**
|
||||
* Construct the default implementation of BackgroundExecutor.
|
||||
*/
|
||||
public DefaultBackgroundExecutor(int schedulePoolSize, int shutdownWaitSeconds, String namePrefix) {
|
||||
this.pool = new DaemonExecutorService(shutdownWaitSeconds, namePrefix);
|
||||
this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix + "-periodic-");
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a Runnable using a background thread.
|
||||
*/
|
||||
@Override
|
||||
public void execute(Runnable r) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
pool.execute(r);
|
||||
} else {
|
||||
pool.execute(() -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executePeriodically(Runnable r, long delay, TimeUnit unit) {
|
||||
executePeriodically(r, delay, delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executePeriodically(Runnable r, long initialDelay, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
schedulePool.scheduleWithFixedDelay(r, initialDelay, delay, unit);
|
||||
} else {
|
||||
schedulePool.scheduleWithFixedDelay(() -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}, initialDelay, delay, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
return schedulePool.schedule(r, delay, unit);
|
||||
} else {
|
||||
return schedulePool.schedule(() -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}, delay, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
return schedulePool.schedule(c, delay, unit);
|
||||
} else {
|
||||
return schedulePool.schedule(() -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
return c.call();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}, delay, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
pool.shutdown();
|
||||
schedulePool.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -215,7 +215,6 @@ class DefaultBeanLoader {
|
||||
desc.contextPut(pc, id, bean);
|
||||
ebi.setPersistenceContext(pc);
|
||||
}
|
||||
|
||||
boolean draft = desc.isDraftInstance(bean);
|
||||
|
||||
if (embeddedOwnerIndex == -1) {
|
||||
@@ -245,7 +244,6 @@ class DefaultBeanLoader {
|
||||
// and put the data into the original bean
|
||||
query.setUsageProfiling(false);
|
||||
query.setPersistenceContext(pc);
|
||||
|
||||
query.setMode(mode);
|
||||
query.setId(id);
|
||||
|
||||
|
||||
@@ -10,14 +10,15 @@ import io.ebean.config.TenantMode;
|
||||
import io.ebean.config.UnderscoreNamingConvention;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.event.ShutdownManager;
|
||||
import io.ebean.service.SpiContainer;
|
||||
import io.ebeaninternal.api.DbOffline;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.DbOffline;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
import io.ebeaninternal.server.core.bootup.BootupClassPathSearch;
|
||||
import io.ebeaninternal.server.core.bootup.BootupClasses;
|
||||
import io.ebean.event.ShutdownManager;
|
||||
import io.ebeaninternal.server.executor.DefaultBackgroundExecutor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -35,7 +36,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger("io.ebean.internal.DefaultContainer");
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock(false);
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final ClusterManager clusterManager;
|
||||
|
||||
public DefaultContainer(ContainerConfig containerConfig) {
|
||||
@@ -134,16 +135,11 @@ public class DefaultContainer implements SpiContainer {
|
||||
configProvider.apply((ServerConfig)config);
|
||||
}
|
||||
}
|
||||
if (config.isAutoLoadModuleInfo()) {
|
||||
// auto register entity classes (default db)
|
||||
for (ModuleInfoLoader loader : ServiceLoader.load(ModuleInfoLoader.class)) {
|
||||
config.addAll(loader.entityClasses());
|
||||
}
|
||||
}
|
||||
} else if (config.isAutoLoadModuleInfo()) {
|
||||
// auto register entity classes (other named db)
|
||||
}
|
||||
if (config.isAutoLoadModuleInfo()) {
|
||||
// auto register entity classes
|
||||
for (ModuleInfoLoader loader : ServiceLoader.load(ModuleInfoLoader.class)) {
|
||||
config.addAll(loader.entityClassesFor(config.getName()));
|
||||
config.addAll(loader.classesFor(config.getName(), config.isDefaultServer()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.config.QueryPlanCapture;
|
||||
import io.ebean.config.QueryPlanListener;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class DefaultQueryPlanListener implements QueryPlanListener {
|
||||
|
||||
static final QueryPlanListener INSTANT = new DefaultQueryPlanListener();
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger("io.ebean.QUERYPLAN");
|
||||
|
||||
@Override
|
||||
public void process(QueryPlanCapture capture) {
|
||||
// better to log this in JSON form?
|
||||
String dbName = capture.getDatabase().getName();
|
||||
for (MetaQueryPlan plan : capture.getPlans()) {
|
||||
log.info("queryPlan db:{} label:{} queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
|
||||
dbName, plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
|
||||
plan.getSql(), plan.getBind(), plan.getPlan());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,12 +45,7 @@ import io.ebean.bean.PersistenceContext.WithOption;
|
||||
import io.ebean.bean.SingleBeanLoader;
|
||||
import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.common.CopyOnFirstWriteList;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.EncryptKeyManager;
|
||||
import io.ebean.config.SlowQueryEvent;
|
||||
import io.ebean.config.SlowQueryListener;
|
||||
import io.ebean.config.TenantMode;
|
||||
import io.ebean.config.*;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.event.ShutdownManager;
|
||||
@@ -125,6 +120,7 @@ import io.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
@@ -137,7 +133,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -145,6 +140,7 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.Spliterator;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@@ -161,7 +157,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultServer.class);
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock(false);
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final DatabaseConfig config;
|
||||
private final String serverName;
|
||||
private final DatabasePlatform databasePlatform;
|
||||
@@ -196,7 +192,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
private final SpiLogManager logManager;
|
||||
private final PersistenceContextScope defaultPersistenceContextScope;
|
||||
private final int lazyLoadBatchSize;
|
||||
private final int queryBatchSize;
|
||||
private final boolean updateAllPropertiesInBatch;
|
||||
private final long slowQueryMicros;
|
||||
private final SlowQueryListener slowQueryListener;
|
||||
@@ -217,7 +212,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.extraMetrics = config.getExtraMetrics();
|
||||
this.serverName = this.config.getName();
|
||||
this.lazyLoadBatchSize = this.config.getLazyLoadBatchSize();
|
||||
this.queryBatchSize = this.config.getQueryBatchSize();
|
||||
this.cqueryEngine = config.getCQueryEngine();
|
||||
this.expressionFactory = config.getExpressionFactory();
|
||||
this.encryptKeyManager = this.config.getEncryptKeyManager();
|
||||
@@ -305,10 +299,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return lazyLoadBatchSize;
|
||||
}
|
||||
|
||||
public int getQueryBatchSize() {
|
||||
return queryBatchSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object currentTenantId() {
|
||||
return currentTenantProvider == null ? null : currentTenantProvider.currentId();
|
||||
@@ -410,6 +400,31 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
migrationRunner.loadProperties(config.getProperties());
|
||||
migrationRunner.run(config.getDataSource());
|
||||
}
|
||||
startQueryPlanCapture();
|
||||
}
|
||||
|
||||
private void startQueryPlanCapture() {
|
||||
if (config.isQueryPlanCapture()) {
|
||||
long secs = config.getQueryPlanCapturePeriodSecs();
|
||||
if (secs > 10) {
|
||||
logger.info("capture query plan enabled, every {}secs", secs);
|
||||
backgroundExecutor.scheduleWithFixedDelay(this::collectQueryPlans, secs, secs, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void collectQueryPlans() {
|
||||
QueryPlanRequest request = new QueryPlanRequest();
|
||||
request.setMaxCount(config.getQueryPlanCaptureMaxCount());
|
||||
request.setMaxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
|
||||
|
||||
// obtains query explain plans ...
|
||||
List<MetaQueryPlan> plans = metaInfoManager.queryPlanCollectNow(request);
|
||||
QueryPlanListener listener = config.getQueryPlanListener();
|
||||
if (listener == null) {
|
||||
listener = DefaultQueryPlanListener.INSTANT;
|
||||
}
|
||||
listener.process(new QueryPlanCapture(this, plans));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -451,16 +466,20 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
backgroundExecutor.shutdown();
|
||||
// shutdown DataSource (if its an Ebean one)
|
||||
transactionManager.shutdown(shutdownDataSource, deregisterDriver);
|
||||
dumpMetrics();
|
||||
shutdown = true;
|
||||
if (shutdownDataSource) {
|
||||
config.setDataSource(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownPlugins() {
|
||||
private void dumpMetrics() {
|
||||
if (config.isDumpMetricsOnShutdown()) {
|
||||
new DumpMetrics(this, config.getDumpMetricsOptions()).dump();
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownPlugins() {
|
||||
for (Plugin plugin : serverPlugins) {
|
||||
try {
|
||||
plugin.shutdown();
|
||||
@@ -1146,6 +1165,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction) {
|
||||
return Optional.ofNullable(findOne(query, transaction));
|
||||
@@ -1176,6 +1196,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public <T> Set<T> findSet(Query<T> query, Transaction t) {
|
||||
@@ -1192,6 +1213,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public <K, T> Map<K, T> findMap(Query<T> query, Transaction t) {
|
||||
@@ -1213,6 +1235,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction t) {
|
||||
@@ -1223,7 +1246,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return (List<A>) request.findSingleAttributeList();
|
||||
return request.findSingleAttributeList();
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1273,6 +1296,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <A, T> List<A> findIds(Query<T> query, Transaction t) {
|
||||
return findIdsWithCopy(((SpiQuery<T>) query).copy(), t);
|
||||
@@ -1333,26 +1357,29 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> FutureRowCount<T> findFutureCount(Query<T> q, Transaction t) {
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) q).copy();
|
||||
copy.setFutureFetch(true);
|
||||
Transaction newTxn = createTransaction();
|
||||
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<T>(this, copy, newTxn));
|
||||
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<>(this, copy, newTxn));
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t) {
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) query).copy();
|
||||
copy.setFutureFetch(true);
|
||||
Transaction newTxn = createTransaction();
|
||||
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<T>(this, copy, newTxn));
|
||||
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<>(this, copy, newTxn));
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> FutureList<T> findFutureList(Query<T> query, Transaction t) {
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
@@ -1365,11 +1392,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
// Create a new transaction solely to execute the findList() at some future time
|
||||
Transaction newTxn = createTransaction();
|
||||
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<T>(this, spiQuery, newTxn));
|
||||
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<>(this, spiQuery, newTxn));
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction) {
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
@@ -1383,6 +1411,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new LimitOffsetPagedList<>(this, spiQuery);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
|
||||
@@ -1395,11 +1424,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> Stream<T> findLargeStream(Query<T> query, Transaction transaction) {
|
||||
return findStream(query, transaction);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> Stream<T> findStream(Query<T> query, Transaction transaction) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, transaction);
|
||||
@@ -1442,6 +1473,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
// no try finally - findEach guarantee's cleanup of the transaction if required
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void findEach(Query<T> query, int batch, Consumer<List<T>> consumer, Transaction t) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
|
||||
// if (request.isUseDocStore()) {
|
||||
// docStore().findEach(request, consumer);
|
||||
// return;
|
||||
// }
|
||||
request.initTransIfRequired();
|
||||
request.findEach(batch, consumer);
|
||||
// no try finally - findEach guarantee's cleanup of the transaction if required
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction t) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
|
||||
@@ -1454,6 +1497,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
// no try finally - findEachWhile guarantee's cleanup of the transaction if required
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, transaction);
|
||||
@@ -1465,6 +1509,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> List<T> findList(Query<T> query, Transaction t) {
|
||||
return findList(query, t, false);
|
||||
@@ -1524,6 +1569,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<SqlRow> findList(SqlQuery query, Transaction t) {
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
|
||||
@@ -1581,6 +1627,17 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void findDtoEach(SpiDtoQuery<T> query, int batch, Consumer<List<T>> consumer) {
|
||||
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
request.findEach(batch, consumer);
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void findDtoEachWhile(SpiDtoQuery<T> query, Predicate<T> consumer) {
|
||||
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
|
||||
@@ -2214,6 +2271,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return checkUniqueness(bean, null);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Property> checkUniqueness(Object bean, Transaction transaction) {
|
||||
EntityBean entityBean = checkEntityBean(bean);
|
||||
@@ -2225,14 +2283,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
Object id = idProperty.getVal(entityBean);
|
||||
if (entityBean._ebean_getIntercept().isNew() && id != null) {
|
||||
// Primary Key is changeable only on new models - so skip check if we are not
|
||||
// new.
|
||||
// Primary Key is changeable only on new models - so skip check if we are not new
|
||||
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
|
||||
query.setId(id);
|
||||
if (findCount(query, transaction) > 0) {
|
||||
Set<Property> ret = new HashSet<>();
|
||||
ret.add(idProperty);
|
||||
return ret;
|
||||
return Collections.singleton(idProperty);
|
||||
}
|
||||
}
|
||||
for (BeanProperty[] props : beanDesc.getUniqueProps()) {
|
||||
@@ -2295,6 +2350,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
List<MetaQueryPlan> queryPlanInit(QueryPlanInit initRequest) {
|
||||
if (initRequest.isAll()) {
|
||||
queryPlanManager.setDefaultThreshold(initRequest.getThresholdMicros());
|
||||
}
|
||||
return beanDescriptorManager.queryPlanInit(initRequest);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
ormQuery.setManualId();
|
||||
|
||||
// execute the underlying ORM query returning the ResultSet
|
||||
SpiResultSet result = server.findResultSet(ormQuery, trans);
|
||||
SpiResultSet result = server.findResultSet(ormQuery, transaction);
|
||||
this.pstmt = result.getStatement();
|
||||
this.sql = ormQuery.getGeneratedSql();
|
||||
setResultSet(result.getResultSet(), ormQuery.getQueryPlanKey());
|
||||
@@ -88,14 +88,22 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
public void findEach(Consumer<T> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEach(this, consumer);
|
||||
}
|
||||
|
||||
public void findEach(int batch, Consumer<List<T>> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEach(this, batch, consumer);
|
||||
}
|
||||
|
||||
public void findEachWhile(Predicate<T> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEachWhile(this, consumer);
|
||||
}
|
||||
|
||||
public List<T> findList() {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findList(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ public final class InternString {
|
||||
|
||||
private static final HashMap<String, String> map = new HashMap<>();
|
||||
|
||||
private static final ReentrantLock lock = new ReentrantLock(false);
|
||||
private static final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Return the shared instance of this string.
|
||||
|
||||
@@ -617,10 +617,10 @@ public class InternalConfiguration {
|
||||
}
|
||||
|
||||
public QueryPlanManager initQueryPlanManager(TransactionManager transactionManager) {
|
||||
if (!config.isCollectQueryPlans()) {
|
||||
if (!config.isQueryPlanEnable()) {
|
||||
return QueryPlanManager.NOOP;
|
||||
}
|
||||
long threshold = config.getCollectQueryPlanThresholdMicros();
|
||||
long threshold = config.getQueryPlanThresholdMicros();
|
||||
return new CQueryPlanManager(transactionManager, threshold, queryPlanLogger(databasePlatform.getPlatform()), extraMetrics);
|
||||
}
|
||||
|
||||
|
||||
@@ -173,8 +173,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
* iteration is fine.
|
||||
* </p>
|
||||
*/
|
||||
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
|
||||
return loadContext.getSecondaryQueriesMinBatchSize(defaultQueryBatch);
|
||||
public int getSecondaryQueriesMinBatchSize() {
|
||||
return loadContext.getSecondaryQueriesMinBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -430,6 +430,24 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEach(int batch, Consumer<List<T>> batchConsumer) {
|
||||
final List<T> buffer = new ArrayList<>(batch);
|
||||
try (QueryIterator<T> it = queryEngine.findIterate(this)) {
|
||||
while (it.hasNext()) {
|
||||
buffer.add(it.next());
|
||||
if (buffer.size() >= batch) {
|
||||
batchConsumer.accept(buffer);
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
if (!buffer.isEmpty()) {
|
||||
// consume the remainder
|
||||
batchConsumer.accept(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEachWhile(Predicate<T> consumer) {
|
||||
try (QueryIterator<T> it = queryEngine.findIterate(this)) {
|
||||
|
||||
@@ -388,7 +388,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
@Override
|
||||
public void preGetterTrigger(int propertyIndex) {
|
||||
if (flushBatchOnGetter(propertyIndex)) {
|
||||
transaction.flushBatch();
|
||||
transaction.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,13 +73,13 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
/**
|
||||
* Add this request to BatchControl to flush later.
|
||||
*/
|
||||
public void addToFlushQueue(boolean early) {
|
||||
public void addToFlushQueue(int pos) {
|
||||
BatchControl control = transaction.getBatchControl();
|
||||
if (control == null) {
|
||||
control = persistExecute.createBatchControl(transaction);
|
||||
}
|
||||
flushQueue = true;
|
||||
control.addToFlushQueue(this, early);
|
||||
control.addToFlushQueue(this, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -104,18 +104,14 @@ public interface Persister {
|
||||
void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue);
|
||||
|
||||
/**
|
||||
* Queue the SqlUpdate for early execution (with JDBC batch).
|
||||
* Queue the SqlUpdate for execution with position 0, 1 or 2 defining
|
||||
* when it executes relative to the flush of beans .
|
||||
*/
|
||||
void addToFlushQueue(SpiSqlUpdate update, SpiTransaction t);
|
||||
void addToFlushQueue(SpiSqlUpdate update, SpiTransaction t, int pos);
|
||||
|
||||
/**
|
||||
* Queue the SqlUpdate for late execution (with JDBC batch).
|
||||
* Add the statement to JDBC batch for later execution via executeBatch.
|
||||
*/
|
||||
void addToFlushQueueLast(SpiSqlUpdate update, SpiTransaction t);
|
||||
|
||||
/**
|
||||
* Add the statement to JDBC batch for later execution via executeBatch.
|
||||
*/
|
||||
void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,35 +56,43 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
boolean findEachRow(RowConsumer mapper) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEachRow(this, mapper);
|
||||
return true;
|
||||
}
|
||||
|
||||
<T> List<T> findListMapper(RowMapper<T> mapper) {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findListMapper(this, mapper);
|
||||
}
|
||||
|
||||
<T> T findOneMapper(RowMapper<T> mapper) {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findOneMapper(this, mapper);
|
||||
}
|
||||
|
||||
public <T> List<T> findSingleAttributeList(Class<T> cls) {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findSingleAttributeList(this, cls);
|
||||
}
|
||||
|
||||
public <T> T findSingleAttribute(Class<T> cls) {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findSingleAttribute(this, cls);
|
||||
}
|
||||
|
||||
public void findEach(Consumer<SqlRow> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEach(this, consumer);
|
||||
}
|
||||
|
||||
public void findEachWhile(Predicate<SqlRow> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEach(this, consumer);
|
||||
}
|
||||
|
||||
public List<SqlRow> findList() {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findList(this);
|
||||
}
|
||||
|
||||
@@ -92,9 +100,7 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
* Build the list of property names.
|
||||
*/
|
||||
private String[] getPropertyNames() throws SQLException {
|
||||
|
||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||
|
||||
int columnsPlusOne = metaData.getColumnCount() + 1;
|
||||
ArrayList<String> propNames = new ArrayList<>(columnsPlusOne - 1);
|
||||
for (int i = 1; i < columnsPlusOne; i++) {
|
||||
@@ -107,9 +113,7 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
* Read and return the next SqlRow.
|
||||
*/
|
||||
public SqlRow createNewRow() throws SQLException {
|
||||
|
||||
rows++;
|
||||
|
||||
SqlRow sqlRow = queryEngine.createSqlRow(estimateCapacity);
|
||||
int index = 0;
|
||||
for (String propertyName : propertyNames) {
|
||||
@@ -121,9 +125,9 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
public void logSummary() {
|
||||
if (trans.isLogSummary()) {
|
||||
if (transaction.isLogSummary()) {
|
||||
long micros = (System.nanoTime() - startNano) / 1000L;
|
||||
trans.logSummary("SqlQuery rows[" + rows + "] micros[" + micros + "] bind[" + bindLog + "]");
|
||||
transaction.logSummary("SqlQuery rows[" + rows + "] micros[" + micros + "] bind[" + bindLog + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +140,6 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
public <T> List<T> mapList(RowMapper<T> mapper) throws SQLException {
|
||||
|
||||
List<T> list = new ArrayList<>();
|
||||
while (next()) {
|
||||
list.add(mapper.map(resultSet, rows++));
|
||||
|
||||
@@ -80,10 +80,15 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
|
||||
<A> List<A> findIds();
|
||||
|
||||
/**
|
||||
* Execute the find returning a QueryIterator and visitor pattern.
|
||||
* Execute findEach iterating results one bean at a time.
|
||||
*/
|
||||
void findEach(Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Execute findEach with a batch consumer.
|
||||
*/
|
||||
void findEach(int batch, Consumer<List<T>> batchConsumer);
|
||||
|
||||
/**
|
||||
* Execute the find returning a QueryIterator and visitor pattern.
|
||||
*/
|
||||
|
||||
@@ -223,12 +223,9 @@ public class BootupClasses implements ClassFilter {
|
||||
*/
|
||||
private <T> T create(Class<T> cls, boolean logOnException) {
|
||||
try {
|
||||
// instantiate via found class
|
||||
Constructor<T> constructor = cls.getConstructor();
|
||||
return constructor.newInstance();
|
||||
|
||||
return cls.getConstructor().newInstance();
|
||||
} catch (NoSuchMethodException e) {
|
||||
logger.debug("Ignore/expected - no default constructor", e);
|
||||
logger.debug("Ignore/expected - no default constructor: " +e.getMessage());
|
||||
return null;
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -522,7 +522,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (EntityBean) beanType.newInstance();
|
||||
return (EntityBean) beanType.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Error trying to create the prototypeEntityBean for " + beanType, e);
|
||||
}
|
||||
@@ -727,7 +727,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
String[] cols = indexDef.getColumns();
|
||||
BeanProperty[] props = new BeanProperty[cols.length];
|
||||
for (int i = 0; i < cols.length; i++) {
|
||||
String propName = findBeanPath("", cols[i]);
|
||||
String propName = findBeanPath("", "", cols[i]);
|
||||
if (propName == null) {
|
||||
return;
|
||||
}
|
||||
@@ -814,7 +814,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
int propertyLength = toEbi.getPropertyLength();
|
||||
String[] names = getProperties();
|
||||
for (int i = 0; i < propertyLength; i++) {
|
||||
|
||||
if (fromEbi.isLoadedProperty(i)) {
|
||||
BeanProperty property = getBeanProperty(names[i]);
|
||||
if (!toEbi.isLoadedProperty(i)) {
|
||||
@@ -1865,17 +1864,23 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
|
||||
/**
|
||||
* We actually need to do a query because we don't know the type without the discriminator
|
||||
* value, just select the id property and discriminator column (auto added)
|
||||
* We actually need to do a query because we don't know the type without the discriminator value.
|
||||
*/
|
||||
private T findReferenceBean(Object id, PersistenceContext pc) {
|
||||
DefaultOrmQuery<T> query = new DefaultOrmQuery<>(this, ebeanServer, ebeanServer.getExpressionFactory());
|
||||
query.setPersistenceContext(pc);
|
||||
return query
|
||||
// .select(getIdProperty().getName())
|
||||
// we do not select the id because we
|
||||
// probably have to load the entire bean
|
||||
.setId(id).findOne();
|
||||
return query.setId(id).findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference with a check for the bean in the persistence context.
|
||||
*/
|
||||
public EntityBean createReference(Boolean readOnly, Object id, PersistenceContext pc) {
|
||||
Object refBean = contextGet(pc, id);
|
||||
if (refBean == null) {
|
||||
refBean = createReference(readOnly, false, id, pc);
|
||||
}
|
||||
return (EntityBean)refBean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2087,6 +2092,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the simple name of the entity bean.
|
||||
*/
|
||||
public String getSimpleName() {
|
||||
return beanType.getSimpleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary description.
|
||||
*/
|
||||
@@ -2271,8 +2283,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
*/
|
||||
public void lazyLoadRegister(String prefix, EntityBeanIntercept ebi, EntityBean bean, LoadContext loadContext) {
|
||||
// load the List/Set/Map proxy objects (deferred fetching of lists)
|
||||
BeanPropertyAssocMany<?>[] manys = propertiesMany();
|
||||
for (BeanPropertyAssocMany<?> many : manys) {
|
||||
for (BeanPropertyAssocMany<?> many : propertiesMany()) {
|
||||
if (!ebi.isLoadedProperty(many.getPropertyIndex())) {
|
||||
BeanCollection<?> ref = many.createReferenceIfNull(bean);
|
||||
if (ref != null && !ref.isRegisteredWithLoadContext()) {
|
||||
@@ -2461,13 +2472,16 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Return the property path given the db table and column.
|
||||
*/
|
||||
public String findBeanPath(String tableName, String columnName) {
|
||||
if (tableName.isEmpty() || tableName.equalsIgnoreCase(baseTable)) {
|
||||
public String findBeanPath(String schemaName, String tableName, String columnName) {
|
||||
if (matchBaseTable(schemaName, tableName)) {
|
||||
return columnPath.get(columnName);
|
||||
}
|
||||
BeanPropertyAssoc<?> assocProperty = tablePath.get(tableName);
|
||||
if (assocProperty == null) {
|
||||
assocProperty = tablePath.get(schemaName + "." + tableName);
|
||||
}
|
||||
if (assocProperty != null) {
|
||||
String relativePath = assocProperty.getTargetDescriptor().findBeanPath(tableName, columnName);
|
||||
String relativePath = assocProperty.getTargetDescriptor().findBeanPath(schemaName, tableName, columnName);
|
||||
if (relativePath != null) {
|
||||
return SplitName.add(assocProperty.getName(), relativePath);
|
||||
}
|
||||
@@ -2475,6 +2489,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean matchBaseTable(String schemaName, String tableName) {
|
||||
return tableName.isEmpty()
|
||||
|| baseTable.equalsIgnoreCase(tableName)
|
||||
|| baseTable.equalsIgnoreCase(schemaName + "." + tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a 'dynamic property' used to read a formula.
|
||||
*/
|
||||
|
||||
+1
-42
@@ -89,7 +89,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
BeanDescriptorCacheHelp(BeanDescriptor<T> desc, SpiCacheManager cacheManager, CacheOptions cacheOptions,
|
||||
boolean cacheSharableBeans, BeanPropertyAssocOne<?>[] propertiesOneImported) {
|
||||
|
||||
this.desc = desc;
|
||||
this.beanType = desc.rootBeanType;
|
||||
this.cacheName = beanType.getSimpleName();
|
||||
@@ -99,7 +98,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
this.cacheSharableBeans = cacheSharableBeans;
|
||||
this.propertiesOneImported = propertiesOneImported;
|
||||
this.naturalKey = cacheOptions.getNaturalKey();
|
||||
|
||||
if (!cacheOptions.isEnableQueryCache()) {
|
||||
this.queryCache = null;
|
||||
} else {
|
||||
@@ -133,7 +131,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
void deriveNotifyFlags() {
|
||||
cacheNotifyOnAll = (invalidateQueryCache || beanCache != null || queryCache != null);
|
||||
cacheNotifyOnDelete = !cacheNotifyOnAll && isNotifyOnDeletes();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (cacheNotifyOnAll || cacheNotifyOnDelete) {
|
||||
String notifyMode = cacheNotifyOnAll ? "All" : "Delete";
|
||||
@@ -292,7 +289,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Try to load the bean collection from cache return true if successful.
|
||||
*/
|
||||
boolean manyPropLoad(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, Object parentId, Boolean readOnly) {
|
||||
|
||||
if (many.isElementCollection()) {
|
||||
// held as part of the bean cache so skip
|
||||
return false;
|
||||
@@ -307,14 +303,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
EntityBean ownerBean = bc.getOwnerBean();
|
||||
EntityBeanIntercept ebi = ownerBean._ebean_getIntercept();
|
||||
PersistenceContext persistenceContext = ebi.getPersistenceContext();
|
||||
|
||||
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
|
||||
|
||||
List<Object> idList = entry.getIdList();
|
||||
bc.checkEmptyLazyLoad();
|
||||
for (Object id : idList) {
|
||||
Object refBean = targetDescriptor.createReference(readOnly, false, id, persistenceContext);
|
||||
many.add(bc, (EntityBean) refBean);
|
||||
many.add(bc, targetDescriptor.createReference(readOnly, id, persistenceContext));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -323,7 +317,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Put the beanCollection into the cache.
|
||||
*/
|
||||
void manyPropPut(BeanPropertyAssocMany<?> many, Object details, Object parentId) {
|
||||
|
||||
if (many.isElementCollection()) {
|
||||
CachedBeanData data = (CachedBeanData) beanCache.get(parentId);
|
||||
if (data != null) {
|
||||
@@ -351,7 +344,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
void cachePutManyIds(Object parentId, String manyName, CachedManyIds entry) {
|
||||
|
||||
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, manyName);
|
||||
if (manyLog.isDebugEnabled()) {
|
||||
manyLog.debug(" PUT {}({}).{} - ids:{}", cacheName, parentId, manyName, entry);
|
||||
@@ -360,7 +352,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
private CachedManyIds createManyIds(BeanPropertyAssocMany<?> many, Object details) {
|
||||
|
||||
Collection<?> actualDetails = BeanCollectionUtil.getActualDetails(details);
|
||||
if (actualDetails == null) {
|
||||
return null;
|
||||
@@ -378,7 +369,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Hit the bean cache with the given ids returning the hits.
|
||||
*/
|
||||
BeanCacheResult<T> cacheIdLookup(PersistenceContext context, Collection<?> ids) {
|
||||
|
||||
Set<Object> keys = new HashSet<>(ids.size());
|
||||
for (Object id : ids) {
|
||||
keys.add(desc.cacheKey(id));
|
||||
@@ -395,7 +385,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
T bean = convertToBean(entry.getKey(), false, context, cachedBeanData);
|
||||
result.add(bean, desc.getBeanId(bean));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -403,14 +392,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Use natural keys to hit the bean cache and return resulting hits.
|
||||
*/
|
||||
BeanCacheResult<T> naturalKeyLookup(PersistenceContext context, Set<Object> keys) {
|
||||
|
||||
if (context == null) {
|
||||
context = new DefaultPersistenceContext();
|
||||
}
|
||||
|
||||
// naturalKey -> Id map
|
||||
Map<Object, Object> naturalKeyMap = naturalKeyCache.getAll(keys);
|
||||
|
||||
if (natLog.isTraceEnabled()) {
|
||||
natLog.trace(" MLOOKUP {}({}) - hits:{}", cacheName, keys, naturalKeyMap);
|
||||
}
|
||||
@@ -433,15 +420,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
// process the hits into beans etc
|
||||
for (Map.Entry<Object, Object> entry : beanDataMap.entrySet()) {
|
||||
|
||||
Object id = entry.getKey();
|
||||
CachedBeanData cachedBeanData = (CachedBeanData) entry.getValue();
|
||||
|
||||
T bean = convertToBean(id, false, context, cachedBeanData);
|
||||
Object naturalKey = reverseMap.get(id);
|
||||
result.add(bean, naturalKey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -452,7 +436,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
if (context == null) {
|
||||
context = new DefaultPersistenceContext();
|
||||
}
|
||||
|
||||
// Not using a loadContext for beans coming out of L2 cache
|
||||
// so that means no batch lazy loading for these beans
|
||||
EntityBean entityBean = (EntityBean) bean;
|
||||
@@ -522,7 +505,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Put a bean into the bean cache.
|
||||
*/
|
||||
void beanCachePut(EntityBean bean) {
|
||||
|
||||
if (desc.inheritInfo != null) {
|
||||
desc.descOf(bean.getClass()).cacheBeanPutDirect(bean);
|
||||
} else {
|
||||
@@ -531,7 +513,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
void beanCachePutAllDirect(Collection<EntityBean> beans) {
|
||||
|
||||
Map<Object, Object> natKeys = null;
|
||||
if (naturalKey != null) {
|
||||
natKeys = new LinkedHashMap<>();
|
||||
@@ -566,15 +547,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Put the bean into the bean cache.
|
||||
*/
|
||||
void beanCachePutDirect(EntityBean bean) {
|
||||
|
||||
CachedBeanData beanData = beanExtractData(desc, bean);
|
||||
|
||||
String key = desc.cacheKeyForBean(bean);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" PUT {}({}) data:{}", cacheName, key, beanData);
|
||||
}
|
||||
getBeanCache().put(key, beanData);
|
||||
|
||||
if (naturalKey != null) {
|
||||
String naturalKey = calculateNaturalKey(beanData);
|
||||
if (naturalKey != null) {
|
||||
@@ -618,7 +596,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Return a bean from the bean cache.
|
||||
*/
|
||||
private T beanCacheGetInternal(String key, Boolean readOnly, PersistenceContext context) {
|
||||
|
||||
CachedBeanData data = (CachedBeanData) getBeanCache().get(key);
|
||||
if (data == null) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
@@ -646,7 +623,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
return (T) bean;
|
||||
}
|
||||
}
|
||||
|
||||
return (T) loadBean(id, readOnly, data, context);
|
||||
}
|
||||
|
||||
@@ -654,7 +630,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Load the entity bean taking into account inheritance.
|
||||
*/
|
||||
private EntityBean loadBean(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
|
||||
|
||||
String discValue = data.getDiscValue();
|
||||
if (discValue == null) {
|
||||
return loadBeanDirect(id, readOnly, data, context);
|
||||
@@ -674,7 +649,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Load the entity bean from cache data given this is the root bean type.
|
||||
*/
|
||||
EntityBean loadBeanDirect(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
|
||||
|
||||
id = desc.convertId(id);
|
||||
EntityBean bean = null;
|
||||
if (context == null) {
|
||||
@@ -682,7 +656,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
} else {
|
||||
bean = (EntityBean) desc.contextGet(context, id);
|
||||
}
|
||||
|
||||
if (bean == null) {
|
||||
bean = desc.createEntityBean();
|
||||
desc.setId(id, bean);
|
||||
@@ -698,7 +671,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
CachedBeanDataToBean.load(desc, bean, data, context);
|
||||
|
||||
if (desc.isReadAuditing()) {
|
||||
desc.readAuditBean("l2", "", bean);
|
||||
}
|
||||
@@ -709,7 +681,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Load the embedded bean checking for inheritance.
|
||||
*/
|
||||
EntityBean embeddedBeanLoad(CachedBeanData data, PersistenceContext context) {
|
||||
|
||||
String discValue = data.getDiscValue();
|
||||
if (discValue == null) {
|
||||
return embeddedBeanLoadDirect(data, context);
|
||||
@@ -746,25 +717,20 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Load a batch of entities from L2 bean cache checking the lazy loaded property is loaded.
|
||||
*/
|
||||
Set<EntityBeanIntercept> beanCacheLoadAll(List<EntityBeanIntercept> list, PersistenceContext context, int lazyLoadProperty, String propertyName) {
|
||||
|
||||
Map<Object, EntityBeanIntercept> ebis = new HashMap<>();
|
||||
for (EntityBeanIntercept ebi : list) {
|
||||
ebis.put(desc.cacheKeyForBean(ebi.getOwner()), ebi);
|
||||
}
|
||||
|
||||
|
||||
Map<Object, Object> hits = getBeanCache().getAll(ebis.keySet());
|
||||
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" MLOAD {}({}) - got hits ({})", cacheName, ebis.keySet(), hits.size());
|
||||
}
|
||||
|
||||
Set<EntityBeanIntercept> loaded = new HashSet<>();
|
||||
|
||||
Iterator<Map.Entry<Object, Object>> iterator = hits.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<Object, Object> hit = iterator.next();
|
||||
|
||||
Object key = hit.getKey();
|
||||
EntityBeanIntercept ebi = ebis.remove(key);
|
||||
CachedBeanData cacheData = (CachedBeanData) hit.getValue();
|
||||
@@ -774,7 +740,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
beanLog.trace(" load {}({}) - cache miss on property({})", cacheName, key, propertyName);
|
||||
}
|
||||
iterator.remove();
|
||||
|
||||
} else {
|
||||
CachedBeanDataToBean.load(desc, ebi.getOwner(), cacheData, context);
|
||||
loaded.add(ebi);
|
||||
@@ -783,11 +748,9 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ebis.isEmpty() && beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" load {}({}) - cache miss", cacheName, ebis.keySet());
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
@@ -795,7 +758,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Returns true if it managed to populate/load the single bean from the cache.
|
||||
*/
|
||||
boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, String key, PersistenceContext context) {
|
||||
|
||||
CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(key);
|
||||
if (cacheData == null) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
@@ -810,7 +772,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CachedBeanDataToBean.load(desc, bean, cacheData, context);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" LOAD {}({}) - hit", cacheName, key);
|
||||
@@ -932,7 +893,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Apply changes to the bean cache entry.
|
||||
*/
|
||||
void cacheBeanUpdate(String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
|
||||
ServerCache cache = getBeanCache();
|
||||
CachedBeanData existingData = (CachedBeanData) cache.get(key);
|
||||
if (existingData != null) {
|
||||
@@ -952,7 +912,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
cache.put(key, newData);
|
||||
}
|
||||
|
||||
if (updateNaturalKey) {
|
||||
Object oldKey = calculateNaturalKey(existingData);
|
||||
if (oldKey != null) {
|
||||
|
||||
@@ -14,13 +14,32 @@ import java.util.Arrays;
|
||||
*/
|
||||
abstract class BeanDescriptorElement<T> extends BeanDescriptor<T> {
|
||||
|
||||
private final String simpleName;
|
||||
|
||||
final ElementHelp elementHelp;
|
||||
|
||||
BeanDescriptorElement(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy);
|
||||
this.simpleName = shortName(deploy.getName());
|
||||
this.elementHelp = elementHelp;
|
||||
}
|
||||
|
||||
private String shortName(String name) {
|
||||
int pos = name.lastIndexOf('.');
|
||||
if (pos > 1) {
|
||||
pos = name.lastIndexOf('.', pos - 1);
|
||||
if (pos > 1) {
|
||||
return name.substring(pos + 1);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSimpleName() {
|
||||
return simpleName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJsonReadCollection() {
|
||||
return true;
|
||||
|
||||
+18
-112
@@ -93,6 +93,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorManager.class);
|
||||
|
||||
private static final BeanDescComparator beanDescComparator = new BeanDescComparator();
|
||||
public static final String JAVA_LANG_RECORD = "java.lang.Record";
|
||||
|
||||
private final ReadAnnotations readAnnotations;
|
||||
private final TransientProperties transientProperties;
|
||||
@@ -117,6 +118,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
private final TypeManager typeManager;
|
||||
private final BootupClasses bootupClasses;
|
||||
private final String serverName;
|
||||
private final List<BeanDescriptor<?>> elementDescriptors = new ArrayList<>();
|
||||
private final Map<Class<?>, BeanTable> beanTableMap = new HashMap<>();
|
||||
private final Map<String, BeanDescriptor<?>> descMap = new HashMap<>();
|
||||
private final Map<String, BeanDescriptor<?>> descQueueMap = new HashMap<>();
|
||||
@@ -202,7 +204,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Run periodic trim of query plans.
|
||||
*/
|
||||
public void scheduleBackgroundTrim() {
|
||||
backgroundExecutor.executePeriodically(this::trimQueryPlans, 117L, 60L, TimeUnit.SECONDS);
|
||||
backgroundExecutor.scheduleWithFixedDelay(this::trimQueryPlans, 117L, 60L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void trimQueryPlans() {
|
||||
@@ -237,7 +239,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Return the versions between timestamp suffix based on the DbHistorySupport.
|
||||
*/
|
||||
private String getVersionsBetweenSuffix(DatabasePlatform databasePlatform, DatabaseConfig serverConfig) {
|
||||
|
||||
DbHistorySupport historySupport = databasePlatform.getHistorySupport();
|
||||
// with historySupport returns a simple view suffix or the sql2011 versions between timestamp suffix
|
||||
return (historySupport == null) ? serverConfig.getAsOfViewSuffix() : historySupport.getVersionsBetweenSuffix(serverConfig.getAsOfViewSuffix());
|
||||
@@ -361,7 +362,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private void readEntityMapping(ClassLoader classLoader, XmapEntity entityDeploy) {
|
||||
|
||||
String entityClassName = entityDeploy.getClazz();
|
||||
Class<?> entityClass;
|
||||
try {
|
||||
@@ -411,7 +411,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* For SQL based modifications we need to invalidate appropriate parts of the cache.
|
||||
*/
|
||||
public void cacheNotify(TransactionEventTable.TableIUD tableIUD, CacheChangeSet changeSet) {
|
||||
|
||||
String tableName = tableIUD.getTableName().toLowerCase();
|
||||
List<BeanDescriptor<?>> normalBeanTypes = tableToDescMap.get(tableName);
|
||||
if (normalBeanTypes != null) {
|
||||
@@ -447,7 +446,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Invalidate entity beans based on views via their dependent tables.
|
||||
*/
|
||||
public void processViewInvalidation(Set<String> viewInvalidation) {
|
||||
|
||||
for (String depTable : viewInvalidation) {
|
||||
List<BeanDescriptor<?>> list = tableToViewDescMap.get(depTable.toLowerCase());
|
||||
if (list != null) {
|
||||
@@ -460,12 +458,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
/**
|
||||
* Build a map of table names to BeanDescriptors.
|
||||
* <p>
|
||||
* This is generally used to maintain caches from table names.
|
||||
* </p>
|
||||
*/
|
||||
private void readTableToDescriptor() {
|
||||
|
||||
for (BeanDescriptor<?> desc : descMap.values()) {
|
||||
String baseTable = desc.getBaseTable();
|
||||
if (baseTable != null) {
|
||||
@@ -489,7 +483,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private void readForeignKeys() {
|
||||
|
||||
for (BeanDescriptor<?> d : descMap.values()) {
|
||||
d.initialiseFkeys();
|
||||
}
|
||||
@@ -500,18 +493,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* <p>
|
||||
* This occurs after all the BeanDescriptors have been created. This resolves
|
||||
* circular relationships between BeanDescriptors.
|
||||
* </p>
|
||||
* <p>
|
||||
* Also responsible for creating all the BeanManagers which contain the
|
||||
* persister, listener etc.
|
||||
* </p>
|
||||
*/
|
||||
private void initialiseAll() {
|
||||
|
||||
// now that all the BeanDescriptors are in their map
|
||||
// we can initialise them which sorts out circular
|
||||
// dependencies for OneToMany and ManyToOne etc
|
||||
|
||||
BeanDescriptorInitContext initContext = new BeanDescriptorInitContext(asOfTableMap, draftTableMap, asOfViewSuffix);
|
||||
|
||||
// PASS 1:
|
||||
@@ -569,7 +558,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private void checkMissingHashCodeOrEquals(Exception source, Class<?> idType, Class<?> beanType) {
|
||||
|
||||
String msg = "SERIOUS ERROR: The hashCode() and equals() methods *MUST* be implemented ";
|
||||
msg += "on Embedded bean " + idType + " as it is used as an Id for " + beanType;
|
||||
throw new PersistenceException(msg, source);
|
||||
@@ -603,7 +591,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> BeanManager<T> getBeanManager(Class<T> entityType) {
|
||||
|
||||
return (BeanManager<T>) getBeanManager(entityType.getName());
|
||||
}
|
||||
|
||||
@@ -615,14 +602,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Create the BeanControllers, BeanFinders and BeanListeners.
|
||||
*/
|
||||
private void createListeners() {
|
||||
|
||||
int qa = beanQueryAdapterManager.getRegisterCount();
|
||||
int cc = persistControllerManager.getRegisterCount();
|
||||
int pl = postLoadManager.getRegisterCount();
|
||||
int pc = postConstructManager.getRegisterCount();
|
||||
int lc = persistListenerManager.getRegisterCount();
|
||||
int fc = beanFinderManager.getRegisterCount();
|
||||
|
||||
logger.debug("BeanPersistControllers[{}] BeanFinders[{}] BeanPersistListeners[{}] BeanQueryAdapters[{}] BeanPostLoaders[{}] BeanPostConstructors[{}]", cc, fc, lc, qa, pl, pc);
|
||||
}
|
||||
|
||||
@@ -639,11 +624,16 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private void registerBeanDescriptor(DeployBeanInfo<?> info) {
|
||||
BeanDescriptor desc = new BeanDescriptor<>(this, info.getDescriptor());
|
||||
BeanDescriptor<?> desc = new BeanDescriptor<>(this, info.getDescriptor());
|
||||
descMap.put(desc.getBeanType().getName(), desc);
|
||||
if (desc.isDocStoreMapped()) {
|
||||
descQueueMap.put(desc.getDocStoreQueueId(), desc);
|
||||
}
|
||||
for (BeanPropertyAssocMany<?> many : desc.propertiesMany()) {
|
||||
if (many.isElementCollection()) {
|
||||
elementDescriptors.add(many.getElementDescriptor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -651,10 +641,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* <p>
|
||||
* This stops short of reading relationship meta data until after the
|
||||
* BeanTables have all been created.
|
||||
* </p>
|
||||
*/
|
||||
private void readEntityDeploymentInitial() {
|
||||
|
||||
for (Class<?> entityClass : bootupClasses.getEntities()) {
|
||||
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
|
||||
deployInfoMap.put(entityClass, info);
|
||||
@@ -687,15 +675,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Create the BeanTable information which has the base table and id.
|
||||
* <p>
|
||||
* This is determined prior to resolving relationship information.
|
||||
* </p>
|
||||
*/
|
||||
private void readEntityBeanTable() {
|
||||
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
BeanTable beanTable = createBeanTable(info);
|
||||
beanTableMap.put(beanTable.getBeanType(), beanTable);
|
||||
}
|
||||
|
||||
// register non-id embedded beans (after bean tables are created)
|
||||
for (DeployBeanInfo<?> info : embeddedBeans) {
|
||||
registerEmbeddedBean(info);
|
||||
@@ -706,17 +691,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Create the BeanTable information which has the base table and id.
|
||||
* <p>
|
||||
* This is determined prior to resolving relationship information.
|
||||
* </p>
|
||||
*/
|
||||
private void readEntityDeploymentAssociations() {
|
||||
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
readDeployAssociations(info);
|
||||
}
|
||||
}
|
||||
|
||||
private void readInheritedIdGenerators() {
|
||||
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
DeployBeanDescriptor<?> descriptor = info.getDescriptor();
|
||||
InheritInfo inheritInfo = descriptor.getInheritInfo();
|
||||
@@ -734,17 +716,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Create the BeanTable from the deployment information gathered so far.
|
||||
*/
|
||||
private BeanTable createBeanTable(DeployBeanInfo<?> info) {
|
||||
|
||||
DeployBeanDescriptor<?> deployDescriptor = info.getDescriptor();
|
||||
DeployBeanTable beanTable = deployDescriptor.createDeployBeanTable();
|
||||
return new BeanTable(beanTable, this);
|
||||
}
|
||||
|
||||
private void readEntityRelationships() {
|
||||
|
||||
// We only perform 'circular' checks etc after we have
|
||||
// all the DeployBeanDescriptors created and in the map.
|
||||
|
||||
List<DeployBeanPropertyAssocOne<?>> primaryKeyJoinCheck = new ArrayList<>();
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
checkMappedBy(info, primaryKeyJoinCheck);
|
||||
@@ -752,15 +731,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
for (DeployBeanPropertyAssocOne<?> prop : primaryKeyJoinCheck) {
|
||||
checkUniDirectionalPrimaryKeyJoin(prop);
|
||||
}
|
||||
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
secondaryPropsJoins(info);
|
||||
}
|
||||
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
setInheritanceInfo(info);
|
||||
}
|
||||
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
if (!info.isEmbedded()) {
|
||||
registerBeanDescriptor(info);
|
||||
@@ -769,12 +745,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the inheritance info. ~EMG fix for join problem
|
||||
*
|
||||
* @param info the new inheritance info
|
||||
* Sets the inheritance info.
|
||||
*/
|
||||
private void setInheritanceInfo(DeployBeanInfo<?> info) {
|
||||
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deployInfoMap.get(oneProp.getTargetType());
|
||||
@@ -783,7 +756,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
|
||||
if (!manyProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deployInfoMap.get(manyProp.getTargetType());
|
||||
@@ -795,7 +767,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private void secondaryPropsJoins(DeployBeanInfo<?> info) {
|
||||
|
||||
DeployBeanDescriptor<?> descriptor = info.getDescriptor();
|
||||
for (DeployBeanProperty prop : descriptor.propertiesBase()) {
|
||||
if (prop.isSecondaryTable()) {
|
||||
@@ -819,10 +790,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* This will read join information defined on the 'owning/other' side of the
|
||||
* relationship. It also does some extra work for unidirectional
|
||||
* relationships.
|
||||
* </p>
|
||||
*/
|
||||
private void checkMappedBy(DeployBeanInfo<?> info, List<DeployBeanPropertyAssocOne<?>> primaryKeyJoinCheck) {
|
||||
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
if (oneProp.getMappedBy() != null) {
|
||||
@@ -845,14 +814,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private DeployBeanDescriptor<?> getTargetDescriptor(DeployBeanPropertyAssoc<?> prop) {
|
||||
|
||||
Class<?> targetType = prop.getTargetType();
|
||||
DeployBeanInfo<?> info = deployInfoMap.get(targetType);
|
||||
if (info == null) {
|
||||
String msg = "Can not find descriptor [" + targetType + "] for " + prop.getFullBeanName();
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
|
||||
return info.getDescriptor();
|
||||
}
|
||||
|
||||
@@ -861,10 +828,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* mark it as unidirectional.
|
||||
*/
|
||||
private boolean findMappedBy(DeployBeanPropertyAssocMany<?> prop) {
|
||||
|
||||
// this is the entity bean type - that owns this property
|
||||
Class<?> owningType = prop.getOwningType();
|
||||
|
||||
Set<String> matchSet = new HashSet<>();
|
||||
|
||||
// get the bean descriptor that holds the mappedBy property
|
||||
@@ -918,7 +883,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// multiple options so should specify mappedBy property
|
||||
@@ -930,12 +894,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private void makeOrderColumn(DeployBeanPropertyAssocMany<?> oneToMany) {
|
||||
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(oneToMany);
|
||||
|
||||
DeployOrderColumn orderColumn = oneToMany.getOrderColumn();
|
||||
DeployBeanProperty orderProperty = new DeployBeanProperty(targetDesc, Integer.class, ScalarTypeInteger.INSTANCE, null);
|
||||
|
||||
orderProperty.setName(DeployOrderColumn.LOGICAL_NAME);
|
||||
orderProperty.setDbColumn(orderColumn.getName());
|
||||
orderProperty.setNullable(orderColumn.isNullable());
|
||||
@@ -943,7 +904,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
orderProperty.setDbUpdateable(orderColumn.isUpdatable());
|
||||
orderProperty.setDbRead(true);
|
||||
orderProperty.setOwningType(targetDesc.getBeanType());
|
||||
|
||||
final InheritInfo targetInheritInfo = targetDesc.getInheritInfo();
|
||||
if (targetInheritInfo != null) {
|
||||
for (InheritInfo child : targetInheritInfo.getChildren()) {
|
||||
@@ -951,7 +911,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
childDescriptor.setOrderColumn(orderProperty);
|
||||
}
|
||||
}
|
||||
|
||||
targetDesc.setOrderColumn(orderProperty);
|
||||
}
|
||||
|
||||
@@ -960,19 +919,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* unidirectional.
|
||||
* <p>
|
||||
* This means that inserts MUST cascade for this property.
|
||||
* </p>
|
||||
* <p>
|
||||
* Create a "Shadow"/Unidirectional property on the target. It is used with
|
||||
* inserts to set the foreign key value (e.g. inserts the foreign key value
|
||||
* into the order_id column on the order_lines table).
|
||||
* </p>
|
||||
*/
|
||||
private void makeUnidirectional(DeployBeanPropertyAssocMany<?> oneToMany) {
|
||||
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(oneToMany);
|
||||
|
||||
Class<?> owningType = oneToMany.getOwningType();
|
||||
|
||||
if (!oneToMany.getCascadeInfo().isSave()) {
|
||||
// The property MUST have persist cascading so that inserts work.
|
||||
|
||||
@@ -1020,11 +974,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private void checkMappedByOneToOne(DeployBeanPropertyAssocOne<?> prop) {
|
||||
|
||||
// check that the mappedBy property is valid and read
|
||||
// its associated join information if it is available
|
||||
String mappedBy = prop.getMappedBy();
|
||||
|
||||
// get the mappedBy property
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(prop);
|
||||
DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy);
|
||||
@@ -1077,10 +1029,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* <p>
|
||||
* We can use the join information from the mappedBy property and reverse it
|
||||
* for using in the OneToMany direction.
|
||||
* </p>
|
||||
*/
|
||||
private void checkMappedByOneToMany(DeployBeanInfo<?> info, DeployBeanPropertyAssocMany<?> prop) {
|
||||
|
||||
if (prop.isElementCollection()) {
|
||||
// skip mapping check
|
||||
return;
|
||||
@@ -1161,7 +1111,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* For mappedBy copy the joins from the other side.
|
||||
*/
|
||||
private void checkMappedByManyToMany(DeployBeanPropertyAssocMany<?> prop) {
|
||||
|
||||
// get the bean descriptor that holds the mappedBy property
|
||||
String mappedBy = prop.getMappedBy();
|
||||
if (mappedBy == null) {
|
||||
@@ -1222,50 +1171,40 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private <T> void setBeanControllerFinderListener(DeployBeanDescriptor<T> descriptor) {
|
||||
|
||||
persistControllerManager.addPersistControllers(descriptor);
|
||||
postLoadManager.addPostLoad(descriptor);
|
||||
postConstructManager.addPostConstructListeners(descriptor);
|
||||
persistListenerManager.addPersistListeners(descriptor);
|
||||
beanQueryAdapterManager.addQueryAdapter(descriptor);
|
||||
beanFinderManager.addFindControllers(descriptor);
|
||||
|
||||
if (changeLogRegister != null) {
|
||||
ChangeLogFilter changeFilter = changeLogRegister.getChangeFilter(descriptor.getBeanType());
|
||||
if (changeFilter != null) {
|
||||
descriptor.setChangeLogFilter(changeFilter);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the initial deployment information for a given bean type.
|
||||
*/
|
||||
private <T> DeployBeanInfo<T> createDeployBeanInfo(Class<T> beanClass) {
|
||||
|
||||
DeployBeanDescriptor<T> desc = new DeployBeanDescriptor<>(this, beanClass, config);
|
||||
beanLifecycleAdapterFactory.addLifecycleMethods(desc);
|
||||
|
||||
// set bean controller, finder and listener
|
||||
setBeanControllerFinderListener(desc);
|
||||
deplyInherit.process(desc);
|
||||
desc.checkInheritanceMapping();
|
||||
|
||||
createProperties.createProperties(desc);
|
||||
|
||||
DeployBeanInfo<T> info = new DeployBeanInfo<>(deployUtil, desc);
|
||||
|
||||
readAnnotations.readInitial(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
private <T> void readDeployAssociations(DeployBeanInfo<T> info) {
|
||||
|
||||
DeployBeanDescriptor<T> desc = info.getDescriptor();
|
||||
|
||||
readAnnotations.readAssociations(info, this);
|
||||
|
||||
if (EntityType.SQL == desc.getEntityType()) {
|
||||
desc.setBaseTable(null, null, null);
|
||||
}
|
||||
@@ -1273,15 +1212,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// mark transient properties
|
||||
transientProperties.process(desc);
|
||||
setScalarType(desc);
|
||||
|
||||
if (!desc.isEmbedded()) {
|
||||
// Set IdGenerator or use DB Identity
|
||||
setIdGeneration(desc);
|
||||
|
||||
// find the appropriate default concurrency mode
|
||||
setConcurrencyMode(desc);
|
||||
}
|
||||
|
||||
// generate the byte code
|
||||
createByteCode(desc);
|
||||
}
|
||||
@@ -1290,7 +1226,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Set the Identity generation mechanism.
|
||||
*/
|
||||
private <T> void setIdGeneration(DeployBeanDescriptor<T> desc) {
|
||||
|
||||
if (desc.getIdGenerator() != null) {
|
||||
// already assigned (So custom or UUID)
|
||||
return;
|
||||
@@ -1298,7 +1233,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
if (desc.idProperty() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final DeployIdentityMode identityMode = desc.getIdentityMode();
|
||||
if (identityMode.isSequence() && !dbIdentity.isSupportsSequence()) {
|
||||
// explicit sequence but not supported by the DatabasePlatform
|
||||
@@ -1355,11 +1289,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private void createByteCode(DeployBeanDescriptor<?> deploy) {
|
||||
|
||||
// check to see if the bean supports EntityBean interface
|
||||
// generate a subclass if required
|
||||
setEntityBeanClass(deploy);
|
||||
|
||||
// use Code generation or Standard reflection to support
|
||||
// getter and setter methods
|
||||
setBeanReflect(deploy);
|
||||
@@ -1373,10 +1305,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* <p>
|
||||
* Enums are treated a bit differently in that they always have a ScalarType
|
||||
* as one is built for them.
|
||||
* </p>
|
||||
*/
|
||||
private void setScalarType(DeployBeanDescriptor<?> deployDesc) {
|
||||
|
||||
for (DeployBeanProperty prop : deployDesc.propertiesAll()) {
|
||||
if (!(prop instanceof DeployBeanPropertyAssoc<?>)) {
|
||||
deployUtil.setScalarType(prop);
|
||||
@@ -1390,17 +1320,13 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* This sets the implementation of constructing entity beans and the setting
|
||||
* and getting of properties. It is generally faster to use code generation
|
||||
* rather than reflection to do this.
|
||||
* </p>
|
||||
*/
|
||||
private void setBeanReflect(DeployBeanDescriptor<?> desc) {
|
||||
|
||||
// Set the BeanReflectGetter and BeanReflectSetter that typically
|
||||
// use generated code. NB: Due to Bug 166 so now doing this for
|
||||
// abstract classes as well.
|
||||
|
||||
BeanPropertiesReader reflectProps = new BeanPropertiesReader(desc.getBeanType());
|
||||
desc.setProperties(reflectProps.getProperties());
|
||||
|
||||
for (DeployBeanProperty prop : desc.propertiesAll()) {
|
||||
String propName = prop.getName();
|
||||
Integer pos = reflectProps.getPropertyIndex(propName);
|
||||
@@ -1410,7 +1336,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
"If you are running in an IDE with enhancement plugin try a Build -> Rebuild Project to recompile and enhance all entity beans. " +
|
||||
"Error - property " + propName + " not found in " + reflectProps + " for type " + desc.getBeanType());
|
||||
}
|
||||
|
||||
} else {
|
||||
final int propertyIndex = pos;
|
||||
prop.setPropertyIndex(propertyIndex);
|
||||
@@ -1427,7 +1352,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Return true if this is a persistent field (not transient or static).
|
||||
*/
|
||||
private boolean isPersistentField(DeployBeanProperty prop) {
|
||||
|
||||
Field field = prop.getField();
|
||||
if (field == null) {
|
||||
return false;
|
||||
@@ -1442,12 +1366,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* which contain version properties.
|
||||
*/
|
||||
private void setConcurrencyMode(DeployBeanDescriptor<?> desc) {
|
||||
|
||||
if (desc.getConcurrencyMode() != null) {
|
||||
// concurrency mode explicitly set during deployment
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkForVersionProperties(desc)) {
|
||||
desc.setConcurrencyMode(ConcurrencyMode.VERSION);
|
||||
} else {
|
||||
@@ -1459,23 +1381,16 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Search for version properties also including embedded beans.
|
||||
*/
|
||||
private boolean checkForVersionProperties(DeployBeanDescriptor<?> desc) {
|
||||
|
||||
boolean hasVersionProperty = false;
|
||||
|
||||
List<DeployBeanProperty> props = desc.propertiesBase();
|
||||
for (DeployBeanProperty prop : props) {
|
||||
for (DeployBeanProperty prop : desc.propertiesBase()) {
|
||||
if (prop.isVersionColumn()) {
|
||||
hasVersionProperty = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return hasVersionProperty;
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasEntityBeanInterface(Class<?> beanClass) {
|
||||
|
||||
Class<?>[] interfaces = beanClass.getInterfaces();
|
||||
for (Class<?> anInterface : interfaces) {
|
||||
for (Class<?> anInterface : beanClass.getInterfaces()) {
|
||||
if (anInterface.equals(EntityBean.class)) {
|
||||
return true;
|
||||
}
|
||||
@@ -1487,18 +1402,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Test the bean type to see if it implements EntityBean interface already.
|
||||
*/
|
||||
private void setEntityBeanClass(DeployBeanDescriptor<?> desc) {
|
||||
|
||||
Class<?> beanClass = desc.getBeanType();
|
||||
|
||||
if (!hasEntityBeanInterface(beanClass)) {
|
||||
String msg = "Bean " + beanClass + " is not enhanced? Check packages specified in ebean.mf. If you are running in IDEA or " +
|
||||
"Eclipse check that the enhancement plugin is installed. See https://ebean.io/docs/trouble-shooting#not-enhanced";
|
||||
throw new BeanNotEnhancedException(msg);
|
||||
}
|
||||
|
||||
// the bean already implements EntityBean
|
||||
checkInheritedClasses(beanClass);
|
||||
|
||||
entityBeanCount++;
|
||||
}
|
||||
|
||||
@@ -1507,16 +1418,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* enhanced or all dynamically subclassed).
|
||||
*/
|
||||
private void checkInheritedClasses(Class<?> beanClass) {
|
||||
|
||||
Class<?> superclass = beanClass.getSuperclass();
|
||||
if (Object.class.equals(superclass)) {
|
||||
if (Object.class.equals(superclass) || Model.class.equals(superclass) || JAVA_LANG_RECORD.equals(superclass.getName())) {
|
||||
// we got to the top of the inheritance
|
||||
return;
|
||||
}
|
||||
if (Model.class.equals(superclass)) {
|
||||
// top of the inheritance. Not enhancing Model at this stage
|
||||
return;
|
||||
}
|
||||
if (!EntityBean.class.isAssignableFrom(superclass)) {
|
||||
if (isMappedSuperWithNoProperties(superclass)) {
|
||||
// ok to stop and treat just the same as Object.class
|
||||
@@ -1566,10 +1472,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
|
||||
private void addPrimaryKeyJoin(DeployBeanPropertyAssocOne<?> prop) {
|
||||
|
||||
String baseTable = prop.getDesc().getBaseTable();
|
||||
DeployTableJoin inverse = prop.getTableJoin().createInverse(baseTable);
|
||||
|
||||
TableJoin inverseJoin = new TableJoin(inverse, prop.getForeignKey());
|
||||
DeployBeanInfo<?> target = deployInfoMap.get(prop.getTargetType());
|
||||
target.setPrimaryKeyJoin(inverseJoin);
|
||||
@@ -1586,7 +1490,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Create a BeanDescriptor for an ElementCollection target.
|
||||
*/
|
||||
public <A> BeanDescriptor<A> createElementDescriptor(DeployBeanDescriptor<A> elementDescriptor, ManyType manyType, boolean scalar) {
|
||||
|
||||
ElementHelp elementHelp = elementHelper(manyType);
|
||||
if (manyType.isMap()) {
|
||||
if (scalar) {
|
||||
@@ -1619,6 +1522,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
for (BeanDescriptor<?> desc : immutableDescriptorList) {
|
||||
desc.visitMetrics(visitor);
|
||||
}
|
||||
for (BeanDescriptor<?> desc : elementDescriptors) {
|
||||
desc.visitMetrics(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
public List<MetaQueryPlan> queryPlanInit(QueryPlanInit request) {
|
||||
|
||||
@@ -324,9 +324,9 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
this.generatedProperty = deploy.getGeneratedProperty();
|
||||
this.getter = deploy.getGetter();
|
||||
this.setter = deploy.getSetter();
|
||||
this.aggregation = deploy.parseAggregation();
|
||||
this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null);
|
||||
this.dbComment = deploy.getDbComment();
|
||||
this.aggregation = deploy.parseAggregation();
|
||||
this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin());
|
||||
this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect());
|
||||
this.formula = sqlFormulaSelect != null;
|
||||
|
||||
@@ -262,10 +262,16 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
/**
|
||||
* Return true if the target side has soft delete.
|
||||
*/
|
||||
@Override
|
||||
public boolean isTargetSoftDelete() {
|
||||
return targetDescriptor.isSoftDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSoftDeletePredicate(String tableAlias) {
|
||||
return targetDescriptor.getSoftDeletePredicate(tableAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if REFRESH should cascade.
|
||||
*/
|
||||
@@ -603,7 +609,8 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
|
||||
String msg = "Error with the Join on [" + getFullBeanName()
|
||||
+ "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?"
|
||||
+ " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?";
|
||||
+ " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped? "
|
||||
+ " or a @JoinColumn needs an explicit referencedColumnName specified?";
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,6 +569,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
return elementCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the element bean descriptor (for an element collection only).
|
||||
*/
|
||||
public BeanDescriptor<T> getElementDescriptor() {
|
||||
return elementDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* ManyToMany only, join from local table to intersection table.
|
||||
*/
|
||||
|
||||
+16
-2
@@ -16,6 +16,7 @@ import io.ebean.event.BeanPostLoad;
|
||||
import io.ebean.event.BeanQueryAdapter;
|
||||
import io.ebean.event.changelog.ChangeLogFilter;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
import io.ebeaninternal.server.core.CacheOptions;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
@@ -1149,8 +1150,21 @@ public class DeployBeanDescriptor<T> {
|
||||
return null;
|
||||
}
|
||||
// use 'current' table alias - refer BeanProperty appendSelect() for aggregation
|
||||
DeployBeanProperty property = propMap.get(expression);
|
||||
return (property == null) ? null : "${ta}." + property.getDbColumn();
|
||||
String[] split = SplitName.split(expression);
|
||||
if (split[0] == null) {
|
||||
DeployBeanProperty property = propMap.get(expression);
|
||||
return (property == null) ? null : "${ta}." + property.getDbColumn();
|
||||
} else {
|
||||
DeployBeanProperty property = propMap.get(split[0]);
|
||||
if (property instanceof DeployBeanPropertyAssoc) {
|
||||
DeployBeanPropertyAssoc<?> prop = (DeployBeanPropertyAssoc<?>) property;
|
||||
DeployBeanProperty beanProperty = prop.getTargetDeploy().getBeanProperty(split[1]);
|
||||
if (beanProperty != null) {
|
||||
return "u1." + beanProperty.getDbColumn();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-12
@@ -34,7 +34,6 @@ import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Type;
|
||||
@@ -683,7 +682,6 @@ public class DeployBeanProperty {
|
||||
*/
|
||||
public void setAggregationPrefix(String prefix) {
|
||||
this.aggregationPrefix = prefix;
|
||||
this.aggregation = (prefix == null) ? aggregation : aggregation.replace(aggregationPrefix, "u1");
|
||||
}
|
||||
|
||||
public String getElPrefix() {
|
||||
@@ -716,7 +714,7 @@ public class DeployBeanProperty {
|
||||
return sqlFormulaSelect;
|
||||
}
|
||||
if (aggregation != null) {
|
||||
return aggregation;
|
||||
return aggregationParsed == null ? dbColumn : aggregationParsed;
|
||||
}
|
||||
return dbColumn;
|
||||
}
|
||||
@@ -1140,15 +1138,6 @@ public class DeployBeanProperty {
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Size> getMetaAnnotationSize() {
|
||||
final List<Size> size = getMetaAnnotations(Size.class);
|
||||
final List<Size.List> lists = getMetaAnnotations(Size.List.class);
|
||||
for (Size.List list : lists) {
|
||||
Collections.addAll(size, list.value());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public Formula getMetaAnnotationFormula(Platform platform) {
|
||||
Formula fallback = null;
|
||||
for (Annotation ann : metaAnnotations) {
|
||||
|
||||
+5
@@ -176,4 +176,9 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
public void setFetchPreference(int fetchPreference) {
|
||||
this.fetchPreference = fetchPreference;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setTargetType(Class<?> targetType) {
|
||||
this.targetType = (Class<T>)targetType;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user