mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
71
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbf46bb0ec | ||
|
|
6aa4337b27 | ||
|
|
99d7a4e4b4 | ||
|
|
b7da2cbd97 | ||
|
|
b4ae721998 | ||
|
|
3bb4126e26 | ||
|
|
de439e7695 | ||
|
|
e5ec72df5c | ||
|
|
98a89156bf | ||
|
|
dc65475dbb | ||
|
|
cc64ff5f10 | ||
|
|
ab87174e5b | ||
|
|
a6fdc42934 | ||
|
|
4c16b85e85 | ||
|
|
9e3d6c6a08 | ||
|
|
d567872813 | ||
|
|
7069d3f600 | ||
|
|
b2f8b7c711 | ||
|
|
4092ea307e | ||
|
|
01f64ef9f9 | ||
|
|
f1a48b182e | ||
|
|
8ac5467856 | ||
|
|
758f84f573 | ||
|
|
2073fefc92 | ||
|
|
5b98d8d9c2 | ||
|
|
8e59e72983 | ||
|
|
b56d68dac7 | ||
|
|
e0c3a92b60 | ||
|
|
213562fcf1 | ||
|
|
d3a09b93c0 | ||
|
|
536ea57369 | ||
|
|
ac1dd4cbab | ||
|
|
bc33e05906 | ||
|
|
8ecc407cb0 | ||
|
|
2672dff512 | ||
|
|
74d907bbce | ||
|
|
53aacabce7 | ||
|
|
ab9cdd8f5b | ||
|
|
b3d439ed11 | ||
|
|
d57657c209 | ||
|
|
dd080c5933 | ||
|
|
0be98d7989 | ||
|
|
fca54dbbe3 | ||
|
|
7ec5fe6a85 | ||
|
|
9ea5505214 | ||
|
|
75d3b566a7 | ||
|
|
e38059f52c | ||
|
|
72331aa945 | ||
|
|
e497c54f82 | ||
|
|
b44091ff3a | ||
|
|
2ab889f889 | ||
|
|
23541e661e | ||
|
|
6778e37652 | ||
|
|
5eac31283d | ||
|
|
38016773d4 | ||
|
|
ad80eb267f | ||
|
|
449e0832e0 | ||
|
|
f28f5c9a01 | ||
|
|
9dd28d8879 | ||
|
|
b47eec99a5 | ||
|
|
99bb55efa0 | ||
|
|
4408eb618a | ||
|
|
9ccd1b1b27 | ||
|
|
0c1657fef5 | ||
|
|
ee393c6485 | ||
|
|
7f68b9d846 | ||
|
|
2035a3344e | ||
|
|
ade36582a0 | ||
|
|
a4b8be61e3 | ||
|
|
b7feb62ecd | ||
|
|
7e48f7c8eb |
+1
-1
@@ -293,6 +293,6 @@ ebean.tenant.schemaProvider
|
||||
ebean.updateAllPropertiesInBatch
|
||||
ebean.updateChangesOnly
|
||||
ebean.updatesDeleteMissingChildren
|
||||
ebean.useJavaxValidationNotNull
|
||||
ebean.useValidationNotNull
|
||||
ebean.useJtaTransactionManager
|
||||
|
||||
|
||||
+3
-16
@@ -4,18 +4,13 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
<description>ebean api</description>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.11.3</jackson-core.version>
|
||||
<jackson-databind.version>2.11.3</jackson-databind.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--
|
||||
@@ -74,7 +69,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -82,7 +77,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -102,14 +97,6 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope to read validation annotations Size etc -->
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>1.1.0.Final</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
|
||||
@@ -196,13 +196,6 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> forUpdate();
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to withLock().
|
||||
* Execute using "for update" with given lock type (currently Postgres only).
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> forUpdate(Query.LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with No Wait option.
|
||||
* <p>
|
||||
@@ -211,13 +204,6 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> forUpdateNoWait();
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to withLock().
|
||||
* Execute using "for update nowait" with given lock type (currently Postgres only).
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> forUpdateNoWait(Query.LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with Skip Locked option.
|
||||
* <p>
|
||||
@@ -226,13 +212,6 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> forUpdateSkipLocked();
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to withLock().
|
||||
* Execute using "for update skip locked" with given lock type (currently Postgres only).
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> forUpdateSkipLocked(Query.LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute the query including soft deleted rows.
|
||||
*/
|
||||
|
||||
@@ -3,30 +3,13 @@ package io.ebean;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Defines the configuration options for a "query fetch" or a
|
||||
* "lazy loading fetch". This gives you the ability to use multiple smaller
|
||||
* queries to populate an object graph as opposed to a single large query.
|
||||
* <p>
|
||||
* The primary goal is to provide efficient ways of loading complex object
|
||||
* graphs avoiding SQL Cartesian product and issues around populating object
|
||||
* graphs that have multiple *ToMany relationships.
|
||||
* </p>
|
||||
* <p>
|
||||
* It also provides the ability to control the lazy loading queries (batch size,
|
||||
* selected properties and fetches) to avoid N+1 queries etc.
|
||||
* <p>
|
||||
* There can also be cases loading across a single OneToMany where 2 SQL queries
|
||||
* using Ebean FetchConfig.query() can be more efficient than one SQL query.
|
||||
* When the "One" side is wide (lots of columns) and the cardinality difference
|
||||
* is high (a lot of "Many" beans per "One" bean) then this can be more
|
||||
* efficient loaded as 2 SQL queries.
|
||||
* </p>
|
||||
* Defines how a relationship is fetched via either normal SQL join,
|
||||
* a eager secondary query, via lazy loading or via eagerly hitting L2 cache.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
* // Normal fetch join results in a single SQL query
|
||||
* List<Order> list = DB.find(Order.class).fetch("details").findList();
|
||||
*
|
||||
* // Find Orders join details using a single SQL query
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Example: Using a "query join" instead of a "fetch join" we instead use 2 SQL queries
|
||||
@@ -37,103 +20,13 @@ import java.io.Serializable;
|
||||
* // This will use 2 SQL queries to build this object graph
|
||||
* List<Order> list =
|
||||
* DB.find(Order.class)
|
||||
* .fetch("details", new FetchConfig().query())
|
||||
* .fetch("details", FetchConfig.ofQuery())
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order
|
||||
* // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Example: Using 2 "query joins"
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // This will use 3 SQL queries to build this object graph
|
||||
* List<Order> list =
|
||||
* DB.find(Order.class)
|
||||
* .fetch("details", new FetchConfig().query())
|
||||
* .fetch("customer", new FetchConfig().queryFirst(5))
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order
|
||||
* // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
|
||||
* // query 3) find customer where id in (?,?,?,?,?) // first 5 customers
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Example: Using "query joins" and partial objects
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* <pre>{@code
|
||||
* // This will use 3 SQL queries to build this object graph
|
||||
* List<Order> list =
|
||||
* DB.find(Order.class)
|
||||
* .select("status, shipDate")
|
||||
* .fetch("details", "quantity, price", new FetchConfig().query())
|
||||
* .fetch("details.product", "sku, name")
|
||||
* .fetch("customer", "name", new FetchConfig().queryFirst(5))
|
||||
* .fetch("customer.contacts")
|
||||
* .fetch("customer.shippingAddress")
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order (status, shipDate)
|
||||
* // query 2) find orderDetail (quantity, price) fetch product (sku, name) where
|
||||
* // order.id in (?,? ...)
|
||||
* // query 3) find customer (name) fetch contacts (*) fetch shippingAddress (*)
|
||||
* // where id in (?,?,?,?,?)
|
||||
*
|
||||
* // Note: the fetch of "details.product" is automatically included into the
|
||||
* // fetch of "details"
|
||||
* //
|
||||
* // Note: the fetch of "customer.contacts" and "customer.shippingAddress"
|
||||
* // are automatically included in the fetch of "customer"
|
||||
* }</pre>
|
||||
* <p>
|
||||
* You can use query() and lazy together on a single join. The query is executed
|
||||
* immediately and the lazy defines the batch size to use for further lazy
|
||||
* loading (if lazy loading is invoked).
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> list =
|
||||
* DB.find(Order.class)
|
||||
* .fetch("customer", new FetchConfig().query(10).lazy(5))
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order
|
||||
* // query 2) find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers
|
||||
* // .. then if lazy loading of customers is invoked
|
||||
* // .. use a batch size of 5 to load the customers
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <p>
|
||||
* Example of controlling the lazy loading query:
|
||||
* </p>
|
||||
* <p>
|
||||
* This gives us the ability to optimise the lazy loading query for a given use
|
||||
* case.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> list = DB.find(Order.class)
|
||||
* .fetch("customer","name", new FetchConfig().lazy(5))
|
||||
* .fetch("customer.contacts","contactName, phone, email")
|
||||
* .fetch("customer.shippingAddress")
|
||||
* .where().eq("status",Order.Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order where status = Order.Status.NEW
|
||||
* //
|
||||
* // .. if lazy loading of customers is invoked
|
||||
* // .. use a batch size of 5 to load the customers
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @author mario
|
||||
* @author rbygrave
|
||||
@@ -142,66 +35,122 @@ public class FetchConfig implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int lazyBatchSize = -1;
|
||||
private static final int JOIN_MODE = 0;
|
||||
private static final int QUERY_MODE = 1;
|
||||
private static final int LAZY_MODE = 2;
|
||||
private static final int CACHE_MODE = 3;
|
||||
|
||||
private int queryBatchSize = -1;
|
||||
|
||||
private boolean queryAll;
|
||||
|
||||
private boolean cache;
|
||||
private int mode;
|
||||
private int batchSize;
|
||||
private int hashCode;
|
||||
|
||||
/**
|
||||
* Construct the fetch configuration object.
|
||||
*/
|
||||
public FetchConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that this path should be lazy loaded using the default batch load
|
||||
* size.
|
||||
*/
|
||||
public FetchConfig lazy() {
|
||||
this.lazyBatchSize = 0;
|
||||
this.queryAll = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that this path should be lazy loaded with a specified batch size.
|
||||
* Deprecated - migrate to one of the static factory methods like {@link FetchConfig#ofQuery()}
|
||||
*
|
||||
* @param lazyBatchSize the batch size for lazy loading
|
||||
* Construct using default JOIN mode.
|
||||
*/
|
||||
public FetchConfig lazy(int lazyBatchSize) {
|
||||
this.lazyBatchSize = lazyBatchSize;
|
||||
this.queryAll = false;
|
||||
@Deprecated
|
||||
public FetchConfig() {
|
||||
//this.mode = JOIN_MODE;
|
||||
this.batchSize = 100;
|
||||
this.hashCode = 1000;
|
||||
}
|
||||
|
||||
private FetchConfig(int mode, int batchSize) {
|
||||
this.mode = mode;
|
||||
this.batchSize = batchSize;
|
||||
this.hashCode = mode + 10 * batchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return FetchConfig to eagerly fetch the relationship using L2 cache.
|
||||
* <p>
|
||||
* Any cache misses will be loaded by secondary query to the database.
|
||||
*/
|
||||
public static FetchConfig ofCache() {
|
||||
return new FetchConfig(CACHE_MODE, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return FetchConfig to eagerly fetch the relationship using a secondary query.
|
||||
*/
|
||||
public static FetchConfig ofQuery() {
|
||||
return new FetchConfig(QUERY_MODE, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return FetchConfig to eagerly fetch the relationship using a secondary with a given batch size.
|
||||
*/
|
||||
public static FetchConfig ofQuery(int batchSize) {
|
||||
return new FetchConfig(QUERY_MODE, batchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return FetchConfig to lazily load the relationship.
|
||||
*/
|
||||
public static FetchConfig ofLazy() {
|
||||
return new FetchConfig(LAZY_MODE, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return FetchConfig to lazily load the relationship specifying the batch size.
|
||||
*/
|
||||
public static FetchConfig ofLazy(int batchSize) {
|
||||
return new FetchConfig(LAZY_MODE, batchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return FetchConfig to fetch the relationship using SQL join.
|
||||
*/
|
||||
public static FetchConfig ofDefault() {
|
||||
return new FetchConfig(JOIN_MODE, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to migrate away from mutating FetchConfig to a fully immutable FetchConfig.
|
||||
*/
|
||||
private FetchConfig mutate(int mode, int batchSize) {
|
||||
if (batchSize < 1) {
|
||||
throw new IllegalArgumentException("batch size "+batchSize+" must be > 0");
|
||||
}
|
||||
this.mode = mode;
|
||||
this.batchSize = batchSize;
|
||||
this.hashCode = mode + 10 * batchSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to FetchConfig.ofLazy().
|
||||
*/
|
||||
@Deprecated
|
||||
public FetchConfig lazy() {
|
||||
return mutate(LAZY_MODE, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to FetchConfig.ofLazy(batchSize).
|
||||
*/
|
||||
@Deprecated
|
||||
public FetchConfig lazy(int batchSize) {
|
||||
return mutate(LAZY_MODE, batchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to FetchConfig.ofQuery().
|
||||
*
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
* This will use the default batch size for separate query which is 100.
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
public FetchConfig query() {
|
||||
this.queryBatchSize = 0;
|
||||
this.queryAll = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eagerly fetch the beans fetching the beans from the L2 bean cache
|
||||
* and using the DB for beans not in the cache.
|
||||
*/
|
||||
public FetchConfig cache() {
|
||||
this.cache = true;
|
||||
this.queryBatchSize = 0;
|
||||
this.queryAll = true;
|
||||
return this;
|
||||
return mutate(QUERY_MODE, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to FetchConfig.ofQuery(batchSize).
|
||||
*
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
@@ -213,16 +162,16 @@ public class FetchConfig implements Serializable {
|
||||
* is also used.
|
||||
* </p>
|
||||
*
|
||||
* @param queryBatchSize the batch size used to load beans on this path
|
||||
* @param batchSize the batch size used to load beans on this path
|
||||
*/
|
||||
public FetchConfig query(int queryBatchSize) {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
// queryAll true as long as a lazy batch size has not already been set
|
||||
this.queryAll = (lazyBatchSize == -1);
|
||||
return this;
|
||||
@Deprecated
|
||||
public FetchConfig query(int batchSize) {
|
||||
return mutate(QUERY_MODE, batchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to FetchConfig.ofQuery(batchSize).
|
||||
*
|
||||
* Eagerly fetch the first batch of beans on this path.
|
||||
* This is similar to {@link #query(int)} but only fetches the first batch.
|
||||
* <p>
|
||||
@@ -230,61 +179,68 @@ public class FetchConfig implements Serializable {
|
||||
* loaded eagerly but instead use lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @param queryBatchSize the number of parent beans this path is populated for
|
||||
* @param batchSize the number of parent beans this path is populated for
|
||||
*/
|
||||
public FetchConfig queryFirst(int queryBatchSize) {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
this.queryAll = false;
|
||||
return this;
|
||||
@Deprecated
|
||||
public FetchConfig queryFirst(int batchSize) {
|
||||
return query(batchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch size for lazy loading.
|
||||
* Deprecated - migrate to FetchConfig.ofCache().
|
||||
*
|
||||
* Eagerly fetch the beans fetching the beans from the L2 bean cache
|
||||
* and using the DB for beans not in the cache.
|
||||
*/
|
||||
public int getLazyBatchSize() {
|
||||
return lazyBatchSize;
|
||||
@Deprecated
|
||||
public FetchConfig cache() {
|
||||
return mutate(CACHE_MODE, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch size for separate query load.
|
||||
* Return the batch size for fetching.
|
||||
*/
|
||||
public int getQueryBatchSize() {
|
||||
return queryBatchSize;
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query fetch should fetch 'all' rather than just the
|
||||
* 'first' batch.
|
||||
*/
|
||||
public boolean isQueryAll() {
|
||||
return queryAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this uses L2 bean cache.
|
||||
* Return true if the fetch should use the L2 cache.
|
||||
*/
|
||||
public boolean isCache() {
|
||||
return cache;
|
||||
return mode == CACHE_MODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the fetch should be a eager secondary query.
|
||||
*/
|
||||
public boolean isQuery() {
|
||||
return mode == QUERY_MODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the fetch should be a lazy query.
|
||||
*/
|
||||
public boolean isLazy() {
|
||||
return mode == LAZY_MODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the fetch should try to use SQL join.
|
||||
*/
|
||||
public boolean isJoin() {
|
||||
return mode == JOIN_MODE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FetchConfig that = (FetchConfig) o;
|
||||
if (lazyBatchSize != that.lazyBatchSize) return false;
|
||||
if (queryBatchSize != that.queryBatchSize) return false;
|
||||
if (cache != that.cache) return false;
|
||||
return queryAll == that.queryAll;
|
||||
return (hashCode == ((FetchConfig) o).hashCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = lazyBatchSize;
|
||||
result = 92821 * result + queryBatchSize;
|
||||
result = 92821 * result + (queryAll ? 1 : 0);
|
||||
result = 92821 * result + (cache ? 1 : 0);
|
||||
return result;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,7 +514,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, fetchProperties, new FetchConfig().query())
|
||||
* fetch(path, fetchProperties, FetchConfig.ofQuery())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -548,7 +548,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, fetchProperties, new FetchConfig().lazy())
|
||||
* fetch(path, fetchProperties, FetchConfig.ofLazy())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -573,7 +573,7 @@ public interface Query<T> {
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName,lastName,email", new FetchConfig().lazy(10))
|
||||
* .fetch("contacts", "firstName,lastName,email", FetchConfig.ofLazy(10))
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
@@ -610,7 +610,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, new FetchConfig().query())
|
||||
* fetch(path, FetchConfig.ofQuery())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -639,7 +639,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, new FetchConfig().lazy())
|
||||
* fetch(path, FetchConfig.ofLazy())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -662,7 +662,7 @@ public interface Query<T> {
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* // lazy fetch contacts with a batch size of 100
|
||||
* .fetch("contacts", new FetchConfig().lazy(100))
|
||||
* .fetch("contacts", FetchConfig.ofLazy(100))
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
@@ -844,7 +844,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* DB.find(Customer.class)
|
||||
* .fetch("contacts", new FetchConfig().query(2))
|
||||
* .fetch("contacts", FetchConfig.ofQuery(2))
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .setMaxRows(2000)
|
||||
@@ -1673,12 +1673,6 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> forUpdate();
|
||||
|
||||
/**
|
||||
* Execute using "for update" with given lock type (currently Postgres only).
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> forUpdate(LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with "no wait" option.
|
||||
* <p>
|
||||
@@ -1688,12 +1682,6 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> forUpdateNoWait();
|
||||
|
||||
/**
|
||||
* Execute using "for update nowait" with given lock type (currently Postgres only).
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> forUpdateNoWait(LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with "skip locked" option.
|
||||
* <p>
|
||||
@@ -1703,12 +1691,6 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> forUpdateSkipLocked();
|
||||
|
||||
/**
|
||||
* Execute using "for update skip locked" with given lock type (currently Postgres only).
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> forUpdateSkipLocked(LockType lockType);
|
||||
|
||||
/**
|
||||
* Return true if this query has forUpdate set.
|
||||
*/
|
||||
|
||||
@@ -107,7 +107,7 @@ package io.ebean;
|
||||
*
|
||||
* List<OrderAggregate> orders = DB.find(OrderAggregate.class)
|
||||
* .setRawSql(rawSql)
|
||||
* .fetch("order", "status,orderDate", new FetchConfig().query())
|
||||
* .fetch("order", "status,orderDate", FetchConfig.ofQuery())
|
||||
* .fetch("order.customer", "name")
|
||||
* .where().gt("order.id", 0)
|
||||
* .having().gt("totalAmount", 20)
|
||||
|
||||
@@ -931,10 +931,14 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* OneToMany and ManyToMany only set loaded state.
|
||||
*/
|
||||
public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
|
||||
if (readOnly) {
|
||||
throw new IllegalStateException("This bean is readOnly");
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else {
|
||||
if (readOnly) {
|
||||
throw new IllegalStateException("This bean is readOnly");
|
||||
}
|
||||
setChangedProperty(propertyIndex);
|
||||
}
|
||||
setLoadedProperty(propertyIndex);
|
||||
}
|
||||
|
||||
private void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
|
||||
|
||||
@@ -40,6 +40,13 @@ public class ClassLoadConfig {
|
||||
return isPresent("javax.validation.constraints.NotNull");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if jakarta validation annotations like Size and NotNull are present.
|
||||
*/
|
||||
public boolean isJakartaValidationAnnotationsPresent() {
|
||||
return isPresent("jakarta.validation.constraints.NotNull");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if javax PostConstruct annotation is present (maybe not in java9).
|
||||
* If not we don't support PostConstruct lifecycle events.
|
||||
|
||||
@@ -489,7 +489,7 @@ public class DatabaseConfig {
|
||||
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
|
||||
* If set to false, use io.ebean.annotation.NotNull or Column(nullable=true).
|
||||
*/
|
||||
private boolean useJavaxValidationNotNull = true;
|
||||
private boolean useValidationNotNull = true;
|
||||
|
||||
/**
|
||||
* Generally we want to perform L2 cache notification in the background and not impact
|
||||
@@ -2809,7 +2809,7 @@ public class DatabaseConfig {
|
||||
enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
|
||||
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
|
||||
useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
|
||||
useValidationNotNull = p.getBoolean("useValidationNotNull", useValidationNotNull);
|
||||
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
|
||||
idGeneratorAutomatic = p.getBoolean("idGeneratorAutomatic", idGeneratorAutomatic);
|
||||
|
||||
@@ -3056,20 +3056,21 @@ public class DatabaseConfig {
|
||||
/**
|
||||
* Returns if we use javax.validation.constraints.NotNull
|
||||
*/
|
||||
public boolean isUseJavaxValidationNotNull() {
|
||||
return useJavaxValidationNotNull;
|
||||
public boolean isUseValidationNotNull() {
|
||||
return useValidationNotNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls if Ebean should ignore <code>&x64;javax.validation.contstraints.NotNull</code>
|
||||
* Controls if Ebean should ignore <code>&x64;javax.validation.contstraints.NotNull</code> or
|
||||
* <code>&x64;jakarta.validation.contstraints.NotNull</code>
|
||||
* with respect to generating a <code>NOT NULL</code> column.
|
||||
* <p>
|
||||
* Normally when Ebean sees javax NotNull annotation it means that column is defined as NOT NULL.
|
||||
* Set this to <code>false</code> and the javax NotNull annotation is effectively ignored (and
|
||||
* we instead use Ebean's own NotNull annotation or JPA Column(nullable=false) annotation.
|
||||
*/
|
||||
public void setUseJavaxValidationNotNull(boolean useJavaxValidationNotNull) {
|
||||
this.useJavaxValidationNotNull = useJavaxValidationNotNull;
|
||||
public void setUseValidationNotNull(boolean useValidationNotNull) {
|
||||
this.useValidationNotNull = useValidationNotNull;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</parent>
|
||||
<!-- <parent>-->
|
||||
<!-- <groupId>org.avaje</groupId>-->
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.6.5</tag>
|
||||
<tag>ebean-parent-12.7.1</tag>
|
||||
</scm>
|
||||
|
||||
<name>ebean autotune</name>
|
||||
@@ -26,7 +26,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<plugin>
|
||||
<groupId>io.repaint.maven</groupId>
|
||||
<artifactId>tiles-maven-plugin</artifactId>
|
||||
<version>2.18</version>
|
||||
<version>2.19</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<tiles>
|
||||
|
||||
+6
-9
@@ -11,12 +11,9 @@ import org.tests.model.basic.Order;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
//import org.tests.model.basic.ResetBasicData;
|
||||
|
||||
public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
|
||||
private BeanDescriptor<Order> desc = getBeanDescriptor(Order.class);
|
||||
private final BeanDescriptor<Order> desc = getBeanDescriptor(Order.class);
|
||||
|
||||
@Test
|
||||
public void buildDetail() {
|
||||
@@ -30,7 +27,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString().trim()).isEqualTo("fetch customer (name)");
|
||||
assertThat(detail.asStringDebug().trim()).isEqualTo("fetch customer (name)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -49,7 +46,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString()).isEqualTo("select (orderDate) fetch customer (name)");
|
||||
assertThat(detail.asStringDebug()).isEqualTo("select (orderDate) fetch customer (name)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -67,7 +64,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString().trim()).isEqualTo("select (orderDate,customer)");
|
||||
assertThat(detail.asStringDebug().trim()).isEqualTo("select (orderDate,customer)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,7 +87,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
|
||||
assertThat(detail.asString()).isEqualTo("select (orderDate) fetch customer (billingAddress)");
|
||||
assertThat(detail.asStringDebug()).isEqualTo("select (orderDate) fetch customer (billingAddress)");
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +116,7 @@ public class ProfileOriginTest extends BaseTestCase {
|
||||
po.collectUsageInfo(c);
|
||||
|
||||
OrmQueryDetail detail = po.buildDetail(desc);
|
||||
assertThat(detail.asString()).isEqualTo("fetch customer (name,note) fetch customer.billingAddress (line1)");
|
||||
assertThat(detail.asStringDebug()).isEqualTo("fetch customer (name,note) fetch customer.billingAddress (line1)");
|
||||
}
|
||||
|
||||
private NodeUsageCollector node(String path) {
|
||||
|
||||
+17
-17
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean bom</name>
|
||||
@@ -18,8 +18,8 @@
|
||||
<ebean-migration.version>12.4.0</ebean-migration.version>
|
||||
<ebean-test-docker.version>4.1</ebean-test-docker.version>
|
||||
<ebean-datasource.version>7.0</ebean-datasource.version>
|
||||
<ebean-agent.version>12.6.2</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.6.2</ebean-maven-plugin.version>
|
||||
<ebean-agent.version>12.6.6</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.6.6</ebean-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -81,88 +81,88 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-xml</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-autotune</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,30 +4,25 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<name>ebean core type</name>
|
||||
<description>ebean scalar types api</description>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.11.3</jackson-core.version>
|
||||
<jackson-databind.version>2.11.3</jackson-databind.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
+14
-13
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core</artifactId>
|
||||
@@ -15,14 +15,9 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.6.5</tag>
|
||||
<tag>ebean-parent-12.7.1</tag>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.11.3</jackson-core.version>
|
||||
<jackson-databind.version>2.11.3</jackson-databind.version>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<!-- Note: to use this profile, you need to download manually the db2jcc4 driver.
|
||||
@@ -92,19 +87,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.6.5</version>
|
||||
<version>12.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -138,6 +133,12 @@
|
||||
<version>1.1.0.Final</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
@@ -157,14 +158,14 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -301,7 +302,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>12.5.0</version>
|
||||
<version>12.6.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -20,6 +20,7 @@ import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.query.CancelableQuery;
|
||||
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
import io.ebeaninternal.server.querydefn.OrmUpdateProperties;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
|
||||
@@ -30,7 +31,7 @@ import java.util.Set;
|
||||
/**
|
||||
* Object Relational query - Internal extension to Query object.
|
||||
*/
|
||||
public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCodes {
|
||||
|
||||
enum Mode {
|
||||
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
|
||||
@@ -289,6 +290,16 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
boolean selectAllForLazyLoadProperty();
|
||||
|
||||
/**
|
||||
* Set the select properties.
|
||||
*/
|
||||
void selectProperties(OrmQueryProperties other);
|
||||
|
||||
/**
|
||||
* Set the fetch properties for the given path.
|
||||
*/
|
||||
void fetchProperties(String path, OrmQueryProperties other);
|
||||
|
||||
/**
|
||||
* Set the on a secondary query given the label, relativePath and profile location of the parent query.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.FetchConfig;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Query select and fetch properties (that avoids parsing).
|
||||
*/
|
||||
public interface SpiQueryFetch {
|
||||
|
||||
/**
|
||||
* Specify the select properties.
|
||||
*/
|
||||
void selectProperties(Set<String> properties);
|
||||
|
||||
/**
|
||||
* Specify the fetch properties for the given path.
|
||||
*/
|
||||
void fetchProperties(String name, Set<String> properties, FetchConfig config);
|
||||
|
||||
}
|
||||
@@ -131,19 +131,10 @@ public class DatabasePlatformFactory {
|
||||
* Use JDBC DatabaseMetaData to determine the platform.
|
||||
*/
|
||||
private DatabasePlatform byDataSource(DataSource dataSource) {
|
||||
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = dataSource.getConnection();
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
|
||||
return byDatabaseMeta(metaData, connection);
|
||||
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
return byDatabaseMeta(connection.getMetaData(), connection);
|
||||
} catch (SQLException ex) {
|
||||
throw new PersistenceException(ex);
|
||||
|
||||
} finally {
|
||||
JdbcClose.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +148,7 @@ public class DatabasePlatformFactory {
|
||||
final int minorVersion = metaData.getDatabaseMinorVersion();
|
||||
|
||||
if (dbProductName.contains("oracle")) {
|
||||
return new OraclePlatform();
|
||||
return oracleVersion(majorVersion);
|
||||
} else if (dbProductName.contains("microsoft")) {
|
||||
throw new IllegalArgumentException("For SqlServer please explicitly choose either sqlserver16 or sqlserver17 as the platform via DatabaseConfig.setDatabasePlatformName. Refer to issue #1340 for more details");
|
||||
} else if (dbProductName.contains("h2")) {
|
||||
@@ -188,6 +179,10 @@ public class DatabasePlatformFactory {
|
||||
return new DatabasePlatform();
|
||||
}
|
||||
|
||||
private DatabasePlatform oracleVersion(int majorVersion) {
|
||||
return majorVersion < 12 ? new Oracle11Platform() : new OraclePlatform();
|
||||
}
|
||||
|
||||
private DatabasePlatform mysqlVersion(int majorVersion, int minorVersion) {
|
||||
if (majorVersion <= 5 && minorVersion <= 5) {
|
||||
return new MySql55Platform();
|
||||
|
||||
@@ -215,7 +215,6 @@ class DefaultBeanLoader {
|
||||
desc.contextPut(pc, id, bean);
|
||||
ebi.setPersistenceContext(pc);
|
||||
}
|
||||
|
||||
boolean draft = desc.isDraftInstance(bean);
|
||||
|
||||
if (embeddedOwnerIndex == -1) {
|
||||
@@ -245,7 +244,6 @@ class DefaultBeanLoader {
|
||||
// and put the data into the original bean
|
||||
query.setUsageProfiling(false);
|
||||
query.setPersistenceContext(pc);
|
||||
|
||||
query.setMode(mode);
|
||||
query.setId(id);
|
||||
|
||||
|
||||
@@ -1872,6 +1872,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return query.setId(id).findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference with a check for the bean in the persistence context.
|
||||
*/
|
||||
public EntityBean createReference(Boolean readOnly, Object id, PersistenceContext pc) {
|
||||
Object refBean = contextGet(pc, id);
|
||||
if (refBean == null) {
|
||||
refBean = createReference(readOnly, false, id, pc);
|
||||
}
|
||||
return (EntityBean)refBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference bean based on the id.
|
||||
*/
|
||||
|
||||
+1
-42
@@ -89,7 +89,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
BeanDescriptorCacheHelp(BeanDescriptor<T> desc, SpiCacheManager cacheManager, CacheOptions cacheOptions,
|
||||
boolean cacheSharableBeans, BeanPropertyAssocOne<?>[] propertiesOneImported) {
|
||||
|
||||
this.desc = desc;
|
||||
this.beanType = desc.rootBeanType;
|
||||
this.cacheName = beanType.getSimpleName();
|
||||
@@ -99,7 +98,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
this.cacheSharableBeans = cacheSharableBeans;
|
||||
this.propertiesOneImported = propertiesOneImported;
|
||||
this.naturalKey = cacheOptions.getNaturalKey();
|
||||
|
||||
if (!cacheOptions.isEnableQueryCache()) {
|
||||
this.queryCache = null;
|
||||
} else {
|
||||
@@ -133,7 +131,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
void deriveNotifyFlags() {
|
||||
cacheNotifyOnAll = (invalidateQueryCache || beanCache != null || queryCache != null);
|
||||
cacheNotifyOnDelete = !cacheNotifyOnAll && isNotifyOnDeletes();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (cacheNotifyOnAll || cacheNotifyOnDelete) {
|
||||
String notifyMode = cacheNotifyOnAll ? "All" : "Delete";
|
||||
@@ -292,7 +289,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Try to load the bean collection from cache return true if successful.
|
||||
*/
|
||||
boolean manyPropLoad(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, Object parentId, Boolean readOnly) {
|
||||
|
||||
if (many.isElementCollection()) {
|
||||
// held as part of the bean cache so skip
|
||||
return false;
|
||||
@@ -307,14 +303,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
EntityBean ownerBean = bc.getOwnerBean();
|
||||
EntityBeanIntercept ebi = ownerBean._ebean_getIntercept();
|
||||
PersistenceContext persistenceContext = ebi.getPersistenceContext();
|
||||
|
||||
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
|
||||
|
||||
List<Object> idList = entry.getIdList();
|
||||
bc.checkEmptyLazyLoad();
|
||||
for (Object id : idList) {
|
||||
Object refBean = targetDescriptor.createReference(readOnly, false, id, persistenceContext);
|
||||
many.add(bc, (EntityBean) refBean);
|
||||
many.add(bc, targetDescriptor.createReference(readOnly, id, persistenceContext));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -323,7 +317,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Put the beanCollection into the cache.
|
||||
*/
|
||||
void manyPropPut(BeanPropertyAssocMany<?> many, Object details, Object parentId) {
|
||||
|
||||
if (many.isElementCollection()) {
|
||||
CachedBeanData data = (CachedBeanData) beanCache.get(parentId);
|
||||
if (data != null) {
|
||||
@@ -351,7 +344,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
void cachePutManyIds(Object parentId, String manyName, CachedManyIds entry) {
|
||||
|
||||
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, manyName);
|
||||
if (manyLog.isDebugEnabled()) {
|
||||
manyLog.debug(" PUT {}({}).{} - ids:{}", cacheName, parentId, manyName, entry);
|
||||
@@ -360,7 +352,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
private CachedManyIds createManyIds(BeanPropertyAssocMany<?> many, Object details) {
|
||||
|
||||
Collection<?> actualDetails = BeanCollectionUtil.getActualDetails(details);
|
||||
if (actualDetails == null) {
|
||||
return null;
|
||||
@@ -378,7 +369,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Hit the bean cache with the given ids returning the hits.
|
||||
*/
|
||||
BeanCacheResult<T> cacheIdLookup(PersistenceContext context, Collection<?> ids) {
|
||||
|
||||
Set<Object> keys = new HashSet<>(ids.size());
|
||||
for (Object id : ids) {
|
||||
keys.add(desc.cacheKey(id));
|
||||
@@ -395,7 +385,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
T bean = convertToBean(entry.getKey(), false, context, cachedBeanData);
|
||||
result.add(bean, desc.getBeanId(bean));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -403,14 +392,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Use natural keys to hit the bean cache and return resulting hits.
|
||||
*/
|
||||
BeanCacheResult<T> naturalKeyLookup(PersistenceContext context, Set<Object> keys) {
|
||||
|
||||
if (context == null) {
|
||||
context = new DefaultPersistenceContext();
|
||||
}
|
||||
|
||||
// naturalKey -> Id map
|
||||
Map<Object, Object> naturalKeyMap = naturalKeyCache.getAll(keys);
|
||||
|
||||
if (natLog.isTraceEnabled()) {
|
||||
natLog.trace(" MLOOKUP {}({}) - hits:{}", cacheName, keys, naturalKeyMap);
|
||||
}
|
||||
@@ -433,15 +420,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
// process the hits into beans etc
|
||||
for (Map.Entry<Object, Object> entry : beanDataMap.entrySet()) {
|
||||
|
||||
Object id = entry.getKey();
|
||||
CachedBeanData cachedBeanData = (CachedBeanData) entry.getValue();
|
||||
|
||||
T bean = convertToBean(id, false, context, cachedBeanData);
|
||||
Object naturalKey = reverseMap.get(id);
|
||||
result.add(bean, naturalKey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -452,7 +436,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
if (context == null) {
|
||||
context = new DefaultPersistenceContext();
|
||||
}
|
||||
|
||||
// Not using a loadContext for beans coming out of L2 cache
|
||||
// so that means no batch lazy loading for these beans
|
||||
EntityBean entityBean = (EntityBean) bean;
|
||||
@@ -522,7 +505,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Put a bean into the bean cache.
|
||||
*/
|
||||
void beanCachePut(EntityBean bean) {
|
||||
|
||||
if (desc.inheritInfo != null) {
|
||||
desc.descOf(bean.getClass()).cacheBeanPutDirect(bean);
|
||||
} else {
|
||||
@@ -531,7 +513,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
void beanCachePutAllDirect(Collection<EntityBean> beans) {
|
||||
|
||||
Map<Object, Object> natKeys = null;
|
||||
if (naturalKey != null) {
|
||||
natKeys = new LinkedHashMap<>();
|
||||
@@ -566,15 +547,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Put the bean into the bean cache.
|
||||
*/
|
||||
void beanCachePutDirect(EntityBean bean) {
|
||||
|
||||
CachedBeanData beanData = beanExtractData(desc, bean);
|
||||
|
||||
String key = desc.cacheKeyForBean(bean);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" PUT {}({}) data:{}", cacheName, key, beanData);
|
||||
}
|
||||
getBeanCache().put(key, beanData);
|
||||
|
||||
if (naturalKey != null) {
|
||||
String naturalKey = calculateNaturalKey(beanData);
|
||||
if (naturalKey != null) {
|
||||
@@ -618,7 +596,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Return a bean from the bean cache.
|
||||
*/
|
||||
private T beanCacheGetInternal(String key, Boolean readOnly, PersistenceContext context) {
|
||||
|
||||
CachedBeanData data = (CachedBeanData) getBeanCache().get(key);
|
||||
if (data == null) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
@@ -646,7 +623,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
return (T) bean;
|
||||
}
|
||||
}
|
||||
|
||||
return (T) loadBean(id, readOnly, data, context);
|
||||
}
|
||||
|
||||
@@ -654,7 +630,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Load the entity bean taking into account inheritance.
|
||||
*/
|
||||
private EntityBean loadBean(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
|
||||
|
||||
String discValue = data.getDiscValue();
|
||||
if (discValue == null) {
|
||||
return loadBeanDirect(id, readOnly, data, context);
|
||||
@@ -674,7 +649,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Load the entity bean from cache data given this is the root bean type.
|
||||
*/
|
||||
EntityBean loadBeanDirect(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
|
||||
|
||||
id = desc.convertId(id);
|
||||
EntityBean bean = null;
|
||||
if (context == null) {
|
||||
@@ -682,7 +656,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
} else {
|
||||
bean = (EntityBean) desc.contextGet(context, id);
|
||||
}
|
||||
|
||||
if (bean == null) {
|
||||
bean = desc.createEntityBean();
|
||||
desc.setId(id, bean);
|
||||
@@ -698,7 +671,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
CachedBeanDataToBean.load(desc, bean, data, context);
|
||||
|
||||
if (desc.isReadAuditing()) {
|
||||
desc.readAuditBean("l2", "", bean);
|
||||
}
|
||||
@@ -709,7 +681,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Load the embedded bean checking for inheritance.
|
||||
*/
|
||||
EntityBean embeddedBeanLoad(CachedBeanData data, PersistenceContext context) {
|
||||
|
||||
String discValue = data.getDiscValue();
|
||||
if (discValue == null) {
|
||||
return embeddedBeanLoadDirect(data, context);
|
||||
@@ -746,25 +717,20 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Load a batch of entities from L2 bean cache checking the lazy loaded property is loaded.
|
||||
*/
|
||||
Set<EntityBeanIntercept> beanCacheLoadAll(List<EntityBeanIntercept> list, PersistenceContext context, int lazyLoadProperty, String propertyName) {
|
||||
|
||||
Map<Object, EntityBeanIntercept> ebis = new HashMap<>();
|
||||
for (EntityBeanIntercept ebi : list) {
|
||||
ebis.put(desc.cacheKeyForBean(ebi.getOwner()), ebi);
|
||||
}
|
||||
|
||||
|
||||
Map<Object, Object> hits = getBeanCache().getAll(ebis.keySet());
|
||||
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" MLOAD {}({}) - got hits ({})", cacheName, ebis.keySet(), hits.size());
|
||||
}
|
||||
|
||||
Set<EntityBeanIntercept> loaded = new HashSet<>();
|
||||
|
||||
Iterator<Map.Entry<Object, Object>> iterator = hits.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<Object, Object> hit = iterator.next();
|
||||
|
||||
Object key = hit.getKey();
|
||||
EntityBeanIntercept ebi = ebis.remove(key);
|
||||
CachedBeanData cacheData = (CachedBeanData) hit.getValue();
|
||||
@@ -774,7 +740,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
beanLog.trace(" load {}({}) - cache miss on property({})", cacheName, key, propertyName);
|
||||
}
|
||||
iterator.remove();
|
||||
|
||||
} else {
|
||||
CachedBeanDataToBean.load(desc, ebi.getOwner(), cacheData, context);
|
||||
loaded.add(ebi);
|
||||
@@ -783,11 +748,9 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ebis.isEmpty() && beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" load {}({}) - cache miss", cacheName, ebis.keySet());
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
@@ -795,7 +758,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Returns true if it managed to populate/load the single bean from the cache.
|
||||
*/
|
||||
boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, String key, PersistenceContext context) {
|
||||
|
||||
CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(key);
|
||||
if (cacheData == null) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
@@ -810,7 +772,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CachedBeanDataToBean.load(desc, bean, cacheData, context);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" LOAD {}({}) - hit", cacheName, key);
|
||||
@@ -932,7 +893,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Apply changes to the bean cache entry.
|
||||
*/
|
||||
void cacheBeanUpdate(String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
|
||||
ServerCache cache = getBeanCache();
|
||||
CachedBeanData existingData = (CachedBeanData) cache.get(key);
|
||||
if (existingData != null) {
|
||||
@@ -952,7 +912,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
cache.put(key, newData);
|
||||
}
|
||||
|
||||
if (updateNaturalKey) {
|
||||
Object oldKey = calculateNaturalKey(existingData);
|
||||
if (oldKey != null) {
|
||||
|
||||
@@ -262,10 +262,16 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
/**
|
||||
* Return true if the target side has soft delete.
|
||||
*/
|
||||
@Override
|
||||
public boolean isTargetSoftDelete() {
|
||||
return targetDescriptor.isSoftDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSoftDeletePredicate(String tableAlias) {
|
||||
return targetDescriptor.getSoftDeletePredicate(tableAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if REFRESH should cascade.
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,6 @@ import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Type;
|
||||
@@ -1140,15 +1139,6 @@ public class DeployBeanProperty {
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Size> getMetaAnnotationSize() {
|
||||
final List<Size> size = getMetaAnnotations(Size.class);
|
||||
final List<Size.List> lists = getMetaAnnotations(Size.List.class);
|
||||
for (Size.List list : lists) {
|
||||
Collections.addAll(size, list.value());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public Formula getMetaAnnotationFormula(Platform platform) {
|
||||
Formula fallback = null;
|
||||
for (Annotation ann : metaAnnotations) {
|
||||
|
||||
+5
@@ -176,4 +176,9 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
public void setFetchPreference(int fetchPreference) {
|
||||
this.fetchPreference = fetchPreference;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setTargetType(Class<?> targetType) {
|
||||
this.targetType = (Class<T>)targetType;
|
||||
}
|
||||
}
|
||||
|
||||
-13
@@ -65,19 +65,6 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
this.manyType = manyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* When generics is not used for manyType you can specify via annotations.
|
||||
* <p>
|
||||
* Really only expect this for Scala due to a Scala compiler bug at the moment.
|
||||
* Otherwise I'd probably not bother support this.
|
||||
* </p>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setTargetType(Class<?> cls) {
|
||||
this.targetType = (Class<T>) cls;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the many type.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.config.BeanNotRegisteredException;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
|
||||
abstract class AnnotationAssoc extends AnnotationParser {
|
||||
|
||||
final BeanDescriptorManager factory;
|
||||
|
||||
AnnotationAssoc(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
|
||||
super(info, readConfig);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
void setTargetType(Class<?> targetType, DeployBeanPropertyAssoc<?> prop) {
|
||||
if (!targetType.equals(void.class)) {
|
||||
prop.setTargetType(targetType);
|
||||
}
|
||||
}
|
||||
|
||||
void setBeanTable(DeployBeanPropertyAssoc<?> prop) {
|
||||
BeanTable assoc = getBeanTable(prop);
|
||||
if (assoc == null) {
|
||||
throw new BeanNotRegisteredException(errorMsgMissingBeanTable(prop.getTargetType(), prop.getFullBeanName()));
|
||||
}
|
||||
prop.setBeanTable(assoc);
|
||||
}
|
||||
|
||||
BeanTable getBeanTable(DeployBeanPropertyAssoc<?> prop) {
|
||||
return factory.getBeanTable(prop.getTargetType());
|
||||
}
|
||||
|
||||
private String errorMsgMissingBeanTable(Class<?> type, String from) {
|
||||
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered? See https://ebean.io/docs/trouble-shooting#not-registered";
|
||||
}
|
||||
|
||||
}
|
||||
+10
-32
@@ -5,7 +5,6 @@ import io.ebean.annotation.FetchPreference;
|
||||
import io.ebean.annotation.HistoryExclude;
|
||||
import io.ebean.annotation.Where;
|
||||
import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.config.BeanNotRegisteredException;
|
||||
import io.ebean.config.NamingConvention;
|
||||
import io.ebean.config.TableName;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
@@ -44,16 +43,10 @@ import static io.ebean.util.StringHelper.isNull;
|
||||
/**
|
||||
* Read the deployment annotation for Assoc Many beans.
|
||||
*/
|
||||
class AnnotationAssocManys extends AnnotationParser {
|
||||
class AnnotationAssocManys extends AnnotationAssoc {
|
||||
|
||||
private final BeanDescriptorManager factory;
|
||||
|
||||
/**
|
||||
* Create with the DeployInfo.
|
||||
*/
|
||||
AnnotationAssocManys(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
|
||||
super(info, readConfig);
|
||||
this.factory = factory;
|
||||
super(info, readConfig, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,15 +139,15 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
JoinTable joinTable = get(prop, JoinTable.class);
|
||||
if (joinTable != null) {
|
||||
if (prop.isManyToMany()) {
|
||||
// expected this
|
||||
readJoinTable(joinTable, prop);
|
||||
|
||||
} else {
|
||||
// OneToMany with @JoinTable
|
||||
prop.setO2mJoinTable();
|
||||
readJoinTable(joinTable, prop);
|
||||
manyToManyDefaultJoins(prop);
|
||||
}
|
||||
} else if (prop.isManyToMany()) {
|
||||
checkSelfManyToMany(prop);
|
||||
}
|
||||
|
||||
if (prop.getMappedBy() != null) {
|
||||
@@ -187,6 +180,12 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSelfManyToMany(DeployBeanPropertyAssocMany<?> prop) {
|
||||
if (prop.getTargetType().equals(descriptor.getBeanType())) {
|
||||
throw new IllegalStateException("@ManyToMany mapping for " + prop.getFullBeanName() + " requires explicit @JoinTable with joinColumns & inverseJoinColumns. Refer issue #2157");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void readElementCollection(DeployBeanPropertyAssocMany<?> prop, ElementCollection elementCollection) {
|
||||
|
||||
@@ -435,13 +434,7 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
prop.setInverseJoin(inverseDest);
|
||||
}
|
||||
|
||||
|
||||
private String errorMsgMissingBeanTable(Class<?> type, String from) {
|
||||
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered? See https://ebean.io/docs/trouble-shooting#not-registered";
|
||||
}
|
||||
|
||||
private void readToMany(ManyToMany propAnn, DeployBeanPropertyAssocMany<?> manyProp) {
|
||||
|
||||
manyProp.setMappedBy(propAnn.mappedBy());
|
||||
manyProp.setFetchType(propAnn.fetch());
|
||||
setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo());
|
||||
@@ -453,7 +446,6 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
}
|
||||
|
||||
private void readToOne(OneToMany propAnn, DeployBeanPropertyAssocMany<?> manyProp) {
|
||||
|
||||
manyProp.setMappedBy(propAnn.mappedBy());
|
||||
manyProp.setFetchType(propAnn.fetch());
|
||||
setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo());
|
||||
@@ -462,20 +454,6 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
manyProp.getTableJoin().setType(SqlJoinType.OUTER);
|
||||
}
|
||||
|
||||
private void setTargetType(Class<?> targetType, DeployBeanPropertyAssocMany<?> prop) {
|
||||
if (!targetType.equals(void.class)) {
|
||||
prop.setTargetType(targetType);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBeanTable(DeployBeanPropertyAssocMany<?> manyProp) {
|
||||
BeanTable assoc = factory.getBeanTable(manyProp.getTargetType());
|
||||
if (assoc == null) {
|
||||
throw new BeanNotRegisteredException(errorMsgMissingBeanTable(manyProp.getTargetType(), manyProp.getFullBeanName()));
|
||||
}
|
||||
manyProp.setBeanTable(assoc);
|
||||
}
|
||||
|
||||
private String getM2MJoinTableName(BeanTable lhsTable, BeanTable rhsTable) {
|
||||
|
||||
TableName lhs = new TableName(lhsTable.getBaseTable());
|
||||
|
||||
+12
-34
@@ -4,13 +4,11 @@ import io.ebean.annotation.DbForeignKey;
|
||||
import io.ebean.annotation.FetchPreference;
|
||||
import io.ebean.annotation.TenantId;
|
||||
import io.ebean.annotation.Where;
|
||||
import io.ebean.config.BeanNotRegisteredException;
|
||||
import io.ebean.config.NamingConvention;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
@@ -28,23 +26,19 @@ import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.PrimaryKeyJoinColumn;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* Read the deployment annotations for Associated One beans.
|
||||
*/
|
||||
public class AnnotationAssocOnes extends AnnotationParser {
|
||||
public class AnnotationAssocOnes extends AnnotationAssoc {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AnnotationAssocOnes.class);
|
||||
|
||||
private final BeanDescriptorManager factory;
|
||||
|
||||
/**
|
||||
* Create with the deploy Info.
|
||||
*/
|
||||
AnnotationAssocOnes(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
|
||||
super(info, readConfig);
|
||||
this.factory = factory;
|
||||
super(info, readConfig, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,13 +114,10 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
if (nonNull != null) {
|
||||
prop.setNullable(false);
|
||||
}
|
||||
if (validationAnnotations) {
|
||||
NotNull notNull = get(prop, NotNull.class);
|
||||
if (notNull != null && isEbeanValidationGroups(notNull.groups())) {
|
||||
prop.setNullable(false);
|
||||
// overrides optional attribute of ManyToOne etc
|
||||
prop.getTableJoin().setType(SqlJoinType.INNER);
|
||||
}
|
||||
if (readConfig.isValidationNotNull(prop)) {
|
||||
// overrides optional attribute of ManyToOne etc
|
||||
prop.setNullable(false);
|
||||
prop.getTableJoin().setType(SqlJoinType.INNER);
|
||||
}
|
||||
|
||||
// check for manually defined joins
|
||||
@@ -191,25 +182,11 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
private String errorMsgMissingBeanTable(Class<?> type, String from) {
|
||||
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered? Does it have the @Entity annotation? See https://ebean.io/docs/trouble-shooting#not-registered";
|
||||
}
|
||||
|
||||
private BeanTable beanTable(DeployBeanPropertyAssoc<?> prop) {
|
||||
BeanTable assoc = factory.getBeanTable(prop.getPropertyType());
|
||||
if (assoc == null) {
|
||||
throw new BeanNotRegisteredException(errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName()));
|
||||
}
|
||||
return assoc;
|
||||
}
|
||||
|
||||
private void readManyToOne(ManyToOne propAnn, DeployBeanProperty prop) {
|
||||
|
||||
DeployBeanPropertyAssocOne<?> beanProp = (DeployBeanPropertyAssocOne<?>) prop;
|
||||
private void readManyToOne(ManyToOne propAnn, DeployBeanPropertyAssocOne<?> beanProp) {
|
||||
|
||||
setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo());
|
||||
|
||||
beanProp.setBeanTable(beanTable(beanProp));
|
||||
setTargetType(propAnn.targetEntity(), beanProp);
|
||||
setBeanTable(beanProp);
|
||||
beanProp.setDbInsertable(true);
|
||||
beanProp.setDbUpdateable(true);
|
||||
beanProp.setNullable(propAnn.optional());
|
||||
@@ -232,7 +209,8 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
}
|
||||
|
||||
setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo());
|
||||
prop.setBeanTable(beanTable(prop));
|
||||
setTargetType(propAnn.targetEntity(), prop);
|
||||
setBeanTable(prop);
|
||||
}
|
||||
|
||||
private boolean readOrphanRemoval(OneToOne property) {
|
||||
@@ -261,7 +239,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
BeanTable baseBeanTable = factory.getBeanTable(info.getDescriptor().getBeanType());
|
||||
|
||||
String localPrimaryKey = baseBeanTable.getIdColumn();
|
||||
String foreignColumn = beanTable(prop).getIdColumn();
|
||||
String foreignColumn = getBeanTable(prop).getIdColumn();
|
||||
|
||||
prop.getTableJoin().addJoinColumn(new DeployTableJoinColumn(localPrimaryKey, foreignColumn, false, false));
|
||||
}
|
||||
|
||||
+4
-16
@@ -69,8 +69,6 @@ import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.Version;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.sql.Types;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -258,29 +256,19 @@ public class AnnotationFields extends AnnotationParser {
|
||||
}
|
||||
|
||||
private void initValidation(DeployBeanProperty prop) {
|
||||
NotNull notNull = get(prop, NotNull.class);
|
||||
if (notNull != null && isEbeanValidationGroups(notNull.groups())) {
|
||||
// Not null on all validation groups so enable
|
||||
// DDL generation of Not Null Constraint
|
||||
if (readConfig.isValidationNotNull(prop)) {
|
||||
prop.setNullable(false);
|
||||
}
|
||||
|
||||
if (!prop.isLob()) {
|
||||
// take the max size of all @Size annotations
|
||||
int maxSize = -1;
|
||||
for (Size size : prop.getMetaAnnotationSize()) {
|
||||
if (size.max() < Integer.MAX_VALUE) {
|
||||
maxSize = Math.max(maxSize, size.max());
|
||||
}
|
||||
}
|
||||
if (maxSize != -1) {
|
||||
int maxSize = readConfig.maxValidationSize(prop);
|
||||
if (maxSize > 0) {
|
||||
prop.setDbLength(maxSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initTenantId(DeployBeanProperty prop) {
|
||||
if (validationAnnotations) {
|
||||
if (readConfig.checkValidationAnnotations()) {
|
||||
initValidation(prop);
|
||||
}
|
||||
if (has(prop, TenantId.class)) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import javax.persistence.AttributeOverride;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.validation.groups.Default;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -26,14 +25,11 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
|
||||
final Class<?> beanType;
|
||||
|
||||
final boolean validationAnnotations;
|
||||
|
||||
final ReadAnnotationConfig readConfig;
|
||||
|
||||
AnnotationParser(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
|
||||
super(info.getUtil());
|
||||
this.readConfig = readConfig;
|
||||
this.validationAnnotations = readConfig.isJavaxValidationAnnotations();
|
||||
this.info = info;
|
||||
this.beanType = info.getDescriptor().getBeanType();
|
||||
this.descriptor = info.getDescriptor();
|
||||
@@ -127,17 +123,6 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the validation groups are {@link Default} (respectively empty)
|
||||
* can be applied to DDL generation.
|
||||
*/
|
||||
boolean isEbeanValidationGroups(Class<?>[] groups) {
|
||||
if (!util.isUseJavaxValidationNotNull()) {
|
||||
return false;
|
||||
}
|
||||
return groups.length == 0 || groups.length == 1 && Default.class.isAssignableFrom(groups[0]);
|
||||
}
|
||||
|
||||
String[] convertColumnNames(String[] columnNames) {
|
||||
for (int i = 0; i < columnNames.length; i++) {
|
||||
columnNames[i] = databasePlatform.convertQuotedIdentifiers(columnNames[i]);
|
||||
|
||||
-8
@@ -145,14 +145,6 @@ public class DeployCreateProperties {
|
||||
private DeployBeanProperty createProp(DeployBeanDescriptor<?> desc, Field field) {
|
||||
|
||||
Class<?> propertyType = field.getType();
|
||||
|
||||
ManyToOne manyToOne = AnnotationUtil.get(field, ManyToOne.class);
|
||||
if (manyToOne != null) {
|
||||
Class<?> tt = manyToOne.targetEntity();
|
||||
if (!tt.equals(void.class)) {
|
||||
propertyType = tt;
|
||||
}
|
||||
}
|
||||
if (isSpecialScalarType(field)) {
|
||||
return new DeployBeanProperty(desc, propertyType, field.getGenericType());
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class DeployUtil {
|
||||
|
||||
private final Encryptor bytesEncryptor;
|
||||
|
||||
private final boolean useJavaxValidationNotNull;
|
||||
private final boolean useValidationNotNull;
|
||||
|
||||
public DeployUtil(TypeManager typeMgr, DatabaseConfig config) {
|
||||
this.typeManager = typeMgr;
|
||||
@@ -67,7 +67,7 @@ public class DeployUtil {
|
||||
this.encryptKeyManager = config.getEncryptKeyManager();
|
||||
Encryptor be = config.getEncryptor();
|
||||
this.bytesEncryptor = be != null ? be : new SimpleAesEncryptor();
|
||||
this.useJavaxValidationNotNull = config.isUseJavaxValidationNotNull();
|
||||
this.useValidationNotNull = config.isUseValidationNotNull();
|
||||
}
|
||||
|
||||
public TypeManager getTypeManager() {
|
||||
@@ -286,8 +286,8 @@ public class DeployUtil {
|
||||
return type.equals(String.class);
|
||||
}
|
||||
|
||||
boolean isUseJavaxValidationNotNull() {
|
||||
return useJavaxValidationNotNull;
|
||||
boolean isUseValidationNotNull() {
|
||||
return useValidationNotNull;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
class InitMetaValidationAnnotation {
|
||||
|
||||
static void init(ReadAnnotationConfig readConfig) {
|
||||
readConfig.addMetaAnnotation(Size.class);
|
||||
readConfig.addMetaAnnotation(Size.List.class);
|
||||
}
|
||||
}
|
||||
+52
-8
@@ -3,8 +3,10 @@ package io.ebeaninternal.server.deploy.parse;
|
||||
import io.ebean.annotation.Aggregation;
|
||||
import io.ebean.annotation.Formula;
|
||||
import io.ebean.annotation.Where;
|
||||
import io.ebean.config.ClassLoadConfig;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import java.util.HashSet;
|
||||
@@ -21,9 +23,12 @@ class ReadAnnotationConfig {
|
||||
private final boolean disableL2Cache;
|
||||
private final boolean eagerFetchLobs;
|
||||
private final boolean javaxValidationAnnotations;
|
||||
private final boolean jakartaValidationAnnotations;
|
||||
private final boolean jacksonAnnotations;
|
||||
private final boolean idGeneratorAutomatic;
|
||||
|
||||
private final boolean useValidationNotNull;
|
||||
private final ReadValidationAnnotations javaxValidation;
|
||||
private final ReadValidationAnnotations jakartaValidation;
|
||||
private final Set<Class<?>> metaAnnotations = new HashSet<>();
|
||||
|
||||
ReadAnnotationConfig(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, DatabaseConfig config) {
|
||||
@@ -33,20 +38,32 @@ class ReadAnnotationConfig {
|
||||
this.disableL2Cache = config.isDisableL2Cache();
|
||||
this.eagerFetchLobs = config.isEagerFetchLobs();
|
||||
this.idGeneratorAutomatic = config.isIdGeneratorAutomatic();
|
||||
this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent();
|
||||
this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent();
|
||||
this.useValidationNotNull = config.isUseValidationNotNull();
|
||||
ClassLoadConfig classLoadConfig = generatedPropFactory.getClassLoadConfig();
|
||||
this.javaxValidationAnnotations = classLoadConfig.isJavaxValidationAnnotationsPresent();
|
||||
this.jakartaValidationAnnotations = classLoadConfig.isJakartaValidationAnnotationsPresent();
|
||||
this.jacksonAnnotations = classLoadConfig.isJacksonAnnotationsPresent();
|
||||
this.metaAnnotations.add(Column.class);
|
||||
this.metaAnnotations.add(Formula.class);
|
||||
this.metaAnnotations.add(Formula.List.class);
|
||||
this.metaAnnotations.add(Where.class);
|
||||
this.metaAnnotations.add(Where.List.class);
|
||||
this.metaAnnotations.add(Aggregation.class);
|
||||
this.javaxValidation = javaxValidationAnnotations ? new ReadValidationAnnotationsJavax(this) : null;
|
||||
this.jakartaValidation = jakartaValidationAnnotations ? new ReadValidationAnnotationsJakarta(this) : null;
|
||||
if (jacksonAnnotations) {
|
||||
InitMetaJacksonAnnotation.init(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void addMetaAnnotation(Class<?> annotation) {
|
||||
void addMetaAnnotation(Class<?> annotation) {
|
||||
metaAnnotations.add(annotation);
|
||||
}
|
||||
|
||||
boolean checkValidationAnnotations() {
|
||||
return javaxValidationAnnotations || jakartaValidationAnnotations;
|
||||
}
|
||||
|
||||
GeneratedPropertyFactory getGeneratedPropFactory() {
|
||||
return generatedPropFactory;
|
||||
}
|
||||
@@ -71,10 +88,6 @@ class ReadAnnotationConfig {
|
||||
return idGeneratorAutomatic;
|
||||
}
|
||||
|
||||
boolean isJavaxValidationAnnotations() {
|
||||
return javaxValidationAnnotations;
|
||||
}
|
||||
|
||||
boolean isJacksonAnnotations() {
|
||||
return jacksonAnnotations;
|
||||
}
|
||||
@@ -82,4 +95,35 @@ class ReadAnnotationConfig {
|
||||
public Set<Class<?>> getMetaAnnotations() {
|
||||
return metaAnnotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a NotNull validation annotation is on the property.
|
||||
*/
|
||||
boolean isValidationNotNull(DeployBeanProperty property) {
|
||||
if (!useValidationNotNull) {
|
||||
return false;
|
||||
}
|
||||
if (javaxValidation != null && javaxValidation.isValidationNotNull(property)) {
|
||||
return true;
|
||||
}
|
||||
if (jakartaValidation != null && jakartaValidation.isValidationNotNull(property)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max size of all validation @Size annotations.
|
||||
*/
|
||||
int maxValidationSize(DeployBeanProperty prop) {
|
||||
int maxSize = 0;
|
||||
if (javaxValidation != null) {
|
||||
maxSize = Math.max(maxSize, javaxValidation.maxSize(prop));
|
||||
}
|
||||
if (jakartaValidation != null) {
|
||||
maxSize = Math.max(maxSize, jakartaValidation.maxSize(prop));
|
||||
}
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,12 +14,6 @@ public class ReadAnnotations {
|
||||
|
||||
public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, DatabaseConfig config) {
|
||||
this.readConfig = new ReadAnnotationConfig(generatedPropFactory, asOfViewSuffix, versionsBetweenSuffix, config);
|
||||
if (readConfig.isJavaxValidationAnnotations()) {
|
||||
InitMetaValidationAnnotation.init(readConfig);
|
||||
}
|
||||
if (readConfig.isJacksonAnnotations()) {
|
||||
InitMetaJacksonAnnotation.init(readConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
/**
|
||||
* Reads validation NotNull and Size annotations for mapping.
|
||||
*/
|
||||
interface ReadValidationAnnotations {
|
||||
|
||||
/**
|
||||
* Return true if the property has a NotNull validation annotation.
|
||||
*/
|
||||
boolean isValidationNotNull(DeployBeanProperty property);
|
||||
|
||||
/**
|
||||
* Return the max value of the Size validation annotations on the property.
|
||||
*/
|
||||
int maxSize(DeployBeanProperty property);
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import jakarta.validation.groups.Default;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Jakarta validation annotations reader.
|
||||
*/
|
||||
class ReadValidationAnnotationsJakarta implements ReadValidationAnnotations {
|
||||
|
||||
ReadValidationAnnotationsJakarta(ReadAnnotationConfig readConfig) {
|
||||
readConfig.addMetaAnnotation(Size.class);
|
||||
readConfig.addMetaAnnotation(Size.List.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidationNotNull(DeployBeanProperty property) {
|
||||
NotNull notNull = AnnotationUtil.get(property.getField(), NotNull.class);
|
||||
return (notNull != null && isEbeanValidationGroups(notNull.groups()));
|
||||
}
|
||||
|
||||
private boolean isEbeanValidationGroups(Class<?>[] groups) {
|
||||
return groups.length == 0 || groups.length == 1 && Default.class.isAssignableFrom(groups[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int maxSize(DeployBeanProperty property) {
|
||||
int maxSize = 0;
|
||||
for (Size size : getMetaAnnotationJavaxSize(property)) {
|
||||
if (size.max() < Integer.MAX_VALUE) {
|
||||
maxSize = Math.max(maxSize, size.max());
|
||||
}
|
||||
}
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
private List<Size> getMetaAnnotationJavaxSize(DeployBeanProperty prop) {
|
||||
final List<Size> size = prop.getMetaAnnotations(Size.class);
|
||||
final List<Size.List> lists = prop.getMetaAnnotations(Size.List.class);
|
||||
for (Size.List list : lists) {
|
||||
Collections.addAll(size, list.value());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import javax.validation.groups.Default;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Javax validation annotations reader.
|
||||
*/
|
||||
class ReadValidationAnnotationsJavax implements ReadValidationAnnotations {
|
||||
|
||||
ReadValidationAnnotationsJavax(ReadAnnotationConfig readConfig) {
|
||||
readConfig.addMetaAnnotation(Size.class);
|
||||
readConfig.addMetaAnnotation(Size.List.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidationNotNull(DeployBeanProperty property) {
|
||||
NotNull notNull = AnnotationUtil.get(property.getField(), NotNull.class);
|
||||
return (notNull != null && isEbeanValidationGroups(notNull.groups()));
|
||||
}
|
||||
|
||||
private boolean isEbeanValidationGroups(Class<?>[] groups) {
|
||||
return groups.length == 0 || groups.length == 1 && Default.class.isAssignableFrom(groups[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int maxSize(DeployBeanProperty prop) {
|
||||
int maxSize = 0;
|
||||
for (Size size : getMetaAnnotationJavaxSize(prop)) {
|
||||
if (size.max() < Integer.MAX_VALUE) {
|
||||
maxSize = Math.max(maxSize, size.max());
|
||||
}
|
||||
}
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
private List<Size> getMetaAnnotationJavaxSize(DeployBeanProperty prop) {
|
||||
final List<Size> size = prop.getMetaAnnotations(Size.class);
|
||||
final List<Size.List> lists = prop.getMetaAnnotations(Size.List.class);
|
||||
for (Size.List list : lists) {
|
||||
Collections.addAll(size, list.value());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
-15
@@ -501,31 +501,16 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return query.forUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdate(Query.LockType lockType) {
|
||||
return query.forUpdate(lockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateNoWait() {
|
||||
return query.forUpdateNoWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateNoWait(Query.LockType lockType) {
|
||||
return query.forUpdateNoWait(lockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateSkipLocked() {
|
||||
return query.forUpdateSkipLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateSkipLocked(Query.LockType lockType) {
|
||||
return query.forUpdateSkipLocked(lockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> select(String fetchProperties) {
|
||||
return query.select(fetchProperties);
|
||||
|
||||
@@ -506,31 +506,16 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.forUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdate(Query.LockType lockType) {
|
||||
return exprList.forUpdate(lockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateNoWait() {
|
||||
return exprList.forUpdateNoWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateNoWait(Query.LockType lockType) {
|
||||
return exprList.forUpdateNoWait(lockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateSkipLocked() {
|
||||
return exprList.forUpdateSkipLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateSkipLocked(Query.LockType lockType) {
|
||||
return exprList.forUpdateSkipLocked(lockType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Path exists - for the given path in a JSON document.
|
||||
*/
|
||||
|
||||
@@ -17,11 +17,11 @@ class ParseFetchConfig {
|
||||
|
||||
if (path.startsWith("lazy")) {
|
||||
if (path.length() == 4) {
|
||||
return new FetchConfig().lazy();
|
||||
return FetchConfig.ofLazy();
|
||||
} else if (path.charAt(4) == '(') {
|
||||
path = path.substring(5);
|
||||
int batchSize = parseBatchSize(path);
|
||||
return new FetchConfig().lazy(batchSize);
|
||||
return FetchConfig.ofLazy(batchSize);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -29,11 +29,11 @@ class ParseFetchConfig {
|
||||
|
||||
if (path.startsWith("query")) {
|
||||
if (path.length() == 5) {
|
||||
return new FetchConfig().query();
|
||||
return FetchConfig.ofQuery();
|
||||
} else if (path.charAt(5) == '(') {
|
||||
path = path.substring(6);
|
||||
int batchSize = parseBatchSize(path);
|
||||
return new FetchConfig().query(batchSize);
|
||||
return FetchConfig.ofQuery(batchSize);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.server.loadcontext;
|
||||
|
||||
import io.ebean.FetchConfig;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
@@ -28,9 +27,7 @@ abstract class DLoadBaseContext {
|
||||
|
||||
final boolean hitCache;
|
||||
|
||||
final int firstBatchSize;
|
||||
|
||||
final int secondaryBatchSize;
|
||||
final int batchSize;
|
||||
|
||||
final ObjectGraphNode objectGraphNode;
|
||||
|
||||
@@ -45,38 +42,11 @@ abstract class DLoadBaseContext {
|
||||
this.hitCache = parent.isBeanCacheGet() && desc.isBeanCaching();
|
||||
this.objectGraphNode = parent.getObjectGraphNode(path);
|
||||
this.queryFetch = queryProps != null && queryProps.isQueryFetch();
|
||||
this.firstBatchSize = initFirstBatchSize(defaultBatchSize, queryProps);
|
||||
this.secondaryBatchSize = initSecondaryBatchSize(defaultBatchSize, firstBatchSize, queryProps);
|
||||
this.batchSize = initBatchSize(defaultBatchSize, queryProps);
|
||||
}
|
||||
|
||||
private int initFirstBatchSize(int batchSize, OrmQueryProperties queryProps) {
|
||||
if (queryProps == null) {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
int queryBatchSize = queryProps.getQueryFetchBatch();
|
||||
if (queryBatchSize == -1) {
|
||||
return batchSize;
|
||||
|
||||
} else if (queryBatchSize == 0) {
|
||||
return 100;
|
||||
|
||||
} else {
|
||||
return queryBatchSize;
|
||||
}
|
||||
}
|
||||
|
||||
private int initSecondaryBatchSize(int defaultBatchSize, int firstBatchSize, OrmQueryProperties queryProps) {
|
||||
if (queryProps == null) {
|
||||
return defaultBatchSize;
|
||||
}
|
||||
FetchConfig fetchConfig = queryProps.getFetchConfig();
|
||||
if (fetchConfig.isQueryAll()) {
|
||||
return firstBatchSize;
|
||||
}
|
||||
|
||||
int lazyBatchSize = fetchConfig.getLazyBatchSize();
|
||||
return (lazyBatchSize > 1) ? lazyBatchSize : defaultBatchSize;
|
||||
private int initBatchSize(int batchSize, OrmQueryProperties queryProps) {
|
||||
return queryProps == null ? batchSize : queryProps.getBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +54,6 @@ abstract class DLoadBaseContext {
|
||||
* set onto the secondary query.
|
||||
*/
|
||||
void setLabel(SpiQuery<?> query) {
|
||||
|
||||
String label = parent.getPlanLabel();
|
||||
if (label != null) {
|
||||
query.setProfilePath(label, fullPath, parent.getProfileLocation());
|
||||
|
||||
@@ -35,7 +35,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
super(parent, desc, path, defaultBatchSize, queryProps);
|
||||
// bufferList only required when using query joins (queryFetch)
|
||||
this.bufferList = (!queryFetch) ? null : new ArrayList<>();
|
||||
this.currentBuffer = createBuffer(firstBatchSize);
|
||||
this.currentBuffer = createBuffer(batchSize);
|
||||
this.cache = (queryProps != null) && queryProps.isCache();
|
||||
}
|
||||
|
||||
@@ -52,11 +52,10 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
if (bufferList != null) {
|
||||
bufferList.clear();
|
||||
}
|
||||
currentBuffer = createBuffer(secondaryBatchSize);
|
||||
currentBuffer = createBuffer(batchSize);
|
||||
}
|
||||
|
||||
private void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
|
||||
|
||||
if (cache) {
|
||||
query.setBeanCacheMode(CacheMode.ON);
|
||||
}
|
||||
@@ -70,9 +69,8 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
}
|
||||
|
||||
protected void register(EntityBeanIntercept ebi) {
|
||||
|
||||
if (currentBuffer.isFull()) {
|
||||
currentBuffer = createBuffer(secondaryBatchSize);
|
||||
currentBuffer = createBuffer(batchSize);
|
||||
}
|
||||
ebi.setBeanLoader(currentBuffer, getPersistenceContext());
|
||||
currentBuffer.add(ebi);
|
||||
@@ -97,10 +95,6 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
for (LoadBuffer loadBuffer : bufferList) {
|
||||
if (!loadBuffer.list.isEmpty()) {
|
||||
parent.getEbeanServer().loadBean(new LoadBeanRequest(loadBuffer, parentRequest));
|
||||
if (!queryProps.isQueryFetchAll()) {
|
||||
// Stop - only fetch the first batch ... the rest will be lazy loaded
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (forEach) {
|
||||
clear();
|
||||
@@ -198,8 +192,10 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
// lazy load property was a Many
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.hitCache) {
|
||||
if (list.isEmpty()) {
|
||||
// re-add to the batch and lazy load from DB skipping l2 cache
|
||||
list.add(ebi);
|
||||
} else if (context.hitCache) {
|
||||
Set<EntityBeanIntercept> hits = context.desc.cacheBeanLoadAll(list, persistenceContext, ebi.getLazyLoadPropertyIndex(), ebi.getLazyLoadProperty());
|
||||
list.removeAll(hits);
|
||||
if (list.isEmpty() || hits.contains(ebi)) {
|
||||
|
||||
@@ -188,7 +188,7 @@ public class DLoadContext implements LoadContext {
|
||||
}
|
||||
int maxBatch = 0;
|
||||
for (OrmQueryProperties aSecQuery : secQuery) {
|
||||
int batchSize = aSecQuery.getQueryFetchBatch();
|
||||
int batchSize = aSecQuery.getBatchSize();
|
||||
if (batchSize == 0) {
|
||||
batchSize = defaultQueryBatch;
|
||||
}
|
||||
@@ -300,12 +300,9 @@ public class DLoadContext implements LoadContext {
|
||||
}
|
||||
|
||||
private void registerSecondaryNode(boolean many, OrmQueryProperties props) {
|
||||
int batchSize;
|
||||
if (props.isQueryFetch()) {
|
||||
batchSize = 100;
|
||||
} else {
|
||||
int lazyJoinBatch = props.getLazyFetchBatch();
|
||||
batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize;
|
||||
int batchSize = props.getBatchSize();
|
||||
if (batchSize == 0) {
|
||||
batchSize = defaultBatchSize;
|
||||
}
|
||||
String path = props.getPath();
|
||||
if (many) {
|
||||
|
||||
@@ -40,7 +40,7 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
|
||||
this.docStoreMapped = property.isTargetDocStoreMapped();
|
||||
// bufferList only required when using query joins (queryFetch)
|
||||
this.bufferList = (!queryFetch) ? null : new ArrayList<>();
|
||||
this.currentBuffer = createBuffer(firstBatchSize);
|
||||
this.currentBuffer = createBuffer(batchSize);
|
||||
}
|
||||
|
||||
private LoadBuffer createBuffer(int size) {
|
||||
@@ -58,11 +58,10 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
|
||||
if (bufferList != null) {
|
||||
bufferList.clear();
|
||||
}
|
||||
currentBuffer = createBuffer(secondaryBatchSize);
|
||||
currentBuffer = createBuffer(batchSize);
|
||||
}
|
||||
|
||||
private void configureQuery(SpiQuery<?> query) {
|
||||
|
||||
setLabel(query);
|
||||
parent.propagateQueryState(query, docStoreMapped);
|
||||
query.setParentNode(objectGraphNode);
|
||||
@@ -85,9 +84,8 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
|
||||
}
|
||||
|
||||
public void register(BeanCollection<?> bc) {
|
||||
|
||||
if (currentBuffer.isFull()) {
|
||||
currentBuffer = createBuffer(secondaryBatchSize);
|
||||
currentBuffer = createBuffer(batchSize);
|
||||
}
|
||||
currentBuffer.add(bc);
|
||||
bc.setLoader(currentBuffer);
|
||||
@@ -105,13 +103,8 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
|
||||
if (!loadBuffer.list.isEmpty()) {
|
||||
LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest);
|
||||
parent.getEbeanServer().loadMany(req);
|
||||
if (!queryProps.isQueryFetchAll()) {
|
||||
// Stop - only fetch the first batch ... the rest will be lazy loaded
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (forEach) {
|
||||
clear();
|
||||
} else {
|
||||
|
||||
@@ -298,18 +298,7 @@ public final class BatchControl {
|
||||
// Nothing in queue to flush
|
||||
return;
|
||||
}
|
||||
|
||||
// convert entry map to array for sorting
|
||||
BatchedBeanHolder[] bsArray = getBeanHolderArray();
|
||||
// sort the entries by depth
|
||||
Arrays.sort(bsArray, depthComparator);
|
||||
|
||||
if (transaction.isLogSummary()) {
|
||||
transaction.logSummary("BatchControl flush " + Arrays.toString(bsArray));
|
||||
}
|
||||
for (BatchedBeanHolder beanHolder : bsArray) {
|
||||
beanHolder.executeNow();
|
||||
}
|
||||
executeAll();
|
||||
persistedBeans.clear();
|
||||
if (reset) {
|
||||
beanHoldMap.clear();
|
||||
@@ -323,6 +312,32 @@ public final class BatchControl {
|
||||
}
|
||||
}
|
||||
|
||||
private void executeAll() throws BatchedSqlException {
|
||||
do {
|
||||
// convert entry map to array for sorting
|
||||
BatchedBeanHolder[] bsArray = getBeanHolderArray();
|
||||
Arrays.sort(bsArray, depthComparator);
|
||||
if (transaction.isLogSummary()) {
|
||||
transaction.logSummary("BatchControl flush " + Arrays.toString(bsArray));
|
||||
}
|
||||
for (BatchedBeanHolder beanHolder : bsArray) {
|
||||
beanHolder.executeNow();
|
||||
}
|
||||
} while (!isBeanHoldersEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if all bean holders are empty.
|
||||
*/
|
||||
private boolean isBeanHoldersEmpty() {
|
||||
for (BatchedBeanHolder beanHolder : beanHoldMap.values()) {
|
||||
if (!beanHolder.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an entry for the given type description. The type description is
|
||||
* typically the bean class name (or table name for MapBeans).
|
||||
|
||||
@@ -47,6 +47,11 @@ class BatchedBeanHolder {
|
||||
*/
|
||||
private ArrayList<PersistRequest> deletes;
|
||||
|
||||
/**
|
||||
* The holder is empty
|
||||
*/
|
||||
private boolean empty = true;
|
||||
|
||||
/**
|
||||
* Create a new entry with a given type and depth.
|
||||
*/
|
||||
@@ -63,6 +68,13 @@ class BatchedBeanHolder {
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the BeanHolder is empty.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the persist requests in this entry.
|
||||
* <p>
|
||||
@@ -90,6 +102,7 @@ class BatchedBeanHolder {
|
||||
updates = new ArrayList<>();
|
||||
control.executeNow(bufferedUpdates);
|
||||
}
|
||||
empty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,9 +125,8 @@ class BatchedBeanHolder {
|
||||
* Add the request to the appropriate persist list.
|
||||
*/
|
||||
public int append(PersistRequestBean<?> request) {
|
||||
|
||||
empty = false;
|
||||
request.setBatched();
|
||||
|
||||
switch (request.getType()) {
|
||||
case INSERT:
|
||||
if (inserts == null) {
|
||||
|
||||
@@ -210,18 +210,6 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean hasNewOrDirtyBeans() {
|
||||
if (collection == null) {
|
||||
return false;
|
||||
}
|
||||
for (Object bean : collection) {
|
||||
if (bean instanceof EntityBean && ((EntityBean) bean)._ebean_getIntercept().isNewOrDirty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the Id values of the details to remove 'missing children' for stateless updates.
|
||||
*/
|
||||
@@ -344,14 +332,18 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
transaction.depth(-1);
|
||||
}
|
||||
|
||||
private boolean isChangedProperty() {
|
||||
return parentBean._ebean_getIntercept().isChangedProperty(many.getPropertyIndex());
|
||||
}
|
||||
|
||||
private void removeAssocManyOrphans() {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
if (!(value instanceof BeanCollection<?>)) {
|
||||
// if (!insertedParent && cascade && hasNewOrDirtyBeans()) {
|
||||
// persister.addToFlushQueue(many.deleteByParentId(request.getBeanId(), null), transaction, 0);
|
||||
// }
|
||||
if (!insertedParent && cascade && isChangedProperty()) {
|
||||
persister.addToFlushQueue(many.deleteByParentId(request.getBeanId(), null), transaction, 0);
|
||||
}
|
||||
} else {
|
||||
BeanCollection<?> c = (BeanCollection<?>) value;
|
||||
Set<?> modifyRemovals = c.getModifyRemovals();
|
||||
|
||||
@@ -296,10 +296,10 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
this.cancelled = true;
|
||||
if (pstmt != null) {
|
||||
try {
|
||||
logger.debug("Cancelling query");
|
||||
pstmt.cancel();
|
||||
} catch (SQLException e) {
|
||||
String msg = "Error cancelling query";
|
||||
throw new PersistenceException(msg, e);
|
||||
throw new PersistenceException("Error cancelling query", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -322,7 +322,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
}
|
||||
|
||||
private boolean prepareBindExecuteQueryWithOption(boolean forwardOnlyHint) throws SQLException {
|
||||
|
||||
ResultSet resultSet = prepareResultSet(forwardOnlyHint);
|
||||
if (resultSet == null) {
|
||||
return false;
|
||||
@@ -334,19 +333,12 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
ResultSet prepareResultSet(boolean forwardOnlyHint) throws SQLException {
|
||||
lock.lock();
|
||||
try {
|
||||
if (cancelled || query.isCancelled()) {
|
||||
// cancelled before we started
|
||||
cancelled = true;
|
||||
return null;
|
||||
if (cancelled) {
|
||||
throw new SQLException("Query cancelled");
|
||||
}
|
||||
|
||||
startNano = System.nanoTime();
|
||||
|
||||
// prepare
|
||||
SpiTransaction t = request.getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
|
||||
if (query.isRawSql()) {
|
||||
ResultSet suppliedResultSet = query.getRawSql().getResultSet();
|
||||
if (suppliedResultSet != null) {
|
||||
@@ -356,6 +348,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
}
|
||||
}
|
||||
|
||||
Connection conn = t.getInternalConnection();
|
||||
if (forwardOnlyHint) {
|
||||
// Use forward only hints for large resultSet processing (Issue 56, MySql specific)
|
||||
pstmt = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
|
||||
@@ -363,18 +356,13 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
} else {
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
}
|
||||
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
if (query.getBufferFetchSizeHint() > 0) {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
|
||||
DataBind dataBind = queryPlan.bindEncryptedProperties(pstmt, conn);
|
||||
bindLog = predicates.bind(dataBind);
|
||||
|
||||
// executeQuery
|
||||
bindLog = predicates.bind(queryPlan.bindEncryptedProperties(pstmt, conn));
|
||||
return pstmt.executeQuery();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
|
||||
@@ -571,8 +571,8 @@ class CQueryBuilder {
|
||||
private final CQueryPredicates predicates;
|
||||
private final SqlTree select;
|
||||
private final boolean updateStatement;
|
||||
|
||||
private final boolean distinct;
|
||||
private final boolean countSingleAttribute;
|
||||
private final String dbOrderBy;
|
||||
private boolean useSqlLimiter;
|
||||
private boolean hasWhere;
|
||||
@@ -590,6 +590,7 @@ class CQueryBuilder {
|
||||
this.updateStatement = updateStatement;
|
||||
this.distinct = query.isDistinct() || select.isSqlDistinct();
|
||||
this.dbOrderBy = predicates.getDbOrderBy();
|
||||
this.countSingleAttribute = query.isCountDistinct() && query.isSingleAttribute();
|
||||
}
|
||||
|
||||
private void appendSelect() {
|
||||
@@ -601,8 +602,13 @@ class CQueryBuilder {
|
||||
if (!useSqlLimiter) {
|
||||
appendSelectDistinct();
|
||||
}
|
||||
if (query.isCountDistinct() && query.isSingleAttribute()) {
|
||||
sb.append("r1.attribute_, count(*) from (select ").append(select.getSelectSql()).append(" as attribute_");
|
||||
if (countSingleAttribute) {
|
||||
sb.append("r1.attribute_, count(*) from (select ");
|
||||
if (distinct) {
|
||||
sb.append("distinct t0.");
|
||||
sb.append(request.getBeanDescriptor().getIdProperty().getDbColumn()).append(", ");
|
||||
}
|
||||
sb.append(select.getSelectSql()).append(" as attribute_");
|
||||
} else {
|
||||
sb.append(select.getSelectSql());
|
||||
}
|
||||
@@ -621,7 +627,7 @@ class CQueryBuilder {
|
||||
|
||||
private void appendSelectDistinct() {
|
||||
sb.append("select ");
|
||||
if (distinct) {
|
||||
if (distinct && !countSingleAttribute) {
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append("count(");
|
||||
}
|
||||
@@ -730,7 +736,7 @@ class CQueryBuilder {
|
||||
sb.append(" order by ").append(dbOrderBy);
|
||||
}
|
||||
|
||||
if (query.isCountDistinct() && query.isSingleAttribute()) {
|
||||
if (countSingleAttribute) {
|
||||
sb.append(") r1 group by r1.attribute_");
|
||||
sb.append(toSql(query.getCountDistinctOrder()));
|
||||
}
|
||||
|
||||
@@ -401,12 +401,6 @@ public class CQueryEngine {
|
||||
if (cquery != null) {
|
||||
cquery.close();
|
||||
}
|
||||
if (request.getQuery().isFutureFetch()) {
|
||||
// end the transaction for futureFindIds
|
||||
// as it had it's own transaction
|
||||
logger.debug("Future fetch completed!");
|
||||
request.getTransaction().end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,13 @@ import io.ebeaninternal.server.querydefn.SpiFetchGroup;
|
||||
*/
|
||||
class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
|
||||
|
||||
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
|
||||
private static final FetchConfig DEFAULT_FETCH = FetchConfig.ofDefault();
|
||||
|
||||
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
|
||||
private static final FetchConfig FETCH_CACHE = FetchConfig.ofCache();
|
||||
|
||||
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
|
||||
private static final FetchConfig FETCH_QUERY = FetchConfig.ofQuery();
|
||||
|
||||
private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy();
|
||||
|
||||
private final OrmQueryDetail detail;
|
||||
|
||||
@@ -31,13 +33,13 @@ class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetch(String path) {
|
||||
detail.fetch(path, null, null);
|
||||
detail.fetchProperties(path, null, DEFAULT_FETCH);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetch(String path, FetchGroup nestedGroup) {
|
||||
return fetchNested(path, nestedGroup, null);
|
||||
return fetchNested(path, nestedGroup, DEFAULT_FETCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,7 +53,6 @@ class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
|
||||
}
|
||||
|
||||
private FetchGroupBuilder<T> fetchNested(String path, FetchGroup nestedGroup, FetchConfig fetchConfig) {
|
||||
|
||||
OrmQueryDetail nestedDetail = ((SpiFetchGroup) nestedGroup).underlying();
|
||||
detail.addNested(path, nestedDetail, fetchConfig);
|
||||
return this;
|
||||
@@ -59,25 +60,25 @@ class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetchQuery(String path) {
|
||||
detail.fetch(path, null, FETCH_QUERY);
|
||||
detail.fetchProperties(path, null, FETCH_QUERY);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetchCache(String path) {
|
||||
detail.fetch(path, null, FETCH_CACHE);
|
||||
detail.fetchProperties(path, null, FETCH_CACHE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetchLazy(String path) {
|
||||
detail.fetch(path, null, FETCH_LAZY);
|
||||
detail.fetchProperties(path, null, FETCH_LAZY);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetch(String path, String properties) {
|
||||
detail.fetch(path, properties, null);
|
||||
detail.fetch(path, properties, DEFAULT_FETCH);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
+15
-19
@@ -25,6 +25,7 @@ import io.ebean.Transaction;
|
||||
import io.ebean.UpdateQuery;
|
||||
import io.ebean.Version;
|
||||
import io.ebean.service.SpiFetchGroupQuery;
|
||||
import io.ebeaninternal.api.SpiQueryFetch;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.querydefn.SpiFetchGroup;
|
||||
|
||||
@@ -43,13 +44,13 @@ import java.util.stream.Stream;
|
||||
/**
|
||||
* Implementation of FetchGroup query for use to create FetchGroup via query beans.
|
||||
*/
|
||||
class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
|
||||
class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQueryFetch {
|
||||
|
||||
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
|
||||
private static final FetchConfig FETCH_CACHE = FetchConfig.ofCache();
|
||||
|
||||
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
|
||||
private static final FetchConfig FETCH_QUERY = FetchConfig.ofQuery();
|
||||
|
||||
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
|
||||
private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy();
|
||||
|
||||
private OrmQueryDetail detail = new OrmQueryDetail();
|
||||
|
||||
@@ -569,21 +570,6 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
|
||||
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdate(LockType lockType) {
|
||||
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateNoWait(LockType lockType) {
|
||||
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateSkipLocked(LockType lockType) {
|
||||
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForUpdate() {
|
||||
return false;
|
||||
@@ -643,4 +629,14 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
|
||||
public Query<T> orderById(boolean orderById) {
|
||||
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectProperties(Set<String> props) {
|
||||
detail.selectProperties(props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetchProperties(String property, Set<String> columns, FetchConfig config) {
|
||||
detail.fetchProperties(property, columns, config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,13 @@ public interface STreePropertyAssoc extends STreeProperty {
|
||||
*/
|
||||
void setValue(EntityBean parentBean, Object contextBean);
|
||||
|
||||
/**
|
||||
* Return true if the associated type has soft delete.
|
||||
*/
|
||||
boolean isTargetSoftDelete();
|
||||
|
||||
/**
|
||||
* Return the soft delete predicate.
|
||||
*/
|
||||
String getSoftDeletePredicate(String tableAlias);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ public final class SqlTreeBuilder {
|
||||
* Construct for RawSql query.
|
||||
*/
|
||||
SqlTreeBuilder(OrmQueryRequest<?> request, CQueryPredicates predicates, OrmQueryDetail queryDetail, boolean rawNoId) {
|
||||
|
||||
this.rawSql = true;
|
||||
this.desc = request.getBeanDescriptor();
|
||||
this.rawNoId = rawNoId;
|
||||
@@ -97,7 +96,6 @@ public final class SqlTreeBuilder {
|
||||
* to the root node.
|
||||
*/
|
||||
SqlTreeBuilder(String columnAliasPrefix, CQueryBuilder builder, OrmQueryRequest<?> request, CQueryPredicates predicates) {
|
||||
|
||||
this.rawSql = false;
|
||||
this.rawNoId = false;
|
||||
this.desc = request.getBeanDescriptor();
|
||||
@@ -108,11 +106,9 @@ public final class SqlTreeBuilder {
|
||||
this.includeJoin = query.getM2mIncludeJoin();
|
||||
this.manyWhereJoins = query.getManyWhereJoins();
|
||||
this.queryDetail = query.getDetail();
|
||||
|
||||
this.predicates = predicates;
|
||||
this.alias = new SqlTreeAlias(request.getBaseTableAlias(), temporalMode);
|
||||
this.distinctOnPlatform = builder.isPlatformDistinctOn();
|
||||
|
||||
String fromForUpdate = builder.fromForUpdate(query);
|
||||
CQueryHistorySupport historySupport = builder.getHistorySupport(query);
|
||||
CQueryDraftSupport draftSupport = builder.getDraftSupport(query);
|
||||
@@ -124,10 +120,8 @@ public final class SqlTreeBuilder {
|
||||
* Build based on the includes and using the BeanJoinTree.
|
||||
*/
|
||||
public SqlTree build() {
|
||||
|
||||
// build the appropriate chain of SelectAdapter's
|
||||
buildRoot(desc);
|
||||
|
||||
// build the actual String
|
||||
String distinctOn = null;
|
||||
String selectSql = null;
|
||||
@@ -149,7 +143,6 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
|
||||
private String buildSelectClause() {
|
||||
|
||||
if (rawSql) {
|
||||
return "Not Used";
|
||||
}
|
||||
@@ -158,7 +151,6 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
|
||||
private String buildGroupByClause() {
|
||||
|
||||
if (rawSql || !rootNode.isAggregation()) {
|
||||
return null;
|
||||
}
|
||||
@@ -168,7 +160,6 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
|
||||
private String buildDistinctOn() {
|
||||
|
||||
if (rawSql || !distinctOnPlatform || !sqlDistinct || Type.COUNT == query.getType()) {
|
||||
return null;
|
||||
}
|
||||
@@ -207,7 +198,6 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
|
||||
private String buildWhereClause() {
|
||||
|
||||
if (rawSql) {
|
||||
return "Not Used";
|
||||
}
|
||||
@@ -216,7 +206,6 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
|
||||
private String buildFromClause() {
|
||||
|
||||
if (rawSql) {
|
||||
return "Not Used";
|
||||
}
|
||||
@@ -225,17 +214,13 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
|
||||
private void buildRoot(STreeType desc) {
|
||||
|
||||
rootNode = buildSelectChain(null, null, desc, null);
|
||||
|
||||
if (!rawSql) {
|
||||
alias.addJoin(queryDetail.getFetchPaths(), desc);
|
||||
alias.addJoin(predicates.getPredicateIncludes(), desc);
|
||||
alias.addManyWhereJoins(manyWhereJoins.getPropertyNames());
|
||||
|
||||
// build set of table alias
|
||||
alias.buildAlias();
|
||||
|
||||
predicates.parseTableAlias(alias);
|
||||
}
|
||||
}
|
||||
@@ -246,9 +231,7 @@ public final class SqlTreeBuilder {
|
||||
*/
|
||||
private SqlTreeNode buildSelectChain(String prefix, STreePropertyAssoc prop,
|
||||
STreeType desc, List<SqlTreeNode> joinList) {
|
||||
|
||||
List<SqlTreeNode> myJoinList = new ArrayList<>();
|
||||
|
||||
List<STreePropertyAssocOne> extraProps = new ArrayList<>();
|
||||
for (STreePropertyAssocOne one : desc.propsOne()) {
|
||||
String propPrefix = SplitName.add(prefix, one.getName());
|
||||
@@ -295,7 +278,6 @@ public final class SqlTreeBuilder {
|
||||
* </p>
|
||||
*/
|
||||
private void addManyWhereJoins(List<SqlTreeNode> myJoinList) {
|
||||
|
||||
Collection<PropertyJoin> includes = manyWhereJoins.getPropertyJoins();
|
||||
for (PropertyJoin joinProp : includes) {
|
||||
STreePropertyAssoc beanProperty = (STreePropertyAssoc) desc.findPropertyFromPath(joinProp.getProperty());
|
||||
@@ -311,7 +293,6 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
|
||||
private SqlTreeNode buildNode(String prefix, STreePropertyAssoc prop, STreeType desc, List<SqlTreeNode> myList, SqlTreeProperties props) {
|
||||
|
||||
if (prefix == null) {
|
||||
buildExtraJoins(desc, myList);
|
||||
|
||||
@@ -340,13 +321,10 @@ public final class SqlTreeBuilder {
|
||||
* already in select clause.
|
||||
*/
|
||||
private void buildExtraJoins(STreeType desc, List<SqlTreeNode> myList) {
|
||||
|
||||
if (rawSql) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> predicateIncludes = predicates.getPredicateIncludes();
|
||||
|
||||
if (predicateIncludes == null) {
|
||||
return;
|
||||
}
|
||||
@@ -362,8 +340,7 @@ public final class SqlTreeBuilder {
|
||||
|
||||
// look for predicateIncludes that are not in selectIncludes and add
|
||||
// them as extra joins to the query
|
||||
IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes);
|
||||
|
||||
IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes, temporalMode);
|
||||
Collection<SqlTreeNodeExtraJoin> extraJoins = extraJoinDistill.getExtraJoinRootNodes();
|
||||
if (!extraJoins.isEmpty()) {
|
||||
// add extra joins required to support predicates
|
||||
@@ -389,7 +366,6 @@ public final class SqlTreeBuilder {
|
||||
* </p>
|
||||
*/
|
||||
private void addPropertyToSubQuery(SqlTreeProperties selectProps, STreeType desc, String propName) {
|
||||
|
||||
STreeProperty p = desc.findProperty(propName);
|
||||
if (p == null) {
|
||||
logger.error("property [" + propName + "]not found on " + desc + " for query - excluding it.");
|
||||
@@ -402,13 +378,10 @@ public final class SqlTreeBuilder {
|
||||
p = ((STreePropertyAssoc) p).target().findProperty(name);
|
||||
}
|
||||
}
|
||||
|
||||
selectProps.add(p);
|
||||
}
|
||||
|
||||
private void addProperty(SqlTreeProperties selectProps, STreeType desc,
|
||||
OrmQueryProperties queryProps, String propName) {
|
||||
|
||||
private void addProperty(SqlTreeProperties selectProps, STreeType desc, OrmQueryProperties queryProps, String propName) {
|
||||
if (subQuery) {
|
||||
addPropertyToSubQuery(selectProps, desc, propName);
|
||||
return;
|
||||
@@ -471,7 +444,6 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
|
||||
private SqlTreeProperties getBaseSelectPartial(STreeType desc, OrmQueryProperties queryProps) {
|
||||
|
||||
SqlTreeProperties selectProps = new SqlTreeProperties();
|
||||
// add properties in the order in which they appear
|
||||
// in the query. Gives predictable sql/properties for
|
||||
@@ -480,7 +452,7 @@ public final class SqlTreeBuilder {
|
||||
// Also note that this can include transient properties.
|
||||
// This makes sense for transient properties used to
|
||||
// hold sum() count() type values (with SqlSelect)
|
||||
final Set<String> selectInclude = queryProps.getSelectInclude();
|
||||
final Set<String> selectInclude = queryProps.getIncluded();
|
||||
for (String propName : selectInclude) {
|
||||
if (!propName.isEmpty()) {
|
||||
addProperty(selectProps, desc, queryProps, propName);
|
||||
@@ -502,7 +474,6 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
|
||||
private SqlTreeProperties getBaseSelect(STreeType desc, OrmQueryProperties queryProps) {
|
||||
|
||||
boolean partial = queryProps != null && !queryProps.allProperties();
|
||||
if (partial) {
|
||||
return getBaseSelectPartial(desc, queryProps);
|
||||
@@ -541,13 +512,10 @@ public final class SqlTreeBuilder {
|
||||
* Return true if this many node should be included in the query.
|
||||
*/
|
||||
private boolean isIncludeMany(String propName, STreePropertyAssocMany manyProp) {
|
||||
|
||||
if (queryDetail.isJoinsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queryDetail.includesPath(propName)) {
|
||||
|
||||
if (manyProperty != null) {
|
||||
// only one many associated allowed to be included in fetch
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -555,7 +523,6 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
manyProperty = manyProp;
|
||||
return true;
|
||||
}
|
||||
@@ -571,7 +538,6 @@ public final class SqlTreeBuilder {
|
||||
* </p>
|
||||
*/
|
||||
private boolean isIncludeBean(String prefix) {
|
||||
|
||||
if (queryDetail.includesPath(prefix)) {
|
||||
// explicitly included
|
||||
String[] splitNames = SplitName.split(prefix);
|
||||
@@ -592,26 +558,20 @@ public final class SqlTreeBuilder {
|
||||
*/
|
||||
private static class IncludesDistiller {
|
||||
|
||||
private final STreeType desc;
|
||||
private final Set<String> selectIncludes;
|
||||
private final Set<String> predicateIncludes;
|
||||
private final SpiQuery.TemporalMode temporalMode;
|
||||
|
||||
/**
|
||||
* Contains the 'root' extra joins. We only return the roots back.
|
||||
*/
|
||||
private final Map<String, SqlTreeNodeExtraJoin> joinRegister = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Register of all the extra join nodes.
|
||||
*/
|
||||
private final Map<String, SqlTreeNodeExtraJoin> rootRegister = new HashMap<>();
|
||||
|
||||
private final STreeType desc;
|
||||
|
||||
private IncludesDistiller(STreeType desc, Set<String> selectIncludes,
|
||||
Set<String> predicateIncludes) {
|
||||
Set<String> predicateIncludes, SpiQuery.TemporalMode temporalMode) {
|
||||
this.desc = desc;
|
||||
this.selectIncludes = selectIncludes;
|
||||
this.predicateIncludes = predicateIncludes;
|
||||
this.temporalMode = temporalMode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -623,26 +583,21 @@ public final class SqlTreeBuilder {
|
||||
* </p>
|
||||
*/
|
||||
private Collection<SqlTreeNodeExtraJoin> getExtraJoinRootNodes() {
|
||||
|
||||
String[] extras = findExtras();
|
||||
if (extras.length == 0) {
|
||||
return rootRegister.values();
|
||||
}
|
||||
|
||||
// sort so we process only getting the leaves
|
||||
// excluding nodes between root and the leaf
|
||||
Arrays.sort(extras);
|
||||
|
||||
// reverse order so get the leaves first...
|
||||
for (String extra : extras) {
|
||||
createExtraJoin(extra);
|
||||
}
|
||||
|
||||
return rootRegister.values();
|
||||
}
|
||||
|
||||
private void createExtraJoin(String includeProp) {
|
||||
|
||||
SqlTreeNodeExtraJoin extraJoin = createJoinLeaf(includeProp);
|
||||
if (extraJoin != null) {
|
||||
// add the extra join...
|
||||
@@ -650,7 +605,6 @@ public final class SqlTreeBuilder {
|
||||
// find root of this extra join... linking back to the
|
||||
// parents (creating the tree) as it goes.
|
||||
SqlTreeNodeExtraJoin root = findExtraJoinRoot(includeProp, extraJoin);
|
||||
|
||||
// register the root because these are the only ones we
|
||||
// return back.
|
||||
rootRegister.put(root.getName(), root);
|
||||
@@ -661,12 +615,11 @@ public final class SqlTreeBuilder {
|
||||
* Create a SqlTreeNodeExtraJoin, register and return it.
|
||||
*/
|
||||
private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) {
|
||||
|
||||
ExtraJoin extra = desc.extraJoin(propertyName);
|
||||
if (extra == null) {
|
||||
return null;
|
||||
} else {
|
||||
SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.getProperty(), extra.isContainsMany());
|
||||
SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.getProperty(), extra.isContainsMany(), temporalMode);
|
||||
joinRegister.put(propertyName, extraJoin);
|
||||
return extraJoin;
|
||||
}
|
||||
@@ -713,9 +666,7 @@ public final class SqlTreeBuilder {
|
||||
* by the select.
|
||||
*/
|
||||
private String[] findExtras() {
|
||||
|
||||
List<String> extras = new ArrayList<>();
|
||||
|
||||
for (String predProp : predicateIncludes) {
|
||||
if (!selectIncludes.contains(predProp)) {
|
||||
extras.add(predProp);
|
||||
|
||||
@@ -24,19 +24,17 @@ import java.util.Set;
|
||||
class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
|
||||
private final STreePropertyAssoc assocBeanProperty;
|
||||
|
||||
private final SpiQuery.TemporalMode temporalMode;
|
||||
private final String prefix;
|
||||
|
||||
private final boolean manyJoin;
|
||||
|
||||
private final boolean pathContainsMany;
|
||||
|
||||
private List<SqlTreeNodeExtraJoin> children;
|
||||
|
||||
SqlTreeNodeExtraJoin(String prefix, STreePropertyAssoc assocBeanProperty, boolean pathContainsMany) {
|
||||
SqlTreeNodeExtraJoin(String prefix, STreePropertyAssoc assocBeanProperty, boolean pathContainsMany, SpiQuery.TemporalMode temporalMode) {
|
||||
this.prefix = prefix;
|
||||
this.assocBeanProperty = assocBeanProperty;
|
||||
this.pathContainsMany = pathContainsMany;
|
||||
this.temporalMode = temporalMode;
|
||||
this.manyJoin = assocBeanProperty instanceof STreePropertyAssocMany;
|
||||
}
|
||||
|
||||
@@ -113,9 +111,7 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
|
||||
@Override
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
|
||||
boolean manyToMany = false;
|
||||
|
||||
if (assocBeanProperty instanceof STreePropertyAssocMany) {
|
||||
STreePropertyAssocMany manyProp = (STreePropertyAssocMany) assocBeanProperty;
|
||||
if (manyProp.hasJoinTable()) {
|
||||
@@ -144,15 +140,16 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
assocBeanProperty.appendFrom(ctx, joinType);
|
||||
}
|
||||
joinType = assocBeanProperty.addJoin(joinType, prefix, ctx);
|
||||
if (assocBeanProperty.isTargetSoftDelete() && temporalMode != SpiQuery.TemporalMode.SOFT_DELETED) {
|
||||
ctx.append(" and ").append(assocBeanProperty.getSoftDeletePredicate(ctx.getTableAlias(prefix)));
|
||||
}
|
||||
}
|
||||
|
||||
if (children != null) {
|
||||
|
||||
if (manyJoin || pathContainsMany) {
|
||||
// if AUTO then make all descendants use OUTER JOIN
|
||||
joinType = joinType.autoToOuter();
|
||||
}
|
||||
|
||||
for (SqlTreeNodeExtraJoin child : children) {
|
||||
child.appendFrom(ctx, joinType);
|
||||
}
|
||||
|
||||
@@ -83,11 +83,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private static final String DEFAULT_QUERY_NAME = "default";
|
||||
|
||||
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
|
||||
private static final FetchConfig FETCH_CACHE = FetchConfig.ofCache();
|
||||
|
||||
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
|
||||
private static final FetchConfig FETCH_QUERY = FetchConfig.ofQuery();
|
||||
|
||||
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
|
||||
private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy();
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
@@ -751,7 +751,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
@Override
|
||||
public NaturalKeyQueryData<T> naturalKey() {
|
||||
|
||||
if (whereExpressions == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -767,7 +766,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -815,7 +813,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
copy.m2mIncludeJoin = m2mIncludeJoin;
|
||||
copy.profilingListener = profilingListener;
|
||||
copy.profileLocation = profileLocation;
|
||||
|
||||
copy.baseTable = baseTable;
|
||||
copy.rootTableAlias = rootTableAlias;
|
||||
copy.distinct = distinct;
|
||||
@@ -978,21 +975,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return setForUpdateWithMode(LockWait.WAIT, LockType.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdate(LockType lockType) {
|
||||
return setForUpdateWithMode(LockWait.WAIT, lockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateNoWait(LockType lockType) {
|
||||
return setForUpdateWithMode(LockWait.NOWAIT, lockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateSkipLocked(LockType lockType) {
|
||||
return setForUpdateWithMode(LockWait.SKIPLOCKED, lockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> forUpdateNoWait() {
|
||||
return setForUpdateWithMode(LockWait.NOWAIT, LockType.DEFAULT);
|
||||
@@ -1100,7 +1082,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
@Override
|
||||
public ObjectGraphNode setOrigin(CallOrigin callOrigin) {
|
||||
|
||||
// create a 'origin' which links this query to the profiling information
|
||||
ObjectGraphOrigin o = new ObjectGraphOrigin(calculateOriginQueryHash(), callOrigin, beanType.getName());
|
||||
parentNode = new ObjectGraphNode(o, null);
|
||||
@@ -1140,81 +1121,81 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
private String planDescription() {
|
||||
StringBuilder sb = new StringBuilder(300);
|
||||
if (type != null) {
|
||||
sb.append("t:").append(type.ordinal());
|
||||
sb.append(type.ordinal());
|
||||
}
|
||||
if (useDocStore) {
|
||||
sb.append(",ds:");
|
||||
sb.append("/ds");
|
||||
}
|
||||
if (beanDescriptor.getDiscValue() != null) {
|
||||
sb.append(",disc:").append(beanDescriptor.getDiscValue());
|
||||
sb.append("/dv").append(beanDescriptor.getDiscValue());
|
||||
}
|
||||
if (temporalMode != SpiQuery.TemporalMode.CURRENT) {
|
||||
sb.append(",temp:").append(temporalMode.ordinal());
|
||||
sb.append("/tm").append(temporalMode.ordinal());
|
||||
if (versionsStart != null) {
|
||||
sb.append(",vb:");
|
||||
sb.append("v");
|
||||
}
|
||||
}
|
||||
if (forUpdate != null) {
|
||||
sb.append(",forUpd:").append(forUpdate.ordinal());
|
||||
sb.append("/fu").append(forUpdate.ordinal());
|
||||
if (lockType != null) {
|
||||
sb.append(",lt:").append(lockType.ordinal());
|
||||
sb.append("t").append(lockType.ordinal());
|
||||
}
|
||||
}
|
||||
if (id != null) {
|
||||
sb.append(",id:");
|
||||
sb.append("/id");
|
||||
}
|
||||
if (manualId) {
|
||||
sb.append(",manId:");
|
||||
sb.append("/md");
|
||||
}
|
||||
if (distinct) {
|
||||
sb.append(",dist:");
|
||||
sb.append("/dt");
|
||||
}
|
||||
if (allowLoadErrors) {
|
||||
sb.append(",allowLoadErrors:");
|
||||
sb.append("/ae");
|
||||
}
|
||||
if (disableLazyLoading) {
|
||||
sb.append(",disLazy:");
|
||||
sb.append("/dl");
|
||||
}
|
||||
if (baseTable != null) {
|
||||
sb.append(",baseTable:").append(baseTable);
|
||||
sb.append("/bt").append(baseTable);
|
||||
}
|
||||
if (rootTableAlias != null) {
|
||||
sb.append(",root:").append(rootTableAlias);
|
||||
sb.append("/ra").append(rootTableAlias);
|
||||
}
|
||||
if (orderBy != null) {
|
||||
sb.append(",orderBy:").append(orderBy.toStringFormat());
|
||||
sb.append("/ob").append(orderBy.toStringFormat());
|
||||
}
|
||||
if (m2mIncludeJoin != null) {
|
||||
sb.append(",m2m:").append(m2mIncludeJoin.getTable());
|
||||
sb.append("/m2").append(m2mIncludeJoin.getTable());
|
||||
}
|
||||
if (mapKey != null) {
|
||||
sb.append(",mapKey:").append(mapKey);
|
||||
sb.append("/mk").append(mapKey);
|
||||
}
|
||||
if (countDistinctOrder != null) {
|
||||
sb.append(",countDistOrd:").append(countDistinctOrder.name());
|
||||
sb.append("/cd").append(countDistinctOrder.name());
|
||||
}
|
||||
if (detail != null) {
|
||||
sb.append(" detail[");
|
||||
sb.append("/d[");
|
||||
detail.queryPlanHash(sb);
|
||||
sb.append("]");
|
||||
}
|
||||
if (bindParams != null) {
|
||||
sb.append(" bindParams[");
|
||||
sb.append("/b[");
|
||||
bindParams.buildQueryPlanHash(sb);
|
||||
sb.append("]");
|
||||
}
|
||||
if (whereExpressions != null) {
|
||||
sb.append(" where[");
|
||||
sb.append("/w[");
|
||||
whereExpressions.queryPlanHash(sb);
|
||||
sb.append("]");
|
||||
}
|
||||
if (havingExpressions != null) {
|
||||
sb.append(" having[");
|
||||
sb.append("/h[");
|
||||
havingExpressions.queryPlanHash(sb);
|
||||
sb.append("]");
|
||||
}
|
||||
if (updateProperties != null) {
|
||||
sb.append(" update[");
|
||||
sb.append("/u[");
|
||||
updateProperties.buildQueryPlanHash(sb);
|
||||
sb.append("]");
|
||||
}
|
||||
@@ -1241,7 +1222,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
*/
|
||||
@Override
|
||||
public CQueryPlanKey prepare(SpiOrmQueryRequest<T> request) {
|
||||
|
||||
prepareExpressions(request);
|
||||
prepareForPaging();
|
||||
queryPlanKey = createQueryPlanKey();
|
||||
@@ -1252,7 +1232,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
* Prepare the expressions (compile sub-queries etc).
|
||||
*/
|
||||
private void prepareExpressions(BeanQueryRequest<?> request) {
|
||||
|
||||
if (whereExpressions != null) {
|
||||
whereExpressions.prepareExpression(request);
|
||||
}
|
||||
@@ -1267,7 +1246,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
* case, this is not a distinct query
|
||||
*/
|
||||
private void prepareForPaging() {
|
||||
|
||||
// add the rawSql statement - if any
|
||||
if (orderByIsEmpty()) {
|
||||
if (rawSql != null && rawSql.getSql() != null) {
|
||||
@@ -1414,6 +1392,26 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectProperties(Set<String> props) {
|
||||
detail.selectProperties(props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetchProperties(String property, Set<String> columns, FetchConfig config) {
|
||||
detail.fetchProperties(property, columns, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectProperties(OrmQueryProperties properties) {
|
||||
detail.selectProperties(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetchProperties(String path, OrmQueryProperties other) {
|
||||
detail.fetchProperties(path, other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> select(String columns) {
|
||||
detail.select(columns);
|
||||
@@ -1678,7 +1676,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
if (bindParams == null) {
|
||||
bindParams = new BindParams();
|
||||
}
|
||||
@@ -1972,7 +1969,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
if (namedParams == null) {
|
||||
namedParams = new HashMap<>();
|
||||
}
|
||||
|
||||
return namedParams.computeIfAbsent(name, ONamedParam::new);
|
||||
}
|
||||
|
||||
@@ -2066,8 +2062,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
cancelled = true;
|
||||
if (cancelableQuery != null) {
|
||||
if (!cancelled && cancelableQuery != null) {
|
||||
cancelled = true;
|
||||
cancelableQuery.cancel();
|
||||
}
|
||||
} finally {
|
||||
@@ -2095,7 +2091,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
*/
|
||||
@Override
|
||||
public Set<String> validate(BeanType<T> desc) {
|
||||
|
||||
SpiExpressionValidation validation = new SpiExpressionValidation(desc);
|
||||
if (whereExpressions != null) {
|
||||
whereExpressions.validate(validation);
|
||||
|
||||
@@ -3,6 +3,9 @@ package io.ebeaninternal.server.querydefn;
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.UpdateQuery;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
/**
|
||||
* Default implementation of UpdateQuery.
|
||||
@@ -10,23 +13,30 @@ import io.ebean.UpdateQuery;
|
||||
public class DefaultUpdateQuery<T> implements UpdateQuery<T> {
|
||||
|
||||
private final OrmUpdateProperties values = new OrmUpdateProperties();
|
||||
|
||||
private final DefaultOrmQuery<T> query;
|
||||
private final BeanDescriptor<T> descriptor;
|
||||
|
||||
public DefaultUpdateQuery(DefaultOrmQuery<T> query) {
|
||||
this.query = query;
|
||||
this.descriptor = query.getBeanDescriptor();
|
||||
query.setUpdateProperties(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateQuery<T> set(String property, Object value) {
|
||||
values.set(property, value);
|
||||
if (value == null) {
|
||||
values.setNull(property);
|
||||
} else {
|
||||
final BeanProperty beanProperty = descriptor.getBeanProperty(property);
|
||||
final ScalarType<Object> scalarType = (beanProperty == null) ? null: beanProperty.getScalarType();
|
||||
values.set(property, value, scalarType);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateQuery<T> setNull(String property) {
|
||||
values.set(property, null);
|
||||
values.setNull(property);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,9 +59,9 @@ public class OrmQueryDetail implements Serializable {
|
||||
* Add a nested OrmQueryDetail to this detail.
|
||||
*/
|
||||
public void addNested(String path, OrmQueryDetail other, FetchConfig config) {
|
||||
fetch(path, other.baseProps.getProperties(), config);
|
||||
fetchProperties(path, other.baseProps, config);
|
||||
for (Map.Entry<String, OrmQueryProperties> entry : other.fetchPaths.entrySet()) {
|
||||
fetch(path + "." + entry.getKey(), entry.getValue().getProperties(), entry.getValue().getFetchConfig());
|
||||
fetchProperties(path + "." + entry.getKey(), entry.getValue(), entry.getValue().getFetchConfig());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,25 +106,20 @@ public class OrmQueryDetail implements Serializable {
|
||||
return p1 == null ? p2 == null : p1.isSameByAutoTune(p2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the detail in string form.
|
||||
*/
|
||||
public String asString() {
|
||||
public String asStringDebug() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!baseProps.isEmpty()) {
|
||||
baseProps.append("select ", sb);
|
||||
if (baseProps.hasProperties()) {
|
||||
baseProps.asStringDebug("select ", sb);
|
||||
}
|
||||
if (fetchPaths != null) {
|
||||
for (OrmQueryProperties join : fetchPaths.values()) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(" ");
|
||||
}
|
||||
join.append("fetch ", sb);
|
||||
join.asStringDebug("fetch ", sb);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -138,8 +133,19 @@ public class OrmQueryDetail implements Serializable {
|
||||
/**
|
||||
* set the properties to include on the base / root entity.
|
||||
*/
|
||||
public void select(String columns) {
|
||||
baseProps = new OrmQueryProperties(null, columns, null);
|
||||
public void select(String properties) {
|
||||
baseProps = new OrmQueryProperties(null, properties, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set select properties that are already parsed.
|
||||
*/
|
||||
public void selectProperties(Set<String> properties) {
|
||||
baseProps = new OrmQueryProperties(null, properties, OrmQueryProperties.DEFAULT_FETCH);
|
||||
}
|
||||
|
||||
void selectProperties(OrmQueryProperties other) {
|
||||
baseProps = new OrmQueryProperties(null, other, OrmQueryProperties.DEFAULT_FETCH);
|
||||
}
|
||||
|
||||
boolean containsProperty(String property) {
|
||||
@@ -150,7 +156,7 @@ public class OrmQueryDetail implements Serializable {
|
||||
* Set the base query properties to be empty.
|
||||
*/
|
||||
public void setEmptyBase() {
|
||||
this.baseProps = new OrmQueryProperties(null, new LinkedHashSet<>());
|
||||
this.baseProps = new OrmQueryProperties(null, Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,10 +273,24 @@ public class OrmQueryDetail implements Serializable {
|
||||
* @param partialProps the properties on the join property to include
|
||||
*/
|
||||
public void fetch(String path, String partialProps, FetchConfig fetchConfig) {
|
||||
|
||||
fetch(new OrmQueryProperties(path, partialProps, fetchConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set fetch properties that are already parsed.
|
||||
*/
|
||||
public void fetchProperties(String path, Set<String> properties, FetchConfig fetchConfig) {
|
||||
fetch(new OrmQueryProperties(path, properties, fetchConfig));
|
||||
}
|
||||
|
||||
void fetchProperties(String path, OrmQueryProperties other) {
|
||||
fetchProperties(path, other, other.getFetchConfig());
|
||||
}
|
||||
|
||||
void fetchProperties(String path, OrmQueryProperties other, FetchConfig fetchConfig) {
|
||||
fetch(new OrmQueryProperties(path, other, fetchConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add for raw sql etc when the properties are already parsed into a set.
|
||||
*/
|
||||
@@ -316,7 +336,7 @@ public class OrmQueryDetail implements Serializable {
|
||||
if (addId) {
|
||||
parentProp = new OrmQueryProperties(parentPath, assocOne.getTargetIdProperty());
|
||||
} else {
|
||||
parentProp = new OrmQueryProperties(parentPath, new LinkedHashSet<>());
|
||||
parentProp = new OrmQueryProperties(parentPath, Collections.emptySet());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class OrmQueryPlanKey implements CQueryPlanKey {
|
||||
|
||||
@Override
|
||||
public CQueryPlanKey withDeleteByIds() {
|
||||
return new OrmQueryPlanKey(description + ":deleteByIds", 0, 0, null);
|
||||
return new OrmQueryPlanKey(description + "/deleteByIds", 0, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+61
-136
@@ -26,27 +26,20 @@ public class OrmQueryProperties implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8785582703966455658L;
|
||||
|
||||
static final FetchConfig DEFAULT_FETCH = new FetchConfig();
|
||||
static final FetchConfig DEFAULT_FETCH = FetchConfig.ofDefault();
|
||||
|
||||
private final String parentPath;
|
||||
private final String path;
|
||||
|
||||
private final String rawProperties;
|
||||
private final String trimmedProperties;
|
||||
|
||||
private final LinkedHashSet<String> included;
|
||||
|
||||
private final boolean allProperties;
|
||||
private final Set<String> included;
|
||||
private final FetchConfig fetchConfig;
|
||||
private final boolean cache;
|
||||
|
||||
/**
|
||||
* Flag set when this fetch path needs to be a query join.
|
||||
*/
|
||||
private boolean markForQueryJoin;
|
||||
|
||||
private boolean cache;
|
||||
|
||||
private boolean readOnly;
|
||||
|
||||
/**
|
||||
* Included bean joins.
|
||||
*/
|
||||
@@ -84,68 +77,51 @@ public class OrmQueryProperties implements Serializable {
|
||||
public OrmQueryProperties(String path) {
|
||||
this.path = path;
|
||||
this.parentPath = SplitName.parent(path);
|
||||
this.rawProperties = null;
|
||||
this.trimmedProperties = null;
|
||||
this.allProperties = false;
|
||||
this.included = null;
|
||||
this.cache = false;
|
||||
this.fetchConfig = DEFAULT_FETCH;
|
||||
}
|
||||
|
||||
public OrmQueryProperties(String path, String rawProperties) {
|
||||
this(path, rawProperties, null);
|
||||
this(path, rawProperties, DEFAULT_FETCH);
|
||||
}
|
||||
|
||||
public OrmQueryProperties(String path, String rawProperties, FetchConfig fetchConfig) {
|
||||
|
||||
OrmQueryPropertiesParser.Response response = OrmQueryPropertiesParser.parse(rawProperties);
|
||||
|
||||
this.path = path;
|
||||
this.parentPath = SplitName.parent(path);
|
||||
this.rawProperties = rawProperties;
|
||||
this.trimmedProperties = response.properties;
|
||||
OrmQueryPropertiesParser.Response response = OrmQueryPropertiesParser.parse(rawProperties);
|
||||
this.allProperties = response.allProperties;
|
||||
this.included = response.included;
|
||||
this.cache = response.cache;
|
||||
this.readOnly = response.readOnly;
|
||||
if (fetchConfig != null) {
|
||||
this.fetchConfig = fetchConfig;
|
||||
if (fetchConfig.isCache()) {
|
||||
this.cache = true;
|
||||
}
|
||||
this.cache = fetchConfig.isCache();
|
||||
} else {
|
||||
this.fetchConfig = response.fetchConfig;
|
||||
this.cache = false;
|
||||
this.fetchConfig = DEFAULT_FETCH;
|
||||
}
|
||||
}
|
||||
|
||||
public OrmQueryProperties(String path, LinkedHashSet<String> parsedProperties) {
|
||||
if (parsedProperties == null) {
|
||||
throw new IllegalArgumentException("parsedProperties is null");
|
||||
}
|
||||
public OrmQueryProperties(String path, Set<String> included) {
|
||||
this(path, included, DEFAULT_FETCH);
|
||||
}
|
||||
|
||||
OrmQueryProperties(String path, Set<String> included, FetchConfig fetchConfig) {
|
||||
this.path = path;
|
||||
this.parentPath = SplitName.parent(path);
|
||||
// for rawSql parsedProperties can be empty (when only fetching Id property)
|
||||
this.included = parsedProperties;
|
||||
this.rawProperties = join(parsedProperties);
|
||||
this.trimmedProperties = rawProperties;
|
||||
this.cache = false;
|
||||
this.readOnly = false;
|
||||
this.fetchConfig = DEFAULT_FETCH;
|
||||
this.included = included;
|
||||
this.allProperties = false;
|
||||
this.fetchConfig = fetchConfig;
|
||||
this.cache = fetchConfig.isCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Join the set of properties into a comma delimited string.
|
||||
*/
|
||||
private String join(LinkedHashSet<String> parsedProperties) {
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
boolean first = true;
|
||||
for (String property : parsedProperties) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(property);
|
||||
}
|
||||
return sb.toString();
|
||||
OrmQueryProperties(String path, OrmQueryProperties other, FetchConfig fetchConfig) {
|
||||
this.path = path;
|
||||
this.parentPath = SplitName.parent(path);
|
||||
this.allProperties = other.allProperties;
|
||||
this.included = other.included;
|
||||
this.cache = other.cache;
|
||||
this.fetchConfig = fetchConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,10 +131,8 @@ public class OrmQueryProperties implements Serializable {
|
||||
this.fetchConfig = sourceFetchConfig;
|
||||
this.parentPath = source.parentPath;
|
||||
this.path = source.path;
|
||||
this.rawProperties = source.rawProperties;
|
||||
this.trimmedProperties = source.trimmedProperties;
|
||||
this.allProperties = source.allProperties;
|
||||
this.cache = source.cache;
|
||||
this.readOnly = source.readOnly;
|
||||
this.filterMany = source.filterMany;
|
||||
this.markForQueryJoin = source.markForQueryJoin;
|
||||
this.included = (source.included == null) ? null : new LinkedHashSet<>(source.included);
|
||||
@@ -181,6 +155,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
/**
|
||||
* Move a OrderBy.Property from the main query to this query join.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
void addSecJoinOrderProperty(OrderBy.Property orderProp) {
|
||||
if (orderBy == null) {
|
||||
orderBy = new OrderBy();
|
||||
@@ -196,7 +171,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
* Return the expressions used to filter on this path. This should be a many path to use this
|
||||
* method.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public <T> SpiExpressionList<T> filterMany(Query<T> rootQuery) {
|
||||
if (filterMany == null) {
|
||||
FilterExprPath exprPath = new FilterExprPath(path);
|
||||
@@ -239,9 +214,8 @@ public class OrmQueryProperties implements Serializable {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void configureBeanQuery(SpiQuery<?> query) {
|
||||
|
||||
if (trimmedProperties != null && !trimmedProperties.isEmpty()) {
|
||||
query.select(trimmedProperties);
|
||||
if (hasProperties()) {
|
||||
query.selectProperties(this);
|
||||
}
|
||||
|
||||
if (filterMany != null) {
|
||||
@@ -257,7 +231,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
for (OrmQueryProperties p : secondaryChildren) {
|
||||
String path = p.getPath();
|
||||
path = path.substring(trimPath);
|
||||
query.fetch(path, p.getProperties(), p.getFetchConfig());
|
||||
query.fetchProperties(path, p);
|
||||
query.setFilterMany(path, p.getFilterManyTrimPath(trimPath));
|
||||
}
|
||||
}
|
||||
@@ -268,37 +242,26 @@ public class OrmQueryProperties implements Serializable {
|
||||
}
|
||||
|
||||
public boolean hasSelectClause() {
|
||||
if ("*".equals(trimmedProperties)) {
|
||||
// explicitly selected all properties
|
||||
return true;
|
||||
}
|
||||
// explicitly selected some properties
|
||||
return included != null || filterMany != null;
|
||||
return allProperties || included != null || filterMany != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the properties and configuration are empty.
|
||||
* Return true if explicit properties have been specified.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return rawProperties == null || rawProperties.isEmpty();
|
||||
boolean hasProperties() {
|
||||
return allProperties || included != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(40);
|
||||
append("", sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String append(String prefix, StringBuilder sb) {
|
||||
public void asStringDebug(String prefix, StringBuilder sb) {
|
||||
sb.append(prefix);
|
||||
if (path != null) {
|
||||
sb.append(path).append(" ");
|
||||
}
|
||||
if (!isEmpty()) {
|
||||
sb.append("(").append(rawProperties).append(")");
|
||||
if (allProperties) {
|
||||
sb.append("(*)");
|
||||
} else if (included != null) {
|
||||
sb.append("(").append(String.join(",", included)).append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
boolean isChild(OrmQueryProperties possibleChild) {
|
||||
@@ -315,17 +278,11 @@ public class OrmQueryProperties implements Serializable {
|
||||
secondaryChildren.add(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the raw properties.
|
||||
*/
|
||||
public String getProperties() {
|
||||
return rawProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this includes all properties on the path.
|
||||
*/
|
||||
public boolean allProperties() {
|
||||
// this is really "default" properties
|
||||
return included == null;
|
||||
}
|
||||
|
||||
@@ -350,10 +307,6 @@ public class OrmQueryProperties implements Serializable {
|
||||
includedBeanJoin.add(propertyName);
|
||||
}
|
||||
|
||||
public Set<String> getSelectInclude() {
|
||||
return included;
|
||||
}
|
||||
|
||||
public Set<String> getSelectQueryJoin() {
|
||||
return secondaryQueryJoins;
|
||||
}
|
||||
@@ -373,11 +326,9 @@ public class OrmQueryProperties implements Serializable {
|
||||
}
|
||||
|
||||
boolean isIncluded(String propName) {
|
||||
|
||||
if (includedBeanJoin != null && includedBeanJoin.contains(propName)) {
|
||||
return false;
|
||||
}
|
||||
// all properties included
|
||||
return included == null || included.contains(propName);
|
||||
}
|
||||
|
||||
@@ -392,53 +343,29 @@ public class OrmQueryProperties implements Serializable {
|
||||
* Return true if this path is a 'query join'.
|
||||
*/
|
||||
public boolean isQueryFetch() {
|
||||
return markForQueryJoin || getQueryFetchBatch() > -1;
|
||||
return markForQueryJoin || cache || fetchConfig.isQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path is a 'fetch join'.
|
||||
*/
|
||||
boolean isFetchJoin() {
|
||||
return !isQueryFetch() && !isLazyFetch();
|
||||
return !markForQueryJoin && fetchConfig.isJoin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path is a lazy fetch.
|
||||
*/
|
||||
boolean isLazyFetch() {
|
||||
return getLazyFetchBatch() > -1;
|
||||
return fetchConfig.isLazy();
|
||||
}
|
||||
|
||||
public int getBatchSize() {
|
||||
return fetchConfig.getBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch size to use for the query join.
|
||||
*/
|
||||
public int getQueryFetchBatch() {
|
||||
return fetchConfig.getQueryBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a query join should eagerly fetch 'all' rather than the 'first'.
|
||||
*/
|
||||
public boolean isQueryFetchAll() {
|
||||
return fetchConfig.isQueryAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch size to use for lazy loading.
|
||||
*/
|
||||
public int getLazyFetchBatch() {
|
||||
return fetchConfig.getLazyBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path has the +readonly option.
|
||||
*/
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path has the +cache option to hit the cache.
|
||||
* Return true if this path should hit the L2 cache.
|
||||
*/
|
||||
public boolean isCache() {
|
||||
return cache;
|
||||
@@ -474,26 +401,24 @@ public class OrmQueryProperties implements Serializable {
|
||||
* Calculate the query plan hash.
|
||||
*/
|
||||
public void queryPlanHash(StringBuilder builder) {
|
||||
|
||||
builder.append("qpp[");
|
||||
builder.append(path);
|
||||
if (included != null){
|
||||
builder.append(" included:").append(included);
|
||||
builder.append("{");
|
||||
if (path != null) {
|
||||
builder.append(path);
|
||||
}
|
||||
if (included != null) {
|
||||
builder.append("/i").append(included);
|
||||
}
|
||||
if (secondaryQueryJoins != null) {
|
||||
builder.append(" secondary:").append(secondaryQueryJoins);
|
||||
builder.append("/s").append(secondaryQueryJoins);
|
||||
}
|
||||
|
||||
if (filterMany != null) {
|
||||
builder.append(" filterMany[");
|
||||
builder.append("/f");
|
||||
filterMany.queryPlanHash(builder);
|
||||
builder.append("]");
|
||||
}
|
||||
|
||||
if (fetchConfig != null) {
|
||||
builder.append(" config:").append(fetchConfig.hashCode());
|
||||
builder.append("/c").append(fetchConfig.hashCode());
|
||||
}
|
||||
builder.append("]");
|
||||
builder.append("}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
-157
@@ -1,47 +1,28 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.FetchConfig;
|
||||
import io.ebeaninternal.server.util.DSelectColumnsParser;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Parses the path properties string.
|
||||
*/
|
||||
class OrmQueryPropertiesParser {
|
||||
final class OrmQueryPropertiesParser {
|
||||
|
||||
private static final Response EMPTY = new Response();
|
||||
private static final Response EMPTY = new Response(false, null);
|
||||
private static final Response ALL = new Response(true, null);
|
||||
|
||||
/**
|
||||
* Immutable response of the parsed properties and options.
|
||||
*/
|
||||
static class Response {
|
||||
|
||||
final boolean readOnly;
|
||||
final boolean cache;
|
||||
final FetchConfig fetchConfig;
|
||||
final String properties;
|
||||
final LinkedHashSet<String> included;
|
||||
final boolean allProperties;
|
||||
final Set<String> included;
|
||||
|
||||
Response(boolean readOnly, boolean cache, int queryFetchBatch, int lazyFetchBatch, String properties, LinkedHashSet<String> included) {
|
||||
this.readOnly = readOnly;
|
||||
this.cache = cache;
|
||||
this.properties = properties;
|
||||
private Response(boolean allProperties, Set<String> included) {
|
||||
this.allProperties = allProperties;
|
||||
this.included = included;
|
||||
if (lazyFetchBatch > -1 || queryFetchBatch > -1) {
|
||||
this.fetchConfig = new FetchConfig().lazy(lazyFetchBatch).query(queryFetchBatch);
|
||||
} else {
|
||||
this.fetchConfig = OrmQueryProperties.DEFAULT_FETCH;
|
||||
}
|
||||
}
|
||||
|
||||
Response() {
|
||||
this.readOnly = false;
|
||||
this.cache = false;
|
||||
this.fetchConfig = OrmQueryProperties.DEFAULT_FETCH;
|
||||
this.properties = "";
|
||||
this.included = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,145 +30,21 @@ class OrmQueryPropertiesParser {
|
||||
* Parses the path properties string returning the parsed properties and options.
|
||||
* In general it is comma delimited with some special strings like +lazy(20).
|
||||
*/
|
||||
public static Response parse(String rawProperties) {
|
||||
return new OrmQueryPropertiesParser(rawProperties).parse();
|
||||
}
|
||||
|
||||
private String inputProperties;
|
||||
|
||||
private String outputProperties = "";
|
||||
private boolean allProperties;
|
||||
private boolean readOnly;
|
||||
private boolean cache;
|
||||
private int queryFetchBatch = -1;
|
||||
private int lazyFetchBatch = -1;
|
||||
|
||||
private OrmQueryPropertiesParser(String inputProperties) {
|
||||
this.inputProperties = inputProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the raw string properties input.
|
||||
*/
|
||||
private Response parse() {
|
||||
|
||||
if (inputProperties == null || inputProperties.isEmpty()) {
|
||||
static Response parse(String rawProperties) {
|
||||
if (rawProperties == null || rawProperties.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
int pos = inputProperties.indexOf("+readonly");
|
||||
if (pos > -1) {
|
||||
inputProperties = inputProperties.replace("+readonly", "");
|
||||
readOnly = true;
|
||||
if (rawProperties.equals("*")) {
|
||||
return ALL;
|
||||
}
|
||||
pos = inputProperties.indexOf("+cache");
|
||||
if (pos > -1) {
|
||||
inputProperties = inputProperties.replace("+cache", "");
|
||||
cache = true;
|
||||
}
|
||||
pos = inputProperties.indexOf("+query");
|
||||
if (pos > -1) {
|
||||
queryFetchBatch = parseBatchHint(pos, "+query");
|
||||
}
|
||||
pos = inputProperties.indexOf("+lazy");
|
||||
if (pos > -1) {
|
||||
lazyFetchBatch = parseBatchHint(pos, "+lazy");
|
||||
}
|
||||
|
||||
LinkedHashSet<String> included = parseIncluded();
|
||||
String properties = (allProperties) ? "*" : outputProperties;
|
||||
return new Response(readOnly, cache, queryFetchBatch, lazyFetchBatch, properties, included);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the include separating by comma or semicolon.
|
||||
*/
|
||||
private LinkedHashSet<String> parseIncluded() {
|
||||
|
||||
inputProperties = inputProperties.trim();
|
||||
if (inputProperties.isEmpty()) {
|
||||
// default properties
|
||||
return null;
|
||||
}
|
||||
if (inputProperties.equals("*")) {
|
||||
// explicit all properties
|
||||
allProperties = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> res = splitRawSelect(inputProperties);
|
||||
|
||||
StringBuilder sb = new StringBuilder(70);
|
||||
LinkedHashSet<String> propertySet = new LinkedHashSet<>(res.size() * 2);
|
||||
|
||||
int count = 0;
|
||||
String temp;
|
||||
for (String re : res) {
|
||||
temp = re.trim();
|
||||
if (!temp.isEmpty()) {
|
||||
if (count > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(temp);
|
||||
propertySet.add(temp);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (propertySet.isEmpty()) {
|
||||
// default properties
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertySet.contains("*")) {
|
||||
// explicit all properties
|
||||
allProperties = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
// partial properties
|
||||
outputProperties = sb.toString();
|
||||
return propertySet;
|
||||
return new Response(false, splitRawSelect(rawProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Split allowing 'dynamic function based properties'.
|
||||
*/
|
||||
private List<String> splitRawSelect(String inputProperties) {
|
||||
private static Set<String> splitRawSelect(String inputProperties) {
|
||||
return DSelectColumnsParser.parse(inputProperties);
|
||||
}
|
||||
|
||||
private int parseBatchHint(int pos, String option) {
|
||||
|
||||
int startPos = pos + option.length();
|
||||
int endPos = findEndPos(startPos, inputProperties);
|
||||
if (endPos == -1) {
|
||||
inputProperties = inputProperties.replace(option, "");
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
String batchParam = inputProperties.substring(startPos + 1, endPos);
|
||||
|
||||
if (endPos + 1 >= inputProperties.length()) {
|
||||
inputProperties = inputProperties.substring(0, pos);
|
||||
} else {
|
||||
inputProperties = inputProperties.substring(0, pos) + inputProperties.substring(endPos + 1);
|
||||
}
|
||||
return Integer.parseInt(batchParam);
|
||||
}
|
||||
}
|
||||
|
||||
private int findEndPos(int pos, String props) {
|
||||
|
||||
if (pos < props.length()) {
|
||||
if (props.charAt(pos) == '(') {
|
||||
int endPara = props.indexOf(')', pos + 1);
|
||||
if (endPara == -1) {
|
||||
throw new RuntimeException("Error could not find ')' in " + props + " after position " + pos);
|
||||
}
|
||||
return endPara;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-11
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebeaninternal.server.deploy.DeployParser;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
@@ -53,9 +54,11 @@ public class OrmUpdateProperties {
|
||||
private static class SimpleValue extends Value {
|
||||
|
||||
final Object value;
|
||||
final ScalarType<Object> scalarType;
|
||||
|
||||
SimpleValue(Object value) {
|
||||
SimpleValue(Object value, ScalarType<Object> scalarType) {
|
||||
this.value = value;
|
||||
this.scalarType = scalarType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,7 +73,11 @@ public class OrmUpdateProperties {
|
||||
|
||||
@Override
|
||||
public void bind(Binder binder, DataBind dataBind) throws SQLException {
|
||||
binder.bindObject(dataBind, value);
|
||||
if (scalarType != null) {
|
||||
scalarType.bind(dataBind, value);
|
||||
} else {
|
||||
binder.bindObject(dataBind, value);
|
||||
}
|
||||
dataBind.append(value).append(",");
|
||||
}
|
||||
}
|
||||
@@ -115,16 +122,12 @@ public class OrmUpdateProperties {
|
||||
*/
|
||||
private final LinkedHashMap<String, Value> values = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Normal set property.
|
||||
*/
|
||||
public void set(String propertyName, Object value) {
|
||||
if (value == null) {
|
||||
values.put(propertyName, NULL_VALUE);
|
||||
public void set(String propertyName, Object value, ScalarType<Object> scalarType) {
|
||||
values.put(propertyName, new SimpleValue(value, scalarType));
|
||||
}
|
||||
|
||||
} else {
|
||||
values.put(propertyName, new SimpleValue(value));
|
||||
}
|
||||
public void setNull(String propertyName) {
|
||||
values.put(propertyName, NULL_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.util.DSelectColumnsParser;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@@ -28,11 +29,8 @@ final class DRawSqlColumnsParser {
|
||||
}
|
||||
|
||||
private ColumnMapping parse() {
|
||||
|
||||
List<String> columnList = DSelectColumnsParser.parse(sqlSelect);
|
||||
|
||||
Set<String> columnList = DSelectColumnsParser.parse(sqlSelect);
|
||||
List<ColumnMapping.Column> columns = new ArrayList<>(columnList.size());
|
||||
|
||||
for (String rawColumn : columnList) {
|
||||
columns.add(parseColumn(rawColumn));
|
||||
}
|
||||
@@ -40,7 +38,6 @@ final class DRawSqlColumnsParser {
|
||||
}
|
||||
|
||||
private ColumnMapping.Column parseColumn(String colInfo) {
|
||||
|
||||
String[] split = COLINFO_SPLIT.split(colInfo);
|
||||
if (split.length > 1) {
|
||||
ArrayList<String> tmp = new ArrayList<>(split.length);
|
||||
|
||||
@@ -10,9 +10,14 @@ import java.util.UUID;
|
||||
interface ArrayElementConverter<T> {
|
||||
|
||||
/**
|
||||
* Convert the array element to the logical type.
|
||||
* Convert element it's json serialized form.
|
||||
*/
|
||||
T toElement(Object rawValue);
|
||||
T fromSerialized(Object rawValue);
|
||||
|
||||
/**
|
||||
* Convert the array element from it's DB array form.
|
||||
*/
|
||||
T fromDbArray(Object rawValue);
|
||||
|
||||
default Object[] toDbArray(Object[] objects) {
|
||||
return objects;
|
||||
@@ -46,7 +51,12 @@ interface ArrayElementConverter<T> {
|
||||
class LongConverter implements ArrayElementConverter<Long> {
|
||||
|
||||
@Override
|
||||
public Long toElement(Object rawValue) {
|
||||
public Long fromSerialized(Object rawValue) {
|
||||
return fromDbArray(rawValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long fromDbArray(Object rawValue) {
|
||||
if (rawValue instanceof Long) {
|
||||
return (Long) rawValue;
|
||||
} else {
|
||||
@@ -58,7 +68,12 @@ interface ArrayElementConverter<T> {
|
||||
class IntegerConverter implements ArrayElementConverter<Integer> {
|
||||
|
||||
@Override
|
||||
public Integer toElement(Object rawValue) {
|
||||
public Integer fromSerialized(Object rawValue) {
|
||||
return ((Number) rawValue).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer fromDbArray(Object rawValue) {
|
||||
if (rawValue instanceof Integer) {
|
||||
return (Integer) rawValue;
|
||||
} else {
|
||||
@@ -70,7 +85,12 @@ interface ArrayElementConverter<T> {
|
||||
class DoubleConverter implements ArrayElementConverter<Double> {
|
||||
|
||||
@Override
|
||||
public Double toElement(Object rawValue) {
|
||||
public Double fromSerialized(Object rawValue) {
|
||||
return fromDbArray(rawValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double fromDbArray(Object rawValue) {
|
||||
if (rawValue instanceof Double) {
|
||||
return (Double) rawValue;
|
||||
} else {
|
||||
@@ -82,21 +102,34 @@ interface ArrayElementConverter<T> {
|
||||
/**
|
||||
* String converter (noop based).
|
||||
*/
|
||||
class StringConverter extends NoopConverter<String> {
|
||||
class StringConverter implements ArrayElementConverter<String> {
|
||||
|
||||
public String fromDbArray(Object rawValue) {
|
||||
return (String) rawValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromSerialized(Object rawValue) {
|
||||
return (String) rawValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UUID converter (noop based).
|
||||
* UUID converter.
|
||||
*/
|
||||
class UuidConverter extends NoopConverter<UUID> {
|
||||
}
|
||||
class UuidConverter implements ArrayElementConverter<UUID> {
|
||||
|
||||
class NoopConverter<T> implements ArrayElementConverter<T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T toElement(Object rawValue) {
|
||||
return (T) rawValue;
|
||||
public java.util.UUID fromSerialized(Object rawValue) {
|
||||
return java.util.UUID.fromString((String)rawValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.UUID fromDbArray(Object rawValue) {
|
||||
if (rawValue instanceof UUID) {
|
||||
return (java.util.UUID) rawValue;
|
||||
}
|
||||
return java.util.UUID.fromString(rawValue.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +146,12 @@ interface ArrayElementConverter<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toElement(Object rawValue) {
|
||||
public Object fromSerialized(Object rawValue) {
|
||||
return scalarType.parse((String) rawValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromDbArray(Object rawValue) {
|
||||
return scalarType.toBeanType(rawValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.core.type.ScalarType;
|
||||
|
||||
public class ArrayElementConverterEnum implements ArrayElementConverter<String> {
|
||||
|
||||
final ScalarType<?> scalarType;
|
||||
|
||||
final Class<? extends Enum<?>> valueType1;
|
||||
|
||||
public ArrayElementConverterEnum(ScalarType<?> scalarType, Class<? extends Enum<?>> valueType1) {
|
||||
this.scalarType = scalarType;
|
||||
this.valueType1 = valueType1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toElement(Object rawValue) {
|
||||
// FIXME: Don't understand for what is this.
|
||||
Enum<?>[] enumConstants = valueType1.getEnumConstants();
|
||||
if (scalarType == null) {
|
||||
return rawValue.toString();
|
||||
}
|
||||
return scalarType.format(rawValue);
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ public class ScalarTypeArrayList extends ScalarTypeArrayBase<List> implements Sc
|
||||
if (element == null) {
|
||||
list.add(null);
|
||||
} else {
|
||||
list.add(converter.toElement(element));
|
||||
list.add(converter.fromDbArray(element));
|
||||
}
|
||||
}
|
||||
return new ModifyAwareList(list);
|
||||
@@ -145,15 +145,27 @@ public class ScalarTypeArrayList extends ScalarTypeArrayBase<List> implements Sc
|
||||
@Override
|
||||
public List parse(String value) {
|
||||
try {
|
||||
return EJson.parseList(value, false);
|
||||
return convert(EJson.parseList(value, false));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", value, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from the json types to the proper scalar types (uuid, enum, double etc)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private List convert(List<Object> rawList) {
|
||||
List list = new ArrayList(rawList.size());
|
||||
for (Object rawVal : rawList) {
|
||||
list.add(converter.fromSerialized(rawVal));
|
||||
}
|
||||
return new ModifyAwareList(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List jsonRead(JsonParser parser) throws IOException {
|
||||
return EJson.parseList(parser, parser.getCurrentToken());
|
||||
return convert(EJson.parseList(parser, parser.getCurrentToken()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -101,7 +102,7 @@ public class ScalarTypeArraySet extends ScalarTypeArrayBase<Set> implements Scal
|
||||
protected Set fromArray(Object[] array1) {
|
||||
Set set = new LinkedHashSet();
|
||||
for (Object element : array1) {
|
||||
set.add(converter.toElement(element));
|
||||
set.add(converter.fromDbArray(element));
|
||||
}
|
||||
return new ModifyAwareSet(set);
|
||||
}
|
||||
@@ -140,15 +141,26 @@ public class ScalarTypeArraySet extends ScalarTypeArrayBase<Set> implements Scal
|
||||
@Override
|
||||
public Set parse(String value) {
|
||||
try {
|
||||
return EJson.parseSet(value, false);
|
||||
return convert(EJson.parseList(value, false));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as Set", value, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from the json types to the proper scalar types (uuid, enum, double etc)
|
||||
*/
|
||||
private Set convert(List<Object> rawList) {
|
||||
Set asSet = new LinkedHashSet();
|
||||
for (Object rawVal : rawList) {
|
||||
asSet.add(converter.fromSerialized(rawVal));
|
||||
}
|
||||
return new ModifyAwareSet(asSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set jsonRead(JsonParser parser) throws IOException {
|
||||
return EJson.parseSet(parser, parser.getCurrentToken());
|
||||
return convert(EJson.parseList(parser, parser.getCurrentToken()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,9 +37,10 @@ public class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
|
||||
return LocalDate.fromDateFields(date);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public Date convertToDate(LocalDate value) {
|
||||
return new java.sql.Date(convertToMillis(value));
|
||||
return new Date(value .getYear() - 1900, value .getMonthOfYear() - 1, value .getDayOfMonth());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,7 +54,7 @@ public class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
|
||||
@Override
|
||||
public LocalDate toBeanType(Object value) {
|
||||
if (value instanceof java.util.Date) {
|
||||
return convertFromMillis(((java.util.Date) value).getTime());
|
||||
return LocalDate.fromDateFields((java.util.Date)value);
|
||||
}
|
||||
return (LocalDate) value;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Splits a select clause into 'logical columns' taking into account functions and quotes.
|
||||
@@ -14,7 +14,7 @@ public final class DSelectColumnsParser {
|
||||
|
||||
private int pos;
|
||||
|
||||
public static List<String> parse(String sqlSelect) {
|
||||
public static Set<String> parse(String sqlSelect) {
|
||||
return new DSelectColumnsParser(sqlSelect).parse();
|
||||
}
|
||||
|
||||
@@ -23,9 +23,8 @@ public final class DSelectColumnsParser {
|
||||
this.end = selectClause.length();
|
||||
}
|
||||
|
||||
private List<String> parse() {
|
||||
|
||||
ArrayList<String> columns = new ArrayList<>();
|
||||
private Set<String> parse() {
|
||||
LinkedHashSet<String> columns = new LinkedHashSet<>();
|
||||
while (pos <= end) {
|
||||
columns.add(nextColumnInfo());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- Generated by ebean unknown at 2020-12-07T09:40:50.461215Z
|
||||
-- Generated by ebean unknown at 2021-02-12T03:32:14.253435800Z
|
||||
create table asimple_bean (
|
||||
id bigint generated by default as identity not null,
|
||||
name varchar(255),
|
||||
@@ -486,6 +486,13 @@ create table child_person (
|
||||
constraint pk_child_person primary key (identifier)
|
||||
);
|
||||
|
||||
create table child_with_cache (
|
||||
id bigint generated by default as identity not null,
|
||||
name varchar(255),
|
||||
address varchar(255),
|
||||
constraint pk_child_with_cache primary key (id)
|
||||
);
|
||||
|
||||
create table cke_client (
|
||||
cod_cpny integer not null,
|
||||
cod_client varchar(100) not null,
|
||||
@@ -1168,6 +1175,12 @@ create table ebasic_json_unmapped (
|
||||
constraint pk_ebasic_json_unmapped primary key (id)
|
||||
);
|
||||
|
||||
create table e_basic_log (
|
||||
id bigint generated by default as identity not null,
|
||||
name varchar(255),
|
||||
constraint pk_e_basic_log primary key (id)
|
||||
);
|
||||
|
||||
create table e_basic_ndc (
|
||||
id integer generated by default as identity not null,
|
||||
name varchar(255),
|
||||
@@ -1227,6 +1240,14 @@ create table e_basic_with_ex (
|
||||
constraint pk_e_basic_with_ex primary key (id)
|
||||
);
|
||||
|
||||
create table e_basic_withlog (
|
||||
id bigint generated by default as identity not null,
|
||||
name varchar(255),
|
||||
deleted boolean default false not null,
|
||||
version bigint not null,
|
||||
constraint pk_e_basic_withlog primary key (id)
|
||||
);
|
||||
|
||||
create table e_basicverucon (
|
||||
id integer generated by default as identity not null,
|
||||
name varchar(127),
|
||||
@@ -2256,6 +2277,29 @@ create table looney (
|
||||
constraint pk_looney primary key (id)
|
||||
);
|
||||
|
||||
create table m2_mcache_child (
|
||||
id integer generated by default as identity not null,
|
||||
name varchar(255),
|
||||
constraint pk_m2_mcache_child primary key (id)
|
||||
);
|
||||
|
||||
create table m2_mcache_master (
|
||||
id integer generated by default as identity not null,
|
||||
constraint pk_m2_mcache_master primary key (id)
|
||||
);
|
||||
|
||||
create table m2mcache_set1 (
|
||||
m2_mcache_master_id integer not null,
|
||||
m2_mcache_child_id integer not null,
|
||||
constraint pk_m2mcache_set1 primary key (m2_mcache_master_id,m2_mcache_child_id)
|
||||
);
|
||||
|
||||
create table m2mcache_set2 (
|
||||
m2_mcache_master_id integer not null,
|
||||
m2_mcache_child_id integer not null,
|
||||
constraint pk_m2mcache_set2 primary key (m2_mcache_master_id,m2_mcache_child_id)
|
||||
);
|
||||
|
||||
create table maddress (
|
||||
id uuid not null,
|
||||
street varchar(255),
|
||||
@@ -3230,6 +3274,19 @@ create table e_save_test_c (
|
||||
constraint pk_e_save_test_c primary key (id)
|
||||
);
|
||||
|
||||
create table parent_a (
|
||||
id bigint generated by default as identity not null,
|
||||
child_id bigint,
|
||||
name varchar(255),
|
||||
constraint pk_parent_a primary key (id)
|
||||
);
|
||||
|
||||
create table parent_b (
|
||||
id bigint generated by default as identity not null,
|
||||
child_id bigint,
|
||||
constraint pk_parent_b primary key (id)
|
||||
);
|
||||
|
||||
create table parent_person (
|
||||
identifier integer generated by default as identity not null,
|
||||
name varchar(255),
|
||||
@@ -3361,6 +3418,15 @@ create table person_cache_info (
|
||||
constraint pk_person_cache_info primary key (person_id)
|
||||
);
|
||||
|
||||
create table persona (
|
||||
id bigint generated by default as identity not null,
|
||||
persona varchar(255),
|
||||
person_oid bigint,
|
||||
version integer not null,
|
||||
constraint uq_persona_person_oid unique (person_oid),
|
||||
constraint pk_persona primary key (id)
|
||||
);
|
||||
|
||||
create table phones (
|
||||
id bigint generated by default as identity not null,
|
||||
phone_number varchar(7) not null,
|
||||
@@ -4574,6 +4640,18 @@ alter table la_attr_value_attribute add constraint fk_la_attr_value_attribute_at
|
||||
create index ix_looney_tune_id on looney (tune_id);
|
||||
alter table looney add constraint fk_looney_tune_id foreign key (tune_id) references tune (id) on delete restrict on update restrict;
|
||||
|
||||
create index ix_m2mcache_set1_m2_mcache_master on m2mcache_set1 (m2_mcache_master_id);
|
||||
alter table m2mcache_set1 add constraint fk_m2mcache_set1_m2_mcache_master foreign key (m2_mcache_master_id) references m2_mcache_master (id) on delete restrict on update restrict;
|
||||
|
||||
create index ix_m2mcache_set1_m2_mcache_child on m2mcache_set1 (m2_mcache_child_id);
|
||||
alter table m2mcache_set1 add constraint fk_m2mcache_set1_m2_mcache_child foreign key (m2_mcache_child_id) references m2_mcache_child (id) on delete restrict on update restrict;
|
||||
|
||||
create index ix_m2mcache_set2_m2_mcache_master on m2mcache_set2 (m2_mcache_master_id);
|
||||
alter table m2mcache_set2 add constraint fk_m2mcache_set2_m2_mcache_master foreign key (m2_mcache_master_id) references m2_mcache_master (id) on delete restrict on update restrict;
|
||||
|
||||
create index ix_m2mcache_set2_m2_mcache_child on m2mcache_set2 (m2_mcache_child_id);
|
||||
alter table m2mcache_set2 add constraint fk_m2mcache_set2_m2_mcache_child foreign key (m2_mcache_child_id) references m2_mcache_child (id) on delete restrict on update restrict;
|
||||
|
||||
create index ix_mcontact_customer_id on mcontact (customer_id);
|
||||
alter table mcontact add constraint fk_mcontact_customer_id foreign key (customer_id) references mcustomer (id) on delete restrict on update restrict;
|
||||
|
||||
@@ -4796,6 +4874,12 @@ alter table rawinherit_parent_rawinherit_data add constraint fk_rawinherit_paren
|
||||
create index ix_rawinherit_parent_rawinherit_data_rawinherit_data on rawinherit_parent_rawinherit_data (rawinherit_data_id);
|
||||
alter table rawinherit_parent_rawinherit_data add constraint fk_rawinherit_parent_rawinherit_data_rawinherit_data foreign key (rawinherit_data_id) references rawinherit_data (id) on delete restrict on update restrict;
|
||||
|
||||
create index ix_parent_a_child_id on parent_a (child_id);
|
||||
alter table parent_a add constraint fk_parent_a_child_id foreign key (child_id) references child_with_cache (id) on delete restrict on update restrict;
|
||||
|
||||
create index ix_parent_b_child_id on parent_b (child_id);
|
||||
alter table parent_b add constraint fk_parent_b_child_id foreign key (child_id) references child_with_cache (id) on delete restrict on update restrict;
|
||||
|
||||
create index ix_parent_person_some_bean_id on parent_person (some_bean_id);
|
||||
alter table parent_person add constraint fk_parent_person_some_bean_id foreign key (some_bean_id) references e_basic (id) on delete restrict on update restrict;
|
||||
|
||||
@@ -4829,6 +4913,8 @@ alter table person add constraint fk_person_default_address_oid foreign key (def
|
||||
create index ix_person_cache_email_person_info_person_id on person_cache_email (person_info_person_id);
|
||||
alter table person_cache_email add constraint fk_person_cache_email_person_info_person_id foreign key (person_info_person_id) references person_cache_info (person_id) on delete restrict on update restrict;
|
||||
|
||||
alter table persona add constraint fk_persona_person_oid foreign key (person_oid) references person (oid) on delete restrict on update restrict;
|
||||
|
||||
create index ix_phones_person_id on phones (person_id);
|
||||
alter table phones add constraint fk_phones_person_id foreign key (person_id) references persons (id) on delete restrict on update restrict;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- Generated by ebean unknown at 2020-12-07T09:40:50.461215Z
|
||||
-- Generated by ebean unknown at 2021-02-12T03:32:14.253435800Z
|
||||
alter table bar drop constraint if exists fk_bar_foo_id;
|
||||
drop index if exists ix_bar_foo_id;
|
||||
|
||||
@@ -417,6 +417,18 @@ drop index if exists ix_la_attr_value_attribute_attribute;
|
||||
alter table looney drop constraint if exists fk_looney_tune_id;
|
||||
drop index if exists ix_looney_tune_id;
|
||||
|
||||
alter table m2mcache_set1 drop constraint if exists fk_m2mcache_set1_m2_mcache_master;
|
||||
drop index if exists ix_m2mcache_set1_m2_mcache_master;
|
||||
|
||||
alter table m2mcache_set1 drop constraint if exists fk_m2mcache_set1_m2_mcache_child;
|
||||
drop index if exists ix_m2mcache_set1_m2_mcache_child;
|
||||
|
||||
alter table m2mcache_set2 drop constraint if exists fk_m2mcache_set2_m2_mcache_master;
|
||||
drop index if exists ix_m2mcache_set2_m2_mcache_master;
|
||||
|
||||
alter table m2mcache_set2 drop constraint if exists fk_m2mcache_set2_m2_mcache_child;
|
||||
drop index if exists ix_m2mcache_set2_m2_mcache_child;
|
||||
|
||||
alter table mcontact drop constraint if exists fk_mcontact_customer_id;
|
||||
drop index if exists ix_mcontact_customer_id;
|
||||
|
||||
@@ -639,6 +651,12 @@ drop index if exists ix_rawinherit_parent_rawinherit_data_rawinherit_parent;
|
||||
alter table rawinherit_parent_rawinherit_data drop constraint if exists fk_rawinherit_parent_rawinherit_data_rawinherit_data;
|
||||
drop index if exists ix_rawinherit_parent_rawinherit_data_rawinherit_data;
|
||||
|
||||
alter table parent_a drop constraint if exists fk_parent_a_child_id;
|
||||
drop index if exists ix_parent_a_child_id;
|
||||
|
||||
alter table parent_b drop constraint if exists fk_parent_b_child_id;
|
||||
drop index if exists ix_parent_b_child_id;
|
||||
|
||||
alter table parent_person drop constraint if exists fk_parent_person_some_bean_id;
|
||||
drop index if exists ix_parent_person_some_bean_id;
|
||||
|
||||
@@ -672,6 +690,8 @@ drop index if exists ix_person_default_address_oid;
|
||||
alter table person_cache_email drop constraint if exists fk_person_cache_email_person_info_person_id;
|
||||
drop index if exists ix_person_cache_email_person_info_person_id;
|
||||
|
||||
alter table persona drop constraint if exists fk_persona_person_oid;
|
||||
|
||||
alter table phones drop constraint if exists fk_phones_person_id;
|
||||
drop index if exists ix_phones_person_id;
|
||||
|
||||
@@ -970,6 +990,8 @@ drop table if exists e_save_test_d;
|
||||
|
||||
drop table if exists child_person;
|
||||
|
||||
drop table if exists child_with_cache;
|
||||
|
||||
drop table if exists cke_client;
|
||||
|
||||
drop table if exists cke_user;
|
||||
@@ -1131,6 +1153,8 @@ drop table if exists ebasic_json_node_varchar;
|
||||
|
||||
drop table if exists ebasic_json_unmapped;
|
||||
|
||||
drop table if exists e_basic_log;
|
||||
|
||||
drop table if exists e_basic_ndc;
|
||||
|
||||
drop table if exists ebasic_no_sdchild;
|
||||
@@ -1145,6 +1169,8 @@ drop table if exists e_basic_withlife;
|
||||
|
||||
drop table if exists e_basic_with_ex;
|
||||
|
||||
drop table if exists e_basic_withlog;
|
||||
|
||||
drop table if exists e_basicverucon;
|
||||
|
||||
drop table if exists ecache_child;
|
||||
@@ -1463,6 +1489,14 @@ drop table if exists la_attr_value_attribute;
|
||||
|
||||
drop table if exists looney;
|
||||
|
||||
drop table if exists m2_mcache_child;
|
||||
|
||||
drop table if exists m2_mcache_master;
|
||||
|
||||
drop table if exists m2mcache_set1;
|
||||
|
||||
drop table if exists m2mcache_set2;
|
||||
|
||||
drop table if exists maddress;
|
||||
|
||||
drop table if exists mcontact;
|
||||
@@ -1715,6 +1749,10 @@ drop table if exists rawinherit_parent_rawinherit_data;
|
||||
|
||||
drop table if exists e_save_test_c;
|
||||
|
||||
drop table if exists parent_a;
|
||||
|
||||
drop table if exists parent_b;
|
||||
|
||||
drop table if exists parent_person;
|
||||
|
||||
drop table if exists c_participation;
|
||||
@@ -1745,6 +1783,8 @@ drop table if exists person_cache_email;
|
||||
|
||||
drop table if exists person_cache_info;
|
||||
|
||||
drop table if exists persona;
|
||||
|
||||
drop table if exists phones;
|
||||
|
||||
drop table if exists e_position;
|
||||
|
||||
@@ -8,151 +8,110 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class FetchConfigTest {
|
||||
|
||||
@Test
|
||||
public void testLazy() throws Exception {
|
||||
|
||||
public void testLazy() {
|
||||
FetchConfig config = new FetchConfig().lazy();
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(0);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(-1);
|
||||
assertThat(config.isQueryAll()).isEqualTo(false);
|
||||
assertThat(config.getBatchSize()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazy_withParameter() throws Exception {
|
||||
|
||||
public void testLazy_withParameter() {
|
||||
FetchConfig config = new FetchConfig().lazy(50);
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(50);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(-1);
|
||||
assertThat(config.isQueryAll()).isEqualTo(false);
|
||||
assertThat(config.getBatchSize()).isEqualTo(50);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuery() throws Exception {
|
||||
|
||||
public void testQuery() {
|
||||
FetchConfig config = new FetchConfig().query();
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(-1);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(0);
|
||||
assertThat(config.isQueryAll()).isEqualTo(true);
|
||||
assertThat(config.getBatchSize()).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuery_withParameter() throws Exception {
|
||||
|
||||
public void testQuery_withParameter() {
|
||||
FetchConfig config = new FetchConfig().query(50);
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(-1);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(50);
|
||||
assertThat(config.isQueryAll()).isEqualTo(true);
|
||||
assertThat(config.getBatchSize()).isEqualTo(50);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryFirst() throws Exception {
|
||||
|
||||
public void testQueryFirst() {
|
||||
FetchConfig config = new FetchConfig().queryFirst(50);
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(-1);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(50);
|
||||
assertThat(config.isQueryAll()).isEqualTo(false);
|
||||
assertThat(config.getBatchSize()).isEqualTo(50);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryAndLazy_withParameters() throws Exception {
|
||||
|
||||
FetchConfig config = new FetchConfig().query(50).lazy(10);
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(10);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(50);
|
||||
assertThat(config.isQueryAll()).isEqualTo(false);
|
||||
public void testQueryAndLazy_withParameters() {
|
||||
FetchConfig config = FetchConfig.ofLazy(10);
|
||||
assertThat(config.getBatchSize()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryAndLazy() throws Exception {
|
||||
|
||||
FetchConfig config = new FetchConfig().query(50).lazy();
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(0);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(50);
|
||||
assertThat(config.isQueryAll()).isEqualTo(false);
|
||||
public void testQueryAndLazy() {
|
||||
FetchConfig config = FetchConfig.ofQuery(50);
|
||||
assertThat(config.getBatchSize()).isEqualTo(50);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEquals_when_noOptions() throws Exception {
|
||||
|
||||
public void testEquals_when_noOptions() {
|
||||
assertSame(new FetchConfig(), new FetchConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_query_50_lazy_40() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig().query(50).lazy(40), new FetchConfig().query(50).lazy(40));
|
||||
public void testEquals_when_query_50_lazy_40() {
|
||||
assertSame(new FetchConfig().query(50), FetchConfig.ofQuery(50));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_query_50_lazy() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig().query(50).lazy(), new FetchConfig().query(50).lazy());
|
||||
public void testEquals_when_query_50_lazy() {
|
||||
assertSame(new FetchConfig().lazy(), FetchConfig.ofLazy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_query_50() throws Exception {
|
||||
|
||||
public void testEquals_when_query_50() {
|
||||
assertSame(new FetchConfig().query(50), new FetchConfig().query(50));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_queryFirst_50_lazy_40() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig().queryFirst(50).lazy(40), new FetchConfig().queryFirst(50).lazy(40));
|
||||
public void testEquals_when_queryFirst_50_lazy_40() {
|
||||
assertSame(new FetchConfig().queryFirst(50).lazy(40), FetchConfig.ofLazy(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_queryFirst_50_lazy() throws Exception {
|
||||
|
||||
public void testEquals_when_queryFirst_50_lazy() {
|
||||
assertSame(new FetchConfig().queryFirst(50).lazy(), new FetchConfig().queryFirst(50).lazy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_queryFirst_50() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig().queryFirst(50), new FetchConfig().queryFirst(50));
|
||||
public void testEquals_when_queryFirst_50() {
|
||||
assertSame(new FetchConfig().queryFirst(50), FetchConfig.ofQuery(50));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_query_50() throws Exception {
|
||||
|
||||
public void testNotEquals_when_query_50() {
|
||||
assertDifferent(new FetchConfig().query(50), new FetchConfig().query(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_query_50_lazy() throws Exception {
|
||||
|
||||
public void testNotEquals_when_query_50_lazy() {
|
||||
assertDifferent(new FetchConfig().query(50), new FetchConfig().query(50).lazy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_query_50_lazy_40() throws Exception {
|
||||
|
||||
public void testNotEquals_when_query_50_lazy_40() {
|
||||
assertDifferent(new FetchConfig().query(50), new FetchConfig().query(50).lazy(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_queryFirst_50() throws Exception {
|
||||
|
||||
public void testNotEquals_when_queryFirst_50() {
|
||||
assertDifferent(new FetchConfig().queryFirst(50), new FetchConfig().queryFirst(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_queryFirst_50_lazy() throws Exception {
|
||||
|
||||
public void testNotEquals_when_queryFirst_50_lazy() {
|
||||
assertDifferent(new FetchConfig().queryFirst(50), new FetchConfig().queryFirst(50).lazy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_queryFirst_50_lazy_40() throws Exception {
|
||||
|
||||
public void testNotEquals_when_queryFirst_50_lazy_40() {
|
||||
assertDifferent(new FetchConfig().queryFirst(50), new FetchConfig().queryFirst(50).lazy(40));
|
||||
}
|
||||
|
||||
@@ -161,7 +120,6 @@ public class FetchConfigTest {
|
||||
assertThat(v1.hashCode()).isNotEqualTo(v2.hashCode());
|
||||
}
|
||||
|
||||
|
||||
void assertSame(FetchConfig v1, FetchConfig v2) {
|
||||
assertThat(v1).isEqualTo(v2);
|
||||
assertThat(v1.hashCode()).isEqualTo(v2.hashCode());
|
||||
|
||||
@@ -177,7 +177,7 @@ public class BeanTypeTest {
|
||||
beanType(Order.class).docStore().applyPath(orderQuery);
|
||||
|
||||
OrmQueryDetail detail = orderQuery.getDetail();
|
||||
assertThat(detail.getChunk("customer", false).getSelectInclude()).containsExactly("id", "name");
|
||||
assertThat(detail.getChunk("customer", false).getIncluded()).containsExactly("id", "name");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
|
||||
+7
-7
@@ -76,7 +76,7 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
|
||||
public void when_extra_queryFetchToMany_then_same() {
|
||||
|
||||
assertDifferent(detail(query().select("id,name").fetch("customer")),
|
||||
detail(query().select("id,name").fetch("customer").fetch("details", new FetchConfig().query())));
|
||||
detail(query().select("id,name").fetch("customer").fetch("details", FetchConfig.ofQuery())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -84,7 +84,7 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
|
||||
|
||||
// with the fetch of customer the foreign key must be added to the root query
|
||||
assertDifferent(detail(query().select("id,name")),
|
||||
detail(query().select("id,name").fetch("customer", new FetchConfig().query())));
|
||||
detail(query().select("id,name").fetch("customer", FetchConfig.ofQuery())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,7 +99,7 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
|
||||
|
||||
DefaultOrmQuery<Order> query1 = (DefaultOrmQuery<Order>) Ebean.find(Order.class)
|
||||
.select("status, shipDate")
|
||||
.fetch("details", "orderQty, unitPrice", new FetchConfig().query())
|
||||
.fetchQuery("details", "orderQty, unitPrice")
|
||||
.fetch("details.product", "sku, name");
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("customer", "name")
|
||||
.fetch("details", new FetchConfig().query());
|
||||
.fetchQuery("details");
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
@@ -173,7 +173,7 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("customer", "name")
|
||||
.fetch("details", new FetchConfig().lazy());
|
||||
.fetch("details", FetchConfig.ofLazy());
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
@@ -201,7 +201,7 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("customer", "name")
|
||||
.fetch("details", new FetchConfig().lazy())
|
||||
.fetchLazy("details")
|
||||
.fetch("details.product");
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
@@ -230,7 +230,7 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("details", new FetchConfig().query())
|
||||
.fetchQuery("details")
|
||||
.fetch("details.product")
|
||||
.fetch("customer", "name");
|
||||
|
||||
|
||||
@@ -624,7 +624,7 @@ public class EqlParserTest extends BaseTestCase {
|
||||
public void fetch_query50_asHint() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
Query<Customer> query = parse("fetch billingAddress (+query(50),city)");
|
||||
Query<Customer> query = parse("fetch query(50) billingAddress (city)");
|
||||
query.findList();
|
||||
|
||||
assertSql(query).doesNotContain(", t1.city");
|
||||
@@ -634,7 +634,7 @@ public class EqlParserTest extends BaseTestCase {
|
||||
public void fetch_lazy50_asHint() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
Query<Customer> query = parse("fetch billingAddress (+lazy(50),city) order by id");
|
||||
Query<Customer> query = parse("fetch lazy(50) billingAddress (city) order by id");
|
||||
List<Customer> list = query.findList();
|
||||
|
||||
assertSql(query).doesNotContain(", t1.city");
|
||||
|
||||
@@ -17,30 +17,26 @@ public class ParseFetchConfigTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLazy() throws Exception {
|
||||
|
||||
public void parseLazy() {
|
||||
FetchConfig lazy = ParseFetchConfig.parse("lazy");
|
||||
assertThat(lazy.getLazyBatchSize()).isEqualTo(0);
|
||||
assertThat(lazy.getBatchSize()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLazy100() throws Exception {
|
||||
|
||||
public void parseLazy100() {
|
||||
FetchConfig lazy = ParseFetchConfig.parse("lazy(100)");
|
||||
assertThat(lazy.getLazyBatchSize()).isEqualTo(100);
|
||||
assertThat(lazy.getBatchSize()).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseQuery() throws Exception {
|
||||
|
||||
public void parseQuery() {
|
||||
FetchConfig lazy = ParseFetchConfig.parse("query");
|
||||
assertThat(lazy.getQueryBatchSize()).isEqualTo(0);
|
||||
assertThat(lazy.getBatchSize()).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseQuery100() throws Exception {
|
||||
|
||||
public void parseQuery100() {
|
||||
FetchConfig lazy = ParseFetchConfig.parse("query(50)");
|
||||
assertThat(lazy.getQueryBatchSize()).isEqualTo(50);
|
||||
assertThat(lazy.getBatchSize()).isEqualTo(50);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-17
@@ -31,23 +31,20 @@ public class DLoadContextTest extends BaseTestCase {
|
||||
DLoadContext graphContext = (DLoadContext) queryRequest.getGraphContext();
|
||||
DLoadBeanContext customer = graphContext.getBeanContext("customer");
|
||||
|
||||
|
||||
assertThat(customer.firstBatchSize).isEqualTo(10);
|
||||
assertThat(customer.secondaryBatchSize).isEqualTo(10);
|
||||
assertThat(customer.batchSize).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void construct_when_fetchQuery_expect_100_batchSize() {
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetch("customer", new FetchConfig().query()));
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetch("customer", FetchConfig.ofQuery()));
|
||||
queryRequest.initTransIfRequired();
|
||||
queryRequest.endTransIfRequired();
|
||||
|
||||
DLoadContext graphContext = (DLoadContext) queryRequest.getGraphContext();
|
||||
DLoadBeanContext customer = graphContext.getBeanContext("customer");
|
||||
|
||||
assertThat(customer.firstBatchSize).isEqualTo(100);
|
||||
assertThat(customer.secondaryBatchSize).isEqualTo(100);
|
||||
assertThat(customer.batchSize).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -60,36 +57,33 @@ public class DLoadContextTest extends BaseTestCase {
|
||||
DLoadContext graphContext = (DLoadContext) queryRequest.getGraphContext();
|
||||
DLoadBeanContext customer = graphContext.getBeanContext("customer");
|
||||
|
||||
assertThat(customer.firstBatchSize).isEqualTo(100);
|
||||
assertThat(customer.secondaryBatchSize).isEqualTo(100);
|
||||
assertThat(customer.batchSize).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void construct_when_fetchQuery50_expect_50_batchSize() {
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetch("customer", new FetchConfig().query(50)));
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetch("customer", FetchConfig.ofQuery(50)));
|
||||
queryRequest.initTransIfRequired();
|
||||
queryRequest.endTransIfRequired();
|
||||
|
||||
DLoadContext graphContext = (DLoadContext) queryRequest.getGraphContext();
|
||||
DLoadBeanContext customer = graphContext.getBeanContext("customer");
|
||||
|
||||
assertThat(customer.firstBatchSize).isEqualTo(50);
|
||||
assertThat(customer.secondaryBatchSize).isEqualTo(50);
|
||||
assertThat(customer.batchSize).isEqualTo(50);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void construct_when_fetchQueryFirst20Lazy5_expect_20_5_batchSize() {
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetch("customer", new FetchConfig().queryFirst(20).lazy(5)));
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetchQuery("customer"));
|
||||
queryRequest.initTransIfRequired();
|
||||
queryRequest.endTransIfRequired();
|
||||
|
||||
DLoadContext graphContext = (DLoadContext) queryRequest.getGraphContext();
|
||||
DLoadBeanContext customer = graphContext.getBeanContext("customer");
|
||||
|
||||
assertThat(customer.firstBatchSize).isEqualTo(20);
|
||||
assertThat(customer.secondaryBatchSize).isEqualTo(5);
|
||||
assertThat(customer.batchSize).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,15 +91,14 @@ public class DLoadContextTest extends BaseTestCase {
|
||||
|
||||
BeanPropertyAssocMany<?> many = (BeanPropertyAssocMany<?>)getBeanDescriptor(Order.class).getBeanProperty("details");
|
||||
// the fetch is converted to a query join due to the maxRows
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetch("details").setMaxRows(100));
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetch("details").setMaxRows(50));
|
||||
queryRequest.initTransIfRequired();
|
||||
queryRequest.endTransIfRequired();
|
||||
|
||||
DLoadContext graphContext = (DLoadContext) queryRequest.getGraphContext();
|
||||
DLoadManyContext details = graphContext.getManyContext("details", many);
|
||||
|
||||
assertThat(details.firstBatchSize).isEqualTo(100);
|
||||
assertThat(details.secondaryBatchSize).isEqualTo(100);
|
||||
assertThat(details.batchSize).isEqualTo(100);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -92,7 +92,7 @@ public class OrmQueryDetailParserTest extends BaseTestCase {
|
||||
OrmQueryProperties chunk = detail.getChunk("customer", false);
|
||||
assertThat(chunk.getPath()).isEqualTo("customer");
|
||||
assertThat(chunk.getIncluded()).contains("id", "name", "email");
|
||||
assertThat(chunk.isQueryFetch()).isTrue();
|
||||
//FIXME: assertThat(chunk.isQueryFetch()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -110,7 +110,7 @@ public class OrmQueryDetailParserTest extends BaseTestCase {
|
||||
OrmQueryProperties chunk = detail.getChunk("customer", false);
|
||||
assertThat(chunk.getPath()).isEqualTo("customer");
|
||||
assertThat(chunk.getIncluded()).contains("id", "name", "email");
|
||||
assertThat(chunk.isQueryFetch()).isTrue();
|
||||
//FIXME: assertThat(chunk.isQueryFetch()).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
-86
@@ -11,121 +11,53 @@ public class OrmQueryPropertiesParserTest {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse(null);
|
||||
assertAllDefaults(res);
|
||||
assertThat(res.properties).isEqualTo("");
|
||||
assertThat(res.allProperties).isFalse();
|
||||
assertThat(res.included).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_empty() throws Exception {
|
||||
public void when_empty() {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("");
|
||||
assertAllDefaults(res);
|
||||
assertThat(res.properties).isEqualTo("");
|
||||
assertThat(res.allProperties).isFalse();
|
||||
assertThat(res.included).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_hasStar() throws Exception {
|
||||
public void when_hasStar() {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("*");
|
||||
assertAllDefaults(res);
|
||||
assertThat(res.properties).isEqualTo("*");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_hasCache() throws Exception {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("+cache");
|
||||
assertThat(res.cache).isTrue();
|
||||
assertThat(res.allProperties).isTrue();
|
||||
assertThat(res.included).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_hasCache_first() throws Exception {
|
||||
public void when_no_spaces() {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("+cache,id");
|
||||
assertThat(res.cache).isTrue();
|
||||
assertThat(res.included).containsExactly("id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_hasCache_last() throws Exception {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("name,+cache");
|
||||
assertThat(res.cache).isTrue();
|
||||
assertThat(res.included).containsExactly("name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_hasCache_middle() throws Exception {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("name,+cache, id");
|
||||
assertThat(res.cache).isTrue();
|
||||
assertThat(res.included).containsExactly("name", "id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_hasReadOnly() throws Exception {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("+readonly");
|
||||
assertThat(res.readOnly).isTrue();
|
||||
assertThat(res.included).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_hasLazy() throws Exception {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("+lazy");
|
||||
assertThat(res.fetchConfig.getLazyBatchSize()).isEqualTo(0);
|
||||
assertThat(res.included).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_hasLazyValue() throws Exception {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("+lazy(20)");
|
||||
assertThat(res.fetchConfig.getLazyBatchSize()).isEqualTo(20);
|
||||
assertThat(res.included).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_hasLazyValue_last() throws Exception {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("name,+lazy(20)");
|
||||
assertThat(res.fetchConfig.getLazyBatchSize()).isEqualTo(20);
|
||||
assertThat(res.included).containsExactly("name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_hasLazyValue_first() throws Exception {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("+lazy(20),id,name");
|
||||
assertThat(res.fetchConfig.getLazyBatchSize()).isEqualTo(20);
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("id,name");
|
||||
assertThat(res.allProperties).isFalse();
|
||||
assertThat(res.included).containsExactly("id", "name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_allProperties() throws Exception {
|
||||
public void when_spaced() {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("+query(4),+lazy(5)");
|
||||
assertThat(res.fetchConfig.getLazyBatchSize()).isEqualTo(5);
|
||||
assertThat(res.included).isNull();
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("id, name");
|
||||
assertThat(res.allProperties).isFalse();
|
||||
assertThat(res.included).containsExactly("id", "name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_everything_set() throws Exception {
|
||||
public void when_formula() {
|
||||
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("id, name +readonly +lazy(20) +query(30) +cache");
|
||||
assertThat(res.fetchConfig.getLazyBatchSize()).isEqualTo(20);
|
||||
assertThat(res.fetchConfig.getQueryBatchSize()).isEqualTo(30);
|
||||
assertThat(res.readOnly).isTrue();
|
||||
assertThat(res.cache).isTrue();
|
||||
assertThat(res.included).containsExactly("id", "name");
|
||||
OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("a,MD5(id::text) as b,c");
|
||||
assertThat(res.allProperties).isFalse();
|
||||
assertThat(res.included).containsExactly("a", "MD5(id::text) as b", "c");
|
||||
}
|
||||
|
||||
private void assertAllDefaults(OrmQueryPropertiesParser.Response res) {
|
||||
assertThat(res.cache).isFalse();
|
||||
assertThat(res.readOnly).isFalse();
|
||||
assertThat(res.fetchConfig.getLazyBatchSize()).isEqualTo(-1);
|
||||
assertThat(res.fetchConfig.getQueryBatchSize()).isEqualTo(-1);
|
||||
assertThat(res.included).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
+8
-13
@@ -10,21 +10,16 @@ public class OrmQueryPropertiesTest {
|
||||
|
||||
String append(String prefix, OrmQueryProperties p1) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
p1.append(prefix, sb);
|
||||
p1.asStringDebug(prefix, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void construct_with_propertySet_when_null() {
|
||||
new OrmQueryProperties(null, (LinkedHashSet<String>) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void construct_with_propertySet_when_empty() {
|
||||
|
||||
OrmQueryProperties p1 = new OrmQueryProperties(null, new LinkedHashSet<>());
|
||||
assertThat(p1.getProperties()).isEqualTo("");
|
||||
assertThat(p1.allProperties()).isFalse();
|
||||
assertThat(p1.getIncluded()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -34,7 +29,7 @@ public class OrmQueryPropertiesTest {
|
||||
set.add("name");
|
||||
OrmQueryProperties p1 = new OrmQueryProperties(null, set);
|
||||
|
||||
assertThat(p1.getProperties()).isEqualTo("name");
|
||||
assertThat(p1.getIncluded()).containsOnly("name");
|
||||
assertThat(p1.allProperties()).isFalse();
|
||||
}
|
||||
|
||||
@@ -47,7 +42,7 @@ public class OrmQueryPropertiesTest {
|
||||
set.add("startDate");
|
||||
OrmQueryProperties p1 = new OrmQueryProperties(null, set);
|
||||
|
||||
assertThat(p1.getProperties()).isEqualTo("id,name,startDate");
|
||||
assertThat(p1.getIncluded()).containsOnly("id", "name", "startDate");
|
||||
assertThat(p1.allProperties()).isFalse();
|
||||
}
|
||||
|
||||
@@ -68,8 +63,8 @@ public class OrmQueryPropertiesTest {
|
||||
@Test
|
||||
public void append_when_somePropertiesWithOptions() {
|
||||
|
||||
OrmQueryProperties p1 = new OrmQueryProperties(null, "id,name +cache");
|
||||
assertThat(append("select ", p1)).isEqualTo("select (id,name +cache)");
|
||||
OrmQueryProperties p1 = new OrmQueryProperties(null, "id,name,+cache");
|
||||
//FIXME: assertThat(append("select ", p1)).isEqualTo("select (id,name,+cache)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -82,8 +77,8 @@ public class OrmQueryPropertiesTest {
|
||||
@Test
|
||||
public void append_when_path_and_somePropertiesWithOptions() {
|
||||
|
||||
OrmQueryProperties p1 = new OrmQueryProperties("customer", "id,name +cache");
|
||||
assertThat(append("fetch ", p1)).isEqualTo("fetch customer (id,name +cache)");
|
||||
OrmQueryProperties p1 = new OrmQueryProperties("customer", "id,name,+cache");
|
||||
//FIXME: assertThat(append("fetch ", p1)).isEqualTo("fetch customer (id,name,+cache)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.text.json.EJson;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -63,4 +68,82 @@ public class ScalarTypeArrayListTest extends BasePlatformArrayTypeFactoryTest {
|
||||
assertBindNullTo_Null(factory.typeFor(UUID.class, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withUuidType_expect_uuidTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(UUID.class, true);
|
||||
|
||||
List<UUID> input = new ArrayList<>();
|
||||
input.add(UUID.randomUUID());
|
||||
input.add(UUID.randomUUID());
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Object parsed = scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withIntegerType_expect_longIntegerReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Integer.class, true);
|
||||
|
||||
List<Integer> input = new ArrayList<>();
|
||||
input.add(2);
|
||||
input.add(4);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Object parsed = scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withLongType_expect_longTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Long.class, true);
|
||||
|
||||
List<Long> input = new ArrayList<>();
|
||||
input.add(2L);
|
||||
input.add(4L);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Object parsed = scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_withDoubleType_expect_longTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Double.class, true);
|
||||
|
||||
List<Double> input = new ArrayList<>();
|
||||
input.add(2D);
|
||||
input.add(4D);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Object parsed = scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonRead_withUuidType() throws IOException {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(UUID.class, true);
|
||||
|
||||
List<UUID> input = new ArrayList<>();
|
||||
input.add(UUID.randomUUID());
|
||||
input.add(UUID.randomUUID());
|
||||
String asJson = EJson.write(input);
|
||||
|
||||
JsonParser parser = DB.json().createParser(new StringReader(asJson));
|
||||
|
||||
Object parsed = scalarType.jsonRead(parser);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.text.json.EJson;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -49,4 +57,82 @@ public class ScalarTypeArraySetH2Test extends BasePlatformArrayTypeFactoryTest {
|
||||
assertBindNullTo_Null(factory.typeFor(UUID.class, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withUuidType_expect_uuidTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(UUID.class, true);
|
||||
|
||||
Set<UUID> input = new LinkedHashSet<>();
|
||||
input.add(UUID.randomUUID());
|
||||
input.add(UUID.randomUUID());
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Set<UUID> parsed = (Set<UUID>)scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withIntegerType_expect_longIntegerReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Integer.class, true);
|
||||
|
||||
Set<Integer> input = new LinkedHashSet<>();
|
||||
input.add(2);
|
||||
input.add(4);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Set<Integer> parsed = (Set<Integer>)scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withLongType_expect_longTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Long.class, true);
|
||||
|
||||
Set<Long> input = new LinkedHashSet<>();
|
||||
input.add(2L);
|
||||
input.add(4L);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Set<Long> parsed = (Set<Long>)scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_withDoubleType_expect_longTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Double.class, true);
|
||||
|
||||
Set<Double> input = new LinkedHashSet<>();
|
||||
input.add(2D);
|
||||
input.add(4D);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Object parsed = scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonRead_withUuidType() throws IOException {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(UUID.class, true);
|
||||
|
||||
Set<UUID> input = new LinkedHashSet<>();
|
||||
input.add(UUID.randomUUID());
|
||||
input.add(UUID.randomUUID());
|
||||
String asJson = EJson.write(input);
|
||||
|
||||
JsonParser parser = DB.json().createParser(new StringReader(asJson));
|
||||
|
||||
Object parsed = scalarType.jsonRead(parser);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -6,6 +6,7 @@ import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -24,12 +25,29 @@ public class ScalarTypeJodaLocalDateTest {
|
||||
assertThat(localDate).isEqualTo(localDate1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToDate_convertFromDate_westOfUtc() {
|
||||
final TimeZone originaDefaultTimezone = TimeZone.getDefault();
|
||||
try {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
|
||||
|
||||
convertDate(new LocalDate());
|
||||
convertDate(new LocalDate(1899, 12, 1));
|
||||
convertDate(new LocalDate(1900, 1, 1));
|
||||
convertDate(new LocalDate(2021, 2, 8));
|
||||
|
||||
} finally {
|
||||
TimeZone.setDefault(originaDefaultTimezone);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToDate_convertFromDate() {
|
||||
|
||||
convertDate(new LocalDate());
|
||||
convertDate(new LocalDate(1899, 12, 1));
|
||||
convertDate(new LocalDate(1900, 1, 1));
|
||||
convertDate(new LocalDate(2021, 2, 8));
|
||||
}
|
||||
|
||||
private void convertDate(LocalDate localDate) {
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.ebeaninternal.server.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -11,49 +11,49 @@ public class DSelectColumnsParserTest {
|
||||
@Test
|
||||
public void parse() {
|
||||
|
||||
List<String> cols = DSelectColumnsParser.parse("a,MD5(id::text) as b,c");
|
||||
Set<String> cols = DSelectColumnsParser.parse("a,MD5(id::text) as b,c");
|
||||
assertThat(cols).containsExactly("a", "MD5(id::text) as b", "c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whitespace_is_trimmed() {
|
||||
|
||||
List<String> cols = DSelectColumnsParser.parse("a , MD5(id::text) as b , c ");
|
||||
Set<String> cols = DSelectColumnsParser.parse("a , MD5(id::text) as b , c ");
|
||||
assertThat(cols).containsExactly("a", "MD5(id::text) as b", "c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedFunctions() {
|
||||
|
||||
List<String> cols = DSelectColumnsParser.parse("a , concat(id,'sd',inner(foo)) as b , c ");
|
||||
Set<String> cols = DSelectColumnsParser.parse("a , concat(id,'sd',inner(foo)) as b , c ");
|
||||
assertThat(cols).containsExactly("a", "concat(id,'sd',inner(foo)) as b", "c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basic() {
|
||||
|
||||
List<String> cols = DSelectColumnsParser.parse("name , status , billingAddress ");
|
||||
Set<String> cols = DSelectColumnsParser.parse("name , status , billingAddress ");
|
||||
assertThat(cols).containsExactly("name", "status", "billingAddress");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basic_noWhitespace() {
|
||||
|
||||
List<String> cols = DSelectColumnsParser.parse("a,b,c");
|
||||
Set<String> cols = DSelectColumnsParser.parse("a,b,c");
|
||||
assertThat(cols).containsExactly("a", "b", "c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formula_noWhitespace() {
|
||||
|
||||
List<String> cols = DSelectColumnsParser.parse("a,concat(x,y),c");
|
||||
Set<String> cols = DSelectColumnsParser.parse("a,concat(x,y),c");
|
||||
assertThat(cols).containsExactly("a", "concat(x,y)", "c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void with_logicalCast_andAsAlias() {
|
||||
|
||||
List<String> cols = DSelectColumnsParser.parse("name , concat(status,'-end')::String as fullName , billingAddress ");
|
||||
Set<String> cols = DSelectColumnsParser.parse("name , concat(status,'-end')::String as fullName , billingAddress ");
|
||||
assertThat(cols).containsExactly("name", "concat(status,'-end')::String as fullName", "billingAddress");
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,69 @@ public class TestAnnotationBase extends BaseTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
public static class TestJakartaAnnotationBaseEntity extends MappedBaseEntity {
|
||||
@Where(clause = "SELECT 'mysql' from 1", platforms = Platform.MYSQL)
|
||||
@Where(clause = "SELECT 'h2' from 1", platforms = H2)
|
||||
@Where(clause = "SELECT 'other' from 1")
|
||||
private String direct;
|
||||
|
||||
@MetaTest
|
||||
private String meta;
|
||||
|
||||
@MetaTest
|
||||
@Where(clause = "SELECT 'oracle' from 1", platforms = Platform.ORACLE)
|
||||
private String mixed;
|
||||
|
||||
@jakarta.validation.constraints.Size.List({
|
||||
@jakarta.validation.constraints.Size(max = 10, message = "max length for you is 10"),
|
||||
@jakarta.validation.constraints.Size(min = 1),
|
||||
@jakarta.validation.constraints.Size(max = 40, message = "max value for you is 40", groups = ValidationGroupSomething.class)
|
||||
})
|
||||
private String constraintAnnotation;
|
||||
|
||||
@NotNull
|
||||
private String null1;
|
||||
|
||||
|
||||
@NotNull(groups = ValidationGroupSomething.class)
|
||||
private String null2;
|
||||
|
||||
private String null3;
|
||||
|
||||
public String getConstraintAnnotation() {
|
||||
return constraintAnnotation;
|
||||
}
|
||||
|
||||
public void setConstraintAnnotation(String constraintAnnotation) {
|
||||
this.constraintAnnotation = constraintAnnotation;
|
||||
}
|
||||
|
||||
public String getNull1() {
|
||||
return null1;
|
||||
}
|
||||
|
||||
public void setNull1(String null1) {
|
||||
this.null1 = null1;
|
||||
}
|
||||
|
||||
public String getNull2() {
|
||||
return null2;
|
||||
}
|
||||
|
||||
public void setNull2(String null2) {
|
||||
this.null2 = null2;
|
||||
}
|
||||
|
||||
public String getNull3() {
|
||||
return null3;
|
||||
}
|
||||
|
||||
public void setNull3(String null3) {
|
||||
this.null3 = null3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFindMaxSize() throws SecurityException {
|
||||
@@ -152,6 +215,13 @@ public class TestAnnotationBase extends BaseTestCase {
|
||||
assertEquals(40, bp.getDbLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindJakartaMaxSize() throws SecurityException {
|
||||
BeanDescriptor<TestJakartaAnnotationBaseEntity> descriptor = spiEbeanServer().getBeanDescriptor(TestJakartaAnnotationBaseEntity.class);
|
||||
BeanProperty bp = descriptor.findProperty("constraintAnnotation");
|
||||
assertEquals(40, bp.getDbLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotationClassIndexes() throws SecurityException {
|
||||
BeanDescriptor<TestAnnotationBaseEntity> descriptor = spiEbeanServer().getBeanDescriptor(TestAnnotationBaseEntity.class);
|
||||
@@ -173,6 +243,20 @@ public class TestAnnotationBase extends BaseTestCase {
|
||||
assertTrue(bp.isNullable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJakartaNotNullWithGroup() throws SecurityException {
|
||||
BeanDescriptor<TestJakartaAnnotationBaseEntity> descriptor = spiEbeanServer().getBeanDescriptor(TestJakartaAnnotationBaseEntity.class);
|
||||
|
||||
BeanProperty bp = descriptor.findProperty("null1");
|
||||
assertFalse(bp.isNullable());
|
||||
|
||||
bp = descriptor.findProperty("null2");
|
||||
assertTrue(bp.isNullable());
|
||||
|
||||
bp = descriptor.findProperty("null3");
|
||||
assertTrue(bp.isNullable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindAnnotation() throws NoSuchFieldException, SecurityException {
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public class TestManyOneInterface extends BaseTestCase {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
IAddress a = new Address();
|
||||
IAddress a = new Address("hello");
|
||||
|
||||
IPerson p = new Person();
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package org.tests.basic.lob;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Query;
|
||||
import org.tests.model.basic.EBasicClobNoVer;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
@@ -21,53 +19,50 @@ public class TestBasicClobNoVer extends BaseTestCase {
|
||||
EBasicClobNoVer entity = new EBasicClobNoVer();
|
||||
entity.setName("test");
|
||||
entity.setDescription("initialClobValue");
|
||||
EbeanServer server = Ebean.getServer(null);
|
||||
server.save(entity);
|
||||
|
||||
|
||||
String sqlNoClob = "select t0.id, t0.name from ebasic_clob_no_ver t0 where t0.id = ?";
|
||||
String sqlWithClob = "select t0.id, t0.name, t0.description from ebasic_clob_no_ver t0 where t0.id = ?";
|
||||
|
||||
DB.save(entity);
|
||||
|
||||
// Clob by default is Fetch Lazy
|
||||
Query<EBasicClobNoVer> defaultQuery = Ebean.find(EBasicClobNoVer.class).setId(entity.getId());
|
||||
Query<EBasicClobNoVer> defaultQuery = DB.find(EBasicClobNoVer.class).setId(entity.getId());
|
||||
defaultQuery.findOne();
|
||||
String sql = sqlOf(defaultQuery, 2);
|
||||
|
||||
Assert.assertTrue("Clob is fetch lazy by default", sql.contains(sqlNoClob));
|
||||
// default SQL select excludes clob
|
||||
String sqlNoClob = "select t0.id, t0.name from ebasic_clob_no_ver t0 where t0.id = ?";
|
||||
assertThat(sql).contains(sqlNoClob);
|
||||
|
||||
|
||||
// Explicitly select * including Clob
|
||||
Query<EBasicClobNoVer> explicitQuery = Ebean.find(EBasicClobNoVer.class).setId(entity.getId()).select("*");
|
||||
Query<EBasicClobNoVer> explicitQuery = DB.find(EBasicClobNoVer.class).setId(entity.getId()).select("*");
|
||||
|
||||
explicitQuery.findOne();
|
||||
sql = sqlOf(explicitQuery, 2);
|
||||
|
||||
Assert.assertTrue("Explicitly include Clob", sql.contains(sqlWithClob));
|
||||
// Explicitly include Clob
|
||||
String sqlWithClob = "select t0.id, t0.name, t0.description from ebasic_clob_no_ver t0 where t0.id = ?";
|
||||
assertThat(sql).contains(sqlWithClob);
|
||||
|
||||
// Update description to test refresh
|
||||
|
||||
EBasicClobNoVer updateBean = new EBasicClobNoVer();
|
||||
updateBean.setId(entity.getId());
|
||||
updateBean.setDescription("modified");
|
||||
Ebean.update(updateBean);
|
||||
DB.update(updateBean);
|
||||
|
||||
|
||||
// Test refresh function
|
||||
|
||||
Assert.assertEquals("initialClobValue", entity.getDescription());
|
||||
assertThat(entity.getDescription()).isEqualTo("initialClobValue");
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
// Refresh query includes all properties
|
||||
server.refresh(entity);
|
||||
DB.refresh(entity);
|
||||
|
||||
// Assert all properties fetched in refresh
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
Assert.assertEquals(1, loggedSql.size());
|
||||
assertThat(loggedSql).hasSize(1);
|
||||
assertThat(trimSql(loggedSql.get(0), 2)).contains(sqlWithClob);
|
||||
Assert.assertEquals("modified", entity.getDescription());
|
||||
|
||||
assertThat(entity.getDescription()).isEqualTo("modified");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ public class TestBasicLazy extends BaseTestCase {
|
||||
new FetchThread(tg, 2).start();
|
||||
new FetchThread(tg, 3).start();
|
||||
|
||||
orders = Ebean.find(Order.class).fetch("customer", new FetchConfig().lazy(100)).findList();
|
||||
orders = Ebean.find(Order.class).fetchLazy("customer").findList();
|
||||
Assert.assertTrue(orders.size() >= 4);
|
||||
|
||||
try {
|
||||
|
||||
@@ -44,7 +44,7 @@ public class TestBatchLazyWithDeleted extends BaseTestCase {
|
||||
Ebean.save(twoC);
|
||||
|
||||
List<UUTwo> list = Ebean.find(UUTwo.class)
|
||||
.fetch("master", new FetchConfig().lazy(5))
|
||||
.fetchLazy("master")
|
||||
.where().startsWith("name", "two-bld-")
|
||||
.order("name")
|
||||
.findList();
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
package org.tests.batchload;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.BeanState;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
public class TestBeanState extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
List<Customer> custs = DB.find(Customer.class).findList();
|
||||
|
||||
Customer customer = DB.find(Customer.class).setId(custs.get(0).getId()).select("name")
|
||||
.setUseCache(false)
|
||||
.findOne();
|
||||
|
||||
BeanState beanState = DB.getBeanState(customer);
|
||||
assertFalse(beanState.isNew());
|
||||
assertFalse(beanState.isDirty());
|
||||
assertFalse(beanState.isNewOrDirty());
|
||||
assertNotNull(beanState.getLoadedProps());
|
||||
|
||||
customer.setName("dirtyNameProp");
|
||||
assertTrue(beanState.isDirty());
|
||||
assertThat(beanState.getChangedProps()).containsOnly("name");
|
||||
|
||||
EntityBeanIntercept ebi = ((EntityBean) customer)._ebean_getIntercept();
|
||||
boolean[] dirtyProperties = ebi.getDirtyProperties();
|
||||
for (int i = 0; i < dirtyProperties.length; i++) {
|
||||
if (dirtyProperties[i]) {
|
||||
String dirtyPropertyName = ebi.getProperty(i);
|
||||
assertEquals("name", dirtyPropertyName);
|
||||
}
|
||||
}
|
||||
|
||||
customer.setStatus(Customer.Status.INACTIVE);
|
||||
|
||||
assertTrue(beanState.isDirty());
|
||||
assertThat(beanState.getChangedProps()).containsOnly("name", "status");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDisableLazyLoad_expect_lazyLoadingDisabled() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
List<Customer> custs = DB.find(Customer.class).order("id").findList();
|
||||
|
||||
Customer customer = DB.find(Customer.class)
|
||||
.setId(custs.get(0).getId())
|
||||
.select("id")
|
||||
.setUseCache(false)
|
||||
.findOne();
|
||||
|
||||
BeanState beanState = DB.getBeanState(customer);
|
||||
beanState.setDisableLazyLoad(true);
|
||||
assertNull(customer.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getChangedProps_when_setManyProperty() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Customer customer = DB.find(Customer.class).order("id").setMaxRows(1).findOne();
|
||||
|
||||
BeanState beanState = DB.getBeanState(customer);
|
||||
assertThat(beanState.getChangedProps()).isEmpty();
|
||||
|
||||
customer.setContacts(new ArrayList<>());
|
||||
assertThat(beanState.getChangedProps()).containsOnly("contacts");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getChangedProps_when_setManyProperty_onNewBean() {
|
||||
|
||||
Customer customer = new Customer();
|
||||
|
||||
BeanState beanState = DB.getBeanState(customer);
|
||||
assertThat(beanState.getChangedProps()).isEmpty();
|
||||
|
||||
// when new state, then loaded
|
||||
customer.setContacts(new ArrayList<>());
|
||||
assertThat(beanState.getChangedProps()).isEmpty();
|
||||
assertThat(beanState.getLoadedProps()).containsOnly("contacts");
|
||||
|
||||
// set loaded state, then marked as changed
|
||||
beanState.setLoaded();
|
||||
customer.setContacts(new ArrayList<>());
|
||||
assertThat(beanState.getLoadedProps()).containsOnly("contacts");
|
||||
assertThat(beanState.getChangedProps()).containsOnly("contacts");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void readOnly_when_setManyProperty() {
|
||||
|
||||
Customer customer = new Customer();
|
||||
customer.setContacts(new ArrayList<>());
|
||||
|
||||
BeanState beanState = DB.getBeanState(customer);
|
||||
beanState.setLoaded();
|
||||
beanState.setReadOnly(true);
|
||||
|
||||
// act, try to mutate read only bean
|
||||
customer.setContacts(new ArrayList<>());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void readOnly_when_setProperty() {
|
||||
|
||||
Customer customer = new Customer();
|
||||
customer.setName("a");
|
||||
|
||||
BeanState beanState = DB.getBeanState(customer);
|
||||
beanState.setLoaded();
|
||||
beanState.setReadOnly(true);
|
||||
|
||||
// act, try to mutate read only bean
|
||||
customer.setName("b");
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public class TestLazyJoin extends BaseTestCase {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status")
|
||||
.fetch("customer", "+lazy(10) name, status")
|
||||
.fetchLazy("customer", "name, status")
|
||||
.fetch("customer.contacts")
|
||||
.order().asc("id");
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ public class TestLazyJoin2 extends BaseTestCase {
|
||||
// This will use 3 SQL queries to build this object graph
|
||||
List<Order> l0 = Ebean.find(Order.class).select("status, shipDate")
|
||||
|
||||
.fetch("details", "orderQty, unitPrice", new FetchConfig().query())
|
||||
.fetchQuery("details", "orderQty, unitPrice")
|
||||
.fetch("details.product", "sku, name")
|
||||
|
||||
.fetch("customer", "name", new FetchConfig().query(10))
|
||||
.fetchQuery("customer", "name")
|
||||
.fetch("customer.contacts", "firstName, lastName, mobile")
|
||||
.fetch("customer.shippingAddress", "line1, city").order().asc("id").findList();
|
||||
|
||||
@@ -43,7 +43,7 @@ public class TestLazyJoin2 extends BaseTestCase {
|
||||
|
||||
List<Order> orders = Ebean.find(Order.class)
|
||||
// .select("status")
|
||||
.fetch("customer", new FetchConfig().query(3).lazy(10)).order().asc("id").findList();
|
||||
.fetchQuery("customer").order().asc("id").findList();
|
||||
// .join("customer.contacts");
|
||||
|
||||
// List<Order> list = query.findList();
|
||||
@@ -59,7 +59,7 @@ public class TestLazyJoin2 extends BaseTestCase {
|
||||
|
||||
Assert.assertNotNull(billingAddress);
|
||||
|
||||
List<Order> list = Ebean.find(Order.class).fetch("customer", "name", new FetchConfig().lazy(5))
|
||||
List<Order> list = Ebean.find(Order.class).fetchLazy("customer", "name")
|
||||
.fetch("customer.contacts", "contactName, phone, email").fetch("customer.shippingAddress")
|
||||
.where().eq("status", Order.Status.NEW).order().asc("id").findList();
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ public class TestLazyLoadEmptyCollection extends TransactionalTestCase {
|
||||
Ebean.save(c);
|
||||
|
||||
List<Customer> list = Ebean.find(Customer.class)
|
||||
.fetch("contacts", new FetchConfig().query(0))
|
||||
.fetch("contacts.notes", new FetchConfig().query(100))
|
||||
.fetchQuery("contacts")
|
||||
.fetchQuery("contacts.notes")
|
||||
.findList();
|
||||
|
||||
for (Customer customer : list) {
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
package org.tests.batchload;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.BeanState;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TestLoadOnDirty extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
List<Customer> custs = Ebean.find(Customer.class).findList();
|
||||
|
||||
Customer customer = Ebean.find(Customer.class).setId(custs.get(0).getId()).select("name")
|
||||
.setUseCache(false)
|
||||
.findOne();
|
||||
|
||||
BeanState beanState = Ebean.getBeanState(customer);
|
||||
Assert.assertTrue(!beanState.isNew());
|
||||
Assert.assertTrue(!beanState.isDirty());
|
||||
Assert.assertTrue(!beanState.isNewOrDirty());
|
||||
Assert.assertNotNull(beanState.getLoadedProps());
|
||||
|
||||
customer.setName("dirtyNameProp");
|
||||
Assert.assertTrue(beanState.isDirty());
|
||||
Assert.assertTrue(beanState.getChangedProps().contains("name"));
|
||||
Assert.assertEquals(1, beanState.getChangedProps().size());
|
||||
|
||||
EntityBeanIntercept ebi = ((EntityBean) customer)._ebean_getIntercept();
|
||||
boolean[] dirtyProperties = ebi.getDirtyProperties();
|
||||
for (int i = 0; i < dirtyProperties.length; i++) {
|
||||
if (dirtyProperties[i]) {
|
||||
String dirtyPropertyName = ebi.getProperty(i);
|
||||
Assert.assertEquals("name", dirtyPropertyName);
|
||||
}
|
||||
}
|
||||
|
||||
customer.setStatus(Customer.Status.INACTIVE);
|
||||
|
||||
Assert.assertTrue(beanState.isDirty());
|
||||
Assert.assertTrue(beanState.getChangedProps().contains("status"));
|
||||
Assert.assertTrue(beanState.getChangedProps().contains("name"));
|
||||
Assert.assertEquals(2, beanState.getChangedProps().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisableLazyLoad() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
List<Customer> custs = Ebean.find(Customer.class).order("id").findList();
|
||||
|
||||
Customer customer = Ebean.find(Customer.class)
|
||||
.setId(custs.get(0).getId())
|
||||
.select("id")
|
||||
.setUseCache(false)
|
||||
.findOne();
|
||||
|
||||
BeanState beanState = Ebean.getBeanState(customer);
|
||||
beanState.setDisableLazyLoad(true);
|
||||
Assert.assertNull(customer.getName());
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class TestQueryJoin extends BaseTestCase {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class).select("status")
|
||||
// .join("details","+query(10)")
|
||||
.fetch("customer", "+lazy(10) name, status").fetch("customer.contacts").order().asc("id");
|
||||
.fetchLazy("customer", "name, status").fetch("customer.contacts").order().asc("id");
|
||||
// .join("customer.billingAddress");
|
||||
|
||||
List<Order> list = query.findList();
|
||||
|
||||
@@ -29,7 +29,7 @@ public class TestQueryJoinToAssocOne extends BaseTestCase {
|
||||
|
||||
// This will use 2 SQL queries to build this object graph
|
||||
List<Order> l0 = Ebean.find(Order.class)
|
||||
.fetch("details", "orderQty, unitPrice", new FetchConfig().query())
|
||||
.fetchQuery("details", "orderQty, unitPrice")
|
||||
.fetch("details.product", "sku, name")
|
||||
.findList();
|
||||
|
||||
@@ -56,7 +56,7 @@ public class TestQueryJoinToAssocOne extends BaseTestCase {
|
||||
// This will use 2 SQL queries to build this object graph
|
||||
List<Order> l0 = Ebean.find(Order.class)
|
||||
.select("status, shipDate")
|
||||
.fetch("details", "orderQty, unitPrice", new FetchConfig().query())
|
||||
.fetchQuery("details", "orderQty, unitPrice")
|
||||
.fetch("details.product", "sku, name")
|
||||
.findList();
|
||||
|
||||
@@ -83,7 +83,7 @@ public class TestQueryJoinToAssocOne extends BaseTestCase {
|
||||
List<Order> l0 = Ebean.find(Order.class)
|
||||
.setDisableLazyLoading(true)
|
||||
.select("status, shipDate")
|
||||
.fetch("details", "orderQty, unitPrice", new FetchConfig().query())
|
||||
.fetchQuery("details", "orderQty, unitPrice")
|
||||
.fetch("details.product", "sku, name")
|
||||
.order().asc("id")
|
||||
.findList();
|
||||
|
||||
@@ -25,7 +25,7 @@ public class TestSecondQueryNoRows extends BaseTestCase {
|
||||
Customer c = Ebean.find(Customer.class)
|
||||
.setAutoTune(false)
|
||||
.setId(cnew.getId())
|
||||
.fetch("contacts", new FetchConfig().query())
|
||||
.fetchQuery("contacts")
|
||||
.findOne();
|
||||
|
||||
assertNotNull(c);
|
||||
|
||||
@@ -105,7 +105,7 @@ public class TestSecondaryQueries extends TransactionalTestCase {
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.select("name")
|
||||
.fetch("contacts", "+query")
|
||||
.fetchQuery("contacts")
|
||||
.setId(custId);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
@@ -129,9 +129,9 @@ public class TestSecondaryQueries extends TransactionalTestCase {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status")
|
||||
.fetch("customer", "name, status", new FetchConfig().query())
|
||||
.fetchQuery("customer", "name, status")
|
||||
.fetch("customer.contacts")
|
||||
.fetch("details", new FetchConfig().query())
|
||||
.fetchQuery("details")
|
||||
.where().eq("status", Order.Status.NEW)
|
||||
.query();
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.tests.cache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.tests.model.cache.M2MCacheChild;
|
||||
import org.tests.model.cache.M2MCacheMaster;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
|
||||
public class TestM2MCache extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testM2MWithCache() throws Exception {
|
||||
M2MCacheChild cld = new M2MCacheChild();
|
||||
cld.setName("test");
|
||||
cld.setId(1);
|
||||
DB.save(cld);
|
||||
|
||||
M2MCacheMaster cfg = new M2MCacheMaster();
|
||||
cfg.setId(42);
|
||||
cfg.getSet1().add(cld);
|
||||
cfg.getSet2().add(cld);
|
||||
DB.save(cfg);
|
||||
|
||||
// find master and access set1 + set2.
|
||||
// lazy load something from set1
|
||||
M2MCacheMaster cfg1 = DB.find(M2MCacheMaster.class, 42);
|
||||
|
||||
cfg1.getSet1().size();
|
||||
cfg1.getSet2().size();
|
||||
assertThat(cfg1.getSet1().iterator().next().getName()).isEqualTo("test");
|
||||
|
||||
// do it again
|
||||
cfg1 = DB.find(M2MCacheMaster.class, 42);
|
||||
|
||||
cfg1.getSet1().size();
|
||||
cfg1.getSet2().size();
|
||||
|
||||
assertThat(cfg1.getSet1().iterator().next().getName()).isEqualTo("test");
|
||||
// do it again
|
||||
cfg1 = DB.find(M2MCacheMaster.class, 42);
|
||||
|
||||
cfg1.getSet1().size();
|
||||
cfg1.getSet2().size();
|
||||
|
||||
assertThat(cfg1.getSet1().iterator().next().getName()).isEqualTo("test");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testM2MWithCacheMinimal() throws Exception {
|
||||
M2MCacheChild cld = new M2MCacheChild();
|
||||
cld.setName("test");
|
||||
cld.setId(2);
|
||||
DB.save(cld);
|
||||
|
||||
M2MCacheMaster cfg = new M2MCacheMaster();
|
||||
cfg.setId(43);
|
||||
cfg.getSet1().add(cld);
|
||||
cfg.getSet2().add(cld);
|
||||
DB.save(cfg);
|
||||
|
||||
DB.find(M2MCacheMaster.class, 43);
|
||||
|
||||
M2MCacheMaster cfg1 = DB.find(M2MCacheMaster.class, 43);
|
||||
cfg1.getSet2().size();
|
||||
|
||||
cfg1 = DB.find(M2MCacheMaster.class, 43);
|
||||
|
||||
cfg1.getSet1().size();
|
||||
cfg1.getSet2().size();
|
||||
|
||||
assertThat(cfg1.getSet1().iterator().next().getName()).isEqualTo("test");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,7 @@ public class TestDeleteO2MOrphans extends BaseTestCase {
|
||||
|
||||
// assert
|
||||
COOne check = findById(id);
|
||||
//FIXME #2127 #2141: assertThat(check.getChildren()).hasSize(2);
|
||||
assertThat(check.getChildren()).hasSize(4);
|
||||
assertThat(check.getChildren()).hasSize(2);
|
||||
DB.delete(check);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user