mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
111
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14fbc3a955 | ||
|
|
e41da2f995 | ||
|
|
ea60e42a4d | ||
|
|
efce6b57e1 | ||
|
|
988e4c7386 | ||
|
|
9e1d7ab722 | ||
|
|
0c03a8b925 | ||
|
|
37a66f1ee2 | ||
|
|
a02673359d | ||
|
|
f9d13cbba7 | ||
|
|
f787483e30 | ||
|
|
9e33f36808 | ||
|
|
6a7e6caa66 | ||
|
|
5a4a0e3886 | ||
|
|
c3f586c461 | ||
|
|
99f7243d6c | ||
|
|
7026c00290 | ||
|
|
448d94a335 | ||
|
|
961a7ef7ea | ||
|
|
5cef1ad017 | ||
|
|
b814ca6e81 | ||
|
|
a24e6b6064 | ||
|
|
b751823abb | ||
|
|
f3cb7e2cfd | ||
|
|
31d7c6fed8 | ||
|
|
c5fb1dbe23 | ||
|
|
b7770a9617 | ||
|
|
41abf34ef6 | ||
|
|
89beaee0f6 | ||
|
|
5052afbd07 | ||
|
|
d89a58482c | ||
|
|
eb1bca805e | ||
|
|
9cb66b112f | ||
|
|
210901ee2d | ||
|
|
396fb49acd | ||
|
|
a83896f1ff | ||
|
|
3483372ae3 | ||
|
|
4e1f817ade | ||
|
|
d4139aa8ef | ||
|
|
a6cfed7c44 | ||
|
|
cc17ea8911 | ||
|
|
141f0a8b16 | ||
|
|
9dbd903a73 | ||
|
|
b8105190d1 | ||
|
|
ef3d911e19 | ||
|
|
31fd0b7ed5 | ||
|
|
26d1288757 | ||
|
|
1d49e5b341 | ||
|
|
4a0806e453 | ||
|
|
ab48f796c6 | ||
|
|
0cb8bb0415 | ||
|
|
471691c747 | ||
|
|
c86e6b3cf9 | ||
|
|
b399713a85 | ||
|
|
f8d4ad7f33 | ||
|
|
7984a9ccd1 | ||
|
|
27cda09523 | ||
|
|
20fc23c19c | ||
|
|
bac53a5d75 | ||
|
|
2e0690efa9 | ||
|
|
fdd9b1905c | ||
|
|
fc966618a6 | ||
|
|
669ba7837a | ||
|
|
0edf90056c | ||
|
|
6b27a9d280 | ||
|
|
2b0669c2ae | ||
|
|
9fc10e8e04 | ||
|
|
fb5eb0cdd2 | ||
|
|
3351882915 | ||
|
|
31c6d16e73 | ||
|
|
cd8c12a9a8 | ||
|
|
3207ae1f08 | ||
|
|
5fe532c393 | ||
|
|
93b9cbf9ac | ||
|
|
8a34d6050b | ||
|
|
ab3e6ccbf7 | ||
|
|
cc58fc8195 | ||
|
|
1c34c09f3c | ||
|
|
8544712448 | ||
|
|
5e22079366 | ||
|
|
903b0a3929 | ||
|
|
b18bc82b8a | ||
|
|
50de32af29 | ||
|
|
0b18189e62 | ||
|
|
0b6e73d729 | ||
|
|
3108076953 | ||
|
|
7ed4435ccc | ||
|
|
7aef4ff1bd | ||
|
|
5bcad17ae1 | ||
|
|
b1beb0b787 | ||
|
|
585eb2b57b | ||
|
|
2653ec50aa | ||
|
|
cb4432a588 | ||
|
|
4d93b70bf9 | ||
|
|
0b299edf0d | ||
|
|
cf10246783 | ||
|
|
babba914a0 | ||
|
|
0397140e0b | ||
|
|
82def2710e | ||
|
|
60ad417e1b | ||
|
|
3af1b574fd | ||
|
|
60c8683914 | ||
|
|
72263c0af0 | ||
|
|
f66316138d | ||
|
|
b403942491 | ||
|
|
baa3909c63 | ||
|
|
81f42f8e95 | ||
|
|
0377e3ec5c | ||
|
|
58f6c67d78 | ||
|
|
84f7fc5d81 | ||
|
|
ea02bebc6f |
@@ -1,8 +1,11 @@
|
||||
[](https://travis-ci.org/ebean-orm/ebean)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean)
|
||||
|
||||
# Need help?
|
||||
Post questions or issues to the Ebean google group - https://groups.google.com/forum/#!forum/ebean
|
||||
|
||||
# Documentation
|
||||
Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
|
||||
Goto [https://ebean.io/docs/](https://ebean.io/docs/)
|
||||
|
||||
|
||||
## Maven cental links:
|
||||
@@ -10,8 +13,7 @@ Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Web
|
||||
|
||||
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects")
|
||||
|
||||
## Current versions
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean) - ebean
|
||||
## Other versions
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.30.1</version>
|
||||
<version>11.34.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.30.1</tag>
|
||||
<tag>ebean-11.34.1</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -117,13 +117,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>4.4</version>
|
||||
<version>4.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-datasource</artifactId>
|
||||
<version>4.3.2</version>
|
||||
<version>4.3.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -135,13 +135,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>11.12.1</version>
|
||||
<version>11.14.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>4.7.1</version>
|
||||
<version>4.7.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson core used internally by Ebean -->
|
||||
@@ -229,7 +229,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-agent</artifactId>
|
||||
<version>11.25.1</version>
|
||||
<version>11.27.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -259,7 +259,7 @@
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
<version>6.4.0.jre8</version>
|
||||
<version>7.2.0.jre8</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -309,7 +309,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>11.25.1</version>
|
||||
<version>11.27.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package io.ebean;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -37,4 +39,21 @@ public interface BackgroundExecutor {
|
||||
* </p>
|
||||
*/
|
||||
void executePeriodically(Runnable r, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Schedules a Runnable for one-shot action that becomes enabled after the given delay.
|
||||
*
|
||||
* @return a ScheduledFuture representing pending completion of the task and
|
||||
* whose get() method will return null upon completion
|
||||
*/
|
||||
ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Schedules a Callable for one-shot action that becomes enabled after the given delay.
|
||||
*
|
||||
* @return a ScheduledFuture that can be used to extract result or cancel
|
||||
*/
|
||||
<V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package io.ebean;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Provides access to the internal state of an entity bean.
|
||||
*/
|
||||
@@ -111,4 +113,10 @@ public interface BeanState {
|
||||
* Reset the bean putting it into NEW state such that a save() results in an insert.
|
||||
*/
|
||||
void resetForInsert();
|
||||
|
||||
/**
|
||||
* Returns a map with load erros.
|
||||
*/
|
||||
@Nullable
|
||||
Map<String, Exception> getLoadErrors();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebean.config.ContainerConfig;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
|
||||
/**
|
||||
* Creates Database instances.
|
||||
* <p>
|
||||
* This uses either DatabaseConfig or properties in the application.properties file to
|
||||
* configure and create a Database instance.
|
||||
* </p>
|
||||
* <p>
|
||||
* The Database instance can either be registered with the DB singleton or
|
||||
* not. The DB singleton effectively holds a map of Database by a name.
|
||||
* If the Database is registered with the DB singleton you can retrieve it
|
||||
* later via {@link DB#byName(String)}.
|
||||
* </p>
|
||||
* <p>
|
||||
* One Database can be nominated as the 'default/primary' Database. Many
|
||||
* methods on the DB singleton such as {@link DB#find(Class)} are just a
|
||||
* convenient way of using the 'default/primary' Database.
|
||||
* </p>
|
||||
*/
|
||||
public class DatabaseFactory {
|
||||
|
||||
/**
|
||||
* Initialise the container with clustering configuration.
|
||||
* <p>
|
||||
* Call this prior to creating any Database instances or alternatively set the
|
||||
* ContainerConfig on the ServerConfig when creating the first Database instance.
|
||||
*/
|
||||
public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
|
||||
EbeanServerFactory.initialiseContainer(containerConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using ebean.properties to configure the server.
|
||||
*/
|
||||
public static synchronized Database create(String name) {
|
||||
return EbeanServerFactory.create(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using the ServerConfig object to configure the server.
|
||||
*/
|
||||
public static synchronized Database create(DatabaseConfig config) {
|
||||
return EbeanServerFactory.create(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using the ServerConfig additionally specifying a classLoader to use as the context class loader.
|
||||
*/
|
||||
public static synchronized Database createWithContextClassLoader(DatabaseConfig config, ClassLoader classLoader) {
|
||||
return EbeanServerFactory.createWithContextClassLoader(config, classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown gracefully all EbeanServers cleaning up any resources as required.
|
||||
* <p>
|
||||
* This is typically invoked via JVM shutdown hook and not explicitly called.
|
||||
* </p>
|
||||
*/
|
||||
public static synchronized void shutdown() {
|
||||
EbeanServerFactory.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -120,7 +120,7 @@ public final class Ebean {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Ebean.class);
|
||||
|
||||
static {
|
||||
EbeanVersion.getVersion(); // initalizes the version class and logs the version.
|
||||
EbeanVersion.getVersion(); // initialises the version class and logs the version.
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -64,6 +64,17 @@ public class Expr {
|
||||
return Ebean.getExpressionFactory().ieq(propertyName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* In Range - property >= value1 and property < value2.
|
||||
* <p>
|
||||
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
|
||||
* </p>
|
||||
*/
|
||||
public static Expression inRange(String propertyName, Object value1, Object value2) {
|
||||
|
||||
return Ebean.getExpressionFactory().inRange(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
|
||||
@@ -149,6 +149,14 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression ineObject(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* In Range - property >= value1 and property < value2.
|
||||
* <p>
|
||||
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
|
||||
* </p>
|
||||
*/
|
||||
Expression inRange(String propertyName, Object value1, Object value2);
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
|
||||
@@ -226,6 +226,32 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
int update(Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning true if a row is found.
|
||||
* <p>
|
||||
* The query is executed using max rows of 1 and will only select the id property.
|
||||
* This method is really just a convenient way to optimise a query to perform a
|
||||
* 'does a row exist in the db' check.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Example using a query bean:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
boolean exists();
|
||||
|
||||
/**
|
||||
* Execute the query iterating over the results.
|
||||
*
|
||||
@@ -290,7 +316,7 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Execute the query returning a list of values for a single property.
|
||||
* <p>
|
||||
*
|
||||
* <h3>Example 1:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -301,7 +327,7 @@ public interface ExpressionList<T> {
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
*
|
||||
* <h3>Example 2:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -323,7 +349,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Execute a query returning a single value of a single property/column.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* String name =
|
||||
@@ -408,7 +433,6 @@ public interface ExpressionList<T> {
|
||||
* If maxRows is not set on the query prior to calling findPagedList() then a
|
||||
* PersistenceException is thrown.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* PagedList<Order> pagedList = Ebean.find(Order.class)
|
||||
@@ -547,7 +571,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<CountedValue<Order.Status>> orderStatusCount =
|
||||
@@ -657,7 +680,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Equal to expression for the value at the given path in the JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonEqualTo("content", "path.other", 34)
|
||||
@@ -672,7 +694,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Not Equal to - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonNotEqualTo("content", "path.other", 34)
|
||||
@@ -687,7 +708,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Greater than - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonGreaterThan("content", "path.other", 34)
|
||||
@@ -698,7 +718,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Greater than or equal to - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonGreaterOrEqual("content", "path.other", 34)
|
||||
@@ -709,7 +728,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Less than - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonLessThan("content", "path.other", 34)
|
||||
@@ -720,7 +738,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Less than or equal to - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonLessOrEqualTo("content", "path.other", 34)
|
||||
@@ -731,7 +748,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Between - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonBetween("content", "orderDate", lowerDateTime, upperDateTime)
|
||||
@@ -745,7 +761,6 @@ public interface ExpressionList<T> {
|
||||
* <p>
|
||||
* This returns the list so that add() can be chained.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Customer> query = Ebean.find(Customer.class);
|
||||
@@ -788,8 +803,16 @@ public interface ExpressionList<T> {
|
||||
ExpressionList<T> ine(String propertyName, String value);
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
* In Range - property >= value1 and property < value2.
|
||||
* <p>
|
||||
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
|
||||
* </p>
|
||||
*/
|
||||
ExpressionList<T> inRange(String propertyName, Object value1, Object value2);
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
ExpressionList<T> between(String propertyName, Object value1, Object value2);
|
||||
|
||||
/**
|
||||
@@ -842,7 +865,6 @@ public interface ExpressionList<T> {
|
||||
* To get control over the options you can create an ExampleExpression and set
|
||||
* those options such as case insensitive etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // create an example bean and set the properties
|
||||
@@ -851,7 +873,7 @@ public interface ExpressionList<T> {
|
||||
* example.setName("Rob%");
|
||||
* example.setNotes("%something%");
|
||||
*
|
||||
* List<Customer> list = Ebean.find(Customer.class).where()
|
||||
* List<Customer> list = Ebean.find(Customer.class).where()
|
||||
* // pass the bean into the where() clause
|
||||
* .exampleLike(example)
|
||||
* // you can add other expressions to the same query
|
||||
@@ -860,7 +882,7 @@ public interface ExpressionList<T> {
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Similarly you can create an ExampleExpression
|
||||
* <p>
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Customer example = new Customer();
|
||||
@@ -1082,7 +1104,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Add expression for ALL of the given bit flags to be set.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().bitwiseAll("flags", BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
|
||||
@@ -1096,7 +1117,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Add expression for the given bit flags to be NOT set.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().bitwiseNot("flags", BwFlags.HAS_COLOUR)
|
||||
@@ -1147,6 +1167,13 @@ public interface ExpressionList<T> {
|
||||
* raw("add_days(orderDate, 10) < ?", someDate)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h4>Subquery example:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> raw(String raw, Object value);
|
||||
|
||||
@@ -1171,12 +1198,18 @@ public interface ExpressionList<T> {
|
||||
* then they are not translated. logical property name names (not fully
|
||||
* qualified) will still be translated to their physical name.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* raw("orderQty < shipQty")
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h4>Subquery example:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> raw(String raw);
|
||||
|
||||
@@ -1250,12 +1283,10 @@ public interface ExpressionList<T> {
|
||||
* typically you only explicitly need to use the and() junction
|
||||
* when it is nested inside an or() or not() junction.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Example: Nested and()
|
||||
*
|
||||
* Ebean.find(Customer.class)
|
||||
* .where()
|
||||
* .or()
|
||||
* .and() // nested and
|
||||
@@ -1276,16 +1307,27 @@ public interface ExpressionList<T> {
|
||||
* Return a list of expressions that will be joined by OR's.
|
||||
* This is exactly the same as disjunction();
|
||||
* <p>
|
||||
* <p>
|
||||
* Use endOr() or endJunction() to end the OR junction.
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Example: (status active OR anniversary is null)
|
||||
*
|
||||
* .where()
|
||||
* .or()
|
||||
* .eq("status", Customer.Status.ACTIVE)
|
||||
* .isNull("anniversary")
|
||||
* .orderBy().asc("name")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Example: Use or() to join
|
||||
* // two nested and() expressions
|
||||
*
|
||||
* Ebean.find(Customer.class)
|
||||
* .where()
|
||||
* .or()
|
||||
* .and()
|
||||
@@ -1309,8 +1351,8 @@ public interface ExpressionList<T> {
|
||||
* Use endNot() or endJunction() to end expressions being added to the
|
||||
* NOT expression list.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>@{code
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* .where()
|
||||
* .not()
|
||||
@@ -1319,12 +1361,11 @@ public interface ExpressionList<T> {
|
||||
* .endNot()
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <pre>@{code
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Example: nested not()
|
||||
*
|
||||
* Ebean.find(Customer.class)
|
||||
* .where()
|
||||
* .eq("status", Customer.Status.ACTIVE)
|
||||
* .not()
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
@@ -42,6 +45,32 @@ public interface ExtendedServer {
|
||||
*/
|
||||
void setClock(Clock clock);
|
||||
|
||||
/**
|
||||
* Execute the query returning true if a row is found.
|
||||
* <p>
|
||||
* The query is executed using max rows of 1 and will only select the id property.
|
||||
* This method is really just a convenient way to optimise a query to perform a
|
||||
* 'does a row exist in the db' check.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Example using a query bean:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
<T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
* Return the number of 'top level' or 'root' entities this query should return.
|
||||
*
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.ebean;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -112,26 +111,26 @@ public class Finder<I, T> {
|
||||
* <p>
|
||||
* This provides full access to the API such as explicit transaction demarcation etc.
|
||||
*/
|
||||
public EbeanServer db() {
|
||||
return Ebean.getServer(serverName);
|
||||
public Database db() {
|
||||
return DB.byName(serverName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return typically a different EbeanServer to the default.
|
||||
* Return typically a different Database to the default.
|
||||
* <p>
|
||||
* This is equivalent to {@link Ebean#getServer(String)}
|
||||
* This is equivalent to {@link DB#byName(String)}
|
||||
*
|
||||
* @param server The name of the EbeanServer. If this is null then the default EbeanServer is
|
||||
* @param server The name of the Database. If this is null then the default EbeanServer is
|
||||
* returned.
|
||||
*/
|
||||
public EbeanServer db(String server) {
|
||||
return Ebean.getServer(server);
|
||||
public Database db(String server) {
|
||||
return DB.byName(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entity reference for this ID.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#getReference(Class, Object)}
|
||||
* Equivalent to {@link Database#getReference(Class, Object)}
|
||||
*/
|
||||
@Nonnull
|
||||
public T ref(I id) {
|
||||
@@ -141,7 +140,7 @@ public class Finder<I, T> {
|
||||
/**
|
||||
* Retrieves an entity by ID.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#find(Class, Object)}
|
||||
* Equivalent to {@link Database#find(Class, Object)}
|
||||
*/
|
||||
@Nullable
|
||||
public T byId(I id) {
|
||||
@@ -151,7 +150,7 @@ public class Finder<I, T> {
|
||||
/**
|
||||
* Delete a bean by Id.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#delete(Class, Object)}
|
||||
* Equivalent to {@link Database#delete(Class, Object)}
|
||||
*/
|
||||
public void deleteById(I id) {
|
||||
db().delete(type, id);
|
||||
@@ -181,7 +180,7 @@ public class Finder<I, T> {
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#update(Class)}
|
||||
* Equivalent to {@link Database#update(Class)}
|
||||
*/
|
||||
public UpdateQuery<T> update() {
|
||||
return db().update(type);
|
||||
@@ -190,7 +189,7 @@ public class Finder<I, T> {
|
||||
/**
|
||||
* Creates a query.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#find(Class)}
|
||||
* Equivalent to {@link Database#find(Class)}
|
||||
*/
|
||||
public Query<T> query() {
|
||||
return db().find(type);
|
||||
|
||||
@@ -76,15 +76,14 @@ import io.ebean.bean.EntityBean;
|
||||
public abstract class Model {
|
||||
|
||||
/**
|
||||
* Return the underlying 'default' EbeanServer.
|
||||
* Return the underlying 'default' Database.
|
||||
* <p>
|
||||
* This provides full access to the API such as explicit transaction demarcation etc.
|
||||
* <p>
|
||||
* Example:
|
||||
* <pre>{@code
|
||||
*
|
||||
* Transaction transaction = Customer.db().beginTransaction();
|
||||
* try {
|
||||
* try (Transaction transaction = Customer.db().beginTransaction()) {
|
||||
*
|
||||
* // turn off cascade persist for this transaction
|
||||
* transaction.setPersistCascade(false);
|
||||
@@ -104,26 +103,21 @@ public abstract class Model {
|
||||
*
|
||||
* transaction.commit();
|
||||
*
|
||||
* } finally {
|
||||
* transaction.end();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public static EbeanServer db() {
|
||||
return Ebean.getDefaultServer();
|
||||
public static Database db() {
|
||||
return DB.getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a named EbeanServer that is typically different to the default server.
|
||||
* <p>
|
||||
* If you are using multiple databases then each database has a name and maps to a single
|
||||
* EbeanServer. You can use this method to get an EbeanServer for another database.
|
||||
* Return a named Database that is typically different to the default database.
|
||||
*
|
||||
* @param server The name of the EbeanServer. If this is null then the default EbeanServer is returned.
|
||||
* @param server The name of the Database. If this is null then the default Database is returned.
|
||||
*/
|
||||
public static EbeanServer db(String server) {
|
||||
return Ebean.getServer(server);
|
||||
public static Database db(String server) {
|
||||
return DB.byName(server);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,7 +140,7 @@ public abstract class Model {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#markAsDirty(Object)
|
||||
* @see Database#markAsDirty(Object)
|
||||
*/
|
||||
public void markAsDirty() {
|
||||
db().markAsDirty(this);
|
||||
@@ -182,7 +176,7 @@ public abstract class Model {
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#save(Object)
|
||||
* @see Database#save(Object)
|
||||
*/
|
||||
public void save() {
|
||||
db().save(this);
|
||||
@@ -202,7 +196,7 @@ public abstract class Model {
|
||||
/**
|
||||
* Update this entity.
|
||||
*
|
||||
* @see EbeanServer#update(Object)
|
||||
* @see Database#update(Object)
|
||||
*/
|
||||
public void update() {
|
||||
db().update(this);
|
||||
@@ -211,7 +205,7 @@ public abstract class Model {
|
||||
/**
|
||||
* Insert this entity.
|
||||
*
|
||||
* @see EbeanServer#insert(Object)
|
||||
* @see Database#insert(Object)
|
||||
*/
|
||||
public void insert() {
|
||||
db().insert(this);
|
||||
@@ -232,7 +226,7 @@ public abstract class Model {
|
||||
* deleted. Note that, if JDBC batch mode is used then this always returns true.
|
||||
* </p>
|
||||
*
|
||||
* @see EbeanServer#delete(Object)
|
||||
* @see Database#delete(Object)
|
||||
*/
|
||||
public boolean delete() {
|
||||
return db().delete(this);
|
||||
@@ -245,7 +239,7 @@ public abstract class Model {
|
||||
* want to perform a hard/permanent delete.
|
||||
* </p>
|
||||
*
|
||||
* @see EbeanServer#deletePermanent(Object)
|
||||
* @see Database#deletePermanent(Object)
|
||||
*/
|
||||
public boolean deletePermanent() {
|
||||
return db().deletePermanent(this);
|
||||
@@ -275,7 +269,7 @@ public abstract class Model {
|
||||
/**
|
||||
* Refreshes this entity from the database.
|
||||
*
|
||||
* @see EbeanServer#refresh(Object)
|
||||
* @see Database#refresh(Object)
|
||||
*/
|
||||
public void refresh() {
|
||||
db().refresh(this);
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.ebean.util.StringHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -412,15 +411,15 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
String[] chunks = orderByClause.split(",");
|
||||
for (String chunk : chunks) {
|
||||
String[] pairs = chunk.split(" ");
|
||||
Property p = parseProperty(pairs);
|
||||
Property p = parseProperty(chunk);
|
||||
if (p != null) {
|
||||
list.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Property parseProperty(String[] pairs) {
|
||||
private Property parseProperty(String chunk) {
|
||||
String[] pairs = chunk.split(" ");
|
||||
if (pairs.length == 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -446,8 +445,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
boolean asc = isAscending(wordList.get(1));
|
||||
return new Property(wordList.get(0), asc, wordList.get(2), wordList.get(3));
|
||||
}
|
||||
String m = "Expecting a 1, 2 or 4 words in [" + Arrays.toString(pairs) + "] but got " + wordList;
|
||||
throw new RuntimeException(m);
|
||||
return new Property(chunk.trim(), true);
|
||||
}
|
||||
|
||||
private boolean isAscending(String s) {
|
||||
|
||||
@@ -372,6 +372,27 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> setAutoTune(boolean autoTune);
|
||||
|
||||
/**
|
||||
* Execute the query allowing properties with invalid JSON to be collected and not fail the query.
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch a bean with JSON content
|
||||
* EBasicJsonList bean= Ebean.find(EBasicJsonList.class)
|
||||
* .setId(42)
|
||||
* .setAllowLoadErrors() // collect errors into bean state if we have invalid JSON
|
||||
* .findOne();
|
||||
*
|
||||
*
|
||||
* // get the invalid JSON errors from the bean state
|
||||
* Map<String, Exception> errors = server().getBeanState(bean).getLoadErrors();
|
||||
*
|
||||
* // If this map is not empty tell we have invalid JSON
|
||||
* // and should try and fix the JSON content or inform the user
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
Query<T> setAllowLoadErrors();
|
||||
|
||||
/**
|
||||
* Set the default lazy loading batch size to use.
|
||||
* <p>
|
||||
@@ -868,6 +889,37 @@ public interface Query<T> {
|
||||
*/
|
||||
boolean isCountDistinct();
|
||||
|
||||
/**
|
||||
* Execute the query returning true if a row is found.
|
||||
* <p>
|
||||
* The query is executed using max rows of 1 and will only select the id property.
|
||||
* This method is really just a convenient way to optimise a query to perform a
|
||||
* 'does a row exist in the db' check.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Example using a query bean:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists =
|
||||
* new QContact()
|
||||
* .email.equalTo("rob@foo.com")
|
||||
* .exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = query()
|
||||
* .where().eq("email", "rob@foo.com")
|
||||
* .exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
boolean exists();
|
||||
|
||||
/**
|
||||
* Execute the query returning either a single bean or null (if no matching
|
||||
* bean is found).
|
||||
@@ -1570,11 +1622,50 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> alias(String alias);
|
||||
|
||||
/**
|
||||
* Set the base table to use for this query.
|
||||
* <p>
|
||||
* Typically this is used when a table has partitioning and we wish to specify a specific
|
||||
* partition/table to query against.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* QOrder()
|
||||
* .setBaseTable("order_2019_05")
|
||||
* .status.equalTo(Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
Query<T> setBaseTable(String baseTable);
|
||||
|
||||
/**
|
||||
* Return the type of beans being queried.
|
||||
*/
|
||||
Class<T> getBeanType();
|
||||
|
||||
/**
|
||||
* Restrict the query to only return subtypes of the given inherit type.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Animal> animals =
|
||||
* new QAnimal()
|
||||
* .name.startsWith("Fluffy")
|
||||
* .setInheritType(Cat.class)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param type An inheritance subtype of the
|
||||
*/
|
||||
Query<T> setInheritType(Class<? extends T> type);
|
||||
|
||||
/**
|
||||
* Returns the inherit type. This is normally the same as getBeanType() returns as long as no other type is set.
|
||||
*/
|
||||
Class<? extends T> getInheritType();
|
||||
|
||||
/**
|
||||
* Return the type of query being executed.
|
||||
*/
|
||||
|
||||
@@ -10,34 +10,30 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Query object for performing native SQL queries that return SqlRow's.
|
||||
* <p>
|
||||
* Firstly note that you can use your own sql queries with <em>entity beans</em>
|
||||
* by using the SqlSelect annotation. This should be your first approach when
|
||||
* wanting to use your own SQL queries.
|
||||
* </p>
|
||||
* <p>
|
||||
* If ORM Mapping is too tight and constraining for your problem then SqlQuery
|
||||
* could be a good approach.
|
||||
* </p>
|
||||
* Query object for performing native SQL queries that return SqlRow or directly read
|
||||
* ResultSet using a RowMapper.
|
||||
* <p>
|
||||
* The returned SqlRow objects are similar to a LinkedHashMap with some type
|
||||
* conversion support added.
|
||||
* </p>
|
||||
* <p>
|
||||
* Refer to {@link DtoQuery} for native sql queries returning DTO beans.
|
||||
* </p>
|
||||
* <p>
|
||||
* Refer to {@link Database#findNative(Class, String)} for native sql queries returning entity beans.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // its typically a good idea to use a named query
|
||||
* // and put the sql in the orm.xml instead of in your code
|
||||
* // example using named parameters
|
||||
*
|
||||
* String sql = "select id, name from customer where name like :name and status_code = :status";
|
||||
*
|
||||
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
|
||||
* sqlQuery.setParameter("name", "Acme%");
|
||||
* sqlQuery.setParameter("status", "ACTIVE");
|
||||
*
|
||||
* // execute the query returning a List of MapBean objects
|
||||
* List<SqlRow> list = sqlQuery.findList();
|
||||
* List<SqlRow> list =
|
||||
* DB.sqlQuery(sql)
|
||||
* .setParameter("name", "Acme%")
|
||||
* .setParameter("status", "ACTIVE")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@@ -104,7 +100,7 @@ public interface SqlQuery extends Serializable {
|
||||
*
|
||||
* String sql = "select id, name, status from customer order by name desc";
|
||||
*
|
||||
* Ebean.createSqlQuery(sql)
|
||||
* DB.sqlQuery(sql)
|
||||
* .findEachRow((resultSet, rowNum) -> {
|
||||
*
|
||||
* // read directly from ResultSet
|
||||
@@ -134,7 +130,7 @@ public interface SqlQuery extends Serializable {
|
||||
*
|
||||
* String sql = "select max(unit_price) from o_order_detail where order_qty > ?";
|
||||
*
|
||||
* BigDecimal maxPrice = Ebean.createSqlQuery(sql)
|
||||
* BigDecimal maxPrice = DB.sqlQuery(sql)
|
||||
* .setParameter(1, 2)
|
||||
* .findSingleAttribute(BigDecimal.class);
|
||||
*
|
||||
@@ -176,9 +172,10 @@ public interface SqlQuery extends Serializable {
|
||||
* " order by (unit_price * order_qty) desc";
|
||||
*
|
||||
* //
|
||||
* List<BigDecimal> lineAmounts = Ebean.createSqlQuery(sql)
|
||||
* .setParameter(1, 3)
|
||||
* .findSingleAttributeList(BigDecimal.class);
|
||||
* List<BigDecimal> lineAmounts =
|
||||
* DB.sqlQuery(sql)
|
||||
* .setParameter(1, 3)
|
||||
* .findSingleAttributeList(BigDecimal.class);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -195,6 +192,33 @@ public interface SqlQuery extends Serializable {
|
||||
*/
|
||||
SqlQuery setParameter(String name, Object value);
|
||||
|
||||
/**
|
||||
* Set one of more positioned parameters.
|
||||
* <p>
|
||||
* This is a convenient alternative to multiple calls setParameter().
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "select id, name from customer where name like ? and status = ?";
|
||||
*
|
||||
* List<SqlRow> list =
|
||||
* DB.sqlQuery(sql)
|
||||
* .setParams("Rob", Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
*
|
||||
* // is the same as ...
|
||||
*
|
||||
* List<SqlRow> list =
|
||||
* DB.sqlQuery(sql)
|
||||
* .setParameter(1, "Rob")
|
||||
* .setParameter(2, "Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
SqlQuery setParams(Object... values);
|
||||
|
||||
/**
|
||||
* The same as bind for positioned parameters.
|
||||
*/
|
||||
|
||||
@@ -16,20 +16,33 @@ package io.ebean;
|
||||
* notify Ebean of external changes and enable Ebean to maintain it's "L2"
|
||||
* server cache.
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example that uses 'named' parameters
|
||||
* // example using 'positioned' parameters
|
||||
*
|
||||
* String s = "UPDATE f_topic set post_count = :count where id = :id";
|
||||
* String sql = "insert into audit_log (group, title, description) values (?, ?, ?);
|
||||
*
|
||||
* SqlUpdate update = Ebean.createSqlUpdate(s);
|
||||
* update.setParameter("id", 1);
|
||||
* update.setParameter("count", 50);
|
||||
* int rows =
|
||||
* DB.sqlUpdate(sql)
|
||||
* .setParams("login", "new user", "user rob was created")
|
||||
* .executeNow();
|
||||
*
|
||||
* int modifiedCount = update.execute();
|
||||
* }</pre>
|
||||
*
|
||||
* String msg = "There were " + modifiedCount + " rows updated";
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example using 'named' parameters
|
||||
*
|
||||
* String sql = "update topic set post_count = :count where id = :id";
|
||||
*
|
||||
* int rows =
|
||||
* DB.sqlUpdate(sql)
|
||||
* .setParameter("id", 1)
|
||||
* .setParameter("count", 50)
|
||||
* .execute();
|
||||
*
|
||||
* String msg = "There were " + rows + " rows updated";
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -37,9 +50,10 @@ package io.ebean;
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "insert into audit_log (id, description, modified_description) values (?,?,?)";
|
||||
* SqlUpdate insert = Ebean.createSqlUpdate(sql);
|
||||
*
|
||||
* try (Transaction txn = Ebean.beginTransaction()) {
|
||||
* SqlUpdate insert = DB.sqlUpdate(sql);
|
||||
*
|
||||
* try (Transaction txn = DB.beginTransaction()) {
|
||||
* txn.setBatchMode(true);
|
||||
*
|
||||
* insert.setNextParameter(10000);
|
||||
@@ -65,7 +79,7 @@ package io.ebean;
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* try (Transaction txn = Ebean.beginTransaction()) {
|
||||
* try (Transaction txn = DB.beginTransaction()) {
|
||||
*
|
||||
* insert.setNextParameter(10000);
|
||||
* insert.setNextParameter("hello");
|
||||
@@ -217,6 +231,33 @@ public interface SqlUpdate {
|
||||
*/
|
||||
SqlUpdate setTimeout(int secs);
|
||||
|
||||
/**
|
||||
* Set one of more positioned parameters.
|
||||
* <p>
|
||||
* This is a convenient alternative to multiple setParameter() calls.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "insert into audit_log (id, name, version) values (?,?,?)";
|
||||
*
|
||||
* DB.sqlUpdate(sql)
|
||||
* .setParams(UUID.randomUUID(), "Hello", 1)
|
||||
* .executeNow();
|
||||
*
|
||||
*
|
||||
* // is the same as ...
|
||||
*
|
||||
* DB.sqlUpdate(sql)
|
||||
* .setParameter(1, UUID.randomUUID())
|
||||
* .setParameter(2, "Hello")
|
||||
* .setParameter(3, 1)
|
||||
* .executeNow();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*/
|
||||
SqlUpdate setParams(Object... values);
|
||||
|
||||
/**
|
||||
* Set the next positioned parameter.
|
||||
*
|
||||
|
||||
@@ -244,8 +244,7 @@ public interface Transaction extends AutoCloseable {
|
||||
*
|
||||
* // assume Customer has L2 bean caching enabled ...
|
||||
*
|
||||
* Transaction transaction = Ebean.beginTransaction();
|
||||
* try {
|
||||
* try (Transaction transaction = ebeanServer.beginTransaction()) {
|
||||
*
|
||||
* // this uses L2 bean cache as the transaction
|
||||
* // ... is considered "query only" at this point
|
||||
@@ -253,7 +252,7 @@ public interface Transaction extends AutoCloseable {
|
||||
*
|
||||
* // transaction no longer "query only" once
|
||||
* // ... a bean has been saved etc
|
||||
* Ebean.save(someBean);
|
||||
* someBean.save();
|
||||
*
|
||||
* // will NOT use L2 bean cache as the transaction
|
||||
* // ... is no longer considered "query only"
|
||||
@@ -273,8 +272,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* Customer.find.byId(99); // skips l2 bean cache
|
||||
*
|
||||
*
|
||||
* } finally {
|
||||
* transaction.end();
|
||||
* transaction.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
@@ -290,36 +288,66 @@ public interface Transaction extends AutoCloseable {
|
||||
boolean isSkipCache();
|
||||
|
||||
/**
|
||||
* Turn on or off statement batching. Statement batching can be transparent
|
||||
* for drivers and databases that support getGeneratedKeys. Otherwise you may
|
||||
* wish to specifically control when batching is used via this method.
|
||||
* <p>
|
||||
* Refer to <code>java.sql.PreparedStatement.addBatch();</code>
|
||||
* <p>
|
||||
* Note that you may also wish to use the setPersistCascade method to stop
|
||||
* save and delete cascade behaviour. You may do this to have full control
|
||||
* over the order of execution rather than the normal cascading fashion.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that the <em>execution order</em> in batch mode may be different from
|
||||
* non batch mode execution order. Also note that <em>insert behaviour</em>
|
||||
* may be different depending on the JDBC driver and its support for
|
||||
* getGeneratedKeys. That is, for JDBC drivers that do not support
|
||||
* getGeneratedKeys you may not get back the generated IDs (used for inserting
|
||||
* associated detail beans etc).
|
||||
* </p>
|
||||
* Turn on or off use of JDBC statement batching.
|
||||
* <p>
|
||||
* Calls to save(), delete(), insert() and execute() all support batch
|
||||
* processing. This includes normal beans, MapBean, CallableSql and UpdateSql.
|
||||
* processing. This includes normal beans, CallableSql and UpdateSql.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* try (Transaction transaction = server.beginTransaction()) {
|
||||
*
|
||||
* // turn on JDBC batch
|
||||
* transaction.setBatchMode(true);
|
||||
*
|
||||
* // tune the batch size
|
||||
* transaction.setBatchSize(50);
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* transaction.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>getGeneratedKeys</h3>
|
||||
* <p>
|
||||
* The flushing of the batched statements is automatic but you can call
|
||||
* batchFlush when you like. Note that flushing occurs when a query is
|
||||
* executed or when you mix UpdateSql and CallableSql with save and delete of
|
||||
* Often with large batch inserts we want to turn off getGeneratedKeys. We do
|
||||
* this via {@link #setBatchGetGeneratedKeys(boolean)}.
|
||||
* Also note that some JDBC drivers do not support getGeneratedKeys in JDBC batch mode.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* try (Transaction transaction = server.beginTransaction()) {
|
||||
*
|
||||
* transaction.setBatchMode(true);
|
||||
* transaction.setBatchSize(100);
|
||||
* // insert but don't bother getting back the generated keys
|
||||
* transaction.setBatchGetGeneratedKeys(false);
|
||||
*
|
||||
*
|
||||
* // perform lots of inserts ...
|
||||
* ...
|
||||
*
|
||||
* transaction.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Flush</h3>
|
||||
* <p>
|
||||
* The batch is automatically flushed when it hits the batch size and also when we
|
||||
* execute queries or when we mix UpdateSql and CallableSql with save and delete of
|
||||
* beans.
|
||||
* </p>
|
||||
* <p>
|
||||
* Example: batch processing executing every 3 rows
|
||||
* We use {@link #flush()} to explicitly flush the batch and we can use
|
||||
* {@link #setBatchFlushOnQuery(boolean)} and {@link #setBatchFlushOnMixed(boolean)}
|
||||
* to control the automatic flushing behaviour.
|
||||
* </p>
|
||||
* <p>
|
||||
* Example: batch processing of CallableSql executing every 10 rows
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
@@ -334,24 +362,21 @@ public interface Transaction extends AutoCloseable {
|
||||
* CallableSql cs = new CallableSql(sql);
|
||||
* cs.registerOut(2, Types.INTEGER);
|
||||
*
|
||||
* // (optional) inform eBean this stored procedure
|
||||
* // (optional) inform Ebean this stored procedure
|
||||
* // inserts into a table called sp_test
|
||||
* cs.addModification("sp_test", true, false, false);
|
||||
*
|
||||
* Transaction txn = ebeanServer.beginTransaction();
|
||||
* txn.setBatchMode(true);
|
||||
* txn.setBatchSize(3);
|
||||
* try {
|
||||
* try (Transaction txn = ebeanServer.beginTransaction()) {
|
||||
* txn.setBatchMode(true);
|
||||
* txn.setBatchSize(10);
|
||||
*
|
||||
* for (int i = 0; i < da.length;) {
|
||||
* cs.setParameter(1, da[i]);
|
||||
* ebeanServer.execute(cs);
|
||||
* }
|
||||
*
|
||||
* // NB: commit implicitly flushes
|
||||
* // Note: commit implicitly flushes
|
||||
* txn.commit();
|
||||
*
|
||||
* } finally {
|
||||
* txn.end();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
|
||||
@@ -98,6 +98,8 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
private Object[] origValues;
|
||||
|
||||
private Exception[] loadErrors;
|
||||
|
||||
private int lazyLoadProperty = -1;
|
||||
|
||||
private Object ownerId;
|
||||
@@ -1115,4 +1117,36 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void setSortOrder(int sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the load error that happened on this property.
|
||||
*/
|
||||
public void setLoadError(int propertyIndex, Exception t) {
|
||||
if (loadErrors == null) {
|
||||
loadErrors = new Exception[owner._ebean_getPropertyNames().length];
|
||||
}
|
||||
loadErrors[propertyIndex] = t;
|
||||
flags[propertyIndex] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loadErrors.
|
||||
*/
|
||||
public Map<String, Exception> getLoadErrors() {
|
||||
if (loadErrors == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Exception> ret = null;
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
Exception loadError = loadErrors[i];
|
||||
if (loadError != null) {
|
||||
if (ret == null) {
|
||||
ret = new LinkedHashMap<>();
|
||||
}
|
||||
ret.put(getProperty(i), loadError);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ public class ServerCacheNotification {
|
||||
this.dependentTables = dependentTables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ts:" + modifyTimestamp + " tables:" + dependentTables;
|
||||
}
|
||||
|
||||
public long getModifyTimestamp() {
|
||||
return modifyTimestamp;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,15 @@ public class ServerCacheOptions {
|
||||
copy.maxIdleSecs = maxIdleSecs;
|
||||
copy.maxSecsToLive = maxSecsToLive;
|
||||
copy.trimFrequency = trimFrequency;
|
||||
copy.nearCache = this.nearCache;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this object with nearCache option.
|
||||
*/
|
||||
public ServerCacheOptions copy(boolean nearCache) {
|
||||
ServerCacheOptions copy = copy();
|
||||
copy.nearCache = nearCache;
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import io.ebean.DatabaseFactory;
|
||||
|
||||
/**
|
||||
* The configuration used for creating a Database.
|
||||
* <p>
|
||||
* Used to programmatically construct an Database and optionally register it
|
||||
* with the DB singleton.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you just use DB thout this programmatic configuration Ebean will read
|
||||
* the application.properties file and take the configuration from there. This usually
|
||||
* includes searching the class path and automatically registering any entity
|
||||
* classes and listeners etc.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DatabaseConfig config = new DatabaseConfig();
|
||||
*
|
||||
* // read the ebean.properties and load
|
||||
* // those settings into this serverConfig object
|
||||
* config.loadFromProperties();
|
||||
*
|
||||
* // explicitly register the entity beans to avoid classpath scanning
|
||||
* config.addClass(Customer.class);
|
||||
* config.addClass(User.class);
|
||||
*
|
||||
* Database db = DatabaseFactory.create(config);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Note that ServerConfigProvider provides a standard Java ServiceLoader mechanism that can
|
||||
* be used to apply configuration to the DatabaseConfig.
|
||||
* </p>
|
||||
*
|
||||
* @author emcgreal
|
||||
* @author rbygrave
|
||||
* @see DatabaseFactory
|
||||
*/
|
||||
public class DatabaseConfig extends ServerConfig {
|
||||
|
||||
}
|
||||
@@ -252,6 +252,8 @@ public class ServerConfig {
|
||||
|
||||
private boolean ddlRun;
|
||||
|
||||
private boolean ddlExtra = true;
|
||||
|
||||
private boolean ddlCreateOnly;
|
||||
|
||||
private String ddlInitSql;
|
||||
@@ -366,9 +368,10 @@ public class ServerConfig {
|
||||
private UuidVersion uuidVersion = UuidVersion.VERSION4;
|
||||
|
||||
/**
|
||||
* The UUID state file (for Version 1 UUIDs).
|
||||
* The UUID state file (for Version 1 UUIDs). By default, the file is created in
|
||||
* ${HOME}/.ebean/${servername}-uuid.state
|
||||
*/
|
||||
private String uuidStateFile = "ebean-uuid.state";
|
||||
private String uuidStateFile;
|
||||
|
||||
/**
|
||||
* The clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
|
||||
@@ -633,12 +636,11 @@ public class ServerConfig {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param cls The
|
||||
* @param <P>
|
||||
* @return
|
||||
* @param cls The type of the service object to obtain
|
||||
* @return The service object given the class type
|
||||
*/
|
||||
public <P> P getServiceObject(Class<P> cls) {
|
||||
return (P)serviceObject.get(serviceObjectKey(cls));
|
||||
return (P) serviceObject.get(serviceObjectKey(cls));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2116,6 +2118,16 @@ public class ServerConfig {
|
||||
this.ddlRun = ddlRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false if you not want to run the extra-ddl.xml scripts. (default = true)
|
||||
* <p>
|
||||
* Typically we want this on when we are running tests.
|
||||
*/
|
||||
public void setDdlExtra(boolean ddlExtra) {
|
||||
this.ddlExtra = ddlExtra;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the "drop all ddl" should be skipped.
|
||||
* <p>
|
||||
@@ -2186,6 +2198,13 @@ public class ServerConfig {
|
||||
return ddlRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true, if extra-ddl.xml should be executed.
|
||||
*/
|
||||
public boolean isDdlExtra() {
|
||||
return ddlExtra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the class path search should be disabled.
|
||||
*/
|
||||
@@ -2962,6 +2981,7 @@ public class ServerConfig {
|
||||
|
||||
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
|
||||
ddlRun = p.getBoolean("ddl.run", ddlRun);
|
||||
ddlExtra = p.getBoolean("ddl.extra", ddlExtra);
|
||||
ddlCreateOnly = p.getBoolean("ddl.createOnly", ddlCreateOnly);
|
||||
ddlInitSql = p.get("ddl.initSql", ddlInitSql);
|
||||
ddlSeedSql = p.get("ddl.seedSql", ddlSeedSql);
|
||||
|
||||
@@ -50,6 +50,11 @@ public interface DbMigration {
|
||||
throw new IllegalStateException("No service implementation found for DbMigration?");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false to suppress logging to System out.
|
||||
*/
|
||||
void setLogToSystemOut(boolean logToSystemOut);
|
||||
|
||||
/**
|
||||
* Set the path from the current working directory to the application resources.
|
||||
* <p>
|
||||
|
||||
@@ -17,6 +17,16 @@ public interface TimedMetric {
|
||||
*/
|
||||
void add(long micros, long beans);
|
||||
|
||||
/**
|
||||
* Add a time event given the start nanos.
|
||||
*/
|
||||
void addSinceNanos(long startNanos);
|
||||
|
||||
/**
|
||||
* Add a time event given the start nanos and bean count.
|
||||
*/
|
||||
void addSinceNanos(long startNanos, long beans);
|
||||
|
||||
/**
|
||||
* Return true if there are no metrics collected since the last collection.
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,16 @@ import io.ebean.meta.MetricVisitor;
|
||||
*/
|
||||
public interface TimedMetricMap {
|
||||
|
||||
/**
|
||||
* Add a time event given the start nanos.
|
||||
*/
|
||||
void addSinceNanos(String key, long startNanos);
|
||||
|
||||
/**
|
||||
* Add a time event given the start nanos and beans.
|
||||
*/
|
||||
void addSinceNanos(String key, long startNanos, int beans);
|
||||
|
||||
/**
|
||||
* Add an execution for the given key.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebean.plugin;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* Errorhandler to handle load errors and may be recover correct value.
|
||||
*
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LoadErrorHandler {
|
||||
void handleLoadError(EntityBean bean, Property prop, String fullName, Exception e);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package io.ebean.text;
|
||||
public class TextException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1601310159486033148L;
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* Construct with an error message.
|
||||
@@ -27,4 +28,41 @@ public class TextException extends RuntimeException {
|
||||
public TextException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a detailed exception.
|
||||
*
|
||||
* @param message
|
||||
* the message. The placeholder {} will be replaced by
|
||||
* <code>text</code>
|
||||
* @param text
|
||||
* the fault text.
|
||||
* @param cause
|
||||
* the case
|
||||
*/
|
||||
public TextException(String message, String text, Exception cause) {
|
||||
super(message.replace("{}", String.valueOf(text)), cause);
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a detailed exception.
|
||||
*
|
||||
* @param message
|
||||
* the message. The placeholder {} will be replaced by
|
||||
* <code>text</code>
|
||||
* @param text
|
||||
* the fault text.
|
||||
*/
|
||||
public TextException(String message, String text) {
|
||||
super(message.replace("{}", String.valueOf(text)));
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text, that caused the error. (e.g. the JSON). May be null.
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,14 @@ public class BindParams implements Serializable {
|
||||
public BindParams() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset positioned parameters (usually due to bind parameter expansion).
|
||||
*/
|
||||
public void reset() {
|
||||
bindHash = null;
|
||||
positionedParameters.clear();
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
int hc = namedParameters.hashCode();
|
||||
for (Param positionedParameter : positionedParameters) {
|
||||
@@ -171,8 +179,7 @@ public class BindParams implements Serializable {
|
||||
}
|
||||
|
||||
private Param getParam(String name) {
|
||||
Param p = namedParameters.computeIfAbsent(name, k -> new Param());
|
||||
return p;
|
||||
return namedParameters.computeIfAbsent(name, k -> new Param());
|
||||
}
|
||||
|
||||
private Param getParam(int position) {
|
||||
|
||||
@@ -11,4 +11,8 @@ public interface CQueryPlanKey {
|
||||
*/
|
||||
String getPartialKey();
|
||||
|
||||
/**
|
||||
* Return a query plan key taking into account persist cascading.
|
||||
*/
|
||||
CQueryPlanKey withDeleteByIds();
|
||||
}
|
||||
|
||||
@@ -439,6 +439,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
void setDelete();
|
||||
|
||||
/**
|
||||
* Set the query to be delete by ids due to cascading delete.
|
||||
*/
|
||||
CQueryPlanKey setDeleteByIdsPlan();
|
||||
|
||||
/**
|
||||
* Set the query to select the id property only.
|
||||
*/
|
||||
@@ -749,23 +754,6 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
boolean isDisableLazyLoading();
|
||||
|
||||
/**
|
||||
* Internally set by Ebean when this query must use the DISTINCT keyword.
|
||||
* <p>
|
||||
* This does not exclude/remove the use of the id property.
|
||||
*/
|
||||
void setSqlDistinct(boolean sqlDistinct);
|
||||
|
||||
/**
|
||||
* Return true if this query has been specified by a user or internally by Ebean to use DISTINCT.
|
||||
*/
|
||||
boolean isDistinctQuery();
|
||||
|
||||
/**
|
||||
* Return true if this was internally set to sql distinct (ie. many where predicate).
|
||||
*/
|
||||
boolean isSqlDistinct();
|
||||
|
||||
/**
|
||||
* Return true if this query has been specified by a user to use DISTINCT.
|
||||
*/
|
||||
@@ -837,6 +825,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* Return the base table to use if user defined on the query.
|
||||
*/
|
||||
String getBaseTable();
|
||||
|
||||
/**
|
||||
* Return root table alias set by {@link #alias(String)} command.
|
||||
*/
|
||||
@@ -861,4 +854,9 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
* Returns the count distinct order setting.
|
||||
*/
|
||||
CountDistinctOrder getCountDistinctOrder();
|
||||
|
||||
/**
|
||||
* Handles load errors.
|
||||
*/
|
||||
void handleLoadError(String fullName, Exception e);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@ import io.ebean.SqlUpdate;
|
||||
|
||||
public interface SpiSqlUpdate extends SqlUpdate {
|
||||
|
||||
/**
|
||||
* Return the sql taking into account bind parameter expansion.
|
||||
*/
|
||||
String getBaseSql();
|
||||
|
||||
/**
|
||||
* Return the Bind parameters.
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,7 @@ public class DdlGenerator {
|
||||
|
||||
private final boolean generateDdl;
|
||||
private final boolean runDdl;
|
||||
private final boolean extraDdl;
|
||||
private final boolean createOnly;
|
||||
private final boolean jaxbPresent;
|
||||
private final boolean ddlCommitOnCreateIndex;
|
||||
@@ -54,6 +55,7 @@ public class DdlGenerator {
|
||||
this.server = server;
|
||||
this.jaxbPresent = serverConfig.getClassLoadConfig().isJavaxJAXBPresent();
|
||||
this.generateDdl = serverConfig.isDdlGenerate();
|
||||
this.extraDdl = serverConfig.isDdlExtra();
|
||||
this.createOnly = serverConfig.isDdlCreateOnly();
|
||||
this.dbSchema = serverConfig.getDbSchema();
|
||||
if (!serverConfig.getTenantMode().isDdlEnabled() && serverConfig.isDdlRun()) {
|
||||
@@ -166,8 +168,7 @@ public class DdlGenerator {
|
||||
|
||||
protected void runDropSql(Connection connection) throws IOException {
|
||||
if (!createOnly) {
|
||||
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
|
||||
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
|
||||
if (extraDdl && jaxbPresent) {
|
||||
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), true);
|
||||
if (extraApply != null) {
|
||||
runScript(connection, false, extraApply, "extra-ddl");
|
||||
@@ -187,8 +188,7 @@ public class DdlGenerator {
|
||||
}
|
||||
runScript(connection, false, createAllContent, getCreateFileName());
|
||||
|
||||
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
|
||||
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
|
||||
if (extraDdl && jaxbPresent) {
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
|
||||
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
|
||||
|
||||
@@ -72,6 +72,8 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
private static final String GENERATED_COMMENT = "THIS IS A GENERATED FILE - DO NOT MODIFY";
|
||||
|
||||
private boolean logToSystemOut = true;
|
||||
|
||||
/**
|
||||
* Set to true if DefaultDbMigration run with online EbeanServer instance.
|
||||
*/
|
||||
@@ -316,6 +318,11 @@ public class DefaultDbMigration implements DbMigration {
|
||||
return generateDiff(request);
|
||||
}
|
||||
|
||||
} catch (UnknownResourcePathException e) {
|
||||
logError("ERROR - " + e.getMessage());
|
||||
logError("Check the working directory or change dbMigration.setPathToResources() value?");
|
||||
return null;
|
||||
|
||||
} finally {
|
||||
if (!online) {
|
||||
DbOffline.reset();
|
||||
@@ -387,8 +394,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
private void writeExtraDdl(File migrationDir, DdlScript script) throws IOException {
|
||||
|
||||
String fullName = repeatableMigrationName(script.isInit(), script.getName());
|
||||
|
||||
logger.info("writing repeatable script {}", fullName);
|
||||
logger.debug("writing repeatable script {}", fullName);
|
||||
|
||||
File file = new File(migrationDir, fullName);
|
||||
try (FileWriter writer = new FileWriter(file)) {
|
||||
@@ -397,6 +403,30 @@ public class DefaultDbMigration implements DbMigration {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogToSystemOut(boolean logToSystemOut) {
|
||||
this.logToSystemOut = logToSystemOut;
|
||||
}
|
||||
|
||||
private void logError(String message) {
|
||||
if (logToSystemOut) {
|
||||
System.out.println("DbMigration> " + message);
|
||||
} else {
|
||||
logger.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void logInfo(String message, Object value) {
|
||||
if (value != null) {
|
||||
message = String.format(message, value);
|
||||
}
|
||||
if (logToSystemOut) {
|
||||
System.out.println("DbMigration> " + message);
|
||||
} else {
|
||||
logger.info(message);
|
||||
}
|
||||
}
|
||||
|
||||
private String repeatableMigrationName(boolean init, String scriptName) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (init) {
|
||||
@@ -416,12 +446,12 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
List<String> pendingDrops = request.getPendingDrops();
|
||||
if (!pendingDrops.isEmpty()) {
|
||||
logger.info("Pending un-applied drops in versions {}", pendingDrops);
|
||||
logInfo("Pending un-applied drops in versions %s", pendingDrops);
|
||||
}
|
||||
|
||||
Migration migration = request.createDiffMigration();
|
||||
if (migration == null) {
|
||||
logger.info("no changes detected - no migration written");
|
||||
logInfo("no changes detected - no migration written", null);
|
||||
return null;
|
||||
} else {
|
||||
// there were actually changes to write
|
||||
@@ -440,7 +470,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
List<String> pendingDrops = request.getPendingDrops();
|
||||
if (!pendingDrops.isEmpty()) {
|
||||
logger.info("... remaining pending un-applied drops in versions {}", pendingDrops);
|
||||
logInfo("... remaining pending un-applied drops in versions %s", pendingDrops);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
@@ -520,9 +550,9 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
String fullVersion = getFullVersion(request.nextVersion(), dropsFor);
|
||||
|
||||
logger.info("generating migration:{}", fullVersion);
|
||||
logInfo("generating migration:%s", fullVersion);
|
||||
if (!request.dbinitMigration && !writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
|
||||
logger.warn("migration already exists, not generating DDL");
|
||||
logError("migration already exists, not generating DDL");
|
||||
return null;
|
||||
} else {
|
||||
if (!platforms.isEmpty()) {
|
||||
@@ -627,7 +657,6 @@ public class DefaultDbMigration implements DbMigration {
|
||||
if (vanillaPlatform || databasePlatform == null) {
|
||||
// not explicitly set so use the platform of the server
|
||||
databasePlatform = server.getDatabasePlatform();
|
||||
logger.trace("set platform to {}", databasePlatform.getName());
|
||||
}
|
||||
if (migrationConfig != null) {
|
||||
if (strictMode != null) {
|
||||
@@ -713,13 +742,17 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
// path to src/main/resources in typical maven project
|
||||
File resourceRootDir = new File(pathToResources);
|
||||
if (!resourceRootDir.exists()) {
|
||||
String msg = String.format("Error - path to resources %s does not exist. Absolute path is %s", pathToResources, resourceRootDir.getAbsolutePath());
|
||||
throw new UnknownResourcePathException(msg);
|
||||
}
|
||||
String resourcePath = migrationConfig.getMigrationPath(dbinitMigration);
|
||||
|
||||
// expect to be a path to something like - src/main/resources/dbmigration/model
|
||||
File path = new File(resourceRootDir, resourcePath);
|
||||
if (!path.exists()) {
|
||||
if (!path.mkdirs()) {
|
||||
logger.debug("Unable to ensure migration directory exists at {}", path.getAbsolutePath());
|
||||
logInfo("Warning - Unable to ensure migration directory exists at %s", path.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
return path;
|
||||
@@ -735,7 +768,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
}
|
||||
File modelDir = new File(migrationDirectory, migrationConfig.getModelPath());
|
||||
if (!modelDir.exists() && !modelDir.mkdirs()) {
|
||||
logger.debug("Unable to ensure migration model directory exists at {}", modelDir.getAbsolutePath());
|
||||
logInfo("Warning - Unable to ensure migration model directory exists at %s", modelDir.getAbsolutePath());
|
||||
}
|
||||
return modelDir;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebeaninternal.dbmigration;
|
||||
|
||||
/**
|
||||
* Exception when db migration resource path does not exist.
|
||||
* <p>
|
||||
* Typically the working directory or pathToResources is incorrect.
|
||||
* </p>
|
||||
*/
|
||||
public class UnknownResourcePathException extends RuntimeException {
|
||||
|
||||
public UnknownResourcePathException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -147,7 +147,11 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
|
||||
visitScalar(p);
|
||||
if (p instanceof BeanPropertyAssocOne) {
|
||||
visitOneImported((BeanPropertyAssocOne)p);
|
||||
} else {
|
||||
visitScalar(p);
|
||||
}
|
||||
if (embedded.isId()) {
|
||||
// compound primary key
|
||||
lastColumn.setPrimaryKey(true);
|
||||
|
||||
@@ -10,14 +10,14 @@ import java.util.Map;
|
||||
class CacheChangeBeanUpdate implements CacheChange {
|
||||
|
||||
private final BeanDescriptor<?> desc;
|
||||
private final Object id;
|
||||
private final String key;
|
||||
private final Map<String, Object> changes;
|
||||
private final boolean updateNaturalKey;
|
||||
private final long version;
|
||||
|
||||
CacheChangeBeanUpdate(BeanDescriptor<?> desc, Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
CacheChangeBeanUpdate(BeanDescriptor<?> desc, String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
this.desc = desc;
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
this.changes = changes;
|
||||
this.updateNaturalKey = updateNaturalKey;
|
||||
this.version = version;
|
||||
@@ -25,6 +25,6 @@ class CacheChangeBeanUpdate implements CacheChange {
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
desc.cacheApplyBeanUpdate(id, changes, updateNaturalKey, version);
|
||||
desc.cacheApplyBeanUpdate(key, changes, updateNaturalKey, version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,17 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
class CacheChangeNaturalKeyPut implements CacheChange {
|
||||
|
||||
private final BeanDescriptor<?> descriptor;
|
||||
private final Object id;
|
||||
private final Object newKey;
|
||||
private final String key;
|
||||
private final String newKey;
|
||||
|
||||
CacheChangeNaturalKeyPut(BeanDescriptor<?> descriptor, Object id, Object newKey) {
|
||||
CacheChangeNaturalKeyPut(BeanDescriptor<?> descriptor, String key, String newKey) {
|
||||
this.descriptor = descriptor;
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
this.newKey = newKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
descriptor.cacheNaturalKeyPut(id, newKey);
|
||||
descriptor.cacheNaturalKeyPut(key, newKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,16 +152,16 @@ public class CacheChangeSet {
|
||||
/**
|
||||
* Update a bean entry.
|
||||
*/
|
||||
public <T> void addBeanUpdate(BeanDescriptor<T> desc, Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
public <T> void addBeanUpdate(BeanDescriptor<T> desc, String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
touchedTables.add(desc.getBaseTable());
|
||||
entries.add(new CacheChangeBeanUpdate(desc, id, changes, updateNaturalKey, version));
|
||||
entries.add(new CacheChangeBeanUpdate(desc, key, changes, updateNaturalKey, version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a natural key.
|
||||
*/
|
||||
public <T> void addNaturalKeyPut(BeanDescriptor<T> desc, Object id, Object val) {
|
||||
entries.add(new CacheChangeNaturalKeyPut(desc, id, val));
|
||||
public <T> void addNaturalKeyPut(BeanDescriptor<T> desc, String key, String val) {
|
||||
entries.add(new CacheChangeNaturalKeyPut(desc, key, val));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -132,7 +132,7 @@ class DefaultCacheHolder {
|
||||
if (tuning != null) {
|
||||
return new ServerCacheOptions(nearCache, tuning).applyDefaults(beanDefault);
|
||||
}
|
||||
return beanDefault.copy();
|
||||
return beanDefault.copy(nearCache);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.ebean.ScriptRunner;
|
||||
import io.ebean.migration.ddl.DdlRunner;
|
||||
import io.ebean.migration.runner.ScriptTransform;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.util.UrlHelper;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
@@ -63,7 +64,7 @@ class DScriptRunner implements ScriptRunner {
|
||||
throw new IllegalArgumentException("resource is null?");
|
||||
}
|
||||
|
||||
try (InputStream inputStream = resource.openStream()) {
|
||||
try (InputStream inputStream = UrlHelper.openNoCache(resource)) {
|
||||
return readContent(new InputStreamReader(inputStream));
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -6,6 +6,8 @@ import io.ebeaninternal.server.lib.DaemonScheduleThreadPool;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -64,6 +66,46 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
|
||||
if (map == null) {
|
||||
return schedulePool.schedule(r, delay, unit);
|
||||
} else {
|
||||
return schedulePool.schedule(() -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}, delay, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
|
||||
if (map == null) {
|
||||
return schedulePool.schedule(c, delay, unit);
|
||||
} else {
|
||||
return schedulePool.schedule(new Callable<V>() {
|
||||
@Override
|
||||
public V call() throws Exception {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
return c.call();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}, delay, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
pool.shutdown();
|
||||
|
||||
@@ -88,4 +88,10 @@ public class DefaultBeanState implements BeanState {
|
||||
public void resetForInsert() {
|
||||
intercept.setNew();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Exception> getLoadErrors() {
|
||||
return intercept.getLoadErrors();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1083,15 +1083,25 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlQuery createSqlQuery(String sql) {
|
||||
public SqlQuery sqlQuery(String sql) {
|
||||
return new DefaultRelationalQuery(this, sql.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlUpdate createSqlUpdate(String sql) {
|
||||
public SqlQuery createSqlQuery(String sql) {
|
||||
return sqlQuery(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlUpdate sqlUpdate(String sql) {
|
||||
return new DefaultSqlUpdate(this, sql.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlUpdate createSqlUpdate(String sql) {
|
||||
return sqlUpdate(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableSql createCallableSql(String sql) {
|
||||
return new DefaultCallableSql(this, sql.trim());
|
||||
@@ -1363,6 +1373,21 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return !ids.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction) {
|
||||
|
||||
ormQuery.setMaxRows(1);
|
||||
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQuery, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
List<Object> ids = request.findIds();
|
||||
return !ids.isEmpty();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A, T> List<A> findIds(Query<T> query, Transaction t) {
|
||||
|
||||
@@ -1444,10 +1469,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
public <T> FutureRowCount<T> findFutureRowCount(Query<T> q, Transaction t) {
|
||||
return findFutureCount(q, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t) {
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A SQL Update Delete or Insert statement that can be executed. For the times
|
||||
@@ -35,9 +36,14 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
private final BindParams bindParams;
|
||||
|
||||
/**
|
||||
* The sql update or delete statement.
|
||||
* The original sql update or delete statement.
|
||||
*/
|
||||
private final String sql;
|
||||
private final String origSql;
|
||||
|
||||
/**
|
||||
* The sql taking into account bind parameter expansion.
|
||||
*/
|
||||
private String baseSql;
|
||||
|
||||
/**
|
||||
* The actual sql with named parameters converted.
|
||||
@@ -66,6 +72,8 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
*/
|
||||
private int addPos;
|
||||
|
||||
private int bindExpansion;
|
||||
|
||||
private boolean getGeneratedKeys;
|
||||
|
||||
private Object generatedKey;
|
||||
@@ -89,7 +97,8 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
*/
|
||||
public DefaultSqlUpdate(SpiEbeanServer server, String sql, BindParams bindParams) {
|
||||
this.server = server;
|
||||
this.sql = sql;
|
||||
this.origSql = sql;
|
||||
this.baseSql = sql;
|
||||
this.bindParams = bindParams;
|
||||
}
|
||||
|
||||
@@ -221,11 +230,21 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
@Override
|
||||
public void setGeneratedSql(String generatedSql) {
|
||||
this.generatedSql = generatedSql;
|
||||
this.baseSql = origSql;
|
||||
if (bindExpansion > 0) {
|
||||
bindParams.reset();
|
||||
bindExpansion = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
return sql;
|
||||
return origSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseSql() {
|
||||
return baseSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,27 +258,61 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlUpdate setParams(Object... values) {
|
||||
for (Object value : values) {
|
||||
setParameter(++addPos, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlUpdate setNextParameter(Object value) {
|
||||
setParameter(++addPos, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
private SqlUpdate setParamWithBindExpansion(int position, Collection values, String bindLiteral) {
|
||||
|
||||
StringBuilder sqlExpand = new StringBuilder(values.size() * 2);
|
||||
position = position + bindExpansion;
|
||||
int offset = 0;
|
||||
for (Object val : values) {
|
||||
if (offset > 0) {
|
||||
sqlExpand.append(",");
|
||||
}
|
||||
sqlExpand.append("?");
|
||||
bindParams.setParameter(position + offset++, val);
|
||||
}
|
||||
bindExpansion += (offset - 1);
|
||||
baseSql = baseSql.replace(bindLiteral, sqlExpand.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlUpdate setParameter(int position, Object value) {
|
||||
bindParams.setParameter(position, value);
|
||||
|
||||
if (value instanceof Collection) {
|
||||
String bindLiteral = "?" + position;
|
||||
int pos = baseSql.indexOf(bindLiteral);
|
||||
if (pos > -1) {
|
||||
return setParamWithBindExpansion(position, (Collection) value, bindLiteral);
|
||||
}
|
||||
}
|
||||
|
||||
bindParams.setParameter(bindExpansion + position, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlUpdate setNull(int position, int jdbcType) {
|
||||
bindParams.setNullParameter(position, jdbcType);
|
||||
bindParams.setNullParameter(bindExpansion + position, jdbcType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlUpdate setNullParameter(int position, int jdbcType) {
|
||||
bindParams.setNullParameter(position, jdbcType);
|
||||
bindParams.setNullParameter(bindExpansion + position, jdbcType);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
|
||||
@Override
|
||||
public boolean isDeleteByStatement() {
|
||||
return beanDescriptor.isDeleteByStatement();
|
||||
if (!transaction.isPersistCascade() || beanDescriptor.isDeleteByStatement()) {
|
||||
// plain delete by query
|
||||
return true;
|
||||
} else {
|
||||
// delete by ids due to cascading delete needs
|
||||
queryPlanKey = query.setDeleteByIdsPlan();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,6 +27,7 @@ import io.ebeaninternal.server.persist.BatchedSqlException;
|
||||
import io.ebeaninternal.server.persist.DeleteMode;
|
||||
import io.ebeaninternal.server.persist.Flags;
|
||||
import io.ebeaninternal.server.persist.PersistExecute;
|
||||
import io.ebeaninternal.server.persist.SaveManyBeans;
|
||||
import io.ebeaninternal.server.transaction.BeanPersistIdMap;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdate;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateContext;
|
||||
@@ -174,7 +175,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
private int pendingPostUpdateNotify;
|
||||
|
||||
/**
|
||||
* Set to true when post execute has occured (so includes batch flush).
|
||||
* Set to true when post execute has occurred (so includes batch flush).
|
||||
*/
|
||||
private boolean postExecute;
|
||||
|
||||
@@ -183,6 +184,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
private boolean complete;
|
||||
|
||||
/**
|
||||
* Many to many intersection table changes that are held for later batch processing.
|
||||
*/
|
||||
private SaveManyBeans saveManyIntersection;
|
||||
|
||||
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
|
||||
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
|
||||
|
||||
@@ -955,6 +961,13 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (isLogSummary()) {
|
||||
logSummary();
|
||||
}
|
||||
saveQueuedManyIntersection();
|
||||
}
|
||||
|
||||
private void saveQueuedManyIntersection() {
|
||||
if (saveManyIntersection != null) {
|
||||
saveManyIntersection.saveIntersectionBatch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1119,6 +1132,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
updatedManysOnly = true;
|
||||
setNotifyCache();
|
||||
addPostCommitListeners();
|
||||
saveQueuedManyIntersection();
|
||||
}
|
||||
notifyCacheOnComplete();
|
||||
postUpdateNotify();
|
||||
@@ -1405,6 +1419,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
|
||||
boolean updateNaturalKey = false;
|
||||
|
||||
String key = beanDescriptor.cacheKey(idValue);
|
||||
|
||||
Map<String, Object> changes = new LinkedHashMap<>();
|
||||
EntityBean bean = getEntityBean();
|
||||
boolean[] dirtyProperties = getDirtyProperties();
|
||||
@@ -1417,7 +1433,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
changes.put(property.getName(), val);
|
||||
if (property.isNaturalKey()) {
|
||||
updateNaturalKey = true;
|
||||
changeSet.addNaturalKeyPut(beanDescriptor, idValue, val);
|
||||
changeSet.addNaturalKeyPut(beanDescriptor, key, val.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1428,7 +1444,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
changes.putAll(collectionChanges);
|
||||
}
|
||||
|
||||
changeSet.addBeanUpdate(beanDescriptor, idValue, changes, updateNaturalKey, getVersion());
|
||||
changeSet.addBeanUpdate(beanDescriptor, key, changes, updateNaturalKey, getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1452,4 +1468,18 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
public String getSelectLastInsertedId() {
|
||||
return beanDescriptor.getSelectLastInsertedId(publish);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the intersection table updates should be queued and batched.
|
||||
*/
|
||||
public boolean isQueueManyIntersection() {
|
||||
return !postExecute;
|
||||
}
|
||||
|
||||
/**
|
||||
* The intersection table updates to the batch executed later on postExecute.
|
||||
*/
|
||||
public void setManyIntersection(SaveManyBeans saveManyIntersection) {
|
||||
this.saveManyIntersection = saveManyIntersection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ package io.ebeaninternal.server.core;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.lib.util.Str;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.persist.PersistExecute;
|
||||
import io.ebeaninternal.server.persist.TrimLogSql;
|
||||
|
||||
/**
|
||||
* Persist request specifically for CallableSql.
|
||||
@@ -24,12 +27,12 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
|
||||
private String tableName;
|
||||
|
||||
private String description;
|
||||
|
||||
private boolean addBatch;
|
||||
|
||||
private boolean forceNoBatch;
|
||||
|
||||
private boolean batchThisRequest;
|
||||
|
||||
public PersistRequestUpdateSql(SpiEbeanServer server, SpiSqlUpdate sqlUpdate,
|
||||
SpiTransaction t, PersistExecute persistExecute, boolean forceNoBatch) {
|
||||
|
||||
@@ -58,6 +61,25 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
return executeOrQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute using jdbc batch.
|
||||
*/
|
||||
public void executeAddBatch() {
|
||||
this.addBatch = true;
|
||||
persistExecute.executeSqlUpdate(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this request to BatchControl to flush later.
|
||||
*/
|
||||
public void addToFlushQueue(boolean early) {
|
||||
BatchControl control = transaction.getBatchControl();
|
||||
if (control == null) {
|
||||
control = persistExecute.createBatchControl(transaction);
|
||||
}
|
||||
control.addToFlushQueue(this, early);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeNow() {
|
||||
return persistExecute.executeSqlUpdate(this);
|
||||
@@ -104,10 +126,9 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
* Specify the type of statement executed. Used to automatically register
|
||||
* with the transaction event.
|
||||
*/
|
||||
public void setType(SqlType sqlType, String tableName, String description) {
|
||||
public void setType(SqlType sqlType, String tableName) {
|
||||
this.sqlType = sqlType;
|
||||
this.tableName = tableName;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,6 +138,20 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
this.bindLog = bindLog;
|
||||
}
|
||||
|
||||
public void startBind(boolean batchThisRequest) {
|
||||
this.batchThisRequest = batchThisRequest;
|
||||
super.startBind(batchThisRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the sql bind used with jdbc batch.
|
||||
*/
|
||||
public void logSqlBatchBind() {
|
||||
if (transaction.isLogSql()) {
|
||||
transaction.logSql(Str.add(" -- bind(", bindLog, ")"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform post execute processing.
|
||||
*/
|
||||
@@ -125,9 +160,8 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
if (startNanos > 0) {
|
||||
persistExecute.collectSqlUpdate(label, startNanos, rowCount);
|
||||
}
|
||||
if (transaction.isLogSummary()) {
|
||||
String m = description + " table[" + tableName + "] rows[" + rowCount + "] bind[" + bindLog + "]";
|
||||
transaction.logSummary(m);
|
||||
if (transaction.isLogSql() && !batchThisRequest) {
|
||||
transaction.logSql(Str.add(TrimLogSql.trim(updateSql.getGeneratedSql()), "; -- bind(", bindLog, ") rows(", String.valueOf(rowCount), ")"));
|
||||
}
|
||||
|
||||
if (updateSql.isAutoTableMod()) {
|
||||
|
||||
@@ -103,6 +103,11 @@ public interface Persister {
|
||||
*/
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
|
||||
/**
|
||||
* Execute or queue the update.
|
||||
*/
|
||||
void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue);
|
||||
|
||||
/**
|
||||
* Add the statement to JDBC batch for later execution via executeBatch.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.core.bootup;
|
||||
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.util.UrlHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -59,7 +60,7 @@ class ManifestReader {
|
||||
try {
|
||||
Enumeration<URL> resources = classLoader.getResources(resourcePath);
|
||||
while (resources.hasMoreElements()) {
|
||||
try (InputStream is = resources.nextElement().openStream()) {
|
||||
try (InputStream is = UrlHelper.openNoCache(resources.nextElement())) {
|
||||
read(new Manifest(is));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,16 @@ abstract class AssocOneHelp {
|
||||
|
||||
protected final BeanDescriptor<?> target;
|
||||
|
||||
private final String path;
|
||||
|
||||
AssocOneHelp(BeanPropertyAssocOne<?> property) {
|
||||
this(property, null);
|
||||
}
|
||||
|
||||
AssocOneHelp(BeanPropertyAssocOne<?> property, String embeddedPrefix) {
|
||||
this.property = property;
|
||||
this.target = property.targetDescriptor;
|
||||
this.path = (embeddedPrefix == null) ? property.name : embeddedPrefix + "." + property.name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +74,7 @@ abstract class AssocOneHelp {
|
||||
boolean disableLazyLoading = ctx.isDisableLazyLoading();
|
||||
Object ref = target.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
|
||||
if (!disableLazyLoading) {
|
||||
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
|
||||
ctx.register(path, ((EntityBean) ref)._ebean_getIntercept());
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ package io.ebeaninternal.server.deploy;
|
||||
*/
|
||||
class AssocOneHelpRefSimple extends AssocOneHelp {
|
||||
|
||||
AssocOneHelpRefSimple(BeanPropertyAssocOne<?> property) {
|
||||
super(property);
|
||||
AssocOneHelpRefSimple(BeanPropertyAssocOne<?> property, String embeddedPrefix) {
|
||||
super(property, embeddedPrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,8 @@ package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.query.CQueryCollectionAdd;
|
||||
|
||||
|
||||
/**
|
||||
@@ -10,10 +12,10 @@ import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
public class BeanCollectionHelpFactory {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final BeanListHelp LIST_HELP = new BeanListHelp();
|
||||
private static final BeanListHelp LIST_HELP = new BeanListHelp();
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final BeanSetHelp SET_HELP = new BeanSetHelp();
|
||||
private static final BeanSetHelp SET_HELP = new BeanSetHelp();
|
||||
|
||||
/**
|
||||
* Create the helper based on the many property.
|
||||
@@ -36,7 +38,7 @@ public class BeanCollectionHelpFactory {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> BeanCollectionHelp<T> create(SpiQuery.Type manyType, OrmQueryRequest<T> request) {
|
||||
public static <T> CQueryCollectionAdd<T> create(SpiQuery.Type manyType, OrmQueryRequest<T> request) {
|
||||
|
||||
if (manyType == SpiQuery.Type.LIST) {
|
||||
return LIST_HELP;
|
||||
@@ -44,10 +46,13 @@ public class BeanCollectionHelpFactory {
|
||||
} else if (manyType == SpiQuery.Type.SET) {
|
||||
return SET_HELP;
|
||||
|
||||
} else {
|
||||
} else if (manyType == SpiQuery.Type.MAP) {
|
||||
BeanDescriptor<T> target = request.getBeanDescriptor();
|
||||
String mapKey = request.getQuery().getMapKey();
|
||||
return new BeanMapHelp<>(target, mapKey);
|
||||
ElPropertyValue elProperty = target.getElGetValue(request.getQuery().getMapKey());
|
||||
return new BeanMapQueryHelp<>(elProperty);
|
||||
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -724,16 +724,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
inheritInfo.setDescriptor(this);
|
||||
}
|
||||
|
||||
if (isEmbedded()) {
|
||||
// initialise all the properties
|
||||
for (BeanProperty prop : propertiesAll()) {
|
||||
prop.initialise(initContext);
|
||||
}
|
||||
} else {
|
||||
// initialise just the Id properties
|
||||
if (idProperty != null) {
|
||||
idProperty.initialise(initContext);
|
||||
}
|
||||
// initialise just the Id property only
|
||||
if (idProperty != null) {
|
||||
idProperty.initialise(initContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,14 +752,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEmbedded()) {
|
||||
// initialise all the non-id properties
|
||||
for (BeanProperty prop : propertiesAll()) {
|
||||
if (!prop.isId()) {
|
||||
prop.initialise(initContext);
|
||||
}
|
||||
prop.registerColumn(this, null);
|
||||
// initialise all the non-id properties
|
||||
for (BeanProperty prop : propertiesAll()) {
|
||||
if (!prop.isId()) {
|
||||
prop.initialise(initContext);
|
||||
}
|
||||
prop.registerColumn(this, null);
|
||||
}
|
||||
|
||||
if (unidirectional != null) {
|
||||
@@ -1487,30 +1478,33 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Return a bean from the bean cache (or null).
|
||||
*/
|
||||
public T cacheBeanGet(Object id, Boolean readOnly, PersistenceContext context) {
|
||||
return cacheHelp.beanCacheGet(id, readOnly, context);
|
||||
return cacheHelp.beanCacheGet(cacheKey(id), readOnly, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a collection of beans from the cache given the ids.
|
||||
*/
|
||||
public void cacheApplyInvalidate(Collection<Object> ids) {
|
||||
cacheHelp.beanCacheApplyInvalidate(ids);
|
||||
List<String> keys = new ArrayList<>(ids.size());
|
||||
for (Object id : ids) {
|
||||
keys.add(cacheKey(id));
|
||||
}
|
||||
cacheHelp.beanCacheApplyInvalidate(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hit the bean cache trying to load a list/batch of entities.
|
||||
* Return the set of entities that were successfully loaded from L2 cache.
|
||||
*/
|
||||
public Set<EntityBeanIntercept> cacheBeanLoadAll(List<EntityBeanIntercept> list, PersistenceContext persistenceContext, int lazyLoadProperty, String propertyName) {
|
||||
return cacheHelp.beanCacheLoadAll(list, persistenceContext, lazyLoadProperty, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it managed to populate/load the bean from the cache.
|
||||
*/
|
||||
public boolean cacheBeanLoad(EntityBean bean, EntityBeanIntercept ebi, Object id, PersistenceContext context) {
|
||||
return cacheHelp.beanCacheLoad(bean, ebi, id, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it managed to populate/load the bean from the cache.
|
||||
*/
|
||||
public boolean cacheBeanLoad(EntityBeanIntercept ebi, PersistenceContext context) {
|
||||
EntityBean bean = ebi.getOwner();
|
||||
Object id = getId(bean);
|
||||
return cacheBeanLoad(bean, ebi, id, context);
|
||||
return cacheHelp.beanCacheLoad(bean, ebi, cacheKey(id), context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1520,8 +1514,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return cacheHelp.naturalKeyLookup(context, keys);
|
||||
}
|
||||
|
||||
public void cacheNaturalKeyPut(Object id, Object newKey) {
|
||||
cacheHelp.cacheNaturalKeyPut(id, newKey);
|
||||
public void cacheNaturalKeyPut(String key, String newKey) {
|
||||
cacheHelp.cacheNaturalKeyPut(key, newKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1569,8 +1563,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Apply the update to the cache.
|
||||
*/
|
||||
public void cacheApplyBeanUpdate(Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
cacheHelp.cacheBeanUpdate(id, changes, updateNaturalKey, version);
|
||||
public void cacheApplyBeanUpdate(String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
cacheHelp.cacheBeanUpdate(key, changes, updateNaturalKey, version);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1989,7 +1983,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
public T createReference(Boolean readOnly, boolean disableLazyLoad, Object id, PersistenceContext pc) {
|
||||
|
||||
if (cacheSharableBeans && !disableLazyLoad && !Boolean.FALSE.equals(readOnly)) {
|
||||
CachedBeanData d = cacheHelp.beanCacheGetData(id);
|
||||
CachedBeanData d = cacheHelp.beanCacheGetData(cacheKey(id));
|
||||
if (d != null) {
|
||||
Object shareableBean = d.getSharableBean();
|
||||
if (shareableBean != null) {
|
||||
@@ -2270,6 +2264,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return (idProperty == null) ? null : idProperty.getValueIntercept(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache key for the given bean (based on id value).
|
||||
*/
|
||||
public String cacheKeyForBean(EntityBean bean) {
|
||||
return cacheKey(idProperty.getValue(bean));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache key for the given id value.
|
||||
*/
|
||||
public String cacheKey(Object id) {
|
||||
return idBinder.cacheKey(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object beanId(Object bean) {
|
||||
return getId((EntityBean) bean);
|
||||
@@ -2578,9 +2586,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Return a 'dynamic property' used to read a formula.
|
||||
*/
|
||||
private STreeProperty findSqlTreeFormula(String formulaExpression) {
|
||||
|
||||
return dynamicProperty.computeIfAbsent(formulaExpression, (formula) -> new FormulaPropertyPath(this, formula).build());
|
||||
private STreeProperty findSqlTreeFormula(String formula, String path) {
|
||||
String key = formula + "-" + path;
|
||||
return dynamicProperty.computeIfAbsent(key, (fullKey) -> new FormulaPropertyPath(this, formula, path).build());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2589,9 +2597,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* The property can be a dynamic formula or a well known bean property.
|
||||
*/
|
||||
@Override
|
||||
public STreeProperty findPropertyWithDynamic(String propName) {
|
||||
public STreeProperty findPropertyWithDynamic(String propName, String path) {
|
||||
if (propName.indexOf('(') > -1) {
|
||||
return findSqlTreeFormula(propName);
|
||||
return findSqlTreeFormula(propName, path);
|
||||
}
|
||||
return _findBeanProperty(propName);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -282,7 +283,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
if (many.isElementCollection()) {
|
||||
// held as part of the bean cache so skip
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
CachedManyIds entry = manyPropGet(parentId, many.getName());
|
||||
@@ -317,7 +318,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
try {
|
||||
// add as JSON to bean cache
|
||||
String asJson = many.jsonWriteCollection(details);
|
||||
Map<String,Object> changes = new HashMap<>();
|
||||
Map<String, Object> changes = new HashMap<>();
|
||||
changes.put(many.getName(), asJson);
|
||||
|
||||
CachedBeanData newData = data.update(changes, data.getVersion());
|
||||
@@ -475,7 +476,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
private Class<?> theClassOf(Collection<EntityBean> beans) {
|
||||
if (beans instanceof List) {
|
||||
return ((List<?>)beans).get(0).getClass();
|
||||
return ((List<?>) beans).get(0).getClass();
|
||||
}
|
||||
return beans.iterator().next().getClass();
|
||||
}
|
||||
@@ -494,20 +495,20 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
void beanCachePutAllDirect(Collection<EntityBean> beans) {
|
||||
|
||||
Map<Object,Object> natKeys = null;
|
||||
Map<Object, Object> natKeys = null;
|
||||
if (naturalKey != null) {
|
||||
natKeys = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
Map<Object,Object> map = new LinkedHashMap<>();
|
||||
Map<Object, Object> map = new LinkedHashMap<>();
|
||||
for (EntityBean bean : beans) {
|
||||
CachedBeanData beanData = beanExtractData(desc, bean);
|
||||
Object id = desc.getId(bean);
|
||||
map.put(id, beanData);
|
||||
String key = desc.cacheKeyForBean(bean);
|
||||
map.put(key, beanData);
|
||||
if (naturalKey != null) {
|
||||
Object naturalKey = calculateNaturalKey(beanData);
|
||||
if (naturalKey != null) {
|
||||
natKeys.put(naturalKey, id);
|
||||
natKeys.put(naturalKey, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -525,32 +526,33 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the bean into the bean cache.
|
||||
*/
|
||||
* Put the bean into the bean cache.
|
||||
*/
|
||||
void beanCachePutDirect(EntityBean bean) {
|
||||
|
||||
CachedBeanData beanData = beanExtractData(desc, bean);
|
||||
|
||||
Object id = desc.getId(bean);
|
||||
String key = desc.cacheKeyForBean(bean);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" PUT {}({}) data:{}", cacheName, id, beanData);
|
||||
beanLog.debug(" PUT {}({}) data:{}", cacheName, key, beanData);
|
||||
}
|
||||
getBeanCache().put(id, beanData);
|
||||
getBeanCache().put(key, beanData);
|
||||
|
||||
if (naturalKey != null) {
|
||||
Object naturalKey = calculateNaturalKey(beanData);
|
||||
String naturalKey = calculateNaturalKey(beanData);
|
||||
if (naturalKey != null) {
|
||||
if (natLog.isDebugEnabled()) {
|
||||
natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, id);
|
||||
natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, key);
|
||||
}
|
||||
naturalKeyCache.put(naturalKey, id);
|
||||
naturalKeyCache.put(naturalKey, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object calculateNaturalKey(CachedBeanData beanData) {
|
||||
private String calculateNaturalKey(CachedBeanData beanData) {
|
||||
if (naturalKey.length == 1) {
|
||||
return beanData.getData(naturalKey[0]);
|
||||
Object data = beanData.getData(naturalKey[0]);
|
||||
return (data == null) ? null : data.toString();
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String key : naturalKey) {
|
||||
@@ -563,12 +565,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
CachedBeanData beanCacheGetData(Object id) {
|
||||
return (CachedBeanData) getBeanCache().get(id);
|
||||
CachedBeanData beanCacheGetData(String key) {
|
||||
return (CachedBeanData) getBeanCache().get(key);
|
||||
}
|
||||
|
||||
T beanCacheGet(Object id, Boolean readOnly, PersistenceContext context) {
|
||||
T bean = beanCacheGetInternal(id, readOnly, context);
|
||||
T beanCacheGet(String key, Boolean readOnly, PersistenceContext context) {
|
||||
T bean = beanCacheGetInternal(key, readOnly, context);
|
||||
if (bean != null) {
|
||||
setupContext(bean, context);
|
||||
}
|
||||
@@ -578,19 +580,19 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
/**
|
||||
* Return a bean from the bean cache.
|
||||
*/
|
||||
private T beanCacheGetInternal(Object id, Boolean readOnly, PersistenceContext context) {
|
||||
private T beanCacheGetInternal(String key, Boolean readOnly, PersistenceContext context) {
|
||||
|
||||
CachedBeanData data = (CachedBeanData) getBeanCache().get(id);
|
||||
CachedBeanData data = (CachedBeanData) getBeanCache().get(key);
|
||||
if (data == null) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" GET {}({}) - cache miss", cacheName, id);
|
||||
beanLog.trace(" GET {}({}) - cache miss", cacheName, key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" GET {}({}) - hit", cacheName, id);
|
||||
beanLog.trace(" GET {}({}) - hit", cacheName, key);
|
||||
}
|
||||
return convertToBean(id, readOnly, context, data);
|
||||
return convertToBean(key, readOnly, context, data);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -686,12 +688,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
/**
|
||||
* Remove a bean from the cache given its Id.
|
||||
*/
|
||||
void beanCacheApplyInvalidate(Collection<Object> ids) {
|
||||
void beanCacheApplyInvalidate(Collection<String> keys) {
|
||||
if (beanCache != null) {
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" REMOVE {}({})", cacheName, ids);
|
||||
beanLog.debug(" REMOVE {}({})", cacheName, keys);
|
||||
}
|
||||
beanCache.removeAll(new HashSet<>(ids));
|
||||
beanCache.removeAll(new HashSet<>(keys));
|
||||
}
|
||||
for (BeanPropertyAssocOne<?> imported : propertiesOneImported) {
|
||||
imported.cacheClear();
|
||||
@@ -699,28 +701,77 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it managed to populate/load the bean from the cache.
|
||||
* Load a batch of entities from L2 bean cache checking the lazy loaded property is loaded.
|
||||
*/
|
||||
boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, Object id, PersistenceContext context) {
|
||||
Set<EntityBeanIntercept> beanCacheLoadAll(List<EntityBeanIntercept> list, PersistenceContext context, int lazyLoadProperty, String propertyName) {
|
||||
|
||||
CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(id);
|
||||
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(" LOAD ALL {}({}) - 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();
|
||||
|
||||
if (lazyLoadProperty > -1 && !cacheData.isLoaded(propertyName)) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, key, propertyName);
|
||||
}
|
||||
iterator.remove();
|
||||
|
||||
} else {
|
||||
CachedBeanDataToBean.load(desc, ebi.getOwner(), cacheData, context);
|
||||
loaded.add(ebi);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" LOAD {}({}) - hit", cacheName, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ebis.isEmpty() && beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" LOAD {}({}) - cache miss", cacheName, ebis.keySet());
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
beanLog.trace(" LOAD {}({}) - cache miss", cacheName, id);
|
||||
beanLog.trace(" LOAD {}({}) - cache miss", cacheName, key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int lazyLoadProperty = ebi.getLazyLoadPropertyIndex();
|
||||
if (lazyLoadProperty > -1 && !cacheData.isLoaded(ebi.getLazyLoadProperty())) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, id, ebi.getLazyLoadProperty());
|
||||
beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, key, ebi.getLazyLoadProperty());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CachedBeanDataToBean.load(desc, bean, cacheData, context);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" LOAD {}({}) - hit", cacheName, id);
|
||||
beanLog.debug(" LOAD {}({}) - hit", cacheName, key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -736,7 +787,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
*/
|
||||
void persistDeleteIds(Collection<Object> ids, CacheChangeSet changeSet) {
|
||||
if (invalidateQueryCache) {
|
||||
changeSet.addInvalidate(desc);
|
||||
changeSet.addInvalidate(desc);
|
||||
} else {
|
||||
queryCacheClear(changeSet);
|
||||
if (beanCache != null) {
|
||||
@@ -833,42 +884,42 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
void cacheNaturalKeyPut(Object id, Object newKey) {
|
||||
void cacheNaturalKeyPut(String key, String newKey) {
|
||||
if (newKey != null) {
|
||||
naturalKeyCache.put(newKey, id);
|
||||
naturalKeyCache.put(newKey, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply changes to the bean cache entry.
|
||||
*/
|
||||
void cacheBeanUpdate(Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
void cacheBeanUpdate(String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
|
||||
ServerCache cache = getBeanCache();
|
||||
CachedBeanData existingData = (CachedBeanData) cache.get(id);
|
||||
CachedBeanData existingData = (CachedBeanData) cache.get(key);
|
||||
if (existingData != null) {
|
||||
long currentVersion = existingData.getVersion();
|
||||
if (version > 0 && version < currentVersion) {
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" REMOVE {}({}) - version conflict old:{} new:{}", cacheName, id, currentVersion, version);
|
||||
beanLog.debug(" REMOVE {}({}) - version conflict old:{} new:{}", cacheName, key, currentVersion, version);
|
||||
}
|
||||
cache.remove(id);
|
||||
cache.remove(key);
|
||||
} else {
|
||||
if (version == 0) {
|
||||
version = currentVersion;
|
||||
}
|
||||
CachedBeanData newData = existingData.update(changes, version);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, id, changes);
|
||||
beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, key, changes);
|
||||
}
|
||||
cache.put(id, newData);
|
||||
cache.put(key, newData);
|
||||
}
|
||||
|
||||
if (updateNaturalKey) {
|
||||
Object oldKey = calculateNaturalKey(existingData);
|
||||
if (oldKey != null) {
|
||||
if (natLog.isDebugEnabled()) {
|
||||
natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, id);
|
||||
natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, key);
|
||||
}
|
||||
naturalKeyCache.remove(oldKey);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ class BeanDescriptorInitContext {
|
||||
private final Map<String, String> draftTables;
|
||||
private final String asOfViewSuffix;
|
||||
|
||||
private String embeddedPrefix;
|
||||
|
||||
BeanDescriptorInitContext(Map<String, String> withHistoryTables, Map<String, String> draftTables, String asOfViewSuffix) {
|
||||
this.withHistoryTables = withHistoryTables;
|
||||
this.draftTables = draftTables;
|
||||
@@ -29,4 +31,12 @@ class BeanDescriptorInitContext {
|
||||
void addDraftIntersection(String intersectionPublishTable, String intersectionDraftTable) {
|
||||
draftTables.put(intersectionPublishTable, intersectionDraftTable);
|
||||
}
|
||||
|
||||
public void setEmbeddedPrefix(String embeddedPrefix) {
|
||||
this.embeddedPrefix = embeddedPrefix;
|
||||
}
|
||||
|
||||
public String getEmbeddedPrefix() {
|
||||
return embeddedPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,8 +150,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final String serverName;
|
||||
|
||||
private Map<Class<?>, DeployBeanInfo<?>> deployInfoMap = new HashMap<>();
|
||||
|
||||
private final Map<Class<?>, BeanTable> beanTableMap = new HashMap<>();
|
||||
|
||||
private final Map<String, BeanDescriptor<?>> descMap = new HashMap<>();
|
||||
@@ -196,6 +194,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final int queryPlanTTLSeconds;
|
||||
|
||||
// temporary collections used during startup and then cleared
|
||||
|
||||
private Map<Class<?>, DeployBeanInfo<?>> deployInfoMap = new HashMap<>();
|
||||
private Set<Class<?>> embeddedIdTypes = new HashSet<>();
|
||||
private List<DeployBeanInfo<?>> embeddedBeans = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Create for a given database dbConfig.
|
||||
*/
|
||||
@@ -366,7 +370,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
createListeners();
|
||||
readEntityDeploymentInitial();
|
||||
readXmlMapping(mappings);
|
||||
readEmbeddedDeployment();
|
||||
readEntityBeanTable();
|
||||
readEntityDeploymentAssociations();
|
||||
readInheritedIdGenerators();
|
||||
@@ -385,7 +388,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
logStatus();
|
||||
|
||||
deployInfoMap.clear();
|
||||
// clear collections we no longer need
|
||||
embeddedIdTypes = null;
|
||||
embeddedBeans = null;
|
||||
deployInfoMap = null;
|
||||
|
||||
return asOfTableMap;
|
||||
@@ -592,8 +597,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
for (BeanDescriptor<?> d : descMap.values()) {
|
||||
d.initLast();
|
||||
if (!d.isEmbedded()) {
|
||||
BeanManager<?> m = beanManagerFactory.create(d);
|
||||
beanManagerMap.put(d.getFullName(), m);
|
||||
beanManagerMap.put(d.getFullName(), beanManagerFactory.create(d));
|
||||
checkForValidEmbeddedId(d);
|
||||
}
|
||||
}
|
||||
@@ -676,11 +680,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
logger.debug("Entities[{}]", entityBeanCount);
|
||||
}
|
||||
|
||||
private <T> BeanDescriptor<T> createEmbedded(Class<T> beanClass) {
|
||||
DeployBeanInfo<T> info = getDeploy(beanClass);
|
||||
return new BeanDescriptor<>(this, info.getDescriptor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean deploy info for the given class.
|
||||
*/
|
||||
@@ -689,24 +688,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return (DeployBeanInfo<T>) deployInfoMap.get(cls);
|
||||
}
|
||||
|
||||
private void registerBeanDescriptor(BeanDescriptor<?> desc) {
|
||||
private void registerBeanDescriptor(DeployBeanInfo<?> info) {
|
||||
BeanDescriptor desc = new BeanDescriptor<>(this, info.getDescriptor());
|
||||
descMap.put(desc.getBeanType().getName(), desc);
|
||||
if (desc.isDocStoreMapped()) {
|
||||
descQueueMap.put(desc.getDocStoreQueueId(), desc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read deployment information for all the embedded beans.
|
||||
*/
|
||||
private void readEmbeddedDeployment() {
|
||||
|
||||
List<Class<?>> embeddedClasses = bootupClasses.getEmbeddables();
|
||||
for (Class<?> embeddedClass : embeddedClasses) {
|
||||
registerBeanDescriptor(createEmbedded(embeddedClass));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the initial deployment information for the entities.
|
||||
* <p>
|
||||
@@ -719,14 +708,31 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
for (Class<?> entityClass : bootupClasses.getEntities()) {
|
||||
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
|
||||
deployInfoMap.put(entityClass, info);
|
||||
Class<?> embeddedIdType = info.getEmbeddedIdType();
|
||||
if (embeddedIdType != null){
|
||||
embeddedIdTypes.add(embeddedIdType);
|
||||
}
|
||||
}
|
||||
for (Class<?> entityClass : bootupClasses.getEmbeddables()) {
|
||||
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
|
||||
readDeployAssociations(info);
|
||||
deployInfoMap.put(entityClass, info);
|
||||
if (embeddedIdTypes.contains(entityClass)) {
|
||||
// register embeddedId types early - scalar properties only
|
||||
// and needed for creating BeanTables (id properties)
|
||||
registerEmbeddedBean(info);
|
||||
} else {
|
||||
// delay register of other embedded beans until after
|
||||
// the BeanTables have been created to support ManyToOne
|
||||
embeddedBeans.add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerEmbeddedBean(DeployBeanInfo<?> info) {
|
||||
readDeployAssociations(info);
|
||||
registerBeanDescriptor(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the BeanTable information which has the base table and id.
|
||||
* <p>
|
||||
@@ -739,6 +745,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
BeanTable beanTable = createBeanTable(info);
|
||||
beanTableMap.put(beanTable.getBeanType(), beanTable);
|
||||
}
|
||||
|
||||
// register non-id embedded beans (after bean tables are created)
|
||||
for (DeployBeanInfo<?> info : embeddedBeans) {
|
||||
registerEmbeddedBean(info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -816,13 +827,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
secondaryPropsJoins(info);
|
||||
}
|
||||
|
||||
// Set inheritance info
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
setInheritanceInfo(info);
|
||||
}
|
||||
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
registerBeanDescriptor(new BeanDescriptor(this, info.getDescriptor()));
|
||||
if (!info.isEmbedded()) {
|
||||
registerBeanDescriptor(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public class BeanEmbeddedMetaFactory {
|
||||
String columnPrefix = prop.getColumnPrefix();
|
||||
Map<String, String> propColMap = prop.getDeployEmbedded().getPropertyColumnMap();
|
||||
|
||||
BeanProperty[] sourceProperties = targetDesc.propertiesBaseScalar();
|
||||
BeanProperty[] sourceProperties = targetDesc.propertiesNonTransient();
|
||||
BeanProperty[] embeddedProperties = new BeanProperty[sourceProperties.length];
|
||||
|
||||
for (int i = 0; i < sourceProperties.length; i++) {
|
||||
@@ -45,7 +45,11 @@ public class BeanEmbeddedMetaFactory {
|
||||
}
|
||||
|
||||
BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn);
|
||||
embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
|
||||
if (sourceProperties[i] instanceof BeanPropertyAssocOne) {
|
||||
embeddedProperties[i] = new BeanPropertyAssocOne((BeanPropertyAssocOne)sourceProperties[i], overrides);
|
||||
} else {
|
||||
embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
|
||||
}
|
||||
}
|
||||
|
||||
return new BeanEmbeddedMeta(embeddedProperties);
|
||||
|
||||
@@ -56,11 +56,10 @@ public final class BeanFkeyProperty implements ElPropertyValue {
|
||||
}
|
||||
}
|
||||
|
||||
public BeanFkeyProperty create(String expression, boolean containsMany) {
|
||||
public BeanFkeyProperty create(String expression, boolean pathContainsMany) {
|
||||
int len = expression.length() - name.length() - 1;
|
||||
String prefix = expression.substring(0, len);
|
||||
|
||||
return new BeanFkeyProperty(prefix, name, dbColumn, deployOrder, containsMany);
|
||||
return new BeanFkeyProperty(prefix, name, dbColumn, deployOrder, containsMany || pathContainsMany);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,16 +24,6 @@ public class BeanMapHelp<T> extends BaseCollectionHelp<T> {
|
||||
private final String propertyName;
|
||||
private final BeanProperty beanProperty;
|
||||
|
||||
/**
|
||||
* When created for a given query that will return a map.
|
||||
*/
|
||||
BeanMapHelp(BeanDescriptor<T> targetDescriptor, String mapKey) {
|
||||
this.targetDescriptor = targetDescriptor;
|
||||
this.beanProperty = targetDescriptor.getBeanProperty(mapKey);
|
||||
this.many = null;
|
||||
this.propertyName = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* When help is attached to a specific many property.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanMap;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.query.CQueryCollectionAdd;
|
||||
|
||||
class BeanMapQueryHelp<T> implements CQueryCollectionAdd<T> {
|
||||
|
||||
private final ElPropertyValue elGetValue;
|
||||
|
||||
/**
|
||||
* Create for a findMap query.
|
||||
*/
|
||||
BeanMapQueryHelp(ElPropertyValue elGetValue) {
|
||||
this.elGetValue = elGetValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanCollection<T> createEmptyNoParent() {
|
||||
return new BeanMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection collection, EntityBean bean, boolean withCheck) {
|
||||
if (bean == null) {
|
||||
((BeanMap<?, ?>) collection).internalPutNull();
|
||||
} else {
|
||||
Object keyValue = elGetValue.pathGet(bean);
|
||||
BeanMap<?, ?> map = ((BeanMap<?, ?>) collection);
|
||||
map.internalPutWithCheck(keyValue, bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -827,6 +827,13 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value in String format (for bean cache key).
|
||||
*/
|
||||
public String format(Object value) {
|
||||
return scalarType.format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value for this property from L2 cache entry and set it to the bean.
|
||||
* <p>
|
||||
|
||||
@@ -104,6 +104,22 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
this.fetchPreference = deploy.getFetchPreference();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor for ManyToOne inside Embeddable.
|
||||
*/
|
||||
public BeanPropertyAssoc(BeanPropertyAssoc source, BeanPropertyOverride override) {
|
||||
super(source, override);
|
||||
foreignKey = source.foreignKey;
|
||||
extraWhere = source.extraWhere;
|
||||
beanTable = source.beanTable;
|
||||
mappedBy = source.mappedBy;
|
||||
docStoreDoc = source.docStoreDoc;
|
||||
targetType = source.targetType;
|
||||
cascadeInfo = source.cascadeInfo;
|
||||
fetchPreference = source.fetchPreference;
|
||||
tableJoin = source.tableJoin.withOverrideColumn(override.getDbColumn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise post construction.
|
||||
*/
|
||||
|
||||
@@ -204,7 +204,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
public void initialisePostTarget() {
|
||||
if (childMasterProperty != null) {
|
||||
BeanProperty masterId = childMasterProperty.getTargetDescriptor().getIdProperty();
|
||||
childMasterIdProperty = childMasterProperty.getName() + "." + masterId.getName();
|
||||
if (masterId != null) { // in docstore only, the master-id may be not available
|
||||
childMasterIdProperty = childMasterProperty.getName() + "." + masterId.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -972,8 +974,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) {
|
||||
try {
|
||||
String asJson = (String) cacheData;
|
||||
Object collection = jsonReadCollection(asJson);
|
||||
setValue(bean, collection);
|
||||
if (asJson != null && !asJson.isEmpty()) {
|
||||
Object collection = jsonReadCollection(asJson);
|
||||
setValue(bean, collection);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Error setting value from L2 cache", e);
|
||||
}
|
||||
@@ -999,7 +1003,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
public String jsonWriteCollection(Object value) throws IOException {
|
||||
StringWriter writer = new StringWriter(300);
|
||||
SpiJsonWriter ctx = descriptor.createJsonWriter(writer);
|
||||
help.jsonWrite(ctx, null, value, false);
|
||||
help.jsonWrite(ctx, null, value, true);
|
||||
ctx.flush();
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
@@ -93,14 +93,35 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor for ManyToOne inside Embeddable.
|
||||
*/
|
||||
public BeanPropertyAssocOne(BeanPropertyAssocOne source, BeanPropertyOverride override) {
|
||||
super(source, override);
|
||||
primaryKeyExport = source.primaryKeyExport;
|
||||
oneToOne = source.oneToOne;
|
||||
oneToOneExported = source.oneToOneExported;
|
||||
orphanRemoval = source.orphanRemoval;
|
||||
embeddedProps = null;
|
||||
embeddedPropsMap = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
super.initialise(initContext);
|
||||
initialiseAssocOne();
|
||||
initialiseAssocOne(initContext.getEmbeddedPrefix());
|
||||
if (embedded) {
|
||||
// initialise ManyToOne importedId
|
||||
initContext.setEmbeddedPrefix(name);
|
||||
for (BeanProperty embeddedProp : embeddedProps) {
|
||||
embeddedProp.initialise(initContext);
|
||||
}
|
||||
initContext.setEmbeddedPrefix(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseAssocOne() {
|
||||
localHelp = createHelp(embedded, oneToOneExported);
|
||||
private void initialiseAssocOne(String embeddedPrefix) {
|
||||
localHelp = createHelp(embedded, oneToOneExported, embeddedPrefix);
|
||||
|
||||
if (!isTransient) {
|
||||
//noinspection StatementWithEmptyBody
|
||||
@@ -413,6 +434,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(Object value) {
|
||||
return targetDescriptor.getIdBinder().cacheKey(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) {
|
||||
if (cacheData == null) {
|
||||
@@ -663,7 +689,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
}
|
||||
|
||||
private AssocOneHelp createHelp(boolean embedded, boolean oneToOneExported) {
|
||||
private AssocOneHelp createHelp(boolean embedded, boolean oneToOneExported, String embeddedPrefix) {
|
||||
if (embedded) {
|
||||
return new AssocOneHelpEmbedded(this);
|
||||
} else if (oneToOneExported) {
|
||||
@@ -672,7 +698,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
if (targetInheritInfo != null) {
|
||||
return new AssocOneHelpRefInherit(this);
|
||||
} else {
|
||||
return new AssocOneHelpRefSimple(this);
|
||||
return new AssocOneHelpRefSimple(this, embeddedPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,4 +90,10 @@ public interface DbReadContext {
|
||||
* Return true if this request disables lazy loading.
|
||||
*/
|
||||
boolean isDisableLazyLoading();
|
||||
|
||||
/**
|
||||
* Handles a load error on given property.
|
||||
*/
|
||||
void handleLoadError(String fullName, Exception e);
|
||||
|
||||
}
|
||||
|
||||
@@ -49,15 +49,15 @@ class DynamicPropertyAggregationFormula extends DynamicPropertyBase {
|
||||
|
||||
@Override
|
||||
public void load(SqlBeanLoad sqlBeanLoad) {
|
||||
|
||||
Object value;
|
||||
try {
|
||||
Object value = scalarType.read(sqlBeanLoad.ctx().getDataReader());
|
||||
if (asTarget != null) {
|
||||
sqlBeanLoad.load(asTarget, value);
|
||||
}
|
||||
|
||||
value = scalarType.read(sqlBeanLoad.ctx().getDataReader());
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException("Error loading on " + fullName, e);
|
||||
sqlBeanLoad.ctx().handleLoadError(fullName, e);
|
||||
return;
|
||||
}
|
||||
if (asTarget != null) {
|
||||
sqlBeanLoad.load(asTarget, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,15 +20,17 @@ class FormulaPropertyPath {
|
||||
|
||||
private final String internalExpression;
|
||||
|
||||
private final String path;
|
||||
|
||||
private boolean countDistinct;
|
||||
|
||||
private String cast;
|
||||
private String alias;
|
||||
|
||||
FormulaPropertyPath(BeanDescriptor<?> descriptor, String formula) {
|
||||
|
||||
FormulaPropertyPath(BeanDescriptor<?> descriptor, String formula, String path) {
|
||||
this.descriptor = descriptor;
|
||||
this.formula = formula;
|
||||
this.path = path;
|
||||
|
||||
int openBracket = formula.indexOf('(');
|
||||
int closeBracket = formula.lastIndexOf(')');
|
||||
@@ -94,6 +96,11 @@ class FormulaPropertyPath {
|
||||
DeployPropertyParser parser = descriptor.parser().setCatchFirst(true);
|
||||
|
||||
String parsed = parser.parse(internalExpression);
|
||||
if (path != null) {
|
||||
// fetch("machineStats", "sum(hours), sum(totalKms)")
|
||||
parsed = parsed.replace("${}", "${" + path + "}");
|
||||
}
|
||||
|
||||
ElPropertyDeploy firstProp = parser.getFirstProp();
|
||||
|
||||
ScalarType<?> scalarType;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.expression.DefaultExpressionRequest;
|
||||
import io.ebeaninternal.server.expression.IdInExpression;
|
||||
@@ -45,7 +45,7 @@ public class IntersectionRow {
|
||||
values.put(key, value);
|
||||
}
|
||||
|
||||
public SqlUpdate createInsert(SpiEbeanServer server) {
|
||||
public SpiSqlUpdate createInsert(SpiEbeanServer server) {
|
||||
|
||||
BindParams bindParams = new BindParams();
|
||||
|
||||
@@ -73,7 +73,7 @@ public class IntersectionRow {
|
||||
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
|
||||
}
|
||||
|
||||
public SqlUpdate createDelete(SpiEbeanServer server, DeleteMode deleteMode) {
|
||||
public SpiSqlUpdate createDelete(SpiEbeanServer server, DeleteMode deleteMode) {
|
||||
|
||||
BindParams bindParams = new BindParams();
|
||||
|
||||
@@ -108,7 +108,7 @@ public class IntersectionRow {
|
||||
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
|
||||
}
|
||||
|
||||
public SqlUpdate createDeleteChildren(SpiEbeanServer server) {
|
||||
public SpiSqlUpdate createDeleteChildren(SpiEbeanServer server) {
|
||||
|
||||
BindParams bindParams = new BindParams();
|
||||
|
||||
|
||||
@@ -51,6 +51,15 @@ public final class TableJoin {
|
||||
this.queryHash = calcQueryHash();
|
||||
}
|
||||
|
||||
private TableJoin(TableJoin source, String overrideColumn) {
|
||||
this.table = source.table;
|
||||
this.type = source.type;
|
||||
this.inheritInfo = source.inheritInfo;
|
||||
this.columns = new TableJoinColumn[1];
|
||||
this.columns[0] = source.columns[0].withOverrideColumn(overrideColumn);
|
||||
this.queryHash = calcQueryHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a hash value for adding to a query plan.
|
||||
*/
|
||||
@@ -166,4 +175,11 @@ public final class TableJoin {
|
||||
sb.append(a2).append(".").append(pair.getForeignDbColumn());
|
||||
}
|
||||
}
|
||||
|
||||
TableJoin withOverrideColumn(String overrideColumn) {
|
||||
if (columns.length == 1 && overrideColumn != null && !overrideColumn.equals(columns[0].getLocalDbColumn())) {
|
||||
return new TableJoin(this, overrideColumn);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,16 @@ public class TableJoinColumn {
|
||||
this.queryHash = hash();
|
||||
}
|
||||
|
||||
private TableJoinColumn(TableJoinColumn source, String overrideColumn) {
|
||||
this.localDbColumn = InternString.intern(overrideColumn);
|
||||
this.foreignDbColumn = source.foreignDbColumn;
|
||||
this.localSqlFormula = null;
|
||||
this.foreignSqlFormula = null;
|
||||
this.insertable = source.isInsertable();
|
||||
this.updateable = source.isUpdateable();
|
||||
this.queryHash = hash();
|
||||
}
|
||||
|
||||
int hash() {
|
||||
int result = localDbColumn != null ? localDbColumn.hashCode() : 0;
|
||||
result = 92821 * result + (foreignDbColumn != null ? foreignDbColumn.hashCode() : 0);
|
||||
@@ -123,4 +133,7 @@ public class TableJoinColumn {
|
||||
return foreignSqlFormula;
|
||||
}
|
||||
|
||||
TableJoinColumn withOverrideColumn(String overrideColumn) {
|
||||
return new TableJoinColumn(this, overrideColumn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,4 +207,10 @@ public interface IdBinder {
|
||||
* Cast or convert the Id value if necessary.
|
||||
*/
|
||||
Object convertId(Object idValue);
|
||||
|
||||
/**
|
||||
* Return a key to use for bean caches given the id value.
|
||||
*/
|
||||
String cacheKey(Object idValue);
|
||||
|
||||
}
|
||||
|
||||
@@ -479,4 +479,20 @@ public final class IdBinderEmbedded implements IdBinder {
|
||||
|
||||
return idValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cacheKey(Object value) {
|
||||
|
||||
EntityBean bean = (EntityBean)value;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (BeanProperty prop : props) {
|
||||
Object val = prop.getValue(bean);
|
||||
if (val != null) {
|
||||
sb.append(prop.format(val));
|
||||
}
|
||||
sb.append("|");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -178,4 +178,10 @@ public final class IdBinderEmpty implements IdBinder {
|
||||
public void writeData(DataOutput dataOutput, Object idValue) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cacheKey(Object bean) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -262,4 +262,10 @@ public final class IdBinderSimple implements IdBinder {
|
||||
}
|
||||
return idValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cacheKey(Object value) {
|
||||
return scalarType.format(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import io.ebean.annotation.Index;
|
||||
import io.ebean.annotation.JsonIgnore;
|
||||
import io.ebean.annotation.Length;
|
||||
import io.ebean.annotation.SoftDelete;
|
||||
import io.ebean.annotation.Sum;
|
||||
import io.ebean.annotation.TenantId;
|
||||
import io.ebean.annotation.UnmappedJson;
|
||||
import io.ebean.annotation.UpdatedTimestamp;
|
||||
@@ -132,6 +133,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
prop.setId();
|
||||
prop.setNullable(false);
|
||||
prop.setEmbedded();
|
||||
info.setEmbeddedId(prop);
|
||||
}
|
||||
|
||||
DocEmbedded docEmbedded = get(prop, DocEmbedded.class);
|
||||
@@ -284,6 +286,10 @@ public class AnnotationFields extends AnnotationParser {
|
||||
if (aggregation != null) {
|
||||
prop.setAggregation(aggregation.value());
|
||||
}
|
||||
Sum sum = get(prop, Sum.class);
|
||||
if (sum != null) {
|
||||
prop.setAggregation("sum(" + prop.getName() + ")");
|
||||
}
|
||||
|
||||
Version version = get(prop, Version.class);
|
||||
if (version != null) {
|
||||
|
||||
@@ -54,6 +54,7 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
} else {
|
||||
prop.setId();
|
||||
prop.setEmbedded();
|
||||
info.setEmbeddedId(prop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +68,7 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
} else {
|
||||
prop.setId();
|
||||
if (prop.getPropertyType().equals(UUID.class)) {
|
||||
if (descriptor.getIdGeneratorName() == null) {
|
||||
if (readConfig.isIdGeneratorAutomatic() && descriptor.getIdGeneratorName() == null) {
|
||||
descriptor.setUuidGenerator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy.parse;
|
||||
import io.ebean.RawSql;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
@@ -23,6 +24,8 @@ public class DeployBeanInfo<T> {
|
||||
|
||||
private final DeployBeanDescriptor<T> descriptor;
|
||||
|
||||
private DeployBeanPropertyAssoc<?> embeddedId;
|
||||
|
||||
/**
|
||||
* Create with a DeployUtil and BeanDescriptor.
|
||||
*/
|
||||
@@ -89,4 +92,19 @@ public class DeployBeanInfo<T> {
|
||||
public void setPrimaryKeyJoin(TableJoin join) {
|
||||
descriptor.setPrimaryKeyJoin(join);
|
||||
}
|
||||
|
||||
/**
|
||||
* This bean type has an embedded Id property.
|
||||
*/
|
||||
public void setEmbeddedId(DeployBeanPropertyAssoc<?> embeddedId) {
|
||||
this.embeddedId = embeddedId;
|
||||
}
|
||||
|
||||
public Class<?> getEmbeddedIdType() {
|
||||
return (embeddedId == null) ? null : embeddedId.getTargetType();
|
||||
}
|
||||
|
||||
public boolean isEmbedded() {
|
||||
return descriptor.isEmbedded();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ class ReadAnnotationConfig {
|
||||
private final boolean eagerFetchLobs;
|
||||
private final boolean javaxValidationAnnotations;
|
||||
private final boolean jacksonAnnotations;
|
||||
private final boolean idGeneratorAutomatic;
|
||||
|
||||
ReadAnnotationConfig(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, ServerConfig serverConfig) {
|
||||
|
||||
@@ -23,6 +24,7 @@ class ReadAnnotationConfig {
|
||||
this.versionsBetweenSuffix = versionsBetweenSuffix;
|
||||
this.disableL2Cache = serverConfig.isDisableL2Cache();
|
||||
this.eagerFetchLobs = serverConfig.isEagerFetchLobs();
|
||||
this.idGeneratorAutomatic = serverConfig.isIdGeneratorAutomatic();
|
||||
|
||||
this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent();
|
||||
this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent();
|
||||
@@ -48,6 +50,10 @@ class ReadAnnotationConfig {
|
||||
return eagerFetchLobs;
|
||||
}
|
||||
|
||||
public boolean isIdGeneratorAutomatic() {
|
||||
return idGeneratorAutomatic;
|
||||
}
|
||||
|
||||
boolean isJavaxValidationAnnotations() {
|
||||
return javaxValidationAnnotations;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.io.IOException;
|
||||
|
||||
class BetweenExpression extends AbstractExpression {
|
||||
|
||||
private static final String BETWEEN = " between ";
|
||||
private static final String _BETWEEN = " between ? and ?";
|
||||
|
||||
private final Object valueHigh;
|
||||
|
||||
@@ -40,7 +40,7 @@ class BetweenExpression extends AbstractExpression {
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
request.append(propName).append(BETWEEN).append(" ? and ? ");
|
||||
request.append(propName).append(_BETWEEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebean.util.SplitName;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -80,7 +80,7 @@ class BetweenPropertyExpression extends NonPrepareExpression {
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
request.append(" ?").append(BETWEEN).append(name(lowProperty)).append(" and ").append(name(highProperty)).append(" ");
|
||||
request.append(" ?").append(BETWEEN).append(name(lowProperty)).append(" and ").append(name(highProperty));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -53,9 +53,9 @@ class CaseInsensitiveEqualExpression extends AbstractValueExpression {
|
||||
pname = prop.getBeanProperty().getDecryptProperty(propName);
|
||||
}
|
||||
if (not) {
|
||||
request.append("lower(").append(pname).append(") !=? ");
|
||||
request.append("lower(").append(pname).append(") != ?");
|
||||
} else {
|
||||
request.append("lower(").append(pname).append(") =? ");
|
||||
request.append("lower(").append(pname).append(") = ?");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
}
|
||||
item.addSql(request);
|
||||
}
|
||||
request.append(") ");
|
||||
request.append(")");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -217,8 +217,15 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
@Override
|
||||
public Expression between(String propertyName, Object value1, Object value2) {
|
||||
public Expression inRange(String propertyName, Object value1, Object value2) {
|
||||
return new InRangeExpression(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
@Override
|
||||
public Expression between(String propertyName, Object value1, Object value2) {
|
||||
return new BetweenExpression(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
@@ -227,7 +234,6 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression betweenProperties(String lowProperty, String highProperty, Object value) {
|
||||
|
||||
return new BetweenPropertyExpression(lowProperty, highProperty, value);
|
||||
}
|
||||
|
||||
@@ -236,7 +242,6 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression gt(String propertyName, Object value) {
|
||||
|
||||
return new SimpleExpression(propertyName, Op.GT, value);
|
||||
}
|
||||
|
||||
@@ -246,7 +251,6 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression ge(String propertyName, Object value) {
|
||||
|
||||
return new SimpleExpression(propertyName, Op.GT_EQ, value);
|
||||
}
|
||||
|
||||
@@ -255,7 +259,6 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression lt(String propertyName, Object value) {
|
||||
|
||||
return new SimpleExpression(propertyName, Op.LT, value);
|
||||
}
|
||||
|
||||
@@ -264,7 +267,6 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression le(String propertyName, Object value) {
|
||||
|
||||
return new SimpleExpression(propertyName, Op.LT_EQ, value);
|
||||
}
|
||||
|
||||
@@ -273,7 +275,6 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression isNull(String propertyName) {
|
||||
|
||||
return new NullExpression(propertyName, false);
|
||||
}
|
||||
|
||||
@@ -282,7 +283,6 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression isNotNull(String propertyName) {
|
||||
|
||||
return new NullExpression(propertyName, true);
|
||||
}
|
||||
|
||||
@@ -529,7 +529,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression raw(String raw, Object value) {
|
||||
return new RawExpression(raw, new Object[]{value});
|
||||
return RawExpressionBuilder.buildSingle(raw, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -541,7 +541,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression raw(String raw, Object[] values) {
|
||||
return new RawExpression(raw, values);
|
||||
return RawExpressionBuilder.build(raw, values);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -443,6 +443,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return query.findSingleAttributeList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return query.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T findOne() {
|
||||
return query.findOne();
|
||||
@@ -789,6 +794,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> inRange(String propertyName, Object value1, Object value2) {
|
||||
add(expr.inRange(propertyName, value1, value2));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> between(String propertyName, Object value1, Object value2) {
|
||||
add(expr.between(propertyName, value1, value2));
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -9,7 +10,6 @@ import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.query.CQuery;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -98,7 +98,7 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress
|
||||
}
|
||||
request.append(" exists (");
|
||||
request.append(sql);
|
||||
request.append(") ");
|
||||
request.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,10 +11,10 @@ import io.ebean.Query;
|
||||
import io.ebeaninternal.api.SpiExpressionList;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class FilterExpressionList<T> extends DefaultExpressionList<T> {
|
||||
@@ -89,6 +89,21 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
|
||||
return rootQuery.findSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T findOne() {
|
||||
return rootQuery.findOne();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> findOneOrEmpty() {
|
||||
return rootQuery.findOneOrEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return rootQuery.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> having() {
|
||||
throw new PersistenceException(notAllowedMessage);
|
||||
|
||||
@@ -60,7 +60,7 @@ class IdExpression extends NonPrepareExpression implements SpiExpression {
|
||||
DefaultExpressionRequest r = (DefaultExpressionRequest) request;
|
||||
String idSql = r.getBeanDescriptor().getIdBinderIdSql(null);
|
||||
|
||||
request.append(idSql).append(" ");
|
||||
request.append(idSql);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,7 +85,7 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor
|
||||
}
|
||||
request.append(" in (");
|
||||
request.append(sql);
|
||||
request.append(") ");
|
||||
request.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class InRangeExpression extends AbstractExpression {
|
||||
|
||||
private final Object valueHigh;
|
||||
|
||||
private final Object valueLow;
|
||||
|
||||
InRangeExpression(String propertyName, Object valueLow, Object valueHigh) {
|
||||
super(propertyName);
|
||||
this.valueLow = valueLow;
|
||||
this.valueHigh = valueHigh;
|
||||
}
|
||||
|
||||
private Object low() {
|
||||
return NamedParamHelp.value(valueLow);
|
||||
}
|
||||
|
||||
private Object high() {
|
||||
return NamedParamHelp.value(valueHigh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDocQuery(DocQueryContext context) throws IOException {
|
||||
context.writeRange(propName, Op.GT_EQ, low(), Op.LT, high());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
request.addBindValue(low());
|
||||
request.addBindValue(high());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
request.append("(").append(propName).append(" >= ? and ").append(propName).append(" < ?)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanHash(StringBuilder builder) {
|
||||
builder.append("InRange[").append(propName).append("]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hc = low().hashCode();
|
||||
hc = hc * 92821 + high().hashCode();
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
InRangeExpression that = (InRangeExpression) other;
|
||||
return low().equals(that.low()) && high().equals(that.high());
|
||||
}
|
||||
}
|
||||
@@ -192,7 +192,7 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
}
|
||||
item.addSql(request);
|
||||
}
|
||||
request.append(") ");
|
||||
request.append(")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,6 +280,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.and(expOne, expTwo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> inRange(String propertyName, Object value1, Object value2) {
|
||||
return exprList.inRange(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> between(String propertyName, Object value1, Object value2) {
|
||||
return exprList.between(propertyName, value1, value2);
|
||||
@@ -375,6 +380,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.apply(fetchPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return exprList.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FutureIds<T> findFutureIds() {
|
||||
return exprList.findFutureIds();
|
||||
|
||||
@@ -51,7 +51,7 @@ class LikeExpression extends AbstractValueExpression {
|
||||
request.append(pname);
|
||||
}
|
||||
if (type == LikeType.EQUAL_TO) {
|
||||
request.append(" = ? ");
|
||||
request.append(" = ?");
|
||||
} else {
|
||||
// append db platform like clause
|
||||
request.appendLike(type == LikeType.RAW);
|
||||
|
||||
@@ -4,11 +4,11 @@ import io.ebean.Expression;
|
||||
import io.ebean.Junction;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -140,7 +140,7 @@ abstract class LogicExpression implements SpiExpression {
|
||||
expOne.addSql(request);
|
||||
request.append(conjunction ? AND : OR);
|
||||
expTwo.addSql(request);
|
||||
request.append(") ");
|
||||
request.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -42,7 +42,7 @@ class NativeILikeExpression extends AbstractExpression {
|
||||
pname = prop.getBeanProperty().getDecryptProperty(propName);
|
||||
}
|
||||
|
||||
request.append(pname).append(" ilike ? ");
|
||||
request.append(pname).append(" ilike ?");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,11 +2,11 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -76,7 +76,7 @@ class NoopExpression implements SpiExpression {
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
request.append("1=1 ");
|
||||
request.append("1=1");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,18 +3,18 @@ package io.ebeaninternal.server.expression;
|
||||
import io.ebean.Expression;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
final class NotExpression implements SpiExpression {
|
||||
|
||||
private static final String NOT_START = "not (";
|
||||
private static final String NOT_END = ") ";
|
||||
private static final String NOT_END = ")";
|
||||
|
||||
private final SpiExpression exp;
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class NullExpression extends AbstractExpression {
|
||||
return;
|
||||
}
|
||||
|
||||
String nullExpr = notNull ? " is not null " : " is null ";
|
||||
String nullExpr = notNull ? " is not null" : " is null";
|
||||
if (elProperty != null && elProperty.isAssocId()) {
|
||||
request.append(elProperty.getAssocIdExpression(propName, nullExpr));
|
||||
} else {
|
||||
|
||||
@@ -8,48 +8,48 @@ public enum Op {
|
||||
/**
|
||||
* Exists (JSON).
|
||||
*/
|
||||
EXISTS(" is not null ", ""),
|
||||
EXISTS(" is not null", ""),
|
||||
|
||||
/**
|
||||
* Not Exists (JSON).
|
||||
*/
|
||||
NOT_EXISTS(" is null ", ""),
|
||||
NOT_EXISTS(" is null", ""),
|
||||
|
||||
/**
|
||||
* Between (JSON).
|
||||
*/
|
||||
BETWEEN(" between ? and ? ", ""),
|
||||
BETWEEN(" between ? and ?", ""),
|
||||
|
||||
/**
|
||||
* Equal to
|
||||
*/
|
||||
EQ(" = ? ", ""),
|
||||
EQ(" = ?", ""),
|
||||
|
||||
/**
|
||||
* Not equal to.
|
||||
*/
|
||||
NOT_EQ(" <> ? ", ""),
|
||||
NOT_EQ(" <> ?", ""),
|
||||
|
||||
/**
|
||||
* Less than.
|
||||
*/
|
||||
|
||||
LT(" < ? ", "lt"),
|
||||
LT(" < ?", "lt"),
|
||||
|
||||
/**
|
||||
* Less than or equal to.
|
||||
*/
|
||||
LT_EQ(" <= ? ", "lte"),
|
||||
LT_EQ(" <= ?", "lte"),
|
||||
|
||||
/**
|
||||
* Greater than.
|
||||
*/
|
||||
GT(" > ? ", "gt"),
|
||||
GT(" > ?", "gt"),
|
||||
|
||||
/**
|
||||
* Greater than or equal to.
|
||||
*/
|
||||
GT_EQ(" >= ? ", "gte");
|
||||
GT_EQ(" >= ?", "gte");
|
||||
|
||||
final String exp;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user