Compare commits

..
Author SHA1 Message Date
Rob Bygrave 2c4b8492da ebean-test: Add PersistenceContextAccess.clear() for tests that need to clear the Persistence Context
To support tests that use a transaction that wraps test setup and running the test logic. The need for this case is to clear the persistence context after the test setup phase.
2024-03-16 14:56:24 +13:00
146 changed files with 4129 additions and 3816 deletions
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -41,7 +41,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -60,13 +60,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
</parent>
<artifactId>composites</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
</parent>
<name>ebean api</name>
@@ -2209,15 +2209,6 @@ public interface DatabaseBuilder {
@Deprecated
DatabaseBuilder setLoadModuleInfo(boolean loadModuleInfo);
/**
* Set if generated SQL SELECT should include the query label as an
* inline SQL comment (to help reference back from the SQL to the code
* that executed the query.
*
* @param includeLabelInSql When true include a SQL inline comment in generated SELECT queries.
*/
DatabaseConfig includeLabelInSql(boolean includeLabelInSql);
/**
* Set the naming convention to apply to metrics names.
*/
@@ -3113,12 +3104,6 @@ public interface DatabaseBuilder {
*/
boolean isLoadModuleInfo();
/**
* Return true if generated sql select query should include an inline sql comment with the
* query label or profile location label.
*/
boolean isIncludeLabelInSql();
/**
* Return the naming convention to apply to metrics names.
*/
@@ -222,8 +222,6 @@ public interface ExpressionList<T> {
int delete();
/**
* @deprecated migrate to {@link #usingTransaction(Transaction)} then delete().
* <p>
* Execute as a delete query deleting the 'root level' beans that match the predicates
* in the query.
* <p>
@@ -233,7 +231,6 @@ public interface ExpressionList<T> {
*
* @return the number of rows that were deleted.
*/
@Deprecated(forRemoval = true, since = "13.1.0")
int delete(Transaction transaction);
/**
@@ -245,14 +242,11 @@ public interface ExpressionList<T> {
int update();
/**
* @deprecated migrate to {@link #usingTransaction(Transaction)} then update().
* <p>
* Execute as a update query with the given transaction.
*
* @return the number of rows that were updated.
* @see UpdateQuery
*/
@Deprecated(forRemoval = true, since = "13.1.0")
int update(Transaction transaction);
/**
File diff suppressed because it is too large Load Diff
@@ -1,975 +0,0 @@
package io.ebean;
import io.avaje.lang.Nullable;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* Build and execute an ORM query.
*
* @param <SELF> The type of the builder
* @param <T> The entity bean type
*/
public interface QueryBuilder<SELF, T> extends QueryBuilderProjection<SELF, T> {
/**
* Set root table alias.
*/
SELF alias(String alias);
/**
* Apply changes to the query conditional on the supplied predicate.
* <p>
* Typically, the changes are extra predicates etc.
*
* @param predicate The predicate which when true the changes are applied
* @param apply The changes to apply to the query
*/
SELF alsoIf(BooleanSupplier predicate, Consumer<SELF> apply);
/**
* Perform an 'As of' query using history tables to return the object graph
* as of a time in the past.
* <p>
* To perform this query the DB must have underlying history tables.
*
* @param asOf the date time in the past at which you want to view the data
*/
SELF asOf(Timestamp asOf);
/**
* Execute the query against the draft set of tables.
*/
SELF asDraft();
/**
* Convert the query to a DTO bean query.
* <p>
* We effectively use the underlying ORM query to build the SQL and then execute
* and map it into DTO beans.
*/
<D> DtoQuery<D> asDto(Class<D> dtoClass);
/**
* Convert the query to a UpdateQuery.
* <p>
* Typically this is used with query beans to covert a query bean
* query into an UpdateQuery like the examples below.
* </p>
*
* <pre>{@code
*
* int rowsUpdated = new QCustomer()
* .name.startsWith("Rob")
* .asUpdate()
* .set("active", false)
* .update();;
*
* }</pre>
*
* <pre>{@code
*
* int rowsUpdated = new QContact()
* .notes.note.startsWith("Make Inactive")
* .email.endsWith("@foo.com")
* .customer.id.equalTo(42)
* .asUpdate()
* .set("inactive", true)
* .setRaw("email = lower(email)")
* .update();
*
* }</pre>
*/
UpdateQuery<T> asUpdate();
/**
* Return a copy of the query.
* <p>
* This is so that you can use a Query as a "prototype" for creating other
* query instances. You could create a Query with various where expressions
* and use that as a "prototype" - using this copy() method to create a new
* instance that you can then add other expressions then execute.
* </p>
*/
SELF copy();
/**
* Execute the query using the given transaction.
*/
SELF usingTransaction(Transaction transaction);
/**
* Execute the query using the given connection.
*/
SELF usingConnection(Connection connection);
/**
* Execute the query using the given database.
*/
SELF usingDatabase(Database database);
/**
* Ensure that the master DataSource is used if there is a read only data source
* being used (that is using a read replica database potentially with replication lag).
* <p>
* When the database is configured with a read-only DataSource via
* say {@link io.ebean.config.DatabaseConfig#setReadOnlyDataSource(DataSource)} then
* by default when a query is run without an active transaction, it uses the read-only data
* source. We we use {@code usingMaster()} to instead ensure that the query is executed
* against the master data source.
*/
SELF usingMaster();
/**
* Set the base table to use for this query.
* <p>
* Typically this is used when a table has partitioning and we wish to specify a specific
* partition/table to query against.
* </p>
* <pre>{@code
*
* QOrder()
* .setBaseTable("order_2019_05")
* .status.equalTo(Status.NEW)
* .findList();
*
* }</pre>
*/
SELF setBaseTable(String baseTable);
/**
* Specify the PersistenceContextScope to use for this query.
* <p>
* When this is not set the 'default' configured on {@link io.ebean.config.DatabaseConfig#setPersistenceContextScope(PersistenceContextScope)}
* is used - this value defaults to {@link PersistenceContextScope#TRANSACTION}.
* <p>
* Note that the same persistence Context is used for subsequent lazy loading and query join queries.
* <p>
* Note that #findEach uses a 'per object graph' PersistenceContext so this scope is ignored for
* queries executed as #findIterate, #findEach, #findEachWhile.
*
* @param scope The scope to use for this query and subsequent lazy loading.
*/
SELF setPersistenceContextScope(PersistenceContextScope scope);
/**
* Explicitly specify whether to use AutoTune for this query.
* <p>
* If you do not call this method on a query the "Implicit AutoTune mode" is
* used to determine if AutoTune should be used for a given query.
* <p>
* AutoTune can add additional fetch paths to the query and specify which
* properties are included for each path. If you have explicitly defined some
* fetch paths AutoTune will not remove them.
*/
SELF setAutoTune(boolean autoTune);
/**
* Execute the query allowing properties with invalid JSON to be collected and not fail the query.
* <pre>{@code
*
* // fetch a bean with JSON content
* EBasicJsonList bean= DB.find(EBasicJsonList.class)
* .setId(42)
* .setAllowLoadErrors() // collect errors into bean state if we have invalid JSON
* .findOne();
*
*
* // get the invalid JSON errors from the bean state
* Map<String, Exception> errors = server().getBeanState(bean).getLoadErrors();
*
* // If this map is not empty tell we have invalid JSON
* // and should try and fix the JSON content or inform the user
*
* }</pre>
*/
SELF setAllowLoadErrors();
/**
* Set the default lazy loading batch size to use.
* <p>
* When lazy loading is invoked on beans loaded by this query then this sets the
* batch size used to load those beans.
*
* @param lazyLoadBatchSize the number of beans to lazy load in a single batch
*/
SELF setLazyLoadBatchSize(int lazyLoadBatchSize);
/**
* Set a label on the query.
* <p>
* This label can be used to help identify query performance metrics but we can also use
* profile location enhancement on Finders so for some that would be a better option.
*/
SELF setLabel(String label);
/**
* Set a SQL query hint.
* <p>
* This results in an inline comment that immediately follows
* after the select keyword in the form: {@code /*+ hint *\/ }
*/
SELF setHint(String hint);
/**
* Set the index(es) to search for a document store which uses partitions.
* <p>
* For example, when executing a query against ElasticSearch with daily indexes we can
* explicitly specify the indexes to search against.
* </p>
* <pre>{@code
*
* // explicitly specify the indexes to search
* query.setDocIndexName("logstash-2016.11.5,logstash-2016.11.6")
*
* // search today's index
* query.setDocIndexName("$today")
*
* // search the last 3 days
* query.setDocIndexName("$last-3")
*
* }</pre>
* <p>
* If the indexName is specified with ${daily} e.g. "logstash-${daily}" ... then we can use
* $today and $last-x as the search docIndexName like the examples below.
* </p>
* <pre>{@code
*
* // search today's index
* query.setDocIndexName("$today")
*
* // search the last 3 days
* query.setDocIndexName("$last-3")
*
* }</pre>
*
* @param indexName The index or indexes to search against
* @return This query
*/
SELF setDocIndexName(String indexName);
/**
* Execute the query including soft deleted rows.
* <p>
* This means that Ebean will not add any predicates to the query for filtering out
* soft deleted rows. You can still add your own predicates for the deleted properties
* and effectively you have full control over the query to include or exclude soft deleted
* rows as needed for a given use case.
*/
SELF setIncludeSoftDeletes();
/**
* Disable read auditing for this query.
* <p>
* This is intended to be used when the query is not a user initiated query and instead
* part of the internal processing in an application to load a cache or document store etc.
* In these cases we don't want the query to be part of read auditing.
*/
SELF setDisableReadAuditing();
/**
* Set true if you want to disable lazy loading.
* <p>
* That is, once the object graph is returned further lazy loading is disabled.
*/
SELF setDisableLazyLoading(boolean disableLazyLoading);
/**
* Set whether this query uses DISTINCT.
*/
SELF setDistinct(boolean distinct);
/**
* Restrict the query to only return subtypes of the given inherit type.
* <pre>{@code
*
* List<Animal> animals =
* new QAnimal()
* .name.startsWith("Fluffy")
* .setInheritType(Cat.class)
* .findList();
*
* }</pre>
*/
SELF setInheritType(Class<? extends T> type);
/**
* Set the first row to return for this query.
*
* @param firstRow the first row to include in the query result.
*/
SELF setFirstRow(int firstRow);
/**
* Set the maximum number of rows to return in the query.
*
* @param maxRows the maximum number of rows to return in the query.
*/
SELF setMaxRows(int maxRows);
/**
* Set RawSql to use for this query.
*/
SELF setRawSql(RawSql rawSql);
/**
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
*
* <pre>{@code
*
* List<CountedValue<Order.Status>> orderStatusCount =
*
* DB.find(Order.class)
* .select("status")
* .where()
* .gt("orderDate", LocalDate.now().minusMonths(3))
*
* // fetch as single attribute with a COUNT
* .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC)
* .findSingleAttributeList();
*
* for (CountedValue<Order.Status> entry : orderStatusCount) {
* System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() );
* }
*
* // produces
*
* count:3 orderStatus:NEW
* count:1 orderStatus:SHIPPED
* count:1 orderStatus:COMPLETE
*
* }</pre>
*/
SELF setCountDistinct(CountDistinctOrder orderBy);
/**
* Set the property to use as keys for a map.
* <p>
* If no property is set then the id property is used.
* </p>
* <pre>{@code
*
* // Assuming sku is unique for products...
*
* Map<String,Product> productMap = DB.find(Product.class)
* .setMapKey("sku") // sku map keys...
* .findMap();
*
* }</pre>
*
* @param mapKey the property to use as keys for a map.
*/
SELF setMapKey(String mapKey);
/**
* Set to true if this query should execute against the doc store.
* <p>
* When setting this you may also consider disabling lazy loading.
*/
SELF setUseDocStore(boolean useDocStore);
/**
* When set to true when you want the returned beans to be read only.
*/
SELF setReadOnly(boolean readOnly);
/**
* Set a timeout on this query.
* <p>
* This will typically result in a call to setQueryTimeout() on a
* preparedStatement. If the timeout occurs an exception will be thrown - this
* will be a SQLException wrapped up in a PersistenceException.
* </p>
*
* @param secs the query timeout limit in seconds. Zero means there is no limit.
*/
SELF setTimeout(int secs);
/**
* A hint which for JDBC translates to the Statement.fetchSize().
* <p>
* Gives the JDBC driver a hint as to the number of rows that should be
* fetched from the database when more rows are needed for ResultSet.
* </p>
* <p>
* Note that internally findEach and findEachWhile will set the fetch size
* if it has not already as these queries expect to process a lot of rows.
* If we didn't then Postgres and MySql for example would eagerly pull back
* all the row data and potentially consume a lot of memory in the process.
* </p>
* <p>
* As findEach and findEachWhile automatically set the fetch size we don't have
* to do so generally but we might still wish to for tuning a specific use case.
* </p>
*/
SELF setBufferFetchSizeHint(int fetchSize);
/**
* Set the mode to use the bean cache when executing this query.
* <p>
* By default, "find by id" and "find by natural key" will use the bean cache
* when bean caching is enabled. Setting this to false means that the query
* will not use the bean cache and instead hit the database.
* <p>
* By default, findList() with natural keys will not use the bean cache. In that
* case we need to explicitly use the bean cache.
*/
SELF setBeanCacheMode(CacheMode beanCacheMode);
/**
* Set the {@link CacheMode} to use the query for executing this query.
*/
SELF setUseQueryCache(CacheMode cacheMode);
/**
* Set this to false to not use the bean cache.
* <p>
* This method is now superseded by {@link #setBeanCacheMode(CacheMode)}
* which provides more explicit options controlled bean cache use.
* <p>
* This method is likely to be deprecated in the future with migration
* over to setUseBeanCache().
*/
default SELF setUseCache(boolean useCache) {
return setBeanCacheMode(useCache ? CacheMode.ON : CacheMode.OFF);
}
/**
* Calls {@link #setUseQueryCache(CacheMode)} with <code>ON</code> or <code>OFF</code>.
*/
default SELF setUseQueryCache(boolean enabled) {
return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF);
}
/**
* Set the order by clause replacing the existing order by clause if there is
* one.
* <p>
* This follows SQL syntax using commas between each property with the
* optional asc and desc keywords representing ascending and descending order
* respectively.
*/
SELF orderBy(String orderByClause);
/**
* Set an OrderBy object to replace any existing OrderBy clause.
*/
SELF setOrderBy(OrderBy<T> orderBy);
/**
* Controls, if paginated queries should always append an 'order by id' statement at the end to
* guarantee a deterministic sort result. This may affect performance.
* If this is not enabled, and an orderBy is set on the query, it's up to the programmer that
* this query provides a deterministic result.
*/
SELF orderById(boolean orderById);
/**
* Execute the query with the given lock type and WAIT.
* <p>
* Note that <code>forUpdate()</code> is the same as
* <code>withLock(LockType.UPDATE)</code>.
* <p>
* Provides us with the ability to explicitly use Postgres
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
*/
SELF withLock(Query.LockType lockType);
/**
* Execute the query with the given lock type and lock wait.
* <p>
* Note that <code>forUpdateNoWait()</code> is the same as
* <code>withLock(LockType.UPDATE, LockWait.NOWAIT)</code>.
* <p>
* Provides us with the ability to explicitly use Postgres
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
*/
SELF withLock(Query.LockType lockType, Query.LockWait lockWait);
/**
* Execute using "for update" clause which results in the DB locking the record.
* <p>
* The same as <code>withLock(LockType.UPDATE, LockWait.WAIT)</code>.
*/
SELF forUpdate();
/**
* Execute using "for update" clause with "no wait" option.
* <p>
* This is typically a Postgres and Oracle only option at this stage.
* <p>
* The same as <code>withLock(LockType.UPDATE, LockWait.NOWAIT)</code>.
*/
SELF forUpdateNoWait();
/**
* Execute using "for update" clause with "skip locked" option.
* <p>
* This is typically a Postgres and Oracle only option at this stage.
* <p>
* The same as <code>withLock(LockType.UPDATE, LockWait.SKIPLOCKED)</code>.
*/
SELF forUpdateSkipLocked();
/**
* Returns the set of properties or paths that are unknown (do not map to known properties or paths).
* <p>
* Validate the query checking the where and orderBy expression paths to confirm if
* they represent valid properties or paths for the given bean type.
*/
Set<String> validate();
/**
* Return the sql that was generated for executing this query.
* <p>
* This is only available after the query has been executed and provided only
* for informational purposes.
*/
String getGeneratedSql();
/**
* Return the type of beans being queried.
*/
Class<T> getBeanType();
/**
* Execute as a delete query deleting the 'root level' beans that match the predicates
* in the query.
* <p>
* Note that if the query includes joins then the generated delete statement may not be
* optimal depending on the database platform.
*
* @return the number of beans/rows that were deleted.
*/
int delete();
/**
* Execute the query returning true if a row is found.
* <p>
* The query is executed using max rows of 1 and will only select the id property.
* This method is really just a convenient way to optimise a query to perform a
* 'does a row exist in the db' check.
*
* <h2>Example using a query bean:</h2>
* <pre>{@code
*
* boolean userExists =
* new QContact()
* .email.equalTo("rob@foo.com")
* .exists();
*
* }</pre>
*
* <h2>Example:</h2>
* <pre>{@code
*
* boolean userExists = query()
* .where().eq("email", "rob@foo.com")
* .exists();
*
* }</pre>
*
* @return True if the query finds a matching row in the database
*/
boolean exists();
/**
* Execute the query returning either a single bean or null (if no matching
* bean is found).
* <p>
* If more than 1 row is found for this query then a PersistenceException is
* thrown.
* <p>
* This is useful when your predicates dictate that your query should only
* return 0 or 1 results.
* <p>
* <pre>{@code
*
* // assuming the sku of products is unique...
* Product product =
* new QProduct()
* .sku.equalTo("aa113")
* .findOne();
* ...
* }</pre>
* <p>
* It is also useful with finding objects by their id when you want to specify
* further join information to optimise the query.
* <p>
* <pre>{@code
*
* // Fetch order 42 and additionally fetch join its order details...
* Order order =
* new QOrder()
* .fetch("details") // eagerly load the order details
* .id.equalTo(42)
* .findOne();
*
* // the order details were eagerly loaded
* List<OrderDetail> details = order.getDetails();
* ...
* }</pre>
*/
@Nullable
T findOne();
/**
* Execute the query returning an optional bean.
*/
Optional<T> findOneOrEmpty();
/**
* Execute the query returning the list of objects.
* <p>
* This query will execute against the EbeanServer that was used to create it.
* <p>
* <pre>{@code
*
* List<Customer> customers =
* new QCustomer()
* .name.ilike("rob%")
* .findList();
*
* }</pre>
*
* @see Query#findList()
*/
List<T> findList();
/**
* Execute the query returning the result as a Stream.
* <p>
* Note that this can support very large queries iterating
* any number of results. To do so internally it can use
* multiple persistence contexts.
* </p>
* <pre>{@code
*
* // use try with resources to ensure Stream is closed
*
* try (Stream<Customer> stream = query.findStream()) {
* stream
* .map(...)
* .collect(...);
* }
*
* }</pre>
*/
Stream<T> findStream();
/**
* Execute the query returning the set of objects.
* <p>
* This query will execute against the EbeanServer that was used to create it.
* <p>
* <pre>{@code
*
* Set<Customer> customers =
* new QCustomer()
* .name.ilike("rob%")
* .findSet();
*
* }</pre>
*
* @see Query#findSet()
*/
Set<T> findSet();
/**
* Execute the query returning the list of Id's.
* <p>
* This query will execute against the EbeanServer that was used to create it.
*
* @see Query#findIds()
*/
<A> List<A> findIds();
/**
* Execute the query returning a map of the objects.
* <p>
* This query will execute against the EbeanServer that was used to create it.
* <p>
* You can use setMapKey() or asMapKey() to specify the property to be used as keys
* on the map. If one is not specified then the id property is used.
* <p>
* <pre>{@code
*
* Map<String, Product> map =
* new QProduct()
* .sku.asMapKey()
* .findMap();
*
* }</pre>
*
* @see Query#findMap()
*/
<K> Map<K, T> findMap();
/**
* Execute the query iterating over the results.
* <p>
* Note that findIterate (and findEach and findEachWhile) uses a "per graph"
* persistence context scope and adjusts jdbc fetch buffer size for large
* queries. As such it is better to use findList for small queries.
* <p>
* Remember that with {@link QueryIterator} you must call {@link QueryIterator#close()}
* when you have finished iterating the results (typically in a finally block).
* <p>
* findEach() and findEachWhile() are preferred to findIterate() as they ensure
* the jdbc statement and resultSet are closed at the end of the iteration.
* <p>
* This query will execute against the EbeanServer that was used to create it.
* </p>
* <pre>{@code
*
* Query<Customer> query =
* new QCustomer()
* .status.equalTo(Customer.Status.NEW)
* .orderBy()
* id.asc()
* .query();
*
* try (QueryIterator<Customer> it = query.findIterate()) {
* while (it.hasNext()) {
* Customer customer = it.next();
* // do something with customer ...
* }
* }
*
* }</pre>
*/
QueryIterator<T> findIterate();
/**
* Execute the query returning a list of values for a single property.
* <p>
* <h3>Example</h3>
* <pre>{@code
*
* List<String> names =
* new QCustomer()
* .setDistinct(true)
* .select(name)
* .findSingleAttributeList();
*
* }</pre>
*
* @return the list of values for the selected property
*/
<A> List<A> findSingleAttributeList();
/**
* Execute the query returning a single value or null for a single property.
* <p>
* <h3>Example</h3>
* <pre>{@code
*
* LocalDate maxDate =
* new QCustomer()
* .select("max(startDate)")
* .findSingleAttribute();
*
* }</pre>
*
* @return a single value or null for the selected property
*/
@Nullable
<A> A findSingleAttribute();
/**
* Execute the query returning a single optional attribute value.
* <p>
* <h3>Example</h3>
* <pre>{@code
*
* Optional<String> maybeName =
* new QCustomer()
* .select(name)
* .id.eq(42)
* .status.eq(NEW)
* .findSingleAttributeOrEmpty();
*
* }</pre>
*
* @return an optional value for the selected property
*/
<A> Optional<A> findSingleAttributeOrEmpty();
/**
* Execute the query returning a hashset of values for a single property.
*/
<A> Set<A> findSingleAttributeSet();
/**
* Execute the query processing the beans one at a time.
* <p>
* This method is appropriate to process very large query results as the
* beans are consumed one at a time and do not need to be held in memory
* (unlike #findList #findSet etc)
* <p>
* Note that internally Ebean can inform the JDBC driver that it is expecting larger
* resultSet and specifically for MySQL this hint is required to stop it's JDBC driver
* from buffering the entire resultSet. As such, for smaller resultSets findList() is
* generally preferable.
* <p>
* Compared with #findEachWhile this will always process all the beans where as
* #findEachWhile provides a way to stop processing the query result early before
* all the beans have been read.
* <p>
* This method is functionally equivalent to findIterate() but instead of using an
* iterator uses the Consumer interface which is better suited to use with closures.
*
* <pre>{@code
*
* new QCustomer()
* .status.equalTo(Status.NEW)
* .orderBy().id.asc()
* .findEach((Customer customer) -> {
*
* // do something with customer
* System.out.println("-- visit " + customer);
* });
*
* }</pre>
*
* @param consumer the consumer used to process the queried beans.
*/
void findEach(Consumer<T> consumer);
/**
* Execute findEach streaming query batching the results for consuming.
* <p>
* This query execution will stream the results and is suited to consuming
* large numbers of results from the database.
* <p>
* Typically, we use this batch consumer when we want to do further processing on
* the beans and want to do that processing in batch form, for example - 100 at
* a time.
*
* @param batch The number of beans processed in the batch
* @param consumer Process the batch of beans
*/
void findEach(int batch, Consumer<List<T>> consumer);
/**
* Execute the query using callbacks to a visitor to process the resulting
* beans one at a time.
* <p>
* This method is functionally equivalent to findIterate() but instead of using an
* iterator uses the Predicate interface which is better suited to use with closures.
*
* <pre>{@code
*
* new QCustomer()
* .status.equalTo(Status.NEW)
* .orderBy().id.asc()
* .findEachWhile((Customer customer) -> {
*
* // do something with customer
* System.out.println("-- visit " + customer);
*
* // return true to continue processing or false to stop
* return (customer.getId() < 40);
* });
*
* }</pre>
*
* @param consumer the consumer used to process the queried beans.
*/
void findEachWhile(Predicate<T> consumer);
/**
* Return versions of a @History entity bean.
* <p>
* Generally this query is expected to be a find by id or unique predicates query.
* It will execute the query against the history returning the versions of the bean.
*/
List<Version<T>> findVersions();
/**
* Return versions of a @History entity bean between a start and end timestamp.
* <p>
* Generally this query is expected to be a find by id or unique predicates query.
* It will execute the query against the history returning the versions of the bean.
*/
List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end);
/**
* Return the count of entities this query should return.
* <p>
* This is the number of 'top level' or 'root level' entities.
*/
int findCount();
/**
* Execute find row count query in a background thread.
* <p>
* This returns a Future object which can be used to cancel, check the
* execution status (isDone etc) and get the value (with or without a
* timeout).
*
* @return a Future object for the row count query
*/
FutureRowCount<T> findFutureCount();
/**
* Execute find Id's query in a background thread.
* <p>
* This returns a Future object which can be used to cancel, check the
* execution status (isDone etc) and get the value (with or without a
* timeout).
*
* @return a Future object for the list of Id's
*/
FutureIds<T> findFutureIds();
/**
* Execute find list 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 list result of the query
*/
FutureList<T> findFutureList();
/**
* Return a PagedList for this query using firstRow and maxRows.
* <p>
* The benefit of using this over findList() is that it provides functionality to get the
* total row count etc.
* <p>
* If maxRows is not set on the query prior to calling findPagedList() then a
* PersistenceException is thrown.
* <p>
* <pre>{@code
*
* PagedList<Order> pagedList =
* new QOrder()
* .setFirstRow(50)
* .setMaxRows(20)
* .findPagedList();
*
* // fetch the total row count in the background
* pagedList.loadRowCount();
*
* List<Order> orders = pagedList.getList();
* int totalRowCount = pagedList.getTotalRowCount();
*
* }</pre>
*
* @return The PagedList
*/
PagedList<T> findPagedList();
}
@@ -1,242 +0,0 @@
package io.ebean;
/**
* Builder for ORM Query projection (the select and fetch part).
*
* @param <SELF> The builder type
* @param <T> The entity bean type
*/
public interface QueryBuilderProjection<SELF, T> {
/**
* Apply the path properties replacing the select and fetch clauses.
* <p>
* This is typically used when the FetchPath is applied to both the query and the JSON output.
*/
SELF apply(FetchPath fetchPath);
/**
* Specify the properties to fetch on the root level entity bean in comma delimited format.
* <p>
* The Id property is automatically included in the properties to fetch unless setDistinct(true)
* is set on the query.
* </p>
* <p>
* Use {@link #fetch(String, String)} to specify specific properties to fetch
* on other non-root level paths of the object graph.
* </p>
* <pre>{@code
*
* List<Customer> customers = DB.find(Customer.class)
* // Only fetch the customer id, name and status.
* // This is described as a "Partial Object"
* .select("name, status")
* .where.ilike("name", "rob%")
* .findList();
*
* }</pre>
*
* @param fetchProperties the properties to fetch for this bean (* = all properties).
*/
SELF select(String fetchProperties);
/**
* Apply the fetchGroup which defines what part of the object graph to load.
*/
SELF select(FetchGroup<T> fetchGroup);
/**
* Specify a path to fetch eagerly including specific properties.
* <p>
* Ebean will endeavour to fetch this path using a SQL join. If Ebean determines that it can
* not use a SQL join (due to maxRows or because it would result in a cartesian product) Ebean
* will automatically convert this fetch query into a "query join" - i.e. use fetchQuery().
* </p>
* <pre>{@code
*
* // query orders...
* List<Order> orders = DB.find(Order.class)
* // fetch the customer...
* // ... getting the customers name and phone number
* .fetch("customer", "name, phoneNumber")
*
* // ... also fetch the customers billing address (* = all properties)
* .fetch("customer.billingAddress", "*")
* .findList();
* }</pre>
* <p>
* If columns is null or "*" then all columns/properties for that path are fetched.
* </p>
* <pre>{@code
*
* // fetch customers (their id, name and status)
* List<Customer> customers = DB.find(Customer.class)
* .select("name, status")
* .fetch("contacts", "firstName,lastName,email")
* .findList();
*
* }</pre>
*
* @param path the property path we wish to fetch eagerly.
* @param fetchProperties properties of the associated bean that you want to include in the
* fetch (* means all properties, null also means all properties).
*/
SELF fetch(String path, String fetchProperties);
/**
* Fetch the path and properties using a "query join" (separate SQL query).
* <p>
* This is the same as:
* </p>
* <pre>{@code
*
* fetch(path, fetchProperties, FetchConfig.ofQuery())
*
* }</pre>
* <p>
* This would be used instead of a fetch() when we use a separate SQL query to fetch this
* part of the object graph rather than a SQL join.
* <p>
* We might typically get a performance benefit when the path to fetch is a OneToMany
* or ManyToMany, the 'width' of the 'root bean' is wide and the cardinality of the many
* is high.
*
* @param path the property path we wish to fetch eagerly.
* @param fetchProperties properties of the associated bean that you want to include in the
* fetch (* means all properties, null also means all properties).
*/
SELF fetchQuery(String path, String fetchProperties);
/**
* Fetch the path and properties using L2 bean cache.
*
* @param path The path of the beans we are fetching from L2 cache.
* @param fetchProperties The properties that should be loaded.
*/
SELF fetchCache(String path, String fetchProperties);
/**
* Fetch the path and properties lazily (via batch lazy loading).
* <p>
* This is the same as:
*
* <pre>{@code
*
* fetch(path, fetchProperties, FetchConfig.ofLazy())
*
* }</pre>
* <p>
* The reason for using fetchLazy() is to either:
* <ul>
* <li>Control/tune what is fetched as part of lazy loading</li>
* <li>Make use of the L2 cache, build this part of the graph from L2 cache</li>
* </ul>
*
* @param path the property path we wish to fetch lazily.
* @param fetchProperties properties of the associated bean that you want to include in the
* fetch (* means all properties, null also means all properties).
*/
SELF fetchLazy(String path, String fetchProperties);
/**
* Additionally specify a FetchConfig to use a separate query or lazy loading
* to load this path.
* <pre>{@code
*
* // fetch customers (their id, name and status)
* List<Customer> customers = DB.find(Customer.class)
* .select("name, status")
* .fetch("contacts", "firstName,lastName,email", FetchConfig.ofLazy(10))
* .findList();
*
* }</pre>
*
* @param path the property path we wish to fetch eagerly.
*/
SELF fetch(String path, String fetchProperties, FetchConfig fetchConfig);
/**
* Specify a path to fetch eagerly including all its properties.
* <p>
* Ebean will endeavour to fetch this path using a SQL join. If Ebean determines that it can
* not use a SQL join (due to maxRows or because it would result in a cartesian product) Ebean
* will automatically convert this fetch query into a "query join" - i.e. use fetchQuery().
* </p>
* <pre>{@code
*
* // fetch customers (their id, name and status)
* List<Customer> customers = DB.find(Customer.class)
* // eager fetch the contacts
* .fetch("contacts")
* .findList();
*
* }</pre>
*
* @param path the property path we wish to fetch eagerly.
*/
SELF fetch(String path);
/**
* Fetch the path eagerly using a "query join" (separate SQL query).
* <p>
* This is the same as:
* <pre>{@code
*
* fetch(path, FetchConfig.ofQuery())
*
* }</pre>
* <p>
* This would be used instead of a fetch() when we use a separate SQL query to fetch this
* part of the object graph rather than a SQL join.
* <p>
* We might typically get a performance benefit when the path to fetch is a OneToMany
* or ManyToMany, the 'width' of the 'root bean' is wide and the cardinality of the many
* is high.
*
* @param path the property path we wish to fetch eagerly
*/
SELF fetchQuery(String path);
/**
* Fetch the path eagerly using L2 cache.
*/
SELF fetchCache(String path);
/**
* Fetch the path lazily (via batch lazy loading).
* <p>
* This is the same as:
* </p>
* <pre>{@code
*
* fetch(path, FetchConfig.ofLazy())
*
* }</pre>
* <p>
* The reason for using fetchLazy() is to either:
* </p>
* <ul>
* <li>Control/tune what is fetched as part of lazy loading</li>
* <li>Make use of the L2 cache, build this part of the graph from L2 cache</li>
* </ul>
*
* @param path the property path we wish to fetch lazily.
*/
SELF fetchLazy(String path);
/**
* Additionally specify a JoinConfig to specify a "query join" and or define
* the lazy loading query.
* <pre>{@code
*
* // fetch customers (their id, name and status)
* List<Customer> customers = DB.find(Customer.class)
* // lazy fetch contacts with a batch size of 100
* .fetch("contacts", FetchConfig.ofLazy(100))
* .findList();
*
* }</pre>
*/
SELF fetch(String path, FetchConfig fetchConfig);
}
@@ -125,12 +125,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
*/
private boolean loadModuleInfo = true;
/**
* When true then include a sql comment in generated SELECT queries with the query
* label or profile location label.
*/
private boolean includeLabelInSql;
/**
* Interesting classes such as entities, embedded, ScalarTypes,
* Listeners, Finders, Controllers, AttributeConverters etc.
@@ -2139,7 +2133,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
readOnlyDatabase = p.getBoolean("readOnlyDatabase", readOnlyDatabase);
autoPersistUpdates = p.getBoolean("autoPersistUpdates", autoPersistUpdates);
loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
includeLabelInSql = p.getBoolean("includeLabelInSql", includeLabelInSql);
maxCallStack = p.getInt("maxCallStack", maxCallStack);
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
@@ -2554,11 +2547,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
return loadModuleInfo;
}
@Override
public boolean isIncludeLabelInSql() {
return includeLabelInSql;
}
/**
* @deprecated - migrate to {@link #isLoadModuleInfo()}.
*/
@@ -2575,12 +2563,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
return this;
}
@Override
public DatabaseConfig includeLabelInSql(boolean includeLabelInSql) {
this.includeLabelInSql = includeLabelInSql;
return this;
}
@Override
public Function<String, String> getMetricNaming() {
return metricNaming;
@@ -1,29 +1,50 @@
package io.ebean.config;
import io.ebean.ProfileLocation;
import io.ebean.bean.ObjectGraphNode;
import java.util.List;
/**
* The data for the slow query.
* Slow query event.
*/
public interface SlowQueryEvent {
public class SlowQueryEvent {
private final String sql;
private final long timeMillis;
private final int rowCount;
private final ObjectGraphNode originNode;
/**
* Construct with the SQL and execution time in millis.
*/
public SlowQueryEvent(String sql, long timeMillis, int rowCount, ObjectGraphNode originNode) {
this.sql = sql;
this.timeMillis = timeMillis;
this.rowCount = rowCount;
this.originNode = originNode;
}
/**
* Return the SQL for the slow query.
*/
String getSql();
public String getSql() {
return sql;
}
/**
* Return the execution time in millis.
*/
long getTimeMillis();
public long getTimeMillis() {
return timeMillis;
}
/**
* Return the total row count associated with the query.
*/
int getRowCount();
public int getRowCount() {
return rowCount;
}
/**
* Return the origin point for the root query.
@@ -32,20 +53,7 @@ public interface SlowQueryEvent {
* shows the code that invoked the query.
* </p>
*/
ObjectGraphNode getOriginNode();
/**
* Return the bind parameters.
*/
List<Object> getBindParams();
/**
* Return the label.
*/
String getLabel();
/**
* Return the profile location.
*/
ProfileLocation getProfileLocation();
public ObjectGraphNode getOriginNode() {
return originNode;
}
}
@@ -769,15 +769,4 @@ public class DatabasePlatform {
public boolean supportsNativeJavaTime() {
return supportsNativeJavaTime;
}
public String inlineSqlComment(String label) {
if (label == null) {
return "";
}
return "/* " + label + " */ ";
}
public String inlineSqlHint(String hint) {
return "/*+ " + hint + " */ ";
}
}
@@ -76,7 +76,6 @@ class DatabaseConfigTest {
props.setProperty("skipDataSourceCheck", "true");
props.setProperty("readOnlyDatabase", "true");
props.setProperty("lengthCheck", "ON");
props.setProperty("includeLabelInSql", "true");
props.setProperty("queryPlan.enable", "true");
props.setProperty("queryPlan.thresholdMicros", "10000");
@@ -97,7 +96,6 @@ class DatabaseConfigTest {
assertTrue(settings.isLoadModuleInfo());
assertTrue(settings.skipDataSourceCheck());
assertTrue(settings.readOnlyDatabase());
assertTrue(settings.isIncludeLabelInSql());
assertThat(settings.getLengthCheck()).isEqualTo(LengthCheck.ON);
assertTrue(settings.isIdGeneratorAutomatic());
@@ -183,7 +181,6 @@ class DatabaseConfigTest {
assertEquals(10000L, config.getQueryPlanCaptureMaxTimeMillis());
assertEquals(10, config.getQueryPlanCaptureMaxCount());
assertThat(config.getLengthCheck()).isEqualTo(LengthCheck.OFF);
assertFalse(config.isIncludeLabelInSql());
config.setLoadModuleInfo(false);
assertFalse(config.isAutoLoadModuleInfo());
+26 -26
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
</parent>
<name>ebean bom</name>
@@ -89,25 +89,25 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -125,13 +125,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -155,37 +155,37 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-spring-txn</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<!-- platforms -->
@@ -193,79 +193,79 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-clickhouse</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-db2</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-h2</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-hana</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mariadb</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mysql</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-nuodb</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-oracle</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgres</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlite</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlserver</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
+6 -6
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -22,7 +22,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -46,7 +46,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -159,21 +159,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>test</scope>
</dependency>
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
import io.ebean.CacheMode;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.server.core.BindPadding;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
@@ -58,22 +57,17 @@ public final class LoadManyRequest extends LoadRequest {
return loadContext.fullPath();
}
private List<Object> parentIdList(SpiEbeanServer server, BeanPropertyAssocMany<?> many, PersistenceContext pc) {
final var idList = new ArrayList<>(loadContext.size());
final var descriptor = many.descriptor();
private List<Object> parentIdList(SpiEbeanServer server) {
List<Object> idList = new ArrayList<>();
BeanPropertyAssocMany<?> many = many();
for (int i = 0; i < loadContext.size(); i++) {
final BeanCollection<?> bc = loadContext.get(i);
BeanCollection<?> bc = loadContext.get(i);
if (bc != null) {
final var parent = bc.owner();
final var parentId = descriptor.getId(parent);
idList.add(parentId);
bc.setLoader(server); // don't use the load buffer again
if (lazy) {
descriptor.contextPutIfAbsent(pc, parentId, parent);
if (!originIncluded && bc == originCollection) {
originIncluded = true;
}
if (lazy && !originIncluded && bc == originCollection) {
originIncluded = true;
}
idList.add(many.parentId(bc.owner()));
bc.setLoader(server); // don't use the load buffer again
}
}
if (originCollection != null && !originIncluded) {
@@ -81,7 +75,7 @@ public final class LoadManyRequest extends LoadRequest {
idList.add(many.parentId(originCollection.owner()));
originCollection.setLoader(server); // don't use the load buffer again
}
if (descriptor.isPadInExpression()) {
if (many.targetDescriptor().isPadInExpression()) {
BindPadding.padIds(idList);
}
return idList;
@@ -105,9 +99,8 @@ public final class LoadManyRequest extends LoadRequest {
query.where().raw(extraWhere.replace("${ta}", "t0").replace("${mta}", "int_"));
}
query.setLazyLoadForParents(many);
final var pc = loadContext.persistenceContext();
many.addWhereParentIdIn(query, parentIdList(server, many, pc), loadContext.isUseDocStore());
query.setPersistenceContext(pc);
many.addWhereParentIdIn(query, parentIdList(server), loadContext.isUseDocStore());
query.setPersistenceContext(loadContext.persistenceContext());
query.setLoadDescription(lazy ? "lazy" : "query", description());
if (lazy) {
query.setLazyLoadBatchSize(loadContext.batchSize());
@@ -84,7 +84,7 @@ public interface SpiExpression extends Expression {
*
* @param request the associated request.
*/
void addBindValues(SpiExpressionBind request);
void addBindValues(SpiExpressionRequest request);
/**
* Validate all the properties/paths associated with this expression.
@@ -1,29 +0,0 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.deploy.BeanDescriptor;
/**
* Expression bind values capture.
*/
public interface SpiExpressionBind {
/**
* Return the bean descriptor for the root type.
*/
BeanDescriptor<?> descriptor();
/**
* Add an encryption key to bind to this request.
*/
void addBindEncryptKey(Object encryptKey);
/**
* Add a bind value to this request.
*/
void addBindValue(Object bindValue);
/**
* Escapes a string to use it as exact match in Like clause.
*/
String escapeLikeString(String value);
}
@@ -1,6 +1,7 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
import io.ebeaninternal.server.expression.platform.DbExpressionRequest;
@@ -9,7 +10,7 @@ import java.util.List;
/**
* Request object used for gathering expression sql and bind values.
*/
public interface SpiExpressionRequest extends SpiExpressionBind, DbExpressionRequest {
public interface SpiExpressionRequest extends DbExpressionRequest {
/**
* Return the DB specific handler for JSON and ARRAY expressions.
@@ -21,6 +22,11 @@ public interface SpiExpressionRequest extends SpiExpressionBind, DbExpressionReq
*/
String parseDeploy(String logicalProp);
/**
* Return the bean descriptor for the root type.
*/
BeanDescriptor<?> descriptor();
/**
* Return the associated QueryRequest.
*/
@@ -52,6 +58,16 @@ public interface SpiExpressionRequest extends SpiExpressionBind, DbExpressionReq
*/
SpiExpressionRequest parse(String expression);
/**
* Add an encryption key to bind to this request.
*/
void addBindEncryptKey(Object encryptKey);
/**
* Add a bind value to this request.
*/
void addBindValue(Object bindValue);
/**
* Return the accumulated expression sql for all expressions in this request.
*/
@@ -72,6 +88,11 @@ public interface SpiExpressionRequest extends SpiExpressionBind, DbExpressionReq
*/
void appendLike(boolean rawLikeExpression);
/**
* Escapes a string to use it as exact match in Like clause.
*/
String escapeLikeString(String value);
/**
* Append IN expression taking into account platform and type support for Multi-value.
*/
@@ -226,11 +226,6 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
*/
ProfileLocation profileLocation();
/**
* Return the SQL hint to include in the query.
*/
String hint();
/**
* Return the label set on the query.
*/
@@ -1,70 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebean.ProfileLocation;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.config.SlowQueryEvent;
import java.util.List;
/**
* Slow query event.
*/
final class DSlowQueryEvent implements SlowQueryEvent {
private final String sql;
private final long timeMillis;
private final int rowCount;
private final ObjectGraphNode originNode;
private final List<Object> bindParams;
private final String label;
private final ProfileLocation profileLocation;
/**
* Construct with the SQL and execution time in millis.
*/
DSlowQueryEvent(String sql, long timeMillis, int rowCount, ObjectGraphNode originNode,
List<Object> bindParams, String label, ProfileLocation profileLocation) {
this.sql = sql;
this.timeMillis = timeMillis;
this.rowCount = rowCount;
this.originNode = originNode;
this.bindParams = bindParams;
this.profileLocation = profileLocation;
this.label = label != null ? label : profileLocation == null ? null : profileLocation.label();
}
@Override
public String getSql() {
return sql;
}
@Override
public long getTimeMillis() {
return timeMillis;
}
@Override
public int getRowCount() {
return rowCount;
}
@Override
public ObjectGraphNode getOriginNode() {
return originNode;
}
@Override
public List<Object> getBindParams() {
return bindParams;
}
@Override
public String getLabel() {
return label;
}
@Override
public ProfileLocation getProfileLocation() {
return profileLocation;
}
}
@@ -63,8 +63,8 @@ final class DefaultBeanLoader {
Object parentId = parentDesc.getId(parentBean);
if (pc == null) {
pc = new DefaultPersistenceContext();
parentDesc.contextPut(pc, parentId, parentBean);
}
parentDesc.contextPutIfAbsent(pc, parentId, parentBean);
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching() && many.isUseCache();
if (useManyIdCache) {
Boolean readOnly = null;
@@ -2184,9 +2184,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public void slowQueryCheck(long timeMicros, int rowCount, SpiQuery<?> query) {
if (timeMicros > slowQueryMicros && slowQueryListener != null) {
List<Object> bindParams = new SlowQueryBindCapture(query).capture();
slowQueryListener.process(new DSlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount,
query.parentNode(), bindParams, query.label(), query.profileLocation()));
slowQueryListener.process(new SlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount, query.parentNode()));
}
}
@@ -1,12 +1,10 @@
package io.ebeaninternal.server.core;
import io.avaje.applog.AppLog;
import io.ebean.ProfileLocation;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.config.SlowQueryEvent;
import io.ebean.config.SlowQueryListener;
import java.util.List;
import static java.lang.System.Logger.Level.WARNING;
/**
@@ -18,10 +16,11 @@ final class DefaultSlowQueryListener implements SlowQueryListener {
@Override
public void process(SlowQueryEvent event) {
ProfileLocation profileLocation = event.getProfileLocation();
String loc = profileLocation == null ? "" : profileLocation.fullLocation();
List<Object> bindParams = event.getBindParams();
log.log(WARNING, "Slow query warning - millis:{0} rows:{1} location:{2} sql[{3}] params{4}",
event.getTimeMillis(), event.getRowCount(), loc, event.getSql(), bindParams);
String firstStack = "";
ObjectGraphNode node = event.getOriginNode();
if (node != null) {
firstStack = node.origin().top();
}
log.log(WARNING, "Slow query warning - millis:{0} rows:{1} caller[{2}] sql[{3}]", event.getTimeMillis(), event.getRowCount(), firstStack, event.getSql());
}
}
@@ -919,27 +919,21 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
private void controllerPost() {
boolean old = transaction.isFlushOnQuery();
transaction.setFlushOnQuery(false);
try {
switch (type) {
case INSERT:
controller.postInsert(this);
break;
case UPDATE:
controller.postUpdate(this);
break;
case DELETE_SOFT:
controller.postSoftDelete(this);
break;
case DELETE:
controller.postDelete(this);
break;
default:
break;
}
} finally {
transaction.setFlushOnQuery(old);
switch (type) {
case INSERT:
controller.postInsert(this);
break;
case UPDATE:
controller.postUpdate(this);
break;
case DELETE_SOFT:
controller.postSoftDelete(this);
break;
case DELETE:
controller.postDelete(this);
break;
default:
break;
}
}
@@ -1,71 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionList;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.persist.MultiValueWrapper;
import java.util.ArrayList;
import java.util.List;
final class SlowQueryBindCapture implements SpiExpressionBind {
private final SpiQuery<?> query;
private final List<Object> bindParams = new ArrayList<>();
SlowQueryBindCapture(SpiQuery<?> query) {
this.query = query;
}
List<Object> capture() {
var params = query.bindParams();
if (params != null) {
var positionedParameters = params.positionedParameters();
for (BindParams.Param param : positionedParameters) {
if (param.isInParam()) {
add(param.inValue());
}
}
}
Object id = query.getId();
if (id != null) {
add(id);
}
SpiExpressionList<?> spiExpressionList = query.whereExpressions();
if (spiExpressionList != null) {
spiExpressionList.addBindValues(this);
}
return bindParams;
}
private void add(Object value) {
if (value instanceof MultiValueWrapper) {
var mvw = (MultiValueWrapper) value;
bindParams.add(mvw.getValues());
} else {
bindParams.add(value);
}
}
@Override
public BeanDescriptor<?> descriptor() {
return query.descriptor();
}
@Override
public void addBindValue(Object bindValue) {
add(bindValue);
}
@Override
public void addBindEncryptKey(Object encryptKey) {
bindParams.add("*");
}
@Override
public String escapeLikeString(String value) {
return query.descriptor().ebeanServer().databasePlatform().escapeLikeString(value);
}
}
@@ -588,9 +588,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
}
/**
* Set the parent bean to the child (update the relationship).
* Set the join properties from the parent bean to the child bean.
* This is only valid for OneToMany and NOT valid for ManyToMany.
*/
public void setParentToChild(EntityBean parent, EntityBean child, Object mapKeyValue) {
public void setJoinValuesToChild(EntityBean parent, EntityBean child, Object mapKeyValue) {
if (mapKeyProperty != null) {
mapKeyProperty.setValue(child, mapKeyValue);
}
@@ -601,31 +602,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
}
}
/**
* Return true if the parent bean has been set to the child (updated the relationship).
*/
public boolean setParentToChild(EntityBean parent, EntityBean child, Object mapKeyValue, BeanDescriptor<?> parentDesc) {
if (manyToMany
|| childMasterProperty == null
|| !child._ebean_getIntercept().isLoadedProperty(childMasterProperty.propertyIndex())) {
return false;
}
Object currentParent = childMasterProperty.getValue(child);
if (currentParent != null) {
Object newId = parentDesc.getId(parent);
Object oldId = parentDesc.id(currentParent);
if (Objects.equals(newId, oldId)) {
return false;
}
}
childMasterProperty.setValueIntercept(child, parent);
if (mapKeyProperty != null) {
mapKeyProperty.setValue(child, mapKeyValue);
}
return true;
}
/**
* Return the order by clause used to order the fetching of the data for
* this list, set or map.
@@ -1,7 +1,6 @@
package io.ebeaninternal.server.deploy.id;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
import io.ebeaninternal.server.deploy.BeanProperty;
@@ -144,7 +143,7 @@ public interface IdBinder {
/**
* Binds multiple id value to a request.
*/
void addBindValues(SpiExpressionBind request, Collection<?> ids);
void addBindValues(SpiExpressionRequest request, Collection<?> ids);
/**
* Return the sql for binding the id using an IN clause.
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.deploy.id;
import io.ebean.bean.EntityBean;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
import io.ebeaninternal.server.deploy.BeanDescriptor;
@@ -270,7 +269,7 @@ public final class IdBinderEmbedded implements IdBinder {
}
@Override
public void addBindValues(SpiExpressionBind request, Collection<?> values) {
public void addBindValues(SpiExpressionRequest request, Collection<?> values) {
for (Object value : values) {
final EntityBean bean = (EntityBean) value;
for (BeanProperty prop : props) {
@@ -1,7 +1,6 @@
package io.ebeaninternal.server.deploy.id;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.bind.DataBind;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
@@ -131,7 +130,7 @@ final class IdBinderEmpty implements IdBinder {
}
@Override
public void addBindValues(SpiExpressionBind request, Collection<?> ids) {
public void addBindValues(SpiExpressionRequest request, Collection<?> ids) {
}
@Override
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.deploy.id;
import io.ebean.bean.EntityBean;
import io.ebean.core.type.ScalarType;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
import io.ebeaninternal.server.core.InternString;
@@ -144,7 +143,7 @@ public final class IdBinderSimple implements IdBinder {
}
@Override
public void addBindValues(SpiExpressionBind request, Collection<?> values) {
public void addBindValues(SpiExpressionRequest request, Collection<?> values) {
List<Object> copy = new ArrayList<>(values);
copy.replaceAll(idValue -> convertSetId(idValue, null));
request.addBindValue(new MultiValueWrapper(copy));
@@ -97,7 +97,7 @@ abstract class AbstractExpression implements SpiExpression {
validation.validate(propName);
}
protected final ElPropertyValue getElProp(SpiExpressionBind request) {
protected final ElPropertyValue getElProp(SpiExpressionRequest request) {
return request.descriptor().elGetValue(propName);
}
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
/**
@@ -29,7 +28,7 @@ abstract class AbstractTextExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
// do nothing, only execute against document store
}
@@ -51,7 +51,7 @@ final class AllEqualsExpression extends NonPrepareExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
if (propMap.isEmpty()) {
return;
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import java.io.IOException;
@@ -73,7 +72,7 @@ final class ArrayContainsExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
for (Object value : values) {
request.addBindValue(value);
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import java.io.IOException;
@@ -50,7 +49,7 @@ final class ArrayIsEmptyExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
// nothing to bind
}
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -35,7 +34,7 @@ final class BetweenExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
ElPropertyValue prop = getElProp(request);
if (prop != null && prop.isDbEncrypted()) {
// bind the key as well as the value
@@ -75,7 +75,7 @@ final class BetweenPropertyExpression extends NonPrepareExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
request.addBindValue(val());
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
/**
@@ -53,7 +52,7 @@ final class BitwiseExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
request.addBindValue(flags);
request.addBindValue(match);
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -34,7 +33,7 @@ final class CaseInsensitiveEqualExpression extends AbstractValueExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
ElPropertyValue prop = getElProp(request);
if (prop != null && prop.isDbEncrypted()) {
// bind the key as well as the value
@@ -5,7 +5,12 @@ import io.ebean.LikeType;
import io.ebean.bean.EntityBean;
import io.ebean.event.BeanQueryRequest;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiExpressionValidation;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
@@ -189,7 +194,8 @@ final class DefaultExampleExpression implements SpiExpression, ExampleExpression
* Adds bind values to the request.
*/
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
for (SpiExpression item : list) {
item.addBindValues(request);
}
@@ -591,7 +591,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
for (SpiExpression expr : list) {
expr.addBindValues(request);
}
@@ -99,7 +99,7 @@ final class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreE
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
for (Object bindParam : bindParams) {
request.addBindValue(bindParam);
}
@@ -77,7 +77,7 @@ final class ExistsSqlQueryExpression implements SpiExpression, UnsupportedDocSto
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
for (Object bindParam : bindParams) {
request.addBindValue(bindParam);
}
@@ -45,7 +45,7 @@ final class IdExpression extends NonPrepareExpression implements SpiExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
// 'flatten' EmbeddedId and multiple ID cases
// into an array of the underlying scalar field values
for (Object bindIdValue : request.descriptor().bindIdValues(value)) {
@@ -61,7 +61,7 @@ public final class IdInExpression extends NonPrepareExpression implements IdInCo
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
if (idCollection.isEmpty()) {
return;
}
@@ -2,7 +2,10 @@ package io.ebeaninternal.server.expression;
import io.ebean.bean.EntityBean;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.persist.MultiValueWrapper;
@@ -100,7 +103,7 @@ public final class InExpression extends AbstractExpression implements IdInCommon
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
if (empty) {
return;
}
@@ -3,7 +3,10 @@ package io.ebeaninternal.server.expression;
import io.ebean.Pairs;
import io.ebean.Pairs.Entry;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.persist.MultiValueWrapper;
import java.util.ArrayList;
@@ -55,7 +58,8 @@ final class InPairsExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
// Note at this point entries may have been removed when used with l2 caching
// ... for each l2 cache hit an entry was removed
this.concatBindValues = new ArrayList<>(entries.size());
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -33,7 +32,7 @@ final class InRangeExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
ElPropertyValue prop = getElProp(request);
if (prop != null && prop.isDbEncrypted()) {
// bind the key twice, for both values
@@ -2,7 +2,10 @@ package io.ebeaninternal.server.expression;
import io.ebean.InTuples;
import io.ebean.service.SpiInTuples;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import java.util.List;
@@ -34,7 +37,7 @@ final class InTuplesExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
for (Object[] entry : entries) {
for (Object value : entry) {
requireNonNull(value);
@@ -1,7 +1,10 @@
package io.ebeaninternal.server.expression;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -50,7 +53,7 @@ final class IsEmptyExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
// no bind values
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import java.io.IOException;
@@ -103,7 +102,7 @@ final class JsonPathExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
if (value != null) {
// value is null for EXISTS/NOT EXISTS
request.addBindValue(value);
@@ -141,7 +141,7 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
for (SpiExpression expr : exprList.internalList()) {
expr.addBindValues(request);
}
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.expression;
import io.ebean.LikeType;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -26,7 +25,7 @@ final class LikeExpression extends AbstractValueExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
ElPropertyValue prop = getElProp(request);
if (prop != null && prop.isDbEncrypted()) {
// bind the key as well as the value
@@ -79,7 +78,7 @@ final class LikeExpression extends AbstractValueExpression {
return strValue().equals(that.strValue());
}
private static String getValue(String value, boolean caseInsensitive, LikeType type, SpiExpressionBind request) {
private static String getValue(String value, boolean caseInsensitive, LikeType type, SpiExpressionRequest request) {
if (caseInsensitive) {
value = value.toLowerCase();
}
@@ -3,7 +3,12 @@ package io.ebeaninternal.server.expression;
import io.ebean.Expression;
import io.ebean.Junction;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiExpressionValidation;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import java.io.IOException;
@@ -128,7 +133,7 @@ abstract class LogicExpression implements SpiExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
expOne.addBindValues(request);
expTwo.addBindValues(request);
}
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.expression;
import io.ebean.LikeType;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -24,7 +23,8 @@ final class NativeILikeExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
ElPropertyValue prop = getElProp(request);
if (prop != null && prop.isDbEncrypted()) {
// bind the key as well as the value
@@ -1,8 +1,13 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiExpressionValidation;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.api.NaturalKeyQueryData;
import java.io.IOException;
@@ -89,7 +94,7 @@ final class NestedPathWrapperExpression implements SpiExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
delegate.addBindValues(request);
}
@@ -78,7 +78,7 @@ final class NoopExpression implements SpiExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
// nothing to do
}
@@ -2,7 +2,12 @@ package io.ebeaninternal.server.expression;
import io.ebean.Expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiExpressionValidation;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import java.io.IOException;
@@ -68,7 +73,7 @@ final class NotExpression implements SpiExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
exp.addBindValues(request);
}
@@ -1,7 +1,10 @@
package io.ebeaninternal.server.expression;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -48,7 +51,7 @@ final class NullExpression extends AbstractExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
}
@@ -42,7 +42,7 @@ final class RawExpression extends NonPrepareExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
if (values != null) {
for (Object value : values) {
if (value instanceof Collection<?>) {
@@ -2,7 +2,10 @@ package io.ebeaninternal.server.expression;
import io.ebean.bean.EntityBean;
import io.ebean.plugin.ExpressionPath;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
import java.io.IOException;
@@ -65,7 +68,8 @@ public final class SimpleExpression extends AbstractValueExpression {
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
ElPropertyValue prop = getElProp(request);
if (prop != null) {
if (prop.isAssocId()) {
@@ -79,7 +79,7 @@ final class SubQueryExpression extends AbstractExpression implements Unsupported
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
for (Object bindParam : bindParams) {
request.addBindValue(bindParam);
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import java.util.Arrays;
@@ -48,7 +47,7 @@ final class SubQueryRawExpression extends AbstractExpression implements Unsuppor
}
@Override
public void addBindValues(SpiExpressionBind request) {
public void addBindValues(SpiExpressionRequest request) {
for (Object bindParam : bindParams) {
request.addBindValue(bindParam);
}
@@ -226,7 +226,7 @@ public final class BatchControl {
void executeNow(ArrayList<PersistRequest> list) throws BatchedSqlException {
boolean old = transaction.isFlushOnQuery();
transaction.setFlushOnQuery(false);
// disable flush on query due transaction callbacks
// disable flush on query due transsaction callbacks
try {
for (int i = 0; i < list.size(); i++) {
if (i % batchSize == 0) {
@@ -172,9 +172,7 @@ final class SaveManyBeans extends SaveManyBase {
} else if (ebi.isNewOrDirty()) {
skipSavingThisBean = false;
// set the parent bean to detailBean
many.setParentToChild(parentBean, detail, mapKeyValue);
} else if (many.setParentToChild(parentBean, detail, mapKeyValue, request.descriptor())) {
skipSavingThisBean = false;
many.setJoinValuesToChild(parentBean, detail, mapKeyValue);
} else {
skipSavingThisBean = saveRecurseSkippable;
}
@@ -1,6 +1,9 @@
package io.ebeaninternal.server.query;
import io.ebean.*;
import io.ebean.CountDistinctOrder;
import io.ebean.Query;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.SqlLimitRequest;
@@ -49,13 +52,11 @@ final class CQueryBuilder {
private final CQueryDraftSupport draftSupport;
private final DatabasePlatform dbPlatform;
private final boolean selectCountWithColumnAlias;
private final boolean includeLabelInSql;
/**
* Create the SqlGenSelect.
*/
CQueryBuilder(DatabaseBuilder.Settings config, DatabasePlatform dbPlatform, Binder binder, CQueryHistorySupport historySupport, CQueryDraftSupport draftSupport) {
this.includeLabelInSql = config.isIncludeLabelInSql();
CQueryBuilder(DatabasePlatform dbPlatform, Binder binder, CQueryHistorySupport historySupport, CQueryDraftSupport draftSupport) {
this.dbPlatform = dbPlatform;
this.binder = binder;
this.draftSupport = draftSupport;
@@ -595,7 +596,7 @@ final class CQueryBuilder {
}
private void appendSelectDistinct() {
sb.append("select ").append(hint()).append(inlineSqlComment());
sb.append("select ");
if (distinct && !countSingleAttribute) {
if (request.isInlineCountDistinct()) {
sb.append("count(");
@@ -608,30 +609,6 @@ final class CQueryBuilder {
}
}
private String hint() {
String hint = query.hint();
return hint == null ? "" : dbPlatform.inlineSqlHint(hint);
}
private String inlineSqlComment() {
if (!includeLabelInSql) {
return "";
}
SpiQuery.Type type = query.type();
if (type == SpiQuery.Type.SQ_EX || type == SpiQuery.Type.SQ_EXISTS) {
return "";
}
final var label = query.label();
if (label != null) {
return dbPlatform.inlineSqlComment(label);
}
final var profileLocation = query.profileLocation();
if (profileLocation != null) {
return dbPlatform.inlineSqlComment(profileLocation.label());
}
return "";
}
private void appendFrom() {
if (selectClause == null || !selectClause.startsWith("update")) {
sb.append(" from ");
@@ -44,7 +44,7 @@ public final class CQueryEngine {
this.defaultFetchSizeFindList = config.getJdbcFetchSizeFindList();
this.forwardOnlyHintOnFindIterate = dbPlatform.forwardOnlyHintOnFindIterate();
this.historySupport = new CQueryHistorySupport(dbPlatform.historySupport(), asOfTableMapping, config.getAsOfSysPeriod());
this.queryBuilder = new CQueryBuilder(config, dbPlatform, binder, historySupport, new CQueryDraftSupport(draftTableMap));
this.queryBuilder = new CQueryBuilder(dbPlatform, binder, historySupport, new CQueryDraftSupport(draftTableMap));
}
public int forwardOnlyFetchSize() {
@@ -133,11 +133,6 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
return this;
}
@Override
public Query<T> setHint(String hint) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
// Everything else deemed invalid
@Override
@@ -3,9 +3,8 @@ package io.ebeaninternal.server.query;
import io.ebean.PagedList;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import jakarta.persistence.PersistenceException;
import java.util.Collections;
import jakarta.persistence.PersistenceException;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;
@@ -21,7 +20,7 @@ public final class LimitOffsetPagedList<T> implements PagedList<T> {
private final int firstRow;
private final int maxRows;
private int totalRowCount = -1;
private int foregroundTotalRowCount = -1;
private Future<Integer> futureRowCount;
private List<T> list;
@@ -58,12 +57,7 @@ public final class LimitOffsetPagedList<T> implements PagedList<T> {
lock.lock();
try {
if (list == null) {
if (totalRowCount == 0) {
// already count and no rows
list = Collections.emptyList();
} else {
list = server.findList(query);
}
list = server.findList(query);
}
return list;
} finally {
@@ -93,21 +87,20 @@ public final class LimitOffsetPagedList<T> implements PagedList<T> {
public int getTotalCount() {
lock.lock();
try {
if (totalRowCount > -1) {
return totalRowCount;
}
if (futureRowCount != null) {
try {
// background query already initiated so get it with a wait
totalRowCount = futureRowCount.get();
return totalRowCount;
return futureRowCount.get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
// already fetched?
if (foregroundTotalRowCount > -1) return foregroundTotalRowCount;
// just using foreground thread
totalRowCount = server.findCount(query);
return totalRowCount;
foregroundTotalRowCount = server.findCount(query);
return foregroundTotalRowCount;
} finally {
lock.unlock();
}
@@ -250,9 +250,6 @@ public final class DefaultDtoQuery<T> extends AbstractQuery implements SpiDtoQue
@Override
public DtoQuery<T> setLabel(String label) {
this.label = label;
if (ormQuery != null) {
ormQuery.setLabel(label);
}
return this;
}
@@ -57,7 +57,6 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
private ProfilingListener profilingListener;
private Type type;
private String label;
private String hint;
private Mode mode = Mode.NORMAL;
private boolean usingFuture;
private Object tenantId;
@@ -236,11 +235,6 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
return label;
}
@Override
public final String hint() {
return hint;
}
@Override
public final String planLabel() {
if (label != null) {
@@ -264,12 +258,6 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
return this;
}
@Override
public final Query<T> setHint(String hint) {
this.hint = hint;
return this;
}
@Override
public final boolean isAutoTunable() {
return nativeSql == null && beanDescriptor.isAutoTunable();
@@ -744,7 +732,6 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
copy.timeout = timeout;
copy.mapKey = mapKey;
copy.id = id;
copy.hint = hint;
copy.label = label;
copy.nativeSql = nativeSql;
copy.useBeanCache = useBeanCache;
@@ -1082,9 +1069,6 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
if (manualId) {
sb.append("/md");
}
if (hint != null) {
sb.append("/h:").append(hint);
}
if (distinct) {
sb.append("/dt");
}
@@ -1,74 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.Query;
import io.ebeaninternal.api.SpiQuery;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.Customer;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class SlowQueryBindCaptureTest {
static Database db;
@BeforeAll
static void beforeAll() {
db = DB.getDefault();
db.script().run("/h2-init.sql");
}
@Test
void bindParams_capture() {
String sql = "select * from o_customer p where p.status = ?";
Query<Customer> query = db.findNative(Customer.class, sql)
.setParameter("N");
query.findList();
var sbc = new SlowQueryBindCapture((SpiQuery<?>) query);
List<Object> bindParams = sbc.capture();
assertThat(bindParams).hasSize(1);
assertThat(bindParams.get(0)).isEqualTo("N");
}
@Test
void expressionsCapture() {
Query<Customer> query = db.find(Customer.class)
.where()
.startsWith("name", "rob")
.eq("status", "N")
.query();
query.findList();
var sbc = new SlowQueryBindCapture((SpiQuery<?>) query);
List<Object> bindParams = sbc.capture();
assertThat(bindParams).hasSize(2);
assertThat(bindParams.get(0)).isEqualTo("rob%");
assertThat(bindParams.get(1)).isEqualTo("N");
}
@SuppressWarnings("unchecked")
@Test
void multiValueCapture() {
Query<Customer> query = db.find(Customer.class)
.where()
.in("id", List.of(1, 2, 3, 4))
.query();
query.findList();
var sbc = new SlowQueryBindCapture((SpiQuery<?>) query);
List<Object> bindParams = sbc.capture();
assertThat(bindParams).hasSize(1);
Object bindParam0 = bindParams.get(0);
assertThat(bindParam0).isInstanceOf(ArrayList.class);
var values = (ArrayList<Object>) bindParam0;
assertThat(values).contains(1, 2, 3, 4);
}
}
@@ -29,27 +29,6 @@ public class OrmQueryPlanKeyTest extends BaseTest {
assertSame(query().createQueryPlanKey(), query().createQueryPlanKey());
}
@Test
public void equals_when_hintIsDifferent_expect_different() {
DefaultOrmQuery<Customer> q1 = query();
q1.setHint("a");
assertDifferent(q1, query());
DefaultOrmQuery<Customer> q2 = query();
q2.setHint("b");
assertDifferent(q1, q2);
}
@Test
public void equals_when_hintIsSame() {
DefaultOrmQuery<Customer> q1 = query();
q1.setHint("b");
DefaultOrmQuery<Customer> q2 = query();
q2.setHint("b");
assertSame(q1, q2);
}
@Test
public void equals_when_diffTableJoinNull() {
+3 -3
View File
@@ -1,5 +1,5 @@
create table if not exists animal (
create table animal (
species varchar(255) not null,
id bigint generated by default as identity not null,
shelter_id bigint,
@@ -11,14 +11,14 @@ create table if not exists animal (
constraint pk_animal primary key (id)
);
create table if not exists animal_shelter (
create table animal_shelter (
id bigint generated by default as identity not null,
name varchar(255),
version bigint not null,
constraint pk_animal_shelter primary key (id)
);
create table if not exists o_customer (
create table o_customer (
id integer generated by default as identity not null,
status varchar(1),
name varchar(40) not null,
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
</parent>
<name>ebean ddl generation</name>
@@ -28,14 +28,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>provided</scope>
</dependency>
@@ -65,7 +65,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>test</scope>
</dependency>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -14,7 +14,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>provided</scope>
</dependency>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
</parent>
<name>ebean postgis types</name>
@@ -19,14 +19,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
</dependency>
<!-- provided scope -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>provided</scope>
</dependency>
@@ -62,7 +62,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>test</scope>
</dependency>
@@ -82,11 +82,11 @@
<plugin>
<groupId>io.repaint.maven</groupId>
<artifactId>tiles-maven-plugin</artifactId>
<version>2.40</version>
<version>2.34</version>
<extensions>true</extensions>
<configuration>
<tiles>
<tile>io.ebean.tile:enhancement:14.2.0</tile>
<tile>io.ebean.tile:enhancement:13.22.0</tile>
</tiles>
</configuration>
</plugin>
+7 -7
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.1.0</version>
<version>14.0.1</version>
</parent>
<name>ebean querybean</name>
@@ -17,7 +17,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>provided</scope>
</dependency>
@@ -63,14 +63,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>test</scope>
</dependency>
@@ -84,7 +84,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>14.1.0</version>
<version>14.0.1</version>
<scope>provided</scope>
</dependency>
@@ -96,11 +96,11 @@
<plugin>
<groupId>io.repaint.maven</groupId>
<artifactId>tiles-maven-plugin</artifactId>
<version>2.40</version>
<version>2.34</version>
<extensions>true</extensions>
<configuration>
<tiles>
<tile>io.ebean.tile:enhancement:14.2.0</tile>
<tile>io.ebean.tile:enhancement:13.22.0</tile>
</tiles>
</configuration>
</plugin>
@@ -1,586 +0,0 @@
package io.ebean.typequery;
import io.ebean.*;
import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import java.util.Collection;
/**
* Query bean for strongly typed query construction and execution.
* <p>
* For each entity bean querybean-generator generates a query bean that implements QueryBean.
*
* <h2>Example - usage of QCustomer</h2>
* <pre>{@code
*
* Date fiveDaysAgo = ...
*
* List<Customer> customers =
* new QCustomer()
* .name.ilike("rob")
* .status.equalTo(Customer.Status.GOOD)
* .registered.after(fiveDaysAgo)
* .contacts.email.endsWith("@foo.com")
* .orderBy()
* .name.asc()
* .registered.desc()
* .findList();
*
* }</pre>
* <p>
* <h2>Resulting SQL where</h2>
* <pre>{@code
*
* where lower(t0.name) like ? and t0.status = ? and t0.registered > ? and u1.email like ?
* order by t0.name, t0.registered desc;
*
* --bind(rob,GOOD,Mon Jul 27 12:05:37 NZST 2015,%@foo.com)
*
* }</pre>
*
* @param <T> the entity bean type (normal entity bean type e.g. Customer)
* @param <R> the specific query bean type (e.g. QCustomer)
*/
public interface IQueryBean<T, R> extends QueryBuilder<R, T> {
/**
* Return the underlying query.
* <p>
* Generally it is not expected that you will need to do this but typically use
* the find methods available on this 'root query bean' instance like findList().
*/
Query<T> query();
/**
* Return the fetch group.
*/
FetchGroup<T> buildFetchGroup();
/**
* Specify the properties to be loaded on the 'main' root level entity bean.
* <p>
* The resulting entities with be "partially loaded" aka partial objects.
* <p>
* Alternatively we can use a {@link #select(FetchGroup)} to specify all properties
* to load on all parts of the graph.
*
* <pre>{@code
*
* // alias for the customer properties in select()
* QCustomer cust = QCustomer.alias();
*
* // alias for the contact properties in contacts.fetch()
* QContact contact = QContact.alias();
*
* List<Customer> customers =
* new QCustomer()
* // specify the parts of the graph we want to load
* .select(cust.id, cust.name)
* .contacts.fetch(contact.firstName, contact.lastName, contact.email)
*
* // predicates
* .id.gt(1)
* .findList();
*
* }</pre>
*
* @param properties the list of properties to fetch
*/
@SuppressWarnings("unchecked")
R select(TQProperty<R, ?>... properties);
/**
* Specify the properties to be loaded on the 'main' root level entity bean
* also allowing for functions to be used like {@link StdOperators#max(Query.Property)}.
*
* @param properties the list of properties to fetch
*/
R select(Query.Property<?>... properties);
/**
* Add an expression to the WHERE or HAVING clause.
*/
R add(Expression expression);
/**
* Add EXISTS sub-query predicate.
*/
R exists(Query<?> subQuery);
/**
* Add NOT EXISTS sub-query predicate.
*/
R notExists(Query<?> subQuery);
/**
* EXISTS using a SQL SubQuery.
*
* @param sqlSubQuery The SQL SubQuery
* @param bindValues Optional bind values if the SubQuery uses {@code ? } bind values.
*/
R exists(String sqlSubQuery, Object... bindValues);
/**
* Not EXISTS using a SQL SubQuery.
*
* @param sqlSubQuery The SQL SubQuery
* @param bindValues Optional bind values if the SubQuery uses {@code ? } bind values.
*/
R notExists(String sqlSubQuery, Object... bindValues);
/**
* Set the Id value to query. This is used with findOne().
* <p>
* You can use this to have further control over the query. For example adding fetch joins.
* <p>
* <pre>{@code
*
* Order order =
* new QOrder()
* .setId(1)
* .fetch("details")
* .findOne();
*
* // the order details were eagerly fetched
* List<OrderDetail> details = order.getDetails();
*
* }</pre>
*/
R setId(Object id);
/**
* Set a list of Id values to match.
* <p>
* <pre>{@code
*
* List<Order> orders =
* new QOrder()
* .setIdIn(42, 43, 44)
* .findList();
*
* }</pre>
*/
R setIdIn(Object... ids);
/**
* Set a collection of Id values to match.
* <p>
* <pre>{@code
*
* Collection<?> ids = ...
*
* List<Order> orders =
* new QOrder()
* .setIdIn(ids)
* .findList();
*
* }</pre>
*/
R setIdIn(Collection<?> ids);
/**
* Add raw expression with no parameters.
* <p>
* When properties in the clause are fully qualified as table-column names
* then they are not translated. logical property name names (not fully
* qualified) will still be translated to their physical name.
* <p>
* <pre>{@code
*
* raw("orderQty < shipQty")
*
* }</pre>
*
* <h4>Subquery example:</h4>
* <pre>{@code
*
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
*
* }</pre>
*/
R raw(String rawExpression);
/**
* Add raw expression with an array of parameters.
* <p>
* The raw expression should contain the same number of ? as there are
* parameters.
* <p>
* When properties in the clause are fully qualified as table-column names
* then they are not translated. logical property name names (not fully
* qualified) will still be translated to their physical name.
*/
R raw(String rawExpression, Object... bindValues);
/**
* Only add the raw expression if the values is not null or empty.
* <p>
* This is a pure convenience expression to make it nicer to deal with the pattern where we use
* raw() expression with a subquery and only want to add the subquery predicate when the collection
* of values is not empty.
*
* <h3>Without inOrEmpty()</h3>
* <pre>{@code
*
* QCustomer query = new QCustomer() // add some predicates
* .status.equalTo(Status.NEW);
*
* // common pattern - we can use rawOrEmpty() instead
* if (orderIds != null && !orderIds.isEmpty()) {
* query.raw("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
* }
*
* query.findList();
*
* }</pre>
*
* <h3>Using rawOrEmpty()</h3>
* Note that in the example below we use the <code>?1</code> bind parameter to get "parameter expansion"
* for each element in the collection.
*
* <pre>{@code
*
* new QCustomer()
* .status.equalTo(Status.NEW)
* // only add the expression if orderIds is not empty
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
* .findList();
*
* }</pre>
*
* <h3>Postgres ANY</h3>
* With Postgres we would often use the SQL <code>ANY</code> expression and array parameter binding
* rather than <code>IN</code>.
*
* <pre>{@code
*
* new QCustomer()
* .status.equalTo(Status.NEW)
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id = any(?))", orderIds);
* .findList();
*
* }</pre>
* <p>
* Note that we need to cast the Postgres array for UUID types like:
* </p>
* <pre>{@code
*
* " ... = any(?::uuid[])"
*
* }</pre>
*
* @param raw The raw expression that is typically a subquery
* @param values The values which is typically a list or set of id values.
*/
R rawOrEmpty(String raw, Collection<?> values);
/**
* Add raw expression with a single parameter.
* <p>
* The raw expression should contain a single ? at the location of the
* parameter.
* <p>
* When properties in the clause are fully qualified as table-column names
* then they are not translated. logical property name names (not fully
* qualified) will still be translated to their physical name.
* <p>
* <h4>Example:</h4>
* <pre>{@code
*
* // use a database function
* raw("add_days(orderDate, 10) < ?", someDate)
*
* }</pre>
*
* <h4>Subquery example:</h4>
* <pre>{@code
*
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
*
* }</pre>
*/
R raw(String rawExpression, Object bindValue);
/**
* In expression using multiple columns.
*/
R inTuples(InTuples inTuples);
/**
* Marker that can be used to indicate that the order by clause is defined after this.
* <p>
* <h2>Example: order by customer name, order date</h2>
* <pre>{@code
* List<Order> orders =
* new QOrder()
* .customer.name.ilike("rob")
* .orderBy()
* .customer.name.asc()
* .orderDate.asc()
* .findList();
*
* }</pre>
*/
R orderBy();
/**
* @deprecated migrate to {@link #orderBy()}.
*/
@Deprecated(since = "13.19", forRemoval = true)
R order();
/**
* @deprecated migrate to {@link #orderBy(String)}
*/
@Deprecated(since = "13.19", forRemoval = true)
R order(String orderByClause);
/**
* Begin a list of expressions added by 'OR'.
* <p>
* Use endOr() or endJunction() to stop added to OR and 'pop' to the parent expression list.
* <p>
* <h2>Example</h2>
* <p>
* This example uses an 'OR' expression list with an inner 'AND' expression list.
* </p>
* <pre>{@code
*
* List<Customer> customers =
* new QCustomer()
* .status.equalTo(Customer.Status.GOOD)
* .or()
* .id.greaterThan(1000)
* .and()
* .name.startsWith("super")
* .registered.after(fiveDaysAgo)
* .endAnd()
* .endOr()
* .orderBy().id.desc()
* .findList();
*
* }</pre>
* <h2>Resulting SQL where clause</h2>
* <pre>{@code sql
*
* where t0.status = ? and (t0.id > ? or (t0.name like ? and t0.registered > ? ) )
* order by t0.id desc;
*
* --bind(GOOD,1000,super%,Wed Jul 22 00:00:00 NZST 2015)
*
* }</pre>
*/
R or();
/**
* Begin a list of expressions added by 'AND'.
* <p>
* Use endAnd() or endJunction() to stop added to AND and 'pop' to the parent expression list.
* <p>
* Note that typically the AND expression is only used inside an outer 'OR' expression.
* This is because the top level expression list defaults to an 'AND' expression list.
*
* <h2>Example</h2>
* <p>
* This example uses an 'OR' expression list with an inner 'AND' expression list.
* </p>
* <pre>{@code
*
* List<Customer> customers =
* new QCustomer()
* .status.equalTo(Customer.Status.GOOD)
* .or() // OUTER 'OR'
* .id.greaterThan(1000)
* .and() // NESTED 'AND' expression list
* .name.startsWith("super")
* .registered.after(fiveDaysAgo)
* .endAnd()
* .endOr()
* .orderBy().id.desc()
* .findList();
*
* }</pre>
* <h2>Resulting SQL where clause</h2>
* <pre>{@code sql
*
* where t0.status = ? and (t0.id > ? or (t0.name like ? and t0.registered > ? ) )
* order by t0.id desc;
*
* --bind(GOOD,1000,super%,Wed Jul 22 00:00:00 NZST 2015)
*
* }</pre>
*/
R and();
/**
* Begin a list of expressions added by NOT.
* <p>
* Use endNot() or endJunction() to stop added to NOT and 'pop' to the parent expression list.
*/
R not();
/**
* Begin a list of expressions added by MUST.
* <p>
* This automatically makes this query a document store query.
* <p>
* Use endJunction() to stop added to MUST and 'pop' to the parent expression list.
*/
R must();
/**
* Begin a list of expressions added by MUST NOT.
* <p>
* This automatically makes this query a document store query.
* <p>
* Use endJunction() to stop added to MUST NOT and 'pop' to the parent expression list.
*/
R mustNot();
/**
* Begin a list of expressions added by SHOULD.
* <p>
* This automatically makes this query a document store query.
* <p>
* Use endJunction() to stop added to SHOULD and 'pop' to the parent expression list.
*/
R should();
/**
* End a list of expressions added by 'OR'.
*/
R endJunction();
/**
* End OR junction - synonym for endJunction().
*/
R endOr();
/**
* End AND junction - synonym for endJunction().
*/
R endAnd();
/**
* End NOT junction - synonym for endJunction().
*/
R endNot();
/**
* Add expression after this to the WHERE expression list.
* <p>
* For queries against the normal database (not the doc store) this has no effect.
* <p>
* This is intended for use with Document Store / ElasticSearch where expressions can be put into either
* the "query" section or the "filter" section of the query. Full text expressions like MATCH are in the
* "query" section but many expression can be in either - expressions after the where() are put into the
* "filter" section which means that they don't add to the relevance and are also cache-able.
*/
R where();
/**
* Begin added expressions to the 'Text' expression list.
* <p>
* This automatically makes the query a document store query.
* <p>
* For ElasticSearch expressions added to 'text' go into the ElasticSearch 'query context'
* and expressions added to 'where' go into the ElasticSearch 'filter context'.
*/
R text();
/**
* Add a Text Multi-match expression (document store only).
* <p>
* This automatically makes the query a document store query.
*/
R multiMatch(String query, MultiMatch multiMatch);
/**
* Add a Text Multi-match expression (document store only).
* <p>
* This automatically makes the query a document store query.
*/
R multiMatch(String query, String... properties);
/**
* Add a Text common terms expression (document store only).
* <p>
* This automatically makes the query a document store query.
*/
R textCommonTerms(String query, TextCommonTerms options);
/**
* Add a Text simple expression (document store only).
* <p>
* This automatically makes the query a document store query.
*/
R textSimple(String query, TextSimple options);
/**
* Add a Text query string expression (document store only).
* <p>
* This automatically makes the query a document store query.
*/
R textQueryString(String query, TextQueryString options);
/**
* Return the expression list that has been built for this query.
*/
ExpressionList<T> getExpressionList();
/**
* Start adding expressions to the having clause when using @Aggregation properties.
*
* <pre>{@code
*
* new QMachineUse()
* // where ...
* .date.inRange(fromDate, toDate)
*
* .having()
* .sumHours.greaterThan(1)
* .findList()
*
* // The sumHours property uses @Aggregation
* // e.g. @Aggregation("sum(hours)")
*
* }</pre>
*/
R having();
/**
* Return the underlying having clause to typically when using dynamic aggregation formula.
* <p>
* Note that after this we no longer have the query bean so typically we use this right
* at the end of the query.
*
* <pre>{@code
*
* // sum(distanceKms) ... is a "dynamic formula"
* // so we use havingClause() for it like:
*
* List<MachineUse> machineUse =
*
* new QMachineUse()
* .select("machine, sum(fuelUsed), sum(distanceKms)")
*
* // where ...
* .date.greaterThan(LocalDate.now().minusDays(7))
*
* .havingClause()
* .gt("sum(distanceKms)", 2)
* .findList();
*
* }</pre>
*/
ExpressionList<T> havingClause();
/**
* Set the profile location of this query. This is used to relate query execution metrics
* back to a location like a specific line of code.
*/
R setProfileLocation(ProfileLocation profileLocation);
}
@@ -1,912 +0,0 @@
package io.ebean.typequery;
import io.avaje.lang.NonNullApi;
import io.avaje.lang.Nullable;
import io.ebean.*;
import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import io.ebean.service.SpiFetchGroupQuery;
import io.ebeaninternal.api.SpiQueryFetch;
import io.ebeaninternal.server.util.ArrayStack;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.*;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* Base root query bean providing common features for all root query beans.
* <p>
* For each entity bean querybean-generator generates a query bean that extends TQRootBean.
*
* <h2>Example - usage of QCustomer</h2>
* <pre>{@code
*
* Date fiveDaysAgo = ...
*
* List<Customer> customers =
* new QCustomer()
* .name.ilike("rob")
* .status.equalTo(Customer.Status.GOOD)
* .registered.after(fiveDaysAgo)
* .contacts.email.endsWith("@foo.com")
* .orderBy()
* .name.asc()
* .registered.desc()
* .findList();
*
* }</pre>
* <p>
* <h2>Resulting SQL where</h2>
* <p>
* <pre>{@code
*
* where lower(t0.name) like ? and t0.status = ? and t0.registered > ? and u1.email like ?
* order by t0.name, t0.registered desc;
*
* --bind(rob,GOOD,Mon Jul 27 12:05:37 NZST 2015,%@foo.com)
*
* }</pre>
*
* @param <T> the entity bean type (normal entity bean type e.g. Customer)
* @param <R> the specific root query bean type (e.g. QCustomer)
*/
@NonNullApi
public abstract class QueryBean<T, R> implements IQueryBean<T, R> {
/**
* The underlying query.
*/
private final Query<T> query;
/**
* The root query bean instance. Used to provide fluid query construction.
*/
private final R root;
/**
* The underlying expression lists held as a stack. Pushed and popped based on and/or (conjunction/disjunction).
*/
private ArrayStack<ExpressionList<T>> whereStack;
/**
* Stack of Text expressions ("query" section of ElasticSearch query rather than "filter" section).
*/
private ArrayStack<ExpressionList<T>> textStack;
/**
* When true expressions should be added to the "text" stack - ElasticSearch "query" section
* rather than the "where" stack.
*/
private boolean textMode;
/**
* Construct using the type of bean to query on and the default database.
*/
public QueryBean(Class<T> beanType) {
this(beanType, DB.getDefault());
}
/**
* Construct using the type of bean to query on and a given database.
*/
public QueryBean(Class<T> beanType, Database database) {
this(database.find(beanType));
}
/**
* Construct with a transaction.
*/
protected QueryBean(Class<T> beanType, Transaction transaction) {
this(beanType);
query.usingTransaction(transaction);
}
/**
* Construct with a database and transaction.
*/
protected QueryBean(Class<T> beanType, Database database, Transaction transaction) {
this(beanType, database);
query.usingTransaction(transaction);
}
/**
* Construct using a query.
*/
@SuppressWarnings("unchecked")
public QueryBean(Query<T> query) {
this.query = query;
this.root = (R) this;
}
/**
* Construct for using as an 'Alias' to use the properties as known string
* values for select() and fetch().
*/
@SuppressWarnings("unchecked")
public QueryBean(boolean aliasDummy) {
this.query = null;
this.root = (R) this;
}
/** Construct for FilterMany */
protected QueryBean(ExpressionList<T> filter) {
this.query = null;
this.root = null;
this.whereStack = new ArrayStack<>();
whereStack.push(filter);
}
/**
* The enhancement will no longer use this method. Will be removed once the new IntelliJ plugin is released.
*/
protected void setRoot(R root) {
// do nothing, remove this method shortly.
}
@Override
public FetchGroup<T> buildFetchGroup() {
return ((SpiFetchGroupQuery<T>) query()).buildFetchGroup();
}
@Override
public Query<T> query() {
return query;
}
@Override
public R select(String properties) {
query.select(properties);
return root;
}
@Override
public R select(FetchGroup<T> fetchGroup) {
query.select(fetchGroup);
return root;
}
@Override
@SafeVarargs
public final R select(TQProperty<R, ?>... properties) {
((SpiQueryFetch) query).selectProperties(properties(properties));
return root;
}
private Set<String> properties(Query.Property<?>[] properties) {
Set<String> props = new LinkedHashSet<>();
for (Query.Property<?> property : properties) {
props.add(property.toString());
}
return props;
}
@Override
public final R select(Query.Property<?>... properties) {
((SpiQueryFetch) query).selectProperties(properties(properties));
return root;
}
@Override
public final R fetch(String path) {
query.fetch(path);
return root;
}
@Override
public final R fetchQuery(String path) {
query.fetchQuery(path);
return root;
}
@Override
public final R fetchCache(String path) {
query.fetchCache(path);
return root;
}
@Override
public final R fetchQuery(String path, String properties) {
query.fetchQuery(path, properties);
return root;
}
@Override
public final R fetchCache(String path, String properties) {
query.fetchCache(path, properties);
return root;
}
@Override
public final R fetch(String path, String properties) {
query.fetch(path, properties);
return root;
}
@Override
public final R fetch(String path, String properties, FetchConfig fetchConfig) {
query.fetch(path, properties, fetchConfig);
return root;
}
@Override
public final R fetch(String path, FetchConfig fetchConfig) {
query.fetch(path, fetchConfig);
return root;
}
@Override
public R fetchLazy(String path, String fetchProperties) {
query.fetchLazy(path, fetchProperties);
return root;
}
@Override
public R fetchLazy(String path) {
query.fetchLazy(path);
return root;
}
@Override
public final R apply(FetchPath pathProperties) {
query.apply(pathProperties);
return root;
}
@Override
public final R alsoIf(BooleanSupplier predicate, Consumer<R> apply) {
if (predicate.getAsBoolean()) {
apply.accept(root);
}
return root;
}
@Override
public final R asOf(Timestamp asOf) {
query.asOf(asOf);
return root;
}
@Override
public final R asDraft() {
query.asDraft();
return root;
}
@Override
public final R setIncludeSoftDeletes() {
query.setIncludeSoftDeletes();
return root;
}
@Override
public final R add(Expression expression) {
peekExprList().add(expression);
return root;
}
@Override
public final R alias(String alias) {
query.alias(alias);
return root;
}
@Override
public final R setMaxRows(int maxRows) {
query.setMaxRows(maxRows);
return root;
}
@Override
public final R setFirstRow(int firstRow) {
query.setFirstRow(firstRow);
return root;
}
@Override
public final R setAllowLoadErrors() {
query.setAllowLoadErrors();
return root;
}
@Override
public final R setAutoTune(boolean autoTune) {
query.setAutoTune(autoTune);
return root;
}
@Override
public final R setBufferFetchSizeHint(int fetchSize) {
query.setBufferFetchSizeHint(fetchSize);
return root;
}
@Override
public final R setDistinct(boolean distinct) {
query.setDistinct(distinct);
return root;
}
@Override
public final R setDocIndexName(String indexName) {
query.setDocIndexName(indexName);
return root;
}
@Override
public final R setInheritType(Class<? extends T> type) {
query.setInheritType(type);
return root;
}
@Override
public final R setBaseTable(String baseTable) {
query.setBaseTable(baseTable);
return root;
}
@Override
public final R withLock(Query.LockType lockType) {
query.withLock(lockType);
return root;
}
@Override
public final R withLock(Query.LockType lockType, Query.LockWait lockWait) {
query.withLock(lockType, lockWait);
return root;
}
@Override
public final R exists(Query<?> subQuery) {
peekExprList().exists(subQuery);
return root;
}
@Override
public final R notExists(Query<?> subQuery) {
peekExprList().notExists(subQuery);
return root;
}
@Override
public final R exists(String sqlSubQuery, Object... bindValues) {
peekExprList().exists(sqlSubQuery, bindValues);
return root;
}
@Override
public final R notExists(String sqlSubQuery, Object... bindValues) {
peekExprList().notExists(sqlSubQuery, bindValues);
return root;
}
@Override
public final R forUpdate() {
query.forUpdate();
return root;
}
@Override
public final R forUpdateNoWait() {
query.forUpdateNoWait();
return root;
}
@Override
public final R forUpdateSkipLocked() {
query.forUpdateSkipLocked();
return root;
}
@Override
public final UpdateQuery<T> asUpdate() {
return query.asUpdate();
}
@Override
public final <D> DtoQuery<D> asDto(Class<D> dtoClass) {
return query.asDto(dtoClass);
}
@Override
public final R setId(Object id) {
query.setId(id);
return root;
}
@Override
public final R setIdIn(Object... ids) {
query.where().idIn(ids);
return root;
}
@Override
public final R setIdIn(Collection<?> ids) {
query.where().idIn(ids);
return root;
}
@Override
public final R setLabel(String label) {
query.setLabel(label);
return root;
}
@Override
public final R setHint(String hint) {
query.setHint(hint);
return root;
}
@Override
public final R setProfileLocation(ProfileLocation profileLocation) {
query.setProfileLocation(profileLocation);
return root;
}
@Override
public final R setLazyLoadBatchSize(int lazyLoadBatchSize) {
query.setLazyLoadBatchSize(lazyLoadBatchSize);
return root;
}
@Override
public final R setMapKey(String mapKey) {
query.setMapKey(mapKey);
return root;
}
@Override
public final R setPersistenceContextScope(PersistenceContextScope scope) {
query.setPersistenceContextScope(scope);
return root;
}
@Override
public R setCountDistinct(CountDistinctOrder orderBy) {
query.setCountDistinct(orderBy);
return root;
}
@Override
public final R setRawSql(RawSql rawSql) {
query.setRawSql(rawSql);
return root;
}
@Override
public final R setReadOnly(boolean readOnly) {
query.setReadOnly(readOnly);
return root;
}
@Override
public final R setBeanCacheMode(CacheMode beanCacheMode) {
query.setBeanCacheMode(beanCacheMode);
return root;
}
@Override
public final R setUseDocStore(boolean useDocStore) {
query.setUseDocStore(useDocStore);
return root;
}
@Override
public final R setDisableLazyLoading(boolean disableLazyLoading) {
query.setDisableLazyLoading(disableLazyLoading);
return root;
}
@Override
public final R setDisableReadAuditing() {
query.setDisableReadAuditing();
return root;
}
@Override
public final R setUseQueryCache(CacheMode cacheMode) {
query.setUseQueryCache(cacheMode);
return root;
}
@Override
public final R setTimeout(int secs) {
query.setTimeout(secs);
return root;
}
@Override
public final Set<String> validate() {
return query.validate();
}
@Override
public final R raw(String rawExpression) {
peekExprList().raw(rawExpression);
return root;
}
@Override
public final R raw(String rawExpression, Object... bindValues) {
peekExprList().raw(rawExpression, bindValues);
return root;
}
@Override
public final R rawOrEmpty(String raw, Collection<?> values) {
peekExprList().rawOrEmpty(raw, values);
return root;
}
@Override
public final R raw(String rawExpression, Object bindValue) {
peekExprList().raw(rawExpression, bindValue);
return root;
}
@Override
public final R inTuples(InTuples inTuples) {
peekExprList().inTuples(inTuples);
return root;
}
@Override
public final R orderBy() {
// Yes this does not actually do anything! We include it because style wise it makes
// the query nicer to read and suggests that order by definitions are added after this
return root;
}
@Override
@Deprecated(since = "13.19", forRemoval = true)
public final R order() {
return root;
}
@Override
public final R orderBy(String orderByClause) {
query.orderBy(orderByClause);
return root;
}
@Override
public R setOrderBy(OrderBy<T> orderBy) {
query.setOrderBy(orderBy);
return root;
}
@Override
public R orderById(boolean orderById) {
query.orderById(orderById);
return root;
}
@Override
@Deprecated(since = "13.19", forRemoval = true)
public final R order(String orderByClause) {
return orderBy(orderByClause);
}
@Override
public final R or() {
pushExprList(peekExprList().or());
return root;
}
@Override
public final R and() {
pushExprList(peekExprList().and());
return root;
}
@Override
public final R not() {
pushExprList(peekExprList().not());
return root;
}
@Override
public final R must() {
pushExprList(peekExprList().must());
return root;
}
@Override
public final R mustNot() {
return pushExprList(peekExprList().mustNot());
}
@Override
public final R should() {
return pushExprList(peekExprList().should());
}
@Override
public final R endJunction() {
if (textMode) {
textStack.pop();
} else {
whereStack.pop();
}
return root;
}
@Override
public final R endOr() {
return endJunction();
}
@Override
public final R endAnd() {
return endJunction();
}
@Override
public final R endNot() {
return endJunction();
}
/**
* Push the expression list onto the appropriate stack.
*/
private R pushExprList(ExpressionList<T> list) {
if (textMode) {
textStack.push(list);
} else {
whereStack.push(list);
}
return root;
}
@Override
public final R where() {
textMode = false;
return root;
}
@Override
public final R text() {
textMode = true;
return root;
}
@Override
public final R multiMatch(String query, MultiMatch multiMatch) {
peekExprList().multiMatch(query, multiMatch);
return root;
}
@Override
public final R multiMatch(String query, String... properties) {
peekExprList().multiMatch(query, properties);
return root;
}
@Override
public final R textCommonTerms(String query, TextCommonTerms options) {
peekExprList().textCommonTerms(query, options);
return root;
}
@Override
public final R textSimple(String query, TextSimple options) {
peekExprList().textSimple(query, options);
return root;
}
@Override
public final R textQueryString(String query, TextQueryString options) {
peekExprList().textQueryString(query, options);
return root;
}
@Override
public final R usingTransaction(Transaction transaction) {
query.usingTransaction(transaction);
return root;
}
@Override
public final R usingConnection(Connection connection) {
query.usingConnection(connection);
return root;
}
@Override
public R usingDatabase(Database database) {
query.usingDatabase(database);
return root;
}
@Override
public final R usingMaster() {
query.usingMaster();
return root;
}
@Override
public final boolean exists() {
return query.exists();
}
@Override
@Nullable
public final T findOne() {
return query.findOne();
}
@Override
public final Optional<T> findOneOrEmpty() {
return query.findOneOrEmpty();
}
@Override
public final List<T> findList() {
return query.findList();
}
@Override
public final Stream<T> findStream() {
return query.findStream();
}
@Override
public final Set<T> findSet() {
return query.findSet();
}
@Override
public final <A> List<A> findIds() {
return query.findIds();
}
@Override
public final <K> Map<K, T> findMap() {
return query.findMap();
}
@Override
public final QueryIterator<T> findIterate() {
return query.findIterate();
}
@Override
public final <A> List<A> findSingleAttributeList() {
return query.findSingleAttributeList();
}
@Override
public final <A> Set<A> findSingleAttributeSet() {
return query.findSingleAttributeSet();
}
@Override
@Nullable
public final <A> A findSingleAttribute() {
return query.findSingleAttribute();
}
@Override
public final <A> Optional<A> findSingleAttributeOrEmpty() {
return query.findSingleAttributeOrEmpty();
}
@Override
public final void findEach(Consumer<T> consumer) {
query.findEach(consumer);
}
@Override
public final void findEach(int batch, Consumer<List<T>> consumer) {
query.findEach(batch, consumer);
}
@Override
public final void findEachWhile(Predicate<T> consumer) {
query.findEachWhile(consumer);
}
@Override
public final List<Version<T>> findVersions() {
return query.findVersions();
}
@Override
public final List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end) {
return query.findVersionsBetween(start, end);
}
@Override
public final int findCount() {
return query.findCount();
}
@Override
public final FutureRowCount<T> findFutureCount() {
return query.findFutureCount();
}
@Override
public final FutureIds<T> findFutureIds() {
return query.findFutureIds();
}
@Override
public final FutureList<T> findFutureList() {
return query.findFutureList();
}
@Override
public final PagedList<T> findPagedList() {
return query.findPagedList();
}
@Override
public final int delete() {
return query.delete();
}
@Override
public final String getGeneratedSql() {
return query.getGeneratedSql();
}
@Override
public final Class<T> getBeanType() {
return query.getBeanType();
}
@Override
public final ExpressionList<T> getExpressionList() {
return query.where();
}
@Override
public final R having() {
if (whereStack == null) {
whereStack = new ArrayStack<>();
}
// effectively putting having expression list onto stack
// such that expression now add to the having clause
whereStack.push(query.having());
return root;
}
@Override
public final ExpressionList<T> havingClause() {
return query.having();
}
/**
* Return the current expression list that expressions should be added to.
*/
protected final ExpressionList<T> peekExprList() {
if (textMode) {
// return the current text expression list
return _peekText();
}
if (whereStack == null) {
whereStack = new ArrayStack<>();
whereStack.push(query.where());
}
// return the current expression list
return whereStack.peek();
}
protected final ExpressionList<T> _peekText() {
if (textStack == null) {
textStack = new ArrayStack<>();
// empty so push on the queries base expression list
textStack.push(query.text());
}
// return the current expression list
return textStack.peek();
}
}
@@ -46,7 +46,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
* Eagerly fetch this association fetching all the properties.
*/
public final R fetch() {
((QueryBean) _root).query().fetch(_name);
((TQRootBean) _root).query().fetch(_name);
return _root;
}
@@ -54,7 +54,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
* Eagerly fetch this association using a "query join".
*/
public final R fetchQuery() {
((QueryBean) _root).query().fetchQuery(_name);
((TQRootBean) _root).query().fetchQuery(_name);
return _root;
}
@@ -63,7 +63,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
* Cache misses are populated via fetchQuery().
*/
public final R fetchCache() {
((QueryBean) _root).query().fetchCache(_name);
((TQRootBean) _root).query().fetchCache(_name);
return _root;
}
@@ -71,7 +71,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
* Use lazy loading for fetching this association.
*/
public final R fetchLazy() {
((QueryBean) _root).query().fetchLazy(_name);
((TQRootBean) _root).query().fetchLazy(_name);
return _root;
}
@@ -79,7 +79,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
* Eagerly fetch this association with the properties specified.
*/
public final R fetch(String properties) {
((QueryBean) _root).query().fetch(_name, properties);
((TQRootBean) _root).query().fetch(_name, properties);
return _root;
}
@@ -87,7 +87,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
* Eagerly fetch this association using a "query join" with the properties specified.
*/
public final R fetchQuery(String properties) {
((QueryBean) _root).query().fetchQuery(_name, properties);
((TQRootBean) _root).query().fetchQuery(_name, properties);
return _root;
}
@@ -96,7 +96,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
* Cache misses are populated via fetchQuery().
*/
public final R fetchCache(String properties) {
((QueryBean) _root).query().fetchCache(_name, properties);
((TQRootBean) _root).query().fetchCache(_name, properties);
return _root;
}
@@ -177,7 +177,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
}
private SpiQueryFetch spiQuery() {
return (SpiQueryFetch) ((QueryBean) _root).query();
return (SpiQueryFetch) ((TQRootBean) _root).query();
}
private Set<String> properties(TQProperty<?, ?>... props) {
@@ -41,7 +41,7 @@ public class TQProperty<R, T> implements Query.Property<T> {
* Internal method to return the underlying expression list.
*/
protected final ExpressionList<?> expr() {
return ((QueryBean<?, ?>) _root).peekExprList();
return ((TQRootBean<?, ?>) _root).peekExprList();
}
/**
File diff suppressed because it is too large Load Diff
@@ -68,9 +68,9 @@ class MyInnerTest {
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(4);
assertThat(sql.get(0)).contains("select /* MyInnerTest.insert_and_find:36 */ t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.one = ?;");
assertThat(sql.get(1)).contains("select /* MyInnerTest.insert_and_find:43 */ t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.description = ?;");
assertThat(sql.get(2)).contains("select /* MyInnerTest.insert_and_find:54 */ t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.id < ? and t0.description = ?;");
assertThat(sql.get(3)).contains("select /* MyInnerTest.insert_and_find:65 */ t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.id < ? and t0.one > ? and t0.one >= ? and t0.one < ? and t0.one <= ? and t0.id > ?;");
assertThat(sql.get(0)).contains("select t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.one = ?;");
assertThat(sql.get(1)).contains("select t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.description = ?;");
assertThat(sql.get(2)).contains("select t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.id < ? and t0.description = ?;");
assertThat(sql.get(3)).contains("select t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.id < ? and t0.one > ? and t0.one >= ? and t0.one < ? and t0.one <= ? and t0.id > ?;");
}
}
@@ -62,7 +62,6 @@ public class QCustomerAndOrTest {
@Test
public void testOrWithExists() {
QCustomer query = Customer.find.typed()
.setLabel("hiLabel")
.alias("_cust")
.or()
.name.eq("Superman")
@@ -73,12 +72,12 @@ public class QCustomerAndOrTest {
.query()
)
.endOr()
.select(QCustomer.Alias.id);
.select(QCustomer.alias().id);
query.findList();
assertThat(query.getGeneratedSql()).isEqualTo(
"select /* hiLabel */ _cust.id from be_customer _cust where (" +
"select _cust.id from be_customer _cust where (" +
"_cust.name = ? or exists (select 1 from be_contact contact where " +
"contact.first_name = ? and contact.customer_id = _cust.id))"
);

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