Compare commits

..
Author SHA1 Message Date
rbygrave fc174c8988 [maven-release-plugin] prepare release ebean-parent-12.8.2 2021-04-09 17:49:07 +12:00
rbygrave 06802896ef #2213 Bump agent for @Transactional(autoPersistUpdates = TxOption.ON) 2021-04-09 16:17:58 +12:00
Robin Bygrave 7f13ac9e5e #2213 - Support autoPersistUpdates via TxScope 2021-04-08 23:15:49 +12:00
Rob BygraveandGitHub 4a1374d22d Merge pull request #2213 from ebean-orm/feature/transparentPersistence
Initial experimental support for autoPersistUpdates / "transparent persistence"
2021-04-08 23:03:05 +12:00
rbygrave 4ee8d647b7 No effective change - tidy test TestCustomerFinder 2021-04-07 23:28:53 +12:00
rbygrave 59793f3c01 Improve javadoc for Transaction#setGetGeneratedKeys
More clearly document the limitation that we can't update beans that don't have id values
2021-04-07 15:50:04 +12:00
rbygrave f7f383d8f3 No effective change - tidy tests TestRawSqlPositionedParams, TestRawSqlUnparsedQuery 2021-04-07 13:38:34 +12:00
rbygrave 562a3959b6 Merge branch 'master' of github.com:ebean-orm/ebean 2021-04-06 21:14:21 +12:00
rbygrave bb1275f4f9 #2217 - Inserting an entity bean is skipped with a String @Id property and no other properties set 2021-04-06 21:13:59 +12:00
Rob BygraveandGitHub 7fde6b9331 Merge pull request #2216 from ebean-orm/feature/2177-take2
#2177 - Fix where schema not provided by JDBC driver
2021-04-06 20:13:24 +12:00
rbygrave 3e97a4005a Change TestCustomerFinder drop query plan threshold for tests only 2021-04-06 20:09:09 +12:00
Rob BygraveandGitHub c271760230 Merge pull request #2215 from PromanSEW/patch-1
Fix copy/paste typo in javadoc for endOr()
2021-04-06 20:00:02 +12:00
rbygrave f7c1845f0c #2177 - Fix where schema not provided by JDBC driver
For @Table with schema, using findNative with a JDBC driver that does not provide the schema this fix matches by just using the table name.
2021-04-06 19:58:02 +12:00
Roman ParshikovandGitHub 9b04f20ed3 Fix copy/paste typo 2021-04-06 13:32:57 +08:00
Rob BygraveandGitHub 62b6b6293b Merge pull request #2214 from cit-consulting/fix_with_lock
Explicit locking method withLock() is unaccessible on query beans
2021-04-06 08:27:38 +12:00
Vladimir Konkov 6d8a1c9398 Explicit locking method withLock() is unaccessible on query beans 2021-04-05 21:45:48 +03:00
Robin Bygrave 303039735f Refactor rename transparentPersistence to autoPersistUpdates 2021-04-01 23:04:22 +13:00
Robin Bygrave 6bcf5c6881 Add DatabaseConfig.autoPersistUpdates for experimental "transparent persistence" 2021-04-01 22:57:34 +13:00
Robin Bygrave 0527f45f5a For experimental "transparent persistence" - Inserts added to PC
When inserting bean with transparent persistence the beans are registered with the persistence context. This is done to support the cases where the beans are inserted, a flush() occurs, the inserted bean is mutated and now dirty - we want that to be detected and get an update
2021-04-01 16:59:03 +13:00
Robin Bygrave ba397bbb0c For experimental "transparent persistence" - Delete removes from PC early
When delete bean executed with transparent persistence, this marks the bean as removed from the persistence context early.

This avoids a "dirty" deleted bean from being seen as a "dirty" bean in the persistence context at flush() time.
2021-04-01 16:04:18 +13:00
Robin Bygrave a617d58902 Initial experimental support for "transparent persistence"
In short, when this is turned on at flush() get any dirty beans held by the PersistenceContext and persist them (always update for these beans)
2021-04-01 14:47:54 +13:00
Robin Bygrave 7fc4a229ae Merge branch 'master' of https://github.com/ebean-orm/ebean 2021-04-01 12:23:00 +13:00
Robin Bygrave f53d31aa2e No functional change - final field for BatchControl 2021-04-01 12:22:45 +13:00
Rob BygraveandGitHub b889b6a6b0 Merge pull request #2212 from tobias-/bitwise_querybean_operators
Add bitwise operators to query beans
2021-04-01 09:38:00 +13:00
Tobias f9b55a06b8 Add bitwise operators to query beans 2021-03-31 12:53:06 +02:00
rob bygrave 09d772a5e5 [maven-release-plugin] prepare for next development iteration 2021-03-31 00:06:13 +13:00
rob bygrave f7035eb228 [maven-release-plugin] prepare release ebean-parent-12.8.1 2021-03-31 00:06:04 +13:00
Robin Bygrave 1bf09ddd38 #2211 - Improve javadoc for queryPlanTTLSeconds
Hopefully people understand it is unrelated. Unfortunately we now have some properties with similar names so maybe we need a better name for this sometime in the future.
2021-03-30 23:49:44 +13:00
Robin Bygrave a8bf27da55 #2211 - Improve query plan capture - Align config property names
collectQueryPlans -> queryPlanEnable
collectQueryPlanThresholdMicros -> queryPlanThresholdMicros

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

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

Hitting error InaccessibleObjectException: Unable to make protected void java.util.ResourceBundle.setParent(java.util.ResourceBundle) accessible: module java.base does not "opens java.util" to unnamed module @62732be7
2021-03-22 23:24:16 +13:00
rob bygrave f7905c8e94 [maven-release-plugin] prepare for next development iteration 2021-03-18 22:30:17 +13:00
rob bygrave 7ab7b612f6 [maven-release-plugin] prepare release ebean-parent-12.8.0 2021-03-18 22:23:34 +13:00
rob bygrave ce1d2803ce Bump test wait time for redis 2021-03-18 22:18:34 +13:00
rob bygrave 9b066e4eac Bump version to 12.8.0-SNAPSHOT 2021-03-18 21:26:50 +13:00
Robin Bygrave e1f9d106f0 Additional tests for #2191 query findEach batch consumer 2021-03-18 17:12:21 +13:00
Robin Bygrave e40a539406 #2200 - ENH: Add DtoQuery findEach with batch consumer - findEach(int batchSize, Consumer<List<T>> consumer) 2021-03-18 17:02:41 +13:00
Robin Bygrave bb73c30bb7 #2199 - ScalarTypeWrapper doesn't handle "nullValue" correctly
Fix for - standard JPA AttributeConverter which doesn't have the getNullValue method, he/she can't convert null to a custom null object

