Compare commits

..
Author SHA1 Message Date
Rob BygraveandGitHub 4bcfa80df2 Merge pull request #3594 from ebean-orm/feature/improved-readOnly-immutable-DisabledLazyLoad
Modify disableLazyLoad to throw LazyInitialisationException instead o…
2025-04-29 23:32:46 +12:00
Rob Bygrave c583879d57 Modify disableLazyLoad to throw LazyInitialisationException instead of returning null
Modify such that accessing an unloaded property throws a LazyInitialisationException. This includes ToMany collections that where previously initialised as empty collections (that didn't lazy load).

This change brings the disableLazyLoad behaviour in line with the unmodifiable behaviour.

It does mean that the use case of bulk mapping from entities to DTOs via something like MapStruct will no longer work nicely (mapping nulls and empty lists versus LazyInitialisationException).
2025-03-18 23:46:42 +13:00
231 changed files with 1124 additions and 2089 deletions
+1 -1
View File
@@ -40,5 +40,5 @@ jobs:
# - name: Maven single test
# run: mvn --batch-mode clean verify -Dtest="io.ebeaninternal.server.core.DefaultServer_getReferenceTest" -DfailIfNoTests=false
- name: Build with Maven
run: mvn -T 8 clean test -Pdefault
run: mvn -T 8 clean test
+2 -2
View File
@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
java_version: [GA,EA]
java_version: [GA,EA,23]
os: [ubuntu-latest]
steps:
@@ -37,5 +37,5 @@ jobs:
- name: Maven version
run: mvn --version
- name: Build with Maven
run: mvn test -Pea
run: mvn -T 8 test
+1 -1
View File
@@ -34,5 +34,5 @@ jobs:
path:
~/.m2
key: build-${{ env.cache-name }}
- name: mariadb 10.11
- name: mariadb 10.6
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-mariadb.properties
+2 -2
View File
@@ -34,5 +34,5 @@ jobs:
path:
~/.m2
key: build-${{ env.cache-name }}
- name: sqlserver 2022
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-sqlserver.properties
- name: sqlserver 2017
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-sqlserver17.properties
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-net-postgis-types</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -41,7 +41,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -60,13 +60,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
</parent>
<artifactId>composites</artifactId>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
</parent>
<name>ebean api</name>
@@ -35,7 +35,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-config</artifactId>
<version>4.1</version>
<version>3.12</version>
</dependency>
<dependency>
@@ -137,7 +137,6 @@ public interface Database {
/**
* Return the associated read only DataSource for this Database instance (can be null).
*/
@Nullable
DataSource readOnlyDataSource();
/**
@@ -1062,11 +1062,6 @@ public interface DatabaseBuilder {
*/
DatabaseBuilder readOnlyDatabase(boolean readOnlyDatabase);
/**
* Set to false such that the instance does not register a JVM shutdown hook.
*/
DatabaseBuilder shutdownHook(boolean shutdownHook);
/**
* Set a DataSource.
*/
@@ -2024,11 +2019,6 @@ public interface DatabaseBuilder {
@Deprecated
DatabaseBuilder setQueryPlanTTLSeconds(int queryPlanTTLSeconds);
/**
* Set the EXPLAIN (with options) to use for query plan capture.
*/
DatabaseBuilder queryPlanExplain(String queryPlanExplain);
/**
* Create a new PlatformConfig based of the one held but with overridden properties by reading
* properties with the given path and prefix.
@@ -2648,11 +2638,6 @@ public interface DatabaseBuilder {
*/
boolean readOnlyDatabase();
/**
* Return if a JVM shutdown hook should be registered.
*/
boolean shutdownHook();
/**
* Return the DataSource.
*/
@@ -3059,11 +3044,6 @@ public interface DatabaseBuilder {
*/
int getQueryPlanTTLSeconds();
/**
* Return the EXPLAIN (with options) to use for capturing query plans.
*/
String getQueryPlanExplain();
/**
* Return mapping locations to search for xml mapping via class path search.
*/
@@ -1,74 +0,0 @@
package io.ebean;
import jakarta.persistence.PersistenceException;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* FutureMap represents the result of a background query execution that will
* return a map of entities.
* <p>
* It extends the java.util.concurrent.Future with the ability to cancel the
* query, check if it is finished and get the resulting list waiting for the
* query to finish (ie. the standard features of java.util.concurrent.Future).
* </p>
* <p>
* A simple example:
* </p>
* <pre>{@code
*
* // create a query to find all orders
* Query<Long,Order> query = DB.find(Order.class)
* .setMapKey("id");
*
* // execute the query in a background thread
* // immediately returning the futureMap
* FutureMap<Long,Order> futureMap = query.findFutureMap();
*
* // do something else ...
*
* if (!futureMap.isDone()){
* // we can cancel the query execution. This will cancel
* // the underlying query if that is supported by the JDBC
* // driver and database
* futureMap.cancel(true);
* }
*
* if (!futureMap.isCancelled()){
* // wait for the query to finish and return the map
* Map<Long,Order> map = futureMap.get();
* ...
* }
*
* }</pre>
*/
public interface FutureMap<K, T> extends Future<Map<K, T>> {
/**
* Return the query that is being executed by a background thread.
*/
Query<T> getQuery();
/**
* Same as {@link #get()} but wraps InterruptedException and ExecutionException in the
* unchecked PersistenceException.
*
* @return The query list result
* @throws PersistenceException when a InterruptedException or ExecutionException occurs.
*/
Map<K, T> getUnchecked();
/**
* Same as {@link #get(long, TimeUnit)} but wraps InterruptedException
* and ExecutionException in the unchecked PersistenceException.
*
* @return The query list result
* @throws TimeoutException if the wait timed out
* @throws PersistenceException if a InterruptedException or ExecutionException occurs.
*/
Map<K, T> getUnchecked(long timeout, TimeUnit unit) throws TimeoutException;
}
@@ -1,7 +1,6 @@
package io.ebean;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* Object relational query for finding a List, Set, Map or single entity bean.
@@ -311,7 +310,6 @@ public interface Query<T> extends CancelableQuery, QueryBuilder<Query<T>, T> {
/**
* Return the Id value.
*/
@Nullable
Object getId();
/**
@@ -448,13 +446,11 @@ public interface Query<T> extends CancelableQuery, QueryBuilder<Query<T>, T> {
/**
* Return the "for update" wait mode to use.
*/
@Nullable
LockWait getForUpdateLockWait();
/**
* Return the lock type (strength) to use with "for update".
*/
@Nullable
LockType getForUpdateLockType();
/**
@@ -956,16 +956,6 @@ public interface QueryBuilder<SELF extends QueryBuilder<SELF, T>, T> extends Que
*/
FutureList<T> findFutureList();
/**
* Execute find map query in a background thread.
* <p>
* This query will execute in it's own PersistenceContext and using its own transaction.
* What that means is that it will not share any bean instances with other queries.
*
* @return a Future object for the map result of the query
*/
<K> FutureMap<K,T> findFutureMap();
/**
* Return a PagedList for this query using firstRow and maxRows.
* <p>
@@ -381,11 +381,6 @@ public interface EntityBeanIntercept extends Serializable {
*/
void loadBean(int loadProperty);
/**
* Invoke the lazy loading. This method is synchronised externally.
*/
void loadBeanInternal(int loadProperty, BeanLoader loader);
/**
* Called when a BeanCollection is initialised automatically.
*/
@@ -393,11 +393,6 @@ public final class InterceptReadOnly extends InterceptBase {
}
@Override
public void loadBeanInternal(int loadProperty, BeanLoader loader) {
}
@Override
public void initialisedMany(int propertyIndex) {
loaded[propertyIndex] = true;
@@ -2,6 +2,7 @@ package io.ebean.bean;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.LazyInitialisationException;
import io.ebean.ValuePair;
import jakarta.persistence.EntityNotFoundException;
@@ -59,7 +60,7 @@ public final class InterceptReadWrite extends InterceptBase {
private EntityBean embeddedOwner;
private int embeddedOwnerIndex;
/**
* One of NEW, REF, UPD.
* One of NEW, REFERENCE, LOADED.
*/
private int state;
private boolean forceUpdate;
@@ -645,6 +646,9 @@ public final class InterceptReadWrite extends InterceptBase {
public void loadBean(int loadProperty) {
lock.lock();
try {
if (disableLazyLoad) {
throw new LazyInitialisationException("Property not loaded: " + property(loadProperty));
}
if (beanLoader == null) {
final Database database = DB.byName(ebeanServerName);
if (database == null) {
@@ -668,8 +672,7 @@ public final class InterceptReadWrite extends InterceptBase {
}
}
@Override
public void loadBeanInternal(int loadProperty, BeanLoader loader) {
private void loadBeanInternal(int loadProperty, BeanLoader loader) {
if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
// race condition where multiple threads calling preGetter concurrently
return;
@@ -771,7 +774,7 @@ public final class InterceptReadWrite extends InterceptBase {
@Override
public void preGetter(int propertyIndex) {
preGetterCallback(propertyIndex);
if (state == STATE_NEW || disableLazyLoad) {
if (state == STATE_NEW) {
return;
}
if (!isLoadedProperty(propertyIndex)) {
@@ -1,6 +1,5 @@
package io.ebean.cache;
import java.time.Instant;
import java.util.Set;
/**
@@ -11,11 +10,11 @@ import java.util.Set;
* the query cache entry is treated as invalid.
* </p>
*/
public final class QueryCacheEntry {
public class QueryCacheEntry {
private final Object value;
private final Set<String> dependentTables;
private final Instant timestamp;
private final long timestamp;
/**
* Create with dependent tables and timestamp.
@@ -24,7 +23,7 @@ public final class QueryCacheEntry {
* @param dependentTables The extra tables the query is dependent on (joins to)
* @param timestamp The timestamp that the query uses to check for modifications
*/
public QueryCacheEntry(Object value, Set<String> dependentTables, Instant timestamp) {
public QueryCacheEntry(Object value, Set<String> dependentTables, long timestamp) {
this.value = value;
this.dependentTables = dependentTables;
this.timestamp = timestamp;
@@ -33,21 +32,21 @@ public final class QueryCacheEntry {
/**
* Return the actual query result.
*/
public Object value() {
public Object getValue() {
return value;
}
/**
* Return the tables the query result is dependent on.
*/
public Set<String> dependentTables() {
public Set<String> getDependentTables() {
return dependentTables;
}
/**
* Return the timestamp used to check for modifications on the dependent tables.
*/
public Instant timestamp() {
public long getTimestamp() {
return timestamp;
}
}
@@ -251,7 +251,7 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
/**
* The default batch size for lazy loading
*/
private int lazyLoadBatchSize = 100;
private int lazyLoadBatchSize = 10;
/**
* The default batch size for 'query joins'.
@@ -311,7 +311,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
private boolean skipDataSourceCheck;
private boolean readOnlyDatabase;
private boolean shutdownHook = true;
/**
* The data source (if programmatically provided).
@@ -482,7 +481,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
* Time to live for query plans - defaults to 5 minutes.
*/
private int queryPlanTTLSeconds = 60 * 5;
private String queryPlanExplain;
/**
* Set to true to globally disable L2 caching (typically for performance testing).
@@ -1366,17 +1364,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
return readOnlyDatabase;
}
@Override
public DatabaseBuilder shutdownHook(boolean shutdownHook) {
this.shutdownHook = shutdownHook;
return this;
}
@Override
public boolean shutdownHook() {
return shutdownHook;
}
@Override
public DataSource getDataSource() {
return dataSource;
@@ -2149,7 +2136,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
loadDocStoreSettings(p);
defaultServer = p.getBoolean("defaultServer", defaultServer);
shutdownHook = p.getBoolean("shutdownHook", shutdownHook);
readOnlyDatabase = p.getBoolean("readOnlyDatabase", readOnlyDatabase);
autoPersistUpdates = p.getBoolean("autoPersistUpdates", autoPersistUpdates);
loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
@@ -2165,7 +2151,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
queryPlanCapturePeriodSecs = p.getLong("queryPlan.capturePeriodSecs", queryPlanCapturePeriodSecs);
queryPlanCaptureMaxTimeMillis = p.getLong("queryPlan.captureMaxTimeMillis", queryPlanCaptureMaxTimeMillis);
queryPlanCaptureMaxCount = p.getInt("queryPlan.captureMaxCount", queryPlanCaptureMaxCount);
queryPlanExplain = p.get("queryPlan.explain", queryPlanExplain);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
localOnlyL2Cache = p.getBoolean("localOnlyL2Cache", localOnlyL2Cache);
@@ -2417,17 +2402,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
return queryPlanTTLSeconds;
}
@Override
public String getQueryPlanExplain() {
return queryPlanExplain;
}
@Override
public DatabaseBuilder queryPlanExplain(String queryPlanExplain) {
this.queryPlanExplain = queryPlanExplain;
return this;
}
@Override
public DatabaseConfig setQueryPlanTTLSeconds(int queryPlanTTLSeconds) {
this.queryPlanTTLSeconds = queryPlanTTLSeconds;
@@ -167,7 +167,6 @@ public class DatabasePlatform {
* findIterate() and findVisit().
*/
protected boolean forwardOnlyHintOnFindIterate;
protected boolean autoCommitFalseOnFindIterate;
/**
* If set then use the CONCUR_UPDATABLE hint when creating ResultSets.
@@ -529,10 +528,6 @@ public class DatabasePlatform {
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
}
public boolean autoCommitFalseOnFindIterate() {
return autoCommitFalseOnFindIterate;
}
/**
* Return true if the ResultSet CONCUR_UPDATABLE Hint should be used on
* createNativeSqlTree() PreparedStatements.
@@ -785,5 +780,4 @@ public class DatabasePlatform {
public String inlineSqlHint(String hint) {
return "/*+ " + hint + " */ ";
}
}
@@ -97,7 +97,8 @@ public final class ShutdownManager {
private static void registerShutdownHook() {
lock.lock();
try {
if ("true".equalsIgnoreCase(System.getProperty("ebean.registerShutdownHook", "true"))) {
String value = System.getProperty("ebean.registerShutdownHook");
if (value == null || !value.trim().equalsIgnoreCase("false")) {
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
} catch (IllegalStateException ex) {
@@ -1,19 +1,18 @@
package io.ebean.meta;
import java.util.*;
import static java.util.Objects.requireNonNull;
import java.util.HashSet;
import java.util.Set;
/**
* Initiate query plan collection for plans by their hash or all query plans.
*/
public class QueryPlanInit {
private final Map<String,Long> hashes = new HashMap<>();
private boolean all;
private long defaultThresholdMicros;
private Set<String> hashes = new HashSet<>();
private long thresholdMicros;
/**
* Return true if this initiates bind collection on all query plans.
@@ -30,74 +29,39 @@ public class QueryPlanInit {
}
/**
* Return the default query execution time threshold which must be exceeded to initiate
* Return the query execution time threshold which must be exceeded to initiate
* query plan collection.
*/
public long thresholdMicros() {
return defaultThresholdMicros;
return thresholdMicros;
}
/**
* Set the default query execution time threshold which must be exceeded to initiate
* Set the query execution time threshold which must be exceeded to initiate
* query plan collection.
*/
public void thresholdMicros(long thresholdMicros) {
this.defaultThresholdMicros = thresholdMicros;
this.thresholdMicros = thresholdMicros;
}
/**
* Return true if the query plan should be initiated based on it's hash.
*/
public boolean includeHash(String hash) {
return all || hashes.containsKey(hash);
return all || hashes.contains(hash);
}
/**
* Return the specific hashes that we want to collect query plans on.
*
* @param hash The hash of the query plan.
* @param thresholdMicros The threshold in micros to use.
*/
public void add(String hash, long thresholdMicros) {
requireNonNull(hash);
if (!"all".equals(hash)) {
hashes.put(hash, thresholdMicros);
} else {
all = true;
if (thresholdMicros > 0) {
defaultThresholdMicros = thresholdMicros;
}
}
public Set<String> hashes() {
return hashes;
}
/**
* Remove a hash from this request.
* Set the specific hashes that we want to collect query plans on.
*/
public void remove(String hash) {
hashes.remove(hash);
}
/**
* Return the threshold in micros to use for the given hash.
*/
public long thresholdMicros(String hash) {
Long threshold = hashes.get(hash);
return threshold == null || threshold < 1 ? defaultThresholdMicros : threshold;
}
/**
* Return true if there are no registered hashes and not collect <em>All</em> plans.
*/
public boolean isEmpty() {
return !all && hashes.isEmpty();
}
@Override
public String toString() {
return "QueryPlanInit{" +
"all=" + all +
", hashes=" + hashes +
", thresholdMicros=" + defaultThresholdMicros +
'}';
public void hashes(Set<String> hashes) {
this.hashes = hashes;
}
}
@@ -77,9 +77,6 @@ class DatabaseConfigTest {
props.setProperty("readOnlyDatabase", "true");
props.setProperty("lengthCheck", "ON");
props.setProperty("includeLabelInSql", "false");
props.setProperty("lazyLoadBatchSize", "50");
props.setProperty("queryBatchSize", "60");
props.setProperty("shutdownHook", "false");
props.setProperty("queryPlan.enable", "true");
props.setProperty("queryPlan.thresholdMicros", "10000");
@@ -87,7 +84,6 @@ class DatabaseConfigTest {
props.setProperty("queryPlan.capturePeriodSecs", "42");
props.setProperty("queryPlan.captureMaxTimeMillis", "560");
props.setProperty("queryPlan.captureMaxCount", "7");
props.setProperty("queryPlan.explain", "explain (verbose)");
config.loadFromProperties(props);
@@ -101,11 +97,8 @@ class DatabaseConfigTest {
assertTrue(settings.isLoadModuleInfo());
assertTrue(settings.skipDataSourceCheck());
assertTrue(settings.readOnlyDatabase());
assertFalse(settings.shutdownHook());
assertFalse(settings.isIncludeLabelInSql());
assertThat(settings.getLengthCheck()).isEqualTo(LengthCheck.ON);
assertThat(settings.getLazyLoadBatchSize()).isEqualTo(50);
assertThat(settings.getQueryBatchSize()).isEqualTo(60);
assertTrue(settings.isIdGeneratorAutomatic());
assertFalse(settings.getPlatformConfig().isCaseSensitiveCollation());
@@ -136,18 +129,15 @@ class DatabaseConfigTest {
assertEquals(42, settings.getQueryPlanCapturePeriodSecs());
assertEquals(560, settings.getQueryPlanCaptureMaxTimeMillis());
assertEquals(7, settings.getQueryPlanCaptureMaxCount());
assertEquals("explain (verbose)", settings.getQueryPlanExplain());
assertThat(settings.getMappingLocations()).containsExactly("classpath:/foo","bar");
config.persistBatch(PersistBatch.NONE)
.persistBatchOnCascade(PersistBatch.NONE)
.lengthCheck(LengthCheck.ON)
.lengthCheck(LengthCheck.UTF8)
.queryPlanExplain("explain (buffers)");
.lengthCheck(LengthCheck.UTF8);
assertThat(config.settings().getQueryPlanExplain()).isEqualTo("explain (buffers)");
Properties props1 = new Properties();
props1.setProperty("ebean.persistBatch", "ALL");
props1.setProperty("ebean.persistBatchOnCascade", "ALL");
@@ -174,7 +164,6 @@ class DatabaseConfigTest {
DatabaseBuilder.Settings config = new DatabaseConfig().settings();
assertTrue(config.isIdGeneratorAutomatic());
assertTrue(config.isDefaultServer());
assertTrue(config.shutdownHook());
assertFalse(config.isAutoPersistUpdates());
assertFalse(config.skipDataSourceCheck());
@@ -186,8 +175,6 @@ class DatabaseConfigTest {
assertTrue(config.getPlatformConfig().isCaseSensitiveCollation());
assertTrue(config.isAutoLoadModuleInfo());
assertTrue(config.isLoadModuleInfo());
assertThat(config.getLazyLoadBatchSize()).isEqualTo(100);
assertThat(config.getQueryBatchSize()).isEqualTo(100);
assertFalse(config.isQueryPlanEnable());
assertEquals(Long.MAX_VALUE, config.getQueryPlanThresholdMicros());
@@ -195,12 +182,9 @@ class DatabaseConfigTest {
assertEquals(600, config.getQueryPlanCapturePeriodSecs());
assertEquals(10000L, config.getQueryPlanCaptureMaxTimeMillis());
assertEquals(10, config.getQueryPlanCaptureMaxCount());
assertThat(config.getQueryPlanExplain()).isNull();
assertThat(config.getLengthCheck()).isEqualTo(LengthCheck.OFF);
assertTrue(config.isIncludeLabelInSql());
config.shutdownHook(false);
assertFalse(config.shutdownHook());
config.setLoadModuleInfo(false);
assertFalse(config.isAutoLoadModuleInfo());
assertFalse(config.isLoadModuleInfo());
@@ -208,10 +192,6 @@ class DatabaseConfigTest {
assertTrue(config.isAutoPersistUpdates());
config.setSkipDataSourceCheck(true);
assertTrue(config.skipDataSourceCheck());
config.lazyLoadBatchSize(20);
assertThat(config.getLazyLoadBatchSize()).isEqualTo(20);
config.queryBatchSize(30);
assertThat(config.getQueryBatchSize()).isEqualTo(30);
}
@Test
@@ -1,43 +0,0 @@
package io.ebean.meta;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class QueryPlanInitTest {
@Test
void initialQueryPlanInit() {
var init = new QueryPlanInit();
assertThat(init.isEmpty()).isTrue();
assertThat(init.isAll()).isFalse();
assertThat(init.thresholdMicros()).isEqualTo(0L);
}
@Test
void add_all() {
var init = new QueryPlanInit();
init.add("all", 57L);
assertThat(init.isEmpty()).isFalse();
assertThat(init.isAll()).isTrue();
assertThat(init.thresholdMicros()).isEqualTo(57L);
}
@Test
void addWithThresholds() {
var init = new QueryPlanInit();
init.thresholdMicros(1000);
init.add("xOne", 0);
init.add("xTwo", 2000L);
assertThat(init.isEmpty()).isFalse();
assertThat(init.isAll()).isFalse();
assertThat(init.includeHash("xJunk")).isFalse();
assertThat(init.includeHash("xOne")).isTrue();
assertThat(init.includeHash("xTwo")).isTrue();
assertThat(init.thresholdMicros("xJunk")).isEqualTo(1000L);
assertThat(init.thresholdMicros("xOne")).isEqualTo(1000L);
assertThat(init.thresholdMicros("xTwo")).isEqualTo(2000L);
}
}
+26 -26
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
</parent>
<name>ebean bom</name>
@@ -89,25 +89,25 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -125,13 +125,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -155,37 +155,37 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-spring-txn</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<!-- platforms -->
@@ -193,79 +193,79 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-clickhouse</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-db2</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-h2</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-hana</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mariadb</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mysql</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-nuodb</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-oracle</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgres</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlite</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlserver</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
</dependencies>
-32
View File
@@ -1,32 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.ebean</groupId>
<artifactId>ebean-parent</artifactId>
<version>16.1.0</version>
</parent>
<artifactId>ebean-core-json</artifactId>
<name>ebean-core-json</name>
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>HEAD</tag>
</scm>
<dependencies>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
</dependency>
<!-- Jackson core used internally by Ebean -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>
@@ -1,9 +0,0 @@
module io.ebean.core.json {
requires io.ebean.api;
requires transitive com.fasterxml.jackson.core;
exports io.ebeaninternal.json to io.ebean.test, io.ebean.core;
provides io.ebean.service.BootstrapService with io.ebeaninternal.json.DJsonService;
}
@@ -1 +0,0 @@
io.ebeaninternal.json.DJsonService
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
+6 -12
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.1.0</version>
<version>14.10.0</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -22,13 +22,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.1.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-json</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -52,7 +46,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -165,21 +159,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>16.1.0</version>
<version>14.10.0</version>
<scope>test</scope>
</dependency>
@@ -1,6 +1,5 @@
package io.ebeaninternal.api;
import io.ebean.bean.EntityBean;
import java.util.ArrayList;
import java.util.List;
@@ -14,23 +13,10 @@ public final class BindValuesKey {
private final List<Object> values = new ArrayList<>();
private final SpiEbeanServer server;
public BindValuesKey(SpiEbeanServer server) {
this.server = server;
}
/**
* Add a bind value.
*/
public BindValuesKey add(Object value) {
if (value instanceof EntityBean) {
// only interested in id to keep the memory footprint low
Object id = server.beanId(value);
if (id != null) {
value = id;
}
}
values.add(value);
return this;
}
@@ -45,4 +31,5 @@ public final class BindValuesKey {
return values.hashCode();
}
}
@@ -348,8 +348,6 @@ public interface SpiEbeanServer extends SpiServer, BeanCollectionLoader {
<T> FutureList<T> findFutureList(SpiQuery<T> query);
<K, T> FutureMap<K, T> findFutureMap(SpiQuery<T> query);
<T> PagedList<T> findPagedList(SpiQuery<T> query);
<T> Set<T> findSet(SpiQuery<T> query);
@@ -14,7 +14,6 @@ import io.ebeanservice.docstore.api.DocStoreTransaction;
import jakarta.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Instant;
/**
* Extends Transaction with additional API required on server.
@@ -65,12 +64,12 @@ public interface SpiTransaction extends Transaction {
* <p>
* This is to handle bi-directional relationships where both sides Cascade.
*/
void registerDeleteBean(Class<?> type, Object id);
void registerDeleteBean(Integer hash);
/**
* Return true if this is a bean that has already been saved/deleted.
*/
boolean isRegisteredDeleteBean(Class<?> type, Object id);
boolean isRegisteredDeleteBean(Integer hash);
/**
* Unregister the persisted beans. Expected after persisting top level beans
@@ -96,7 +95,7 @@ public interface SpiTransaction extends Transaction {
/**
* Return the start timestamp for the transaction (JVM side).
*/
Instant startTime();
long startNanoTime();
/**
* Return true if this transaction has updateAllLoadedProperties set.
@@ -362,14 +361,4 @@ public interface SpiTransaction extends Transaction {
* Set the transaction to be inactive via external transaction manager.
*/
void deactivateExternal();
/**
* Set autocommit to false for a findIterate query.
* <p>
* This is done for specific platforms that need it, in order to make
* use cursors to stream a large or unbounded query result to the client.
*/
default void setAutoCommitOnFindIterate() {
throw new UnsupportedOperationException();
}
}
@@ -14,7 +14,6 @@ import io.ebeanservice.docstore.api.DocStoreTransaction;
import jakarta.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Instant;
/**
* Proxy for an underlying SpiTransaction (most of the API).
@@ -29,8 +28,8 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public Instant startTime() {
return transaction.startTime();
public long startNanoTime() {
return transaction.startNanoTime();
}
@Override
@@ -195,13 +194,13 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void registerDeleteBean(Class<?> type, Object id) {
transaction.registerDeleteBean(type, id);
public void registerDeleteBean(Integer hash) {
transaction.registerDeleteBean(hash);
}
@Override
public boolean isRegisteredDeleteBean(Class<?> type, Object id) {
return transaction.isRegisteredDeleteBean(type, id);
public boolean isRegisteredDeleteBean(Integer hash) {
return transaction.isRegisteredDeleteBean(hash);
}
@Override
@@ -8,7 +8,11 @@ import io.ebean.service.SpiJsonService;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.*;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Utility that converts between JSON content and simple java Maps/Lists.
@@ -48,8 +52,8 @@ public final class DJsonService implements SpiJsonService {
}
/**
* Parse the json and return as a Map additionally specifying if the returned map should be modify
* aware meaning that it can detect when it has been modified.
* Parse the json and return as a Map additionally specifying if the returned map should
* be modify aware meaning that it can detect when it has been modified.
*/
@Override
public Map<String, Object> parseObject(String json, boolean modifyAware) throws IOException {
@@ -90,8 +94,9 @@ public final class DJsonService implements SpiJsonService {
/**
* Parse the json and return as a Map taking a JsonParser and a starting token.
*
* <p>Used when the first token is checked to see if the value is null prior to calling this.
* <p>
* Used when the first token is checked to see if the value is null prior to calling this.
* </p>
*/
@Override
public Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
@@ -8,23 +8,15 @@ import io.ebean.ModifyAwareType;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.*;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
final class EJsonReader {
static final JsonFactory json = new JsonFactory();
private final JsonParser parser;
private final boolean modifyAware;
private final ModifyAwareFlag modifyAwareOwner;
private int depth;
private Stack stack;
private Context currentContext;
EJsonReader(JsonParser parser, boolean modifyAware) {
this.parser = parser;
this.modifyAware = modifyAware;
this.modifyAwareOwner = modifyAware ? new ModifyAwareFlag() : null;
}
@SuppressWarnings("unchecked")
static Map<String, Object> parseObject(String json, boolean modifyAware) throws IOException {
@@ -110,6 +102,24 @@ final class EJsonReader {
return new EJsonReader(parser, modifyAware).parseJson(token);
}
private final JsonParser parser;
private final boolean modifyAware;
private final ModifyAwareFlag modifyAwareOwner;
private int depth;
private Stack stack;
private Context currentContext;
EJsonReader(JsonParser parser, boolean modifyAware) {
this.parser = parser;
this.modifyAware = modifyAware;
this.modifyAwareOwner = (modifyAware) ? new ModifyAwareFlag() : null;
}
private void startArray() {
depth++;
stack.push(currentContext);
@@ -187,6 +197,7 @@ final class EJsonReader {
*/
private void processJsonToken(JsonToken token) throws IOException {
switch (token) {
case START_ARRAY:
startArray();
break;
@@ -262,17 +273,17 @@ final class EJsonReader {
}
}
private abstract static class Context {
private static abstract class Context {
Context next;
abstract void popContext(Context temp);
abstract Object getValue();
abstract void setValue(Object value);
abstract void setKey(String key);
abstract void setValue(Object value);
abstract void setValueNull();
}
@@ -301,13 +312,13 @@ final class EJsonReader {
}
@Override
void setValue(Object value) {
map.put(key, value);
void setKey(String key) {
this.key = key;
}
@Override
void setKey(String key) {
this.key = key;
void setValue(Object value) {
map.put(key, value);
}
@Override
@@ -353,4 +364,5 @@ final class EJsonReader {
// not expected
}
}
}
@@ -20,11 +20,6 @@ final class EJsonWriter {
* Base jsonFactory implementation used when it is not passed in.
*/
static final JsonFactory jsonFactory = new JsonFactory();
private final JsonGenerator jsonGenerator;
private EJsonWriter(JsonGenerator jsonGenerator) {
this.jsonGenerator = jsonGenerator;
}
static String write(Object object) throws IOException {
StringWriter writer = new StringWriter(200);
@@ -47,6 +42,12 @@ final class EJsonWriter {
new EJsonWriter(jsonGenerator).writeCollection(null, collection);
}
private final JsonGenerator jsonGenerator;
private EJsonWriter(JsonGenerator jsonGenerator) {
this.jsonGenerator = jsonGenerator;
}
private void writeJson(Object object) {
writeJson(null, object);
}
@@ -209,4 +210,5 @@ final class EJsonWriter {
}
jsonGenerator.writeEndObject();
}
}
@@ -22,4 +22,5 @@ public final class ModifyAwareFlag implements ModifyAwareType, Serializable {
public void setMarkedDirty(boolean markedDirty) {
this.markedDirty = markedDirty;
}
}
@@ -14,8 +14,9 @@ public final class ModifyAwareIterator<E> implements Iterator<E> {
/**
* Create with an Owner and the underlying Iterator this wraps.
*
* <p>The owner is notified of the removals.
* <p>
* The owner is notified of the removals.
* </p>
*/
public ModifyAwareIterator(ModifyAwareType owner, Iterator<E> it) {
this.owner = owner;
@@ -37,4 +38,5 @@ public final class ModifyAwareIterator<E> implements Iterator<E> {
owner.setMarkedDirty(true);
it.remove();
}
}
@@ -34,11 +34,11 @@ public final class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareType, S
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof ModifyAwareMap) {
ModifyAwareMap<?, ?> that = (ModifyAwareMap<?, ?>) o;
ModifyAwareMap<?,?> that = (ModifyAwareMap<?,?>) o;
return Objects.equals(map, that.map);
}
if (!(o instanceof Map)) return false;
Map<?, ?> that = (Map<?, ?>) o;
Map<?,?> that = (Map<?,?>) o;
return Objects.equals(map, that);
}
@@ -112,6 +112,7 @@ public final class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareType, S
map.putAll(m);
}
@Override
public void clear() {
if (!map.isEmpty()) {
@@ -134,4 +135,5 @@ public final class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareType, S
public Set<Map.Entry<K, V>> entrySet() {
return new ModifyAwareSet<>(this, map.entrySet());
}
}
@@ -23,7 +23,7 @@ public class DefaultServerQueryCache extends DefaultServerCache {
@Override
protected Object unwrapEntry(CacheEntry entry) {
return ((QueryCacheEntry) entry.getValue()).value();
return ((QueryCacheEntry) entry.getValue()).getValue();
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.core;
import io.ebean.CancelableQuery;
import io.ebean.Transaction;
import io.ebean.util.JdbcClose;
import io.ebeaninternal.api.*;
import io.ebeaninternal.server.persist.Binder;
@@ -97,13 +98,6 @@ public abstract class AbstractSqlQueryRequest implements CancelableQuery {
public void setDefaultFetchBuffer(int fetchSize) {
query.setDefaultFetchBuffer(fetchSize);
}
public void setAutoCommitOnFindIterate() {
if (createdTransaction) {
transaction.setAutoCommitOnFindIterate();
}
}
/**
* Close the underlying resources.
*/
@@ -162,10 +162,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.scriptRunner = new DScriptRunner(this);
configureServerPlugins();
if (this.config.shutdownHook() && "true".equalsIgnoreCase(System.getProperty("ebean.registerShutdownHook", "true"))) {
// register with the JVM Shutdown hook
ShutdownManager.registerDatabase(this);
}
// Register with the JVM Shutdown hook
ShutdownManager.registerDatabase(this);
}
/**
@@ -280,7 +278,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
@Nullable
public DataSource readOnlyDataSource() {
return transactionManager.readOnlyDataSource();
}
@@ -1252,55 +1249,66 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
private <T> SpiQuery<T> configureForFuture(SpiQuery<T> query) {
query.usingFuture();
if (query.transaction() == null) {
// use a current transaction if active
query.usingTransaction(currentServerTransaction());
}
return query;
}
@Override
public <T> FutureRowCount<T> findFutureCount(SpiQuery<T> query) {
SpiQuery<T> copy = configureForFuture(query.copy());
var queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<>(this, copy));
SpiQuery<T> copy = query.copy();
copy.usingFuture();
boolean createdTransaction = false;
SpiTransaction transaction = query.transaction();
if (transaction == null) {
transaction = currentServerTransaction();
if (transaction == null) {
transaction = (SpiTransaction) createTransaction();
createdTransaction = true;
}
copy.usingTransaction(transaction);
}
var queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<>(this, copy, createdTransaction));
backgroundExecutor.execute(queryFuture.futureTask());
return queryFuture;
}
@Override
public <T> FutureIds<T> findFutureIds(SpiQuery<T> query) {
SpiQuery<T> copy = configureForFuture(query.copy());
final var queryFuture = new QueryFutureIds<T>(new CallableQueryIds<>(this, copy));
SpiQuery<T> copy = query.copy();
copy.usingFuture();
boolean createdTransaction = false;
SpiTransaction transaction = query.transaction();
if (transaction == null) {
transaction = currentServerTransaction();
if (transaction == null) {
transaction = (SpiTransaction) createTransaction();
createdTransaction = true;
}
copy.usingTransaction(transaction);
}
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<>(this, copy, createdTransaction));
backgroundExecutor.execute(queryFuture.futureTask());
return queryFuture;
}
@Override
public <T> FutureList<T> findFutureList(SpiQuery<T> query) {
SpiQuery<T> spiQuery = configureForFuture(query.copy());
// FutureList query always run in its own persistence content
SpiQuery<T> spiQuery = query.copy();
spiQuery.usingFuture();
// FutureList query always run in it's own persistence content
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
if (!spiQuery.isDisableReadAudit()) {
BeanDescriptor<T> desc = descriptorManager.descriptor(spiQuery.getBeanType());
desc.readAuditFutureList(spiQuery);
}
final var queryFuture = new QueryFutureList<T>(new CallableQueryList<>(this, spiQuery));
backgroundExecutor.execute(queryFuture.futureTask());
return queryFuture;
}
@Override
public <K, T> FutureMap<K, T> findFutureMap(SpiQuery<T> query) {
SpiQuery<T> spiQuery = configureForFuture(query.copy());
// FutureMap query always run in it's own persistence content
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
if (!spiQuery.isDisableReadAudit()) {
BeanDescriptor<T> desc = descriptorManager.descriptor(spiQuery.getBeanType());
desc.readAuditFutureList(spiQuery);
// Create a new transaction solely to execute the findList() at some future time
boolean createdTransaction = false;
SpiTransaction transaction = query.transaction();
if (transaction == null) {
transaction = currentServerTransaction();
if (transaction == null) {
transaction = (SpiTransaction) createTransaction();
createdTransaction = true;
}
spiQuery.usingTransaction(transaction);
}
final var queryFuture = new QueryFutureMap<K, T>(new CallableQueryMap<>(this, spiQuery));
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<>(this, spiQuery, createdTransaction));
backgroundExecutor.execute(queryFuture.futureTask());
return queryFuture;
}
@@ -301,12 +301,12 @@ public final class InternalConfiguration {
}
DtoQueryEngine createDtoQueryEngine() {
return new DtoQueryEngine(binder, config.getJdbcFetchSizeFindEach(), config.getJdbcFetchSizeFindList(), databasePlatform.autoCommitFalseOnFindIterate());
return new DtoQueryEngine(binder, config.getJdbcFetchSizeFindEach(), config.getJdbcFetchSizeFindList());
}
RelationalQueryEngine createRelationalQueryEngine() {
return new DefaultRelationalQueryEngine(binder, config.getDatabaseBooleanTrue(), config.getPlatformConfig().getDbUuid().useBinaryOptimized(),
config.getJdbcFetchSizeFindEach(), config.getJdbcFetchSizeFindList(), databasePlatform.autoCommitFalseOnFindIterate());
config.getJdbcFetchSizeFindEach(), config.getJdbcFetchSizeFindList());
}
OrmQueryEngine createOrmQueryEngine() {
@@ -585,32 +585,27 @@ public final class InternalConfiguration {
return QueryPlanManager.NOOP;
}
long threshold = config.getQueryPlanThresholdMicros();
return new CQueryPlanManager(transactionManager, threshold, queryPlanLogger(databasePlatform.platform(), config), extraMetrics);
return new CQueryPlanManager(transactionManager, threshold, queryPlanLogger(databasePlatform.platform()), extraMetrics);
}
/**
* Returns the logger to log query plans for the given platform.
*/
QueryPlanLogger queryPlanLogger(Platform platform, DatabaseBuilder.Settings config) {
QueryPlanLogger queryPlanLogger(Platform platform) {
switch (platform.base()) {
case SQLSERVER:
return new QueryPlanLoggerSqlServer();
case ORACLE:
return new QueryPlanLoggerOracle();
case POSTGRES:
return new QueryPlanLoggerExplain(explain(config, "explain (analyze, costs, verbose, buffers) "));
return new QueryPlanLoggerExplain("explain (analyze, buffers) ");
case YUGABYTE:
return new QueryPlanLoggerExplain(explain(config,"explain (analyze, buffers, dist) "));
return new QueryPlanLoggerExplain("explain (analyze, buffers, dist) ");
default:
return new QueryPlanLoggerExplain(explain(config,"explain "));
return new QueryPlanLoggerExplain("explain ");
}
}
private static String explain(DatabaseBuilder.Settings config, String defaultExplain) {
String explain = config.getQueryPlanExplain();
return explain == null ? defaultExplain : explain + ' ';
}
/**
* Return the DDL generator.
*/
@@ -665,7 +665,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
}
public void putToQueryCache(Object result) {
beanDescriptor.queryCachePut(cacheKey, new QueryCacheEntry(result, dependentTables, transaction.startTime()));
beanDescriptor.queryCachePut(cacheKey, new QueryCacheEntry(result, dependentTables, transaction.startNanoTime()));
}
/**
@@ -786,10 +786,4 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
beanDescriptor.freeze(bean);
}
}
public void setAutoCommitOnFindIterate() {
if (createdTransaction) {
transaction.setAutoCommitOnFindIterate();
}
}
}
@@ -54,6 +54,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* The unique id used for logging summary.
*/
private Object idValue;
/**
* Hash value used to handle cascade delete both ways in a relationship.
*/
private Integer beanHash;
private boolean statelessUpdate;
private boolean notifyCache;
/**
@@ -281,9 +285,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private void onFailedUpdateUndoGeneratedProperties() {
for (BeanProperty prop : beanDescriptor.propertiesGenUpdate()) {
Object oldVal = intercept.origValue(prop.propertyIndex());
if (oldVal != null) {
prop.setValue(entityBean, oldVal);
}
prop.setValue(entityBean, oldVal);
}
}
@@ -551,17 +553,34 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
}
/**
* The hash used to register the bean with the transaction.
* <p>
* Takes into account the class type and id value.
*/
private Integer beanHash() {
if (beanHash == null) {
Object id = beanDescriptor.getId(entityBean);
int hc = 92821 * bean.getClass().getName().hashCode();
if (id != null) {
hc += id.hashCode();
}
beanHash = hc;
}
return beanHash;
}
public void registerDeleteBean() {
final Object id = beanDescriptor.id(entityBean);
transaction.registerDeleteBean(beanDescriptor.type(), id);
Integer hash = beanHash();
transaction.registerDeleteBean(hash);
}
public boolean isRegisteredForDeleteBean() {
if (transaction == null) {
return false;
} else {
final Object id = beanDescriptor.id(entityBean);
return transaction.isRegisteredDeleteBean(beanDescriptor.type(), id);
Integer hash = beanHash();
return transaction.isRegisteredDeleteBean(hash);
}
}
@@ -790,9 +809,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
public void checkRowCount(int rowCount) {
if (rowCount != 1 && rowCount != Statement.SUCCESS_NO_INFO) {
if (ConcurrencyMode.VERSION == concurrencyMode) {
if (type == Type.UPDATE) {
onFailedUpdateUndoGeneratedProperties();
}
onFailedUpdateUndoGeneratedProperties();
throw new OptimisticLockException("Data has changed. updated row count " + rowCount, null, bean);
} else if (rowCount == 0 && type == Type.UPDATE) {
throw new EntityNotFoundException("No rows updated");
@@ -1471,7 +1471,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
void queryPlanInit(QueryPlanInit request, List<MetaQueryPlan> list) {
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (request.includeHash(queryPlan.hash())) {
queryPlan.queryPlanInit(request.thresholdMicros(queryPlan.hash()));
queryPlan.queryPlanInit(request.thresholdMicros());
list.add(queryPlan.createMeta(null, null));
}
}
@@ -58,7 +58,7 @@ final class BeanEmbeddedMetaFactory {
}
private static int dbLength(Column override, BeanProperty source) {
return (override != null && (override.length() != 0)) ? override.length() : source.dbLength();
return (override != null && (override.length() != 255)) ? override.length() : source.dbLength();
}
private static int dbScale(Column override, BeanProperty source) {
@@ -436,15 +436,14 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
}
TableJoinColumn[] cols = join.columns();
if (!idProp.isEmbedded()) {
// simple single scalar id, match on the foreign column, allow extra TableJoinColumn for #3664
String matchColumn = idProp.dbColumn();
for (TableJoinColumn col : cols) {
if (matchColumn.equals(col.getForeignDbColumn())) {
return createImportedScalar(owner, col, new BeanProperty[]{idProp}, others);
}
// simple single scalar id
if (cols.length != 1) {
CoreLog.log.log(ERROR, "No Imported Id column for {0} in table {1}", idProp, join.getTable());
return null;
} else {
BeanProperty[] idProps = {idProp};
return createImportedScalar(owner, cols[0], idProps, others);
}
CoreLog.log.log(ERROR, "No Imported Id column for {0} in table {1}", idProp, join.getTable());
return null;
} else {
// embedded id
BeanPropertyAssocOne<?> embProp = (BeanPropertyAssocOne<?>) idProp;
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.deploy;
import io.ebean.Query;
import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import io.ebean.ValuePair;
@@ -124,9 +125,15 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
// no imported or exported information
} else if (!oneToOneExported) {
importedId = createImportedId(this, targetDescriptor, tableJoin);
if (importedId == null) {
throw new PersistenceException("Cannot find imported id for " + fullName() + " from " + targetDescriptor
+ ". If using native-image, possibly missing reflect-config for the Id property.");
if (importedId.isScalar()) {
// limit JoinColumn mapping to the @Id / primary key
TableJoinColumn[] columns = tableJoin.columns();
String foreignJoinColumn = columns[0].getForeignDbColumn();
String foreignIdColumn = targetDescriptor.idProperty().dbColumn();
if (!foreignJoinColumn.equalsIgnoreCase(foreignIdColumn)) {
throw new PersistenceException("Mapping limitation - @JoinColumn on " + fullName() + " needs to map to a primary key as per Issue #529 "
+ " - joining to " + foreignJoinColumn + " and not " + foreignIdColumn);
}
}
} else {
exportedProperties = createExported();
@@ -264,6 +264,6 @@ public final class IdBinderSimple implements IdBinder {
@Override
public String idNullOr(String prefix, String filterManyExpression) {
return "(${" + prefix + "}" + idProperty.dbColumn() + " is null or (" + filterManyExpression + "))";
return "(${" + prefix + "}" + idProperty.name() + " is null or (" + filterManyExpression + "))";
}
}
@@ -98,7 +98,7 @@ public abstract class AnnotationParser extends AnnotationBase {
prop.setUnique(columnAnn.unique());
if (columnAnn.precision() > 0) {
prop.setDbLength(columnAnn.precision());
} else if (columnAnn.length() != 0) {
} else if (columnAnn.length() != 255) {
// set default 255 on DbTypeMap
prop.setDbLength(columnAnn.length());
}
@@ -726,7 +726,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
*/
@Override
public <T> Junction<T> conjunction(Query<T> query) {
return new JunctionExpression<>(Junction.Type.AND, query, this, query.where());
return new JunctionExpression<>(Junction.Type.AND, query, query.where());
}
/**
@@ -734,7 +734,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
*/
@Override
public <T> Junction<T> disjunction(Query<T> query) {
return new JunctionExpression<>(Junction.Type.OR, query, this, query.where());
return new JunctionExpression<>(Junction.Type.OR, query, query.where());
}
/**
@@ -742,7 +742,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
*/
@Override
public <T> Junction<T> conjunction(Query<T> query, ExpressionList<T> parent) {
return new JunctionExpression<>(Junction.Type.AND, query, this, parent);
return new JunctionExpression<>(Junction.Type.AND, query, parent);
}
/**
@@ -750,14 +750,14 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
*/
@Override
public <T> Junction<T> disjunction(Query<T> query, ExpressionList<T> parent) {
return new JunctionExpression<>(Junction.Type.OR, query, this, parent);
return new JunctionExpression<>(Junction.Type.OR, query, parent);
}
/**
* Return a list of expressions that are wrapped by NOT.
*/
public <T> Junction<T> junction(Junction.Type type, Query<T> query) {
return new JunctionExpression<>(type, query, this, query.where());
return new JunctionExpression<>(type, query, query.where());
}
/**
@@ -765,6 +765,6 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
*/
@Override
public <T> Junction<T> junction(Junction.Type type, Query<T> query, ExpressionList<T> parent) {
return new JunctionExpression<>(type, query, this, parent);
return new JunctionExpression<>(type, query, parent);
}
}
@@ -40,8 +40,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
/**
* Construct for Text root expression list - this handles implicit Bool Should, Must etc.
*/
public DefaultExpressionList(Query<T> query, boolean textRoot) {
this(query, query.getExpressionFactory(), null, new ArrayList<>(), textRoot);
public DefaultExpressionList(Query<T> query) {
this(query, query.getExpressionFactory(), null, new ArrayList<>(), true);
}
public DefaultExpressionList(Query<T> query, ExpressionList<T> parentExprList) {
this(query, query.getExpressionFactory(), parentExprList, new ArrayList<>());
}
DefaultExpressionList(Query<T> query, ExpressionFactory expr, ExpressionList<T> parentExprList, List<SpiExpression> list) {
@@ -24,9 +24,9 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
DefaultExpressionList<T> exprList;
Junction.Type type;
JunctionExpression(Junction.Type type, Query<T> query, ExpressionFactory expr, ExpressionList<T> parent) {
JunctionExpression(Junction.Type type, Query<T> query, ExpressionList<T> parent) {
this.type = type;
this.exprList = new DefaultExpressionList<>(query, expr, parent, new ArrayList<>());
this.exprList = new DefaultExpressionList<>(query, parent);
}
/**
@@ -97,7 +97,7 @@ public final class DLoadContext implements LoadContext {
this.profilingListener = query.profilingListener();
this.planLabel = query.planLabel();
this.profileLocation = query.profileLocation();
this.secondaryProperties = query.isUnmodifiable() ? new HashSet<>() : null;
this.secondaryProperties = query.isUnmodifiable() || query.isDisableLazyLoading() ? new HashSet<>() : null;
ObjectGraphNode parentNode = query.parentNode();
if (parentNode != null) {
@@ -37,14 +37,12 @@ public final class CQueryEngine {
private final CQueryBuilder queryBuilder;
private final CQueryHistorySupport historySupport;
private final DatabasePlatform dbPlatform;
private final boolean autoCommitFalseOnFindIterate;
public CQueryEngine(DatabaseBuilder.Settings config, DatabasePlatform dbPlatform, Binder binder, Map<String, String> asOfTableMapping, Map<String, String> draftTableMap) {
this.dbPlatform = dbPlatform;
this.defaultFetchSizeFindEach = config.getJdbcFetchSizeFindEach();
this.defaultFetchSizeFindList = config.getJdbcFetchSizeFindList();
this.forwardOnlyHintOnFindIterate = dbPlatform.forwardOnlyHintOnFindIterate();
this.autoCommitFalseOnFindIterate = dbPlatform.autoCommitFalseOnFindIterate();
this.historySupport = new CQueryHistorySupport(dbPlatform.historySupport(), asOfTableMapping, config.getAsOfSysPeriod());
this.queryBuilder = new CQueryBuilder(config, dbPlatform, binder, historySupport, new CQueryDraftSupport(draftTableMap));
}
@@ -183,9 +181,6 @@ public final class CQueryEngine {
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
if (autoCommitFalseOnFindIterate) {
request.setAutoCommitOnFindIterate();
}
if (!cquery.prepareBindExecuteQueryForwardOnly(forwardOnlyHintOnFindIterate)) {
// query has been cancelled already
return null;
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.query;
import io.ebean.Transaction;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
@@ -10,9 +11,11 @@ abstract class CallableQuery<T> {
final SpiQuery<T> query;
final SpiEbeanServer server;
final Transaction transaction;
CallableQuery(SpiEbeanServer server, SpiQuery<T> query) {
this.server = server;
this.query = query;
this.transaction = query.transaction();
}
}
@@ -10,8 +10,15 @@ import java.util.concurrent.Callable;
*/
public final class CallableQueryCount<T> extends CallableQuery<T> implements Callable<Integer> {
public CallableQueryCount(SpiEbeanServer server, SpiQuery<T> query) {
private final boolean createdTransaction;
/**
* Note that the transaction passed in is always a new transaction solely to
* find the row count so it must be cleaned up by this CallableQueryRowCount.
*/
public CallableQueryCount(SpiEbeanServer server, SpiQuery<T> query, boolean createdTransaction) {
super(server, query);
this.createdTransaction = createdTransaction;
}
/**
@@ -19,7 +26,13 @@ public final class CallableQueryCount<T> extends CallableQuery<T> implements Cal
*/
@Override
public Integer call() {
return server.findCountWithCopy(query);
try {
return server.findCountWithCopy(query);
} finally {
if (createdTransaction) {
transaction.end();
}
}
}
}
@@ -11,16 +11,28 @@ import java.util.concurrent.Callable;
*/
public final class CallableQueryIds<T> extends CallableQuery<T> implements Callable<List<Object>> {
public CallableQueryIds(SpiEbeanServer server, SpiQuery<T> query) {
private final boolean createdTransaction;
public CallableQueryIds(SpiEbeanServer server, SpiQuery<T> query, boolean createdTransaction) {
super(server, query);
this.createdTransaction = createdTransaction;
}
/**
* Execute the find Id's query returning the list of Id's.
*/
@Override
public List<Object> call() {
// we have already made a copy of the query
// this way the same query instance is available to the
// QueryFutureIds (as so has access to the List before it is done)
return server.findIdsWithCopy(query);
try {
return server.findIdsWithCopy(query);
} finally {
if (createdTransaction) {
transaction.end();
}
}
}
}
@@ -11,13 +11,25 @@ import java.util.concurrent.Callable;
*/
public final class CallableQueryList<T> extends CallableQuery<T> implements Callable<List<T>> {
public CallableQueryList(SpiEbeanServer server, SpiQuery<T> query) {
private final boolean createdTransaction;
public CallableQueryList(SpiEbeanServer server, SpiQuery<T> query, boolean createdTransaction) {
super(server, query);
this.createdTransaction = createdTransaction;
}
/**
* Execute the query returning the resulting List.
*/
@Override
public List<T> call() {
return server.findList(query);
try {
return server.findList(query);
} finally {
if (createdTransaction) {
transaction.end();
}
}
}
}
@@ -1,23 +0,0 @@
package io.ebeaninternal.server.query;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* Represent the findMap query as a Callable.
*/
public final class CallableQueryMap<K, T> extends CallableQuery<T> implements Callable<Map<K, T>> {
public CallableQueryMap(SpiEbeanServer server, SpiQuery<T> query) {
super(server, query);
}
@Override
public Map<K, T> call() {
return server.findMap(query);
}
}
@@ -15,8 +15,8 @@ final class DFetchGroup<T> implements SpiFetchGroup<T> {
}
@Override
public OrmQueryDetail detail(OrmQueryDetail existing) {
return detail.copy(existing);
public OrmQueryDetail detail() {
return detail.copy();
}
@Override
@@ -52,7 +52,7 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
@SuppressWarnings("rawtypes")
@Override
public Query<T> select(FetchGroup fetchGroup) {
this.detail = ((SpiFetchGroup) fetchGroup).detail(detail);
this.detail = ((SpiFetchGroup) fetchGroup).detail();
return this;
}
@@ -365,11 +365,6 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public <K> FutureMap<K,T> findFutureMap() {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public PagedList<T> findPagedList() {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
@@ -15,8 +15,6 @@ import io.ebeaninternal.server.core.RowReader;
import io.ebeaninternal.server.persist.Binder;
import jakarta.persistence.PersistenceException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@@ -30,18 +28,15 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
private final Binder binder;
private final String dbTrueValue;
private final boolean binaryOptimizedUUID;
private final boolean autoCommitFalseOnFindIterate;
private final TimedMetricMap timedMetricMap;
private final int defaultFetchSizeFindEach;
private final int defaultFetchSizeFindList;
public DefaultRelationalQueryEngine(Binder binder, String dbTrueValue, boolean binaryOptimizedUUID,
int defaultFetchSizeFindEach, int defaultFetchSizeFindList,
boolean autoCommitFalseOnFindIterate) {
int defaultFetchSizeFindEach, int defaultFetchSizeFindList) {
this.binder = binder;
this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue;
this.binaryOptimizedUUID = binaryOptimizedUUID;
this.autoCommitFalseOnFindIterate = autoCommitFalseOnFindIterate;
this.timedMetricMap = MetricFactory.get().createTimedMetricMap("sql.query.");
this.defaultFetchSizeFindEach = defaultFetchSizeFindEach;
this.defaultFetchSizeFindList = defaultFetchSizeFindList;
@@ -66,20 +61,13 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
return "Query threw SQLException:" + msg + " Query was:" + sql;
}
private <T> void prepareForIterate(RelationalQueryRequest request) throws SQLException {
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
if (autoCommitFalseOnFindIterate) {
request.setAutoCommitOnFindIterate();
}
request.executeSql(binder, SpiQuery.Type.ITERATE);
}
@Override
public void findEach(RelationalQueryRequest request, RowConsumer consumer) {
try {
prepareForIterate(request);
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
request.executeSql(binder, SpiQuery.Type.ITERATE);
request.mapEach(consumer);
request.logSummary();
@@ -94,7 +82,10 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
@Override
public <T> void findEach(RelationalQueryRequest request, RowReader<T> reader, Predicate<T> consumer) {
try {
prepareForIterate(request);
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
request.executeSql(binder, SpiQuery.Type.ITERATE);
while (request.next()) {
if (!consumer.test(reader.read())) {
break;
@@ -17,13 +17,11 @@ public final class DtoQueryEngine {
private final Binder binder;
private final int defaultFetchSizeFindEach;
private final int defaultFetchSizeFindList;
private final boolean autoCommitFalseOnFindIterate;
public DtoQueryEngine(Binder binder, int defaultFetchSizeFindEach, int defaultFetchSizeFindList, boolean autoCommitFalseOnFindIterate) {
public DtoQueryEngine(Binder binder, int defaultFetchSizeFindEach, int defaultFetchSizeFindList) {
this.binder = binder;
this.defaultFetchSizeFindEach = defaultFetchSizeFindEach;
this.defaultFetchSizeFindList = defaultFetchSizeFindList;
this.autoCommitFalseOnFindIterate = autoCommitFalseOnFindIterate;
}
public <T> List<T> findList(DtoQueryRequest<T> request) {
@@ -45,19 +43,12 @@ public final class DtoQueryEngine {
}
}
private <T> void prepareForIterate(DtoQueryRequest<T> request) throws SQLException {
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
if (autoCommitFalseOnFindIterate) {
request.setAutoCommitOnFindIterate();
}
request.executeSql(binder, SpiQuery.Type.ITERATE);
}
public <T> QueryIterator<T> findIterate(DtoQueryRequest<T> request) {
try {
prepareForIterate(request);
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
request.executeSql(binder, SpiQuery.Type.ITERATE);
return new DtoQueryIterator<>(request);
} catch (SQLException e) {
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
@@ -66,7 +57,10 @@ public final class DtoQueryEngine {
public <T> void findEach(DtoQueryRequest<T> request, Consumer<T> consumer) {
try {
prepareForIterate(request);
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
request.executeSql(binder, SpiQuery.Type.ITERATE);
while (request.next()) {
consumer.accept(request.readNextBean());
}
@@ -79,8 +73,11 @@ public final class DtoQueryEngine {
public <T> void findEach(DtoQueryRequest<T> request, int batchSize, Consumer<List<T>> consumer) {
try {
prepareForIterate(request);
List<T> buffer = new ArrayList<>();
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
request.executeSql(binder, SpiQuery.Type.ITERATE);
while (request.next()) {
buffer.add(request.readNextBean());
if (buffer.size() >= batchSize) {
@@ -101,7 +98,10 @@ public final class DtoQueryEngine {
public <T> void findEachWhile(DtoQueryRequest<T> request, Predicate<T> consumer) {
try {
prepareForIterate(request);
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
request.executeSql(binder, SpiQuery.Type.ITERATE);
while (request.next()) {
if (!consumer.test(request.readNextBean())) {
break;
@@ -23,6 +23,10 @@ public final class QueryFutureIds<T> extends BaseFuture<List<Object>> implements
return futureTask;
}
public Transaction transaction() {
return call.transaction;
}
@Override
public Query<T> getQuery() {
return call.query;
@@ -27,6 +27,10 @@ public final class QueryFutureList<T> extends BaseFuture<List<T>> implements Fut
return futureTask;
}
public Transaction transaction() {
return call.transaction;
}
@Override
public Query<T> getQuery() {
return call.query;
@@ -1,71 +0,0 @@
package io.ebeaninternal.server.query;
import io.ebean.FutureMap;
import io.ebean.Query;
import io.ebean.Transaction;
import jakarta.persistence.PersistenceException;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Default implementation for FutureMap.
*/
public final class QueryFutureMap<K, T> extends BaseFuture<Map<K, T>> implements FutureMap<K, T> {
private final CallableQueryMap<K, T> call;
public QueryFutureMap(CallableQueryMap<K, T> call) {
super(new FutureTask<>(call));
this.call = call;
}
public FutureTask<Map<K, T>> futureTask() {
return futureTask;
}
@Override
public Query<T> getQuery() {
return call.query;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
call.query.cancel();
return super.cancel(mayInterruptIfRunning);
}
@Override
public Map<K, T> getUnchecked() {
try {
return get();
} catch (InterruptedException e) {
// restore the interrupted status (so client can check for that)
Thread.currentThread().interrupt();
throw new PersistenceException(e);
} catch (ExecutionException e) {
throw new PersistenceException(e);
}
}
@Override
public Map<K, T> getUnchecked(long timeout, TimeUnit unit) throws TimeoutException {
try {
return get(timeout, unit);
} catch (InterruptedException e) {
// restore the interrupted status (so client can check for that)
Thread.currentThread().interrupt();
throw new PersistenceException(e);
} catch (ExecutionException e) {
throw new PersistenceException(e);
}
}
}
@@ -21,6 +21,10 @@ public final class QueryFutureRowCount<T> extends BaseFuture<Integer> implements
return futureTask;
}
public Transaction transaction() {
return call.transaction;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
call.query.cancel();
@@ -32,6 +32,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
private final boolean readIdNormal;
private final boolean disableLazyLoad;
private final boolean unmodifiable;
private final boolean loadListReferences;
private final InheritInfo inheritInfo;
final String prefix;
private final Map<String, String> pathMap;
@@ -55,6 +56,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
this.readIdNormal = readId && !temporalVersions;
this.disableLazyLoad = node.disableLazyLoad;
this.unmodifiable = node.unmodifiable;
this.loadListReferences = !unmodifiable && !disableLazyLoad;
this.partialObject = node.partialObject;
this.properties = node.properties;
this.pathMap = node.pathMap;
@@ -301,7 +303,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
boolean forceNewReference = queryMode == Mode.REFRESH_BEAN;
for (STreePropertyAssocMany many : localDesc.propsMany()) {
if (many != loadingChildProperty) {
if (!unmodifiable || ctx.includeSecondary(many.asMany())) {
if (loadListReferences || ctx.includeSecondary(many.asMany())) {
// create a proxy for the many (deferred fetching)
BeanCollection<?> ref = many.createReference(localBean, forceNewReference);
if (ref != null) {
@@ -76,7 +76,6 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean {
@Override
public void dependentTables(Set<String> tables) {
tables.add(baseTable);
for (SqlTreeNode child : children) {
child.dependentTables(tables);
}
@@ -769,7 +769,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
copy.useQueryCache = useQueryCache;
copy.unmodifiable = unmodifiable;
if (detail != null) {
copy.detail = detail.copy(null);
copy.detail = detail.copy();
}
copy.temporalMode = temporalMode;
copy.firstRow = firstRow;
@@ -1244,7 +1244,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
public final HashQuery queryHash() {
// calculateQueryPlanHash is called just after potential AutoTune tuning
// so queryPlanHash is calculated well before this method is called
BindValuesKey bindKey = new BindValuesKey(server);
BindValuesKey bindKey = new BindValuesKey();
queryBindKey(bindKey);
return new HashQuery(queryPlanKey, bindKey);
}
@@ -1387,7 +1387,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
@Override
public final Query<T> select(FetchGroup<T> fetchGroup) {
if (fetchGroup != null) {
this.detail = ((SpiFetchGroup<T>) fetchGroup).detail(detail);
this.detail = ((SpiFetchGroup<T>) fetchGroup).detail();
}
return this;
}
@@ -1614,11 +1614,6 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
return server.findFutureList(this);
}
@Override
public final <K> FutureMap<K, T> findFutureMap() {
return server.findFutureMap(this);
}
@Override
public final FutureRowCount<T> findFutureCount() {
return server.findFutureCount(this);
@@ -1895,7 +1890,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
public final ExpressionList<T> text() {
if (textExpressions == null) {
useDocStore = true;
textExpressions = new DefaultExpressionList<>(this, true);
textExpressions = new DefaultExpressionList<>(this);
}
return textExpressions;
}
@@ -1903,7 +1898,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
@Override
public final ExpressionList<T> where() {
if (whereExpressions == null) {
whereExpressions = new DefaultExpressionList<>(this, false);
whereExpressions = new DefaultExpressionList<>(this, null);
}
return whereExpressions;
}
@@ -1924,7 +1919,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
@Override
public final ExpressionList<T> having() {
if (havingExpressions == null) {
havingExpressions = new DefaultExpressionList<>(this, false);
havingExpressions = new DefaultExpressionList<>(this, null);
}
return havingExpressions;
}
@@ -39,21 +39,12 @@ public final class OrmQueryDetail implements Serializable {
/**
* Return a deep copy of the OrmQueryDetail.
*/
public OrmQueryDetail copy(OrmQueryDetail existing) {
public OrmQueryDetail copy() {
OrmQueryDetail copy = new OrmQueryDetail();
copy.baseProps = baseProps.copy();
for (Map.Entry<String, OrmQueryProperties> entry : fetchPaths.entrySet()) {
copy.fetchPaths.put(entry.getKey(), entry.getValue().copy());
}
if (existing != null) {
// transfer any existing filterMany expressions
for (Map.Entry<String, OrmQueryProperties> entry : existing.fetchPaths.entrySet()) {
var filterMany = entry.getValue().getFilterMany();
if (filterMany != null) {
copy.getChunk(entry.getKey(), true).setFilterMany(filterMany);
}
}
}
return copy;
}
@@ -10,7 +10,7 @@ public interface SpiFetchGroup<T> extends FetchGroup<T> {
/**
* Return the detail to use for query execution.
*/
OrmQueryDetail detail(OrmQueryDetail existing);
OrmQueryDetail detail();
/**
* Return the underlying detail for copy purposes.
@@ -1,7 +1,5 @@
package io.ebeaninternal.server.transaction;
import org.jspecify.annotations.Nullable;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@@ -27,7 +25,6 @@ public interface DataSourceSupplier {
* This can return null meaning that no read only DataSource (with autoCommit)
* is available for use so normal transactions with explicit commit should be used.
*/
@Nullable
DataSource readOnlyDataSource();
/**
@@ -20,7 +20,7 @@ public final class DocStoreTransactionManager extends TransactionManager {
@Override
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
return new DocStoreOnlyTransaction(explicit, this);
return createTransaction(explicit, null);
}
@Override
@@ -28,4 +28,8 @@ public final class DocStoreTransactionManager extends TransactionManager {
return new DocStoreOnlyTransaction(false, this);
}
@Override
protected SpiTransaction createTransaction(boolean explicit, Connection c) {
return new DocStoreOnlyTransaction(explicit, this);
}
}
@@ -14,7 +14,6 @@ import io.ebeanservice.docstore.api.DocStoreTransaction;
import jakarta.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
@@ -36,7 +35,7 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
/**
* Set false when using autoCommit (as a performance optimisation for the read-only case).
*/
private boolean useCommit;
private final boolean useCommit;
private final TransactionManager manager;
private final SpiTxnLogger logger;
private final boolean logSql;
@@ -59,7 +58,6 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
private SpiPersistenceContext persistenceContext;
private Object tenantId;
private Map<String, Object> userObjects;
private final Instant startTime = Instant.now();
private final long startNanos;
private ProfileLocation profileLocation;
@@ -91,8 +89,9 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
}
@Override
public Instant startTime() {
return startTime;
public long startNanoTime() {
// not used on read only transaction
return startNanos;
}
@Override
@@ -200,7 +199,7 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
}
@Override
public void registerDeleteBean(Class<?> type, Object id) {
public void registerDeleteBean(Integer persistingBean) {
throw new IllegalStateException(notExpectedMessage);
}
@@ -208,7 +207,7 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
* Return true if this is a bean that has already been saved/deleted.
*/
@Override
public boolean isRegisteredDeleteBean(Class<?> type, Object id) {
public boolean isRegisteredDeleteBean(Integer persistingBean) {
return false;
}
@@ -550,16 +549,6 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
throw new IllegalStateException(notExpectedMessage);
}
@Override
public void setAutoCommitOnFindIterate() {
try {
connection.setAutoCommit(false);
useCommit = true;
} catch (SQLException e) {
throw new PersistenceException(e);
}
}
@Override
public void rollbackAndContinue() {
// do nothing
@@ -1,32 +0,0 @@
package io.ebeaninternal.server.transaction;
import org.jspecify.annotations.Nullable;
import java.sql.Connection;
/**
* Ebean transaction used for AutoCommit true connections.
*/
final class JdbcAutoCommitTransaction extends JdbcTransaction {
JdbcAutoCommitTransaction(boolean explicit, Connection connection, @Nullable TransactionManager manager) {
super(true, explicit, connection, manager);
}
@Override
void performRollback() {
long offset = profileOffset();
if (profileStream != null) {
profileStream.addEvent(EVT_ROLLBACK, offset);
}
}
@Override
void performCommit() {
long offset = profileOffset();
if (profileStream != null) {
profileStream.addEvent(EVT_COMMIT, offset);
}
}
}
@@ -17,7 +17,6 @@ import jakarta.persistence.PersistenceException;
import jakarta.persistence.RollbackException;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Instant;
import java.util.*;
import java.util.function.Consumer;
@@ -68,7 +67,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
private int depth;
private boolean autoCommit;
private IdentityHashMap<Object, Object> persistingBeans;
private Map<Class<?>, Set<Object>> deletingBeans;
private HashSet<Integer> deletingBeansHash;
private HashMap<String, String> m2mIntersectionSave;
private Map<String, Object> userObjects;
private List<TransactionCallback> callbackList;
@@ -90,17 +89,12 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
*/
private final boolean skipCacheAfterWrite;
DocStoreTransaction docStoreTxn;
protected ProfileStream profileStream;
private ProfileStream profileStream;
private ProfileLocation profileLocation;
private final Instant startTime = Instant.now();
private final long startNanos;
private boolean autoPersistUpdates;
JdbcTransaction(boolean explicit, Connection connection, TransactionManager manager) {
this(false, explicit, connection, manager);
}
JdbcTransaction(boolean autoCommit, boolean explicit, Connection connection, TransactionManager manager) {
try {
this.active = true;
this.explicit = explicit;
@@ -129,9 +123,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
this.onQueryOnlyCommit = true;
}
if (!autoCommit) {
checkAutoCommit(connection);
}
checkAutoCommit(connection);
} catch (Exception e) {
throw new PersistenceException(e);
@@ -149,8 +141,8 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
}
@Override
public final Instant startTime() {
return startTime;
public final long startNanoTime() {
return startNanos;
}
@Override
@@ -334,28 +326,40 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
deferredList.add(derived);
}
/**
* Add a bean to the registed list.
* <p>
* This is to handle bi-directional relationships where both sides Cascade.
* </p>
*/
@Override
public final void registerDeleteBean(Class<?> type, Object id) {
deleteBeanIds(type).add(id);
}
@Override
public final boolean isRegisteredDeleteBean(Class<?> type, Object id) {
return deleteBeanIds(type).contains(id);
}
private Set<Object> deleteBeanIds(Class<?> type) {
if (deletingBeans == null) {
deletingBeans = new HashMap<>();
public final void registerDeleteBean(Integer persistingBean) {
if (deletingBeansHash == null) {
deletingBeansHash = new HashSet<>();
}
return deletingBeans.computeIfAbsent(type, k -> new HashSet<>());
deletingBeansHash.add(persistingBean);
}
/**
* Return true if this is a bean that has already been saved/deleted.
*/
@Override
public final boolean isRegisteredDeleteBean(Integer persistingBean) {
return deletingBeansHash != null && deletingBeansHash.contains(persistingBean);
}
/**
* Unregister the persisted beans (when persisting at the top level).
*/
@Override
public final void unregisterBeans() {
persistingBeans.clear();
}
/**
* Return true if this is a bean that has already been saved. This will
* register the bean if it is not already.
*/
@Override
public final boolean isRegisteredBean(Object bean) {
if (persistingBeans == null) {
@@ -364,6 +368,10 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
return (persistingBeans.put(bean, PLACEHOLDER) != null);
}
/**
* Return true if the m2m intersection save is allowed from a given bean direction.
* This is to stop m2m intersection management via both directions of a m2m.
*/
@Override
public final boolean isSaveAssocManyIntersection(String intersectionTable, String beanName) {
if (m2mIntersectionSave == null) {
@@ -17,7 +17,6 @@ import io.ebeanservice.docstore.api.DocStoreTransaction;
import jakarta.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Instant;
/**
* Placeholder for use with SUPPORTS and NEVER transactional when there really isn't a transaction.
@@ -57,9 +56,9 @@ final class NoTransaction implements SpiTransaction {
}
@Override
public Instant startTime() {
public long startNanoTime() {
// not used
return Instant.now();
return System.nanoTime();
}
@Override
@@ -154,12 +153,12 @@ final class NoTransaction implements SpiTransaction {
}
@Override
public void registerDeleteBean(Class<?> type, Object id) {
public void registerDeleteBean(Integer hash) {
}
@Override
public boolean isRegisteredDeleteBean(Class<?> type, Object id) {
public boolean isRegisteredDeleteBean(Integer hash) {
return false;
}
@@ -6,7 +6,6 @@ import io.ebean.cache.QueryCacheEntryValidate;
import io.ebean.cache.ServerCacheNotification;
import io.ebean.cache.ServerCacheNotify;
import java.time.Instant;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -24,7 +23,7 @@ public final class TableModState implements QueryCacheEntryValidate, ServerCache
private static final System.Logger log = AppLog.getLogger("io.ebean.cache.TABLEMOD");
private final Map<String, Instant> tableModStamp = new ConcurrentHashMap<>();
private final Map<String, Long> tableModStamp = new ConcurrentHashMap<>();
public TableModState() {
}
@@ -33,22 +32,22 @@ public final class TableModState implements QueryCacheEntryValidate, ServerCache
* Set the modified timestamp on the tables that have been touched.
*/
void touch(Set<String> touchedTables) {
final var modTime = Instant.now();
long modNanoTime = System.nanoTime();
for (String tableName : touchedTables) {
tableModStamp.put(tableName, modTime);
tableModStamp.put(tableName, modNanoTime);
}
if (log.isLoggable(DEBUG)) {
log.log(DEBUG, "TableModState updated - touched:{0} modTime:{1}", touchedTables, modTime);
log.log(DEBUG, "TableModState updated - touched:{0} modNanoTime:{1}", touchedTables, modNanoTime);
}
}
/**
* Return true if all the tables are valid based on timestamp comparison.
*/
boolean isValid(Set<String> tables, Instant sinceTime) {
boolean isValid(Set<String> tables, long sinceNanoTime) {
for (String tableName : tables) {
final var modTime = tableModStamp.get(tableName);
if (modTime != null && !modTime.isBefore(sinceTime)) {
Long modTime = tableModStamp.get(tableName);
if (modTime != null && modTime >= sinceNanoTime) {
if (log.isLoggable(TRACE)) {
log.log(TRACE, "Invalidate on table:{0}", tableName);
}
@@ -60,9 +59,9 @@ public final class TableModState implements QueryCacheEntryValidate, ServerCache
@Override
public boolean isValid(QueryCacheEntry entry) {
Set<String> dependentTables = entry.dependentTables();
Set<String> dependentTables = entry.getDependentTables();
if (dependentTables != null && !dependentTables.isEmpty()) {
return isValid(dependentTables, entry.timestamp());
return isValid(dependentTables, entry.getTimestamp());
}
return true;
}
@@ -13,18 +13,9 @@ import java.sql.SQLException;
abstract class TransactionFactory {
final TransactionManager manager;
private final boolean autoCommitMode;
TransactionFactory(TransactionManager manager) {
this.manager = manager;
this.autoCommitMode = manager.isAutoCommitMode();
}
/**
* Return a new transaction.
*/
SpiTransaction createTransaction(boolean explicit, Connection connection) {
return autoCommitMode ? new JdbcAutoCommitTransaction(explicit, connection, manager) : new JdbcTransaction(explicit, connection, manager);
}
/**
@@ -43,7 +34,7 @@ abstract class TransactionFactory {
/**
* Set the Transaction Isolation level if required.
*/
final SpiTransaction setIsolationLevel(SpiTransaction t, int isolationLevel) {
final SpiTransaction setIsolationLevel(SpiTransaction t, boolean explicit, int isolationLevel) {
if (isolationLevel > -1) {
Connection connection = t.internalConnection();
try {
@@ -39,8 +39,8 @@ class TransactionFactoryBasic extends TransactionFactory {
Connection connection = null;
try {
connection = dataSource.getConnection();
SpiTransaction t = createTransaction(explicit, connection);
return setIsolationLevel(t, isolationLevel);
SpiTransaction t = create(explicit, connection);
return setIsolationLevel(t, explicit, isolationLevel);
} catch (PersistenceException ex) {
JdbcClose.close(connection);
throw ex;
@@ -49,4 +49,8 @@ class TransactionFactoryBasic extends TransactionFactory {
}
}
private SpiTransaction create(boolean explicit, Connection c) {
return manager.createTransaction(explicit, c);
}
}

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