Compare commits

..
Author SHA1 Message Date
rob bygrave 7ec5fe6a85 [maven-release-plugin] prepare release ebean-parent-12.6.6 2021-01-24 17:42:38 +13:00
rob bygrave 9ea5505214 Bump to ebean agent 12.6.6 2021-01-24 17:40:13 +13:00
Robin Bygrave 75d3b566a7 Merge branch 'FOCONIS-prePersist-postPersist-fix-batch-flush-12' 2021-01-22 14:01:19 +13:00
Robin Bygrave e38059f52c #2149 - Fix for ADD: jdbc batch flush doesnt execute sql statements created in lifecycle methods (e.g. beans saved in PostInsert etc)
Note that the foconis fork has a onPersist extension which is removed from the test.
2021-01-22 14:00:48 +13:00
Robin Bygrave 72331aa945 Merge branch 'prePersist-postPersist-fix-batch-flush-12' of https://github.com/FOCONIS/ebean into FOCONIS-prePersist-postPersist-fix-batch-flush-12 2021-01-22 13:14:05 +13:00
Robin Bygrave e497c54f82 No effective change - modify test redis ClusterTest to have 20ms wait 2021-01-21 17:01:36 +13:00
Robin Bygrave b44091ff3a #2147 Fix for ADD: multiple to many outer joins cause wrong count in distinct count…
Copy of Rolands fix in FOCONIS branch. This also brings over some of the extra tests found there.