AttributeConverterAdapter probes the AttributeConverter for the nullValue rather than just assuming it is null.
2021-03-18 16:14:46 +13:00
Robin Bygrave 2e16f05a70 #2199 - ScalarTypeWrapper doesn't handle "nullValue" correctly
Handles the case for ScalarTypeConverter with custom null value and binding the custom null value (via query parameter, CallableSql parameter etc)
2021-03-18 10:03:35 +13:00
Robin Bygrave 1837c3443d #2186 - Invalid generated table and column names of @ManyToMany relation with allQuotedIdentifiers=true 2021-03-17 21:02:59 +13:00
Rob BygraveandGitHub bd1060ff28 Merge pull request #2198 from ebean-orm/sebastian-mrozek-feature/json-assert-compare-arrays
Feature/json assert compare arrays
2021-03-16 22:06:28 +13:00
Robin Bygrave c9968a9662 Change JsonAssertContains array matching to match on size 2021-03-16 22:03:12 +13:00
Robin Bygrave e3001e8bdd Fix JsonAssertContains testContainsFails with missing parts of JSON
Just add missing parts of test/resources/original.json and original-subset-modified.json
2021-03-16 09:30:54 +13:00
Robin Bygrave 82bb2b4567 Merge branch 'feature/json-assert-compare-arrays' of https://github.com/sebastian-mrozek/ebean into sebastian-mrozek-feature/json-assert-compare-arrays 2021-03-16 09:14:06 +13:00
Robin Bygrave f483002e39 #2197 - [ebean-redis] - Bump jedis dependency to 3.5.2 (from 3.4.0 2021-03-16 09:13:01 +13:00
Robin Bygrave c2a87a0c88 #2196 - SQLException:Column "T0.ID" must be in the GROUP BY list - when findCount with having clause 2021-03-15 22:43:25 +13:00
Robin Bygrave 733886c70b #2195 - @Aggregation on OneToMany property not mapping to column properly 2021-03-15 22:03:14 +13:00
sebastian-mrozek 899fdca995 Improve assertions in case json compare does not fail as expected
Remove sys out print.
2021-03-15 13:04:05 +13:00
sebastian-mrozek 4731ee034e Handle searching for matching elements in an array
Add unit tests.
Refactor internals to allow reusing assertion method for finding matches in an array.
2021-03-15 12:51:36 +13:00
Robin Bygrave e6faf9efd1 #2193 - Prevent registration of 2 default servers (2 Database that have defaultServer=true) 2021-03-11 23:21:44 +13:00
Rob BygraveandGitHub d75d1fce43 Change ModuleInfoLoader API and querybean-generator for named default database (#2190)
Currently we can not use @DbName with the entities of the default database. With this change the generated code that registers entity classes will support using @DbName with the default database.
2021-03-11 20:42:25 +13:00
sebastian-mrozek ef3980a3ee Add test resources 2021-03-11 13:47:46 +13:00
sebastian-mrozek 2a1c170aa4 Add test for shuffled array comparison 2021-03-11 13:44:59 +13:00
Rob BygraveandGitHub d65e769dd7 Merge pull request #2191 from ebean-orm/feature/findEachBatch
ENH: Add Query findEach() with batch consumer
2021-03-11 13:34:58 +13:00
Rob Bygrave 2bb5a85bd6 ENH: Add Query findEach() with batch consumer
This is a variation of findEach() that makes it easy to have a batch consumer processing a large result in batches.  For example, process in batches of 50 beans.

Note that the last batch consumed/processed will often have less than the batch size.
2021-03-10 00:18:35 +13:00
rob bygrave 98aee9ca62 Merge branch 'master' of github.com:ebean-orm/ebean 2021-03-09 20:13:47 +13:00
Robin Bygrave c86f965d36 #2187 - Improve error message thrown when @JoinTable @JoinColumn not satisfied.
Added " or a @JoinColumn needs an explicit referencedColumnName specified?";
2021-03-04 08:55:11 +13:00
Robin Bygrave 81e445e386 #2184 - Fix for findNative() with fetch() - convert fetch to fetchQuery with nativeSql
When using nativeSql (think of it as the "root query") we can't use "fetch joins" (FetchConfig.ofDefault()) as that means to prefer to use a SQL JOIN. In this nativeSql case we need to effectively automatically convert fetch() to fetchQuery()
2021-03-02 15:47:07 +13:00
Robin Bygrave 2cbbbc4419 Merge branch 'master' of https://github.com/ebean-orm/ebean 2021-03-02 15:32:40 +13:00
Rob BygraveandGitHub 632f43016e Merge pull request #2183 from bkempe/findNative_fetch_inconsistent
added tests for findNative query with fetch() and fetchQuery()
2021-03-02 15:31:38 +13:00
Robin Bygrave fe62ad2ea2 #2184 - Initial refactor of DefaultOrmQuery to support 2184
- Add fetchInternal() and point fetchQuery() fetchLazy() and fetchCache() to it
- Move the fetch() methods so they are located together
2021-03-02 15:28:17 +13:00
Robin Bygrave f416309863 No effective change - change DefaultOrmQuery methods to return Query<T> rather than DefaultOrmQuery<T>
This is no effective functional change but we do need to update the test OrmQueryPlanKeyTest to cast now.
2021-03-02 14:44:54 +13:00
Rob BygraveandGitHub dcf5e7a15c Merge pull request #2185 from bkempe/querybeans_is_in_subquery
ENH: add in(subQuery) support for query beans
2021-03-02 14:10:53 +13:00
Ben Kempe af4f70e3a7 add in(subQuery) support for query beans 2021-03-01 13:21:05 +01:00
Ben Kempe 79dde23ec6 added tests for findNative fetches 2021-03-01 12:16:22 +01:00
robin e4d0341e67 [maven-release-plugin] prepare for next development iteration 2021-03-01 23:45:15 +13:00
robin 907b182931 [maven-release-plugin] prepare release ebean-parent-12.7.2 2021-03-01 23:45:05 +13:00
robin 1a26cd717f #2182 - Tidy javadoc - this may mean JDK 15 is required to generate javadoc 2021-03-01 23:40:06 +13:00
Robin Bygrave 7bfacb095b #2181 - Oracle 9,10,11 rownum based limiting for SqlQuery and DtoQuery (rather than Oracle 12g row limiting) 2021-03-01 22:47:34 +13:00
Robin Bygrave 6315edfc21 #2177 - findNative not mapping bean when schema specified in entity mapping 2021-03-01 22:08:17 +13:00
Robin Bygrave 464f48d717 Bump test kotlin-querybean-generator version 2021-03-01 21:34:17 +13:00
Robin Bygrave eacfaf3403 No effective change - refactor tidy SqlTreeNodeBean 2021-03-01 21:19:25 +13:00
Rob BygraveandGitHub 36d8028045 Merge pull request #2180 from ebean-orm/feature/refactor-SqlTreeNodeBean
Improved fix for #2179 - Fetch on nested one-to-many collectio…
2021-03-01 20:32:08 +13:00
Robin Bygrave 297c1d7abb #2179 - Improved fix for (2179) Fetch on nested one-to-many collections results in duplicates
The improvement here is that SqlTreeNodeManyRoot uses load and isContextBean() so that it only checks if the collection already contains the detailBean if the detailBean was already in the persistence context (aka not newly loaded and added).  The vast majority case is that detailBean is loaded fresh so for the common case we can skip the extra collection contains() check.
2021-03-01 20:27:50 +13:00
Robin Bygrave 5a73c85e6e Refactor internals - SqlTreeNodeBean add derived readIdNormal + private methods
This is no effective change
2021-03-01 18:36:49 +13:00
Robin Bygrave 68138bc009 Refactor internals no effective change - reorder methods of SqlTreeNodeManyRoot 2021-03-01 17:45:52 +13:00
Robin Bygrave 8c7df3212a Refactor internals add SqlTreeRoot as follow up to #2179
Adds SqlTreeRoot interface to make it more explicit that SqlTreeNodeRoot is the only valid top level node in the sql tree (and as such has the root level load() method)
2021-03-01 17:44:13 +13:00
Robin Bygrave 5ebad53072 #2179 - Improve internals for outer join resulting in null many bean
Refactor improving the BeanList.internalAddWithCheck() for the null bean case
2021-03-01 14:00:40 +13:00
Robin Bygrave 640824ae31 Merge branch 'bkempe-nested_fetch_duplicates' 2021-03-01 13:39:16 +13:00
Robin Bygrave 6acf93c978 #2178 - Fix for duplicate instances added to collection for nested one-to-many fetch
This is an initial fix for this case.
2021-03-01 13:38:40 +13:00
Robin Bygrave 6897e06dcd Merge branch 'nested_fetch_duplicates' of https://github.com/bkempe/ebean into bkempe-nested_fetch_duplicates 2021-03-01 10:23:49 +13:00
Ben Kempe d036af8d1a Add more assertions for nested one-to-many fetch 2021-02-28 19:06:51 +01:00
robin b77c62e0db [maven-release-plugin] prepare for next development iteration 2021-02-19 22:51:03 +13:00
rob bygrave bab637667d Update test only - update TestBasicClobNoVer to use DB and AssertJ 2021-02-14 11:50:54 +13:00
207 changed files with 4278 additions and 1060 deletions
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.7.1</version>
<version>12.8.2</version>
</parent>
<name>ebean api</name>
@@ -50,7 +50,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>6.15</version>
<version>7.0</version>
</dependency>
<dependency>
@@ -14,7 +14,7 @@ import java.util.Optional;
*
* public class CustomerFinder extends BeanFinder<Long,Customer> {
*
* Inject
* @Inject
* public CustomerFinder(Database database) {
* super(Customer.class, database);
* }
@@ -9,10 +9,10 @@ import java.util.Collection;
* <p>
* <pre>{@code
*
* Repository
* @Repository
* public class CustomerRepository extends BeanRepository<Long,Customer> {
*
* Inject
* @Inject
* public CustomerRepository(Database server) {
* super(Customer.class, server);
* }
@@ -897,7 +897,7 @@ public interface Database {
* <pre>{@code
* public class Order { ...
*
* OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* List<OrderDetail> details;
* ...
* }
@@ -33,6 +33,7 @@ public class DatabaseFactory {
private static final ReentrantLock lock = new ReentrantLock();
private static SpiContainer container;
private static String defaultServerName;
static {
EbeanVersion.getVersion();
@@ -76,6 +77,12 @@ public class DatabaseFactory {
}
Database server = createInternal(config);
if (config.isRegister()) {
if (config.isDefaultServer()) {
if (defaultServerName != null && !defaultServerName.equals(config.getName())) {
throw new IllegalStateException("Registering [" + config.getName() + "] as the default server but [" + defaultServerName + "] is already registered as the default");
}
defaultServerName = config.getName();
}
DbPrimary.setSkip(true);
DbContext.getInstance().register(server, config.isDefaultServer());
}
@@ -149,7 +149,7 @@ public interface DocumentStore {
* .setUseDocStore(true)
* .where()... // perhaps add predicates
* .findEachWhile(new Predicate<Order>() {
* Override
* @Override
* public void accept(Order bean) {
* // process the bean
*
@@ -53,6 +53,17 @@ public interface DtoQuery<T> {
*/
void findEach(Consumer<T> consumer);
/**
* Execute the query iterating the results and batching them for the consumer.
* <p>
* This runs like findEach streaming results from the database but just collects the results
* into batches to pass to the consumer.
*
* @param batch The number of dto beans to collect before given them to the consumer
* @param consumer The consumer to process the batch of DTO beans
*/
void findEach(int batch, Consumer<List<T>> consumer);
/**
* Execute the query iterating a row at a time with the ability to stop consuming part way through.
* <p>
+1 -1
View File
@@ -346,7 +346,7 @@ public final class Ebean {
* <pre>{@code
* public class Order { ...
*
* OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* List<OrderDetail> details;
* ...
* }
@@ -200,7 +200,7 @@ public interface ExpressionFactory {
Expression gtOrNull(String propertyName, Object value);
/**
* Greater than or Equal to OR Null <code> >= or null </code>
* Greater than or Equal to OR Null ({@code >= or null })
* <p>
* A convenient expression combining GE and Is Null. Most often useful for range
* expressions where the top range value is nullable.
@@ -227,7 +227,7 @@ public interface ExpressionFactory {
Expression ltOrNull(String propertyName, Object value);
/**
* Less Than or Equal to OR Null <code> <= or null </code>
* Less Than or Equal to OR Null ({@code <= or null })
* <p>
* A convenient expression combining LE and Is Null. Most often useful for range
* expressions where the bottom range value is nullable.
@@ -307,6 +307,13 @@ public interface ExpressionList<T> {
*/
void findEach(Consumer<T> consumer);
/**
* Execute findEach with a batch consumer.
*
* @see Query#findEach(int, Consumer)
*/
void findEach(int batch, Consumer<List<T>> consumer);
/**
* Execute the query processing the beans one at a time with the ability to
* stop processing before reading all the beans.
@@ -909,7 +916,7 @@ public interface ExpressionList<T> {
ExpressionList<T> gtOrNull(String propertyName, Object value);
/**
* Greater Than or Equal to OR Null - <code> >= or null </code>.
* Greater Than or Equal to OR Null - ({@code >= or null }).
*/
ExpressionList<T> geOrNull(String propertyName, Object value);
@@ -930,7 +937,7 @@ public interface ExpressionList<T> {
ExpressionList<T> ltOrNull(String propertyName, Object value);
/**
* Less Than or Equal to OR Null - <code> <= or null </code>.
* Less Than or Equal to OR Null - ({@code <= or null }).
*/
ExpressionList<T> leOrNull(String propertyName, Object value);
@@ -1664,7 +1671,7 @@ public interface ExpressionList<T> {
ExpressionList<T> endAnd();
/**
* End a AND junction - synonym for endJunction().
* End a OR junction - synonym for endJunction().
*/
ExpressionList<T> endOr();
@@ -159,6 +159,13 @@ public interface ExtendedServer {
*/
<T> void findEach(Query<T> query, Consumer<T> consumer, Transaction transaction);
/**
* Execute findEach with batch consumer.
*
* @see Query#findEach(int, Consumer)
*/
<T> void findEach(Query<T> query, int batch, Consumer<List<T>> consumer, Transaction t);
/**
* Execute the query visiting the each bean one at a time.
* <p>
@@ -46,7 +46,7 @@ public class FetchConfig implements Serializable {
/**
* Deprecated - migrate to one of the static factory methods like {@link FetchConfig#ofQuery()}
*
* <p>
* Construct using default JOIN mode.
*/
@Deprecated
@@ -89,7 +89,7 @@ public class FetchConfig implements Serializable {
* Return FetchConfig to lazily load the relationship.
*/
public static FetchConfig ofLazy() {
return new FetchConfig(LAZY_MODE, 10);
return new FetchConfig(LAZY_MODE, 0);
}
/**
@@ -110,8 +110,8 @@ public class FetchConfig implements Serializable {
* We want to migrate away from mutating FetchConfig to a fully immutable FetchConfig.
*/
private FetchConfig mutate(int mode, int batchSize) {
if (batchSize < 1) {
throw new IllegalArgumentException("batch size "+batchSize+" must be > 0");
if (batchSize < 0) {
throw new IllegalArgumentException("batch size " + batchSize + " must be > 0");
}
this.mode = mode;
this.batchSize = batchSize;
@@ -124,7 +124,7 @@ public class FetchConfig implements Serializable {
*/
@Deprecated
public FetchConfig lazy() {
return mutate(LAZY_MODE, 10);
return mutate(LAZY_MODE, 0);
}
/**
@@ -137,7 +137,7 @@ public class FetchConfig implements Serializable {
/**
* Deprecated - migrate to FetchConfig.ofQuery().
*
* <p>
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
* <p>
@@ -150,17 +150,15 @@ public class FetchConfig implements Serializable {
/**
* Deprecated - migrate to FetchConfig.ofQuery(batchSize).
*
* <p>
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
* <p>
* The queryBatchSize is the number of parent id's that this separate query
* will load per batch.
* </p>
* <p>
* This will load all beans on this path eagerly unless a {@link #lazy(int)}
* is also used.
* </p>
*
* @param batchSize the batch size used to load beans on this path
*/
@@ -171,13 +169,12 @@ public class FetchConfig implements Serializable {
/**
* Deprecated - migrate to FetchConfig.ofQuery(batchSize).
*
* <p>
* Eagerly fetch the first batch of beans on this path.
* This is similar to {@link #query(int)} but only fetches the first batch.
* <p>
* If there are more parent beans than the batch size then they will not be
* loaded eagerly but instead use lazy loading.
* </p>
*
* @param batchSize the number of parent beans this path is populated for
*/
@@ -188,7 +185,7 @@ public class FetchConfig implements Serializable {
/**
* Deprecated - migrate to FetchConfig.ofCache().
*
* <p>
* Eagerly fetch the beans fetching the beans from the L2 bean cache
* and using the DB for beans not in the cache.
*/
+2 -2
View File
@@ -37,7 +37,7 @@ import java.util.List;
* }
* }
*
* Entity
* @Entity
* public class Customer extends BaseModel {
*
* public static final CustomerFinder find = new CustomerFinder();
@@ -80,7 +80,7 @@ public class Finder<I, T> {
* // ... add extra customer specific finder methods
* }
*
* Entity
* @Entity
* public class Customer extends BaseModel {
*
* public static final CustomerFinder find = new CustomerFinder();
+6 -6
View File
@@ -32,16 +32,16 @@ import io.ebean.bean.EntityBean;
* // Typically there is a common base model that has some
* // common properties like the ones below
*
* MappedSuperclass
* @MappedSuperclass
* public class BaseModel extends Model {
*
* Id Long id;
* @Id Long id;
*
* Version Long version;
* @Version Long version;
*
* WhenCreated Timestamp whenCreated;
* @WhenCreated Timestamp whenCreated;
*
* WhenUpdated Timestamp whenUpdated;
* @WhenUpdated Timestamp whenUpdated;
*
* ...
* }
@@ -52,7 +52,7 @@ import io.ebean.bean.EntityBean;
*
* // Extend the mappedSuperclass
*
* Entity Table(name="o_account")
* @Entity @Table(name="o_account")
* public class Customer extends BaseModel {
*
* String name;
+1 -1
View File
@@ -18,7 +18,7 @@ import java.util.List;
*
* // where a bean is annotated with a complex
* // natural key made of several properties
* Cache(naturalKey = {"store","code","sku"})
* @Cache(naturalKey = {"store","code","sku"})
*
*
* Pairs pairs = new Pairs("sku", "code");
+18 -3
View File
@@ -810,7 +810,7 @@ public interface Query<T> {
* </p>
* <p>
* This method is functionally equivalent to findIterate() but instead of using an
* iterator uses the Consumer interface which is better suited to use with Java8 closures.
* iterator uses the Consumer interface which is better suited to use with closures.
* </p>
* <pre>{@code
*
@@ -829,6 +829,21 @@ public interface Query<T> {
*/
void findEach(Consumer<T> consumer);
/**
* Execute findEach streaming query batching the results for consuming.
* <p>
* This query execution will stream the results and is suited to consuming
* large numbers of results from the database.
* <p>
* Typically we use this batch consumer when we want to do further processing on
* the beans and want to do that processing in batch form, for example - 100 at
* a time.
*
* @param batch The number of beans processed in the batch
* @param consumer Process the batch of beans
*/
void findEach(int batch, Consumer<List<T>> consumer);
/**
* Execute the query using callbacks to a visitor to process the resulting
* beans one at a time.
@@ -839,12 +854,12 @@ public interface Query<T> {
* </p>
* <p>
* This method is functionally equivalent to findIterate() but instead of using an
* iterator uses the Predicate (SAM) interface which is better suited to use with Java8 closures.
* iterator uses the Predicate interface which is better suited to use with closures.
* </p>
* <pre>{@code
*
* DB.find(Customer.class)
* .fetch("contacts", FetchConfig.ofQuery(2))
* .fetchQuery("contacts")
* .where().eq("status", Status.NEW)
* .order().asc("id")
* .setMaxRows(2000)
+4 -4
View File
@@ -41,14 +41,14 @@ package io.ebean;
* <h3>Example OrderAggregate</h3>
* <pre>{@code
* ...
* // Sql indicates to that this bean
* // @Sql indicates to that this bean
* // is based on RawSql rather than a table
*
* Entity
* Sql
* @Entity
* @Sql
* public class OrderAggregate {
*
* OneToOne
* @OneToOne
* Order order;
*
* Double totalAmount;
@@ -22,7 +22,7 @@ import java.sql.SQLException;
* //
* class CustomerMapper implements RowMapper<CustomerDto> {
*
* Override
* @Override
* public CustomerDto map(ResultSet rset, int rowNum) throws SQLException {
*
* long id = rset.getLong(1);
@@ -59,6 +59,14 @@ public interface Transaction extends AutoCloseable {
*/
void register(TransactionCallback callback);
/**
* EXPERIMENTAL - turn on automatic persistence of dirty beans and batchMode true.
* <p>
* With this turned on beans that are dirty in the persistence context
* are automatically persisted on flush() and commit().
*/
void setAutoPersistUpdates(boolean autoPersistUpdates);
/**
* Set a label on the transaction.
* <p>
@@ -339,15 +347,12 @@ public interface Transaction extends AutoCloseable {
* The batch is automatically flushed when it hits the batch size and also when we
* execute queries or when we mix UpdateSql and CallableSql with save and delete of
* beans.
* </p>
* <p>
* We use {@link #flush()} to explicitly flush the batch and we can use
* {@link #setFlushOnQuery(boolean)} and {@link #setFlushOnMixed(boolean)}
* to control the automatic flushing behaviour.
* </p>
* <p>
* Example: batch processing of CallableSql executing every 10 rows
* </p>
*
* <pre>{@code
*
@@ -392,14 +397,11 @@ public interface Transaction extends AutoCloseable {
* <p>
* This only takes effect when batch mode on the transaction has not already meant that
* JDBC batch mode is being used.
* </p>
* <p>
* This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades
* and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch.
* </p>
* <p>
* This is effectively already turned on for all platforms apart from older Sql Server.
* </p>
*
* @param batchMode the batch mode to use per save(), insert(), update() or delete()
* @see io.ebean.config.DatabaseConfig#setPersistBatchOnCascade(PersistBatch)
@@ -422,15 +424,19 @@ public interface Transaction extends AutoCloseable {
int getBatchSize();
/**
* Specify if you want batched inserts to use getGeneratedKeys.
* Specify if we want batched inserts to use getGeneratedKeys.
* <p>
* By default batched inserts will try to use getGeneratedKeys if it is
* supported by the underlying jdbc driver and database.
* </p>
* <p>
* You may want to turn getGeneratedKeys off when you are inserting a large
* number of objects and you don't care about getting back the ids.
* </p>
* We want to turn off getGeneratedKeys when we are inserting a large
* number of objects and we don't care about getting back the ids. In this
* way we avoid the extra cost of getting back the generated id values
* from the database.
* <p>
* Note that when we do turn off getGeneratedKeys then we have the limitation
* that after a bean has been inserted we are unable to then mutate the bean
* and update it in the same transaction as we have not obtained it's id value.
*/
void setGetGeneratedKeys(boolean getGeneratedKeys);
@@ -449,13 +455,11 @@ public interface Transaction extends AutoCloseable {
* <p>
* If you want to execute both WITHOUT having the batch automatically flush
* you need to call this with batchFlushOnMixed = false.
* </p>
* <p>
* Note that UpdateSql and CallableSql are ALWAYS executed first (before the
* beans are executed). This is because the UpdateSql and CallableSql have
* already been bound to their PreparedStatements. The beans on the other hand
* have a 2 step process (delayed binding).
* </p>
*/
void setFlushOnMixed(boolean batchFlushOnMixed);
@@ -473,7 +477,6 @@ public interface Transaction extends AutoCloseable {
* <p>
* Calling this method with batchFlushOnQuery = false means that you can
* execute a query and the batch will not be automatically flushed.
* </p>
*/
void setFlushOnQuery(boolean batchFlushOnQuery);
@@ -490,7 +493,6 @@ public interface Transaction extends AutoCloseable {
* should be flushed prior to executing a query.
* <p>
* The default is for this to be true.
* </p>
*/
boolean isFlushOnQuery();
@@ -507,7 +509,6 @@ public interface Transaction extends AutoCloseable {
* flush the batch if you like.
* <p>
* Flushing occurs automatically when:
* </p>
* <ul>
* <li>the batch size is reached</li>
* <li>A query is executed on the same transaction</li>
@@ -533,11 +534,9 @@ public interface Transaction extends AutoCloseable {
* commit() rollback() and end() methods on the Transaction should still be
* used. Calling these methods on the Connection would be a big no no unless
* you know what you are doing.
* </p>
* <p>
* Examples of when a developer may wish to use the connection directly are:
* Savepoints, advanced CLOB BLOB use and advanced stored procedure calls.
* </p>
*/
Connection getConnection();
@@ -545,17 +544,14 @@ public interface Transaction extends AutoCloseable {
* Add table modification information to the TransactionEvent.
* <p>
* Use this in conjunction with getConnection() and raw JDBC.
* </p>
* <p>
* This effectively informs Ebean of the data that has been changed by the
* transaction and this information is normally automatically handled by Ebean
* when you save entity beans or use UpdateSql etc.
* </p>
* <p>
* If you use raw JDBC then you can use this method to inform Ebean for the
* tables that have been modified. Ebean uses this information to keep its
* caches in synch and maintain text indexes.
* </p>
*/
void addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
@@ -2,6 +2,7 @@ package io.ebean;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.TxIsolation;
import io.ebean.annotation.TxOption;
import io.ebean.annotation.TxType;
import java.util.ArrayList;
@@ -33,6 +34,8 @@ public final class TxScope {
private TxIsolation isolation;
private TxOption autoPersistUpdates;
private PersistBatch batch;
private PersistBatch batchOnCascade;
@@ -123,6 +126,13 @@ public final class TxScope {
+ "] serverName[" + serverName + "] rollbackFor[" + rollbackFor + "] noRollbackFor[" + noRollbackFor + "]";
}
/**
* Return the AutoPersistUpdates mode as a nullable Boolean.
*/
public Boolean getAutoPersistUpdates() {
return autoPersistUpdates == null ? null : autoPersistUpdates.asBoolean();
}
/**
* Return true if PersistBatch has been set.
*/
@@ -176,6 +186,14 @@ public final class TxScope {
return this;
}
/**
* Set the autoPersistUpdates mode.
*/
public TxScope setAutoPersistUpdates(TxOption autoPersistUpdates) {
this.autoPersistUpdates = autoPersistUpdates;
return this;
}
/**
* Return the transaction profile id.
*/
+6 -6
View File
@@ -13,23 +13,23 @@ package io.ebean;
* </p>
* <pre>{@code
* ...
* NamedUpdates(value = {
* NamedUpdate(
* @NamedUpdates(value = {
* @NamedUpdate(
* name = "setTitle",
* notifyCache = false,
* update = "update topic set title = :title, postCount = :count where id = :id"),
* NamedUpdate(
* @NamedUpdate(
* name = "setPostCount",
* notifyCache = false,
* update = "update f_topic set post_count = :postCount where id = :id"),
* NamedUpdate(
* @NamedUpdate(
* name = "incrementPostCount",
* notifyCache = false,
* update = "update Topic set postCount = postCount + 1 where id = :id")
* //update = "update f_topic set post_count = post_count + 1 where id = :id")
* })
* Entity
* Table(name = "f_topic")
* @Entity
* @Table(name = "f_topic")
* public class Topic {
* ...
* }
@@ -1,5 +1,7 @@
package io.ebean.bean;
import java.util.List;
/**
* Holds entity beans by there type and id.
* <p>
@@ -77,6 +79,11 @@ public interface PersistenceContext {
*/
boolean resetLimit();
/**
* Return the list of dirty beans held by this persistence context.
*/
List<Object> dirtyBeans();
/**
* Wrapper on a bean to also indicate if a bean has been deleted.
* <p>
@@ -87,7 +87,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public void internalAddWithCheck(Object bean) {
if (list == null || !containsInstance(bean)) {
if (list == null || bean == null || !containsInstance(bean)) {
internalAdd(bean);
}
}
@@ -77,7 +77,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
public void internalPutWithCheck(Object key, Object bean) {
if (map == null || !map.containsKey(key)) {
if (map == null || key == null || !map.containsKey(key)) {
internalPut(key, bean);
}
}
@@ -70,9 +70,8 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
@Override
public void internalAddWithCheck(Object bean) {
if (set == null || !set.contains(bean)) {
internalAdd(bean);
}
// set add() already de-dups so just add it
internalAdd(bean);
}
@Override
@@ -7,6 +7,8 @@ import javax.persistence.DiscriminatorValue;
import javax.persistence.Inheritance;
import javax.persistence.Table;
import static io.ebean.util.StringHelper.isNull;
/**
* Provides some base implementation for NamingConventions.
*
@@ -78,10 +80,16 @@ public abstract class AbstractNamingConvention implements NamingConvention {
@Override
public String getSequenceName(String rawTableName, String pkColumn) {
final String tableNameUnquoted = databasePlatform.unQuote(rawTableName);
TableName tableName = new TableName(rawTableName);
String seqName = seqName(pkColumn, tableName.getName());
return tableName.withCatalogAndSchema(seqName);
}
private String seqName(String pkColumn, String tableName) {
final String tableNameUnquoted = unQuote(tableName);
String seqName = sequenceFormat.replace("{table}", tableNameUnquoted);
pkColumn = (pkColumn == null) ? "" : databasePlatform.unQuote(pkColumn);
return seqName.replace("{column}", pkColumn);
pkColumn = (pkColumn == null) ? "" : unQuote(pkColumn);
return quoteIdentifiers(seqName.replace("{column}", pkColumn));
}
/**
@@ -216,15 +224,13 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|| AnnotationUtil.has(supCls, DiscriminatorValue.class);
}
@Override
public TableName getM2MJoinTableName(TableName lhsTable, TableName rhsTable) {
StringBuilder buffer = new StringBuilder();
buffer.append(lhsTable.getName());
buffer.append(unQuote(lhsTable.getName()));
buffer.append("_");
String rhsTableName = rhsTable.getName();
String rhsTableName = unQuote(rhsTable.getName());
if (rhsTableName.indexOf('_') < rhsPrefixLength) {
// trim off a xx_ prefix if there is one
rhsTableName = rhsTableName.substring(rhsTableName.indexOf('_') + 1);
@@ -238,7 +244,13 @@ public abstract class AbstractNamingConvention implements NamingConvention {
buffer.setLength(maxTableNameLength);
}
return new TableName(lhsTable.getCatalog(), lhsTable.getSchema(), buffer.toString());
String tableName = quoteIdentifiers(buffer.toString());
return new TableName(lhsTable.getCatalog(), lhsTable.getSchema(), tableName);
}
@Override
public String deriveM2MColumn(String tableName, String dbColumn) {
return quoteIdentifiers(unQuote(tableName) +"_" + unQuote(dbColumn));
}
/**
@@ -255,14 +267,29 @@ public abstract class AbstractNamingConvention implements NamingConvention {
return null;
}
@Override
public String getTableName(String catalog, String schema, String name) {
StringBuilder sb = new StringBuilder();
if (!isNull(catalog)) {
sb.append(quoteIdentifiers(catalog)).append(".");
}
if (!isNull(schema)) {
sb.append(quoteIdentifiers(schema)).append(".");
}
return sb.append(quoteIdentifiers(name)).toString();
}
/**
* Replace back ticks (if they are used) with database platform specific
* quoted identifiers.
* Replace back ticks (if they are used) with database platform specific quoted identifiers.
*/
protected String quoteIdentifiers(String s) {
return databasePlatform.convertQuotedIdentifiers(s);
}
private String unQuote(String val) {
return databasePlatform.unQuote(val);
}
/**
* Checks string is null or empty .
*/
@@ -232,6 +232,12 @@ public class DatabaseConfig {
*/
private String historyTableSuffix = "_history";
/**
* When true explicit transactions beans that have been made dirty will be
* automatically persisted via update on flush.
*/
private boolean autoPersistUpdates;
/**
* Use for transaction scoped batch mode.
*/
@@ -498,14 +504,23 @@ public class DatabaseConfig {
private boolean notifyL2CacheInForeground;
/**
* Set to true to support query plan capture.
* Set to true to enable bind capture required for query plan capture.
*/
private boolean collectQueryPlans;
private boolean queryPlanEnable;
/**
* The default threshold in micros for collecting query plans.
*/
private long collectQueryPlanThresholdMicros = Long.MAX_VALUE;
private long queryPlanThresholdMicros = Long.MAX_VALUE;
/**
* Set to true to enable automatic periodic query plan capture.
*/
private boolean queryPlanCapture;
private long queryPlanCapturePeriodSecs = 60 * 10; // 10 minutes
private long queryPlanCaptureMaxTimeMillis = 10_000; // 10 seconds
private int queryPlanCaptureMaxCount = 10;
private QueryPlanListener queryPlanListener;
/**
* The time in millis used to determine when a query is alerted for being slow.
@@ -896,6 +911,20 @@ public class DatabaseConfig {
this.tenantCatalogProvider = tenantCatalogProvider;
}
/**
* Return true if dirty beans are automatically persisted.
*/
public boolean isAutoPersistUpdates() {
return autoPersistUpdates;
}
/**
* Set to true if dirty beans are automatically persisted.
*/
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
this.autoPersistUpdates = autoPersistUpdates;
}
/**
* Return the PersistBatch mode to use by default at the transaction level.
* <p>
@@ -1045,7 +1074,6 @@ public class DatabaseConfig {
* This is a performance optimisation to reduce the number times Ebean
* requests a sequence to be used as an Id for a bean (aka reduce network
* chatter).
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
@@ -2693,7 +2721,10 @@ public class DatabaseConfig {
}
/**
* Load settings from ebean.properties.
* Load settings from application.properties, application.yaml and other sources.
* <p>
* Uses <code>avaje-config</code> to load configuration properties. Goto https://avaje.io/config
* for detail on how and where properties are loaded from.
*/
public void loadFromProperties() {
this.properties = Config.asProperties();
@@ -2795,14 +2826,20 @@ public class DatabaseConfig {
}
loadDocStoreSettings(p);
defaultServer = p.getBoolean("defaultServer", defaultServer);
autoPersistUpdates = p.getBoolean("autoPersistUpdates", autoPersistUpdates);
loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
maxCallStack = p.getInt("maxCallStack", maxCallStack);
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans);
collectQueryPlanThresholdMicros = p.getLong("collectQueryPlanThresholdMicros", collectQueryPlanThresholdMicros);
queryPlanEnable = p.getBoolean("queryPlan.enable", queryPlanEnable);
queryPlanThresholdMicros = p.getLong("queryPlan.thresholdMicros", queryPlanThresholdMicros);
queryPlanCapture = p.getBoolean("queryPlan.capture", queryPlanCapture);
queryPlanCapturePeriodSecs = p.getLong("queryPlan.capturePeriodSecs", queryPlanCapturePeriodSecs);
queryPlanCaptureMaxTimeMillis = p.getLong("queryPlan.captureMaxTimeMillis", queryPlanCaptureMaxTimeMillis);
queryPlanCaptureMaxCount = p.getInt("queryPlan.captureMaxCount", queryPlanCaptureMaxCount);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
localOnlyL2Cache = p.getBoolean("localOnlyL2Cache", localOnlyL2Cache);
@@ -3092,14 +3129,17 @@ public class DatabaseConfig {
}
/**
* Return the query plan time to live.
* Return the time to live for ebean's internal query plan.
*/
public int getQueryPlanTTLSeconds() {
return queryPlanTTLSeconds;
}
/**
* Set the query plan time to live.
* Set the time to live for ebean's internal query plan.
* <p>
* This is the plan that knows how to execute the query, read the result
* and collects execution metrics. By default this is set to 5 mins.
*/
public void setQueryPlanTTLSeconds(int queryPlanTTLSeconds) {
this.queryPlanTTLSeconds = queryPlanTTLSeconds;
@@ -3172,29 +3212,110 @@ public class DatabaseConfig {
/**
* Return true if query plan capture is enabled.
*/
public boolean isCollectQueryPlans() {
return collectQueryPlans;
public boolean isQueryPlanEnable() {
return queryPlanEnable;
}
/**
* Set to true to enable query plan capture.
*/
public void setCollectQueryPlans(boolean collectQueryPlans) {
this.collectQueryPlans = collectQueryPlans;
public void setQueryPlanEnable(boolean queryPlanEnable) {
this.queryPlanEnable = queryPlanEnable;
}
/**
* Return the query plan collection threshold in microseconds.
*/
public long getCollectQueryPlanThresholdMicros() {
return collectQueryPlanThresholdMicros;
public long getQueryPlanThresholdMicros() {
return queryPlanThresholdMicros;
}
/**
* Set the query plan collection threshold in microseconds.
* <p>
* Queries executing slower than this will have bind values captured such that later
* the query plan can be captured and reported.
*/
public void setCollectQueryPlanThresholdMicros(long collectQueryPlanThresholdMicros) {
this.collectQueryPlanThresholdMicros = collectQueryPlanThresholdMicros;
public void setQueryPlanThresholdMicros(long queryPlanThresholdMicros) {
this.queryPlanThresholdMicros = queryPlanThresholdMicros;
}
/**
* Return true if periodic capture of query plans is enabled.
*/
public boolean isQueryPlanCapture() {
return queryPlanCapture;
}
/**
* Set to true to turn on periodic capture of query plans.
*/
public void setQueryPlanCapture(boolean queryPlanCapture) {
this.queryPlanCapture = queryPlanCapture;
}
/**
* Return the frequency to capture query plans.
*/
public long getQueryPlanCapturePeriodSecs() {
return queryPlanCapturePeriodSecs;
}
/**
* Set the frequency in seconds to capture query plans.
*/
public void setQueryPlanCapturePeriodSecs(long queryPlanCapturePeriodSecs) {
this.queryPlanCapturePeriodSecs = queryPlanCapturePeriodSecs;
}
/**
* Return the time after which a capture query plans request will
* stop capturing more query plans.
* <p>
* Effectively this controls the amount of load/time we want to
* allow for query plan capture.
*/
public long getQueryPlanCaptureMaxTimeMillis() {
return queryPlanCaptureMaxTimeMillis;
}
/**
* Set the time after which a capture query plans request will
* stop capturing more query plans.
* <p>
* Effectively this controls the amount of load/time we want to
* allow for query plan capture.
*/
public void setQueryPlanCaptureMaxTimeMillis(long queryPlanCaptureMaxTimeMillis) {
this.queryPlanCaptureMaxTimeMillis = queryPlanCaptureMaxTimeMillis;
}
/**
* Return the max number of query plans captured per request.
*/
public int getQueryPlanCaptureMaxCount() {
return queryPlanCaptureMaxCount;
}
/**
* Set the max number of query plans captured per request.
*/
public void setQueryPlanCaptureMaxCount(int queryPlanCaptureMaxCount) {
this.queryPlanCaptureMaxCount = queryPlanCaptureMaxCount;
}
/**
* Return the listener used to process captured query plans.
*/
public QueryPlanListener getQueryPlanListener() {
return queryPlanListener;
}
/**
* Set the listener used to process captured query plans.
*/
public void setQueryPlanListener(QueryPlanListener queryPlanListener) {
this.queryPlanListener = queryPlanListener;
}
/**
@@ -3248,7 +3369,7 @@ public class DatabaseConfig {
this.loadModuleInfo = loadModuleInfo;
}
public enum UuidVersion {
public enum UuidVersion {
VERSION4,
VERSION1,
VERSION1RND
@@ -15,7 +15,7 @@ package io.ebean.config;
*
* public class EbeanConfigProvider implements DatabaseConfigProvider {
*
* Override
* @Override
* public void apply(DatabaseConfig config) {
*
* // register the entity bean classes explicitly
@@ -39,11 +39,6 @@ public class MatchingNamingConvention extends AbstractNamingConvention {
return new TableName(quoteIdentifiers(getCatalog()), quoteIdentifiers(getSchema()), quoteIdentifiers(beanClass.getSimpleName()));
}
@Override
public String getPropertyFromColumn(Class<?> beanClass, String dbColumnName) {
return dbColumnName;
}
@Override
public String getForeignKey(String prefix, String fkProperty) {
prefix = databasePlatform.unQuote(prefix);
@@ -7,13 +7,8 @@ import java.util.List;
*/
public interface ModuleInfoLoader {
/**
* Return the entity classes to register with the default DB.
*/
List<Class<?>> entityClasses();
/**
* Return entity classes to register for a named DB (not default DB).
*/
List<Class<?>> entityClassesFor(String dbName);
List<Class<?>> classesFor(String dbName, boolean defaultServer);
}
@@ -53,6 +53,16 @@ public interface NamingConvention {
*/
TableName getM2MJoinTableName(TableName lhsTable, TableName rhsTable);
/**
* Derive a DB Column from a FK table and column.
*/
String deriveM2MColumn(String tableName, String dbColumn);
/**
* Return the full table name taking into account quoted identifiers.
*/
String getTableName(String catalog, String schema, String name);
/**
* Return the column name given the property name.
*
@@ -60,18 +70,6 @@ public interface NamingConvention {
*/
String getColumnFromProperty(Class<?> beanClass, String propertyName);
/**
* Return the property name from the column name.
* <p>
* This is used to help mapping of raw SQL queries onto bean properties.
* </p>
*
* @param beanClass the bean class
* @param dbColumnName the db column name
* @return the property name from the column name
*/
String getPropertyFromColumn(Class<?> beanClass, String dbColumnName);
/**
* Return the sequence name given the table name (for DB's that use sequences).
* <p>
@@ -0,0 +1,34 @@
package io.ebean.config;
import io.ebean.Database;
import io.ebean.meta.MetaQueryPlan;
import java.util.List;
/**
* The captured query plans.
*/
public class QueryPlanCapture {
private final Database database;
private final List<MetaQueryPlan> plans;
public QueryPlanCapture(Database database, List<MetaQueryPlan> plans) {
this.database = database;
this.plans = plans;
}
/**
* Return the database the plans were captured for.
*/
public Database getDatabase() {
return database;
}
/**
* Return the captured query plans.
*/
public List<MetaQueryPlan> getPlans() {
return plans;
}
}
@@ -0,0 +1,13 @@
package io.ebean.config;
/**
* EXPERIMENTAL: Listener for captured query plans.
*/
@FunctionalInterface
public interface QueryPlanListener {
/**
* Process the captured query plans.
*/
void process(QueryPlanCapture capture);
}
@@ -16,7 +16,7 @@ package io.ebean.config;
*
* public class EbeanConfigProvider implements ServerConfigProvider {
*
* Override
* @Override
* public void apply(ServerConfig config) {
*
* // register the entity bean classes explicitly
@@ -20,7 +20,7 @@ public final class TableName {
/**
* The name.
*/
private String name;
private final String name;
/**
* Construct with the given catalog schema and table name.
@@ -29,7 +29,6 @@ public final class TableName {
* </p>
*/
public TableName(String catalog, String schema, String name) {
super();
this.catalog = catalog != null ? catalog.trim() : null;
this.schema = schema != null ? schema.trim() : null;
this.name = name != null ? name.trim() : null;
@@ -110,14 +109,11 @@ public final class TableName {
* @return the qualified name
*/
public String getQualifiedName() {
StringBuilder buffer = new StringBuilder();
// Add catalog
if (catalog != null) {
buffer.append(catalog);
}
// Add schema
if (schema != null) {
if (buffer.length() > 0) {
@@ -125,31 +121,27 @@ public final class TableName {
}
buffer.append(schema);
}
if (buffer.length() > 0) {
buffer.append(".");
}
buffer.append(name);
return buffer.toString();
return buffer.append(name).toString();
}
/**
* Append a catalog and schema prefix if they exist to the string builder.
*/
public void appendCatalogAndSchema(StringBuilder buffer) {
if (catalog != null) {
buffer.append(catalog).append(".");
}
public String withCatalogAndSchema(String name) {
if (schema != null) {
buffer.append(schema).append(".");
name = schema + "." + name;
}
if (catalog != null) {
name = catalog + "." + name;
}
return name;
}
/**
* Checks if is table name is valid i.e. it has at least a name.
*
* @return true, if is valid
*/
public boolean isValid() {
return name != null && !name.isEmpty();
@@ -60,18 +60,6 @@ public class UnderscoreNamingConvention extends AbstractNamingConvention {
return toUnderscoreFromCamel(propertyName);
}
/**
* Converts underscore based column name to Camel case property name.
*
* @param beanClass the bean class
* @param dbColumnName the db column name
* @return the property from column
*/
@Override
public String getPropertyFromColumn(Class<?> beanClass, String dbColumnName) {
return toCamelFromUnderscore(dbColumnName);
}
/**
* Return true if the result will be upper case.
* <p>
@@ -79,7 +79,7 @@ public class DbDefaultValue {
}
/**
* This method checks & convert the {@link DbDefault#value()} to a valid SQL literal.
* This method checks and converts the {@link DbDefault#value()} to a valid SQL literal.
*
* This is mainly to quote string literals and verify integer/dates for correctness.
* <p>
@@ -13,6 +13,7 @@ public class Oracle11Platform extends OraclePlatform {
this.platform = Platform.ORACLE11;
this.columnAliasPrefix = "c";
this.sqlLimiter = new OracleRownumSqlLimiter();
this.basicSqlLimiter = new OracleRownumBasicLimiter();
dbIdentity.setIdType(IdType.SEQUENCE);
}
}
@@ -0,0 +1,35 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.config.dbplatform.BasicSqlLimiter;
/**
* Row limiter for Oracle 9,10,11 using rownum.
*/
public class OracleRownumBasicLimiter implements BasicSqlLimiter {
@Override
public String limit(String dbSql, int firstRow, int maxRows) {
if (firstRow < 1 && maxRows < 1) {
return dbSql;
}
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int lastRow = maxRows;
if (lastRow > 0) {
lastRow += firstRow;
}
sb.append("select * from (select ");
if (maxRows > 0) {
sb.append("/*+ FIRST_ROWS(").append(maxRows).append(") */ ");
}
sb.append("a.*, rownum rn_ from (");
sb.append(dbSql).append(") a ");
if (lastRow > 0) {
sb.append(" where rownum <= ").append(lastRow);
}
sb.append(") ");
if (firstRow > 0) {
sb.append(" where rn_ > ").append(firstRow);
}
return sb.toString();
}
}
@@ -41,6 +41,9 @@ public class QueryPlanRequest {
/**
* Set the maximum number of plans to capture.
* <p>
* Use this to limit how much query plan capturing is done as query
* plan capture is actual database load.
*/
public void setMaxCount(int maxCount) {
this.maxCount = maxCount;
@@ -58,7 +61,9 @@ public class QueryPlanRequest {
/**
* Set the maximum amount of time we want to use to capture plans.
* <p>
* Query plan collection will stop once this time is exceeded.
* Query plan collection will stop once this time is exceeded. We use
* this to ensure the query plan capture does not use excessive amount
* of time - put too much load on the database.
*/
public void setMaxTimeMillis(long maxTimeMillis) {
this.maxTimeMillis = maxTimeMillis;
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.7.1</version>
<version>12.8.2</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.7.1</tag>
<tag>ebean-parent-12.8.2</tag>
</scm>
<name>ebean autotune</name>
@@ -26,7 +26,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
<scope>provided</scope>
</dependency>
+17 -17
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.7.1</version>
<version>12.8.2</version>
</parent>
<name>ebean bom</name>
@@ -18,8 +18,8 @@
<ebean-migration.version>12.4.0</ebean-migration.version>
<ebean-test-docker.version>4.1</ebean-test-docker.version>
<ebean-datasource.version>7.0</ebean-datasource.version>
<ebean-agent.version>12.6.6</ebean-agent.version>
<ebean-maven-plugin.version>12.6.6</ebean-maven-plugin.version>
<ebean-agent.version>12.8.2</ebean-agent.version>
<ebean-maven-plugin.version>12.8.2</ebean-maven-plugin.version>
</properties>
<dependencyManagement>
@@ -81,88 +81,88 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-xml</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-autotune</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.7.1</version>
<version>12.8.2</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
+6 -6
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.7.1</version>
<version>12.8.2</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.7.1</tag>
<tag>ebean-parent-12.8.2</tag>
</scm>
<profiles>
@@ -87,19 +87,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.7.1</version>
<version>12.8.2</version>
</dependency>
<dependency>
@@ -302,7 +302,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.6.6</version>
<version>12.8.2</version>
<executions>
<execution>
<id>test</id>
@@ -1,52 +1,25 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.expression.IdInExpression;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Used for bean cache lookup with where ids in expression.
* Process Cache lookup by Id(s).
*/
public class CacheIdLookup<T> {
private final IdInExpression idInExpression;
private int remaining;
public CacheIdLookup(IdInExpression idInExpression) {
this.idInExpression = idInExpression;
}
public interface CacheIdLookup<T> {
/**
* Return the Id values for the in expression.
* Return the Id values to lookup against the L2 cache.
*/
public Collection<?> idValues() {
return idInExpression.idValues();
}
Collection<?> idValues();
/**
* Process the hits returning the beans fetched from cache and
* adjusting the in expression (to not fetch the hits).
* Remove the hits returning the beans fetched from L2 cache.
*/
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
List<T> removeHits(BeanCacheResult<T> cacheResult);
Set<Object> hitIds = new HashSet<>();
List<T> beans = new ArrayList<>(hitIds.size());
for (BeanCacheResult.Entry<T> hit : cacheResult.hits()) {
hitIds.add(hit.getKey());
beans.add(hit.getBean());
}
this.remaining = idInExpression.removeIds(hitIds);
return beans;
}
public boolean allHits() {
return remaining == 0;
}
/**
* Return true if all beans where found in L2 cache.
*/
boolean allHits();
}
@@ -0,0 +1,55 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.expression.IdInExpression;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Used for bean cache lookup with where ids in expression.
*/
public class CacheIdLookupMany<T> implements CacheIdLookup<T> {
private final IdInExpression idInExpression;
private int remaining;
public CacheIdLookupMany(IdInExpression idInExpression) {
this.idInExpression = idInExpression;
}
/**
* Return the Id values for the in expression.
*/
@Override
public Collection<?> idValues() {
return idInExpression.idValues();
}
/**
* Process the hits returning the beans fetched from cache and
* adjusting the in expression (to not fetch the hits).
*/
@Override
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
Set<Object> hitIds = new HashSet<>();
List<T> beans = new ArrayList<>(hitIds.size());
for (BeanCacheResult.Entry<T> hit : cacheResult.hits()) {
hitIds.add(hit.getKey());
beans.add(hit.getBean());
}
this.remaining = idInExpression.removeIds(hitIds);
return beans;
}
@Override
public boolean allHits() {
return remaining == 0;
}
}
@@ -0,0 +1,38 @@
package io.ebeaninternal.api;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Used for bean cache lookup with a single id value.
*/
public class CacheIdLookupSingle<T> implements CacheIdLookup<T> {
private final Object idValue;
private boolean found;
public CacheIdLookupSingle(Object idValue) {
this.idValue = idValue;
}
@Override
public Collection<?> idValues() {
return Collections.singleton(idValue);
}
@Override
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
final List<BeanCacheResult.Entry<T>> hits = cacheResult.hits();
if (hits.size() == 1) {
found = true;
return Collections.singletonList(hits.get(0).getBean());
}
return Collections.emptyList();
}
@Override
public boolean allHits() {
return found;
}
}
@@ -16,7 +16,7 @@ public interface LoadContext {
/**
* Return the minimum batch size when using QueryIterator with query joins.
*/
int getSecondaryQueriesMinBatchSize(int defaultQueryBatch);
int getSecondaryQueriesMinBatchSize();
/**
* Execute any secondary (+query) queries if there are any defined.
@@ -8,6 +8,11 @@ import java.util.List;
class NoopQueryPlanManager implements QueryPlanManager {
@Override
public void setDefaultThreshold(long thresholdMicros) {
// do nothing
}
@Override
public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) {
return SpiQueryBindCapture.NOOP;
@@ -12,6 +12,11 @@ public interface QueryPlanManager {
QueryPlanManager NOOP = new NoopQueryPlanManager();
/**
* Update the global default threshold used when new query plans are created.
*/
void setDefaultThreshold(long thresholdMicros);
/**
* Create the bind capture for the given query plan.
*/
@@ -73,6 +73,10 @@ public class ScopeTrans {
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
restoreBatchFlushOnQuery = transaction.isFlushOnQuery();
}
Boolean autoPersistUpdates = txScope.getAutoPersistUpdates();
if (autoPersistUpdates != null) {
transaction.setAutoPersistUpdates(autoPersistUpdates);
}
if (txScope.isBatchSet()) {
transaction.setBatchMode(txScope.isBatchMode());
}
@@ -264,6 +264,11 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
*/
<T> void findDtoEach(SpiDtoQuery<T> query, Consumer<T> consumer);
/**
* DTO findEach batch query.
*/
<T> void findDtoEach(SpiDtoQuery<T> query, int batch, Consumer<List<T>> consumer);
/**
* DTO findEachWhile query.
*/
@@ -1,6 +1,7 @@
package io.ebeaninternal.api;
import io.ebean.FetchConfig;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import java.util.Set;
@@ -19,4 +20,8 @@ public interface SpiQueryFetch {
*/
void fetchProperties(String name, Set<String> properties, FetchConfig config);
/**
* Add a nested fetch graph.
*/
void addNested(String name, OrmQueryDetail nestedDetail, FetchConfig config);
}
@@ -147,6 +147,11 @@ public interface SpiTransaction extends Transaction {
*/
int depth();
/**
* Return true if dirty beans are automatically persisted.
*/
boolean isAutoPersistUpdates();
/**
* Return true if this transaction was created explicitly via
* <code>Ebean.beginTransaction()</code>.
@@ -43,6 +43,16 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
return transaction.getLabel();
}
@Override
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
transaction.setAutoPersistUpdates(autoPersistUpdates);
}
@Override
public boolean isAutoPersistUpdates() {
return transaction.isAutoPersistUpdates();
}
@Override
public void commitAndContinue() {
transaction.commitAndContinue();
@@ -135,16 +135,11 @@ public class DefaultContainer implements SpiContainer {
configProvider.apply((ServerConfig)config);
}
}
if (config.isAutoLoadModuleInfo()) {
// auto register entity classes (default db)
for (ModuleInfoLoader loader : ServiceLoader.load(ModuleInfoLoader.class)) {
config.addAll(loader.entityClasses());
}
}
} else if (config.isAutoLoadModuleInfo()) {
// auto register entity classes (other named db)
}
if (config.isAutoLoadModuleInfo()) {
// auto register entity classes
for (ModuleInfoLoader loader : ServiceLoader.load(ModuleInfoLoader.class)) {
config.addAll(loader.entityClassesFor(config.getName()));
config.addAll(loader.classesFor(config.getName(), config.isDefaultServer()));
}
}
}
@@ -0,0 +1,25 @@
package io.ebeaninternal.server.core;
import io.ebean.config.QueryPlanCapture;
import io.ebean.config.QueryPlanListener;
import io.ebean.meta.MetaQueryPlan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class DefaultQueryPlanListener implements QueryPlanListener {
static final QueryPlanListener INSTANT = new DefaultQueryPlanListener();
private static final Logger log = LoggerFactory.getLogger("io.ebean.QUERYPLAN");
@Override
public void process(QueryPlanCapture capture) {
// better to log this in JSON form?
String dbName = capture.getDatabase().getName();
for (MetaQueryPlan plan : capture.getPlans()) {
log.info("queryPlan db:{} label:{} queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
dbName, plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
plan.getSql(), plan.getBind(), plan.getPlan());
}
}
}
@@ -45,12 +45,7 @@ import io.ebean.bean.PersistenceContext.WithOption;
import io.ebean.bean.SingleBeanLoader;
import io.ebean.cache.ServerCacheManager;
import io.ebean.common.CopyOnFirstWriteList;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.EncryptKeyManager;
import io.ebean.config.SlowQueryEvent;
import io.ebean.config.SlowQueryListener;
import io.ebean.config.TenantMode;
import io.ebean.config.*;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.BeanPersistController;
import io.ebean.event.ShutdownManager;
@@ -125,6 +120,7 @@ import io.ebeanservice.docstore.api.DocStoreIntegration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
@@ -137,7 +133,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -145,6 +140,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -196,7 +192,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final SpiLogManager logManager;
private final PersistenceContextScope defaultPersistenceContextScope;
private final int lazyLoadBatchSize;
private final int queryBatchSize;
private final boolean updateAllPropertiesInBatch;
private final long slowQueryMicros;
private final SlowQueryListener slowQueryListener;
@@ -217,7 +212,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.extraMetrics = config.getExtraMetrics();
this.serverName = this.config.getName();
this.lazyLoadBatchSize = this.config.getLazyLoadBatchSize();
this.queryBatchSize = this.config.getQueryBatchSize();
this.cqueryEngine = config.getCQueryEngine();
this.expressionFactory = config.getExpressionFactory();
this.encryptKeyManager = this.config.getEncryptKeyManager();
@@ -242,7 +236,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.clockService = config.getClockService();
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
this.transactionManager = config.createTransactionManager(this, docStoreComponents.updateProcessor());
this.documentStore = docStoreComponents.documentStore();
this.queryPlanManager = config.initQueryPlanManager(transactionManager);
this.metaInfoManager = new DefaultMetaInfoManager(this);
@@ -305,10 +299,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return lazyLoadBatchSize;
}
public int getQueryBatchSize() {
return queryBatchSize;
}
@Override
public Object currentTenantId() {
return currentTenantProvider == null ? null : currentTenantProvider.currentId();
@@ -410,6 +400,31 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
migrationRunner.loadProperties(config.getProperties());
migrationRunner.run(config.getDataSource());
}
startQueryPlanCapture();
}
private void startQueryPlanCapture() {
if (config.isQueryPlanCapture()) {
long secs = config.getQueryPlanCapturePeriodSecs();
if (secs > 10) {
logger.info("capture query plan enabled, every {}secs", secs);
backgroundExecutor.scheduleWithFixedDelay(this::collectQueryPlans, secs, secs, TimeUnit.SECONDS);
}
}
}
private void collectQueryPlans() {
QueryPlanRequest request = new QueryPlanRequest();
request.setMaxCount(config.getQueryPlanCaptureMaxCount());
request.setMaxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
// obtains query explain plans ...
List<MetaQueryPlan> plans = metaInfoManager.queryPlanCollectNow(request);
QueryPlanListener listener = config.getQueryPlanListener();
if (listener == null) {
listener = DefaultQueryPlanListener.INSTANT;
}
listener.process(new QueryPlanCapture(this, plans));
}
@Override
@@ -1150,6 +1165,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction) {
return Optional.ofNullable(findOne(query, transaction));
@@ -1180,6 +1196,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> Set<T> findSet(Query<T> query, Transaction t) {
@@ -1196,6 +1213,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public <K, T> Map<K, T> findMap(Query<T> query, Transaction t) {
@@ -1217,6 +1235,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
@SuppressWarnings("unchecked")
public <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction t) {
@@ -1227,7 +1246,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
try {
request.initTransIfRequired();
return (List<A>) request.findSingleAttributeList();
return request.findSingleAttributeList();
} finally {
request.endTransIfRequired();
}
@@ -1277,6 +1296,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public <A, T> List<A> findIds(Query<T> query, Transaction t) {
return findIdsWithCopy(((SpiQuery<T>) query).copy(), t);
@@ -1337,26 +1357,29 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public <T> FutureRowCount<T> findFutureCount(Query<T> q, Transaction t) {
SpiQuery<T> copy = ((SpiQuery<T>) q).copy();
copy.setFutureFetch(true);
Transaction newTxn = createTransaction();
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<T>(this, copy, newTxn));
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<>(this, copy, newTxn));
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
@Nonnull
@Override
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t) {
SpiQuery<T> copy = ((SpiQuery<T>) query).copy();
copy.setFutureFetch(true);
Transaction newTxn = createTransaction();
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<T>(this, copy, newTxn));
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<>(this, copy, newTxn));
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
@Nonnull
@Override
public <T> FutureList<T> findFutureList(Query<T> query, Transaction t) {
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
@@ -1369,11 +1392,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
// Create a new transaction solely to execute the findList() at some future time
Transaction newTxn = createTransaction();
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<T>(this, spiQuery, newTxn));
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<>(this, spiQuery, newTxn));
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
@Nonnull
@Override
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction) {
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
@@ -1387,6 +1411,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return new LimitOffsetPagedList<>(this, spiQuery);
}
@Nonnull
@Override
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
@@ -1399,11 +1424,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public <T> Stream<T> findLargeStream(Query<T> query, Transaction transaction) {
return findStream(query, transaction);
}
@Nonnull
@Override
public <T> Stream<T> findStream(Query<T> query, Transaction transaction) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, transaction);
@@ -1446,6 +1473,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
// no try finally - findEach guarantee's cleanup of the transaction if required
}
@Override
public <T> void findEach(Query<T> query, int batch, Consumer<List<T>> consumer, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
// if (request.isUseDocStore()) {
// docStore().findEach(request, consumer);
// return;
// }
request.initTransIfRequired();
request.findEach(batch, consumer);
// no try finally - findEach guarantee's cleanup of the transaction if required
}
@Override
public <T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
@@ -1458,6 +1497,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
// no try finally - findEachWhile guarantee's cleanup of the transaction if required
}
@Nonnull
@Override
public <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, transaction);
@@ -1469,6 +1509,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public <T> List<T> findList(Query<T> query, Transaction t) {
return findList(query, t, false);
@@ -1528,6 +1569,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Nonnull
@Override
public List<SqlRow> findList(SqlQuery query, Transaction t) {
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
@@ -1585,6 +1627,17 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Override
public <T> void findDtoEach(SpiDtoQuery<T> query, int batch, Consumer<List<T>> consumer) {
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
try {
request.initTransIfRequired();
request.findEach(batch, consumer);
} finally {
request.endTransIfRequired();
}
}
@Override
public <T> void findDtoEachWhile(SpiDtoQuery<T> query, Predicate<T> consumer) {
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
@@ -2218,6 +2271,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return checkUniqueness(bean, null);
}
@Nonnull
@Override
public Set<Property> checkUniqueness(Object bean, Transaction transaction) {
EntityBean entityBean = checkEntityBean(bean);
@@ -2229,14 +2283,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
Object id = idProperty.getVal(entityBean);
if (entityBean._ebean_getIntercept().isNew() && id != null) {
// Primary Key is changeable only on new models - so skip check if we are not
// new.
// Primary Key is changeable only on new models - so skip check if we are not new
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
query.setId(id);
if (findCount(query, transaction) > 0) {
Set<Property> ret = new HashSet<>();
ret.add(idProperty);
return ret;
return Collections.singleton(idProperty);
}
}
for (BeanProperty[] props : beanDesc.getUniqueProps()) {
@@ -2299,6 +2350,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
List<MetaQueryPlan> queryPlanInit(QueryPlanInit initRequest) {
if (initRequest.isAll()) {
queryPlanManager.setDefaultThreshold(initRequest.getThresholdMicros());
}
return beanDescriptorManager.queryPlanInit(initRequest);
}
@@ -92,6 +92,11 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
queryEngine.findEach(this, consumer);
}
public void findEach(int batch, Consumer<List<T>> consumer) {
flushJdbcBatchOnQuery();
queryEngine.findEach(this, batch, consumer);
}
public void findEachWhile(Predicate<T> consumer) {
flushJdbcBatchOnQuery();
queryEngine.findEachWhile(this, consumer);
@@ -423,13 +423,13 @@ public class InternalConfiguration {
/**
* Create the TransactionManager taking into account autoCommit mode.
*/
TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) {
TransactionManager createTransactionManager(SpiServer server, DocStoreUpdateProcessor indexUpdateProcessor) {
TransactionScopeManager scopeManager = createTransactionScopeManager();
boolean notifyL2CacheInForeground = cacheManager.isLocalL2Caching() || config.isNotifyL2CacheInForeground();
TransactionManagerOptions options =
new TransactionManagerOptions(notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
new TransactionManagerOptions(server, notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager,
tableModState, cacheNotify, clockService);
@@ -617,10 +617,10 @@ public class InternalConfiguration {
}
public QueryPlanManager initQueryPlanManager(TransactionManager transactionManager) {
if (!config.isCollectQueryPlans()) {
if (!config.isQueryPlanEnable()) {
return QueryPlanManager.NOOP;
}
long threshold = config.getCollectQueryPlanThresholdMicros();
long threshold = config.getQueryPlanThresholdMicros();
return new CQueryPlanManager(transactionManager, threshold, queryPlanLogger(databasePlatform.getPlatform()), extraMetrics);
}
@@ -173,8 +173,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
* iteration is fine.
* </p>
*/
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
return loadContext.getSecondaryQueriesMinBatchSize(defaultQueryBatch);
public int getSecondaryQueriesMinBatchSize() {
return loadContext.getSecondaryQueriesMinBatchSize();
}
/**
@@ -430,6 +430,24 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
}
}
@Override
public void findEach(int batch, Consumer<List<T>> batchConsumer) {
final List<T> buffer = new ArrayList<>(batch);
try (QueryIterator<T> it = queryEngine.findIterate(this)) {
while (it.hasNext()) {
buffer.add(it.next());
if (buffer.size() >= batch) {
batchConsumer.accept(buffer);
buffer.clear();
}
}
if (!buffer.isEmpty()) {
// consume the remainder
batchConsumer.accept(buffer);
}
}
}
@Override
public void findEachWhile(Predicate<T> consumer) {
try (QueryIterator<T> it = queryEngine.findIterate(this)) {
@@ -887,6 +887,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
}
/**
* Remove deleted beans from the persistence context early.
*/
public void removeFromPersistenceContext() {
idValue = beanDescriptor.getId(entityBean);
beanDescriptor.contextDeleted(transaction.getPersistenceContext(), idValue);
}
/**
* Aggressive L1 and L2 cache cleanup for deletes.
*/
@@ -1030,6 +1038,17 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
if (!publish) {
beanDescriptor.setDraft(entityBean);
}
if (transaction.isAutoPersistUpdates() && idValue != null) {
// with getGeneratedKeys off we will not have a idValue
beanDescriptor.contextPut(transaction.getPersistenceContext(), idValue, entityBean);
}
}
/**
* Return if persist can be skipped on the reference only bean.
*/
public boolean isSkipReference() {
return intercept.isReference() || (Flags.isRecurse(flags) && beanDescriptor.referenceIdPropertyOnly(intercept));
}
public boolean isReference() {
@@ -80,10 +80,15 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
<A> List<A> findIds();
/**
* Execute the find returning a QueryIterator and visitor pattern.
* Execute findEach iterating results one bean at a time.
*/
void findEach(Consumer<T> consumer);
/**
* Execute findEach with a batch consumer.
*/
void findEach(int batch, Consumer<List<T>> batchConsumer);
/**
* Execute the find returning a QueryIterator and visitor pattern.
*/
@@ -727,7 +727,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
String[] cols = indexDef.getColumns();
BeanProperty[] props = new BeanProperty[cols.length];
for (int i = 0; i < cols.length; i++) {
String propName = findBeanPath("", cols[i]);
String propName = findBeanPath("", "", cols[i]);
if (propName == null) {
return;
}
@@ -2472,13 +2472,16 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Return the property path given the db table and column.
*/
public String findBeanPath(String tableName, String columnName) {
if (tableName.isEmpty() || tableName.equalsIgnoreCase(baseTable)) {
public String findBeanPath(String schemaName, String tableName, String columnName) {
if (matchBaseTable(tableName)) {
return columnPath.get(columnName);
}
BeanPropertyAssoc<?> assocProperty = tablePath.get(tableName);
if (assocProperty == null) {
assocProperty = tablePath.get(schemaName + "." + tableName);
}
if (assocProperty != null) {
String relativePath = assocProperty.getTargetDescriptor().findBeanPath(tableName, columnName);
String relativePath = assocProperty.getTargetDescriptor().findBeanPath(schemaName, tableName, columnName);
if (relativePath != null) {
return SplitName.add(assocProperty.getName(), relativePath);
}
@@ -2486,6 +2489,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return null;
}
boolean matchBaseTable(String tableName) {
return tableName.isEmpty()
|| baseTable.equalsIgnoreCase(tableName)
|| baseTable.endsWith("." + tableName);
}
/**
* Return a 'dynamic property' used to read a formula.
*/
@@ -3125,7 +3134,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return ebi.isReference() || referenceIdPropertyOnly(ebi);
}
boolean referenceIdPropertyOnly(EntityBeanIntercept ebi) {
public boolean referenceIdPropertyOnly(EntityBeanIntercept ebi) {
return idOnlyReference && ebi.hasIdOnly(idPropertyIndex);
}
@@ -93,6 +93,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorManager.class);
private static final BeanDescComparator beanDescComparator = new BeanDescComparator();
public static final String JAVA_LANG_RECORD = "java.lang.Record";
private final ReadAnnotations readAnnotations;
private final TransientProperties transientProperties;
@@ -1418,14 +1419,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void checkInheritedClasses(Class<?> beanClass) {
Class<?> superclass = beanClass.getSuperclass();
if (Object.class.equals(superclass)) {
if (Object.class.equals(superclass) || Model.class.equals(superclass) || JAVA_LANG_RECORD.equals(superclass.getName())) {
// we got to the top of the inheritance
return;
}
if (Model.class.equals(superclass)) {
// top of the inheritance. Not enhancing Model at this stage
return;
}
if (!EntityBean.class.isAssignableFrom(superclass)) {
if (isMappedSuperWithNoProperties(superclass)) {
// ok to stop and treat just the same as Object.class
@@ -324,9 +324,9 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
this.generatedProperty = deploy.getGeneratedProperty();
this.getter = deploy.getGetter();
this.setter = deploy.getSetter();
this.aggregation = deploy.parseAggregation();
this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null);
this.dbComment = deploy.getDbComment();
this.aggregation = deploy.parseAggregation();
this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin());
this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect());
this.formula = sqlFormulaSelect != null;
@@ -609,7 +609,8 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
String msg = "Error with the Join on [" + getFullBeanName()
+ "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?"
+ " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?";
+ " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped? "
+ " or a @JoinColumn needs an explicit referencedColumnName specified?";
throw new PersistenceException(msg);
}
}
@@ -16,6 +16,7 @@ import io.ebean.event.BeanPostLoad;
import io.ebean.event.BeanQueryAdapter;
import io.ebean.event.changelog.ChangeLogFilter;
import io.ebean.text.PathProperties;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.ConcurrencyMode;
import io.ebeaninternal.server.core.CacheOptions;
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
@@ -1149,8 +1150,21 @@ public class DeployBeanDescriptor<T> {
return null;
}
// use 'current' table alias - refer BeanProperty appendSelect() for aggregation
DeployBeanProperty property = propMap.get(expression);
return (property == null) ? null : "${ta}." + property.getDbColumn();
String[] split = SplitName.split(expression);
if (split[0] == null) {
DeployBeanProperty property = propMap.get(expression);
return (property == null) ? null : "${ta}." + property.getDbColumn();
} else {
DeployBeanProperty property = propMap.get(split[0]);
if (property instanceof DeployBeanPropertyAssoc) {
DeployBeanPropertyAssoc<?> prop = (DeployBeanPropertyAssoc<?>) property;
DeployBeanProperty beanProperty = prop.getTargetDeploy().getBeanProperty(split[1]);
if (beanProperty != null) {
return "u1." + beanProperty.getDbColumn();
}
}
return null;
}
}
/**
@@ -682,7 +682,6 @@ public class DeployBeanProperty {
*/
public void setAggregationPrefix(String prefix) {
this.aggregationPrefix = prefix;
this.aggregation = (prefix == null) ? aggregation : aggregation.replace(aggregationPrefix, "u1");
}
public String getElPrefix() {
@@ -715,7 +714,7 @@ public class DeployBeanProperty {
return sqlFormulaSelect;
}
if (aggregation != null) {
return aggregation;
return aggregationParsed == null ? dbColumn : aggregationParsed;
}
return dbColumn;
}
@@ -38,8 +38,6 @@ import javax.persistence.OrderBy;
import javax.persistence.OrderColumn;
import java.util.Set;
import static io.ebean.util.StringHelper.isNull;
/**
* Read the deployment annotation for Assoc Many beans.
*/
@@ -346,18 +344,6 @@ class AnnotationAssocManys extends AnnotationAssoc {
return append(joinTable.catalog(), joinTable.schema(), joinTable.name());
}
private String append(String catalog, String schema, String name) {
StringBuilder sb = new StringBuilder();
if (!isNull(catalog)) {
sb.append(catalog).append(".");
}
if (!isNull(schema)) {
sb.append(schema).append(".");
}
sb.append(name);
return sb.toString();
}
/**
* Return the full table name
*/
@@ -368,6 +354,13 @@ class AnnotationAssocManys extends AnnotationAssoc {
return append(collectionTable.catalog(), collectionTable.schema(), collectionTable.name());
}
/**
* Return the full table name taking into account quoted identifiers.
*/
private String append(String catalog, String schema, String name) {
return namingConvention.getTableName(catalog, schema, name);
}
/**
* Define intersection table and foreign key columns for ManyToMany.
* <p>
@@ -414,8 +407,8 @@ class AnnotationAssocManys extends AnnotationAssoc {
BeanProperty localId = localTable.getIdProperty();
if (localId != null) {
// add the source to intersection join columns
String fkCol = localTableName + "_" + localId.getDbColumn();
intJoin.addJoinColumn(new DeployTableJoinColumn(localId.getDbColumn(), namingConvention.getColumnFromProperty(null, fkCol)));
String fkCol = namingConvention.deriveM2MColumn(localTableName, localId.getDbColumn());
intJoin.addJoinColumn(new DeployTableJoinColumn(localId.getDbColumn(), fkCol));
}
}
@@ -424,8 +417,8 @@ class AnnotationAssocManys extends AnnotationAssoc {
BeanProperty otherId = otherTable.getIdProperty();
if (otherId != null) {
// set the intersection to dest table join columns
final String fkCol = otherTableName + "_" + otherId.getDbColumn();
destJoin.addJoinColumn(new DeployTableJoinColumn(namingConvention.getColumnFromProperty(null, fkCol), otherId.getDbColumn()));
String fkCol = namingConvention.deriveM2MColumn(otherTableName, otherId.getDbColumn());
destJoin.addJoinColumn(new DeployTableJoinColumn(fkCol, otherId.getDbColumn()));
}
}
@@ -436,6 +436,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
query.findEach(consumer);
}
@Override
public void findEach(int batch, Consumer<List<T>> consumer) {
query.findEach(batch, consumer);
}
@Override
public void findEachWhile(Predicate<T> consumer) {
query.findEachWhile(consumer);
@@ -446,6 +446,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
exprList.findEach(consumer);
}
@Override
public void findEach(int batch, Consumer<List<T>> consumer) {
exprList.findEach(batch, consumer);
}
@Override
public void findEachWhile(Predicate<T> consumer) {
exprList.findEachWhile(consumer);
@@ -33,7 +33,7 @@ abstract class DLoadBaseContext {
final boolean queryFetch;
DLoadBaseContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
DLoadBaseContext(DLoadContext parent, BeanDescriptor<?> desc, String path, OrmQueryProperties queryProps) {
this.parent = parent;
this.serverName = parent.getEbeanServer().getName();
this.desc = desc;
@@ -42,11 +42,7 @@ abstract class DLoadBaseContext {
this.hitCache = parent.isBeanCacheGet() && desc.isBeanCaching();
this.objectGraphNode = parent.getObjectGraphNode(path);
this.queryFetch = queryProps != null && queryProps.isQueryFetch();
this.batchSize = initBatchSize(defaultBatchSize, queryProps);
}
private int initBatchSize(int batchSize, OrmQueryProperties queryProps) {
return queryProps == null ? batchSize : queryProps.getBatchSize();
this.batchSize = parent.batchSize(queryProps);
}
/**
@@ -31,8 +31,8 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
private LoadBuffer currentBuffer;
DLoadBeanContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
super(parent, desc, path, defaultBatchSize, queryProps);
DLoadBeanContext(DLoadContext parent, BeanDescriptor<?> desc, String path, OrmQueryProperties queryProps) {
super(parent, desc, path, queryProps);
// bufferList only required when using query joins (queryFetch)
this.bufferList = (!queryFetch) ? null : new ArrayList<>();
this.currentBuffer = createBuffer(batchSize);
@@ -90,7 +90,7 @@ public class DLoadContext implements LoadContext {
this.planLabel = null;
this.profileLocation = null;
this.profilingListener = null;
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, null);
}
private ObjectGraphOrigin initOrigin() {
@@ -128,7 +128,7 @@ public class DLoadContext implements LoadContext {
}
// initialise rootBeanContext after origin and relativePath have been set
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, null);
registerSecondaryQueries(secondaryQueries);
}
@@ -182,7 +182,7 @@ public class DLoadContext implements LoadContext {
* Return the minimum batch size when using QueryIterator with query joins.
*/
@Override
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
public int getSecondaryQueriesMinBatchSize() {
if (secQuery == null) {
return -1;
}
@@ -190,7 +190,7 @@ public class DLoadContext implements LoadContext {
for (OrmQueryProperties aSecQuery : secQuery) {
int batchSize = aSecQuery.getBatchSize();
if (batchSize == 0) {
batchSize = defaultQueryBatch;
batchSize = 100;
}
maxBatch = Math.max(maxBatch, batchSize);
}
@@ -287,51 +287,55 @@ public class DLoadContext implements LoadContext {
getManyContext(path, many).register(bc);
}
int batchSize(OrmQueryProperties props) {
if (props == null) {
return defaultBatchSize;
}
int batchSize = props.getBatchSize();
return batchSize == 0 ? defaultBatchSize : batchSize;
}
DLoadBeanContext getBeanContext(String path) {
if (path == null) {
return rootBeanContext;
}
return beanMap.computeIfAbsent(path, p -> createBeanContext(p, defaultBatchSize, null));
return beanMap.computeIfAbsent(path, p -> createBeanContext(p, null));
}
DLoadBeanContext getBeanContextWithInherit(String path, BeanPropertyAssocOne<?> property) {
String key = path + ":" + property.getTargetDescriptor().getName();
return beanMap.computeIfAbsent(key, p -> createBeanContext(property, path, defaultBatchSize, null));
return beanMap.computeIfAbsent(key, p -> createBeanContext(property, path, null));
}
private void registerSecondaryNode(boolean many, OrmQueryProperties props) {
int batchSize = props.getBatchSize();
if (batchSize == 0) {
batchSize = defaultBatchSize;
}
String path = props.getPath();
if (many) {
manyMap.put(path, createManyContext(path, batchSize, props));
manyMap.put(path, createManyContext(path, props));
} else {
beanMap.put(path, createBeanContext(path, batchSize, props));
beanMap.put(path, createBeanContext(path, props));
}
}
DLoadManyContext getManyContext(String path, BeanPropertyAssocMany<?> many) {
return manyMap.computeIfAbsent(path, p -> createManyContext(p, many, defaultBatchSize));
return manyMap.computeIfAbsent(path, p -> createManyContext(p, many));
}
private DLoadManyContext createManyContext(String path, BeanPropertyAssocMany<?> many, int batchSize) {
return new DLoadManyContext(this, many, path, batchSize, null);
private DLoadManyContext createManyContext(String path, BeanPropertyAssocMany<?> many) {
return new DLoadManyContext(this, many, path, null);
}
private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) {
private DLoadManyContext createManyContext(String path, OrmQueryProperties queryProps) {
BeanPropertyAssocMany<?> p = (BeanPropertyAssocMany<?>) getBeanProperty(rootDescriptor, path);
return new DLoadManyContext(this, p, path, batchSize, queryProps);
return new DLoadManyContext(this, p, path, queryProps);
}
private DLoadBeanContext createBeanContext(String path, int batchSize, OrmQueryProperties queryProps) {
private DLoadBeanContext createBeanContext(String path, OrmQueryProperties queryProps) {
BeanPropertyAssoc<?> p = (BeanPropertyAssoc<?>) getBeanProperty(rootDescriptor, path);
return new DLoadBeanContext(this, p.getTargetDescriptor(), path, batchSize, queryProps);
return new DLoadBeanContext(this, p.getTargetDescriptor(), path, queryProps);
}
private DLoadBeanContext createBeanContext(BeanPropertyAssoc<?> property, String path, int batchSize, OrmQueryProperties queryProps) {
return new DLoadBeanContext(this, property.getTargetDescriptor(), path, batchSize, queryProps);
private DLoadBeanContext createBeanContext(BeanPropertyAssoc<?> property, String path, OrmQueryProperties queryProps) {
return new DLoadBeanContext(this, property.getTargetDescriptor(), path, queryProps);
}
private BeanProperty getBeanProperty(BeanDescriptor<?> desc, String path) {
@@ -31,11 +31,8 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
private LoadBuffer currentBuffer;
DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> property,
String path, int defaultBatchSize, OrmQueryProperties queryProps) {
super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> property, String path, OrmQueryProperties queryProps) {
super(parent, property.getBeanDescriptor(), path, queryProps);
this.property = property;
this.docStoreMapped = property.isTargetDocStoreMapped();
// bufferList only required when using query joins (queryFetch)
@@ -81,7 +81,7 @@ public final class BatchControl {
*/
private int bufferMax;
private Queue[] queues = new Queue[3];
private final Queue[] queues = new Queue[3];
/**
* Create for a given transaction, PersistExecute, default size and getGeneratedKeys.
@@ -447,7 +447,7 @@ public final class DefaultPersister implements Persister {
public void insert(EntityBean bean, Transaction t) {
PersistRequestBean<?> req = createRequest(bean, t, PersistRequest.Type.INSERT);
if (req.isReference()) {
if (req.isSkipReference()) {
// skip insert on reference bean
return;
}
@@ -881,6 +881,7 @@ public final class DefaultPersister implements Persister {
}
int count = request.executeOrQueue();
request.removeFromPersistenceContext();
if (request.isPersistCascade()) {
deleteAssocOne(request);
@@ -142,7 +142,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
* Tree that knows how to build the master and detail beans from the
* resultSet.
*/
private final SqlTreeNode rootNode;
private final SqlTreeRoot rootNode;
/**
* For master detail query.
@@ -444,13 +444,13 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
if (manyProperty == null) {
// only single resultSet row required to build object so we are done
// read a single resultSet row into single bean
nextBean = rootNode.load(this, null, null);
nextBean = rootNode.load(this);
return true;
}
if (nextBean == null) {
// very first read
nextBean = rootNode.load(this, null, null);
nextBean = rootNode.load(this);
} else {
// nextBean set to previously read currentBean
nextBean = currentBean;
@@ -478,7 +478,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
* is different to the nextBean (false if we need to read more rows).
*/
private boolean checkForDifferentBean() throws SQLException {
currentBean = rootNode.load(this, null, null);
currentBean = rootNode.load(this);
return currentBean != nextBean;
}
@@ -9,7 +9,7 @@ import java.util.concurrent.locks.ReentrantLock;
class CQueryBindCapture implements SpiQueryBindCapture {
private static final double multiplier = 1.3d;
private static final double multiplier = 1.5d;
private final ReentrantLock lock = new ReentrantLock();
private final CQueryPlanManager manager;
@@ -442,9 +442,10 @@ class CQueryBuilder {
int cols = 1 + metaData.getColumnCount();
List<String> propertyNames = new ArrayList<>(cols - 1);
for (int i = 1; i < cols; i++) {
String schemaName = metaData.getSchemaName(i).toLowerCase();
String tableName = metaData.getTableName(i).toLowerCase();
String columnName = metaData.getColumnName(i).toLowerCase();
String path = desc.findBeanPath(tableName, columnName);
String path = desc.findBeanPath(schemaName, tableName, columnName);
if (path != null) {
propertyNames.add(path);
} else {
@@ -37,8 +37,6 @@ public class CQueryEngine {
private static final Logger logger = LoggerFactory.getLogger(CQueryEngine.class);
private static final int defaultSecondaryQueryBatchSize = 100;
private static final String T0 = "t0";
private final int defaultFetchSizeFindList;
@@ -214,7 +212,7 @@ public class CQueryEngine {
logSql(cquery);
}
// first check batch sizes set on query joins
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize);
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize();
if (iterateBufferSize < 1) {
// not set on query joins so check if batch size set on query itself
int queryBatch = request.getQuery().getLazyLoadBatchSize();
@@ -30,14 +30,14 @@ public class CQueryPlanManager implements QueryPlanManager {
private final TransactionManager transactionManager;
private final long defaultThreshold;
private final QueryPlanLogger planLogger;
private final TimedMetric timeCollection;
private final TimedMetric timeBindCapture;
private long defaultThreshold;
public CQueryPlanManager(TransactionManager transactionManager, long defaultThreshold, QueryPlanLogger planLogger, ExtraMetrics extraMetrics) {
this.transactionManager = transactionManager;
this.defaultThreshold = defaultThreshold;
@@ -46,6 +46,11 @@ public class CQueryPlanManager implements QueryPlanManager {
this.timeBindCapture = extraMetrics.getBindCapture();
}
@Override
public void setDefaultThreshold(long thresholdMicros) {
this.defaultThreshold = thresholdMicros;
}
@Override
public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) {
return new CQueryBindCapture(this, queryPlan, defaultThreshold);
@@ -263,6 +263,11 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQueryFetch
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public void findEach(int batch, Consumer<List<T>> consumer) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public void findEachWhile(Predicate<T> consumer) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
@@ -639,4 +644,9 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQueryFetch
public void fetchProperties(String property, Set<String> columns, FetchConfig config) {
detail.fetchProperties(property, columns, config);
}
@Override
public void addNested(String name, OrmQueryDetail nestedDetail, FetchConfig config) {
detail.addNested(name, nestedDetail, config);
}
}
@@ -42,7 +42,28 @@ public class DtoQueryEngine {
}
} catch (Exception e) {
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
} finally {
request.close();
}
}
public <T> void findEach(DtoQueryRequest<T> request, int batchSize, Consumer<List<T>> consumer) {
try {
List<T> buffer = new ArrayList<>();
request.executeSql(binder, SpiQuery.Type.ITERATE);
while (request.next()) {
buffer.add(request.readNextBean());
if (buffer.size() >= batchSize) {
consumer.accept(buffer);
buffer.clear();
}
}
if (!buffer.isEmpty()) {
// consume the remainder
consumer.accept(buffer);
}
} catch (Exception e) {
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
} finally {
request.close();
}
@@ -122,8 +122,8 @@ class SqlTree {
return inheritanceWhereSql;
}
SqlTreeNode getRootNode() {
return rootNode;
SqlTreeRoot getRootNode() {
return (SqlTreeRoot)rootNode;
}
/**
@@ -151,7 +151,7 @@ public final class SqlTreeBuilder {
}
private String buildGroupByClause() {
if (rawSql || !rootNode.isAggregation()) {
if (rawSql || (!rootNode.isAggregation() && query.getHavingExpressions() == null)) {
return null;
}
ctx.startGroupBy();
@@ -73,11 +73,6 @@ interface SqlTreeNode {
*/
EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException;
/**
* Load a version of a @History bean with effective dates.
*/
<T> Version<T> loadVersion(DbReadContext ctx) throws SQLException;
/**
* Return true if the query has a many join.
*/
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.query;
import io.ebean.Version;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
@@ -16,7 +15,6 @@ import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.id.IdBinder;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -56,6 +54,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
* False if report bean and has no id property.
*/
final boolean readId;
private final boolean readIdNormal;
private final boolean disableLazyLoad;
@@ -88,7 +87,6 @@ class SqlTreeNodeBean implements SqlTreeNode {
*/
SqlTreeNodeBean(String prefix, STreePropertyAssoc beanProp, SqlTreeProperties props,
List<SqlTreeNode> myChildren, boolean withId, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
this(prefix, beanProp, beanProp.target(), props, myChildren, withId, null, temporalMode, disableLazyLoad);
}
@@ -120,7 +118,8 @@ class SqlTreeNodeBean implements SqlTreeNode {
boolean aggregationRoot = props.isAggregationRoot();
// the bean has an Id property and we want to use it
this.readId = !aggregationRoot && withId && desc.hasId();
this.disableLazyLoad = disableLazyLoad || !readId || desc.isRawSqlBased() || temporalVersions;
this.readIdNormal = readId && !temporalVersions;
this.disableLazyLoad = disableLazyLoad || !readIdNormal || desc.isRawSqlBased();
this.partialObject = props.isPartialObject();
this.properties = props.getProps();
this.children = myChildren == null ? NO_CHILDREN : myChildren.toArray(new SqlTreeNode[0]);
@@ -192,26 +191,12 @@ class SqlTreeNodeBean implements SqlTreeNode {
}
}
/**
* Read the version bean.
*/
@Override
@SuppressWarnings("unchecked")
public <T> Version<T> loadVersion(DbReadContext ctx) throws SQLException {
// read the sys period lower and upper bounds
// these are always the first 2 columns in the resultSet
Timestamp start = ctx.getDataReader().getTimestamp();
Timestamp end = ctx.getDataReader().getTimestamp();
T bean = (T) load(ctx, null, null);
return new Version<>(bean, start, end);
}
/**
* Load that takes into account inheritance.
*/
private class LoadInherit extends Load {
LoadInherit(DbReadContext ctx, EntityBean parentBean) {
private LoadInherit(DbReadContext ctx, EntityBean parentBean) {
super(ctx, parentBean);
}
@@ -232,10 +217,8 @@ class SqlTreeNodeBean implements SqlTreeNode {
@Override
void loadProperties() {
// take account of inheritance and due to subclassing approach
// need to get a 'local' version of the property
// take account of inheritance
for (STreeProperty property : properties) {
// get a local version of the BeanProperty
localDesc.inheritanceLoad(sqlBeanLoad, property, ctx);
}
}
@@ -244,7 +227,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
/**
* Load a bean instance.
*/
private class Load {
class Load {
final DbReadContext ctx;
final EntityBean parentBean;
@@ -262,12 +245,12 @@ class SqlTreeNodeBean implements SqlTreeNode {
SqlBeanLoad sqlBeanLoad;
boolean lazyLoadMany;
Load(DbReadContext ctx, EntityBean parentBean) {
private Load(DbReadContext ctx, EntityBean parentBean) {
this.ctx = ctx;
this.parentBean = parentBean;
}
void initLazyParent() throws SQLException {
private void initLazyParent() throws SQLException {
if (lazyLoadParentIdBinder != null) {
lazyLoadParentId = lazyLoadParentIdBinder.read(ctx);
}
@@ -279,12 +262,12 @@ class SqlTreeNodeBean implements SqlTreeNode {
localIdBinder = idBinder;
}
void initPersistenceContext() {
private void initPersistenceContext() {
queryMode = ctx.getQueryMode();
persistenceContext = (!readId || temporalVersions) ? null : ctx.getPersistenceContext();
persistenceContext = (!readIdNormal) ? null : ctx.getPersistenceContext();
}
void readId() throws SQLException {
private void readId() throws SQLException {
if (readId) {
id = localIdBinder.readSet(ctx, localBean);
if (id == null) {
@@ -325,7 +308,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
}
}
void initSqlLoadBean() {
private void initSqlLoadBean() {
ctx.setCurrentPrefix(prefix, pathMap);
ctx.propagateState(localBean);
sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode);
@@ -337,33 +320,29 @@ class SqlTreeNodeBean implements SqlTreeNode {
}
}
void loadChildren() throws SQLException {
//boolean lazyLoadMany = false;
private void loadChildren() throws SQLException {
if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) {
// batch lazy load many into existing contextBean
localBean = contextBean;
lazyLoadMany = true;
}
// recursively continue reading...
for (SqlTreeNode aChildren : children) {
// read each child... and let them set their
// values back to this localBean
aChildren.load(ctx, localBean, contextBean);
for (SqlTreeNode child : children) {
child.load(ctx, localBean, contextBean);
}
}
boolean isLazyLoadManyRoot() {
private boolean isLazyLoadManyRoot() {
return queryMode == Mode.LAZYLOAD_MANY && isRoot();
}
EntityBean getContextBean() {
private EntityBean getContextBean() {
return contextBean;
}
void postLoad() {
private void postLoad() {
if (!lazyLoadMany && localBean != null) {
ctx.setCurrentPrefix(prefix, pathMap);
if (readId && !temporalVersions) {
if (readIdNormal) {
createListProxies();
}
if (temporalMode == SpiQuery.TemporalMode.DRAFT) {
@@ -384,7 +363,6 @@ class SqlTreeNodeBean implements SqlTreeNode {
if (disableLazyLoad) {
// bean does not have an Id or is SqlSelect based
ebi.setDisableLazyLoad(true);
} else if (partialObject) {
if (readId) {
// register for lazy loading
@@ -408,7 +386,6 @@ class SqlTreeNodeBean implements SqlTreeNode {
private void createListProxies() {
STreePropertyAssocMany fetchedMany = ctx.getManyProperty();
boolean forceNewReference = queryMode == Mode.REFRESH_BEAN;
// load the List/Set/Map proxy objects (deferred fetching of lists)
for (STreePropertyAssocMany many : localDesc.propsMany()) {
if (many != fetchedMany) {
// create a proxy for the many (deferred fetching)
@@ -425,21 +402,20 @@ class SqlTreeNodeBean implements SqlTreeNode {
}
}
void setBeanToParent() {
private void setBeanToParent() {
if (parentBean != null) {
// set this back to the parentBean
nodeBeanProp.setValue(parentBean, contextBean);
}
}
EntityBean complete() {
if (!readId || temporalVersions) {
private EntityBean complete() {
if (!readIdNormal) {
// a bean with no Id (never found in context)
if (lazyLoadParentId != null) {
ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId);
}
return localBean;
} else {
if (lazyLoadParentId != null) {
ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId);
@@ -448,7 +424,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
}
}
void initialise() throws SQLException {
private void initialise() throws SQLException {
initLazyParent();
initBeanType();
initPersistenceContext();
@@ -457,6 +433,27 @@ class SqlTreeNodeBean implements SqlTreeNode {
loadProperties();
loadChildren();
}
/**
* Perform the load returning the loaded bean.
*/
EntityBean perform() throws SQLException {
initialise();
if (isLazyLoadManyRoot()) {
return getContextBean();
}
postLoad();
setBeanToParent();
return complete();
}
/**
* Return true if this bean was already in the context. If already in the
* context we need to check if it is already contained in the collection.
*/
boolean isContextBean() {
return localBean == null;
}
}
/**
@@ -464,14 +461,14 @@ class SqlTreeNodeBean implements SqlTreeNode {
*/
@Override
public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException {
Load load = (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean);
load.initialise();
if (load.isLazyLoadManyRoot()) {
return load.getContextBean();
}
load.postLoad();
load.setBeanToParent();
return load.complete();
return createLoad(ctx, parentBean).perform();
}
/**
* Create the loader with or without inheritance.
*/
Load createLoad(DbReadContext ctx, EntityBean parentBean) {
return (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean);
}
@Override
@@ -489,8 +486,8 @@ class SqlTreeNodeBean implements SqlTreeNode {
property.appendSelect(ctx, subQuery);
}
}
for (SqlTreeNode aChildren : children) {
aChildren.appendGroupBy(ctx, subQuery);
for (SqlTreeNode child : children) {
child.appendGroupBy(ctx, subQuery);
}
ctx.popTableAlias();
ctx.popJoin();
@@ -527,11 +524,8 @@ class SqlTreeNodeBean implements SqlTreeNode {
appendSelectId(ctx, idBinder.getBeanProperty());
}
appendSelect(ctx, subQuery, properties);
for (SqlTreeNode aChildren : children) {
// read each child... and let them set their
// values back to this localBean
aChildren.appendSelect(ctx, subQuery);
for (SqlTreeNode child : children) {
child.appendSelect(ctx, subQuery);
}
ctx.popTableAlias();
ctx.popJoin();
@@ -579,10 +573,8 @@ class SqlTreeNodeBean implements SqlTreeNode {
}
}
appendExtraWhere(ctx);
for (SqlTreeNode aChildren : children) {
// recursively add to the where clause any
// fixed predicates (extraWhere etc)
aChildren.appendWhere(ctx);
for (SqlTreeNode child : children) {
child.appendWhere(ctx);
}
}
@@ -618,10 +610,9 @@ class SqlTreeNodeBean implements SqlTreeNode {
property.appendFrom(ctx, joinType);
}
for (SqlTreeNode aChildren : children) {
aChildren.appendFrom(ctx, joinType);
for (SqlTreeNode child : children) {
child.appendFrom(ctx, joinType);
}
ctx.popTableAlias();
ctx.popJoin();
}
@@ -646,8 +637,8 @@ class SqlTreeNodeBean implements SqlTreeNode {
if (intersectionAsOfTableAlias) {
query.incrementAsOfTableCount();
}
for (SqlTreeNode aChildren : children) {
aChildren.addAsOfTableAlias(query);
for (SqlTreeNode child : children) {
child.addAsOfTableAlias(query);
}
}
@@ -178,14 +178,6 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
return null;
}
/**
* Does nothing.
*/
@Override
public <T> Version<T> loadVersion(DbReadContext ctx) {
return null;
}
@Override
public boolean hasMany() {
return manyJoin;
@@ -99,12 +99,6 @@ class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode {
return null;
}
@Override
public <T> Version<T> loadVersion(DbReadContext ctx) {
// nothing to do here
return null;
}
@Override
public boolean hasMany() {
return true;
@@ -18,6 +18,25 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
this.manyProp = prop;
}
@Override
public boolean hasMany() {
return true;
}
@Override
public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException {
// pass in null for parentBean because added to a collection rather than set to the parentBean
SqlTreeNodeBean.Load load = createLoad(cquery, null);
EntityBean detailBean = load.perform();
if (contextParent != null) {
// Add to the collection and initialise collection if needed
// A null detailBean may initialise an empty collection
// Check for bean existing in collection based on load.isContextBean()
manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, load.isContextBean());
}
return detailBean;
}
/**
* Append the property columns to the buffer.
*/
@@ -28,20 +47,6 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
ctx.popTableAlias();
}
@Override
public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException {
// pass in null for parentBean because the localBean
// that is built is added to a collection rather than
// being set to the parentBean directly
EntityBean detailBean = super.load(cquery, null, null);
// initialise the collection and add detailBean if it is not null
if (contextParent != null) {
manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, false);
}
return detailBean;
}
/**
* append extraWhere to the join.
*/
@@ -64,9 +69,4 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
super.appendFrom(ctx, joinType.autoToOuter());
}
@Override
public boolean hasMany() {
return true;
}
}
@@ -144,12 +144,6 @@ class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
return null;
}
@Override
public <T> Version<T> loadVersion(DbReadContext ctx) {
// nothing to do here
return null;
}
@Override
public boolean hasMany() {
return true;
@@ -1,16 +1,21 @@
package io.ebeaninternal.server.query;
import io.ebean.Version;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.deploy.DbReadContext;
import io.ebeaninternal.server.deploy.DbSqlContext;
import io.ebeaninternal.server.deploy.TableJoin;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.List;
import java.util.Set;
/**
* Represents the root node of the Sql Tree.
*/
final class SqlTreeNodeRoot extends SqlTreeNodeBean {
final class SqlTreeNodeRoot extends SqlTreeNodeBean implements SqlTreeRoot {
private final TableJoin includeJoin;
@@ -35,6 +40,25 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean {
return true;
}
@Override
public EntityBean load(DbReadContext ctx) throws SQLException {
return load(ctx, null, null);
}
/**
* Read the version bean.
*/
@Override
@SuppressWarnings("unchecked")
public <T> Version<T> loadVersion(DbReadContext ctx) throws SQLException {
// read the sys period lower and upper bounds
// these are always the first 2 columns in the resultSet
Timestamp start = ctx.getDataReader().getTimestamp();
Timestamp end = ctx.getDataReader().getTimestamp();
T bean = (T) load(ctx, null, null);
return new Version<>(bean, start, end);
}
@Override
public boolean isSqlDistinct() {
return sqlDistinct;
@@ -0,0 +1,34 @@
package io.ebeaninternal.server.query;
import io.ebean.Version;
import io.ebean.bean.EntityBean;
import io.ebean.core.type.ScalarDataReader;
import io.ebeaninternal.server.deploy.DbReadContext;
import java.sql.SQLException;
/**
* The root level node of the SqlTree.
*/
interface SqlTreeRoot {
/**
* Load the bean from the DbReadContext.
* <p>
* At a high level this actually controls the reading of the data from the
* jdbc resultSet and putting it into the bean etc.
* </p>
*/
EntityBean load(DbReadContext ctx) throws SQLException;
/**
* Load a version of a @History bean with effective dates.
*/
<T> Version<T> loadVersion(DbReadContext ctx) throws SQLException;
/**
* Return a Scalar single attribute reader based on the first property.
*/
ScalarDataReader<?> getSingleAttributeReader();
}
@@ -102,6 +102,11 @@ public class DefaultDtoQuery<T> implements SpiDtoQuery<T> {
server.findDtoEach(this, consumer);
}
@Override
public void findEach(int batch, Consumer<List<T>> consumer) {
server.findDtoEach(this, batch, consumer);
}
@Override
public void findEachWhile(Predicate<T> consumer) {
server.findDtoEachWhile(this, consumer);
@@ -35,6 +35,8 @@ import io.ebean.plugin.BeanType;
import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.api.CQueryPlanKey;
import io.ebeaninternal.api.CacheIdLookup;
import io.ebeaninternal.api.CacheIdLookupMany;
import io.ebeaninternal.api.CacheIdLookupSingle;
import io.ebeaninternal.api.HashQuery;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
@@ -391,7 +393,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setUseDocStore(boolean useDocStore) {
public Query<T> setUseDocStore(boolean useDocStore) {
this.useDocStore = useDocStore;
return this;
}
@@ -431,7 +433,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setAllowLoadErrors() {
public Query<T> setAllowLoadErrors() {
this.allowLoadErrors = true;
return this;
}
@@ -452,21 +454,21 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> asOf(Timestamp asOfDateTime) {
public Query<T> asOf(Timestamp asOfDateTime) {
this.temporalMode = (asOfDateTime != null) ? TemporalMode.AS_OF : TemporalMode.CURRENT;
this.asOf = asOfDateTime;
return this;
}
@Override
public DefaultOrmQuery<T> asDraft() {
public Query<T> asDraft() {
this.temporalMode = TemporalMode.DRAFT;
this.useBeanCache = CacheMode.OFF;
return this;
}
@Override
public DefaultOrmQuery<T> setIncludeSoftDeletes() {
public Query<T> setIncludeSoftDeletes() {
this.temporalMode = TemporalMode.SOFT_DELETED;
return this;
}
@@ -489,7 +491,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setRawSql(RawSql rawSql) {
public Query<T> setRawSql(RawSql rawSql) {
this.rawSql = (SpiRawSql) rawSql;
return this;
}
@@ -740,10 +742,14 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return null;
}
List<SpiExpression> underlyingList = whereExpressions.getUnderlyingList();
if (underlyingList.size() == 1) {
if (underlyingList.isEmpty()) {
if (id != null) {
return new CacheIdLookupSingle<>(id);
}
} else if (underlyingList.size() == 1) {
SpiExpression singleExpression = underlyingList.get(0);
if (singleExpression instanceof IdInExpression) {
return new CacheIdLookup<>((IdInExpression) singleExpression);
return new CacheIdLookupMany<>((IdInExpression) singleExpression);
}
}
return null;
@@ -802,12 +808,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> copy() {
public SpiQuery<T> copy() {
return copy(server);
}
@Override
public DefaultOrmQuery<T> copy(SpiEbeanServer server) {
public SpiQuery<T> copy(SpiEbeanServer server) {
DefaultOrmQuery<T> copy = new DefaultOrmQuery<>(beanDescriptor, server, expressionFactory);
copy.transaction = transaction;
copy.m2mIncludeJoin = m2mIncludeJoin;
@@ -955,7 +961,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setAutoTune(boolean autoTune) {
public Query<T> setAutoTune(boolean autoTune) {
this.autoTune = autoTune;
return this;
}
@@ -971,21 +977,21 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> forUpdate() {
public Query<T> forUpdate() {
return setForUpdateWithMode(LockWait.WAIT, LockType.DEFAULT);
}
@Override
public DefaultOrmQuery<T> forUpdateNoWait() {
public Query<T> forUpdateNoWait() {
return setForUpdateWithMode(LockWait.NOWAIT, LockType.DEFAULT);
}
@Override
public DefaultOrmQuery<T> forUpdateSkipLocked() {
public Query<T> forUpdateSkipLocked() {
return setForUpdateWithMode(LockWait.SKIPLOCKED, LockType.DEFAULT);
}
private DefaultOrmQuery<T> setForUpdateWithMode(LockWait mode, LockType lockType) {
private Query<T> setForUpdateWithMode(LockWait mode, LockType lockType) {
this.forUpdate = mode;
this.lockType = lockType;
this.useBeanCache = CacheMode.OFF;
@@ -1329,7 +1335,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setReadOnly(boolean readOnly) {
public Query<T> setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
return this;
}
@@ -1375,19 +1381,19 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setUseQueryCache(CacheMode useQueryCache) {
public Query<T> setUseQueryCache(CacheMode useQueryCache) {
this.useQueryCache = useQueryCache;
return this;
}
@Override
public DefaultOrmQuery<T> setLoadBeanCache(boolean loadBeanCache) {
public Query<T> setLoadBeanCache(boolean loadBeanCache) {
this.useBeanCache = CacheMode.PUT;
return this;
}
@Override
public DefaultOrmQuery<T> setTimeout(int secs) {
public Query<T> setTimeout(int secs) {
this.timeout = secs;
return this;
}
@@ -1413,64 +1419,77 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> select(String columns) {
public void addNested(String name, OrmQueryDetail nestedDetail, FetchConfig config) {
detail.addNested(name, nestedDetail, config);
}
@Override
public Query<T> select(String columns) {
detail.select(columns);
return this;
}
@Override
public DefaultOrmQuery<T> select(FetchGroup<T> fetchGroup) {
public Query<T> select(FetchGroup<T> fetchGroup) {
this.detail = ((SpiFetchGroup<T>) fetchGroup).detail();
return this;
}
@Override
public DefaultOrmQuery<T> fetch(String property) {
return fetch(property, null, null);
public Query<T> fetch(String path) {
return fetch(path, null, null);
}
@Override
public Query<T> fetchQuery(String property) {
return fetch(property, null, FETCH_QUERY);
}
public Query<T> fetchCache(String property) {
return fetch(property, null, FETCH_CACHE);
public Query<T> fetch(String path, FetchConfig joinConfig) {
return fetch(path, null, joinConfig);
}
@Override
public Query<T> fetchLazy(String property) {
return fetch(property, null, FETCH_LAZY);
public Query<T> fetch(String path, String properties) {
return fetch(path, properties, null);
}
@Override
public DefaultOrmQuery<T> fetch(String property, FetchConfig joinConfig) {
return fetch(property, null, joinConfig);
public Query<T> fetch(String path, String properties, FetchConfig config) {
if (nativeSql != null && (config == null || config.isJoin())) {
// can't use fetch join with nativeSql (as the root query)
config = FETCH_QUERY;
}
return fetchInternal(path, properties, config);
}
@Override
public DefaultOrmQuery<T> fetch(String property, String columns) {
return fetch(property, columns, null);
public Query<T> fetchQuery(String path) {
return fetchInternal(path, null, FETCH_QUERY);
}
public Query<T> fetchCache(String path) {
return fetchInternal(path, null, FETCH_CACHE);
}
@Override
public Query<T> fetchQuery(String property, String columns) {
return fetch(property, columns, FETCH_QUERY);
public Query<T> fetchLazy(String path) {
return fetchInternal(path, null, FETCH_LAZY);
}
@Override
public Query<T> fetchCache(String property, String columns) {
return fetch(property, columns, FETCH_CACHE);
public Query<T> fetchQuery(String path, String properties) {
return fetchInternal(path, properties, FETCH_QUERY);
}
@Override
public Query<T> fetchLazy(String property, String columns) {
return fetch(property, columns, FETCH_LAZY);
public Query<T> fetchCache(String path, String properties) {
return fetchInternal(path, properties, FETCH_CACHE);
}
@Override
public DefaultOrmQuery<T> fetch(String property, String columns, FetchConfig config) {
detail.fetch(property, columns, config);
public Query<T> fetchLazy(String path, String properties) {
return fetchInternal(path, properties, FETCH_LAZY);
}
private Query<T> fetchInternal(String path, String properties, FetchConfig config) {
detail.fetch(path, properties, config);
return this;
}
@@ -1543,6 +1562,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
server.findEach(this, consumer, transaction);
}
@Override
public void findEach(int batch, Consumer<List<T>> consumer) {
server.findEach(this, batch, consumer, transaction);
}
@Override
public QueryIterator<T> findIterate() {
return server.findIterate(this, transaction);
@@ -1633,7 +1657,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setParameter(Object value) {
public Query<T> setParameter(Object value) {
if (bindParams == null) {
bindParams = new BindParams();
}
@@ -1642,7 +1666,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setParameters(Object... values) {
public Query<T> setParameters(Object... values) {
if (bindParams == null) {
bindParams = new BindParams();
}
@@ -1656,7 +1680,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
* have in the query.
*/
@Override
public DefaultOrmQuery<T> setParameter(int position, Object value) {
public Query<T> setParameter(int position, Object value) {
if (bindParams == null) {
bindParams = new BindParams();
}
@@ -1668,7 +1692,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
* Set a named bind parameter. Named parameters have a colon to prefix the name.
*/
@Override
public DefaultOrmQuery<T> setParameter(String name, Object value) {
public Query<T> setParameter(String name, Object value) {
if (namedParams != null) {
ONamedParam param = namedParams.get(name);
if (param != null) {
@@ -1714,12 +1738,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
@Override
@Deprecated
public DefaultOrmQuery<T> orderBy(String orderByClause) {
public Query<T> orderBy(String orderByClause) {
return order(orderByClause);
}
@Override
public DefaultOrmQuery<T> order(String orderByClause) {
public Query<T> order(String orderByClause) {
if (orderByClause == null || orderByClause.trim().isEmpty()) {
this.orderBy = null;
} else {
@@ -1730,12 +1754,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
@Override
@Deprecated
public DefaultOrmQuery<T> setOrderBy(OrderBy<T> orderBy) {
public Query<T> setOrderBy(OrderBy<T> orderBy) {
return setOrder(orderBy);
}
@Override
public DefaultOrmQuery<T> setOrder(OrderBy<T> orderBy) {
public Query<T> setOrder(OrderBy<T> orderBy) {
this.orderBy = orderBy;
if (orderBy != null) {
orderBy.setQuery(this);
@@ -1767,13 +1791,13 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
* Internally set to use SQL DISTINCT on the query but still have id property included.
*/
@Override
public DefaultOrmQuery<T> setDistinct(boolean distinct) {
public Query<T> setDistinct(boolean distinct) {
this.distinct = distinct;
return this;
}
@Override
public DefaultOrmQuery<T> setCountDistinct(CountDistinctOrder countDistinctOrder) {
public Query<T> setCountDistinct(CountDistinctOrder countDistinctOrder) {
this.countDistinctOrder = countDistinctOrder;
return this;
}
@@ -1824,7 +1848,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setDisableLazyLoading(boolean disableLazyLoading) {
public Query<T> setDisableLazyLoading(boolean disableLazyLoading) {
this.disableLazyLoading = disableLazyLoading;
return this;
}
@@ -1840,7 +1864,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setFirstRow(int firstRow) {
public Query<T> setFirstRow(int firstRow) {
this.firstRow = firstRow;
return this;
}
@@ -1851,7 +1875,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setMaxRows(int maxRows) {
public Query<T> setMaxRows(int maxRows) {
this.maxRows = maxRows;
return this;
}
@@ -1862,7 +1886,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setMapKey(String mapKey) {
public Query<T> setMapKey(String mapKey) {
this.mapKey = mapKey;
return this;
}
@@ -1873,7 +1897,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> setId(Object id) {
public Query<T> setId(Object id) {
if (id == null) {
throw new NullPointerException("The id is null");
}
@@ -1887,7 +1911,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> where(Expression expression) {
public Query<T> where(Expression expression) {
where().add(expression);
return this;
}
@@ -1917,7 +1941,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> having(Expression expression) {
public Query<T> having(Expression expression) {
having().add(expression);
return this;
}
@@ -2043,7 +2067,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public DefaultOrmQuery<T> alias(String alias) {
public Query<T> alias(String alias) {
this.rootTableAlias = alias;
return this;
}
@@ -120,8 +120,8 @@ public class OrmQueryProperties implements Serializable {
this.parentPath = SplitName.parent(path);
this.allProperties = other.allProperties;
this.included = other.included;
this.cache = other.cache;
this.fetchConfig = fetchConfig;
this.cache = fetchConfig.isCache();
}
/**
@@ -1,11 +1,9 @@
package io.ebeaninternal.server.transaction;
import io.ebean.bean.EntityBean;
import io.ebean.bean.PersistenceContext;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
/**
@@ -200,6 +198,20 @@ public final class DefaultPersistenceContext implements PersistenceContext {
}
}
@Override
public List<Object> dirtyBeans() {
lock.lock();
try {
List<Object> list = new ArrayList<>();
for (ClassContext classContext : typeCache.values()) {
classContext.dirtyBeans(list);
}
return list;
} finally {
lock.unlock();
}
}
@Override
public String toString() {
lock.lock();
@@ -318,6 +330,17 @@ public final class DefaultPersistenceContext implements PersistenceContext {
deleteSet.add(id);
map.remove(id);
}
/**
* Add the dirty beans to the list.
*/
void dirtyBeans(List<Object> list) {
for (Object value : map.values()) {
if (((EntityBean) value)._ebean_getIntercept().isDirty()) {
list.add(value);
}
}
}
}
}
@@ -95,6 +95,16 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
return startNanos;
}
@Override
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
// do nothing
}
@Override
public boolean isAutoPersistUpdates() {
return false;
}
@Override
public void setLabel(String label) {
// do nothing

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