Note that the SQL is slightly different from the FOCONIS branch in that there is additional foreign key columns included in the sub-query select clause.
2021-01-21 16:56:42 +13:00
Robin Bygrave 2ab889f889 Merge branch 'FOCONIS-count-distinct-with-two-joins-12' 2021-01-21 14:50:32 +13:00
Robin Bygrave 23541e661e Merge branch 'count-distinct-with-two-joins-12' of https://github.com/FOCONIS/ebean into FOCONIS-count-distinct-with-two-joins-12 2021-01-21 14:45:56 +13:00
trojo 6778e37652 ADD: batch flash doesnt execute sql statements created in lifecycle methods 2021-01-20 17:43:14 +01:00
rob bygrave 5eac31283d #2148 - PersistenceException: No ScalarType registered for class java.util.LinkedHashMap 2021-01-20 23:31:45 +13:00
trojo 38016773d4 added comments 2021-01-20 10:47:53 +01:00
trojo ad80eb267f ADD: multiple to many outer joins cause wrong count in distinct count queries 2021-01-20 09:57:25 +01:00
rob bygrave 449e0832e0 Merge branch 'FOCONIS-cache-with-lazyload-wrong-12' 2021-01-20 21:37:57 +13:00
rob bygrave f28f5c9a01 #2146 - no change - Tidy test only TestWithCacheAndLazyLoad 2021-01-20 21:37:29 +13:00
rob bygrave 9dd28d8879 #2146 - Fix for Cache with lazy load doesnt work
When the list is empty() it has already been loaded and loading should skip the l2 cache
2021-01-20 21:10:49 +13:00
rob bygrave b47eec99a5 Merge branch 'cache-with-lazyload-wrong-12' of https://github.com/FOCONIS/ebean into FOCONIS-cache-with-lazyload-wrong-12 2021-01-20 20:36:18 +13:00
Robin Bygrave 99bb55efa0 No change - update ebean-test test configuration only for windows 2021-01-20 17:22:00 +13:00
Robin Bygrave 4408eb618a No change - update test only - TestHistoryOneToMany with wait for when run on windows 2021-01-20 17:11:07 +13:00
rob bygrave 9ccd1b1b27 No effective change - tidy whitespace in SqlTreeBuilder and SqlTreeNodeExtraJoin 2021-01-20 00:10:09 +13:00
rob bygrave 0c1657fef5 #2145 - SoftDelete predicate missing on join to ManyToOne supporting predicate expression 2021-01-20 00:00:27 +13:00
Noemi Szemenyei ee393c6485 ADD: testcase for wrong lazyload with cache 2021-01-19 11:41:19 +01:00
rob bygrave 7f68b9d846 No effective change - tidy whitespace in BeanDescriptorCacheHelp 2021-01-19 22:48:29 +13:00
rob bygrave 2035a3344e Merge branch 'FOCONIS-persistcontext-wrong' 2021-01-19 22:37:45 +13:00
rob bygrave ade36582a0 #2144 - Fix for NPE via Wrong beans in PersistContext avoid proper lazy load 2021-01-19 22:37:15 +13:00
Roland Praml a4b8be61e3 ADD: some suggestions/debug hints 2021-01-11 11:50:25 +01:00
Roland Praml b7feb62ecd ADD: testcase for broken lazyload 2021-01-11 11:50:05 +01:00
rob bygrave 7e48f7c8eb [maven-release-plugin] prepare for next development iteration 2021-01-05 18:02:21 +13:00
rob bygrave 501f8111fe [maven-release-plugin] prepare release ebean-parent-12.6.5 2021-01-05 18:02:09 +13:00
rob bygrave d8f4503d98 #2141 - Fix for failing test, revert of fix for #2127 2021-01-05 17:56:55 +13:00
Rob BygraveandGitHub 0d83978c58 Merge pull request #2141 from tobias-/failing_cascade_test_case_2
Failing testcase that shows wipe of all old children on save
2021-01-05 17:30:56 +13:00
Tobias 69e56be459 Failing testcase that shows wipe of all old children on save
When adding a child to a newly saved bean (A), all other children are
killed on save of A.
2020-12-28 15:35:15 +01:00
rob bygrave 35a721039d [maven-release-plugin] prepare for next development iteration 2020-12-23 20:25:47 +13:00
rob bygrave 98b4ac5029 [maven-release-plugin] prepare release ebean-parent-12.6.4 2020-12-23 20:25:22 +13:00
rob bygrave fe874c946d #2140 - Bump ebean-migration dependency of ebean-ddl-generator to 12.4.0 (update ebean-test) 2020-12-23 20:22:50 +13:00
rob bygrave 53386e9bf4 Update ebean-core-type name and description only 2020-12-22 16:12:20 +13:00
rob bygrave 5435055f5c Add ebean-redis, ebean-postgis and ebean-core-type to ebean-bom 2020-12-22 16:08:59 +13:00
rob bygrave 583e10f5c6 #2139 - Fix for - Failing testcase that shows wipe of all children on save (related to #2127) 2020-12-22 15:49:37 +13:00
Rob BygraveandGitHub 5393d709a3 Merge pull request #2139 from tobias-/failing_cascade_test_case
Failing testcase that shows wipe of all children on save
2020-12-22 14:55:12 +13:00
Tobias fc1c9392aa Failing testcase that shows wipe of all children on save
When adding a newly saved bean (A) to another bean (B), all children
in A are wiped on saving B.
2020-12-21 15:32:40 +01:00
rob bygrave 6b2c48d4f6 #2135 - ebean doest not support Spring @Transactional(propagation = Propagation.REQUIRES_NEW) 2020-12-21 16:40:19 +13:00
rob bygrave 400ae3cf4b #2138 - Mark Transaction.flushBatch() as deprecated - migrate to flush(); 2020-12-21 14:58:04 +13:00
rob bygrave f22a322104 No effective change - Refactor tidy AbstractSqlQueryRequest after #2137
- Rename trans to transaction
- Tidy whitespace
2020-12-21 14:18:13 +13:00
rob bygrave e98ff952bf #2137 - Expected Transaction flush on SqlQuery and DtoQuery not occurring 2020-12-21 14:11:28 +13:00
rob bygrave 573e1b553c [maven-release-plugin] prepare for next development iteration 2020-12-18 15:04:05 +13:00
rob bygrave a674d0d696 [maven-release-plugin] prepare release ebean-parent-12.6.3 2020-12-18 15:03:53 +13:00
Rob BygraveandGitHub 5393161c16 Merge pull request #2132 from ebean-orm/feature/redisModule
Add ebean-redis as module
2020-12-18 14:58:07 +13:00
rob bygrave 1bdc6d8f7f #2134 - Modify ChangeLog to send the last set of changes Pre Commit (rather than post commit). Expose this for spring transactions. 2020-12-18 14:54:44 +13:00
rob bygrave e9081a176f #2133 - Spring transactions not getting ChangeLog changeSet as part of postCommit() processing 2020-12-18 14:14:43 +13:00
rob bygrave 3e7db2887f Add ebean-redis as module 2020-12-17 14:38:54 +13:00
rob bygrave fcf14ac222 Add ebean-redis as module 2020-12-17 11:56:34 +13:00
Rob BygraveandGitHub 55324653a5 #2127 - Adding a new list of children to @OneToMany with orphanRemoval true does not delete existing orphans (#2131) 2020-12-16 15:36:44 +13:00
SnōwballandGitHub a132f09b03 Ignore javax.persistence.Transient in query beans (#2129)
* Add failing tests for transient types

* Ignore jpa @Transient fields in query beans

Any field marked with jpa @Transient should be ignored
2020-12-15 12:40:06 +13:00
rob bygrave 48eac84930 #2128 - Bump avaje-config dependency to 1.3 - Fix for - Repeated calls to Config.get() with no value, expect to return passed default value 2020-12-14 09:31:08 +13:00
rob bygrave 948619a125 [maven-release-plugin] prepare for next development iteration 2020-12-09 13:11:19 +13:00
rob bygrave 0cd0741f18 [maven-release-plugin] prepare release ebean-parent-12.6.2 2020-12-09 13:11:07 +13:00
rob bygrave 01a3455ecb Bump test dependency for kotlin-querybean-generator release 2020-12-08 20:51:52 +13:00
rob bygrave 610f7db1cb Bump test dependency kotlin version to 1.4.21 after issues downloading 1.4.10 from maven central? 2020-12-08 20:46:36 +13:00
rob bygrave 373ef6d1eb #2126 - Bump ebean-migration to 12.4.0 2020-12-08 20:29:34 +13:00
rob bygrave f1b1cee2fd Bump enhancement tile 2020-12-08 15:43:31 +13:00
rob bygrave a73e4f23ff Bump agent version in BOM 2020-12-08 15:41:59 +13:00
rob bygrave 9a5d3498c1 #2125 - Bump ebean-test-docker dependency to 4.1 2020-12-07 23:00:02 +13:00
rob bygrave 0b24d635ad Tests only - update ddl review 2020-12-07 22:48:32 +13:00
rob bygrave b623e19dae update git ignore 2020-12-07 22:47:43 +13:00
rob bygrave 7389a8b344 #2124 - ENH: Add name() attribute to @DbArray, @DbJson, @DbJsonB, @DbMap 2020-12-07 22:46:04 +13:00
rob bygrave d68f477e8e #2120 - DbEnumValue without database constraint 2020-12-07 21:53:03 +13:00
rob bygrave dbe273bb56 #2123 - Modify BackgroundExecutor API - add scheduleWithFixedDelay() and mark executePeriodically() as deprecated 2020-12-07 17:14:03 +13:00
rob bygrave 1a4377da29 Improve tests TestQueryForUpdate and TestSoftDeleteBasic 2020-12-07 13:29:47 +13:00
rob bygrave c3cdecaa60 Fix test TestSqlUpdateBatch with correct bind type for Postgres 2020-12-07 10:06:15 +13:00
rob bygrave 9d706056c6 No functional change, replace new ReentrantLock(false) with new ReentrantLock() 2020-12-04 21:18:55 +13:00
rob bygrave cb0100f02a Refactor tidy BeanDescriptor, no effective change 2020-12-04 21:11:02 +13:00
rob bygrave 9c6dd0989a Refactor tidy BeanDescriptorManager whitespace and raw types 2020-12-04 21:03:08 +13:00
rob bygrave a23a179bbf Fix test bump kotlin-querybean-generator 12.6.2-SNAPSHOT 2020-12-04 20:45:38 +13:00
rob bygrave 365b7c4600 #2122 - Metrics for ElementCollection query not included in reporting (via MetaInfoManager) 2020-12-04 20:41:40 +13:00
rob bygrave ed2455f460 #2121 - Refactor internals move DefaultBackgroundExecutor etc 2020-12-03 15:47:14 +13:00
rob bygrave 046033627a Update tests - generated sql for add constraint foreign key 2020-12-03 13:22:13 +13:00
Rob BygraveandGitHub 786444a6b9 Refactor BackgroundExecutor add submit() methods returning Future (#2121)
- Adds submit() methods that return Future
- Refactor internals to use DaemonScheduleThreadPool
- Delete the now unused DaemonExecutorService
- Tidy internals using wrapMDC() methods
2020-12-03 13:16:51 +13:00
rob bygrave 9ab26cfb9d #2118 - Fix test only PlatformDdl_AlterColumnTest 2020-12-03 09:48:41 +13:00
rob bygrave aa1e3ea1bc #2118 - Generated code for @DbForeignKey reading onDelete and Ignores onUpdate 2020-12-01 22:04:07 +13:00
Rob BygraveandGitHub c66875449b Add Query.withLock(LockType) and withLock(LockType, LockWait) (#2116)
Add Query.withLock(LockType) and withLock(LockType, LockWait)
2020-12-01 21:31:53 +13:00
rob bygrave f6c4024ae5 No effective change - tidy test only, TestStatelessUpdate 2020-12-01 21:17:19 +13:00
rob bygrave 2c8cc793ab No effective change - tidy test only, TestStatelessUpdate 2020-12-01 14:29:37 +13:00
rob bygrave 66454d2e61 No effective change - bump test dependency for ebean-ddl-generator 2020-11-26 08:57:19 +13:00
rob bygrave e7b13cf2d9 [maven-release-plugin] prepare for next development iteration 2020-11-25 22:48:17 +13:00
205 changed files with 46753 additions and 1255 deletions
+1 -4
View File
@@ -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
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.6.1</version>
<version>12.6.6</version>
</parent>
<name>ebean api</name>
@@ -32,7 +32,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-config</artifactId>
<version>1.2</version>
<version>1.3</version>
</dependency>
<!--
@@ -55,7 +55,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>6.13</version>
<version>6.15</version>
</dependency>
<dependency>
@@ -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);
}
@@ -31,7 +31,7 @@ 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;
static {
@@ -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;
@@ -169,14 +169,38 @@ 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();
/**
* Deprecated - migrate to withLock().
* Execute using "for update" with given lock type (currently Postgres only).
*/
@Deprecated
Query<T> forUpdate(Query.LockType lockType);
/**
@@ -188,8 +212,10 @@ public interface ExpressionList<T> {
Query<T> forUpdateNoWait();
/**
* Deprecated - migrate to withLock().
* Execute using "for update nowait" with given lock type (currently Postgres only).
*/
@Deprecated
Query<T> forUpdateNoWait(Query.LockType lockType);
/**
@@ -201,8 +227,10 @@ public interface ExpressionList<T> {
Query<T> forUpdateSkipLocked();
/**
* Deprecated - migrate to withLock().
* Execute using "for update skip locked" with given lock type (currently Postgres only).
*/
@Deprecated
Query<T> forUpdateSkipLocked(Query.LockType lockType);
/**
+36 -6
View File
@@ -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
}
@@ -1643,40 +1644,69 @@ public interface Query<T> {
*/
String getGeneratedSql();
/**
* 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(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(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" with given lock type (currently Postgres only).
*/
@Deprecated
Query<T> forUpdate(LockType lockType);
/**
* 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).
*/
@Deprecated
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).
*/
@Deprecated
Query<T> forUpdateSkipLocked(LockType lockType);
/**
@@ -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;
/**
@@ -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;
@@ -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;
@@ -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.
@@ -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.
@@ -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<>();
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.6.1</version>
<version>12.6.6</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.6.6</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.6.6</version>
<scope>provided</scope>
</dependency>
@@ -64,7 +64,7 @@
<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>
@@ -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);
}
}
}
@@ -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;
@@ -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;
@@ -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;
+34 -16
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.6.1</version>
<version>12.6.6</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.6.6</ebean-agent.version>
<ebean-maven-plugin.version>12.6.6</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.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-xml</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-autotune</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>12.6.6</version>
</dependency>
</dependencies>
</dependencyManagement>
+4 -2
View File
@@ -4,10 +4,12 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.6.1</version>
<version>12.6.6</version>
</parent>
<artifactId>ebean-core-type</artifactId>
<name>ebean core type</name>
<description>ebean scalar types api</description>
<properties>
<jackson-core.version>2.11.3</jackson-core.version>
@@ -19,7 +21,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
+9 -9
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.6.1</version>
<version>12.6.6</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -15,7 +15,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.6.1</tag>
<tag>ebean-parent-12.6.6</tag>
</scm>
<properties>
@@ -70,14 +70,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 +92,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.6.1</version>
<version>12.6.6</version>
</dependency>
<dependency>
@@ -202,7 +202,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 +301,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.5.0</version>
<version>12.6.6</version>
<executions>
<execution>
<id>test</id>
@@ -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);
}
}
@@ -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<>();
@@ -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<>();
@@ -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);
@@ -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) {
@@ -161,7 +161,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;
@@ -451,16 +451,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();
@@ -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,17 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
}
public void findEach(Consumer<T> consumer) {
flushJdbcBatchOnQuery();
queryEngine.findEach(this, 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.
@@ -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++));
@@ -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);
}
@@ -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()) {
@@ -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;
@@ -117,6 +117,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 +203,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 +238,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 +361,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
private void readEntityMapping(ClassLoader classLoader, XmapEntity entityDeploy) {
String entityClassName = entityDeploy.getClazz();
Class<?> entityClass;
try {
@@ -411,7 +410,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 +445,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 +457,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 +482,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
private void readForeignKeys() {
for (BeanDescriptor<?> d : descMap.values()) {
d.initialiseFkeys();
}
@@ -500,18 +492,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 +557,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 +590,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
@SuppressWarnings("unchecked")
public <T> BeanManager<T> getBeanManager(Class<T> entityType) {
return (BeanManager<T>) getBeanManager(entityType.getName());
}
@@ -615,14 +601,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 +623,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 +640,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 +674,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 +690,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 +715,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 +730,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 +744,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 +755,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
}
}
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
if (!manyProp.isTransient()) {
DeployBeanInfo<?> assoc = deployInfoMap.get(manyProp.getTargetType());
@@ -795,7 +766,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 +789,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 +813,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 +827,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 +882,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return true;
}
}
}
}
// multiple options so should specify mappedBy property
@@ -930,12 +893,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 +903,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 +910,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
childDescriptor.setOrderColumn(orderProperty);
}
}
targetDesc.setOrderColumn(orderProperty);
}
@@ -960,19 +918,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 +973,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 +1028,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 +1110,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 +1170,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 +1211,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 +1225,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 +1232,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 +1288,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 +1304,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 +1319,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 +1335,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 +1351,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 +1365,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 +1380,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 +1401,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,7 +1417,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* enhanced or all dynamically subclassed).
*/
private void checkInheritedClasses(Class<?> beanClass) {
Class<?> superclass = beanClass.getSuperclass();
if (Object.class.equals(superclass)) {
// we got to the top of the inheritance
@@ -1566,10 +1475,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 +1493,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 +1525,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) {
@@ -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.
*/
@@ -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.
*/
@@ -311,19 +311,23 @@ public class AnnotationFields extends AnnotationParser {
DbMap dbMap = get(prop, DbMap.class);
if (dbMap != null) {
util.setDbMap(prop, dbMap);
setColumnName(prop, dbMap.name());
}
DbJson dbJson = get(prop, DbJson.class);
if (dbJson != null) {
util.setDbJsonType(prop, dbJson);
setColumnName(prop, dbJson.name());
} else {
DbJsonB dbJsonB = get(prop, DbJsonB.class);
if (dbJsonB != null) {
util.setDbJsonBType(prop, dbJsonB);
setColumnName(prop, dbJsonB.name());
}
}
DbArray dbArray = get(prop, DbArray.class);
if (dbArray != null) {
util.setDbArray(prop, dbArray);
setColumnName(prop, dbArray.name());
}
}
@@ -99,11 +99,7 @@ public abstract class AnnotationParser extends AnnotationBase {
}
void readColumn(Column columnAnn, DeployBeanProperty prop) {
if (!isEmpty(columnAnn.name())) {
prop.setDbColumn(databasePlatform.convertQuotedIdentifiers(columnAnn.name()));
}
setColumnName(prop, columnAnn.name());
prop.setDbInsertable(columnAnn.insertable());
prop.setDbUpdateable(columnAnn.updatable());
prop.setNullable(columnAnn.nullable());
@@ -125,6 +121,12 @@ public abstract class AnnotationParser extends AnnotationBase {
}
}
protected void setColumnName(DeployBeanProperty prop, String name) {
if (!isEmpty(name)) {
prop.setDbColumn(databasePlatform.convertQuotedIdentifiers(name));
}
}
/**
* Return true if the validation groups are {@link Default} (respectively empty)
* can be applied to DDL generation.
@@ -1,4 +1,4 @@
package io.ebeaninternal.server.lib;
package io.ebeaninternal.server.executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -9,15 +9,12 @@ import java.util.concurrent.locks.ReentrantLock;
/**
* Daemon based ScheduleThreadPool.
* <p>
* Uses Daemon threads and hooks into shutdown event.
* </p>
*/
public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor {
private static final Logger logger = LoggerFactory.getLogger(DaemonScheduleThreadPool.class);
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
private final String namePrefix;
@@ -35,26 +32,25 @@ public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor
/**
* Shutdown this thread pool nicely if possible.
* <p>
* This will wait a maximum of 20 seconds before terminating any threads still
* working.
* </p>
* This will wait a maximum of shutdownWaitSeconds seconds before
* terminating any threads still working.
*/
@Override
public void shutdown() {
lock.lock();
try {
if (super.isShutdown()) {
logger.debug("DaemonScheduleThreadPool {} already shut down", namePrefix);
logger.debug("Already shutdown {}", namePrefix);
return;
}
try {
logger.debug("DaemonScheduleThreadPool {} shutting down...", namePrefix);
logger.trace("Shutting down {} ...", namePrefix);
super.shutdown();
if (!super.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) {
logger.info("DaemonScheduleThreadPool shut down timeout exceeded. Terminating running threads.");
logger.info("Shutdown wait timeout exceeded. Terminating running threads for {}", namePrefix);
super.shutdownNow();
}
logger.debug("Shutdown complete for {}", namePrefix);
} catch (Exception e) {
logger.error("Error during shutdown of " + namePrefix, e);
e.printStackTrace();
@@ -1,5 +1,4 @@
package io.ebeaninternal.server.lib;
package io.ebeaninternal.server.executor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
@@ -9,42 +8,28 @@ import java.util.concurrent.atomic.AtomicInteger;
* <p>
* Daemon threads do not stop a JVM stopping. If an application only has Daemon
* threads left it will shutdown.
* </p>
* <p>
* In using Daemon threads you need to either not care about being interrupted
* on shutdown or register with the JVM shutdown hook to perform a nice shutdown
* of the daemon threads etc.
* </p>
*
* @author rbygrave
*/
public class DaemonThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public DaemonThreadFactory(String namePrefix) {
SecurityManager s = System.getSecurityManager();
this.group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
this.namePrefix = namePrefix != null ? namePrefix : "pool-" + poolNumber.getAndIncrement() + "-thread-";
this.namePrefix = namePrefix;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
Thread t = new Thread(null, r, namePrefix + threadNumber.getAndIncrement(), 0);
t.setDaemon(true);
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
@@ -0,0 +1,118 @@
package io.ebeaninternal.server.executor;
import io.ebeaninternal.api.SpiBackgroundExecutor;
import org.slf4j.MDC;
import java.util.Map;
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;
/**
* The default implementation of the BackgroundExecutor.
*/
public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
private final ScheduledExecutorService executor;
/**
* Construct the default implementation of BackgroundExecutor.
*/
public DefaultBackgroundExecutor(int schedulePoolSize, int shutdownWaitSeconds, String namePrefix) {
this.executor = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix);
}
/**
* Wrap the task with MDC context if defined.
*/
<T> Callable<T> wrapMDC(Callable<T> task) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
return task;
} else {
return () -> {
MDC.setContextMap(map);
try {
return task.call();
} finally {
MDC.clear();
}
};
}
}
/**
* Wrap the task with MDC context if defined.
*/
Runnable wrapMDC(Runnable task) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
return task;
} else {
return () -> {
MDC.setContextMap(map);
try {
task.run();
} finally {
MDC.clear();
}
};
}
}
@Override
public <T> Future<T> submit(Callable<T> task) {
return executor.submit(wrapMDC(task));
}
/**
* Execute a Runnable using a background thread.
*/
@Override
public Future<?> submit(Runnable task) {
return executor.submit(wrapMDC(task));
}
@Override
public void execute(Runnable task) {
submit(task);
}
@Override
public void executePeriodically(Runnable task, long delay, TimeUnit unit) {
executor.scheduleWithFixedDelay(wrapMDC(task), delay, delay, unit);
}
@Override
public void executePeriodically(Runnable task, long initialDelay, long delay, TimeUnit unit) {
executor.scheduleWithFixedDelay(wrapMDC(task), initialDelay, delay, unit);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
return executor.scheduleWithFixedDelay(wrapMDC(task), initialDelay, delay, unit);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long delay, TimeUnit unit) {
return executor.scheduleAtFixedRate(wrapMDC(task), initialDelay, delay, unit);
}
@Override
public ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit) {
return executor.schedule(wrapMDC(task), delay, unit);
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
return executor.schedule(wrapMDC(task), delay, unit);
}
@Override
public void shutdown() {
executor.shutdown();
}
}
@@ -486,6 +486,16 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.filterMany(manyProperty).where(expressions, params);
}
@Override
public Query<T> withLock(Query.LockType lockType) {
return query.withLock(lockType);
}
@Override
public Query<T> withLock(Query.LockType lockType, Query.LockWait lockWait) {
return query.withLock(lockType, lockWait);
}
@Override
public Query<T> forUpdate() {
return query.forUpdate();
@@ -491,6 +491,16 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
return exprList.findOneOrEmpty();
}
@Override
public Query<T> withLock(Query.LockType lockType) {
return exprList.withLock(lockType);
}
@Override
public Query<T> withLock(Query.LockType lockType, Query.LockWait lockWait) {
return exprList.withLock(lockType, lockWait);
}
@Override
public Query<T> forUpdate() {
return exprList.forUpdate();
@@ -45,7 +45,7 @@ public class UuidV1RndIdGenerator implements PlatformIdGenerator {
private AtomicLong nanoToMilliOffset = new AtomicLong(currentUuidTime());
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
/**
@@ -1,78 +0,0 @@
package io.ebeaninternal.server.lib;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* A "CachedThreadPool" based on Daemon threads.
* <p>
* The Threads are created as needed and once idle live for 60 seconds.
*/
public final class DaemonExecutorService {
private static final Logger logger = LoggerFactory.getLogger(DaemonExecutorService.class);
private final ReentrantLock lock = new ReentrantLock(false);
private final String namePrefix;
private final int shutdownWaitSeconds;
private final ExecutorService service;
/**
* Construct the DaemonThreadPool.
*
* @param shutdownWaitSeconds the time in seconds allowed for the pool to shutdown nicely. After
* this the pool is forced to shutdown.
*/
public DaemonExecutorService(int shutdownWaitSeconds, String namePrefix) {
this.service = Executors.newCachedThreadPool(new DaemonThreadFactory(namePrefix));
this.shutdownWaitSeconds = shutdownWaitSeconds;
this.namePrefix = namePrefix;
}
/**
* Execute the Runnable.
*/
public void execute(Runnable runnable) {
service.execute(runnable);
}
/**
* Shutdown this thread pool nicely if possible.
* <p>
* This will wait a maximum of 20 seconds before terminating any threads still
* working.
* </p>
*/
public void shutdown() {
lock.lock();
try {
if (service.isShutdown()) {
logger.debug("DaemonExecutorService[{}] already shut down", namePrefix);
return;
}
try {
logger.debug("DaemonExecutorService[{}] shutting down...", namePrefix);
service.shutdown();
if (!service.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) {
logger.info("DaemonExecutorService[{}] shut down timeout exceeded. Terminating running threads.", namePrefix);
service.shutdownNow();
}
} catch (Exception e) {
logger.error("Error during shutdown of DaemonThreadPool[" + namePrefix + "]", e);
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
}
@@ -14,7 +14,7 @@ import java.util.concurrent.locks.ReentrantLock;
*/
abstract class DLoadBaseContext {
protected final ReentrantLock lock = new ReentrantLock(false);
protected final ReentrantLock lock = new ReentrantLock();
protected final DLoadContext parent;
@@ -56,7 +56,6 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
}
private void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
if (cache) {
query.setBeanCacheMode(CacheMode.ON);
}
@@ -70,7 +69,6 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
}
protected void register(EntityBeanIntercept ebi) {
if (currentBuffer.isFull()) {
currentBuffer = createBuffer(secondaryBatchSize);
}
@@ -120,7 +118,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
*/
static class LoadBuffer implements BeanLoader, LoadBeanBuffer {
private final ReentrantLock bufferLock = new ReentrantLock(false);
private final ReentrantLock bufferLock = new ReentrantLock();
private final DLoadBeanContext context;
private final int batchSize;
private final List<EntityBeanIntercept> list;
@@ -198,8 +196,10 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
// lazy load property was a Many
return;
}
if (context.hitCache) {
if (list.isEmpty()) {
// re-add to the batch and lazy load from DB skipping l2 cache
list.add(ebi);
} else if (context.hitCache) {
Set<EntityBeanIntercept> hits = context.desc.cacheBeanLoadAll(list, persistenceContext, ebi.getLazyLoadPropertyIndex(), ebi.getLazyLoadProperty());
list.removeAll(hits);
if (list.isEmpty() || hits.contains(ebi)) {
@@ -130,7 +130,7 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
*/
static class LoadBuffer implements BeanCollectionLoader, LoadManyBuffer {
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
private final PersistenceContext persistenceContext;
private final DLoadManyContext context;
private final int batchSize;
@@ -81,8 +81,7 @@ public final class BatchControl {
*/
private int bufferMax;
private Queue earlyQueue;
private Queue lateQueue;
private Queue[] queues = new Queue[3];
/**
* Create for a given transaction, PersistExecute, default size and getGeneratedKeys.
@@ -271,9 +270,10 @@ public final class BatchControl {
}
private void flushBuffer(boolean reset) throws BatchedSqlException {
flushQueue(queues[0]);
flushInternal(reset);
flushQueue(earlyQueue);
flushQueue(lateQueue);
flushQueue(queues[1]);
flushQueue(queues[2]);
}
private void flushQueue(Queue queue) throws BatchedSqlException {
@@ -298,18 +298,7 @@ public final class BatchControl {
// Nothing in queue to flush
return;
}
// convert entry map to array for sorting
BatchedBeanHolder[] bsArray = getBeanHolderArray();
// sort the entries by depth
Arrays.sort(bsArray, depthComparator);
if (transaction.isLogSummary()) {
transaction.logSummary("BatchControl flush " + Arrays.toString(bsArray));
}
for (BatchedBeanHolder beanHolder : bsArray) {
beanHolder.executeNow();
}
executeAll();
persistedBeans.clear();
if (reset) {
beanHoldMap.clear();
@@ -323,6 +312,32 @@ public final class BatchControl {
}
}
private void executeAll() throws BatchedSqlException {
do {
// convert entry map to array for sorting
BatchedBeanHolder[] bsArray = getBeanHolderArray();
Arrays.sort(bsArray, depthComparator);
if (transaction.isLogSummary()) {
transaction.logSummary("BatchControl flush " + Arrays.toString(bsArray));
}
for (BatchedBeanHolder beanHolder : bsArray) {
beanHolder.executeNow();
}
} while (!isBeanHoldersEmpty());
}
/**
* Return if all bean holders are empty.
*/
private boolean isBeanHoldersEmpty() {
for (BatchedBeanHolder beanHolder : beanHoldMap.values()) {
if (!beanHolder.isEmpty()) {
return false;
}
}
return true;
}
/**
* Return an entry for the given type description. The type description is
* typically the bean class name (or table name for MapBeans).
@@ -368,20 +383,11 @@ public final class BatchControl {
/**
* Add a SqlUpdate request to execute after flush.
*/
public void addToFlushQueue(PersistRequestUpdateSql request, boolean early) {
if (early) {
// add it to the early queue
if (earlyQueue == null) {
earlyQueue = new Queue();
}
earlyQueue.add(request);
} else {
// add it to the late queue
if (lateQueue == null) {
lateQueue = new Queue();
}
lateQueue.add(request);
public void addToFlushQueue(PersistRequestUpdateSql request, int pos) {
if (queues[pos] == null) {
queues[pos] = new Queue();
}
queues[pos].add(request);
}
private static class Queue {
@@ -47,6 +47,11 @@ class BatchedBeanHolder {
*/
private ArrayList<PersistRequest> deletes;
/**
* The holder is empty
*/
private boolean empty = true;
/**
* Create a new entry with a given type and depth.
*/
@@ -63,6 +68,13 @@ class BatchedBeanHolder {
return order;
}
/**
* Returns if the BeanHolder is empty.
*/
public boolean isEmpty() {
return empty;
}
/**
* Execute all the persist requests in this entry.
* <p>
@@ -90,6 +102,7 @@ class BatchedBeanHolder {
updates = new ArrayList<>();
control.executeNow(bufferedUpdates);
}
empty = true;
}
@Override
@@ -112,9 +125,8 @@ class BatchedBeanHolder {
* Add the request to the appropriate persist list.
*/
public int append(PersistRequestBean<?> request) {
empty = false;
request.setBatched();
switch (request.getType()) {
case INSERT:
if (inserts == null) {
@@ -141,24 +141,17 @@ public final class DefaultPersister implements Persister {
@Override
public void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue) {
if (queue) {
addToFlushQueue(update, t, false);
addToFlushQueue(update, t, 2);
} else {
executeSqlUpdate(update, t);
}
}
@Override
public void addToFlushQueue(SpiSqlUpdate update, SpiTransaction t) {
addToFlushQueue(update, t, true);
}
@Override
public void addToFlushQueueLast(SpiSqlUpdate update, SpiTransaction t) {
addToFlushQueue(update, t, false);
}
private void addToFlushQueue(SpiSqlUpdate update, SpiTransaction t, boolean early) {
new PersistRequestUpdateSql(server, update, t, persistExecute).addToFlushQueue(early);
/**
* Add to the flush queue in position 0, 1 or 2.
*/
public void addToFlushQueue(SpiSqlUpdate update, SpiTransaction t, int pos) {
new PersistRequestUpdateSql(server, update, t, persistExecute).addToFlushQueue(pos);
}
/**
@@ -963,7 +956,7 @@ public final class DefaultPersister implements Persister {
void deleteManyIntersection(EntityBean bean, BeanPropertyAssocMany<?> many, SpiTransaction t, boolean publish, boolean queue) {
SpiSqlUpdate sqlDelete = deleteAllIntersection(bean, many, publish);
if (queue) {
addToFlushQueue(sqlDelete, t, true);
addToFlushQueue(sqlDelete, t, 1);
} else {
executeSqlUpdate(sqlDelete, t);
}
@@ -1237,7 +1230,6 @@ public final class DefaultPersister implements Persister {
@SuppressWarnings({"unchecked"})
private <T> PersistRequestBean<T> createRequest(T bean, Transaction t, Object parentBean, BeanManager<?> mgr,
PersistRequest.Type type, int flags) {
// no delete requests come here
return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute, type, flags);
}
@@ -1252,7 +1244,6 @@ public final class DefaultPersister implements Persister {
@SuppressWarnings({"unchecked"})
private <T> PersistRequestBean<T> createDeleteRequest(Object bean, Transaction t, PersistRequest.Type type, int flags) {
BeanManager<T> mgr = getBeanManager(bean);
if (type == Type.DELETE_PERMANENT) {
type = Type.DELETE;
@@ -1281,7 +1272,6 @@ public final class DefaultPersister implements Persister {
*/
@SuppressWarnings("unchecked")
private <T> BeanManager<T> getBeanManager(Object bean) {
BeanManager<T> mgr = (BeanManager<T>) beanDescriptorManager.getBeanManager(bean.getClass());
if (mgr == null) {
throw new PersistenceException(errNotRegistered(bean.getClass()));
@@ -56,7 +56,7 @@ class MergeNodeAssocManyToMany extends MergeNode {
IntersectionTable intersectionTable = many.intersectionTable();
if (!deletions.isEmpty()) {
transaction.flushBatch();
transaction.flush();
SqlUpdate delete = intersectionTable.delete(server, false);
for (EntityBean deletion : deletions) {
@@ -67,7 +67,7 @@ class MergeNodeAssocManyToMany extends MergeNode {
}
if (!additions.isEmpty()) {
transaction.flushBatch();
transaction.flush();
SqlUpdate insert = intersectionTable.insert(server, false);
for (EntityBean addition : additions) {
@@ -49,7 +49,7 @@ abstract class SaveManyBase implements SaveMany {
void preElementCollectionUpdate() {
if (!insertedParent) {
request.preElementCollectionUpdate();
persister.addToFlushQueue(many.deleteByParentId(request.getBeanId(), null), transaction);
persister.addToFlushQueue(many.deleteByParentId(request.getBeanId(), null), transaction, 1);
}
}
@@ -37,7 +37,7 @@ public class SaveManyBeans extends SaveManyBase {
private final boolean saveRecurseSkippable;
private final DeleteMode deleteMode;
private final boolean untouchedBeanCollection;
private Collection<?> collection;
private final Collection<?> collection;
private int sortOrder;
SaveManyBeans(DefaultPersister persister, boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
@@ -49,6 +49,7 @@ public class SaveManyBeans extends SaveManyBase {
this.saveRecurseSkippable = many.isSaveRecurseSkippable();
this.deleteMode = targetDescriptor.isSoftDelete() ? DeleteMode.SOFT : DeleteMode.HARD;
this.untouchedBeanCollection = untouchedBeanCollection();
this.collection = cascade ? BeanCollectionUtil.getActualEntries(value) : null;
}
/**
@@ -108,7 +109,6 @@ public class SaveManyBeans extends SaveManyBase {
private void saveAssocManyDetails() {
// check that the list is not null and if it is a BeanCollection
// check that is has been populated (don't trigger lazy loading)
collection = BeanCollectionUtil.getActualEntries(value);
if (collection != null) {
processDetails();
}
@@ -210,6 +210,18 @@ public class SaveManyBeans extends SaveManyBase {
return true;
}
private boolean hasNewOrDirtyBeans() {
if (collection == null) {
return false;
}
for (Object bean : collection) {
if (bean instanceof EntityBean && ((EntityBean) bean)._ebean_getIntercept().isNewOrDirty()) {
return true;
}
}
return false;
}
/**
* Collect the Id values of the details to remove 'missing children' for stateless updates.
*/
@@ -235,9 +247,6 @@ public class SaveManyBeans extends SaveManyBase {
/**
* Save the additions and removals from a ManyToMany collection as inserts
* and deletes from the intersection table.
* <p>
* This is done via MapBeans.
* </p>
*/
private void saveAssocManyIntersection() {
if (value == null) {
@@ -336,9 +345,14 @@ public class SaveManyBeans extends SaveManyBase {
}
private void removeAssocManyOrphans() {
// check that the list is not null and if it is a BeanCollection
// check that is has been populated (don't trigger lazy loading)
if (value instanceof BeanCollection<?>) {
if (value == null) {
return;
}
if (!(value instanceof BeanCollection<?>)) {
// if (!insertedParent && cascade && hasNewOrDirtyBeans()) {
// persister.addToFlushQueue(many.deleteByParentId(request.getBeanId(), null), transaction, 0);
// }
} else {
BeanCollection<?> c = (BeanCollection<?>) value;
Set<?> modifyRemovals = c.getModifyRemovals();
if (insertedParent) {
@@ -48,7 +48,7 @@ class SaveManyElementCollection extends SaveManyBase {
final SpiSqlUpdate sqlInsert = proto.copy();
sqlInsert.setParameter(parentId);
many.bindElementValue(sqlInsert, value);
persister.addToFlushQueueLast(sqlInsert, transaction);
persister.addToFlushQueue(sqlInsert, transaction, 2);
}
resetModifyState();
postElementCollectionUpdate();
@@ -51,7 +51,7 @@ class SaveManyElementCollectionMap extends SaveManyBase {
sqlInsert.setParameter(parentId);
sqlInsert.setParameter(entry.getKey());
many.bindElementValue(sqlInsert, entry.getValue());
persister.addToFlushQueueLast(sqlInsert, transaction);
persister.addToFlushQueue(sqlInsert, transaction, 2);
}
resetModifyState();
postElementCollectionUpdate();
@@ -59,7 +59,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
private static final CQueryCollectionAddNoop NOOP_ADD = new CQueryCollectionAddNoop();
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
/**
* The resultSet rows read.
@@ -11,7 +11,7 @@ class CQueryBindCapture implements SpiQueryBindCapture {
private static final double multiplier = 1.3d;
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
private final CQueryPlanManager manager;
private final SpiQueryPlan queryPlan;
@@ -571,8 +571,8 @@ class CQueryBuilder {
private final CQueryPredicates predicates;
private final SqlTree select;
private final boolean updateStatement;
private final boolean distinct;
private final boolean countSingleAttribute;
private final String dbOrderBy;
private boolean useSqlLimiter;
private boolean hasWhere;
@@ -590,6 +590,7 @@ class CQueryBuilder {
this.updateStatement = updateStatement;
this.distinct = query.isDistinct() || select.isSqlDistinct();
this.dbOrderBy = predicates.getDbOrderBy();
this.countSingleAttribute = query.isCountDistinct() && query.isSingleAttribute();
}
private void appendSelect() {
@@ -601,8 +602,13 @@ class CQueryBuilder {
if (!useSqlLimiter) {
appendSelectDistinct();
}
if (query.isCountDistinct() && query.isSingleAttribute()) {
sb.append("r1.attribute_, count(*) from (select ").append(select.getSelectSql()).append(" as attribute_");
if (countSingleAttribute) {
sb.append("r1.attribute_, count(*) from (select ");
if (distinct) {
sb.append("distinct t0.");
sb.append(request.getBeanDescriptor().getIdProperty().getDbColumn()).append(", ");
}
sb.append(select.getSelectSql()).append(" as attribute_");
} else {
sb.append(select.getSelectSql());
}
@@ -621,7 +627,7 @@ class CQueryBuilder {
private void appendSelectDistinct() {
sb.append("select ");
if (distinct) {
if (distinct && !countSingleAttribute) {
if (request.isInlineCountDistinct()) {
sb.append("count(");
}
@@ -730,7 +736,7 @@ class CQueryBuilder {
sb.append(" order by ").append(dbOrderBy);
}
if (query.isCountDistinct() && query.isSingleAttribute()) {
if (countSingleAttribute) {
sb.append(") r1 group by r1.attribute_");
sb.append(toSql(query.getCountDistinctOrder()));
}
@@ -13,6 +13,7 @@ import io.ebeaninternal.api.SpiQueryBindCapture;
import io.ebeaninternal.api.SpiQueryPlan;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.lib.Str;
import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.server.type.DataBindCapture;
@@ -106,7 +107,7 @@ public class CQueryPlan implements SpiQueryPlan {
SpiQuery<?> query = request.getQuery();
this.profileLocation = query.getProfileLocation();
this.label = query.getPlanLabel();
this.name = deriveName(label, query.getType());
this.name = deriveName(label, query.getType(), request.getBeanDescriptor().getSimpleName());
this.location = location();
this.asOfTableCount = query.getAsOfTableCount();
this.sql = sqlRes.getSql();
@@ -130,7 +131,7 @@ public class CQueryPlan implements SpiQueryPlan {
SpiQuery<?> query = request.getQuery();
this.profileLocation = query.getProfileLocation();
this.label = query.getPlanLabel();
this.name = deriveName(label, query.getType());
this.name = deriveName(label, query.getType(), request.getBeanDescriptor().getSimpleName());
this.location = location();
this.planKey = buildPlanKey(sql, logWhereSql);
this.asOfTableCount = 0;
@@ -145,14 +146,19 @@ public class CQueryPlan implements SpiQueryPlan {
this.hash = md5Hash();
}
private String deriveName(String label, SpiQuery.Type type) {
private String deriveName(String label, SpiQuery.Type type, String simpleName) {
if (label == null) {
return "orm." + beanType.getSimpleName() + "." + type.label();
return Str.add("orm.", simpleName, ".", type.label());
}
if (label.startsWith(beanType.getSimpleName())) {
return "orm." + label;
int pos = simpleName.indexOf('.');
if (pos > 1) {
// element collection and label
return Str.add("orm.", simpleName.substring(0, pos), "_", label);
}
return "orm." + beanType.getSimpleName() + "_" + label;
if (label.startsWith(simpleName)) {
return Str.add("orm.", label);
}
return Str.add("orm.", simpleName, "_", label);
}
private SpiQueryBindCapture initBindCapture(SpiQuery<?> query) {
@@ -544,6 +544,16 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public Query<T> withLock(LockType lockType) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public Query<T> withLock(LockType lockType, LockWait lockWait) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public Query<T> forUpdate() {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
@@ -58,7 +58,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
// before we perform a query, we need to flush any
// previous persist requests that are queued/batched.
// The query may read data affected by those requests.
t.flushBatch();
t.flush();
}
}
@@ -16,7 +16,7 @@ public class LimitOffsetPagedList<T> implements PagedList<T> {
private final transient SpiEbeanServer server;
private final transient ReentrantLock lock = new ReentrantLock(false);
private final transient ReentrantLock lock = new ReentrantLock();
private final SpiQuery<T> query;
@@ -36,4 +36,13 @@ public interface STreePropertyAssoc extends STreeProperty {
*/
void setValue(EntityBean parentBean, Object contextBean);
/**
* Return true if the associated type has soft delete.
*/
boolean isTargetSoftDelete();
/**
* Return the soft delete predicate.
*/
String getSoftDeletePredicate(String tableAlias);
}
@@ -74,7 +74,6 @@ public final class SqlTreeBuilder {
* Construct for RawSql query.
*/
SqlTreeBuilder(OrmQueryRequest<?> request, CQueryPredicates predicates, OrmQueryDetail queryDetail, boolean rawNoId) {
this.rawSql = true;
this.desc = request.getBeanDescriptor();
this.rawNoId = rawNoId;
@@ -97,7 +96,6 @@ public final class SqlTreeBuilder {
* to the root node.
*/
SqlTreeBuilder(String columnAliasPrefix, CQueryBuilder builder, OrmQueryRequest<?> request, CQueryPredicates predicates) {
this.rawSql = false;
this.rawNoId = false;
this.desc = request.getBeanDescriptor();
@@ -108,11 +106,9 @@ public final class SqlTreeBuilder {
this.includeJoin = query.getM2mIncludeJoin();
this.manyWhereJoins = query.getManyWhereJoins();
this.queryDetail = query.getDetail();
this.predicates = predicates;
this.alias = new SqlTreeAlias(request.getBaseTableAlias(), temporalMode);
this.distinctOnPlatform = builder.isPlatformDistinctOn();
String fromForUpdate = builder.fromForUpdate(query);
CQueryHistorySupport historySupport = builder.getHistorySupport(query);
CQueryDraftSupport draftSupport = builder.getDraftSupport(query);
@@ -124,10 +120,8 @@ public final class SqlTreeBuilder {
* Build based on the includes and using the BeanJoinTree.
*/
public SqlTree build() {
// build the appropriate chain of SelectAdapter's
buildRoot(desc);
// build the actual String
String distinctOn = null;
String selectSql = null;
@@ -149,7 +143,6 @@ public final class SqlTreeBuilder {
}
private String buildSelectClause() {
if (rawSql) {
return "Not Used";
}
@@ -158,7 +151,6 @@ public final class SqlTreeBuilder {
}
private String buildGroupByClause() {
if (rawSql || !rootNode.isAggregation()) {
return null;
}
@@ -168,7 +160,6 @@ public final class SqlTreeBuilder {
}
private String buildDistinctOn() {
if (rawSql || !distinctOnPlatform || !sqlDistinct || Type.COUNT == query.getType()) {
return null;
}
@@ -207,7 +198,6 @@ public final class SqlTreeBuilder {
}
private String buildWhereClause() {
if (rawSql) {
return "Not Used";
}
@@ -216,7 +206,6 @@ public final class SqlTreeBuilder {
}
private String buildFromClause() {
if (rawSql) {
return "Not Used";
}
@@ -225,17 +214,13 @@ public final class SqlTreeBuilder {
}
private void buildRoot(STreeType desc) {
rootNode = buildSelectChain(null, null, desc, null);
if (!rawSql) {
alias.addJoin(queryDetail.getFetchPaths(), desc);
alias.addJoin(predicates.getPredicateIncludes(), desc);
alias.addManyWhereJoins(manyWhereJoins.getPropertyNames());
// build set of table alias
alias.buildAlias();
predicates.parseTableAlias(alias);
}
}
@@ -246,9 +231,7 @@ public final class SqlTreeBuilder {
*/
private SqlTreeNode buildSelectChain(String prefix, STreePropertyAssoc prop,
STreeType desc, List<SqlTreeNode> joinList) {
List<SqlTreeNode> myJoinList = new ArrayList<>();
List<STreePropertyAssocOne> extraProps = new ArrayList<>();
for (STreePropertyAssocOne one : desc.propsOne()) {
String propPrefix = SplitName.add(prefix, one.getName());
@@ -295,7 +278,6 @@ public final class SqlTreeBuilder {
* </p>
*/
private void addManyWhereJoins(List<SqlTreeNode> myJoinList) {
Collection<PropertyJoin> includes = manyWhereJoins.getPropertyJoins();
for (PropertyJoin joinProp : includes) {
STreePropertyAssoc beanProperty = (STreePropertyAssoc) desc.findPropertyFromPath(joinProp.getProperty());
@@ -311,7 +293,6 @@ public final class SqlTreeBuilder {
}
private SqlTreeNode buildNode(String prefix, STreePropertyAssoc prop, STreeType desc, List<SqlTreeNode> myList, SqlTreeProperties props) {
if (prefix == null) {
buildExtraJoins(desc, myList);
@@ -340,13 +321,10 @@ public final class SqlTreeBuilder {
* already in select clause.
*/
private void buildExtraJoins(STreeType desc, List<SqlTreeNode> myList) {
if (rawSql) {
return;
}
Set<String> predicateIncludes = predicates.getPredicateIncludes();
if (predicateIncludes == null) {
return;
}
@@ -362,8 +340,7 @@ public final class SqlTreeBuilder {
// look for predicateIncludes that are not in selectIncludes and add
// them as extra joins to the query
IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes);
IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes, temporalMode);
Collection<SqlTreeNodeExtraJoin> extraJoins = extraJoinDistill.getExtraJoinRootNodes();
if (!extraJoins.isEmpty()) {
// add extra joins required to support predicates
@@ -389,7 +366,6 @@ public final class SqlTreeBuilder {
* </p>
*/
private void addPropertyToSubQuery(SqlTreeProperties selectProps, STreeType desc, String propName) {
STreeProperty p = desc.findProperty(propName);
if (p == null) {
logger.error("property [" + propName + "]not found on " + desc + " for query - excluding it.");
@@ -402,13 +378,10 @@ public final class SqlTreeBuilder {
p = ((STreePropertyAssoc) p).target().findProperty(name);
}
}
selectProps.add(p);
}
private void addProperty(SqlTreeProperties selectProps, STreeType desc,
OrmQueryProperties queryProps, String propName) {
private void addProperty(SqlTreeProperties selectProps, STreeType desc, OrmQueryProperties queryProps, String propName) {
if (subQuery) {
addPropertyToSubQuery(selectProps, desc, propName);
return;
@@ -471,7 +444,6 @@ public final class SqlTreeBuilder {
}
private SqlTreeProperties getBaseSelectPartial(STreeType desc, OrmQueryProperties queryProps) {
SqlTreeProperties selectProps = new SqlTreeProperties();
// add properties in the order in which they appear
// in the query. Gives predictable sql/properties for
@@ -502,7 +474,6 @@ public final class SqlTreeBuilder {
}
private SqlTreeProperties getBaseSelect(STreeType desc, OrmQueryProperties queryProps) {
boolean partial = queryProps != null && !queryProps.allProperties();
if (partial) {
return getBaseSelectPartial(desc, queryProps);
@@ -541,13 +512,10 @@ public final class SqlTreeBuilder {
* Return true if this many node should be included in the query.
*/
private boolean isIncludeMany(String propName, STreePropertyAssocMany manyProp) {
if (queryDetail.isJoinsEmpty()) {
return false;
}
if (queryDetail.includesPath(propName)) {
if (manyProperty != null) {
// only one many associated allowed to be included in fetch
if (logger.isDebugEnabled()) {
@@ -555,7 +523,6 @@ public final class SqlTreeBuilder {
}
return false;
}
manyProperty = manyProp;
return true;
}
@@ -571,7 +538,6 @@ public final class SqlTreeBuilder {
* </p>
*/
private boolean isIncludeBean(String prefix) {
if (queryDetail.includesPath(prefix)) {
// explicitly included
String[] splitNames = SplitName.split(prefix);
@@ -592,26 +558,20 @@ public final class SqlTreeBuilder {
*/
private static class IncludesDistiller {
private final STreeType desc;
private final Set<String> selectIncludes;
private final Set<String> predicateIncludes;
private final SpiQuery.TemporalMode temporalMode;
/**
* Contains the 'root' extra joins. We only return the roots back.
*/
private final Map<String, SqlTreeNodeExtraJoin> joinRegister = new HashMap<>();
/**
* Register of all the extra join nodes.
*/
private final Map<String, SqlTreeNodeExtraJoin> rootRegister = new HashMap<>();
private final STreeType desc;
private IncludesDistiller(STreeType desc, Set<String> selectIncludes,
Set<String> predicateIncludes) {
Set<String> predicateIncludes, SpiQuery.TemporalMode temporalMode) {
this.desc = desc;
this.selectIncludes = selectIncludes;
this.predicateIncludes = predicateIncludes;
this.temporalMode = temporalMode;
}
/**
@@ -623,26 +583,21 @@ public final class SqlTreeBuilder {
* </p>
*/
private Collection<SqlTreeNodeExtraJoin> getExtraJoinRootNodes() {
String[] extras = findExtras();
if (extras.length == 0) {
return rootRegister.values();
}
// sort so we process only getting the leaves
// excluding nodes between root and the leaf
Arrays.sort(extras);
// reverse order so get the leaves first...
for (String extra : extras) {
createExtraJoin(extra);
}
return rootRegister.values();
}
private void createExtraJoin(String includeProp) {
SqlTreeNodeExtraJoin extraJoin = createJoinLeaf(includeProp);
if (extraJoin != null) {
// add the extra join...
@@ -650,7 +605,6 @@ public final class SqlTreeBuilder {
// find root of this extra join... linking back to the
// parents (creating the tree) as it goes.
SqlTreeNodeExtraJoin root = findExtraJoinRoot(includeProp, extraJoin);
// register the root because these are the only ones we
// return back.
rootRegister.put(root.getName(), root);
@@ -661,12 +615,11 @@ public final class SqlTreeBuilder {
* Create a SqlTreeNodeExtraJoin, register and return it.
*/
private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) {
ExtraJoin extra = desc.extraJoin(propertyName);
if (extra == null) {
return null;
} else {
SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.getProperty(), extra.isContainsMany());
SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.getProperty(), extra.isContainsMany(), temporalMode);
joinRegister.put(propertyName, extraJoin);
return extraJoin;
}
@@ -713,9 +666,7 @@ public final class SqlTreeBuilder {
* by the select.
*/
private String[] findExtras() {
List<String> extras = new ArrayList<>();
for (String predProp : predicateIncludes) {
if (!selectIncludes.contains(predProp)) {
extras.add(predProp);
@@ -24,19 +24,17 @@ import java.util.Set;
class SqlTreeNodeExtraJoin implements SqlTreeNode {
private final STreePropertyAssoc assocBeanProperty;
private final SpiQuery.TemporalMode temporalMode;
private final String prefix;
private final boolean manyJoin;
private final boolean pathContainsMany;
private List<SqlTreeNodeExtraJoin> children;
SqlTreeNodeExtraJoin(String prefix, STreePropertyAssoc assocBeanProperty, boolean pathContainsMany) {
SqlTreeNodeExtraJoin(String prefix, STreePropertyAssoc assocBeanProperty, boolean pathContainsMany, SpiQuery.TemporalMode temporalMode) {
this.prefix = prefix;
this.assocBeanProperty = assocBeanProperty;
this.pathContainsMany = pathContainsMany;
this.temporalMode = temporalMode;
this.manyJoin = assocBeanProperty instanceof STreePropertyAssocMany;
}
@@ -113,9 +111,7 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
@Override
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
boolean manyToMany = false;
if (assocBeanProperty instanceof STreePropertyAssocMany) {
STreePropertyAssocMany manyProp = (STreePropertyAssocMany) assocBeanProperty;
if (manyProp.hasJoinTable()) {
@@ -144,15 +140,16 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
assocBeanProperty.appendFrom(ctx, joinType);
}
joinType = assocBeanProperty.addJoin(joinType, prefix, ctx);
if (assocBeanProperty.isTargetSoftDelete() && temporalMode != SpiQuery.TemporalMode.SOFT_DELETED) {
ctx.append(" and ").append(assocBeanProperty.getSoftDeletePredicate(ctx.getTableAlias(prefix)));
}
}
if (children != null) {
if (manyJoin || pathContainsMany) {
// if AUTO then make all descendants use OUTER JOIN
joinType = joinType.autoToOuter();
}
for (SqlTreeNodeExtraJoin child : children) {
child.appendFrom(ctx, joinType);
}
@@ -89,7 +89,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
private final Class<T> beanType;
@@ -963,6 +963,16 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return this;
}
@Override
public Query<T> withLock(LockType lockType) {
return setForUpdateWithMode(LockWait.WAIT, lockType);
}
@Override
public Query<T> withLock(LockType lockType, LockWait lockWait) {
return setForUpdateWithMode(lockWait, lockType);
}
@Override
public DefaultOrmQuery<T> forUpdate() {
return setForUpdateWithMode(LockWait.WAIT, LockType.DEFAULT);
@@ -3,6 +3,9 @@ package io.ebeaninternal.server.querydefn;
import io.ebean.ExpressionList;
import io.ebean.ProfileLocation;
import io.ebean.UpdateQuery;
import io.ebean.core.type.ScalarType;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
/**
* Default implementation of UpdateQuery.
@@ -10,23 +13,30 @@ import io.ebean.UpdateQuery;
public class DefaultUpdateQuery<T> implements UpdateQuery<T> {
private final OrmUpdateProperties values = new OrmUpdateProperties();
private final DefaultOrmQuery<T> query;
private final BeanDescriptor<T> descriptor;
public DefaultUpdateQuery(DefaultOrmQuery<T> query) {
this.query = query;
this.descriptor = query.getBeanDescriptor();
query.setUpdateProperties(values);
}
@Override
public UpdateQuery<T> set(String property, Object value) {
values.set(property, value);
if (value == null) {
values.setNull(property);
} else {
final BeanProperty beanProperty = descriptor.getBeanProperty(property);
final ScalarType<Object> scalarType = (beanProperty == null) ? null: beanProperty.getScalarType();
values.set(property, value, scalarType);
}
return this;
}
@Override
public UpdateQuery<T> setNull(String property) {
values.set(property, null);
values.setNull(property);
return this;
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.querydefn;
import io.ebean.core.type.ScalarType;
import io.ebeaninternal.server.deploy.DeployParser;
import io.ebeaninternal.server.persist.Binder;
import io.ebeaninternal.server.type.DataBind;
@@ -53,9 +54,11 @@ public class OrmUpdateProperties {
private static class SimpleValue extends Value {
final Object value;
final ScalarType<Object> scalarType;
SimpleValue(Object value) {
SimpleValue(Object value, ScalarType<Object> scalarType) {
this.value = value;
this.scalarType = scalarType;
}
@Override
@@ -70,7 +73,11 @@ public class OrmUpdateProperties {
@Override
public void bind(Binder binder, DataBind dataBind) throws SQLException {
binder.bindObject(dataBind, value);
if (scalarType != null) {
scalarType.bind(dataBind, value);
} else {
binder.bindObject(dataBind, value);
}
dataBind.append(value).append(",");
}
}
@@ -115,16 +122,12 @@ public class OrmUpdateProperties {
*/
private final LinkedHashMap<String, Value> values = new LinkedHashMap<>();
/**
* Normal set property.
*/
public void set(String propertyName, Object value) {
if (value == null) {
values.put(propertyName, NULL_VALUE);
public void set(String propertyName, Object value, ScalarType<Object> scalarType) {
values.put(propertyName, new SimpleValue(value, scalarType));
}
} else {
values.put(propertyName, new SimpleValue(value));
}
public void setNull(String propertyName) {
values.put(propertyName, NULL_VALUE);
}
/**
@@ -32,7 +32,7 @@ public final class DefaultPersistenceContext implements PersistenceContext {
*/
private final HashMap<Class<?>, ClassContext> typeCache = new HashMap<>();
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
private int putCount;
@@ -66,7 +66,7 @@ public class DefaultProfileHandler implements SpiProfileHandler, Plugin {
private final ExecutorService executor;
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
private final File dir;
@@ -586,6 +586,21 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
}
@Override
public void preCommit() {
// do nothing
}
@Override
public void postCommit() {
// do nothing
}
@Override
public void postRollback(Throwable cause) {
// do nothing
}
/**
* Return true if the transaction is active.
*/
@@ -373,6 +373,9 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
private void firePreCommit() {
withEachCallback(TransactionCallback::preCommit);
if (changeLogHolder != null) {
changeLogHolder.preCommit();
}
}
private void firePostCommit() {
@@ -981,14 +984,23 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
* Batch flush, jdbc commit, trigger registered TransactionCallbacks, notify l2 cache etc.
*/
private void flushCommitAndNotify() throws SQLException {
internalBatchFlush();
firePreCommit();
// only performCommit can throw an exception
preCommit();
performCommit();
postCommit();
}
@Override
public void postCommit() {
firePostCommit();
notifyCommit();
}
@Override
public void preCommit() {
internalBatchFlush();
firePreCommit();
}
/**
* Perform a commit, fire callbacks and notify l2 cache etc.
* <p>
@@ -1132,11 +1144,16 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
} finally {
// these will not throw an exception
firePostRollback();
notifyRollback(cause);
postRollback(cause);
}
}
@Override
public void postRollback(Throwable cause) {
firePostRollback();
notifyRollback(cause);
}
/**
* If the transaction is active then perform rollback.
*/
@@ -13,9 +13,9 @@ public class JtaTransaction extends JdbcTransaction {
private final UserTransaction userTransaction;
private boolean commmitted;
private final boolean newTransaction;
private boolean newTransaction;
private boolean committed;
/**
* Create the JtaTransaction.
@@ -41,7 +41,6 @@ public class JtaTransaction extends JdbcTransaction {
if (connection.getAutoCommit()) {
connection.setAutoCommit(false);
}
} catch (SQLException e) {
throw new PersistenceException(e);
}
@@ -52,7 +51,7 @@ public class JtaTransaction extends JdbcTransaction {
*/
@Override
public void commit() {
if (commmitted) {
if (committed) {
throw new PersistenceException("This transaction has already been committed.");
}
try {
@@ -60,14 +59,14 @@ public class JtaTransaction extends JdbcTransaction {
if (newTransaction) {
userTransaction.commit();
}
notifyCommit();
postCommit();
} finally {
close();
}
} catch (Exception e) {
throw new PersistenceException(e);
}
commmitted = true;
committed = true;
}
@Override
@@ -80,7 +79,7 @@ public class JtaTransaction extends JdbcTransaction {
*/
@Override
public void rollback(Throwable e) {
if (!commmitted) {
if (!committed) {
try {
try {
if (userTransaction != null) {
@@ -90,7 +89,7 @@ public class JtaTransaction extends JdbcTransaction {
userTransaction.setRollbackOnly();
}
}
notifyRollback(e);
postRollback(e);
} finally {
closeConnection();
}
@@ -98,7 +97,6 @@ public class JtaTransaction extends JdbcTransaction {
throw new PersistenceException(ex);
}
}
}
/**
@@ -190,19 +190,17 @@ public class JtaTransactionManager implements ExternalTransactionManager {
@Override
public void beforeCompletion() {
// Future note: for JPA2 locking we will
// have beforeCommit events to fire
transaction.preCommit();
}
@Override
public void afterCompletion(int status) {
switch (status) {
case Status.STATUS_COMMITTED:
if (logger.isDebugEnabled()) {
logger.debug("Jta Txn [" + transaction.getId() + "] committed");
}
transactionManager.notifyOfCommit(transaction);
transaction.postCommit();
// Remove this transaction object as it is completed
transactionManager.scope().clearExternal();
break;
@@ -211,7 +209,7 @@ public class JtaTransactionManager implements ExternalTransactionManager {
if (logger.isDebugEnabled()) {
logger.debug("Jta Txn [" + transaction.getId() + "] rollback");
}
transactionManager.notifyOfRollback(transaction, null);
transaction.postRollback(null);
// Remove this transaction object as it is completed
transactionManager.scope().clearExternal();
break;
@@ -88,6 +88,21 @@ class NoTransaction implements SpiTransaction {
// do nothing
}
@Override
public void preCommit() {
// do nothing
}
@Override
public void postCommit() {
// do nothing
}
@Override
public void postRollback(Throwable cause) {
// do nothing
}
@Override
public String getLogPrefix() {
return null;
@@ -56,20 +56,31 @@ public class TChangeLogHolder {
* Add a bean change to the change set.
*/
public void addBeanChange(BeanChange change) {
changes.addBeanChange(change);
if (++count >= batchSize) {
// we hit the batch size so send what we have knowing
// that the transaction has not completed yet and
// reset the changes and count
owner.sendChangeLog(changes);
changes = new ChangeSet(transactionId, ++batchId);
count = 0;
sendChanges();
}
}
private void sendChanges() {
owner.sendChangeLog(changes);
changes = new ChangeSet(transactionId, ++batchId);
count = 0;
}
/**
* On post commit send the changes we have collected.
* Send the changes held prior to transaction commit.
*/
public void preCommit() {
sendChanges();
}
/**
* On post commit send the changes we have collected. This should be
* only the COMMITTED state and with all changes sent prior to commit.
*/
public void postCommit() {
changes.setTxnState(TxnState.COMMITTED);
@@ -634,7 +634,7 @@ public class TransactionManager implements SpiTransactionManager {
public ScopedTransaction externalBeginTransaction(SpiTransaction transaction, TxScope txScope) {
ScopedTransaction scopedTxn = new ScopedTransaction(scopeManager);
scopedTxn.push(new ScopeTrans(rollbackOnChecked, false, transaction, txScope));
scopeManager.set(scopedTxn);
scopeManager.replace(scopedTxn);
return scopedTxn;
}
@@ -256,7 +256,6 @@ public final class DefaultTypeManager implements TypeManager {
* Load custom scalar types registered via ExtraTypeFactory and ServiceLoader.
*/
private void loadTypesFromProviders(DatabaseConfig config, Object objectMapper) {
ServiceLoader<ExtraTypeFactory> factories = ServiceLoader.load(ExtraTypeFactory.class);
Iterator<ExtraTypeFactory> iterator = factories.iterator();
if (iterator.hasNext()) {
@@ -291,7 +290,6 @@ public final class DefaultTypeManager implements TypeManager {
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void addEnumType(ScalarType<?> scalarType, Class<? extends Enum> enumClass) {
Set<Class<?>> mappedClasses = new HashSet<>();
mappedClasses.add(enumClass);
for (Object value : EnumSet.allOf(enumClass).toArray()) {
@@ -360,20 +358,16 @@ public final class DefaultTypeManager implements TypeManager {
@Override
public ScalarType<?> getArrayScalarType(Class<?> type, DbArray dbArray, Type genericType, boolean nullable) {
Type valueType = getValueType(genericType);
if (type.equals(List.class)) {
return getArrayScalarTypeList(valueType, nullable);
} else if (type.equals(Set.class)) {
return getArrayScalarTypeSet(valueType, nullable);
} else {
throw new IllegalStateException("Type [" + type + "] not supported for @DbArray");
}
}
@SuppressWarnings("rawtypes")
private ScalarType<?> getArrayScalarTypeSet(Type valueType, boolean nullable) {
if (arrayTypeSetFactory != null) {
if (isEnumType(valueType)) {
@@ -385,7 +379,6 @@ public final class DefaultTypeManager implements TypeManager {
return new ScalarTypeJsonSet.Varchar(getDocType(valueType), nullable);
}
@SuppressWarnings("rawtypes")
private ScalarType<?> getArrayScalarTypeList(Type valueType, boolean nullable) {
if (arrayTypeListFactory != null) {
if (isEnumType(valueType)) {
@@ -407,10 +400,8 @@ public final class DefaultTypeManager implements TypeManager {
@Override
public ScalarType<?> getJsonScalarType(DeployBeanProperty prop, int dbType, int dbLength) {
Class<?> type = prop.getPropertyType();
Type genericType = prop.getGenericType();
boolean hasJacksonAnnotations = objectMapperPresent && checkJacksonAnnotations(prop);
if (type.equals(List.class)) {
@@ -421,7 +412,6 @@ public final class DefaultTypeManager implements TypeManager {
return createJsonObjectMapperType(prop, dbType, docType);
}
}
if (type.equals(Set.class)) {
DocPropertyType docType = getDocType(genericType);
if (!hasJacksonAnnotations && isValueTypeSimple(genericType)) {
@@ -430,7 +420,6 @@ public final class DefaultTypeManager implements TypeManager {
return createJsonObjectMapperType(prop, dbType, docType);
}
}
if (type.equals(Map.class)) {
if (!hasJacksonAnnotations && isMapValueTypeObject(genericType)) {
return ScalarTypeJsonMap.typeFor(postgres, dbType);
@@ -438,7 +427,6 @@ public final class DefaultTypeManager implements TypeManager {
return createJsonObjectMapperType(prop, dbType, DocPropertyType.OBJECT);
}
}
if (objectMapperPresent) {
if (type.equals(JsonNode.class)) {
switch (dbType) {
@@ -455,7 +443,6 @@ public final class DefaultTypeManager implements TypeManager {
}
}
}
return createJsonObjectMapperType(prop, dbType, DocPropertyType.OBJECT);
}
@@ -510,11 +497,9 @@ public final class DefaultTypeManager implements TypeManager {
* <p>
* Used for java.util.Date and java.util.Calendar which can be mapped to
* different jdbcTypes in a single system.
* </p>
*/
@Override
public ScalarType<?> getScalarType(Class<?> type, int jdbcType) {
// File is a special Lob so check for that first
if (File.class.equals(type)) {
return fileType;
@@ -554,10 +539,8 @@ public final class DefaultTypeManager implements TypeManager {
* Kind of special case because these map multiple jdbc types to single Java
* types - like String - Varchar, LongVarchar, Clob. For this reason I check
* for the specific Lob types first before looking for a matching type.
* </p>
*/
private ScalarType<?> getLobTypes(int jdbcType) {
return getScalarType(jdbcType);
}
@@ -601,14 +584,10 @@ public final class DefaultTypeManager implements TypeManager {
* Create the Mapping of Enum fields to DB values using EnumValue annotations.
* <p>
* Return null if the EnumValue annotations are not present/used.
* </p>
*/
private ScalarTypeEnum<?> createEnumScalarType2(Class<?> enumType) {
boolean integerType = true;
Map<String, String> nameValueMap = new LinkedHashMap<>();
Field[] fields = enumType.getDeclaredFields();
for (Field field : fields) {
EnumValue enumValue = AnnotationUtil.get(field, EnumValue.class);
@@ -624,8 +603,7 @@ public final class DefaultTypeManager implements TypeManager {
// Not using EnumValue here
return null;
}
return createEnumScalarType(enumType, nameValueMap, integerType, 0);
return createEnumScalarType(enumType, nameValueMap, integerType, 0, true);
}
/**
@@ -635,17 +613,14 @@ public final class DefaultTypeManager implements TypeManager {
* such as A,I,N rather than the ACTIVE, INACTIVE, NEW. So there really needs
* to be a mapping from the nicely named enumeration values to the typically
* much shorter codes used in the DB.
* </p>
*/
@Override
public ScalarType<?> createEnumScalarType(Class<? extends Enum<?>> enumType, EnumType type) {
ScalarType<?> scalarType = getScalarType(enumType);
if (scalarType instanceof ScalarTypeWrapper) {
// no override or further mapping required
return scalarType;
}
ScalarTypeEnum<?> scalarEnum = (ScalarTypeEnum<?>)scalarType;
if (scalarEnum != null && !scalarEnum.isOverrideBy(type)) {
if (type != null && !scalarEnum.isCompatible(type)) {
@@ -653,7 +628,6 @@ public final class DefaultTypeManager implements TypeManager {
}
return scalarEnum;
}
scalarEnum = createEnumScalarTypePerExtentions(enumType);
if (scalarEnum == null) {
// use JPA normal Enum type (without mapping)
@@ -665,33 +639,27 @@ public final class DefaultTypeManager implements TypeManager {
private ScalarTypeEnum<?> createEnumScalarTypePerSpec(Class<?> enumType, EnumType type) {
if (type == null) {
if(defaultEnumType == EnumType.ORDINAL) {
if (defaultEnumType == EnumType.ORDINAL) {
return new ScalarTypeEnumStandard.OrdinalEnum(enumType);
} else {
return new ScalarTypeEnumStandard.StringEnum(enumType);
}
} else if (type == EnumType.ORDINAL) {
return new ScalarTypeEnumStandard.OrdinalEnum(enumType);
} else {
return new ScalarTypeEnumStandard.StringEnum(enumType);
}
}
private ScalarTypeEnum<?> createEnumScalarTypePerExtentions(Class<? extends Enum<?>> enumType) {
Method[] methods = enumType.getMethods();
for (Method method : methods) {
DbEnumValue dbValue = AnnotationUtil.get(method, DbEnumValue.class);
if (dbValue != null) {
boolean integerValues = DbEnumType.INTEGER == dbValue.storage();
return createEnumScalarTypeDbValue(enumType, method, integerValues, dbValue.length());
return createEnumScalarTypeDbValue(enumType, method, integerValues, dbValue.length(), dbValue.withConstraint());
}
}
// look for EnumValue annotations instead
return createEnumScalarType2(enumType);
}
@@ -702,10 +670,8 @@ public final class DefaultTypeManager implements TypeManager {
* Return null if the EnumValue annotations are not present/used.
* </p>
*/
private ScalarTypeEnum<?> createEnumScalarTypeDbValue(Class<? extends Enum<?>> enumType, Method method, boolean integerType, int length) {
private ScalarTypeEnum<?> createEnumScalarTypeDbValue(Class<? extends Enum<?>> enumType, Method method, boolean integerType, int length, boolean withConstraint) {
Map<String, String> nameValueMap = new LinkedHashMap<>();
Enum<?>[] enumConstants = enumType.getEnumConstants();
for (Enum<?> enumConstant : enumConstants) {
try {
@@ -719,8 +685,7 @@ public final class DefaultTypeManager implements TypeManager {
// Not using EnumValue here
return null;
}
return createEnumScalarType(enumType, nameValueMap, integerType, length);
return createEnumScalarType(enumType, nameValueMap, integerType, length, withConstraint);
}
/**
@@ -728,27 +693,20 @@ public final class DefaultTypeManager implements TypeManager {
* length create the ScalarType for the Enum.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private ScalarTypeEnum<?> createEnumScalarType(Class enumType, Map<String, String> nameValueMap, boolean integerType, int dbColumnLength) {
private ScalarTypeEnum<?> createEnumScalarType(Class enumType, Map<String, String> nameValueMap, boolean integerType, int dbColumnLength, boolean withConstraint) {
EnumToDbValueMap<?> beanDbMap = EnumToDbValueMap.create(integerType);
int maxValueLen = 0;
for (Map.Entry<String, String> entry : nameValueMap.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
maxValueLen = Math.max(maxValueLen, value.length());
Object enumValue = Enum.valueOf(enumType, name.trim());
beanDbMap.add(enumValue, value, name.trim());
}
if (dbColumnLength == 0 && !integerType) {
dbColumnLength = maxValueLen;
}
return new ScalarTypeEnumWithMapping(beanDbMap, enumType, dbColumnLength);
return new ScalarTypeEnumWithMapping(beanDbMap, enumType, dbColumnLength, withConstraint);
}
/**
@@ -760,10 +718,8 @@ public final class DefaultTypeManager implements TypeManager {
* </p>
*/
private void initialiseCustomScalarTypes(BootupClasses bootupClasses) {
for (Class<? extends ScalarType<?>> cls : bootupClasses.getScalarTypes()) {
try {
ScalarType<?> scalarType;
if (objectMapper == null) {
scalarType = cls.newInstance();
@@ -776,9 +732,7 @@ public final class DefaultTypeManager implements TypeManager {
scalarType = cls.newInstance();
}
}
addCustomType(scalarType);
} catch (Exception e) {
String msg = "Error loading ScalarType [" + cls.getName() + "]";
logger.error(msg, e);
@@ -801,30 +755,23 @@ public final class DefaultTypeManager implements TypeManager {
@SuppressWarnings({"unchecked", "rawtypes"})
private void initialiseScalarConverters(BootupClasses bootupClasses) {
List<Class<? extends ScalarTypeConverter<?, ?>>> foundTypes = bootupClasses.getScalarConverters();
for (Class<? extends ScalarTypeConverter<?, ?>> foundType : foundTypes) {
try {
Class<?>[] paramTypes = TypeReflectHelper.getParams(foundType, ScalarTypeConverter.class);
if (paramTypes.length != 2) {
throw new IllegalStateException("Expected 2 generics paramtypes but got: " + Arrays.toString(paramTypes));
}
Class<?> logicalType = paramTypes[0];
Class<?> persistType = paramTypes[1];
ScalarType<?> wrappedType = getScalarType(persistType);
if (wrappedType == null) {
throw new IllegalStateException("Could not find ScalarType for: " + paramTypes[1]);
}
ScalarTypeConverter converter = foundType.newInstance();
ScalarTypeWrapper stw = new ScalarTypeWrapper(logicalType, wrappedType, converter);
logger.debug("Register ScalarTypeWrapper from {} -> {} using:{}", logicalType, persistType, foundType);
add(stw);
} catch (Exception e) {
logger.error("Error registering ScalarTypeConverter [" + foundType.getName() + "]", e);
}
@@ -833,30 +780,23 @@ public final class DefaultTypeManager implements TypeManager {
@SuppressWarnings({"unchecked", "rawtypes"})
private void initialiseAttributeConverters(BootupClasses bootupClasses) {
List<Class<? extends AttributeConverter<?, ?>>> foundTypes = bootupClasses.getAttributeConverters();
for (Class<? extends AttributeConverter<?, ?>> foundType : foundTypes) {
try {
Class<?>[] paramTypes = TypeReflectHelper.getParams(foundType, AttributeConverter.class);
if (paramTypes.length != 2) {
throw new IllegalStateException("Expected 2 generics paramtypes but got: " + Arrays.toString(paramTypes));
}
Class<?> logicalType = paramTypes[0];
Class<?> persistType = paramTypes[1];
ScalarType<?> wrappedType = getScalarType(persistType);
if (wrappedType == null) {
throw new IllegalStateException("Could not find ScalarType for: " + paramTypes[1]);
}
AttributeConverter converter = foundType.newInstance();
ScalarTypeWrapper stw = new ScalarTypeWrapper(logicalType, wrappedType, new AttributeConverterAdapter(converter));
logger.debug("Register ScalarTypeWrapper from {} -> {} using:{}", logicalType, persistType, foundType);
add(stw);
} catch (Exception e) {
logger.error("Error registering AttributeConverter [" + foundType.getName() + "]", e);
}
@@ -875,12 +815,10 @@ public final class DefaultTypeManager implements TypeManager {
jsonNodeVarchar = new ScalarTypeJsonNode.Varchar(mapper);
jsonNodeJson = jsonNodeClob; // Default for non-Postgres databases
jsonNodeJsonb = jsonNodeClob; // Default for non-Postgres databases
if (isPostgres(config.getDatabasePlatform())) {
jsonNodeJson = new ScalarTypeJsonNodePostgres.JSON(mapper);
jsonNodeJsonb = new ScalarTypeJsonNodePostgres.JSONB(mapper);
}
// add as default mapping for JsonNode (when not annotated with @DbJson etc)
typeMap.put(JsonNode.class, jsonNodeJson);
}
@@ -36,7 +36,7 @@ public class ScalarTypeArrayList extends ScalarTypeArrayBase<List> implements Sc
static class Factory implements PlatformArrayTypeFactory {
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
private final Map<String, ScalarTypeArrayList> cache = new HashMap<>();
/**
@@ -27,7 +27,7 @@ class ScalarTypeArrayListH2 extends ScalarTypeArrayList {
static class Factory implements PlatformArrayTypeFactory {
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
private final Map<String, ScalarTypeArrayListH2> cache = new HashMap<>();
/**
@@ -36,7 +36,7 @@ public class ScalarTypeArraySet extends ScalarTypeArrayBase<Set> implements Scal
static class Factory implements PlatformArrayTypeFactory {
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
private final Map<String, ScalarTypeArraySet> cache = new HashMap<>();
/**
@@ -27,7 +27,7 @@ class ScalarTypeArraySetH2 extends ScalarTypeArraySet {
static class Factory implements PlatformArrayTypeFactory {
private final ReentrantLock lock = new ReentrantLock(false);
private final ReentrantLock lock = new ReentrantLock();
private final Map<String, ScalarTypeArraySetH2> cache = new HashMap<>();
/**
@@ -20,13 +20,20 @@ public class ScalarTypeEnumWithMapping extends ScalarTypeEnumStandard.EnumBase i
private final int length;
private final boolean withConstraint;
/**
* Create with an explicit mapping of bean to database values.
*/
public ScalarTypeEnumWithMapping(EnumToDbValueMap<?> beanDbMap, Class<?> enumType, int length) {
public ScalarTypeEnumWithMapping(EnumToDbValueMap<?> beanDbMap, Class<?> enumType, int length, boolean withConstraint) {
super(enumType, false, beanDbMap.getDbType());
this.beanDbMap = beanDbMap;
this.length = length;
this.withConstraint = withConstraint;
}
public ScalarTypeEnumWithMapping(EnumToDbValueMap<?> beanDbMap, Class<?> enumType, int length) {
this(beanDbMap, enumType, length, true);
}
@Override
@@ -49,6 +56,9 @@ public class ScalarTypeEnumWithMapping extends ScalarTypeEnumStandard.EnumBase i
*/
@Override
public Set<String> getDbCheckConstraintValues() {
if (!withConstraint) {
return null;
}
LinkedHashSet values = new LinkedHashSet();
Iterator<?> it = beanDbMap.dbValues();
while (it.hasNext()) {
@@ -64,9 +74,6 @@ public class ScalarTypeEnumWithMapping extends ScalarTypeEnumStandard.EnumBase i
/**
* Return the DB column length for storing the enum value.
* <p>
* This is for enum's mapped to strings.
* </p>
*/
@Override
public int getLength() {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More