mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
119
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30ad4260f0 | ||
|
|
ab421d6f2b | ||
|
|
0b807addc7 | ||
|
|
bb03ef5693 | ||
|
|
248a7b1062 | ||
|
|
9da28262b4 | ||
|
|
fc8dd18fd1 | ||
|
|
70ba658442 | ||
|
|
70b730657f | ||
|
|
bf3cb7e2e2 | ||
|
|
c2708ecc76 | ||
|
|
896aba3053 | ||
|
|
f78b602a90 | ||
|
|
306c1b5cf6 | ||
|
|
cdf2718ca9 | ||
|
|
967ca0c9f3 | ||
|
|
607acb1b4e | ||
|
|
5f923945e9 | ||
|
|
11fbe8bf5b | ||
|
|
0544fa7ab4 | ||
|
|
51eed9bfe4 | ||
|
|
32ce1f1d59 | ||
|
|
f445c118a0 | ||
|
|
a8ff918bb3 | ||
|
|
729aeae0d7 | ||
|
|
17a9341287 | ||
|
|
c863014fec | ||
|
|
8170db4101 | ||
|
|
434a5117a2 | ||
|
|
85ed8cd784 | ||
|
|
9b70900267 | ||
|
|
0e33d58bb2 | ||
|
|
9d9d13060c | ||
|
|
e071eecd59 | ||
|
|
bc8e254a65 | ||
|
|
e0e369904c | ||
|
|
bd85ba17a1 | ||
|
|
8801b8d8ca | ||
|
|
ebd6c535ab | ||
|
|
5c771a7219 | ||
|
|
eb6f722385 | ||
|
|
cef4f59b2c | ||
|
|
67352ab3b2 | ||
|
|
4a21395cda | ||
|
|
fcd7f8b6cf | ||
|
|
4565719728 | ||
|
|
cbdf5609b2 | ||
|
|
f97359502b | ||
|
|
ffaf00c064 | ||
|
|
f6f9511d2a | ||
|
|
5e94c26094 | ||
|
|
5aaed4e12b | ||
|
|
c29a57b5ec | ||
|
|
998b8cc192 | ||
|
|
9c1d930fb8 | ||
|
|
d6d7537a9c | ||
|
|
92e83c49d9 | ||
|
|
d758590da4 | ||
|
|
21a5efa9b9 | ||
|
|
55a2cb12bb | ||
|
|
293700acc0 | ||
|
|
8980a3b6fd | ||
|
|
72efc5d7d6 | ||
|
|
84589fcce6 | ||
|
|
a0e44aedf3 | ||
|
|
41102aca35 | ||
|
|
548cea432e | ||
|
|
592cb37693 | ||
|
|
f2178bf0a6 | ||
|
|
5cae425b09 | ||
|
|
94b78f1c2d | ||
|
|
561affef0f | ||
|
|
3310d2186d | ||
|
|
02bdace144 | ||
|
|
7c17098f17 | ||
|
|
6f194ce95d | ||
|
|
dbe473d5a1 | ||
|
|
ed0e9e7006 | ||
|
|
2a03198393 | ||
|
|
6f1472abe0 | ||
|
|
6dea4f0890 | ||
|
|
4a4ec42cdc | ||
|
|
92ea8e3738 | ||
|
|
c349832d35 | ||
|
|
90493ad57d | ||
|
|
5012f59371 | ||
|
|
149e63a272 | ||
|
|
d52a00a8fb | ||
|
|
f0dcec95dd | ||
|
|
2653931881 | ||
|
|
85207086e6 | ||
|
|
d0b0b02d6a | ||
|
|
34fa1def77 | ||
|
|
9f1aa958fc | ||
|
|
cdc5eefc69 | ||
|
|
c9588586c9 | ||
|
|
52735179d9 | ||
|
|
cef1fb9e80 | ||
|
|
c7e1964aae | ||
|
|
6d43883f77 | ||
|
|
f06f99dfc3 | ||
|
|
41e600be45 | ||
|
|
62a81c531a | ||
|
|
2cb1a626c4 | ||
|
|
18cde36b9d | ||
|
|
e05b6b78ad | ||
|
|
58c49deb06 | ||
|
|
4a41e88162 | ||
|
|
2e5becf48c | ||
|
|
4094339407 | ||
|
|
bf24949efb | ||
|
|
3b4549a7ee | ||
|
|
f194fdb132 | ||
|
|
2d274c46fa | ||
|
|
b61e9af750 | ||
|
|
a01362abad | ||
|
|
4387c662b6 | ||
|
|
ecdd027b3e | ||
|
|
2b0f572f4d |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>12.2.2</version>
|
||||
<version>12.3.5</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-12.2.2</tag>
|
||||
<tag>ebean-12.3.5</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -64,7 +64,7 @@
|
||||
<dependency>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
@@ -87,7 +87,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>6.11</version>
|
||||
<version>6.12</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -99,7 +99,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-datasource</artifactId>
|
||||
<version>4.7.3</version>
|
||||
<version>5.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -200,7 +200,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean.test</groupId>
|
||||
<artifactId>ebean-test-docker</artifactId>
|
||||
<version>3.1.3</version>
|
||||
<version>3.1.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -248,6 +248,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<version>2.6.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sap.cloud.db.jdbc</groupId>
|
||||
<artifactId>ngdbc</artifactId>
|
||||
@@ -292,7 +299,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>12.2.2</version>
|
||||
<version>12.3.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
@@ -310,7 +317,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.1</version>
|
||||
<version>3.0.0-M4</version>
|
||||
<configuration>
|
||||
<useSystemClassLoader>false</useSystemClassLoader>
|
||||
<trimStackTrace>false</trimStackTrace>
|
||||
|
||||
@@ -31,15 +31,18 @@ public interface BackgroundExecutor {
|
||||
* Execute a task periodically with a fixed delay between each execution.
|
||||
* <p>
|
||||
* For example, execute a runnable every minute.
|
||||
* </p>
|
||||
* <p>
|
||||
* The delay is the time between executions no matter how long the task took.
|
||||
* That is, this method has the same behaviour characteristics as
|
||||
* {@link ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)}
|
||||
* </p>
|
||||
*/
|
||||
void executePeriodically(Runnable r, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Execute a task periodically additionally with an initial delay different from delay.
|
||||
*/
|
||||
void executePeriodically(Runnable r, long initialDelay, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Schedules a Runnable for one-shot action that becomes enabled after the given delay.
|
||||
*
|
||||
|
||||
@@ -101,6 +101,22 @@ public class DB {
|
||||
// return originalPrimaryServer;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Return the ScriptRunner for the default database.
|
||||
* <p>
|
||||
* Useful to run SQL scripts that are resources. For example a test script
|
||||
* for inserting seed data for a particular test.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* DB.script().run("/scripts/test-script.sql")
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public static ScriptRunner script() {
|
||||
return getDefault().script();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ExpressionFactory from the default database.
|
||||
* <p>
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Ebean will be Deprecated - please migrate to use DB.
|
||||
* Deprecated - please migrate to use <code>io.ebean.DB</code>.
|
||||
* <p>
|
||||
* Ebean is a registry of {@link Database} by name. Ebean has now been renamed to {@link DB}.
|
||||
* <p>
|
||||
@@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* <p>
|
||||
* The preference is to use DB and Database rather than Ebean and EbeanServer.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class Ebean {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Ebean.class);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebean;
|
||||
|
||||
/**
|
||||
* Deprecated - please migrate to <code>io.ebean.Database</code>.
|
||||
* Provides the API for fetching and saving beans to a particular Database.
|
||||
* <p>
|
||||
* Effectively this is an alias for {@link Database} which is now the new
|
||||
@@ -8,6 +9,7 @@ package io.ebean;
|
||||
* <p>
|
||||
* The preference is to use DB and Database rather than Ebean and EbeanServer.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface EbeanServer extends Database {
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import java.util.Properties;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Deprecated - please migrate to DatabaseFactory.
|
||||
* <p>
|
||||
* Creates EbeanServer instances.
|
||||
* <p>
|
||||
* This uses either a ServerConfig or properties in the ebean.properties file to
|
||||
@@ -28,6 +30,7 @@ import java.util.ServiceLoader;
|
||||
* convenient way of using the 'default/primary' EbeanServer.
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
public class EbeanServerFactory {
|
||||
|
||||
|
||||
|
||||
@@ -70,30 +70,40 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> order(String orderByClause);
|
||||
|
||||
/**
|
||||
* Set the order by clause replacing the existing order by clause if there is
|
||||
* one.
|
||||
* <p>
|
||||
* This follows SQL syntax using commas between each property with the
|
||||
* optional asc and desc keywords representing ascending and descending order
|
||||
* respectively.
|
||||
*/
|
||||
ExpressionList<T> orderBy(String orderBy);
|
||||
|
||||
/**
|
||||
* Return the OrderBy so that you can append an ascending or descending
|
||||
* property to the order by clause.
|
||||
* <p>
|
||||
* This will never return a null. If no order by clause exists then an 'empty'
|
||||
* OrderBy object is returned.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is the same as <code>orderBy()</code>
|
||||
*/
|
||||
OrderBy<T> order();
|
||||
|
||||
/**
|
||||
* Deprecated migrate to order().
|
||||
* Return the OrderBy so that you can append an ascending or descending
|
||||
* property to the order by clause.
|
||||
* <p>
|
||||
* This will never return a null. If no order by clause exists then an 'empty'
|
||||
* OrderBy object is returned.
|
||||
* <p>
|
||||
* This is the same as <code>order()</code>
|
||||
*/
|
||||
@Deprecated
|
||||
OrderBy<T> orderBy();
|
||||
|
||||
/**
|
||||
* Deprecated migrate to {@link #order(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> orderBy(String orderBy);
|
||||
|
||||
/**
|
||||
* Deprecated migrate to {@link #order(String)}
|
||||
* Deprecated migrate to {@link #orderBy(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> setOrderBy(String orderBy);
|
||||
@@ -1241,26 +1251,37 @@ public interface ExpressionList<T> {
|
||||
/**
|
||||
* Add raw expression with a single parameter.
|
||||
* <p>
|
||||
* The raw expression should contain a single ? at the location of the
|
||||
* parameter.
|
||||
* The raw expression should contain a single ? or ?1
|
||||
* at the location of the parameter. We use ?1 when binding a
|
||||
* collection for an IN expression.
|
||||
* </p>
|
||||
* <p>
|
||||
* <p>>
|
||||
* When properties in the clause are fully qualified as table-column names
|
||||
* then they are not translated. logical property name names (not fully
|
||||
* qualified) will still be translated to their physical name.
|
||||
* </p>
|
||||
* <p>
|
||||
* <h4>Example:</h4>
|
||||
* <h4>Examples:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // use a database function
|
||||
* raw("add_days(orderDate, 10) < ?", someDate)
|
||||
*
|
||||
* raw("name like ?", "Rob%")
|
||||
*
|
||||
* raw("name in (?1)", asList("Rob", "Fiona", "Jack"))
|
||||
*
|
||||
* raw("name = any(?)", asList("Rob", "Fiona", "Jack"))
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h4>Subquery example:</h4>
|
||||
* <h4>Subquery examples:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Bind collection using ?1
|
||||
* .raw("id in (select c.id from o_customer c where c.name in (?1))", asList("Rob", "Fiona", "Jack"))
|
||||
*
|
||||
* // Using Postgres ANY expression
|
||||
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
|
||||
*
|
||||
* }</pre>
|
||||
@@ -1270,14 +1291,25 @@ public interface ExpressionList<T> {
|
||||
/**
|
||||
* Add raw expression with an array of parameters.
|
||||
* <p>
|
||||
* The raw expression should contain the same number of ? as there are
|
||||
* parameters.
|
||||
* </p>
|
||||
* The raw expression should contain the same number of ? or ?1, ?2 ... bind parameters
|
||||
* as there are values. We use ?1, ?2 etc when binding a collection for an IN expression.
|
||||
* <p>
|
||||
* When properties in the clause are fully qualified as table-column names
|
||||
* then they are not translated. logical property name names (not fully
|
||||
* qualified) will still be translated to their physical name.
|
||||
* </p>
|
||||
*
|
||||
* <h4>Examples:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* raw("unitPrice > ? and product.id > ?", 2, 3)
|
||||
*
|
||||
* raw("(status = ? or (orderDate < ? and shipDate is null) or customer.name like ?)",
|
||||
* Order.Status.APPROVED,
|
||||
* new Timestamp(System.currentTimeMillis()),
|
||||
* "Rob")
|
||||
*
|
||||
* }</pre></pre>
|
||||
*/
|
||||
ExpressionList<T> raw(String raw, Object... values);
|
||||
|
||||
|
||||
@@ -92,11 +92,9 @@ public interface ExtendedServer {
|
||||
* {@link #findEachWhile(Query, Predicate, Transaction)} is preferred
|
||||
* to findIterate(). The reason is that those methods automatically take care of
|
||||
* closing the queryIterator (and the underlying jdbc statement and resultSet).
|
||||
* </p>
|
||||
* <p>
|
||||
* This is similar to findEach in that not all the result beans need to be held
|
||||
* in memory at the same time and as such is good for processing large queries.
|
||||
* </p>
|
||||
*
|
||||
* @see Query#findIterate()
|
||||
* @see Query#findEach(Consumer)
|
||||
@@ -106,7 +104,10 @@ public interface ExtendedServer {
|
||||
<T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return the query result as a Stream using a single persistence context.
|
||||
* Execute the query returning the result as a Stream.
|
||||
* <p>
|
||||
* Note that this can support very large queries iterating any number of results.
|
||||
* To do so internally it can use multiple persistence contexts.
|
||||
* <p>
|
||||
* Note that the stream needs to be closed so use with try with resources.
|
||||
* </p>
|
||||
@@ -115,12 +116,17 @@ public interface ExtendedServer {
|
||||
<T> Stream<T> findStream(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return the query result as a Stream (with multiple persistence contexts).
|
||||
* Deprecated - migrate to findStream().
|
||||
* <p>
|
||||
* Execute the query returning the result as a Stream.
|
||||
* <p>
|
||||
* Note that this can support very large queries iterating any number of results.
|
||||
* To do so internally it can use multiple persistence contexts.
|
||||
* <p>
|
||||
* Note that the stream needs to be closed so use with try with resources.
|
||||
* </p>
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
<T> Stream<T> findLargeStream(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
|
||||
@@ -711,9 +711,9 @@ public interface Query<T> {
|
||||
/**
|
||||
* Execute the query returning the result as a Stream.
|
||||
* <p>
|
||||
* Note that this will hold all resulting beans in memory using a single
|
||||
* persistence context. Use findLargeStream() for queries that expect to
|
||||
* return a large number of results.
|
||||
* Note that this can support very large queries iterating
|
||||
* any number of results. To do so internally it can use
|
||||
* multiple persistence contexts.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -731,6 +731,8 @@ public interface Query<T> {
|
||||
Stream<T> findStream();
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to findStream.
|
||||
* <p>
|
||||
* Execute the query returning the result as a Stream.
|
||||
* <p>
|
||||
* Note that this uses multiple persistence contexts such that we can use
|
||||
@@ -749,6 +751,7 @@ public interface Query<T> {
|
||||
* }</pre>
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
Stream<T> findLargeStream();
|
||||
|
||||
/**
|
||||
@@ -1352,9 +1355,13 @@ public interface Query<T> {
|
||||
Query<T> having(Expression addExpressionToHaving);
|
||||
|
||||
/**
|
||||
* Deprecated migrate to {@link #order(String)}
|
||||
* Set the order by clause replacing the existing order by clause if there is
|
||||
* one.
|
||||
* <p>
|
||||
* This follows SQL syntax using commas between each property with the
|
||||
* optional asc and desc keywords representing ascending and descending order
|
||||
* respectively.
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> orderBy(String orderByClause);
|
||||
|
||||
/**
|
||||
@@ -1373,13 +1380,20 @@ public interface Query<T> {
|
||||
* <p>
|
||||
* This will never return a null. If no order by clause exists then an 'empty'
|
||||
* OrderBy object is returned.
|
||||
* <p>
|
||||
* This is the same as <code>orderBy()</code>
|
||||
*/
|
||||
OrderBy<T> order();
|
||||
|
||||
/**
|
||||
* Deprecated migrate to order().
|
||||
* Return the OrderBy so that you can append an ascending or descending
|
||||
* property to the order by clause.
|
||||
* <p>
|
||||
* This will never return a null. If no order by clause exists then an 'empty'
|
||||
* OrderBy object is returned.
|
||||
* <p>
|
||||
* This is the same as <code>order()</code>
|
||||
*/
|
||||
@Deprecated
|
||||
OrderBy<T> orderBy();
|
||||
|
||||
/**
|
||||
@@ -1388,9 +1402,8 @@ public interface Query<T> {
|
||||
Query<T> setOrder(OrderBy<T> orderBy);
|
||||
|
||||
/**
|
||||
* Deprecated migrate to {@link #setOrder(OrderBy)}
|
||||
* Set an OrderBy object to replace any existing OrderBy clause.
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> setOrderBy(OrderBy<T> orderBy);
|
||||
|
||||
/**
|
||||
|
||||
@@ -224,12 +224,49 @@ public interface SqlQuery extends Serializable {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* When binding a collection of values into a IN expression we should use
|
||||
* indexed parameters like ?1, ?2, ?3 etc rather than just ?.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "select c.id, c.name from customer c where c.name in (?1)";
|
||||
*
|
||||
* List<SqlRow> rows = DB.sqlQuery(sql)
|
||||
* .setParameter(asList("Rob", "Fiona", "Jack"))
|
||||
* .findList();
|
||||
*
|
||||
*
|
||||
* List<SqlRow> rows = DB.sqlQuery(sql)
|
||||
* .setParameter(1, asList("Rob", "Fiona", "Jack"))
|
||||
* .findList();
|
||||
* }</pre>
|
||||
*
|
||||
* @param value The value to bind
|
||||
*/
|
||||
SqlQuery setParameter(Object value);
|
||||
|
||||
/**
|
||||
* Bind the parameter by its index position (1 based like JDBC).
|
||||
* <p>
|
||||
* When binding a collection of values into a IN expression we should use
|
||||
* indexed parameters like ?1, ?2, ?3 etc rather than just ?.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "select c.id, c.name from customer c where c.name in (?1)";
|
||||
*
|
||||
* List<SqlRow> rows = DB.sqlQuery(sql)
|
||||
* .setParameter(asList("Rob", "Fiona", "Jack"))
|
||||
* .findList();
|
||||
*
|
||||
*
|
||||
* List<SqlRow> rows = DB.sqlQuery(sql)
|
||||
* .setParameter(1, asList("Rob", "Fiona", "Jack"))
|
||||
* .findList();
|
||||
* }</pre>
|
||||
*/
|
||||
SqlQuery setParameter(int position, Object value);
|
||||
|
||||
|
||||
@@ -5,18 +5,16 @@ package io.ebean;
|
||||
* <p>
|
||||
* Provides a simple way to execute raw SQL insert update or delete statements
|
||||
* without having to resort to JDBC.
|
||||
* </p>
|
||||
* <p>
|
||||
* Supports the use of positioned or named parameters and can automatically
|
||||
* notify Ebean of the table modified so that Ebean can maintain its cache.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that {@link #setAutoTableMod(boolean)} and
|
||||
* Ebean#externalModification(String, boolean, boolean, boolean)} can be to
|
||||
* notify Ebean of external changes and enable Ebean to maintain it's "L2"
|
||||
* server cache.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Positioned parameter example</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example using 'positioned' parameters
|
||||
@@ -30,6 +28,7 @@ package io.ebean;
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Named parameter example</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example using 'named' parameters
|
||||
@@ -45,8 +44,31 @@ package io.ebean;
|
||||
* String msg = "There were " + rows + " rows updated";
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Index parameter examples (e.g. ?1, ?2, ?3 ...)</h2>
|
||||
* <p>
|
||||
* <h3>Example: Using setNextParameter()</h3>
|
||||
* We can use index parameters like ?1, ?2, ?3 etc when binding arrays/collections
|
||||
* of values into an IN expression.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Binding a list of 3 values (9991, 9992, 9993) into an IN expression
|
||||
*
|
||||
* DB.sqlUpdate("delete from o_customer where name = ? and id in (?2)")
|
||||
* .setParameter(1, "Foo")
|
||||
* .setParameter(2, asList(9991, 9992, 9993))
|
||||
* .execute();
|
||||
*
|
||||
* // note this effectively is the same as
|
||||
*
|
||||
* DB.sqlUpdate("delete from o_customer where name = ? and id in (?2)")
|
||||
* .setParameter("Foo")
|
||||
* .setParameter(asList(9991, 9992, 9993))
|
||||
* .execute();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Example: Using setParameter()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "insert into audit_log (id, description, modified_description) values (?,?,?)";
|
||||
@@ -56,19 +78,19 @@ package io.ebean;
|
||||
* try (Transaction txn = DB.beginTransaction()) {
|
||||
* txn.setBatchMode(true);
|
||||
*
|
||||
* insert.setNextParameter(10000);
|
||||
* insert.setNextParameter("hello");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.setParameter(10000);
|
||||
* insert.setParameter("hello");
|
||||
* insert.setParameter("rob");
|
||||
* insert.execute();
|
||||
*
|
||||
* insert.setNextParameter(10001);
|
||||
* insert.setNextParameter("goodbye");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.setParameter(10001);
|
||||
* insert.setParameter("goodbye");
|
||||
* insert.setParameter("rob");
|
||||
* insert.execute();
|
||||
*
|
||||
* insert.setNextParameter(10002);
|
||||
* insert.setNextParameter("chow");
|
||||
* insert.setNextParameter("bob");
|
||||
* insert.setParameter(10002);
|
||||
* insert.setParameter("chow");
|
||||
* insert.setParameter("bob");
|
||||
* insert.execute();
|
||||
*
|
||||
* txn.commit();
|
||||
@@ -81,19 +103,19 @@ package io.ebean;
|
||||
*
|
||||
* try (Transaction txn = DB.beginTransaction()) {
|
||||
*
|
||||
* insert.setNextParameter(10000);
|
||||
* insert.setNextParameter("hello");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.setParameter(10000);
|
||||
* insert.setParameter("hello");
|
||||
* insert.setParameter("rob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* insert.setNextParameter(10001);
|
||||
* insert.setNextParameter("goodbye");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.setParameter(10001);
|
||||
* insert.setParameter("goodbye");
|
||||
* insert.setParameter("rob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* insert.setNextParameter(10002);
|
||||
* insert.setNextParameter("chow");
|
||||
* insert.setNextParameter("bob");
|
||||
* insert.setParameter(10002);
|
||||
* insert.setParameter("chow");
|
||||
* insert.setParameter("bob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* int[] rows = insert.executeBatch();
|
||||
|
||||
@@ -36,10 +36,12 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
private transient BeanLoader beanLoader;
|
||||
|
||||
private volatile transient PreGetterCallback preGetterCallback;
|
||||
private transient PreGetterCallback preGetterCallback;
|
||||
|
||||
private String ebeanServerName;
|
||||
|
||||
private boolean deletedFromCollection;
|
||||
|
||||
/**
|
||||
* The actual entity bean that 'owns' this intercept.
|
||||
*/
|
||||
@@ -80,14 +82,14 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
private static final byte FLAG_CHANGED_PROP = 2;
|
||||
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distingush
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private static final byte FLAG_EMBEDDED_DIRTY = 4;
|
||||
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distingush
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
@@ -369,13 +371,11 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
/**
|
||||
* Set the loaded state to true.
|
||||
* <p>
|
||||
* Calls to setter methods after the bean is loaded can result in 'Old Values'
|
||||
* being created to support ConcurrencyMode.ALL
|
||||
* </p>
|
||||
* Calls to setter methods after the bean is loaded can result in
|
||||
* 'Old Values' being created.
|
||||
* <p>
|
||||
* Worth noting that this is also set after a insert/update. By doing so it
|
||||
* 'resets' the bean for making further changes and saving again.
|
||||
* </p>
|
||||
*/
|
||||
public void setLoaded() {
|
||||
this.state = STATE_LOADED;
|
||||
@@ -389,8 +389,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* When finished loading for lazy or refresh on an already partially populated
|
||||
* bean.
|
||||
* When finished loading for lazy or refresh on an already partially populated bean.
|
||||
*/
|
||||
public void setLoadedLazy() {
|
||||
this.state = STATE_LOADED;
|
||||
@@ -421,9 +420,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
/**
|
||||
* Set true to turn off lazy loading.
|
||||
* <p>
|
||||
* Typically used to disable lazy loading on SQL based report beans.
|
||||
* </p>
|
||||
*/
|
||||
public void setDisableLazyLoad(boolean disableLazyLoad) {
|
||||
this.disableLazyLoad = disableLazyLoad;
|
||||
@@ -443,7 +439,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Return true if the embedded bean is new or dirty and hence needs saving.
|
||||
*/
|
||||
public boolean isEmbeddedNewOrDirty(Object embeddedBean) {
|
||||
|
||||
if (embeddedBean == null) {
|
||||
// if it was previously set then the owning bean would
|
||||
// have oldValues containing the previous embedded bean
|
||||
@@ -451,7 +446,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
if (embeddedBean instanceof EntityBean) {
|
||||
return ((EntityBean) embeddedBean)._ebean_getIntercept().isNewOrDirty();
|
||||
|
||||
} else {
|
||||
// non-enhanced so must assume it is new and needs to be saved
|
||||
return true;
|
||||
@@ -469,8 +463,8 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the index position of a given property. Returns -1 if the property
|
||||
* can not be found.
|
||||
* Finds the index position of a given property. Returns -1 if the
|
||||
* property can not be found.
|
||||
*/
|
||||
public int findProperty(String propertyName) {
|
||||
String[] names = owner._ebean_getPropertyNames();
|
||||
@@ -493,7 +487,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of properties.s
|
||||
* Return the number of properties.
|
||||
*/
|
||||
public int getPropertyLength() {
|
||||
return owner._ebean_getPropertyNames().length;
|
||||
@@ -515,8 +509,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property to be treated as unloaded. Used for properties initialised in default
|
||||
* constructor.
|
||||
* Set the property to be treated as unloaded. Used for properties initialised in default constructor.
|
||||
*/
|
||||
public void setPropertyUnloaded(int propertyIndex) {
|
||||
flags[propertyIndex] &= ~FLAG_LOADED_PROP;
|
||||
@@ -603,7 +596,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* For forced update on a 'New' bean set all the loaded properties to changed.
|
||||
*/
|
||||
public void setNewBeanForUpdate() {
|
||||
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
if ((flags[i] & FLAG_LOADED_PROP) != 0) {
|
||||
flags[i] |= FLAG_CHANGED_PROP;
|
||||
@@ -658,8 +650,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
|
||||
// the property has been changed on this bean
|
||||
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
|
||||
props.add(propName);
|
||||
props.add((prefix == null ? getProperty(i) : prefix + getProperty(i)));
|
||||
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
|
||||
// an embedded property has been changed - recurse
|
||||
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
|
||||
@@ -672,12 +663,10 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Return true if any of the given property names are dirty.
|
||||
*/
|
||||
public boolean hasDirtyProperty(Set<String> propertyNames) {
|
||||
|
||||
String[] names = owner._ebean_getPropertyNames();
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
|
||||
// the property has been changed on this bean
|
||||
if (propertyNames.contains(names[i])) {
|
||||
return true;
|
||||
}
|
||||
@@ -710,10 +699,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
|
||||
Object newVal = owner._ebean_getField(i);
|
||||
Object oldVal = getOrigValue(i);
|
||||
if (!areEqual(oldVal, newVal)) {
|
||||
if (notEqual(oldVal, newVal)) {
|
||||
dirtyValues.put(propName, new ValuePair(newVal, oldVal));
|
||||
}
|
||||
|
||||
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
|
||||
// an embedded property has been changed - recurse
|
||||
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
|
||||
@@ -732,10 +720,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
// the property has been changed on this bean
|
||||
Object newVal = owner._ebean_getField(i);
|
||||
Object oldVal = getOrigValue(i);
|
||||
if (!areEqual(oldVal, newVal)) {
|
||||
if (notEqual(oldVal, newVal)) {
|
||||
visitor.visit(i, newVal, oldVal);
|
||||
}
|
||||
|
||||
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
|
||||
// an embedded property has been changed - recurse
|
||||
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
|
||||
@@ -756,7 +743,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a dirty property hash recursing into embedded beans.
|
||||
* Add and return a dirty property hash.
|
||||
*/
|
||||
private void addDirtyPropertyKey(StringBuilder sb) {
|
||||
if (sortOrder > 0) {
|
||||
@@ -816,21 +803,18 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Load the bean when it is a reference.
|
||||
*/
|
||||
protected void loadBean(int loadProperty) {
|
||||
|
||||
synchronized (this) {
|
||||
if (beanLoader == null) {
|
||||
final Database database = DB.byName(ebeanServerName);
|
||||
if (database == null) {
|
||||
throw new PersistenceException("Database [" + ebeanServerName + "] was not found?");
|
||||
}
|
||||
|
||||
// For stand alone reference bean or after deserialisation lazy load
|
||||
// using the ebeanServer. Synchronise only on the bean.
|
||||
loadBeanInternal(loadProperty, database.getPluginApi());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (beanLoader) {
|
||||
// Lazy loading using LoadBeanContext which supports batch loading
|
||||
// Synchronise on the beanLoader (a 'node' of the LoadBeanContext 'tree')
|
||||
@@ -842,32 +826,24 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Invoke the lazy loading. This method is synchronised externally.
|
||||
*/
|
||||
private void loadBeanInternal(int loadProperty, BeanLoader loader) {
|
||||
|
||||
if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
|
||||
// race condition where multiple threads calling preGetter concurrently
|
||||
return;
|
||||
}
|
||||
|
||||
if (lazyLoadFailure) {
|
||||
// failed when batch lazy loaded by another bean in the batch
|
||||
throw new EntityNotFoundException("(Lazy) loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
|
||||
}
|
||||
|
||||
if (lazyLoadProperty == -1) {
|
||||
|
||||
lazyLoadProperty = loadProperty;
|
||||
|
||||
if (nodeUsageCollector != null) {
|
||||
nodeUsageCollector.setLoadProperty(getProperty(lazyLoadProperty));
|
||||
}
|
||||
|
||||
loader.loadBean(this);
|
||||
|
||||
if (lazyLoadFailure) {
|
||||
// failed when lazy loading this bean
|
||||
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted.");
|
||||
}
|
||||
|
||||
// bean should be loaded and intercepting now. setLoaded() has
|
||||
// been called by the lazy loading mechanism
|
||||
}
|
||||
@@ -877,32 +853,31 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Helper method to check if two objects are equal.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected boolean areEqual(Object obj1, Object obj2) {
|
||||
protected static boolean notEqual(Object obj1, Object obj2) {
|
||||
if (obj1 == null) {
|
||||
return (obj2 == null);
|
||||
return (obj2 != null);
|
||||
}
|
||||
if (obj2 == null) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if (obj1 == obj2) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (obj1 instanceof BigDecimal) {
|
||||
// Use comparable for BigDecimal as equals
|
||||
// uses scale in comparison...
|
||||
if (obj2 instanceof BigDecimal) {
|
||||
Comparable com1 = (Comparable) obj1;
|
||||
return (com1.compareTo(obj2) == 0);
|
||||
|
||||
return (com1.compareTo(obj2) != 0);
|
||||
} else {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (obj1 instanceof URL) {
|
||||
// use the string format to determine if dirty
|
||||
return obj1.toString().equals(obj2.toString());
|
||||
return !obj1.toString().equals(obj2.toString());
|
||||
}
|
||||
return obj1.equals(obj2);
|
||||
return !obj1.equals(obj2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -913,6 +888,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
private void preGetterCallback(int propertyIndex) {
|
||||
PreGetterCallback preGetterCallback = this.preGetterCallback;
|
||||
if (preGetterCallback != null) {
|
||||
preGetterCallback.preGetterTrigger(propertyIndex);
|
||||
}
|
||||
@@ -942,25 +918,20 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* OneToMany and ManyToMany don't have any interception so just check for
|
||||
* PropertyChangeSupport.
|
||||
* OneToMany and ManyToMany only set loaded state.
|
||||
*/
|
||||
public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
|
||||
|
||||
if (readOnly) {
|
||||
throw new IllegalStateException("This bean is readOnly");
|
||||
}
|
||||
|
||||
setLoadedProperty(propertyIndex);
|
||||
}
|
||||
|
||||
private void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
|
||||
|
||||
if (readOnly) {
|
||||
throw new IllegalStateException("This bean is readOnly");
|
||||
}
|
||||
setChangedProperty(propertyIndex);
|
||||
|
||||
if (setDirtyState) {
|
||||
setOriginalValue(propertyIndex, origValue);
|
||||
setDirtyStatus();
|
||||
@@ -985,10 +956,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* the old values for use with ConcurrencyMode.ALL.
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (!areEqual(oldValue, newValue)) {
|
||||
} else if (notEqual(oldValue, newValue)) {
|
||||
setChangedPropertyValue(propertyIndex, intercept, oldValue);
|
||||
}
|
||||
}
|
||||
@@ -998,7 +968,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Check for primitive boolean.
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (oldValue != newValue) {
|
||||
@@ -1010,7 +979,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Check for primitive int.
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (oldValue != newValue) {
|
||||
@@ -1022,7 +990,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* long.
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (oldValue != newValue) {
|
||||
@@ -1034,7 +1001,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* double.
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (Double.compare(oldValue, newValue) != 0) {
|
||||
@@ -1046,7 +1012,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* float.
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (Float.compare(oldValue, newValue) != 0) {
|
||||
@@ -1058,7 +1023,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* short.
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (oldValue != newValue) {
|
||||
@@ -1070,7 +1034,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* char.
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (oldValue != newValue) {
|
||||
@@ -1082,7 +1045,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* byte.
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (oldValue != newValue) {
|
||||
@@ -1094,7 +1056,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* char[].
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (!Arrays.equals(oldValue, newValue)) {
|
||||
@@ -1106,7 +1067,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* byte[].
|
||||
*/
|
||||
public void preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
|
||||
|
||||
if (state == STATE_NEW) {
|
||||
setLoadedProperty(propertyIndex);
|
||||
} else if (!Arrays.equals(oldValue, newValue)) {
|
||||
@@ -1137,6 +1097,17 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the entity was deleted from a BeanCollection.
|
||||
*/
|
||||
public void setDeletedFromCollection(final boolean deletedFromCollection) {
|
||||
this.deletedFromCollection = deletedFromCollection;
|
||||
}
|
||||
|
||||
public boolean isOrphanDelete() {
|
||||
return deletedFromCollection && !isNew();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the load error that happened on this property.
|
||||
*/
|
||||
|
||||
@@ -61,6 +61,22 @@ public interface PersistenceContext {
|
||||
*/
|
||||
int size(Class<?> rootType);
|
||||
|
||||
/**
|
||||
* Return a copy of the Persistence context to use for large query iteration.
|
||||
*/
|
||||
PersistenceContext forIterate();
|
||||
|
||||
/**
|
||||
* Return a new Persistence context during iteration of large query result.
|
||||
*/
|
||||
PersistenceContext forIterateReset();
|
||||
|
||||
/**
|
||||
* Return true if the persistence context has grown and hit the 'reset limit'
|
||||
* during large query iteration.
|
||||
*/
|
||||
boolean resetLimit();
|
||||
|
||||
/**
|
||||
* Wrapper on a bean to also indicate if a bean has been deleted.
|
||||
* <p>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebean.common;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -54,6 +56,11 @@ class ModifyHolder<E> implements Serializable {
|
||||
void modifyAddition(E bean) {
|
||||
if (bean != null) {
|
||||
touched = true;
|
||||
|
||||
if (bean instanceof EntityBean) {
|
||||
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(false);
|
||||
}
|
||||
|
||||
// If it is to delete then just remove the deletion
|
||||
if (!undoDeletion(bean)) {
|
||||
// Insert
|
||||
@@ -70,6 +77,11 @@ class ModifyHolder<E> implements Serializable {
|
||||
void modifyRemoval(Object bean) {
|
||||
if (bean != null) {
|
||||
touched = true;
|
||||
|
||||
if (bean instanceof EntityBean) {
|
||||
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(true);
|
||||
}
|
||||
|
||||
// If it is to be added then just remove the addition
|
||||
if (!undoAddition(bean)) {
|
||||
modifyDeletions.add((E) bean);
|
||||
|
||||
@@ -18,4 +18,9 @@ public @interface ModuleInfo {
|
||||
* The db name prefix is added to entity classes for non default databases.
|
||||
*/
|
||||
String[] entities() default {};
|
||||
|
||||
/**
|
||||
* Other classes like Attribute Converters.
|
||||
*/
|
||||
String[] other() default {};
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ import java.util.Properties;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Deprecated - please migrate to <code>io.ebean.DatabaseConfig</code>.
|
||||
*
|
||||
* The configuration used for creating a Database.
|
||||
* <p>
|
||||
* Used to programmatically construct a Database and optionally register it
|
||||
@@ -80,6 +82,7 @@ import java.util.ServiceLoader;
|
||||
* @author rbygrave
|
||||
* @see DatabaseFactory
|
||||
*/
|
||||
@Deprecated
|
||||
public class ServerConfig {
|
||||
|
||||
/**
|
||||
@@ -331,18 +334,6 @@ public class ServerConfig {
|
||||
*/
|
||||
private ClassLoadConfig classLoadConfig = new ClassLoadConfig();
|
||||
|
||||
/**
|
||||
* Set to true if the DataSource uses autoCommit.
|
||||
* <p>
|
||||
* Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
|
||||
*/
|
||||
private boolean autoCommitMode;
|
||||
|
||||
/**
|
||||
* Set to true if transaction begin should be started with explicit statement.
|
||||
*/
|
||||
private boolean explicitTransactionBeginMode;
|
||||
|
||||
/**
|
||||
* The data source JNDI name if using a JNDI DataSource.
|
||||
*/
|
||||
@@ -475,6 +466,11 @@ public class ServerConfig {
|
||||
|
||||
private String enabledL2Regions;
|
||||
|
||||
/**
|
||||
* Set to true to effectively disable L2 cache plugins.
|
||||
*/
|
||||
private boolean localOnlyL2Cache;
|
||||
|
||||
/**
|
||||
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
|
||||
* If set to false, use io.ebean.annotation.NotNull or Column(nullable=true).
|
||||
@@ -1740,42 +1736,6 @@ public class ServerConfig {
|
||||
this.dataSourceJndiName = dataSourceJndiName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if autoCommit mode is on. This indicates to Ebean to use autoCommit friendly Transactions and TransactionManager.
|
||||
*/
|
||||
public boolean isAutoCommitMode() {
|
||||
return autoCommitMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if autoCommit mode is on and Ebean should use autoCommit friendly Transactions and TransactionManager.
|
||||
*/
|
||||
public void setAutoCommitMode(boolean autoCommitMode) {
|
||||
this.autoCommitMode = autoCommitMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if transaction begin should be started with explicit statement.
|
||||
*/
|
||||
public boolean isExplicitTransactionBeginMode() {
|
||||
return explicitTransactionBeginMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if transaction begin should be started with explicit statement.
|
||||
* <p>
|
||||
* This works for H2 and Postgres but not for Oracle - only use this if you first name
|
||||
* is Daryl or you have explicitly talked to Rob about this feature.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is generally not expected to be turned on but instead allow transactions to start
|
||||
* implicitly which is generally the standard approach.
|
||||
* </p>
|
||||
*/
|
||||
public void setExplicitTransactionBeginMode(boolean explicitTransactionBeginMode) {
|
||||
this.explicitTransactionBeginMode = explicitTransactionBeginMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value used to represent TRUE in the database.
|
||||
* <p>
|
||||
@@ -2799,7 +2759,7 @@ public class ServerConfig {
|
||||
*/
|
||||
protected void loadDataSourceSettings(PropertiesWrapper p) {
|
||||
dataSourceConfig.loadSettings(p.properties, name);
|
||||
readOnlyDataSourceConfig.loadSettings(p.properties, name);
|
||||
readOnlyDataSourceConfig.loadSettings(p.properties, name + "-ro");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2860,10 +2820,9 @@ public class ServerConfig {
|
||||
collectQueryPlanThresholdMicros = p.getLong("collectQueryPlanThresholdMicros", collectQueryPlanThresholdMicros);
|
||||
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
|
||||
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
|
||||
localOnlyL2Cache = p.getBoolean("localOnlyL2Cache", localOnlyL2Cache);
|
||||
enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
|
||||
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
|
||||
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
|
||||
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
|
||||
useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
|
||||
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
|
||||
@@ -3100,6 +3059,20 @@ public class ServerConfig {
|
||||
this.disableL2Cache = disableL2Cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to use local only L2 cache. Effectively ignore l2 cache plugin like ebean-redis etc.
|
||||
*/
|
||||
public boolean isLocalOnlyL2Cache() {
|
||||
return localOnlyL2Cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the use of local only L2 cache. Effectively ignore l2 cache plugin like ebean-redis etc.
|
||||
*/
|
||||
public void setLocalOnlyL2Cache(boolean localOnlyL2Cache) {
|
||||
this.localOnlyL2Cache = localOnlyL2Cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if we use javax.validation.constraints.NotNull
|
||||
*/
|
||||
|
||||
@@ -43,11 +43,8 @@ public abstract class AbstractDbEncrypt implements DbEncrypt {
|
||||
public DbEncryptFunction getDbEncryptFunction(int jdbcType) {
|
||||
switch (jdbcType) {
|
||||
case Types.VARCHAR:
|
||||
return varcharEncryptFunction;
|
||||
case Types.CLOB:
|
||||
return varcharEncryptFunction;
|
||||
case Types.CHAR:
|
||||
return varcharEncryptFunction;
|
||||
case Types.LONGVARCHAR:
|
||||
return varcharEncryptFunction;
|
||||
|
||||
|
||||
@@ -7,30 +7,18 @@ package io.ebean.config.dbplatform;
|
||||
* As an alternative to using DB encryption you can encrypt/decrypt in java via
|
||||
* a special ScalarType but this has the limitation that you can't include that
|
||||
* property in query where clauses.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface DbEncrypt {
|
||||
|
||||
// /**
|
||||
// * Return the SQL for decrypting a column returning a VARCHAR.
|
||||
// */
|
||||
// String getDecryptSql(String columnWithTableAlias);
|
||||
//
|
||||
// /**
|
||||
// * Return the DB function with bind variables used to encrypt a VARCHAR
|
||||
// * value.
|
||||
// */
|
||||
// String getEncryptBindSql();
|
||||
|
||||
/**
|
||||
* Return the DB encrypt function for the given JDBC type.
|
||||
*/
|
||||
DbEncryptFunction getDbEncryptFunction(int jdbcType);
|
||||
|
||||
/**
|
||||
* Return the DB type that encrypted Strings are stored in.
|
||||
* <p>
|
||||
* This is VARCHAR for MySql and VARBINARY for most others.
|
||||
* </p>
|
||||
*/
|
||||
int getEncryptDbType();
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.ebean.config.dbplatform.mariadb;
|
||||
|
||||
import io.ebean.config.dbplatform.DbStandardHistorySupport;
|
||||
|
||||
/**
|
||||
* History support for MariaDB.
|
||||
*/
|
||||
public class MariaDbHistorySupport extends DbStandardHistorySupport {
|
||||
|
||||
/**
|
||||
* Return the ' as of timestamp ?' clause appended after the table name.
|
||||
*/
|
||||
@Override
|
||||
public String getAsOfViewSuffix(String asOfViewSuffix) {
|
||||
return " for system_time as of ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersionsBetweenSuffix(String asOfViewSuffix) {
|
||||
return " for system_time between ? and ?";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL Server specific effective start column.
|
||||
*/
|
||||
@Override
|
||||
public String getSysPeriodLower(String tableAlias, String sysPeriod) {
|
||||
return tableAlias + ".row_start";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL Server specific effective end column.
|
||||
*/
|
||||
@Override
|
||||
public String getSysPeriodUpper(String tableAlias, String sysPeriod) {
|
||||
return tableAlias + ".row_end";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.ebean.config.dbplatform.mariadb;
|
||||
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.dbplatform.mysql.BaseMySqlPlatform;
|
||||
|
||||
/**
|
||||
* MariaDB platform.
|
||||
*/
|
||||
public class MariaDbPlatform extends BaseMySqlPlatform {
|
||||
|
||||
public MariaDbPlatform() {
|
||||
super();
|
||||
this.platform = Platform.MARIADB;
|
||||
this.historySupport = new MariaDbHistorySupport();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package io.ebean.config.dbplatform.mysql;
|
||||
|
||||
import io.ebean.Query;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.config.dbplatform.SqlErrorCodes;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Base platform for both MySql and MariaDB.
|
||||
*/
|
||||
public abstract class BaseMySqlPlatform extends DatabasePlatform {
|
||||
|
||||
public BaseMySqlPlatform() {
|
||||
super();
|
||||
this.useExtraTransactionOnIterateSecondaryQueries = true;
|
||||
this.selectCountWithAlias = true;
|
||||
this.supportsSavepointId = false;
|
||||
this.inlineSqlUpdateLimit = true;
|
||||
this.dbEncrypt = new MySqlDbEncrypt();
|
||||
this.historySupport = new MySqlHistorySupport();
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
this.dbIdentity.setSupportsSequence(false);
|
||||
|
||||
this.dbDefaultValue.setNow("now(6)"); // must have same precision as TIMESTAMP
|
||||
this.dbDefaultValue.setFalse("0");
|
||||
this.dbDefaultValue.setTrue("1");
|
||||
|
||||
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
.addAcquireLock("1205")
|
||||
.addDuplicateKey("1062", "1169")
|
||||
.addDataIntegrity("630", "839", "840", "893", "1215", "1216", "1217", "1364", "1451", "1452", "1557")
|
||||
.build();
|
||||
|
||||
this.openQuote = "`";
|
||||
this.closeQuote = "`";
|
||||
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
|
||||
this.likeClauseRaw = "like ? escape''";
|
||||
this.likeClauseEscaped = "like ? escape'|'";
|
||||
|
||||
this.forwardOnlyHintOnFindIterate = true;
|
||||
this.booleanDbType = Types.BIT;
|
||||
|
||||
dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1)"));
|
||||
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1)"));
|
||||
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime(6)"));
|
||||
dbTypeMap.put(DbType.CLOB, new MySqlClob());
|
||||
dbTypeMap.put(DbType.BLOB, new MySqlBlob());
|
||||
dbTypeMap.put(DbType.BINARY, new DbPlatformType("binary", 255));
|
||||
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
|
||||
dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false));
|
||||
dbTypeMap.put(DbType.JSONB, new DbPlatformType("json", false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
|
||||
// NOWAIT and SKIP LOCKED currently not supported with MySQL
|
||||
return sql + " for update";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
|
||||
public class MySql55Platform extends MySqlPlatform {
|
||||
public class MySql55Platform extends BaseMySqlPlatform {
|
||||
|
||||
public MySql55Platform() {
|
||||
super();
|
||||
|
||||
@@ -1,77 +1,15 @@
|
||||
package io.ebean.config.dbplatform.mysql;
|
||||
|
||||
import io.ebean.Query;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.config.dbplatform.SqlErrorCodes;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* MySQL specific platform.
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>supportsGetGeneratedKeys = true</li>
|
||||
* <li>Uses LIMIT OFFSET clause</li>
|
||||
* <li>Uses ` for quoted identifiers</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class MySqlPlatform extends DatabasePlatform {
|
||||
public class MySqlPlatform extends BaseMySqlPlatform {
|
||||
|
||||
public MySqlPlatform() {
|
||||
super();
|
||||
this.platform = Platform.MYSQL;
|
||||
this.useExtraTransactionOnIterateSecondaryQueries = true;
|
||||
this.selectCountWithAlias = true;
|
||||
this.supportsSavepointId = false;
|
||||
this.inlineSqlUpdateLimit = true;
|
||||
this.dbEncrypt = new MySqlDbEncrypt();
|
||||
this.historySupport = new MySqlHistorySupport();
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
this.dbIdentity.setSupportsSequence(false);
|
||||
|
||||
this.dbDefaultValue.setNow("now(6)"); // must have same precision as TIMESTAMP
|
||||
this.dbDefaultValue.setFalse("0");
|
||||
this.dbDefaultValue.setTrue("1");
|
||||
|
||||
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
.addAcquireLock("1205")
|
||||
.addDuplicateKey("1062", "1169")
|
||||
.addDataIntegrity("630", "839", "840", "893", "1215", "1216", "1217", "1364", "1451", "1452", "1557")
|
||||
.build();
|
||||
|
||||
this.openQuote = "`";
|
||||
this.closeQuote = "`";
|
||||
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
|
||||
this.likeClauseRaw = "like ? escape''";
|
||||
this.likeClauseEscaped = "like ? escape'|'";
|
||||
|
||||
this.forwardOnlyHintOnFindIterate = true;
|
||||
this.booleanDbType = Types.BIT;
|
||||
|
||||
dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1)"));
|
||||
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1)"));
|
||||
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime(6)"));
|
||||
dbTypeMap.put(DbType.CLOB, new MySqlClob());
|
||||
dbTypeMap.put(DbType.BLOB, new MySqlBlob());
|
||||
dbTypeMap.put(DbType.BINARY, new DbPlatformType("binary", 255));
|
||||
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
|
||||
dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false));
|
||||
dbTypeMap.put(DbType.JSONB, new DbPlatformType("json", false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
|
||||
// NOWAIT and SKIP LOCKED currently not supported with MySQL
|
||||
return sql + " for update";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebean.config.dbplatform.oracle;
|
||||
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
|
||||
/**
|
||||
* Oracle 11 platform using rownum sql limiting.
|
||||
@@ -12,5 +13,6 @@ public class Oracle11Platform extends OraclePlatform {
|
||||
this.platform = Platform.ORACLE11;
|
||||
this.columnAliasPrefix = "c";
|
||||
this.sqlLimiter = new OracleRownumSqlLimiter();
|
||||
dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
|
||||
this.historySupport = new OracleDbHistorySupport();
|
||||
this.truncateTable = "truncate table %s cascade";
|
||||
dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
dbIdentity.setIdType(IdType.IDENTITY);
|
||||
dbIdentity.setSupportsSequence(true);
|
||||
dbIdentity.setSupportsIdentity(true);
|
||||
dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
|
||||
@@ -147,7 +147,7 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
|
||||
*
|
||||
* @param tenantId For multi-tenant lazy loading provide the tenantId to use.
|
||||
*/
|
||||
SpiTransaction createQueryTransaction(Object tenantId);
|
||||
SpiTransaction createReadOnlyTransaction(Object tenantId);
|
||||
|
||||
/**
|
||||
* An event from another server in the cluster used to notify local
|
||||
|
||||
@@ -458,7 +458,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
/**
|
||||
* Set the query to be a delete query.
|
||||
*/
|
||||
void setupForDeleteOrUpdate(boolean deleteRequest);
|
||||
void setupForDeleteOrUpdate();
|
||||
|
||||
/**
|
||||
* Set the query to be delete by ids due to cascading delete.
|
||||
@@ -788,7 +788,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
/**
|
||||
* Set to true when we only include the Id property if it is explicitly included in the select().
|
||||
*/
|
||||
void setManualId(boolean manualId);
|
||||
void setManualId();
|
||||
|
||||
/**
|
||||
* Set default select clauses where none have been explicitly defined.
|
||||
|
||||
@@ -313,4 +313,9 @@ public interface SpiTransaction extends Transaction {
|
||||
* Return true when nested transactions should create Savepoints.
|
||||
*/
|
||||
boolean isNestedUseSavepoint();
|
||||
|
||||
/**
|
||||
* Return true if explicitly set to skip cache (ignores skipOnWrite).
|
||||
*/
|
||||
boolean isSkipCacheExplicit();
|
||||
}
|
||||
|
||||
@@ -158,6 +158,11 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
transaction.setSkipCache(skipCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCacheExplicit() {
|
||||
return transaction.isSkipCacheExplicit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCache() {
|
||||
return transaction.isSkipCache();
|
||||
|
||||
@@ -15,6 +15,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.config.dbplatform.hana.HanaPlatform;
|
||||
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
|
||||
import io.ebean.config.dbplatform.mariadb.MariaDbPlatform;
|
||||
import io.ebean.config.dbplatform.mysql.MySql55Platform;
|
||||
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
|
||||
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
|
||||
@@ -820,6 +821,8 @@ public class DefaultDbMigration implements DbMigration {
|
||||
return new Postgres9Platform();
|
||||
case POSTGRES:
|
||||
return new PostgresPlatform();
|
||||
case MARIADB:
|
||||
return new MariaDbPlatform();
|
||||
case MYSQL55:
|
||||
return new MySql55Platform();
|
||||
case MYSQL:
|
||||
|
||||
@@ -111,11 +111,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
this.columnName = column.getName();
|
||||
this.defaultValue = platformDdl.convertDefaultValue(column.getDefaultValue());
|
||||
boolean alterNotNull = Boolean.TRUE.equals(column.isNotnull());
|
||||
|
||||
if (column.getBefore().isEmpty() && alterNotNull && defaultValue == null) {
|
||||
handleStrictError(tableName, columnName);
|
||||
}
|
||||
|
||||
before = getScriptsForPlatform(column.getBefore());
|
||||
after = getScriptsForPlatform(column.getAfter());
|
||||
this.withHistory = withHistory;
|
||||
@@ -127,10 +125,8 @@ public class BaseTableDdl implements TableDdl {
|
||||
DdlMigrationHelp(AlterColumn alter) {
|
||||
this.tableName = alter.getTableName();
|
||||
this.columnName = alter.getColumnName();
|
||||
|
||||
String tmp = alter.getDefaultValue() != null ? alter.getDefaultValue() : alter.getCurrentDefaultValue();
|
||||
this.defaultValue = platformDdl.convertDefaultValue(tmp);
|
||||
|
||||
boolean alterNotNull = Boolean.TRUE.equals(alter.isNotnull());
|
||||
// here we add the platform's default update script
|
||||
withHistory = isTrue(alter.isWithHistory());
|
||||
@@ -201,7 +197,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
public String getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,7 +236,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
*/
|
||||
@Override
|
||||
public void generate(DdlWrite writer, CreateTable createTable) throws IOException {
|
||||
|
||||
reset();
|
||||
|
||||
String tableName = lowerTableName(createTable.getName());
|
||||
@@ -275,7 +269,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
if (platformDdl.isInlineForeignKeys()) {
|
||||
writeInlineForeignKeys(writer, createTable);
|
||||
}
|
||||
|
||||
apply.newLine().append(")");
|
||||
addTableStorageEngine(apply, createTable);
|
||||
addTableCommentInline(apply, createTable);
|
||||
@@ -285,9 +278,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
apply.endOfStatement();
|
||||
|
||||
addComments(apply, createTable);
|
||||
|
||||
writeUniqueOneToOneConstraints(writer, createTable);
|
||||
|
||||
if (isTrue(createTable.isWithHistory())) {
|
||||
// create history with rollback before the
|
||||
// associated drop table is written to rollback
|
||||
@@ -305,7 +296,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
// add blank line for a bit of whitespace between tables
|
||||
apply.end();
|
||||
writer.dropAll().end();
|
||||
|
||||
if (!platformDdl.isInlineForeignKeys()) {
|
||||
writeAddForeignKeys(writer, createTable);
|
||||
}
|
||||
@@ -324,7 +314,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
if (hasValue(tableComment)) {
|
||||
platformDdl.addTableComment(apply, createTable.getName(), tableComment);
|
||||
}
|
||||
|
||||
for (Column column : createTable.getColumn()) {
|
||||
if (!StringHelper.isNull(column.getComment())) {
|
||||
platformDdl.addColumnComment(apply, createTable.getName(), column.getName(), column.getComment());
|
||||
@@ -427,7 +416,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
writeForeignKey(write, createTable.getName(), column);
|
||||
}
|
||||
}
|
||||
|
||||
writeAddCompoundForeignKeys(write, createTable);
|
||||
}
|
||||
|
||||
@@ -448,7 +436,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
// no matching unique constraint so add the index
|
||||
fkeyBuffer.appendStatement(platformDdl.createIndex(new WriteCreateIndex(request.indexName(), tableName, request.cols(), false)));
|
||||
}
|
||||
|
||||
alterTableAddForeignKey(write.getOptions(), fkeyBuffer, request);
|
||||
fkeyBuffer.end();
|
||||
|
||||
@@ -456,7 +443,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
if (hasValue(request.indexName())) {
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(request.indexName(), tableName));
|
||||
}
|
||||
|
||||
write.dropAllForeignKeys().end();
|
||||
}
|
||||
|
||||
@@ -513,9 +499,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
* Write the unique constraints inline with the create table statement.
|
||||
*/
|
||||
protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
|
||||
boolean inlineUniqueWhenNullable = platformDdl.isInlineUniqueWhenNullable();
|
||||
|
||||
List<Column> columns = new WriteUniqueConstraint(createTable.getColumn()).uniqueKeys();
|
||||
for (Column column : columns) {
|
||||
if (Boolean.TRUE.equals(column.isNotnull()) || inlineUniqueWhenNullable) {
|
||||
@@ -532,12 +516,10 @@ public class BaseTableDdl implements TableDdl {
|
||||
* Write the unique constraint inline with the create table statement.
|
||||
*/
|
||||
protected void inlineUniqueConstraintSingle(DdlBuffer buffer, Column column) throws IOException {
|
||||
|
||||
String uqName = column.getUnique();
|
||||
if (uqName == null) {
|
||||
uqName = column.getUniqueOneToOne();
|
||||
}
|
||||
|
||||
buffer.append(",").newLine();
|
||||
buffer.append(" constraint ").append(uqName).append(" unique ");
|
||||
buffer.append("(");
|
||||
@@ -549,7 +531,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
* Write the primary key constraint inline with the create table statement.
|
||||
*/
|
||||
protected void writePrimaryKeyConstraint(DdlBuffer buffer, String pkName, String[] pkColumns) throws IOException {
|
||||
|
||||
buffer.append(",").newLine();
|
||||
buffer.append(" constraint ").append(pkName).append(" primary key");
|
||||
appendColumns(pkColumns, buffer);
|
||||
@@ -559,7 +540,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
* Return as an array of string column names.
|
||||
*/
|
||||
protected String[] toColumnNames(List<Column> columns) {
|
||||
|
||||
String[] cols = new String[columns.size()];
|
||||
for (int i = 0; i < cols.length; i++) {
|
||||
cols[i] = columns.get(i).getName();
|
||||
@@ -682,13 +662,11 @@ public class BaseTableDdl implements TableDdl {
|
||||
*/
|
||||
@Override
|
||||
public void generate(DdlWrite writer, AddColumn addColumn) throws IOException {
|
||||
|
||||
String tableName = addColumn.getTableName();
|
||||
List<Column> columns = addColumn.getColumn();
|
||||
for (Column column : columns) {
|
||||
alterTableAddColumn(writer.apply(), tableName, column, false, isTrue(addColumn.isWithHistory()));
|
||||
}
|
||||
|
||||
if (isTrue(addColumn.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
|
||||
// make same changes to the history table
|
||||
String historyTable = historyTable(tableName);
|
||||
@@ -697,14 +675,11 @@ public class BaseTableDdl implements TableDdl {
|
||||
alterTableAddColumn(writer.apply(), historyTable, column, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
for (Column column : columns) {
|
||||
if (hasValue(column.getReferences())) {
|
||||
writeForeignKey(writer, tableName, column);
|
||||
}
|
||||
}
|
||||
|
||||
// add a bit of whitespace
|
||||
writer.apply().end();
|
||||
}
|
||||
|
||||
@@ -713,9 +688,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
*/
|
||||
@Override
|
||||
public void generate(DdlWrite writer, DropTable dropTable) throws IOException {
|
||||
|
||||
dropTable(writer.apply(), dropTable.getName());
|
||||
|
||||
if (hasValue(dropTable.getSequenceCol())
|
||||
&& platformDdl.getPlatform().getDbIdentity().isSupportsSequence()) {
|
||||
String sequenceName = dropTable.getSequenceName();
|
||||
@@ -731,9 +704,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
*/
|
||||
@Override
|
||||
public void generate(DdlWrite writer, DropColumn dropColumn) throws IOException {
|
||||
|
||||
String tableName = dropColumn.getTableName();
|
||||
|
||||
alterTableDropColumn(writer.apply(), tableName, dropColumn.getColumnName());
|
||||
|
||||
if (isTrue(dropColumn.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
|
||||
@@ -741,7 +712,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
regenerateHistoryTriggers(tableName, HistoryTableUpdate.Change.DROP, dropColumn.getColumnName());
|
||||
alterTableDropColumn(writer.apply(), historyTable(tableName), dropColumn.getColumnName());
|
||||
}
|
||||
|
||||
writer.apply().end();
|
||||
}
|
||||
|
||||
@@ -826,7 +796,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
* Register the base table that we need to regenerate the history triggers on.
|
||||
*/
|
||||
protected void regenerateHistoryTriggers(String baseTableName, HistoryTableUpdate.Change change, String column) {
|
||||
|
||||
HistoryTableUpdate update = regenerateHistoryTriggers.computeIfAbsent(baseTableName, HistoryTableUpdate::new);
|
||||
update.add(change, column);
|
||||
}
|
||||
@@ -836,7 +805,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
* Will be called, if there is a type, dbdefault or notnull change.
|
||||
*/
|
||||
protected void alterColumnBaseAttributes(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnBaseAttributes(alter);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().appendStatement(ddl);
|
||||
@@ -857,27 +825,22 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
|
||||
protected void alterColumnDefaultValue(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply().appendStatement(platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue()));
|
||||
}
|
||||
|
||||
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter, String constraintName) throws IOException {
|
||||
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName));
|
||||
}
|
||||
|
||||
protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint()));
|
||||
}
|
||||
|
||||
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply().appendStatement(platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull()));
|
||||
}
|
||||
|
||||
protected void alterColumnType(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().appendStatement(ddl);
|
||||
@@ -891,33 +854,26 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
|
||||
protected void alterColumnAddForeignKey(DdlWrite writer, AlterColumn alterColumn) throws IOException {
|
||||
|
||||
alterTableAddForeignKey(writer.getOptions(), writer.apply(), new WriteForeignKey(alterColumn));
|
||||
}
|
||||
|
||||
protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()));
|
||||
}
|
||||
|
||||
|
||||
protected void alterColumnDropUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()));
|
||||
}
|
||||
|
||||
protected void alterColumnAddUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
addUniqueConstraint(writer, alter, alter.getUniqueOneToOne());
|
||||
}
|
||||
|
||||
protected void alterColumnAddUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
addUniqueConstraint(writer, alter, alter.getUnique());
|
||||
}
|
||||
|
||||
protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter, String uqName) throws IOException {
|
||||
|
||||
String[] cols = {alter.getColumnName()};
|
||||
boolean notNull = alter.isNotnull() != null ? alter.isNotnull() : Boolean.TRUE.equals(alter.isNotnull());
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols));
|
||||
@@ -965,11 +921,4 @@ public class BaseTableDdl implements TableDdl {
|
||||
return Boolean.TRUE.equals(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as an int value with 0 when it is null.
|
||||
*/
|
||||
protected int toInt(BigInteger value) {
|
||||
return (value == null) ? 0 : value.intValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
|
||||
/**
|
||||
* MariaDB platform DDL.
|
||||
*/
|
||||
public class MariaDbDdl extends MySqlDdl {
|
||||
|
||||
public MariaDbDdl(DatabasePlatform platform) {
|
||||
super(platform);
|
||||
this.historyDdl = new MariaDbHistoryDdl();
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
|
||||
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
|
||||
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* History DDL for MariaDB.
|
||||
*/
|
||||
public class MariaDbHistoryDdl implements PlatformHistoryDdl {
|
||||
|
||||
@Override
|
||||
public void configure(ServerConfig serverConfig, PlatformDdl platformDdl) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createWithHistory(DdlWrite writer, MTable table) throws IOException {
|
||||
String baseTable = table.getName();
|
||||
enableSystemVersioning(writer, baseTable);
|
||||
}
|
||||
|
||||
private void enableSystemVersioning(DdlWrite writer, String baseTable) throws IOException {
|
||||
DdlBuffer apply = writer.applyHistoryView();
|
||||
apply.append("alter table ").append(baseTable).append(" add system versioning").endOfStatement();
|
||||
|
||||
DdlBuffer drop = writer.dropAll();
|
||||
drop.append("alter table ").append(baseTable).append(" drop system versioning").endOfStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException {
|
||||
String baseTable = dropHistoryTable.getBaseTable();
|
||||
DdlBuffer apply = writer.applyHistoryView();
|
||||
apply.append("alter table ").append(baseTable).append(" drop system versioning").endOfStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException {
|
||||
String baseTable = addHistoryTable.getBaseTable();
|
||||
enableSystemVersioning(writer, baseTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTriggers(DdlWrite writer, HistoryTableUpdate baseTable) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -6,9 +6,9 @@ import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
/**
|
||||
* Oracle platform specific DDL.
|
||||
*/
|
||||
public class Oracle10Ddl extends PlatformDdl {
|
||||
public class OracleDdl extends PlatformDdl {
|
||||
|
||||
public Oracle10Ddl(DatabasePlatform platform) {
|
||||
public OracleDdl(DatabasePlatform platform) {
|
||||
super(platform);
|
||||
this.dropTableIfExists = "drop table ";
|
||||
this.dropSequenceIfExists = "drop sequence ";
|
||||
@@ -48,4 +48,12 @@ public class Oracle10Ddl extends PlatformDdl {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify and return the column definition for autoincrement or identity definition.
|
||||
*/
|
||||
@Override
|
||||
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
|
||||
return asIdentityStandardOptions(columnDefn, identity);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -195,7 +195,6 @@ public class PlatformDdl {
|
||||
* SQl2003 standard identity definition.
|
||||
*/
|
||||
protected String asIdentityStandardOptions(String columnDefn, DdlIdentity identity) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(columnDefn.length() + 60);
|
||||
sb.append(columnDefn).append(identity.optionGenerated());
|
||||
sb.append(identity.identityOptions(identityStartWith, identityIncrementBy, identityCache));
|
||||
|
||||
@@ -54,4 +54,9 @@ public class PostgresDdl extends PlatformDdl {
|
||||
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
|
||||
return asIdentityStandardOptions(columnDefn, identity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterColumnType(String tableName, String columnName, String type) {
|
||||
return super.alterColumnType(tableName, columnName, type) + " using " + columnName + "::" + convert(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.Objects;
|
||||
*/
|
||||
public class MColumn {
|
||||
|
||||
private final String name;
|
||||
private String name;
|
||||
private String type;
|
||||
private String checkConstraint;
|
||||
private String checkConstraintName;
|
||||
@@ -88,7 +88,6 @@ public class MColumn {
|
||||
* Return a copy of this column used for creating the associated draft table.
|
||||
*/
|
||||
public MColumn copyForDraft() {
|
||||
|
||||
MColumn copy = new MColumn(name, type);
|
||||
copy.draftOnly = draftOnly;
|
||||
copy.checkConstraint = checkConstraint;
|
||||
@@ -470,6 +469,14 @@ public class MColumn {
|
||||
this.dbMigrationInfos = dbMigrationInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the column.
|
||||
*/
|
||||
public MColumn rename(String newName) {
|
||||
this.name = newName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply changes based on the AlterColumn request.
|
||||
*/
|
||||
|
||||
@@ -55,7 +55,7 @@ public class MCompoundUniqueConstraint {
|
||||
public MCompoundUniqueConstraint(AddUniqueConstraint change) {
|
||||
this.name = change.getConstraintName();
|
||||
this.columns = split(change.getColumnNames());
|
||||
this.oneToOne = change.isOneToOne();
|
||||
this.oneToOne = Boolean.TRUE.equals(change.isOneToOne());
|
||||
this.platforms = change.getPlatforms();
|
||||
this.nullableColumns = splitWithNull(change.getNullableColumns());
|
||||
}
|
||||
@@ -63,7 +63,7 @@ public class MCompoundUniqueConstraint {
|
||||
public MCompoundUniqueConstraint(UniqueConstraint uq) {
|
||||
this.name = uq.getName();
|
||||
this.columns = split(uq.getColumnNames());
|
||||
this.oneToOne = uq.isOneToOne();
|
||||
this.oneToOne = Boolean.TRUE.equals(uq.isOneToOne());
|
||||
this.platforms = uq.getPlatforms();
|
||||
this.nullableColumns = splitWithNull(uq.getNullableColumns());
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import io.ebeaninternal.dbmigration.migration.DropColumn;
|
||||
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
|
||||
import io.ebeaninternal.dbmigration.migration.DropTable;
|
||||
import io.ebeaninternal.dbmigration.migration.ForeignKey;
|
||||
import io.ebeaninternal.dbmigration.migration.RenameColumn;
|
||||
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -415,6 +416,15 @@ public class MTable {
|
||||
}
|
||||
}
|
||||
|
||||
public void apply(RenameColumn renameColumn) {
|
||||
checkTableName(renameColumn.getTableName());
|
||||
MColumn column = columns.remove(renameColumn.getOldName());
|
||||
if (column == null) {
|
||||
throw new IllegalStateException("Column [" + renameColumn.getOldName() + "] does not exist for RenameColumn change on table [" + renameColumn.getTableName() + "]?");
|
||||
}
|
||||
addColumn(column.rename(renameColumn.getNewName()));
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
|
||||
import io.ebeaninternal.dbmigration.migration.DropIndex;
|
||||
import io.ebeaninternal.dbmigration.migration.DropTable;
|
||||
import io.ebeaninternal.dbmigration.migration.Migration;
|
||||
import io.ebeaninternal.dbmigration.migration.RenameColumn;
|
||||
import io.ebeaninternal.dbmigration.migration.Sql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -163,6 +164,8 @@ public class ModelContainer {
|
||||
applyChange((AddColumn) change);
|
||||
} else if (change instanceof DropColumn) {
|
||||
applyChange((DropColumn) change);
|
||||
} else if (change instanceof RenameColumn) {
|
||||
applyChange((RenameColumn) change);
|
||||
} else if (change instanceof CreateIndex) {
|
||||
applyChange((CreateIndex) change);
|
||||
} else if (change instanceof DropIndex) {
|
||||
@@ -314,6 +317,14 @@ public class ModelContainer {
|
||||
table.apply(dropColumn);
|
||||
}
|
||||
|
||||
protected void applyChange(RenameColumn renameColumn) {
|
||||
MTable table = tables.get(renameColumn.getTableName());
|
||||
if (table == null) {
|
||||
throw new IllegalStateException("Table [" + renameColumn.getTableName() + "] does not exist in model?");
|
||||
}
|
||||
table.apply(renameColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a table (typically from reading EbeanServer meta data).
|
||||
*/
|
||||
|
||||
@@ -58,7 +58,7 @@ public abstract class AbstractSqlQueryRequest {
|
||||
trans = server.currentServerTransaction();
|
||||
if (trans == null || !trans.isActive()) {
|
||||
// create a local readOnly transaction
|
||||
trans = server.createQueryTransaction(null);
|
||||
trans = server.createReadOnlyTransaction(null);
|
||||
createdTransaction = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.config.dbplatform.hana.HanaPlatform;
|
||||
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
|
||||
import io.ebean.config.dbplatform.mariadb.MariaDbPlatform;
|
||||
import io.ebean.config.dbplatform.mysql.MySql55Platform;
|
||||
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
|
||||
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
|
||||
@@ -76,11 +77,13 @@ public class DatabasePlatformFactory {
|
||||
* Lookup the platform by name.
|
||||
*/
|
||||
private DatabasePlatform byDatabaseName(String dbName) {
|
||||
|
||||
dbName = dbName.toLowerCase();
|
||||
if (dbName.equals("h2")) {
|
||||
return new H2Platform();
|
||||
}
|
||||
if (dbName.equals("mariadb")) {
|
||||
return new MariaDbPlatform();
|
||||
}
|
||||
if (dbName.equals("mysql")) {
|
||||
return new MySqlPlatform();
|
||||
}
|
||||
@@ -123,7 +126,6 @@ public class DatabasePlatformFactory {
|
||||
if (dbName.equals("hana")) {
|
||||
return new HanaPlatform();
|
||||
}
|
||||
|
||||
throw new RuntimeException("database platform " + dbName + " is not known?");
|
||||
}
|
||||
|
||||
@@ -160,14 +162,16 @@ public class DatabasePlatformFactory {
|
||||
return new OraclePlatform();
|
||||
} else if (dbProductName.contains("microsoft")) {
|
||||
throw new IllegalArgumentException("For SqlServer please explicitly choose either sqlserver16 or sqlserver17 as the platform via ServerConfig.setDatabasePlatformName. Refer to issue #1340 for more details");
|
||||
} else if (dbProductName.contains("mysql")) {
|
||||
return mysqlVersion(majorVersion, minorVersion);
|
||||
} else if (dbProductName.contains("h2")) {
|
||||
return new H2Platform();
|
||||
} else if (dbProductName.contains("hsql database engine")) {
|
||||
return new HsqldbPlatform();
|
||||
} else if (dbProductName.contains("postgres")) {
|
||||
return readPostgres(connection, majorVersion);
|
||||
} else if (dbProductName.contains("mariadb")) {
|
||||
return new MariaDbPlatform();
|
||||
} else if (dbProductName.contains("mysql")) {
|
||||
return mysqlVersion(majorVersion, minorVersion);
|
||||
} else if (dbProductName.contains("nuo")) {
|
||||
return new NuoDbPlatform();
|
||||
} else if (dbProductName.contains("sqlite")) {
|
||||
|
||||
@@ -33,7 +33,6 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
|
||||
@Override
|
||||
public void execute(Runnable r) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
|
||||
if (map == null) {
|
||||
pool.execute(r);
|
||||
} else {
|
||||
@@ -50,10 +49,14 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
|
||||
|
||||
@Override
|
||||
public void executePeriodically(Runnable r, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
executePeriodically(r, delay, delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executePeriodically(Runnable r, long initialDelay, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
schedulePool.scheduleWithFixedDelay(r, delay, delay, unit);
|
||||
schedulePool.scheduleWithFixedDelay(r, initialDelay, delay, unit);
|
||||
} else {
|
||||
schedulePool.scheduleWithFixedDelay(() -> {
|
||||
MDC.setContextMap(map);
|
||||
@@ -62,14 +65,13 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}, delay, delay, unit);
|
||||
}, initialDelay, delay, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
@@ -87,7 +89,6 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
|
||||
@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 {
|
||||
|
||||
@@ -170,7 +170,7 @@ class DefaultBeanLoader {
|
||||
private List<?> executeQuery(LoadRequest loadRequest, SpiQuery<?> query) {
|
||||
if (onIterateUseExtraTxn && loadRequest.isParentFindIterate()) {
|
||||
// MySql - we need a different transaction to execute the secondary query
|
||||
SpiTransaction extraTxn = server.createQueryTransaction(query.getTenantId());
|
||||
SpiTransaction extraTxn = server.createReadOnlyTransaction(query.getTenantId());
|
||||
try {
|
||||
return server.findList(query, extraTxn);
|
||||
} finally {
|
||||
|
||||
@@ -8,10 +8,6 @@ import io.ebean.config.TenantMode;
|
||||
import io.ebean.config.UnderscoreNamingConvention;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.datasource.DataSourceAlertFactory;
|
||||
import io.ebean.datasource.DataSourceConfig;
|
||||
import io.ebean.datasource.DataSourceFactory;
|
||||
import io.ebean.datasource.DataSourcePoolListener;
|
||||
import io.ebean.service.SpiContainer;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
@@ -24,7 +20,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
@@ -39,13 +34,8 @@ public class DefaultContainer implements SpiContainer {
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
|
||||
private final JndiDataSourceLookup jndiDataSourceFactory;
|
||||
|
||||
public DefaultContainer(ContainerConfig containerConfig) {
|
||||
|
||||
this.clusterManager = new ClusterManager(containerConfig);
|
||||
this.jndiDataSourceFactory = new JndiDataSourceLookup();
|
||||
|
||||
// register so that we can shutdown any Ebean wide
|
||||
// resources such as clustering
|
||||
ShutdownManager.registerContainer(this);
|
||||
@@ -230,75 +220,10 @@ public class DefaultContainer implements SpiContainer {
|
||||
* Set the DataSource if it has not already been set.
|
||||
*/
|
||||
private void setDataSource(ServerConfig config) {
|
||||
if (config.getDataSource() == null) {
|
||||
config.setDataSource(getDataSourceFromConfig(config, false));
|
||||
}
|
||||
if (config.getReadOnlyDataSource() == null && config.isAutoReadOnlyDataSource()) {
|
||||
config.setReadOnlyDataSource(getDataSourceFromConfig(config, true));
|
||||
}
|
||||
}
|
||||
|
||||
private DataSource getDataSourceFromConfig(ServerConfig config, boolean readOnly) {
|
||||
|
||||
if (isOfflineMode(config)) {
|
||||
logger.debug("... DbOffline using platform [{}]", DbOffline.getPlatform());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!readOnly && config.getDataSourceJndiName() != null) {
|
||||
DataSource ds = jndiDataSourceFactory.lookup(config.getDataSourceJndiName());
|
||||
if (ds == null) {
|
||||
throw new PersistenceException("JNDI lookup for DataSource " + config.getDataSourceJndiName() + " returned null.");
|
||||
} else {
|
||||
return ds;
|
||||
}
|
||||
}
|
||||
|
||||
DataSourceConfig dsConfig = (readOnly) ? config.getReadOnlyDataSourceConfig() : config.getDataSourceConfig();
|
||||
if (dsConfig == null) {
|
||||
throw new PersistenceException("No DataSourceConfig defined for " + config.getName());
|
||||
}
|
||||
|
||||
if (dsConfig.isOffline()) {
|
||||
if (config.getDatabasePlatformName() == null) {
|
||||
throw new PersistenceException("You MUST specify a DatabasePlatformName on ServerConfig when offline");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
DataSourceFactory factory = DataSourceFactory.get();
|
||||
if (factory == null) {
|
||||
throw new IllegalStateException("No DataSourceFactory service implementation found in class path."
|
||||
+ " Probably missing dependency to avaje-datasource?");
|
||||
}
|
||||
|
||||
DataSourceAlertFactory alertFactory = config.service(DataSourceAlertFactory.class);
|
||||
if (alertFactory != null) {
|
||||
dsConfig.setAlert(alertFactory.createAlert());
|
||||
}
|
||||
|
||||
attachListener(config, dsConfig);
|
||||
|
||||
if (readOnly) {
|
||||
// setup to use AutoCommit such that we skip explicit commit
|
||||
dsConfig.setAutoCommit(true);
|
||||
//dsConfig.setReadOnly(true);
|
||||
dsConfig.setDefaults(config.getDataSourceConfig());
|
||||
dsConfig.setIsolationLevel(config.getDataSourceConfig().getIsolationLevel());
|
||||
}
|
||||
String poolName = config.getName() + (readOnly ? "-ro" : "");
|
||||
return factory.createPool(poolName, dsConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and attach a DataSourcePoolListener if it has been specified via properties and there is not one already attached.
|
||||
*/
|
||||
private void attachListener(ServerConfig config, DataSourceConfig dsConfig) {
|
||||
if (dsConfig.getListener() == null) {
|
||||
String poolListener = dsConfig.getPoolListener();
|
||||
if (poolListener != null) {
|
||||
dsConfig.setListener((DataSourcePoolListener) config.getClassLoadConfig().newInstance(poolListener));
|
||||
}
|
||||
} else {
|
||||
InitDataSource.init(config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +256,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
}
|
||||
|
||||
try (Connection connection = serverConfig.getDataSource().getConnection()) {
|
||||
if (!serverConfig.isAutoCommitMode() && connection.getAutoCommit()) {
|
||||
if (connection.getAutoCommit()) {
|
||||
logger.warn("DataSource [{}] has autoCommit defaulting to true!", serverConfig.getName());
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -311,9 +311,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
private void configureServerPlugins() {
|
||||
|
||||
autoTuneService.startup();
|
||||
|
||||
for (Plugin plugin : serverPlugins) {
|
||||
plugin.configure(this);
|
||||
}
|
||||
@@ -323,7 +321,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
* Execute all the plugins with an online flag indicating the DB is up or not.
|
||||
*/
|
||||
public void executePlugins(boolean online) {
|
||||
|
||||
if (!serverConfig.isDocStoreOnly()) {
|
||||
ddlGenerator.execute(online);
|
||||
}
|
||||
@@ -471,14 +468,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
* Shutdown the services like threads and DataSource.
|
||||
*/
|
||||
private void shutdownInternal(boolean shutdownDataSource, boolean deregisterDriver) {
|
||||
|
||||
logger.debug("Shutting down instance:{}", serverName);
|
||||
if (shutdown) {
|
||||
// already shutdown
|
||||
return;
|
||||
}
|
||||
shutdownPlugins();
|
||||
|
||||
autoTuneService.shutdown();
|
||||
// shutdown background threads
|
||||
backgroundExecutor.shutdown();
|
||||
@@ -492,11 +487,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
private void shutdownPlugins() {
|
||||
|
||||
if (serverConfig.isDumpMetricsOnShutdown()) {
|
||||
new DumpMetrics(this, serverConfig.getDumpMetricsOptions()).dump();
|
||||
}
|
||||
|
||||
for (Plugin plugin : serverPlugins) {
|
||||
try {
|
||||
plugin.shutdown();
|
||||
@@ -601,7 +594,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (a == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BeanDescriptor<?> desc = getBeanDescriptor(a.getClass());
|
||||
return DiffHelp.diff(a, b, desc);
|
||||
}
|
||||
@@ -626,10 +618,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void externalModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
|
||||
|
||||
TransactionEventTable evt = new TransactionEventTable();
|
||||
evt.add(tableName, inserts, updates, deletes);
|
||||
|
||||
externalModification(evt);
|
||||
}
|
||||
|
||||
@@ -697,14 +687,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public <T> T getReference(Class<T> type, Object id) {
|
||||
|
||||
if (id == null) {
|
||||
throw new NullPointerException("The id is null");
|
||||
}
|
||||
|
||||
BeanDescriptor desc = getBeanDescriptor(type);
|
||||
id = desc.convertId(id);
|
||||
|
||||
PersistenceContext pc = null;
|
||||
SpiTransaction t = transactionManager.getActive();
|
||||
if (t != null) {
|
||||
@@ -719,12 +706,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (inheritInfo == null || inheritInfo.isConcrete()) {
|
||||
return (T) desc.contextRef(pc, null, false, id);
|
||||
}
|
||||
|
||||
BeanProperty idProp = desc.getIdProperty();
|
||||
if (idProp == null) {
|
||||
throw new PersistenceException("No ID properties for this type? " + desc);
|
||||
}
|
||||
|
||||
// we actually need to do a query because we don't know the type without the discriminator
|
||||
// value, just select the id property and discriminator column (auto added)
|
||||
return find(type).select(idProp.getName()).setId(id).findOne();
|
||||
@@ -769,13 +754,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
ScopedTransaction scopeTrans = transactionManager.beginScopedTransaction(scope);
|
||||
try {
|
||||
return c.call();
|
||||
|
||||
} catch (Error e) {
|
||||
throw scopeTrans.caughtError(e);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(scopeTrans.caughtThrowable(e));
|
||||
|
||||
} finally {
|
||||
scopeTrans.complete();
|
||||
}
|
||||
@@ -791,13 +773,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
ScopedTransaction t = transactionManager.beginScopedTransaction(scope);
|
||||
try {
|
||||
r.run();
|
||||
|
||||
} catch (Error e) {
|
||||
throw t.caughtError(e);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(t.caughtThrowable(e));
|
||||
|
||||
} finally {
|
||||
t.complete();
|
||||
}
|
||||
@@ -879,7 +858,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> void sort(List<T> list, String sortByClause) {
|
||||
|
||||
if (list == null) {
|
||||
throw new NullPointerException("list is null");
|
||||
}
|
||||
@@ -901,7 +879,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> Set<String> validateQuery(Query<T> query) {
|
||||
|
||||
BeanDescriptor<T> beanDescriptor = getBeanDescriptor(query.getBeanType());
|
||||
if (beanDescriptor == null) {
|
||||
throw new PersistenceException("BeanDescriptor not found, is [" + query.getBeanType() + "] an entity bean?");
|
||||
@@ -1010,13 +987,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
String m = beanType.getName() + " is NOT an Entity Bean registered with this server?";
|
||||
throw new PersistenceException(m);
|
||||
}
|
||||
|
||||
return new DefaultOrmUpdate<>(beanType, this, desc.getBaseTable(), ormUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DtoQuery<T> findDto(Class<T> dtoType, String sql) {
|
||||
|
||||
DtoBeanDescriptor<T> descriptor = dtoBeanManager.getDescriptor(dtoType);
|
||||
return new DefaultDtoQuery<>(this, descriptor, sql.trim());
|
||||
}
|
||||
@@ -1033,14 +1008,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> DtoQuery<T> findDto(Class<T> dtoType, SpiQuery<?> ormQuery) {
|
||||
|
||||
DtoBeanDescriptor<T> descriptor = dtoBeanManager.getDescriptor(dtoType);
|
||||
return new DefaultDtoQuery<>(this, descriptor, ormQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiResultSet findResultSet(SpiQuery<?> ormQuery, SpiTransaction transaction) {
|
||||
|
||||
SpiOrmQueryRequest<?> request = createQueryRequest(ormQuery.getType(), ormQuery, transaction);
|
||||
request.initTransIfRequired();
|
||||
return request.findResultSet();
|
||||
@@ -1073,7 +1046,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> T find(Class<T> beanType, Object uid) {
|
||||
|
||||
return find(beanType, uid, null);
|
||||
}
|
||||
|
||||
@@ -1082,33 +1054,27 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public <T> T find(Class<T> beanType, Object id, Transaction t) {
|
||||
|
||||
if (id == null) {
|
||||
throw new NullPointerException("The id is null");
|
||||
}
|
||||
|
||||
Query<T> query = createQuery(beanType).setId(id);
|
||||
return findId(query, t);
|
||||
}
|
||||
|
||||
<T> SpiOrmQueryRequest<T> createQueryRequest(Type type, Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = buildQueryRequest(type, query, t);
|
||||
request.prepareQuery();
|
||||
return request;
|
||||
}
|
||||
|
||||
<T> SpiOrmQueryRequest<T> buildQueryRequest(Type type, Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setType(type);
|
||||
spiQuery.checkNamedParameters();
|
||||
|
||||
return buildQueryRequest(spiQuery, t);
|
||||
}
|
||||
|
||||
private <T> SpiOrmQueryRequest<T> buildQueryRequest(SpiQuery<T> query, Transaction t) {
|
||||
|
||||
if (t == null) {
|
||||
t = currentServerTransaction();
|
||||
}
|
||||
@@ -1118,9 +1084,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
// to define the 'default' select clause
|
||||
query.setDefaultSelectClause();
|
||||
}
|
||||
|
||||
query.selectAllForLazyLoadProperty();
|
||||
|
||||
ProfileLocation profileLocation = query.getProfileLocation();
|
||||
if (profileLocation != null) {
|
||||
profileLocation.obtain();
|
||||
@@ -1129,7 +1093,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (query.getParentNode() == null) {
|
||||
query.setOrigin(createCallOrigin());
|
||||
}
|
||||
|
||||
return new OrmQueryRequest<>(this, queryEngine, query, (SpiTransaction) t);
|
||||
}
|
||||
|
||||
@@ -1138,15 +1101,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T findIdCheckPersistenceContextAndCache(Transaction transaction, SpiQuery<T> query, Object id) {
|
||||
|
||||
SpiTransaction t = (SpiTransaction) transaction;
|
||||
if (t == null) {
|
||||
t = currentServerTransaction();
|
||||
}
|
||||
|
||||
BeanDescriptor<T> desc = query.getBeanDescriptor();
|
||||
id = desc.convertId(id);
|
||||
|
||||
PersistenceContext pc = null;
|
||||
if (t != null && useTransactionPersistenceContext(query)) {
|
||||
// first look in the transaction scoped persistence context
|
||||
@@ -1162,11 +1122,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!query.isBeanCacheGet() || (t != null && t.isSkipCache())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Hit the L2 bean cache
|
||||
return desc.cacheBeanGet(id, query.isReadOnly(), pc);
|
||||
}
|
||||
@@ -1189,7 +1147,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T findId(Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setType(Type.BEAN);
|
||||
if (SpiQuery.Mode.NORMAL == spiQuery.getMode() && !spiQuery.isForceHitDatabase()) {
|
||||
@@ -1200,7 +1157,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
SpiOrmQueryRequest<T> request = buildQueryRequest(spiQuery, t);
|
||||
request.prepareQuery();
|
||||
if (request.isUseDocStore()) {
|
||||
@@ -1209,7 +1165,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return (T) request.findId();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1222,17 +1177,14 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> T findOne(Query<T> query, Transaction transaction) {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
if (spiQuery.isFindById()) {
|
||||
// actually a find by Id query
|
||||
return findId(query, transaction);
|
||||
}
|
||||
|
||||
if (transaction == null) {
|
||||
transaction = currentServerTransaction();
|
||||
}
|
||||
|
||||
// a query that is expected to return either 0 or 1 beans
|
||||
List<T> list = findList(query, transaction, true);
|
||||
return extractUnique(list);
|
||||
@@ -1241,10 +1193,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
private <T> T extractUnique(List<T> list) {
|
||||
if (list.isEmpty()) {
|
||||
return null;
|
||||
|
||||
} else if (list.size() > 1) {
|
||||
throw new NonUniqueResultException("Unique expecting 0 or 1 results but got [" + list.size() + "]");
|
||||
|
||||
} else {
|
||||
return list.get(0);
|
||||
}
|
||||
@@ -1253,7 +1203,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public <T> Set<T> findSet(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest request = createQueryRequest(Type.SET, query, t);
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
@@ -1270,7 +1219,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public <K, T> Map<K, T> findMap(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest request = createQueryRequest(Type.MAP, query, t);
|
||||
request.resetBeanCacheAutoMode(false);
|
||||
if ((t == null || !t.isSkipCache()) && request.getFromBeanCache()) {
|
||||
@@ -1292,7 +1240,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ATTRIBUTE, query, t);
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
@@ -1301,7 +1248,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return (List<A>) request.findSingleAttributeList();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1309,7 +1255,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> int findCount(Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> spiQuery = ((SpiQuery<T>) query);
|
||||
if (!spiQuery.isDistinct()) {
|
||||
spiQuery = spiQuery.copy();
|
||||
@@ -1319,7 +1264,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> int findCountWithCopy(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.COUNT, query, t);
|
||||
Integer result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
@@ -1328,7 +1272,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findCount();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1342,7 +1285,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> boolean exists(Query<?> ormQuery, Transaction transaction) {
|
||||
|
||||
Query<?> ormQueryCopy = ormQuery.copy();
|
||||
ormQueryCopy.setMaxRows(1);
|
||||
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQueryCopy, transaction);
|
||||
@@ -1350,7 +1292,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
request.initTransIfRequired();
|
||||
List<Object> ids = request.findIds();
|
||||
return !ids.isEmpty();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1358,14 +1299,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <A, T> List<A> findIds(Query<T> query, Transaction t) {
|
||||
|
||||
return findIdsWithCopy(((SpiQuery<T>) query).copy(), t);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <A, T> List<A> findIdsWithCopy(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, query, t);
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
@@ -1378,7 +1317,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findIds();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1386,7 +1324,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> int delete(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.DELETE, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
@@ -1410,7 +1347,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> int update(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.UPDATE, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
@@ -1423,82 +1359,60 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> FutureRowCount<T> findFutureCount(Query<T> q, Transaction t) {
|
||||
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) q).copy();
|
||||
copy.setFutureFetch(true);
|
||||
|
||||
Transaction newTxn = createTransaction();
|
||||
|
||||
CallableQueryCount<T> call = new CallableQueryCount<>(this, copy, newTxn);
|
||||
|
||||
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<>(call);
|
||||
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<>(new CallableQueryCount<T>(this, copy, newTxn));
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) query).copy();
|
||||
copy.setFutureFetch(true);
|
||||
|
||||
Transaction newTxn = createTransaction();
|
||||
|
||||
CallableQueryIds<T> call = new CallableQueryIds<>(this, copy, newTxn);
|
||||
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(call);
|
||||
|
||||
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<T>(this, copy, newTxn));
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> FutureList<T> findFutureList(Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setFutureFetch(true);
|
||||
|
||||
// FutureList query always run in it's own persistence content
|
||||
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
|
||||
|
||||
if (!spiQuery.isDisableReadAudit()) {
|
||||
BeanDescriptor<T> desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType());
|
||||
desc.readAuditFutureList(spiQuery);
|
||||
}
|
||||
|
||||
// Create a new transaction solely to execute the findList() at some future time
|
||||
Transaction newTxn = createTransaction();
|
||||
CallableQueryList<T> call = new CallableQueryList<>(this, spiQuery, newTxn);
|
||||
QueryFutureList<T> queryFuture = new QueryFutureList<>(call);
|
||||
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<T>(this, spiQuery, newTxn));
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction) {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
int maxRows = spiQuery.getMaxRows();
|
||||
if (maxRows == 0) {
|
||||
throw new PersistenceException("maxRows must be specified for findPagedList() query");
|
||||
}
|
||||
|
||||
if (spiQuery.isUseDocStore()) {
|
||||
return docStore().findPagedList(createQueryRequest(Type.LIST, query, transaction));
|
||||
}
|
||||
|
||||
return new LimitOffsetPagedList<>(this, spiQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findIterate();
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.endTransIfRequired();
|
||||
throw ex;
|
||||
@@ -1507,19 +1421,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> Stream<T> findLargeStream(Query<T> query, Transaction transaction) {
|
||||
return findStreamWithSingleContext(false, query, transaction);
|
||||
return findStream(query, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Stream<T> findStream(Query<T> query, Transaction transaction) {
|
||||
return findStreamWithSingleContext(true, query, transaction);
|
||||
}
|
||||
|
||||
private <T> Stream<T> findStreamWithSingleContext(boolean singleContext, Query<T> query, Transaction transaction) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, transaction);
|
||||
if (singleContext) {
|
||||
request.setIterateSingleContext();
|
||||
}
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return toStream(request.findIterate());
|
||||
@@ -1549,14 +1456,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> void findEach(Query<T> query, Consumer<T> consumer, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
|
||||
|
||||
if (request.isUseDocStore()) {
|
||||
docStore().findEach(request, consumer);
|
||||
return;
|
||||
}
|
||||
|
||||
request.initTransIfRequired();
|
||||
request.findEach(consumer);
|
||||
// no try finally - findEach guarantee's cleanup of the transaction if required
|
||||
@@ -1564,13 +1468,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
|
||||
if (request.isUseDocStore()) {
|
||||
docStore().findEachWhile(request, consumer);
|
||||
return;
|
||||
}
|
||||
|
||||
request.initTransIfRequired();
|
||||
request.findEachWhile(consumer);
|
||||
// no try finally - findEachWhile guarantee's cleanup of the transaction if required
|
||||
@@ -1578,12 +1480,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findVersions();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1596,14 +1496,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> List<T> findList(Query<T> query, Transaction t, boolean findOne) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = buildQueryRequest(Type.LIST, query, t);
|
||||
request.resetBeanCacheAutoMode(findOne);
|
||||
if ((t == null || !t.isSkipCache()) && request.getFromBeanCache()) {
|
||||
// hit bean cache and got all results from cache
|
||||
return request.getBeanCacheHits();
|
||||
}
|
||||
|
||||
request.prepareQuery();
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
@@ -1612,11 +1510,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (request.isUseDocStore()) {
|
||||
return docStore().findList(request);
|
||||
}
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findList();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1624,7 +1520,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public SqlRow findOne(SqlQuery query, Transaction t) {
|
||||
|
||||
// no findId() method for SqlQuery...
|
||||
// a query that is expected to return either 0 or 1 rows
|
||||
List<SqlRow> list = findList(query, t);
|
||||
@@ -1633,7 +1528,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction) {
|
||||
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
@@ -1645,7 +1539,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction) {
|
||||
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
@@ -1657,12 +1550,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public List<SqlRow> findList(SqlQuery query, Transaction t) {
|
||||
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findList();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1709,7 +1600,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
request.findEach(consumer);
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1721,7 +1611,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
request.findEachWhile(consumer);
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1733,7 +1622,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findList();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1745,7 +1633,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return extractUnique(request.findList());
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1796,7 +1683,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void updateAll(Collection<?> beans, Transaction transaction) {
|
||||
|
||||
if (beans == null || beans.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1837,7 +1723,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void insertAll(Collection<?> beans, Transaction transaction) {
|
||||
|
||||
if (beans == null || beans.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1851,7 +1736,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> List<T> publish(Query<T> query, Transaction transaction) {
|
||||
|
||||
return executeInTrans((txn) -> persister.publish(query, txn), transaction);
|
||||
}
|
||||
|
||||
@@ -1867,7 +1751,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> T publish(Class<T> beanType, Object id, Transaction transaction) {
|
||||
|
||||
Query<T> query = find(beanType).setId(id);
|
||||
List<T> liveBeans = publish(query, transaction);
|
||||
return (liveBeans.size() == 1) ? liveBeans.get(0) : null;
|
||||
@@ -1875,13 +1758,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> List<T> draftRestore(Query<T> query, Transaction transaction) {
|
||||
|
||||
return executeInTrans((txn) -> persister.draftRestore(query, txn), transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T draftRestore(Class<T> beanType, Object id, Transaction transaction) {
|
||||
|
||||
Query<T> query = find(beanType).setId(id);
|
||||
List<T> beans = draftRestore(query, transaction);
|
||||
return (beans.size() == 1) ? beans.get(0) : null;
|
||||
@@ -1926,7 +1807,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
* Save all beans in the iterator with an explicit transaction.
|
||||
*/
|
||||
private int saveAllInternal(Collection<?> beans, Transaction transaction) {
|
||||
|
||||
if (beans == null || beans.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1937,7 +1817,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
persister.save(checkEntityBean(bean), txn);
|
||||
saveCount++;
|
||||
}
|
||||
|
||||
txn.flushBatchOnCollection();
|
||||
return saveCount;
|
||||
}, transaction);
|
||||
@@ -1964,7 +1843,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
private int delete(Class<?> beanType, Object id, Transaction transaction, boolean permanent) {
|
||||
|
||||
return executeInTrans((txn) -> persister.delete(beanType, id, txn, permanent), transaction);
|
||||
}
|
||||
|
||||
@@ -2050,19 +1928,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
* Delete all the beans in the iterator with an explicit transaction.
|
||||
*/
|
||||
private int deleteAllInternal(Collection<?> beans, Transaction transaction, boolean permanent) {
|
||||
|
||||
if (beans == null || beans.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
return executeInTrans((txn) -> {
|
||||
|
||||
txn.checkBatchEscalationOnCollection();
|
||||
int deleteCount = 0;
|
||||
for (Object bean : beans) {
|
||||
persister.delete(checkEntityBean(bean), txn, permanent);
|
||||
deleteCount++;
|
||||
}
|
||||
|
||||
txn.flushBatchOnCollection();
|
||||
return deleteCount;
|
||||
}, transaction);
|
||||
@@ -2163,7 +2038,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public boolean isSupportedType(java.lang.reflect.Type genericType) {
|
||||
|
||||
TypeInfo typeInfo = ParamTypeHelper.getTypeInfo(genericType);
|
||||
return typeInfo != null && getBeanDescriptor(typeInfo.getBeanType()) != null;
|
||||
}
|
||||
@@ -2286,7 +2160,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
P result = fun.apply(wrap.transaction());
|
||||
wrap.commitIfCreated();
|
||||
return result;
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
wrap.endIfCreated();
|
||||
throw e;
|
||||
@@ -2299,7 +2172,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
* Create a transaction if one is not currently active.
|
||||
*/
|
||||
ObtainedTransaction initTransIfRequired(Transaction t) {
|
||||
|
||||
if (t != null) {
|
||||
return new ObtainedTransaction((SpiTransaction) t);
|
||||
}
|
||||
@@ -2322,8 +2194,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
return transactionManager.createQueryTransaction(tenantId);
|
||||
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
|
||||
return transactionManager.createReadOnlyTransaction(tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2368,16 +2240,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public Set<Property> checkUniqueness(Object bean, Transaction transaction) {
|
||||
|
||||
EntityBean entityBean = checkEntityBean(bean);
|
||||
BeanDescriptor<?> beanDesc = getBeanDescriptor(entityBean.getClass());
|
||||
|
||||
BeanProperty idProperty = beanDesc.getIdProperty();
|
||||
// if the ID of the Property is null we are unable to check uniqueness
|
||||
if (idProperty == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Object id = idProperty.getVal(entityBean);
|
||||
if (entityBean._ebean_getIntercept().isNew() && id != null) {
|
||||
// Primary Key is changeable only on new models - so skip check if we are not
|
||||
@@ -2390,7 +2259,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (BeanProperty[] props : beanDesc.getUniqueProps()) {
|
||||
Set<Property> ret = checkUniqueness(entityBean, beanDesc, props, transaction);
|
||||
if (ret != null) {
|
||||
@@ -2407,12 +2275,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
BeanProperty idProperty = beanDesc.getIdProperty();
|
||||
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
|
||||
ExpressionList<?> exprList = query.where();
|
||||
|
||||
if (!entityBean._ebean_getIntercept().isNew()) {
|
||||
// if model is not new, exclude ourself.
|
||||
exprList.ne(idProperty.getName(), idProperty.getVal(entityBean));
|
||||
}
|
||||
|
||||
for (Property prop : props) {
|
||||
Object value = prop.getVal(entityBean);
|
||||
if (value == null) {
|
||||
@@ -2420,7 +2286,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
exprList.eq(prop.getName(), value);
|
||||
}
|
||||
|
||||
if (findCount(query, transaction) > 0) {
|
||||
Set<Property> ret = new LinkedHashSet<>();
|
||||
Collections.addAll(ret, props);
|
||||
|
||||
@@ -47,7 +47,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
SpiQuery<?> ormQuery = query.getOrmQuery();
|
||||
if (ormQuery != null) {
|
||||
ormQuery.setType(type);
|
||||
ormQuery.setManualId(true);
|
||||
ormQuery.setManualId();
|
||||
|
||||
// execute the underlying ORM query returning the ResultSet
|
||||
SpiResultSet result = server.findResultSet(ormQuery, trans);
|
||||
@@ -102,7 +102,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
@SuppressWarnings("unchecked")
|
||||
public T readNextBean() throws SQLException {
|
||||
dataReader.resetColumnPosition();
|
||||
return (T)plan.readRow(dataReader);
|
||||
return (T) plan.readRow(dataReader);
|
||||
}
|
||||
|
||||
private DtoMappingRequest mappingRequest() throws SQLException {
|
||||
@@ -110,19 +110,29 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
private DtoColumn[] readMeta() throws SQLException {
|
||||
|
||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||
int cols = metaData.getColumnCount();
|
||||
DtoColumn[] meta = new DtoColumn[cols];
|
||||
for (int i = 0; i < cols; i++) {
|
||||
int pos = i+1;
|
||||
int pos = i + 1;
|
||||
String columnLabel = metaData.getColumnLabel(pos);
|
||||
if (columnLabel == null) {
|
||||
columnLabel = metaData.getColumnName(pos);
|
||||
}
|
||||
meta[i] = new DtoColumn(columnLabel);
|
||||
meta[i] = new DtoColumn(parseColumn(columnLabel));
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
static String parseColumn(String columnLabel) {
|
||||
if (columnLabel.startsWith("_e_") || columnLabel.startsWith("_E_")) {
|
||||
// encrypted column alias in the form _e_<tableAlias>_<column>
|
||||
final int pos = columnLabel.indexOf("_", 3);
|
||||
if (pos > -1) {
|
||||
return columnLabel.substring(pos + 1);
|
||||
}
|
||||
}
|
||||
return columnLabel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.datasource.DataSourceAlertFactory;
|
||||
import io.ebean.datasource.DataSourceConfig;
|
||||
import io.ebean.datasource.DataSourceFactory;
|
||||
import io.ebean.datasource.DataSourcePoolListener;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* Initialise the main DataSource and read-only DataSource.
|
||||
*/
|
||||
class InitDataSource {
|
||||
|
||||
private final JndiDataSourceLookup jndiDataSourceFactory = new JndiDataSourceLookup();
|
||||
|
||||
private final ServerConfig config;
|
||||
|
||||
/**
|
||||
* Create and set the main DataSource and read-only DataSource.
|
||||
*/
|
||||
static void init(ServerConfig config) {
|
||||
new InitDataSource(config).initialise();
|
||||
}
|
||||
|
||||
InitDataSource(ServerConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
private void initialise() {
|
||||
if (config.getDataSource() == null) {
|
||||
config.setDataSource(initDataSource());
|
||||
}
|
||||
if (config.getReadOnlyDataSource() == null) {
|
||||
config.setReadOnlyDataSource(initReadOnlyDataSource());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the "main" read write DataSource from configuration.
|
||||
*/
|
||||
private DataSource initDataSource() {
|
||||
final String jndiName = config.getDataSourceJndiName();
|
||||
if (jndiName != null) {
|
||||
return jndiDataSource(jndiName);
|
||||
}
|
||||
return createFromConfig(config.getDataSourceConfig(), false);
|
||||
}
|
||||
|
||||
private DataSource jndiDataSource(String jndiName) {
|
||||
DataSource ds = jndiDataSourceFactory.lookup(jndiName);
|
||||
if (ds == null) {
|
||||
throw new PersistenceException("JNDI lookup for DataSource " + jndiName + " returned null.");
|
||||
}
|
||||
return ds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the "read only" DataSource from configuration.
|
||||
*/
|
||||
private DataSource initReadOnlyDataSource() {
|
||||
DataSourceConfig roConfig = readOnlyConfig();
|
||||
return roConfig == null ? null : createFromConfig(roConfig, true);
|
||||
}
|
||||
|
||||
DataSourceConfig readOnlyConfig() {
|
||||
DataSourceConfig roConfig = config.getReadOnlyDataSourceConfig();
|
||||
if (roConfig == null) {
|
||||
// it has explicitly been set to null, not expected but ok
|
||||
return null;
|
||||
}
|
||||
if (urlSet(roConfig.getUrl())) {
|
||||
return roConfig;
|
||||
}
|
||||
// convenient alternate place to set the read-only url
|
||||
final String readOnlyUrl = config.getDataSourceConfig().getReadOnlyUrl();
|
||||
if (urlSet(readOnlyUrl)) {
|
||||
roConfig.setUrl(readOnlyUrl);
|
||||
return roConfig;
|
||||
}
|
||||
if (config.isAutoReadOnlyDataSource()) {
|
||||
roConfig.setUrl(null); // blank out in case it is "none"
|
||||
return roConfig;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean urlSet(String url) {
|
||||
return url != null && !"none".equalsIgnoreCase(url) && !url.trim().isEmpty();
|
||||
}
|
||||
|
||||
private DataSource createFromConfig(DataSourceConfig dsConfig, boolean readOnly) {
|
||||
if (dsConfig == null) {
|
||||
throw new PersistenceException("No DataSourceConfig defined for " + config.getName());
|
||||
}
|
||||
if (dsConfig.isOffline()) {
|
||||
if (config.getDatabasePlatformName() == null) {
|
||||
throw new PersistenceException("You MUST specify a DatabasePlatformName on ServerConfig when offline");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
attachAlert(dsConfig);
|
||||
attachListener(dsConfig);
|
||||
|
||||
if (readOnly) {
|
||||
// setup to use AutoCommit such that we skip explicit commit
|
||||
dsConfig.setAutoCommit(true);
|
||||
dsConfig.setReadOnly(true);
|
||||
dsConfig.setDefaults(config.getDataSourceConfig());
|
||||
dsConfig.setIsolationLevel(config.getDataSourceConfig().getIsolationLevel());
|
||||
}
|
||||
return create(dsConfig, readOnly);
|
||||
}
|
||||
|
||||
private DataSource create(DataSourceConfig dsConfig, boolean readOnly) {
|
||||
String poolName = config.getName() + (readOnly ? "-ro" : "");
|
||||
return DataSourceFactory.create(poolName, dsConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach DataSourceAlert via service loader if present.
|
||||
*/
|
||||
private void attachAlert(DataSourceConfig dsConfig) {
|
||||
DataSourceAlertFactory alertFactory = config.service(DataSourceAlertFactory.class);
|
||||
if (alertFactory != null) {
|
||||
dsConfig.setAlert(alertFactory.createAlert());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and attach a DataSourcePoolListener if it has been specified via properties and there is not one already attached.
|
||||
*/
|
||||
private void attachListener(DataSourceConfig dsConfig) {
|
||||
if (dsConfig.getListener() == null) {
|
||||
String poolListener = dsConfig.getPoolListener();
|
||||
if (poolListener != null) {
|
||||
dsConfig.setListener((DataSourcePoolListener) config.getClassLoadConfig().newInstance(poolListener));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.SlowQueryListener;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbHistorySupport;
|
||||
import io.ebean.datasource.DataSourcePool;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeLogRegister;
|
||||
@@ -45,10 +44,10 @@ import io.ebeaninternal.server.changelog.DefaultChangeLogPrepare;
|
||||
import io.ebeaninternal.server.changelog.DefaultChangeLogRegister;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
import io.ebeaninternal.server.core.bootup.BootupClasses;
|
||||
import io.ebeaninternal.server.core.timezone.CloneDataTimeZone;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.core.timezone.LocalDataTimeZone;
|
||||
import io.ebeaninternal.server.core.timezone.MySqlDataTimeZone;
|
||||
import io.ebeaninternal.server.core.timezone.NoDataTimeZone;
|
||||
import io.ebeaninternal.server.core.timezone.OracleDataTimeZone;
|
||||
import io.ebeaninternal.server.core.timezone.SimpleDataTimeZone;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
|
||||
@@ -78,12 +77,10 @@ import io.ebeaninternal.server.query.QueryPlanLoggerSqlServer;
|
||||
import io.ebeaninternal.server.readaudit.DefaultReadAuditLogger;
|
||||
import io.ebeaninternal.server.readaudit.DefaultReadAuditPrepare;
|
||||
import io.ebeaninternal.server.text.json.DJsonContext;
|
||||
import io.ebeaninternal.server.transaction.AutoCommitTransactionManager;
|
||||
import io.ebeaninternal.server.transaction.DataSourceSupplier;
|
||||
import io.ebeaninternal.server.transaction.DefaultProfileHandler;
|
||||
import io.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
|
||||
import io.ebeaninternal.server.transaction.DocStoreTransactionManager;
|
||||
import io.ebeaninternal.server.transaction.ExplicitTransactionManager;
|
||||
import io.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
|
||||
import io.ebeaninternal.server.transaction.JtaTransactionManager;
|
||||
import io.ebeaninternal.server.transaction.NoopProfileHandler;
|
||||
@@ -100,7 +97,6 @@ import io.ebeanservice.docstore.none.NoneDocStoreFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -450,12 +446,6 @@ public class InternalConfiguration {
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager,
|
||||
tableModState, cacheNotify, clockService);
|
||||
|
||||
if (serverConfig.isExplicitTransactionBeginMode()) {
|
||||
return new ExplicitTransactionManager(options);
|
||||
}
|
||||
if (isAutoCommitMode()) {
|
||||
return new AutoCommitTransactionManager(options);
|
||||
}
|
||||
if (serverConfig.isDocStoreOnly()) {
|
||||
return new DocStoreTransactionManager(options);
|
||||
}
|
||||
@@ -492,18 +482,6 @@ public class InternalConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if autoCommit mode is on.
|
||||
*/
|
||||
private boolean isAutoCommitMode() {
|
||||
if (serverConfig.isAutoCommitMode()) {
|
||||
// explicitly set
|
||||
return true;
|
||||
}
|
||||
DataSource dataSource = serverConfig.getDataSource();
|
||||
return dataSource instanceof DataSourcePool && ((DataSourcePool) dataSource).isAutoCommit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the TransactionScopeManager taking into account JTA or external transaction manager.
|
||||
*/
|
||||
@@ -525,16 +503,15 @@ public class InternalConfiguration {
|
||||
* Create the DataTimeZone implementation to use.
|
||||
*/
|
||||
private DataTimeZone initDataTimeZone() {
|
||||
|
||||
String tz = serverConfig.getDataTimeZone();
|
||||
if (tz == null) {
|
||||
if (isMySql(getPlatform())) {
|
||||
return new LocalDataTimeZone();
|
||||
return new MySqlDataTimeZone();
|
||||
}
|
||||
return new NoDataTimeZone();
|
||||
}
|
||||
if (getPlatform().base() == Platform.ORACLE) {
|
||||
return new CloneDataTimeZone(tz);
|
||||
return new OracleDataTimeZone(tz);
|
||||
} else {
|
||||
return new SimpleDataTimeZone(tz);
|
||||
}
|
||||
@@ -594,7 +571,10 @@ public class InternalConfiguration {
|
||||
}
|
||||
|
||||
private ServerCachePlugin initServerCachePlugin() {
|
||||
|
||||
if (serverConfig.isLocalOnlyL2Cache()) {
|
||||
localL2Caching = true;
|
||||
return new DefaultServerCachePlugin();
|
||||
}
|
||||
ServerCachePlugin plugin = serverConfig.getServerCachePlugin();
|
||||
if (plugin == null) {
|
||||
ServiceLoader<ServerCachePlugin> cacheFactories = ServiceLoader.load(ServerCachePlugin.class);
|
||||
|
||||
@@ -57,14 +57,14 @@ public class MultiTenantDbCatalogSupplier implements DataSourceSupplier {
|
||||
@Override
|
||||
public void shutdown(boolean deregisterDriver) {
|
||||
if (readOnlyDataSource instanceof DataSourcePool) {
|
||||
((DataSourcePool) readOnlyDataSource).shutdown(false);
|
||||
((DataSourcePool) readOnlyDataSource).shutdown();
|
||||
}
|
||||
if (dataSource instanceof DataSourcePool) {
|
||||
((DataSourcePool) dataSource).shutdown(deregisterDriver);
|
||||
((DataSourcePool) dataSource).shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private class CatalogDataSource implements DataSource {
|
||||
private static class CatalogDataSource implements DataSource {
|
||||
|
||||
final DataSource dataSource;
|
||||
final CurrentTenantProvider tenantProvider;
|
||||
|
||||
@@ -57,10 +57,10 @@ class MultiTenantDbSchemaSupplier implements DataSourceSupplier {
|
||||
@Override
|
||||
public void shutdown(boolean deregisterDriver) {
|
||||
if (readOnlyDataSource instanceof DataSourcePool) {
|
||||
((DataSourcePool) readOnlyDataSource).shutdown(false);
|
||||
((DataSourcePool) readOnlyDataSource).shutdown();
|
||||
}
|
||||
if (dataSource instanceof DataSourcePool) {
|
||||
((DataSourcePool) dataSource).shutdown(deregisterDriver);
|
||||
((DataSourcePool) dataSource).shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,8 +89,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
|
||||
private Set<String> dependentTables;
|
||||
|
||||
private boolean iterateSingleContext;
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
*/
|
||||
@@ -108,15 +106,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
return queryEngine.translate(this, bindLog, sql, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIterateSingleContext() {
|
||||
this.iterateSingleContext = true;
|
||||
}
|
||||
|
||||
public boolean isIterateSingleContext() {
|
||||
return iterateSingleContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleteByStatement() {
|
||||
if (!transaction.isPersistCascade() || beanDescriptor.isDeleteByStatement()) {
|
||||
@@ -286,7 +275,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
} else {
|
||||
// create an implicit transaction to execute this query
|
||||
// potentially using read-only DataSource with autoCommit
|
||||
transaction = ebeanServer.createQueryTransaction(query.getTenantId());
|
||||
transaction = ebeanServer.createReadOnlyTransaction(query.getTenantId());
|
||||
}
|
||||
createdTransaction = true;
|
||||
}
|
||||
@@ -316,7 +305,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
*/
|
||||
@Override
|
||||
public JsonReadOptions createJsonReadOptions() {
|
||||
|
||||
persistenceContext = getPersistenceContext(query, transaction);
|
||||
if (query.getPersistenceContext() == null) {
|
||||
query.setPersistenceContext(persistenceContext);
|
||||
@@ -327,7 +315,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
loadContext = new DLoadContext(this, secondaryQueries);
|
||||
jsonRead.setLoadContext(loadContext);
|
||||
}
|
||||
|
||||
return jsonRead;
|
||||
}
|
||||
|
||||
@@ -335,8 +322,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
* For iterate queries reset the persistenceContext and loadContext.
|
||||
*/
|
||||
public void flushPersistenceContextOnIterate() {
|
||||
if (!iterateSingleContext) {
|
||||
persistenceContext = new DefaultPersistenceContext();
|
||||
if (persistenceContext.resetLimit()) {
|
||||
persistenceContext = persistenceContext.forIterateReset();
|
||||
loadContext.resetPersistenceContext(persistenceContext);
|
||||
if (jsonRead != null) {
|
||||
jsonRead.setPersistenceContext(persistenceContext);
|
||||
@@ -350,7 +337,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
* transaction scoped.
|
||||
*/
|
||||
private PersistenceContext getPersistenceContext(SpiQuery<?> query, SpiTransaction t) {
|
||||
|
||||
// check if there is already a persistence context set which is the case
|
||||
// when lazy loading or query joins are executed
|
||||
PersistenceContext ctx = query.getPersistenceContext();
|
||||
@@ -358,7 +344,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
|
||||
// determine the scope (from the query and then server)
|
||||
PersistenceContextScope scope = ebeanServer.getPersistenceContextScope(query);
|
||||
return (scope == PersistenceContextScope.QUERY || t == null) ? new DefaultPersistenceContext() : t.getPersistenceContext();
|
||||
if (scope == PersistenceContextScope.QUERY || t == null) {
|
||||
return new DefaultPersistenceContext();
|
||||
}
|
||||
if (Type.ITERATE == query.getType()) {
|
||||
return t.getPersistenceContext().forIterate();
|
||||
} else {
|
||||
return t.getPersistenceContext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -578,6 +571,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
return cacheKey != null && query.getUseQueryCache().isPut();
|
||||
}
|
||||
|
||||
public boolean isBeanCachePutMany() {
|
||||
return !transaction.isSkipCacheExplicit() && query.isBeanCachePut();
|
||||
}
|
||||
|
||||
public boolean isBeanCachePut() {
|
||||
return !transaction.isSkipCache() && query.isBeanCachePut();
|
||||
}
|
||||
@@ -586,7 +583,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
* Merge in prior L2 bean cache hits with the query result.
|
||||
*/
|
||||
public void mergeCacheHits(BeanCollection<T> result) {
|
||||
|
||||
if (cacheBeans != null && !cacheBeans.isEmpty()) {
|
||||
if (query.getType() == Type.MAP) {
|
||||
mergeCacheHitsToMap(result);
|
||||
|
||||
@@ -188,7 +188,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
|
||||
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
|
||||
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
|
||||
|
||||
super(server, t, persistExecute);
|
||||
this.entityBean = (EntityBean) bean;
|
||||
this.intercept = entityBean._ebean_getIntercept();
|
||||
@@ -204,7 +203,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (Flags.isRecurse(flags)) {
|
||||
this.persistCascade = t.isPersistCascade();
|
||||
}
|
||||
|
||||
if (this.type == Type.UPDATE) {
|
||||
if (intercept.isNew()) {
|
||||
// 'stateless update' - set loaded properties as dirty
|
||||
@@ -279,7 +277,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Init the transaction and also check for batch on cascade escalation.
|
||||
*/
|
||||
public void initTransIfRequiredWithBatchCascade() {
|
||||
|
||||
if (createImplicitTransIfRequired()) {
|
||||
docStoreMode = calcDocStoreMode(transaction, type);
|
||||
}
|
||||
@@ -317,9 +314,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
private void onUpdateGeneratedProperties() {
|
||||
|
||||
for (BeanProperty prop : beanDescriptor.propertiesGenUpdate()) {
|
||||
|
||||
GeneratedProperty generatedProperty = prop.getGeneratedProperty();
|
||||
if (prop.isVersion()) {
|
||||
if (isLoadedProperty(prop)) {
|
||||
@@ -451,7 +446,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Return true if any of the given properties are dirty.
|
||||
*/
|
||||
public boolean hasDirtyProperty(int[] propertyPositions) {
|
||||
|
||||
for (int propertyPosition : propertyPositions) {
|
||||
if (dirtyProperties[propertyPosition]) {
|
||||
return true;
|
||||
@@ -517,7 +511,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
@Override
|
||||
public void docStoreUpdate(DocStoreUpdateContext txn) throws IOException {
|
||||
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
beanDescriptor.docStoreInsert(idValue, this, txn);
|
||||
@@ -554,7 +547,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
|
||||
|
||||
beanPersistMap.add(beanDescriptor, type, idValue);
|
||||
}
|
||||
|
||||
@@ -564,19 +556,15 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
case INSERT:
|
||||
beanPersistListener.inserted(bean);
|
||||
break;
|
||||
|
||||
case UPDATE:
|
||||
beanPersistListener.updated(bean, updatedProperties);
|
||||
break;
|
||||
|
||||
case DELETE:
|
||||
beanPersistListener.deleted(bean);
|
||||
break;
|
||||
|
||||
case DELETE_SOFT:
|
||||
beanPersistListener.softDeleted(bean);
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -785,7 +773,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
case INSERT:
|
||||
executeInsert();
|
||||
return -1;
|
||||
|
||||
case UPDATE:
|
||||
if (beanPersistListener != null) {
|
||||
// store the updated properties for sending later
|
||||
@@ -793,15 +780,12 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
executeUpdate();
|
||||
return -1;
|
||||
|
||||
case DELETE_SOFT:
|
||||
prepareForSoftDelete();
|
||||
executeSoftDelete();
|
||||
return -1;
|
||||
|
||||
case DELETE:
|
||||
return executeDelete();
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Invalid type " + type);
|
||||
}
|
||||
@@ -811,13 +795,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Soft delete is executed as update so we want to set deleted=true property.
|
||||
*/
|
||||
private void prepareForSoftDelete() {
|
||||
|
||||
beanDescriptor.setSoftDeleteValue(entityBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeOrQueue() {
|
||||
|
||||
boolean batch = isBatchThisRequest();
|
||||
try {
|
||||
BatchControl control = transaction.getBatchControl();
|
||||
@@ -827,7 +809,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (batch) {
|
||||
control = persistExecute.createBatchControl(transaction);
|
||||
return control.executeOrQueue(this, true);
|
||||
|
||||
} else {
|
||||
return executeNoBatch();
|
||||
}
|
||||
@@ -926,7 +907,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
controllerPost();
|
||||
}
|
||||
setNotifyCache();
|
||||
|
||||
boolean isChangeLog = beanDescriptor.isChangeLog();
|
||||
if (type == Type.UPDATE && (isChangeLog || notifyCache || docStoreMode == DocStoreMode.UPDATE)) {
|
||||
// get the dirty properties for update notification to the doc store
|
||||
@@ -935,16 +915,13 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (isChangeLog) {
|
||||
changeLog();
|
||||
}
|
||||
|
||||
// if bean persisted again then should result in an update
|
||||
intercept.setLoaded();
|
||||
if (isInsert()) {
|
||||
postInsert();
|
||||
}
|
||||
|
||||
addPostCommitListeners();
|
||||
notifyCacheOnPostExecute();
|
||||
|
||||
if (isLogSummary()) {
|
||||
logSummary();
|
||||
}
|
||||
@@ -994,7 +971,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
private void logSummary() {
|
||||
|
||||
String draft = (beanDescriptor.isDraftable() && !publish) ? " draft[true]" : "";
|
||||
String name = beanDescriptor.getName();
|
||||
switch (type) {
|
||||
@@ -1136,7 +1112,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* of queue events or list of update events.
|
||||
*/
|
||||
public void addDocStoreUpdates(DocStoreUpdates docStoreUpdates) {
|
||||
|
||||
if (type == Type.UPDATE) {
|
||||
beanDescriptor.docStoreUpdateEmbedded(this, docStoreUpdates);
|
||||
}
|
||||
@@ -1165,7 +1140,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* </p>
|
||||
*/
|
||||
private boolean determineUpdateAllLoadedProperties() {
|
||||
|
||||
Boolean txnUpdateAll = transaction.isUpdateAllLoadedProperties();
|
||||
if (txnUpdateAll != null) {
|
||||
// use the setting explicitly set on the transaction
|
||||
@@ -1174,7 +1148,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
// if using batch use the server default setting
|
||||
requestUpdateAllLoadedProps = isBatchThisRequest() && ebeanServer.isUpdateAllPropertiesInBatch();
|
||||
}
|
||||
|
||||
return requestUpdateAllLoadedProps;
|
||||
}
|
||||
|
||||
@@ -1196,25 +1169,21 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Return the key for an update persist request.
|
||||
*/
|
||||
public String getUpdatePlanHash() {
|
||||
|
||||
StringBuilder key;
|
||||
if (determineUpdateAllLoadedProperties()) {
|
||||
key = intercept.getLoadedPropertyKey();
|
||||
} else {
|
||||
key = intercept.getDirtyPropertyKey();
|
||||
}
|
||||
|
||||
BeanProperty versionProperty = beanDescriptor.getVersionProperty();
|
||||
if (versionProperty != null) {
|
||||
if (intercept.isLoadedProperty(versionProperty.getPropertyIndex())) {
|
||||
key.append('v');
|
||||
}
|
||||
}
|
||||
|
||||
if (publish) {
|
||||
key.append('p');
|
||||
}
|
||||
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
@@ -1380,13 +1349,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Build the bean update for the L2 cache.
|
||||
*/
|
||||
public void addBeanUpdate(CacheChangeSet changeSet) {
|
||||
|
||||
if (!updatedManysOnly || collectionChanges != null) {
|
||||
|
||||
boolean updateNaturalKey = false;
|
||||
|
||||
String key = beanDescriptor.cacheKey(idValue);
|
||||
|
||||
Map<String, Object> changes = new LinkedHashMap<>();
|
||||
EntityBean bean = getEntityBean();
|
||||
boolean[] dirtyProperties = getDirtyProperties();
|
||||
@@ -1399,7 +1365,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
changes.put(property.getName(), val);
|
||||
if (property.isNaturalKey()) {
|
||||
updateNaturalKey = true;
|
||||
changeSet.addNaturalKeyPut(beanDescriptor, key, val.toString());
|
||||
String valStr = (val == null) ? null : val.toString();
|
||||
changeSet.addNaturalKeyPut(beanDescriptor, key, valStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1409,7 +1376,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
// add element collection update
|
||||
changes.putAll(collectionChanges);
|
||||
}
|
||||
|
||||
changeSet.addBeanUpdate(beanDescriptor, key, changes, updateNaturalKey, getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ import io.ebeaninternal.dbmigration.ddlgeneration.platform.DB2Ddl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.H2Ddl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.HanaColumnStoreDdl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.HsqldbDdl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.MariaDbDdl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.MySqlDdl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.NuoDbDdl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.Oracle10Ddl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.OracleDdl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.PlatformDdl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.Postgres9Ddl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.PostgresDdl;
|
||||
@@ -31,6 +32,8 @@ public class PlatformDdlBuilder {
|
||||
return new H2Ddl(platform);
|
||||
case DB2:
|
||||
return new DB2Ddl(platform);
|
||||
case MARIADB:
|
||||
return new MariaDbDdl(platform);
|
||||
case MYSQL55:
|
||||
case MYSQL:
|
||||
return new MySqlDdl(platform);
|
||||
@@ -40,7 +43,7 @@ public class PlatformDdlBuilder {
|
||||
return new NuoDbDdl(platform);
|
||||
case ORACLE:
|
||||
case ORACLE11:
|
||||
return new Oracle10Ddl(platform);
|
||||
return new OracleDdl(platform);
|
||||
case SQLITE:
|
||||
return new SQLiteDdl(platform);
|
||||
case POSTGRES9:
|
||||
|
||||
@@ -44,10 +44,10 @@ class SimpleDataSourceProvider implements DataSourceSupplier {
|
||||
@Override
|
||||
public void shutdown(boolean deregisterDriver) {
|
||||
if (readOnlyDataSource instanceof DataSourcePool){
|
||||
((DataSourcePool) readOnlyDataSource).shutdown(false);
|
||||
((DataSourcePool) readOnlyDataSource).shutdown();
|
||||
}
|
||||
if (dataSource instanceof DataSourcePool){
|
||||
((DataSourcePool) dataSource).shutdown(deregisterDriver);
|
||||
((DataSourcePool) dataSource).shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,9 +177,4 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
|
||||
*/
|
||||
boolean isDeleteByStatement();
|
||||
|
||||
/**
|
||||
* Set when we want to use a single persistence context for all beans returned
|
||||
* in the query (so all beans are held in memory)
|
||||
*/
|
||||
void setIterateSingleContext();
|
||||
}
|
||||
|
||||
+5
-5
@@ -5,11 +5,11 @@ import java.util.Calendar;
|
||||
/**
|
||||
* Implementation of DataTimeZone when single Calendar instance is used with local timezone.
|
||||
*/
|
||||
public class LocalDataTimeZone implements DataTimeZone {
|
||||
public class MySqlDataTimeZone implements DataTimeZone {
|
||||
|
||||
protected final Calendar zone;
|
||||
|
||||
public LocalDataTimeZone() {
|
||||
public MySqlDataTimeZone() {
|
||||
this.zone = Calendar.getInstance();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public class LocalDataTimeZone implements DataTimeZone {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar getDateTimeZone() {
|
||||
return zone; // workaround for MySQL. TODO: rename class to MySqlDataTimeZone!?
|
||||
}
|
||||
public Calendar getDateTimeZone() {
|
||||
return zone;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -8,9 +8,9 @@ import java.util.Calendar;
|
||||
* Used with Oracle JDBC driver as that wants to mutate the Calender.
|
||||
* </p>
|
||||
*/
|
||||
public class CloneDataTimeZone extends SimpleDataTimeZone {
|
||||
public class OracleDataTimeZone extends SimpleDataTimeZone {
|
||||
|
||||
public CloneDataTimeZone(String zoneId) {
|
||||
public OracleDataTimeZone(String zoneId) {
|
||||
super(zoneId);
|
||||
}
|
||||
|
||||
@@ -105,7 +105,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -219,9 +218,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
private final BeanProperty draftDirty;
|
||||
|
||||
/**
|
||||
* Map of BeanProperty Linked so as to preserve order.
|
||||
*/
|
||||
private final LinkedHashMap<String, BeanProperty> propMap;
|
||||
|
||||
/**
|
||||
@@ -234,44 +230,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
*/
|
||||
private final Map<String, BeanPropertyAssoc<?>> tablePath = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The type of bean this describes.
|
||||
*/
|
||||
final Class<T> beanType;
|
||||
|
||||
final Class<?> rootBeanType;
|
||||
|
||||
/**
|
||||
* This is not sent to a remote client.
|
||||
*/
|
||||
private final BeanDescriptorMap owner;
|
||||
|
||||
|
||||
final String[] properties;
|
||||
|
||||
/**
|
||||
* Intercept pre post on insert,update, and delete .
|
||||
*/
|
||||
private volatile BeanPersistController persistController;
|
||||
|
||||
private final BeanPostLoad beanPostLoad;
|
||||
private final BeanPostConstructListener beanPostConstructListener;
|
||||
|
||||
/**
|
||||
* Listens for post commit insert update and delete events.
|
||||
*/
|
||||
private volatile BeanPersistController persistController;
|
||||
private volatile BeanPersistListener persistListener;
|
||||
|
||||
private final BeanQueryAdapter queryAdapter;
|
||||
|
||||
/**
|
||||
* If set overrides the find implementation. Server side only.
|
||||
*/
|
||||
private final BeanFindController beanFinder;
|
||||
|
||||
/**
|
||||
* Used for fine grain filtering for the change log.
|
||||
*/
|
||||
private final ChangeLogFilter changeLogFilter;
|
||||
|
||||
/**
|
||||
@@ -281,22 +250,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
private final boolean abstractType;
|
||||
|
||||
/**
|
||||
* Derived list of properties that make up the unique id.
|
||||
*/
|
||||
private final BeanProperty idProperty;
|
||||
|
||||
private final int idPropertyIndex;
|
||||
|
||||
/**
|
||||
* Derived list of properties that are used for version concurrency checking.
|
||||
*/
|
||||
private final BeanProperty versionProperty;
|
||||
|
||||
private final int versionPropertyIndex;
|
||||
|
||||
private final BeanProperty whenModifiedProperty;
|
||||
|
||||
private final BeanProperty whenCreatedProperty;
|
||||
|
||||
/**
|
||||
@@ -313,14 +272,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Scalar mutable properties (need to dirty check on update).
|
||||
*/
|
||||
private final BeanProperty[] propertiesMutable;
|
||||
|
||||
|
||||
private final BeanPropertyAssocOne<?> unidirectional;
|
||||
private final BeanProperty orderColumn;
|
||||
|
||||
/**
|
||||
* list of properties that are Lists/Sets/Maps (Derived).
|
||||
*/
|
||||
private final BeanProperty[] propertiesNonMany;
|
||||
private final BeanProperty[] propertiesAggregate;
|
||||
private final BeanPropertyAssocMany<?>[] propertiesMany;
|
||||
@@ -332,25 +286,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* list of properties that are associated beans and not embedded (Derived).
|
||||
*/
|
||||
private final BeanPropertyAssocOne<?>[] propertiesOne;
|
||||
|
||||
private final BeanPropertyAssocOne<?>[] propertiesOneImported;
|
||||
private final BeanPropertyAssocOne<?>[] propertiesOneImportedSave;
|
||||
private final BeanPropertyAssocOne<?>[] propertiesOneImportedDelete;
|
||||
|
||||
//private final BeanPropertyAssocOne<?>[] propertiesOneExported;
|
||||
private final BeanPropertyAssocOne<?>[] propertiesOneExportedSave;
|
||||
private final BeanPropertyAssocOne<?>[] propertiesOneExportedDelete;
|
||||
|
||||
/**
|
||||
* list of properties that are embedded beans.
|
||||
*/
|
||||
private final BeanPropertyAssocOne<?>[] propertiesEmbedded;
|
||||
|
||||
/**
|
||||
* List of the scalar properties excluding id and secondary table properties.
|
||||
*/
|
||||
private final BeanProperty[] propertiesBaseScalar;
|
||||
|
||||
private final BeanProperty[] propertiesTransient;
|
||||
|
||||
/**
|
||||
@@ -382,11 +325,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
private final EntityBean prototypeEntityBean;
|
||||
|
||||
private final IdBinder idBinder;
|
||||
|
||||
private String idBinderInLHSSql;
|
||||
|
||||
private String idBinderIdSql;
|
||||
|
||||
private String deleteByIdSql;
|
||||
private String deleteByIdInSql;
|
||||
private String whereIdInSql;
|
||||
@@ -394,29 +334,19 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
private String softDeleteByIdInSql;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String baseTableAlias;
|
||||
|
||||
private final boolean cacheSharableBeans;
|
||||
|
||||
private final String docStoreQueueId;
|
||||
|
||||
private final BeanDescriptorDraftHelp<T> draftHelp;
|
||||
private final BeanDescriptorCacheHelp<T> cacheHelp;
|
||||
private final BeanDescriptorJsonHelp<T> jsonHelp;
|
||||
private DocStoreBeanAdapter<T> docStoreAdapter;
|
||||
private DocumentMapping docMapping;
|
||||
private boolean docStoreEmbeddedInvalidation;
|
||||
|
||||
private final String defaultSelectClause;
|
||||
|
||||
private SpiEbeanServer ebeanServer;
|
||||
|
||||
/**
|
||||
* Construct the BeanDescriptor.
|
||||
*/
|
||||
public BeanDescriptor(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy) {
|
||||
|
||||
this.owner = owner;
|
||||
this.multiValueSupported = owner.isMultiValueSupported();
|
||||
this.entityType = deploy.getEntityType();
|
||||
@@ -431,7 +361,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.namedQuery = deploy.getNamedQuery();
|
||||
this.namedRawSql = deploy.getNamedRawSql();
|
||||
this.inheritInfo = deploy.getInheritInfo();
|
||||
|
||||
this.beanFinder = deploy.getBeanFinder();
|
||||
this.persistController = deploy.getPersistController();
|
||||
this.persistListener = deploy.getPersistListener();
|
||||
@@ -439,7 +368,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.beanPostLoad = deploy.getPostLoad();
|
||||
this.queryAdapter = deploy.getQueryAdapter();
|
||||
this.changeLogFilter = deploy.getChangeLogFilter();
|
||||
|
||||
this.defaultSelectClause = deploy.getDefaultSelectClause();
|
||||
this.identityMode = deploy.buildIdentityMode();
|
||||
this.idType = identityMode.getIdType();
|
||||
@@ -449,7 +377,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.selectLastInsertedIdDraft = deploy.getSelectLastInsertedIdDraft();
|
||||
this.concurrencyMode = deploy.getConcurrencyMode();
|
||||
this.indexDefinitions = deploy.getIndexDefinitions();
|
||||
|
||||
this.readAuditing = deploy.isReadAuditing();
|
||||
this.draftable = deploy.isDraftable();
|
||||
this.draftableElement = deploy.isDraftableElement();
|
||||
@@ -464,10 +391,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.partitionMeta = deploy.getPartitionMeta();
|
||||
this.storageEngine = deploy.getStorageEngine();
|
||||
this.autoTunable = beanFinder == null && (entityType == EntityType.ORM || entityType == EntityType.VIEW);
|
||||
|
||||
// helper object used to derive lists of properties
|
||||
DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy);
|
||||
|
||||
this.softDeleteProperty = listHelper.getSoftDeleteProperty();
|
||||
// if formula is set, the property is virtual only (there is no column in db) the formula must evaluate to true,
|
||||
// if there is a join to a deleted bean. Example: '@Formula(select = "${ta}.user_id is null")'
|
||||
@@ -503,22 +428,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.propertiesGenInsert = listHelper.getGeneratedInsert();
|
||||
this.propertiesGenUpdate = listHelper.getGeneratedUpdate();
|
||||
this.idOnlyReference = isIdOnlyReference(propertiesBaseScalar);
|
||||
|
||||
boolean noRelationships = propertiesOne.length + propertiesMany.length == 0;
|
||||
|
||||
this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly();
|
||||
this.cacheHelp = new BeanDescriptorCacheHelp<>(this, owner.getCacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported);
|
||||
this.jsonHelp = new BeanDescriptorJsonHelp<>(this);
|
||||
this.draftHelp = new BeanDescriptorDraftHelp<>(this);
|
||||
|
||||
this.docStoreAdapter = owner.createDocStoreBeanAdapter(this, deploy);
|
||||
this.docStoreQueueId = docStoreAdapter.getQueueId();
|
||||
|
||||
// Check if there are no cascade save associated beans ( subject to change
|
||||
// in initialiseOther()). Note that if we are in an inheritance hierarchy
|
||||
// then we also need to check every BeanDescriptors in the InheritInfo as
|
||||
// well. We do that later in initialiseOther().
|
||||
|
||||
saveRecurseSkippable = (0 == (propertiesOneExportedSave.length + propertiesOneImportedSave.length + propertiesManySave.length));
|
||||
|
||||
// Check if there are no cascade delete associated beans (also subject to
|
||||
@@ -529,7 +449,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.idBinder = owner.createIdBinder(idProperty);
|
||||
this.whenModifiedProperty = findWhenModifiedProperty();
|
||||
this.whenCreatedProperty = findWhenCreatedProperty();
|
||||
|
||||
// derive the index position of the Id and Version properties
|
||||
this.abstractType = Modifier.isAbstract(beanType.getModifiers());
|
||||
if (abstractType) {
|
||||
@@ -537,7 +456,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.versionPropertyIndex = -1;
|
||||
this.unloadProperties = new int[0];
|
||||
this.propertiesIndex = new BeanProperty[0];
|
||||
|
||||
} else {
|
||||
EntityBeanIntercept ebi = prototypeEntityBean._ebean_getIntercept();
|
||||
this.idPropertyIndex = (idProperty == null) ? -1 : ebi.findProperty(idProperty.getName());
|
||||
@@ -568,23 +486,19 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* These properties need to be unloaded when populating beans for queries.
|
||||
*/
|
||||
private int[] derivePropertiesToUnload(EntityBean prototypeEntityBean) {
|
||||
|
||||
boolean[] loaded = prototypeEntityBean._ebean_getIntercept().getLoaded();
|
||||
int[] props = new int[loaded.length];
|
||||
int pos = 0;
|
||||
|
||||
// collect the positions of the properties initialised in the default constructor.
|
||||
for (int i = 0; i < loaded.length; i++) {
|
||||
if (loaded[i]) {
|
||||
props[pos++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos == 0) {
|
||||
// nothing set in the constructor
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
// populate a smaller/minimal array
|
||||
int[] unload = new int[pos];
|
||||
System.arraycopy(props, 0, unload, 0, pos);
|
||||
@@ -665,14 +579,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* <p>
|
||||
* These properties need to be initialised prior to the association properties
|
||||
* as they are used to get the imported and exported properties.
|
||||
* </p>
|
||||
*/
|
||||
void initialiseId(BeanDescriptorInitContext initContext) {
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("BeanDescriptor initialise " + fullName);
|
||||
}
|
||||
|
||||
if (draftable) {
|
||||
initContext.addDraft(baseTable, draftTable);
|
||||
}
|
||||
@@ -680,7 +591,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
// add mapping (used to swap out baseTable for asOf queries)
|
||||
initContext.addHistory(baseTable, baseTableAsOf);
|
||||
}
|
||||
|
||||
if (inheritInfo != null) {
|
||||
inheritInfo.setDescriptor(this);
|
||||
}
|
||||
@@ -695,19 +605,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Initialise the exported and imported parts for associated properties.
|
||||
*/
|
||||
public void initialiseOther(BeanDescriptorInitContext initContext) {
|
||||
|
||||
for (BeanPropertyAssocMany<?> many : propertiesManyToMany) {
|
||||
// register associated draft table for M2M intersection
|
||||
many.registerDraftIntersectionTable(initContext);
|
||||
}
|
||||
|
||||
if (historySupport) {
|
||||
// history support on this bean so check all associated intersection tables
|
||||
// and if they are not excluded register the associated 'with history' table
|
||||
for (BeanPropertyAssocMany<?> aPropertiesManyToMany : propertiesManyToMany) {
|
||||
for (BeanPropertyAssocMany<?> manyToMany : propertiesManyToMany) {
|
||||
// register associated history table for M2M intersection
|
||||
if (!aPropertiesManyToMany.isExcludedFromHistory()) {
|
||||
TableJoin intersectionTableJoin = aPropertiesManyToMany.getIntersectionTableJoin();
|
||||
if (!manyToMany.isExcludedFromHistory()) {
|
||||
TableJoin intersectionTableJoin = manyToMany.getIntersectionTableJoin();
|
||||
initContext.addHistoryIntersection(intersectionTableJoin.getTable());
|
||||
}
|
||||
}
|
||||
@@ -730,11 +638,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
idBinderIdSql = idBinder.getBindIdSql(baseTableAlias);
|
||||
String idBinderInLHSSqlNoAlias = idBinder.getBindIdInSql(null);
|
||||
String idEqualsSql = idBinder.getBindIdSql(null);
|
||||
|
||||
deleteByIdSql = "delete from " + baseTable + " where " + idEqualsSql;
|
||||
whereIdInSql = " where " + idBinderInLHSSqlNoAlias + " ";
|
||||
deleteByIdInSql = "delete from " + baseTable + whereIdInSql;
|
||||
|
||||
if (softDelete) {
|
||||
softDeleteByIdSql = "update " + baseTable + " set " + getSoftDeleteDbSet() + " where " + idEqualsSql;
|
||||
softDeleteByIdInSql = "update " + baseTable + " set " + getSoftDeleteDbSet() + " where " + idBinderInLHSSqlNoAlias + " ";
|
||||
@@ -765,7 +671,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasCircularImportedIdTo(BeanDescriptor sourceDesc) {
|
||||
boolean hasCircularImportedIdTo(BeanDescriptor<?> sourceDesc) {
|
||||
for (BeanPropertyAssocOne<?> assocOne : propertiesOneImportedSave) {
|
||||
if (assocOne.getTargetDescriptor() == sourceDesc) {
|
||||
return true;
|
||||
@@ -792,7 +698,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Perform last initialisation for the descriptor.
|
||||
*/
|
||||
void initLast() {
|
||||
|
||||
for (BeanProperty prop : propertiesNonTransient) {
|
||||
if (prop.isUnique()) {
|
||||
propertiesUnique.add(new BeanProperty[]{prop});
|
||||
@@ -895,13 +800,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
|
||||
public void merge(EntityBean bean, EntityBean existing) {
|
||||
|
||||
EntityBeanIntercept fromEbi = bean._ebean_getIntercept();
|
||||
EntityBeanIntercept toEbi = existing._ebean_getIntercept();
|
||||
|
||||
int propertyLength = toEbi.getPropertyLength();
|
||||
String[] names = getProperties();
|
||||
|
||||
for (int i = 0; i < propertyLength; i++) {
|
||||
|
||||
if (fromEbi.isLoadedProperty(i)) {
|
||||
@@ -977,14 +879,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Return the bean change for an update generating 'new values' and 'old values' in JSON form.
|
||||
*/
|
||||
private BeanChange updateBeanChange(PersistRequestBean<T> request) {
|
||||
|
||||
try {
|
||||
BeanChangeJson changeJson = new BeanChangeJson(this, request.isStatelessUpdate());
|
||||
request.getEntityBeanIntercept().addDirtyPropertyValues(changeJson);
|
||||
changeJson.flush();
|
||||
|
||||
return beanChange(ChangeType.UPDATE, request.getBeanId(), changeJson.newJson(), changeJson.oldJson());
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
logger.error("Failed to write ChangeLog entry for update", e);
|
||||
return null;
|
||||
@@ -995,16 +894,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Return the bean change for an insert.
|
||||
*/
|
||||
private BeanChange insertBeanChange(PersistRequestBean<T> request) {
|
||||
|
||||
try {
|
||||
StringWriter writer = new StringWriter(200);
|
||||
SpiJsonWriter jsonWriter = createJsonWriter(writer);
|
||||
|
||||
jsonWriteForInsert(jsonWriter, request.getEntityBean());
|
||||
jsonWriter.flush();
|
||||
|
||||
return beanChange(ChangeType.INSERT, request.getBeanId(), writer.toString(), null);
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to write ChangeLog entry for insert", e);
|
||||
return null;
|
||||
@@ -1063,12 +958,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* concurrency checking.
|
||||
*/
|
||||
private SqlUpdate deleteByIdList(List<Object> idList, DeleteMode mode) {
|
||||
|
||||
String baseSql = mode.isHard() ? deleteByIdInSql : softDeleteByIdInSql;
|
||||
StringBuilder sb = new StringBuilder(baseSql);
|
||||
String inClause = idBinder.getIdInValueExprDelete(idList.size());
|
||||
sb.append(inClause);
|
||||
|
||||
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
|
||||
idBinder.addIdInBindValues(delete, idList);
|
||||
return delete;
|
||||
@@ -1079,15 +972,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* concurrency checking.
|
||||
*/
|
||||
private SqlUpdate deleteById(Object id, DeleteMode mode) {
|
||||
|
||||
String baseSql = mode.isHard() ? deleteByIdSql : softDeleteByIdSql;
|
||||
DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(baseSql);
|
||||
|
||||
Object[] bindValues = idBinder.getBindValues(id);
|
||||
for (Object bindValue : bindValues) {
|
||||
sqlDelete.setParameter(bindValue);
|
||||
}
|
||||
|
||||
return sqlDelete;
|
||||
}
|
||||
|
||||
@@ -1100,9 +990,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
|
||||
void initialiseFkeys() {
|
||||
for (BeanPropertyAssocOne<?> aPropertiesOneImported : propertiesOneImported) {
|
||||
if (!aPropertiesOneImported.isFormula()) {
|
||||
aPropertiesOneImported.addFkey();
|
||||
for (BeanPropertyAssocOne<?> oneImported : propertiesOneImported) {
|
||||
if (!oneImported.isFormula()) {
|
||||
oneImported.addFkey();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1210,12 +1100,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Build the Document mapping recursively with the given prefix relative to the root of the document.
|
||||
*/
|
||||
public void docStoreMapping(final DocMappingBuilder mapping, final String prefix) {
|
||||
|
||||
if (prefix != null && idProperty != null) {
|
||||
// id property not included in the
|
||||
idProperty.docStoreMapping(mapping, prefix);
|
||||
}
|
||||
|
||||
if (inheritInfo != null) {
|
||||
String discCol = inheritInfo.getDiscriminatorColumn();
|
||||
if (Types.VARCHAR == inheritInfo.getDiscriminatorType()) {
|
||||
@@ -1702,13 +1590,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Trim query plans not used since the passed in epoch time.
|
||||
*/
|
||||
void trimQueryPlans(long unusedSince) {
|
||||
Iterator<CQueryPlan> it = queryPlanCache.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
CQueryPlan queryPlan = it.next();
|
||||
if (queryPlan.getLastQueryTime() < unusedSince) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
queryPlanCache.values().removeIf(queryPlan -> queryPlan.getLastQueryTime() < unusedSince);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1804,9 +1686,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Find a property annotated with @WhenCreated or @CreatedTimestamp.
|
||||
*/
|
||||
private BeanProperty findWhenCreatedProperty() {
|
||||
for (BeanProperty aPropertiesBaseScalar : propertiesBaseScalar) {
|
||||
if (aPropertiesBaseScalar.isGeneratedWhenCreated()) {
|
||||
return aPropertiesBaseScalar;
|
||||
for (BeanProperty baseScalar : propertiesBaseScalar) {
|
||||
if (baseScalar.isGeneratedWhenCreated()) {
|
||||
return baseScalar;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -1816,10 +1698,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Find a property annotated with @WhenModified or @UpdatedTimestamp.
|
||||
*/
|
||||
private BeanProperty findWhenModifiedProperty() {
|
||||
|
||||
for (BeanProperty aPropertiesBaseScalar : propertiesBaseScalar) {
|
||||
if (aPropertiesBaseScalar.isGeneratedWhenModified()) {
|
||||
return aPropertiesBaseScalar;
|
||||
for (BeanProperty baseScalar : propertiesBaseScalar) {
|
||||
if (baseScalar.isGeneratedWhenModified()) {
|
||||
return baseScalar;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -1829,14 +1710,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Return the many property included in the query or null if one is not.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> getManyProperty(SpiQuery<?> query) {
|
||||
|
||||
OrmQueryDetail detail = query.getDetail();
|
||||
for (BeanPropertyAssocMany<?> aPropertiesMany : propertiesMany) {
|
||||
if (detail.includesPath(aPropertiesMany.getName())) {
|
||||
return aPropertiesMany;
|
||||
for (BeanPropertyAssocMany<?> many : propertiesMany) {
|
||||
if (detail.includesPath(many.getName())) {
|
||||
return many;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1905,7 +1784,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Bind the idValue to the preparedStatement.
|
||||
* <p>
|
||||
* This takes care of the various id types such as embedded beans etc.
|
||||
* </p>
|
||||
*/
|
||||
public void bindId(DataBind dataBind, Object idValue) throws SQLException {
|
||||
idBinder.bindId(dataBind, idValue);
|
||||
@@ -1915,7 +1793,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Return the id as an array of scalar bindable values.
|
||||
* <p>
|
||||
* This 'flattens' any EmbeddedId or multiple Id property cases.
|
||||
* </p>
|
||||
*/
|
||||
public Object[] getBindIdValues(Object idValue) {
|
||||
return idBinder.getBindValues(idValue);
|
||||
@@ -1940,7 +1817,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
try {
|
||||
EntityBean bean = (EntityBean) prototypeEntityBean._ebean_newInstance();
|
||||
|
||||
if (beanPostConstructListener != null) {
|
||||
beanPostConstructListener.autowire(bean); // calls all registered listeners
|
||||
beanPostConstructListener.postConstruct(bean); // calls first the @PostConstruct method and then the listeners
|
||||
@@ -1959,7 +1835,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
}
|
||||
return bean;
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new PersistenceException(ex);
|
||||
}
|
||||
@@ -1999,7 +1874,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T createReference(Boolean readOnly, boolean disableLazyLoad, Object id, PersistenceContext pc) {
|
||||
|
||||
if (cacheSharableBeans && !disableLazyLoad && !Boolean.FALSE.equals(readOnly)) {
|
||||
CachedBeanData d = cacheHelp.beanCacheGetData(cacheKey(id));
|
||||
if (d != null) {
|
||||
@@ -2016,10 +1890,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
if (inheritInfo != null && !inheritInfo.isConcrete()) {
|
||||
return findReferenceBean(id, pc);
|
||||
}
|
||||
|
||||
EntityBean eb = createEntityBean();
|
||||
id = convertSetId(id, eb);
|
||||
|
||||
EntityBeanIntercept ebi = eb._ebean_getIntercept();
|
||||
if (disableLazyLoad) {
|
||||
ebi.setDisableLazyLoad(true);
|
||||
@@ -2034,9 +1906,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
contextPut(pc, id, eb);
|
||||
ebi.setPersistenceContext(pc);
|
||||
}
|
||||
|
||||
return (T) eb;
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new PersistenceException(ex);
|
||||
}
|
||||
@@ -2086,14 +1956,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
public BeanProperty findPropertyFromPath(String path) {
|
||||
BeanDescriptor<?> other = this;
|
||||
while (true) {
|
||||
|
||||
String[] split = SplitName.splitBegin(path);
|
||||
if (split[1] == null) {
|
||||
return other._findBeanProperty(split[0]);
|
||||
}
|
||||
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) other._findBeanProperty(split[0]);
|
||||
BeanDescriptor<?> targetDesc = assocProp.getTargetDescriptor();
|
||||
|
||||
path = split[1];
|
||||
other = targetDesc;
|
||||
}
|
||||
@@ -2114,13 +1982,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return result;
|
||||
}
|
||||
String[] splitBegin = SplitName.splitBegin(path);
|
||||
|
||||
BeanProperty beanProperty = result.findProperty(splitBegin[0]);
|
||||
if (beanProperty instanceof BeanPropertyAssoc<?>) {
|
||||
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProperty;
|
||||
path = splitBegin[1];
|
||||
result = assocProp.getTargetDescriptor();
|
||||
|
||||
} else {
|
||||
throw new PersistenceException("Invalid path " + path + " from " + result.getFullName());
|
||||
}
|
||||
@@ -2147,7 +2013,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* For bidirectional this is a real property on the bean. For unidirectional
|
||||
* relationships we have this 'shadow' property which is not externally
|
||||
* visible.
|
||||
* </p>
|
||||
*/
|
||||
public BeanPropertyAssocOne<?> getUnidirectional() {
|
||||
BeanDescriptor<?> other = this;
|
||||
@@ -2168,7 +2033,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* <p>
|
||||
* If this is false and the Id is null it is assumed that a database auto
|
||||
* increment feature is being used to populate the id.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isUseIdGenerator() {
|
||||
return idGenerator != null;
|
||||
@@ -2195,7 +2059,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* <p>
|
||||
* If this BeanDescriptor is for a table then this returns the table name
|
||||
* instead.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
@Nonnull
|
||||
@@ -2315,7 +2178,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Return the Id value for the bean with embeddedId beans converted into maps.
|
||||
* <p>
|
||||
* The usage is to provide simple id types for JSON processing (for embeddedId's).
|
||||
* </p>
|
||||
*/
|
||||
public Object getIdForJson(Object bean) {
|
||||
return idBinder.getIdForJson((EntityBean) bean);
|
||||
@@ -2325,7 +2187,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Convert the idValue assuming embeddedId values are Maps.
|
||||
* <p>
|
||||
* The usage is to provide simple id types for JSON processing (for embeddedId's).
|
||||
* </p>
|
||||
*/
|
||||
Object convertIdFromJson(Object idValue) {
|
||||
return idBinder.convertIdFromJson(idValue);
|
||||
@@ -2359,7 +2220,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* <p>
|
||||
* If the bean is not null, the id value is set to the id property of the bean
|
||||
* after it has been converted to the correct type.
|
||||
* </p>
|
||||
*/
|
||||
public Object convertSetId(Object idValue, EntityBean bean) {
|
||||
return idBinder.convertSetId(idValue, bean);
|
||||
@@ -2385,9 +2245,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
|
||||
public void sort(List<T> list, String sortByClause) {
|
||||
|
||||
ElComparator<T> comparator = getElComparator(sortByClause);
|
||||
list.sort(comparator);
|
||||
list.sort(getElComparator(sortByClause));
|
||||
}
|
||||
|
||||
public ElComparator<T> getElComparator(String propNameOrSortBy) {
|
||||
@@ -2398,10 +2256,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Register all the assoc many properties on this bean that are not populated with the load context.
|
||||
* <p>
|
||||
* This provides further lazy loading via the load context.
|
||||
* </p>
|
||||
*/
|
||||
public void lazyLoadRegister(String prefix, EntityBeanIntercept ebi, EntityBean bean, LoadContext loadContext) {
|
||||
|
||||
// load the List/Set/Map proxy objects (deferred fetching of lists)
|
||||
BeanPropertyAssocMany<?>[] manys = propertiesMany();
|
||||
for (BeanPropertyAssocMany<?> many : manys) {
|
||||
@@ -2420,12 +2276,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* define a Reference for the collection and not invoke a query.
|
||||
*/
|
||||
public boolean lazyLoadMany(EntityBeanIntercept ebi) {
|
||||
|
||||
int lazyLoadProperty = ebi.getLazyLoadPropertyIndex();
|
||||
if (lazyLoadProperty == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inheritInfo != null) {
|
||||
return descOf(ebi.getOwner().getClass()).lazyLoadMany(ebi, lazyLoadProperty);
|
||||
}
|
||||
@@ -2443,7 +2297,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Check for lazy loading of many property.
|
||||
*/
|
||||
private boolean lazyLoadMany(EntityBeanIntercept ebi, int lazyLoadProperty) {
|
||||
|
||||
BeanProperty lazyLoadBeanProp = propertiesIndex[lazyLoadProperty];
|
||||
if (lazyLoadBeanProp instanceof BeanPropertyAssocMany<?>) {
|
||||
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) lazyLoadBeanProp;
|
||||
@@ -2468,7 +2321,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private ElComparator<T> createComparator(String sortByClause) {
|
||||
|
||||
SortByClause sortBy = SortByClauseParser.parse(sortByClause);
|
||||
if (sortBy.size() == 1) {
|
||||
// simple comparator for a single property
|
||||
@@ -2477,24 +2329,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
// create a compound comparator based on the list of properties
|
||||
ElComparator<T>[] comparators = new ElComparator[sortBy.size()];
|
||||
|
||||
List<SortByClause.Property> sortProps = sortBy.getProperties();
|
||||
for (int i = 0; i < sortProps.size(); i++) {
|
||||
SortByClause.Property sortProperty = sortProps.get(i);
|
||||
comparators[i] = createPropertyComparator(sortProperty);
|
||||
}
|
||||
|
||||
return new ElComparatorCompound<>(comparators);
|
||||
}
|
||||
|
||||
private ElComparator<T> createPropertyComparator(SortByClause.Property sortProp) {
|
||||
|
||||
ElPropertyValue elGetValue = getElGetValue(sortProp.getName());
|
||||
if (elGetValue == null) {
|
||||
logger.error("Sort property [" + sortProp + "] not found in " + beanType + ". Cannot sort.");
|
||||
return new ElComparatorNoop<>();
|
||||
}
|
||||
|
||||
if (elGetValue.isAssocMany()) {
|
||||
logger.error("Sort property [" + sortProp + "] in " + beanType + " is a many-property. Cannot sort.");
|
||||
return new ElComparatorNoop<>();
|
||||
@@ -2539,7 +2387,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Similar to ElPropertyValue but also uses foreign key shortcuts.
|
||||
* <p>
|
||||
* The foreign key shortcuts means we can avoid unnecessary joins.
|
||||
* </p>
|
||||
*/
|
||||
public ElPropertyDeploy getElPropertyDeploy(String propName) {
|
||||
ElPropertyDeploy elProp = elDeployCache.get(propName);
|
||||
@@ -2560,7 +2407,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
|
||||
ElPropertyValue buildElGetValue(String propName, ElPropertyChainBuilder chain, boolean propertyDeploy) {
|
||||
|
||||
if (propertyDeploy && chain != null) {
|
||||
ElPropertyDeploy fk = elDeployCache.get(propName);
|
||||
if (fk instanceof BeanFkeyProperty) {
|
||||
@@ -2568,17 +2414,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return ((BeanFkeyProperty) fk).create(chain.getExpression(), chain.isContainsMany());
|
||||
}
|
||||
}
|
||||
|
||||
int basePos = propName.indexOf('.');
|
||||
if (basePos > -1) {
|
||||
// nested or embedded property
|
||||
String baseName = propName.substring(0, basePos);
|
||||
|
||||
BeanProperty assocProp = _findBeanProperty(baseName);
|
||||
if (assocProp == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String remainder = propName.substring(basePos + 1);
|
||||
return assocProp.buildElPropertyValue(propName, remainder, chain, propertyDeploy);
|
||||
}
|
||||
@@ -2639,7 +2482,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* <p>
|
||||
* This searches this BeanDescriptor and then searches further down the
|
||||
* inheritance tree (not up).
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public BeanProperty findProperty(String propName) {
|
||||
@@ -2666,11 +2508,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Reset the many properties to empty state ready for reloading.
|
||||
*/
|
||||
public void resetManyProperties(Object dbBean) {
|
||||
|
||||
EntityBean bean = (EntityBean) dbBean;
|
||||
for (BeanPropertyAssocMany<?> aPropertiesMany : propertiesMany) {
|
||||
if (aPropertiesMany.isCascadeRefresh()) {
|
||||
aPropertiesMany.resetMany(bean);
|
||||
for (BeanPropertyAssocMany<?> many : propertiesMany) {
|
||||
if (many.isCascadeRefresh()) {
|
||||
many.resetMany(bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2680,7 +2521,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* <p>
|
||||
* This means is has no relationships and has readOnly=true in its cache
|
||||
* options.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isCacheSharableBeans() {
|
||||
return cacheSharableBeans;
|
||||
@@ -2781,8 +2621,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* De-register the BeanPersistListener.
|
||||
*/
|
||||
public void deregister(BeanPersistListener listener) {
|
||||
|
||||
// volatile read...
|
||||
BeanPersistListener currentListener = persistListener;
|
||||
if (currentListener != null) {
|
||||
if (currentListener instanceof ChainedBeanPersistListener) {
|
||||
@@ -2798,8 +2636,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* De-register the BeanPersistController.
|
||||
*/
|
||||
public void deregister(BeanPersistController controller) {
|
||||
|
||||
// volatile read...
|
||||
BeanPersistController currentController = persistController;
|
||||
if (currentController != null) {
|
||||
if (currentController instanceof ChainedBeanPersistController) {
|
||||
@@ -2815,9 +2651,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Register the new BeanPersistController.
|
||||
*/
|
||||
public void register(BeanPersistListener newPersistListener) {
|
||||
|
||||
if (newPersistListener.isRegisterFor(beanType)) {
|
||||
// volatile read...
|
||||
BeanPersistListener currentListener = persistListener;
|
||||
if (currentListener == null) {
|
||||
persistListener = newPersistListener;
|
||||
@@ -2837,18 +2671,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Register the new BeanPersistController.
|
||||
*/
|
||||
public void register(BeanPersistController newController) {
|
||||
|
||||
if (newController.isRegisterFor(beanType)) {
|
||||
// volatile read...
|
||||
BeanPersistController currentController = persistController;
|
||||
if (currentController == null) {
|
||||
persistController = newController;
|
||||
} else {
|
||||
if (currentController instanceof ChainedBeanPersistController) {
|
||||
// add it to the existing chain
|
||||
persistController = ((ChainedBeanPersistController) currentController).register(newController);
|
||||
} else {
|
||||
// build new chain of the 2
|
||||
persistController = new ChainedBeanPersistController(currentController, newController);
|
||||
}
|
||||
}
|
||||
@@ -2901,17 +2731,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
/**
|
||||
* Return the dependent tables for a view based entity.
|
||||
* <p>
|
||||
* These tables
|
||||
* </p>
|
||||
*/
|
||||
public String[] getDependentTables() {
|
||||
return dependentTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table. Only properties mapped to the base table are by
|
||||
* default persisted.
|
||||
* Return the base table. Only properties mapped to the base table are by default persisted.
|
||||
*/
|
||||
@Override
|
||||
public String getBaseTable() {
|
||||
@@ -3012,7 +2838,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
@Override
|
||||
public ExtraJoin extraJoin(String propertyPath) {
|
||||
|
||||
ElPropertyValue elGetValue = getElGetValue(propertyPath);
|
||||
if (elGetValue != null) {
|
||||
BeanProperty beanProperty = elGetValue.getBeanProperty();
|
||||
@@ -3150,7 +2975,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* <p>
|
||||
* This is only used with Identity columns and getGeneratedKeys is not
|
||||
* supported.
|
||||
* </p>
|
||||
*/
|
||||
public String getSelectLastInsertedId(boolean publish) {
|
||||
return publish ? selectLastInsertedId : selectLastInsertedIdDraft;
|
||||
@@ -3198,8 +3022,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the beans that are embedded. These share the base table with the
|
||||
* owner bean.
|
||||
* Return the beans that are embedded. These share the base table with the owner bean.
|
||||
*/
|
||||
public BeanPropertyAssocOne<?>[] propertiesEmbedded() {
|
||||
return propertiesEmbedded;
|
||||
@@ -3252,12 +3075,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* @param insertMode true if the 'root request' was an insert rather than an update
|
||||
*/
|
||||
public boolean isInsertMode(EntityBeanIntercept ebi, boolean insertMode) {
|
||||
|
||||
if (ebi.isLoaded()) {
|
||||
// must be an update as the bean is loaded
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idProperty.isEmbedded()) {
|
||||
// not using Id generator so just base on isLoaded()
|
||||
return !ebi.isLoaded();
|
||||
@@ -3329,7 +3150,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
|
||||
public ConcurrencyMode getConcurrencyMode(EntityBeanIntercept ebi) {
|
||||
|
||||
if (!hasVersionProperty(ebi)) {
|
||||
return ConcurrencyMode.NONE;
|
||||
} else {
|
||||
@@ -3350,15 +3170,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Populate the diff for updates with flattened non-null property values.
|
||||
*/
|
||||
public void diff(String prefix, Map<String, ValuePair> map, EntityBean newBean, EntityBean oldBean) {
|
||||
|
||||
for (BeanProperty aPropertiesBaseScalar : propertiesBaseScalar) {
|
||||
aPropertiesBaseScalar.diff(prefix, map, newBean, oldBean);
|
||||
for (BeanProperty baseScalar : propertiesBaseScalar) {
|
||||
baseScalar.diff(prefix, map, newBean, oldBean);
|
||||
}
|
||||
for (BeanPropertyAssocOne<?> aPropertiesOne : propertiesOne) {
|
||||
aPropertiesOne.diff(prefix, map, newBean, oldBean);
|
||||
for (BeanPropertyAssocOne<?> one : propertiesOne) {
|
||||
one.diff(prefix, map, newBean, oldBean);
|
||||
}
|
||||
for (BeanPropertyAssocOne<?> aPropertiesEmbedded : propertiesEmbedded) {
|
||||
aPropertiesEmbedded.diff(prefix, map, newBean, oldBean);
|
||||
for (BeanPropertyAssocOne<?> embedded : propertiesEmbedded) {
|
||||
embedded.diff(prefix, map, newBean, oldBean);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3368,10 +3187,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* <p>
|
||||
* This is primarily used for paging queries to ensure that an order by clause is provided and that the order by
|
||||
* provides unique ordering of the rows (so that the paging is predicable).
|
||||
* </p>
|
||||
*/
|
||||
public void appendOrderById(SpiQuery<T> query) {
|
||||
|
||||
if (idProperty != null && !idProperty.isEmbedded() && !query.order().containsProperty(idProperty.getName())) {
|
||||
query.order().asc(idProperty.getName());
|
||||
}
|
||||
@@ -3409,7 +3226,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Returns ManyToOnes and OneToOnes on the imported owning side.
|
||||
* <p>
|
||||
* Excludes OneToOnes on the exported side.
|
||||
* </p>
|
||||
*/
|
||||
public BeanPropertyAssocOne<?>[] propertiesOneImported() {
|
||||
return propertiesOneImported;
|
||||
@@ -3483,7 +3299,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* no version property exists on the bean.
|
||||
* <p>
|
||||
* Note that this DOES NOT find a version property on an embedded bean.
|
||||
* </p>
|
||||
*/
|
||||
public BeanProperty getVersionProperty() {
|
||||
return versionProperty;
|
||||
|
||||
@@ -44,9 +44,9 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanTable;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployIdentityMode;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployIdentityMode;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployCreateProperties;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployInherit;
|
||||
@@ -245,7 +245,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Run periodic trim of query plans.
|
||||
*/
|
||||
public void scheduleBackgroundTrim() {
|
||||
backgroundExecutor.executePeriodically(this::trimQueryPlans, 60L, TimeUnit.SECONDS);
|
||||
backgroundExecutor.executePeriodically(this::trimQueryPlans, 117L, 60L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void trimQueryPlans() {
|
||||
@@ -357,7 +357,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Deploy returning the asOfTableMap (which is required by the SQL builders).
|
||||
*/
|
||||
public Map<String, String> deploy(List<XmEbean> mappings) {
|
||||
|
||||
try {
|
||||
createListeners();
|
||||
readEntityDeploymentInitial();
|
||||
@@ -365,7 +364,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
readEntityBeanTable();
|
||||
readEntityDeploymentAssociations();
|
||||
readInheritedIdGenerators();
|
||||
setProfileIds();
|
||||
// creates the BeanDescriptors
|
||||
readEntityRelationships();
|
||||
|
||||
@@ -375,9 +373,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
initialiseAll();
|
||||
readForeignKeys();
|
||||
|
||||
readTableToDescriptor();
|
||||
|
||||
logStatus();
|
||||
|
||||
// clear collections we no longer need
|
||||
@@ -778,18 +774,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set profileIds based on descriptor full name order.
|
||||
*/
|
||||
private void setProfileIds() {
|
||||
|
||||
List<? extends DeployBeanDescriptor<?>> deployDescriptors = deployInfoMap.values().stream()
|
||||
.map(DeployBeanInfo::getDescriptor)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
deployDescriptors.sort(Comparator.comparing(DeployBeanDescriptor::getFullName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the BeanTable from the deployment information gathered so far.
|
||||
*/
|
||||
@@ -1004,6 +988,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
orderProperty.setDbRead(true);
|
||||
orderProperty.setOwningType(targetDesc.getBeanType());
|
||||
|
||||
final InheritInfo targetInheritInfo = targetDesc.getInheritInfo();
|
||||
if (targetInheritInfo != null) {
|
||||
for (InheritInfo child : targetInheritInfo.getChildren()) {
|
||||
final DeployBeanDescriptor<?> childDescriptor = deployInfoMap.get(child.getType()).getDescriptor();
|
||||
childDescriptor.setOrderColumn(orderProperty);
|
||||
}
|
||||
}
|
||||
|
||||
targetDesc.setOrderColumn(orderProperty);
|
||||
}
|
||||
|
||||
|
||||
@@ -542,12 +542,11 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
return dbEncryptFunction.getDecryptSql(propertyName);
|
||||
}
|
||||
|
||||
public String getDecryptSql() {
|
||||
return dbEncryptFunction.getDecryptSql(this.getDbColumn());
|
||||
}
|
||||
|
||||
private String getDecryptSql(String tableAlias) {
|
||||
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn());
|
||||
/**
|
||||
* Return the SQL for the column including decryption function and column alias.
|
||||
*/
|
||||
private String getDecryptSqlWithColumnAlias(String tableAlias) {
|
||||
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()) + " _e_" + tableAlias + "_" + this.getDbColumn();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -600,8 +599,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
}
|
||||
|
||||
if (dbEncrypted) {
|
||||
String decryptSql = getDecryptSql(ctx.peekTableAlias());
|
||||
ctx.appendRawColumn(decryptSql);
|
||||
ctx.appendRawColumn(getDecryptSqlWithColumnAlias(ctx.peekTableAlias()));
|
||||
ctx.addEncryptedProp(this);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -36,7 +36,6 @@ public final class IdBinderSimple implements IdBinder {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final ScalarType scalarType;
|
||||
|
||||
|
||||
public IdBinderSimple(BeanProperty idProperty, MultiValueBind multiValueBind) {
|
||||
this.idProperty = idProperty;
|
||||
this.scalarType = idProperty.getScalarType();
|
||||
@@ -71,7 +70,6 @@ public final class IdBinderSimple implements IdBinder {
|
||||
|
||||
@Override
|
||||
public void buildRawSqlSelectChain(String prefix, List<String> selectChain) {
|
||||
|
||||
idProperty.buildRawSqlSelectChain(prefix, selectChain);
|
||||
}
|
||||
|
||||
@@ -218,7 +216,6 @@ public final class IdBinderSimple implements IdBinder {
|
||||
|
||||
@Override
|
||||
public String getAssocOneIdExpr(String prefix, String operator) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (prefix != null) {
|
||||
sb.append(prefix);
|
||||
@@ -231,7 +228,6 @@ public final class IdBinderSimple implements IdBinder {
|
||||
|
||||
@Override
|
||||
public String getAssocIdInExpr(String prefix) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (prefix != null) {
|
||||
sb.append(prefix);
|
||||
@@ -243,7 +239,6 @@ public final class IdBinderSimple implements IdBinder {
|
||||
|
||||
@Override
|
||||
public Object convertId(Object idValue) {
|
||||
|
||||
if (!idValue.getClass().equals(expectedType)) {
|
||||
return scalarType.toBeanType(idValue);
|
||||
}
|
||||
@@ -252,12 +247,10 @@ public final class IdBinderSimple implements IdBinder {
|
||||
|
||||
@Override
|
||||
public Object convertSetId(Object idValue, EntityBean bean) {
|
||||
|
||||
if (!idValue.getClass().equals(expectedType)) {
|
||||
idValue = scalarType.toBeanType(idValue);
|
||||
}
|
||||
if (bean != null) {
|
||||
// support PropertyChangeSupport
|
||||
idProperty.setValueIntercept(bean, idValue);
|
||||
}
|
||||
return idValue;
|
||||
|
||||
@@ -50,4 +50,45 @@ public class DtoMappingRequest {
|
||||
public QueryPlanMetric createMetric() {
|
||||
return MetricFactory.get().createQueryPlanMetric(type, label, profileLocation, sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map all DB columns to setters.
|
||||
*/
|
||||
DtoReadSet[] mapSetters(DtoMeta meta) {
|
||||
DtoReadSet[] setterProps = new DtoReadSet[columnMeta.length];
|
||||
for (int i = 0; i < columnMeta.length; i++) {
|
||||
setterProps[i] = mapColumn(i, meta);
|
||||
}
|
||||
return setterProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map DB columns after constructor to setters.
|
||||
*/
|
||||
DtoReadSet[] mapArgPlusSetters(DtoMeta meta, int firstOnes) {
|
||||
DtoReadSet[] setterProps = new DtoReadSet[columnMeta.length - firstOnes];
|
||||
int pos = 0;
|
||||
for (int i = firstOnes; i < columnMeta.length; i++) {
|
||||
setterProps[pos++] = mapColumn(i, meta);
|
||||
}
|
||||
return setterProps;
|
||||
}
|
||||
|
||||
private DtoReadSet mapColumn(int pos, DtoMeta meta) {
|
||||
String label = columnMeta[pos].getLabel();
|
||||
DtoReadSet property = meta.findProperty(label);
|
||||
if (property == null || property.isReadOnly()) {
|
||||
if (isRelaxedMode()) {
|
||||
property = DtoReadSetColumnSkip.INSTANCE;
|
||||
} else {
|
||||
throw new IllegalStateException(unableToMapColumnMessage(columnMeta[pos], meta));
|
||||
}
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
private String unableToMapColumnMessage(DtoColumn col, DtoMeta meta) {
|
||||
return "Unable to map DB column " + col + " to a property with a setter method on " + meta.dtoType()+". Consider query.setRelaxedMode() to skip mapping this column.";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,55 +72,16 @@ class DtoMeta {
|
||||
}
|
||||
|
||||
private DtoQueryPlanConPlus matchMaxArgPlusSetters(DtoMappingRequest request) {
|
||||
|
||||
|
||||
int firstOnes = maxArgConstructor.getArgCount();
|
||||
|
||||
DtoColumn[] cols = request.getColumnMeta();
|
||||
|
||||
DtoReadSet[] setterProps = new DtoReadSet[cols.length - firstOnes];
|
||||
|
||||
int pos = 0;
|
||||
for (int i = firstOnes; i < cols.length; i++) {
|
||||
String label = cols[i].getLabel();
|
||||
DtoReadSet property = findProperty(label);
|
||||
if (property == null || property.isReadOnly()) {
|
||||
if (request.isRelaxedMode()) {
|
||||
property = DtoReadSetColumnSkip.INSTANCE;
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to map DB column " + cols[i] + " to a property with a setter method on " + dtoType);
|
||||
}
|
||||
}
|
||||
setterProps[pos++] = property;
|
||||
}
|
||||
|
||||
DtoReadSet[] setterProps = request.mapArgPlusSetters(this, maxArgConstructor.getArgCount());
|
||||
return new DtoQueryPlanConPlus(request, maxArgConstructor, setterProps);
|
||||
}
|
||||
|
||||
private DtoQueryPlan matchSetters(DtoMappingRequest request) {
|
||||
|
||||
DtoColumn[] cols = request.getColumnMeta();
|
||||
|
||||
DtoReadSet[] setterProps = new DtoReadSet[cols.length];
|
||||
|
||||
for (int i = 0; i < cols.length; i++) {
|
||||
String label = cols[i].getLabel();
|
||||
DtoReadSet property = findProperty(label);
|
||||
if (property == null || property.isReadOnly()) {
|
||||
if (request.isRelaxedMode()) {
|
||||
property = DtoReadSetColumnSkip.INSTANCE;
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to map DB column " + cols[i] + " to a property with a setter method on " + dtoType);
|
||||
}
|
||||
}
|
||||
setterProps[i] = property;
|
||||
}
|
||||
|
||||
DtoReadSet[] setterProps = request.mapSetters(this);
|
||||
return new DtoQueryPlanConSetter(request, defaultConstructor, setterProps);
|
||||
}
|
||||
|
||||
private DtoReadSet findProperty(String label) {
|
||||
|
||||
DtoReadSet findProperty(String label) {
|
||||
String upperLabel = label.toUpperCase();
|
||||
DtoMetaProperty property = propMap.get(upperLabel);
|
||||
if (property == null && upperLabel.startsWith("IS_")) {
|
||||
@@ -131,4 +92,8 @@ class DtoMeta {
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
Class<?> dtoType() {
|
||||
return dtoType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,8 +346,9 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> orderBy(String orderBy) {
|
||||
return query.order(orderBy);
|
||||
public ExpressionList<T> orderBy(String orderBy) {
|
||||
query.order(orderBy);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -145,8 +145,9 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> orderBy(String orderBy) {
|
||||
return rootQuery.orderBy(orderBy);
|
||||
public ExpressionList<T> orderBy(String orderByClause) {
|
||||
this.orderByClause = orderByClause;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -104,16 +104,18 @@ public class IdInExpression extends NonPrepareExpression {
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
DefaultExpressionRequest r = (DefaultExpressionRequest) request;
|
||||
BeanDescriptor<?> descriptor = r.getBeanDescriptor();
|
||||
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
|
||||
IdBinder idBinder = descriptor.getIdBinder();
|
||||
if (idCollection.isEmpty()) {
|
||||
request.append(SQL_FALSE); // append false for this stage
|
||||
} else {
|
||||
request.append(descriptor.getIdBinderInLHSSql());
|
||||
String inClause = idBinder.getIdInValueExpr(false, idCollection.size());
|
||||
request.append(inClause);
|
||||
if (idBinder.isComplexId()) {
|
||||
request.append(descriptor.getIdBinderInLHSSql());
|
||||
request.append(idBinder.getIdInValueExpr(false, idCollection.size()));
|
||||
} else {
|
||||
request.append(idBinder.getBeanProperty().getName());
|
||||
request.appendInExpression(false, idCollection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -809,7 +809,7 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> orderBy(String orderBy) {
|
||||
public ExpressionList<T> orderBy(String orderBy) {
|
||||
return exprList.orderBy(orderBy);
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -18,6 +18,8 @@ public class DbExpressionHandlerFactory {
|
||||
case POSTGRES:
|
||||
case POSTGRES9:
|
||||
return new PostgresDbExpression();
|
||||
case MARIADB:
|
||||
return new MariaDbExpression();
|
||||
case MYSQL55:
|
||||
case MYSQL:
|
||||
return new MySqlDbExpression();
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.ebeaninternal.server.expression.platform;
|
||||
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.expression.Op;
|
||||
|
||||
/**
|
||||
* MariaDB specific handling of platform specific expressions.
|
||||
*/
|
||||
class MariaDbExpression extends BasicDbExpression {
|
||||
|
||||
@Override
|
||||
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
|
||||
request.append("(").append(propName).append(" ->> '$.").append(path).append("')");
|
||||
request.append(operator.bind());
|
||||
}
|
||||
}
|
||||
@@ -175,7 +175,6 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
@Override
|
||||
public void loadBean(EntityBeanIntercept ebi) {
|
||||
// A synchronized (this) is effectively held by EntityBeanIntercept.loadBean()
|
||||
|
||||
if (context.desc.lazyLoadMany(ebi)) {
|
||||
// lazy load property was a Many
|
||||
return;
|
||||
@@ -183,7 +182,6 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
|
||||
if (context.hitCache) {
|
||||
Set<EntityBeanIntercept> hits = context.desc.cacheBeanLoadAll(list, persistenceContext, ebi.getLazyLoadPropertyIndex(), ebi.getLazyLoadProperty());
|
||||
|
||||
list.removeAll(hits);
|
||||
if (list.isEmpty() || hits.contains(ebi)) {
|
||||
// successfully hit the L2 cache so don't invoke DB lazy loading
|
||||
@@ -193,6 +191,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
|
||||
LoadBeanRequest req = new LoadBeanRequest(this, ebi, context.hitCache);
|
||||
context.desc.getEbeanServer().loadBean(req);
|
||||
list.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,6 @@ public class DLoadContext implements LoadContext {
|
||||
* Construct for use with JSON marshalling (doc store).
|
||||
*/
|
||||
public DLoadContext(BeanDescriptor<?> rootDescriptor, PersistenceContext persistenceContext) {
|
||||
|
||||
this.useDocStore = true;
|
||||
this.rootDescriptor = rootDescriptor;
|
||||
this.ebeanServer = rootDescriptor.getEbeanServer();
|
||||
@@ -100,7 +99,6 @@ public class DLoadContext implements LoadContext {
|
||||
}
|
||||
|
||||
public DLoadContext(OrmQueryRequest<?> request, SpiQuerySecondary secondaryQueries) {
|
||||
|
||||
this.tenantId = request.getTenantId();
|
||||
this.persistenceContext = request.getPersistenceContext();
|
||||
this.ebeanServer = request.getServer();
|
||||
@@ -131,7 +129,6 @@ public class DLoadContext implements LoadContext {
|
||||
|
||||
// initialise rootBeanContext after origin and relativePath have been set
|
||||
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
|
||||
|
||||
registerSecondaryQueries(secondaryQueries);
|
||||
}
|
||||
|
||||
@@ -153,14 +150,12 @@ public class DLoadContext implements LoadContext {
|
||||
* Register the +query and +lazy secondary queries with their appropriate LoadBeanContext or LoadManyContext.
|
||||
*/
|
||||
private void registerSecondaryQueries(SpiQuerySecondary secondaryQueries) {
|
||||
|
||||
this.secQuery = secondaryQueries.getQueryJoins();
|
||||
if (secQuery != null) {
|
||||
for (OrmQueryProperties pathProperties : secQuery) {
|
||||
registerSecondaryQuery(pathProperties);
|
||||
}
|
||||
}
|
||||
|
||||
List<OrmQueryProperties> lazyJoins = secondaryQueries.getLazyJoins();
|
||||
if (lazyJoins != null) {
|
||||
for (OrmQueryProperties lazyJoin : lazyJoins) {
|
||||
@@ -174,9 +169,7 @@ public class DLoadContext implements LoadContext {
|
||||
* used to build the appropriate query for +query or +lazy loading.
|
||||
*/
|
||||
private void registerSecondaryQuery(OrmQueryProperties props) {
|
||||
|
||||
ElPropertyValue elGetValue = rootDescriptor.getElGetValue(props.getPath());
|
||||
|
||||
boolean many = elGetValue.getBeanProperty().containsMany();
|
||||
registerSecondaryNode(many, props);
|
||||
}
|
||||
@@ -190,11 +183,9 @@ public class DLoadContext implements LoadContext {
|
||||
*/
|
||||
@Override
|
||||
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
|
||||
|
||||
if (secQuery == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int maxBatch = 0;
|
||||
for (OrmQueryProperties aSecQuery : secQuery) {
|
||||
int batchSize = aSecQuery.getQueryFetchBatch();
|
||||
@@ -211,7 +202,6 @@ public class DLoadContext implements LoadContext {
|
||||
*/
|
||||
@Override
|
||||
public void executeSecondaryQueries(OrmQueryRequest<?> parentRequest, boolean forEach) {
|
||||
|
||||
if (secQuery != null) {
|
||||
for (OrmQueryProperties aSecQuery : secQuery) {
|
||||
LoadSecondaryQuery load = getLoadSecondaryQuery(aSecQuery.getPath());
|
||||
@@ -234,7 +224,6 @@ public class DLoadContext implements LoadContext {
|
||||
}
|
||||
|
||||
private ObjectGraphNode createObjectGraphNode(String path) {
|
||||
|
||||
if (relativePath != null) {
|
||||
if (path == null) {
|
||||
path = relativePath;
|
||||
@@ -311,7 +300,6 @@ public class DLoadContext implements LoadContext {
|
||||
}
|
||||
|
||||
private void registerSecondaryNode(boolean many, OrmQueryProperties props) {
|
||||
|
||||
int batchSize;
|
||||
if (props.isQueryFetch()) {
|
||||
batchSize = 100;
|
||||
@@ -319,7 +307,6 @@ public class DLoadContext implements LoadContext {
|
||||
int lazyJoinBatch = props.getLazyFetchBatch();
|
||||
batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize;
|
||||
}
|
||||
|
||||
String path = props.getPath();
|
||||
if (many) {
|
||||
manyMap.put(path, createManyContext(path, batchSize, props));
|
||||
@@ -336,7 +323,6 @@ public class DLoadContext implements LoadContext {
|
||||
}
|
||||
|
||||
private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) {
|
||||
|
||||
BeanPropertyAssocMany<?> p = (BeanPropertyAssocMany<?>) getBeanProperty(rootDescriptor, path);
|
||||
return new DLoadManyContext(this, p, path, batchSize, queryProps);
|
||||
}
|
||||
|
||||
@@ -53,8 +53,9 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
|
||||
/**
|
||||
* Create with a given statement.
|
||||
*/
|
||||
public BatchedPstmt(PreparedStatement pstmt, boolean isGenKeys, String sql, SpiTransaction transaction) {
|
||||
public BatchedPstmt(PreparedStatement pstmt, boolean isGenKeys, String sql, SpiTransaction transaction) throws SQLException {
|
||||
this.pstmt = pstmt;
|
||||
this.pstmt.clearBatch();
|
||||
this.isGenKeys = isGenKeys;
|
||||
this.sql = sql;
|
||||
this.transaction = transaction;
|
||||
@@ -92,7 +93,7 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
|
||||
private void flushStatementBatch() throws SQLException {
|
||||
final int[] rows = pstmt.executeBatch();
|
||||
if (rows.length != list.size()) {
|
||||
throw new IllegalStateException("Invalid state? rows:" + rows.length + " != " + list.size());
|
||||
throw new IllegalStateException("Invalid state on executeBatch, rows:" + rows.length + " != " + list.size());
|
||||
}
|
||||
for (BatchPostExecute item : list) {
|
||||
item.postExecute();
|
||||
@@ -156,8 +157,7 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
|
||||
try {
|
||||
results = pstmt.executeBatch();
|
||||
if (results.length != list.size()) {
|
||||
String s = "results array error " + results.length + " " + list.size();
|
||||
throw new SQLException(s);
|
||||
throw new SQLException("Invalid state on executeBatch, rows:" + results.length + " != " + list.size());
|
||||
}
|
||||
// check for concurrency exceptions...
|
||||
for (int i = 0; i < results.length; i++) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
|
||||
@@ -68,10 +69,9 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
resetModifyState();
|
||||
}
|
||||
} else {
|
||||
if (isModifyListenMode()) {
|
||||
// delete any removed beans via private owned. Needs to occur before
|
||||
// a 'deleteMissingChildren' statement occurs
|
||||
removeAssocManyPrivateOwned();
|
||||
if (isModifyListenMode() || many.hasOrderColumn()) {
|
||||
// delete any removed beans / orphans
|
||||
removeAssocManyOrphans();
|
||||
}
|
||||
if (cascade) {
|
||||
// potentially deletes 'missing children' for 'stateless update'
|
||||
@@ -108,7 +108,7 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
BeanProperty orderColumn = null;
|
||||
boolean hasOrderColumn = many.hasOrderColumn();
|
||||
if (hasOrderColumn) {
|
||||
if (!insertedParent && canSkipForOrderColumn()) {
|
||||
if (!insertedParent && canSkipForOrderColumn() && saveRecurseSkippable) {
|
||||
return;
|
||||
}
|
||||
orderColumn = targetDescriptor.getOrderColumn();
|
||||
@@ -157,7 +157,7 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
if (many.hasJoinTable()) {
|
||||
skipSavingThisBean = targetDescriptor.isReference(ebi);
|
||||
} else {
|
||||
if (orderColumn != null) {
|
||||
if (orderColumn != null && !Objects.equals(sortOrder, orderColumn.getValue(detail))) {
|
||||
orderColumn.setValue(detail, sortOrder);
|
||||
ebi.setDirty(true);
|
||||
}
|
||||
@@ -178,6 +178,11 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
|
||||
if (!skipSavingThisBean) {
|
||||
persister.saveRecurse(detail, transaction, parentBean, request.getFlags());
|
||||
if (many.hasOrderColumn()) {
|
||||
// Clear the bean from the PersistenceContext (L1 cache), because the order of referenced beans might have changed
|
||||
final BeanDescriptor<?> beanDescriptor = many.getBeanDescriptor();
|
||||
beanDescriptor.contextClear(transaction.getPersistenceContext(), beanDescriptor.getId(parentBean));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,20 +339,25 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
transaction.depth(-1);
|
||||
}
|
||||
|
||||
private void removeAssocManyPrivateOwned() {
|
||||
|
||||
private void removeAssocManyOrphans() {
|
||||
// check that the list is not null and if it is a BeanCollection
|
||||
// check that is has been populated (don't trigger lazy loading)
|
||||
if (value instanceof BeanCollection<?>) {
|
||||
|
||||
BeanCollection<?> c = (BeanCollection<?>) value;
|
||||
Set<?> modifyRemovals = c.getModifyRemovals();
|
||||
modifyListenReset(c);
|
||||
if (insertedParent) {
|
||||
// after insert set the modify listening mode for private owned etc
|
||||
c.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
// We must not reset when we still have to update other entities in the collection and set their new orderColumn value
|
||||
if (!many.hasOrderColumn()) {
|
||||
c.modifyReset();
|
||||
}
|
||||
if (modifyRemovals != null && !modifyRemovals.isEmpty()) {
|
||||
for (Object removedBean : modifyRemovals) {
|
||||
if (removedBean instanceof EntityBean) {
|
||||
EntityBean eb = (EntityBean) removedBean;
|
||||
if (!eb._ebean_getIntercept().isNew()) {
|
||||
if (eb._ebean_intercept().isOrphanDelete()) {
|
||||
// only delete if the bean was loaded meaning that it is known to exist in the DB
|
||||
persister.deleteRequest(persister.createDeleteRemoved(removedBean, transaction, request.getFlags()));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ import io.ebeaninternal.server.persist.dmlbind.BindableList;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import static io.ebean.annotation.Platform.MARIADB;
|
||||
import static io.ebean.annotation.Platform.MYSQL;
|
||||
|
||||
/**
|
||||
* Meta data for insert handler. The meta data is for a particular bean type. It
|
||||
* is considered immutable and is thread safe.
|
||||
@@ -166,12 +169,7 @@ final class InsertMeta {
|
||||
|
||||
request.append("insert into ").append(table);
|
||||
if (nullId && noColumnsForInsert(draftTable)) {
|
||||
if (this.platform.base() == Platform.MYSQL) {
|
||||
request.append(" values (default)");
|
||||
} else {
|
||||
request.append(" default values");
|
||||
}
|
||||
return request.toString();
|
||||
return request.append(defaultValues()).toString();
|
||||
}
|
||||
|
||||
request.append(" (");
|
||||
@@ -199,6 +197,10 @@ final class InsertMeta {
|
||||
return request.toString();
|
||||
}
|
||||
|
||||
private String defaultValues() {
|
||||
return platform.base() == MYSQL || platform.base() == MARIADB ? " values (default)" : " default values";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the insert actually contains no columns.
|
||||
*/
|
||||
|
||||
@@ -608,12 +608,10 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
}
|
||||
|
||||
QueryIterator<T> readIterate(int bufferSize, OrmQueryRequest<T> request) {
|
||||
|
||||
if (bufferSize > 0) {
|
||||
return new CQueryIteratorWithBuffer<>(this, request, bufferSize);
|
||||
|
||||
} else {
|
||||
if (bufferSize == 1) {
|
||||
return new CQueryIteratorSimple<>(this, request);
|
||||
} else {
|
||||
return new CQueryIteratorWithBuffer<>(this, request, bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ class CQueryBuilder {
|
||||
|
||||
SpiQuery<T> query = request.getQuery();
|
||||
String rootTableAlias = query.getAlias();
|
||||
query.setupForDeleteOrUpdate(deleteRequest);
|
||||
query.setupForDeleteOrUpdate();
|
||||
|
||||
CQueryPredicates predicates = new CQueryPredicates(binder, request);
|
||||
CQueryPlan queryPlan = request.getQueryPlan();
|
||||
@@ -125,7 +125,7 @@ class CQueryBuilder {
|
||||
private <T> String buildDeleteSql(OrmQueryRequest<T> request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) {
|
||||
|
||||
String alias = alias(rootTableAlias);
|
||||
if (sqlTree.noJoins()) {
|
||||
if (sqlTree.noJoins() && !request.getQuery().hasMaxRowsOrFirstRow()) {
|
||||
if (dbPlatform.isSupportsDeleteTableAlias()) {
|
||||
// delete from table <alias> ...
|
||||
return aliasReplace(buildSqlDelete("delete", request, predicates, sqlTree).getSql(), alias);
|
||||
|
||||
@@ -201,26 +201,20 @@ public class CQueryEngine {
|
||||
* when you have finished).
|
||||
*/
|
||||
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
|
||||
|
||||
CQuery<T> cquery = queryBuilder.buildQuery(request);
|
||||
request.setCancelableQuery(cquery);
|
||||
try {
|
||||
if (defaultFetchSizeFindEach > 0) {
|
||||
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
|
||||
}
|
||||
if (request.isIterateSingleContext()) {
|
||||
// expected relatively small number of results, single persistence context
|
||||
cquery.prepareBindExecuteQuery();
|
||||
} else if (!cquery.prepareBindExecuteQueryForwardOnly(forwardOnlyHintOnFindIterate)) {
|
||||
if (!cquery.prepareBindExecuteQueryForwardOnly(forwardOnlyHintOnFindIterate)) {
|
||||
// query has been cancelled already
|
||||
logger.trace("Future fetch already cancelled");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (request.isLogSql()) {
|
||||
logSql(cquery);
|
||||
}
|
||||
|
||||
// first check batch sizes set on query joins
|
||||
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize);
|
||||
if (iterateBufferSize < 1) {
|
||||
@@ -228,20 +222,19 @@ public class CQueryEngine {
|
||||
int queryBatch = request.getQuery().getLazyLoadBatchSize();
|
||||
if (queryBatch > 0) {
|
||||
iterateBufferSize = queryBatch;
|
||||
} else {
|
||||
iterateBufferSize = 100;
|
||||
}
|
||||
}
|
||||
|
||||
QueryIterator<T> readIterate = cquery.readIterate(iterateBufferSize, request);
|
||||
|
||||
if (request.isLogSummary()) {
|
||||
logFindManySummary(cquery);
|
||||
}
|
||||
|
||||
if (request.isAuditReads()) {
|
||||
// indicates we need to audit as the iterator progresses
|
||||
cquery.auditFindIterate();
|
||||
}
|
||||
|
||||
return readIterate;
|
||||
|
||||
} catch (SQLException e) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* A QueryIterator that uses a buffer to execute secondary queries periodically.
|
||||
@@ -17,6 +18,7 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
|
||||
private final OrmQueryRequest<T> request;
|
||||
private final ArrayList<T> buffer;
|
||||
|
||||
private boolean closed;
|
||||
private boolean moreToLoad = true;
|
||||
|
||||
CQueryIteratorWithBuffer(CQuery<T> cquery, OrmQueryRequest<T> request, int bufferSize) {
|
||||
@@ -29,6 +31,7 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean hasNext() {
|
||||
boolean ret = false;
|
||||
try {
|
||||
if (buffer.isEmpty() && moreToLoad) {
|
||||
// load buffer
|
||||
@@ -44,23 +47,33 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
|
||||
}
|
||||
request.executeSecondaryQueries(true);
|
||||
}
|
||||
return !buffer.isEmpty();
|
||||
|
||||
ret = !buffer.isEmpty();
|
||||
return ret;
|
||||
} catch (SQLException e) {
|
||||
throw cquery.createPersistenceException(e);
|
||||
} finally {
|
||||
if (!ret) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (buffer.isEmpty()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return buffer.remove(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
cquery.updateExecutionStatisticsIterator();
|
||||
cquery.close();
|
||||
request.endTransIfRequired();
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
cquery.updateExecutionStatisticsIterator();
|
||||
cquery.close();
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -328,11 +328,9 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendRawColumn(String rawcolumnWithTableAlias) {
|
||||
public void appendRawColumn(String rawColumnWithAlias) {
|
||||
sb.append(COMMA);
|
||||
sb.append(rawcolumnWithTableAlias);
|
||||
|
||||
appendColumnAlias();
|
||||
sb.append(rawColumnWithAlias);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -133,7 +133,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
|
||||
|
||||
SpiQuery<T> query = request.getQuery();
|
||||
|
||||
if (request.isBeanCachePut()) {
|
||||
if (request.isBeanCachePutMany()) {
|
||||
// load the individual beans into the bean cache
|
||||
BeanDescriptor<T> descriptor = request.getBeanDescriptor();
|
||||
Collection<T> c = result.getActualDetails();
|
||||
|
||||
@@ -684,13 +684,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
* Setup to be a delete or update query.
|
||||
*/
|
||||
@Override
|
||||
public void setupForDeleteOrUpdate(boolean deleteRequest) {
|
||||
// unset any paging and select on the id in the case where the query
|
||||
// includes joins and we use - delete ... where id in (...)
|
||||
if (deleteRequest) {
|
||||
maxRows = 0;
|
||||
}
|
||||
firstRow = 0;
|
||||
public void setupForDeleteOrUpdate() {
|
||||
forUpdate = null;
|
||||
rootTableAlias = "${RTA}"; // alias we remove later
|
||||
setSelectId();
|
||||
@@ -1721,8 +1715,10 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setManualId(boolean manualId) {
|
||||
this.manualId = manualId;
|
||||
public void setManualId() {
|
||||
if (detail != null && detail.hasSelectClause()) {
|
||||
this.manualId = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* AutoCommit friendly Transaction.
|
||||
* <p>
|
||||
* Skips actual commit and rollback as these are performed automatically.
|
||||
*/
|
||||
public class AutoCommitJdbcTransaction extends JdbcTransaction {
|
||||
|
||||
public AutoCommitJdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
|
||||
super(id, explicit, connection, manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkAutoCommit(Connection connection) {
|
||||
// do nothing as autoCommit
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performRollback() {
|
||||
// do nothing as autoCommit
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performCommit() {
|
||||
// do nothing as autoCommit
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* AutoCommit based TransactionManager.
|
||||
* <p>
|
||||
* Intended to be used if when autoCommit mode is desired.
|
||||
*/
|
||||
public class AutoCommitTransactionManager extends TransactionManager {
|
||||
|
||||
public AutoCommitTransactionManager(TransactionManagerOptions options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an autoCommit based Transaction.
|
||||
*/
|
||||
@Override
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c) {
|
||||
return new AutoCommitJdbcTransaction(nextTxnId(), explicit, c, this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,18 +34,62 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
|
||||
private final Monitor monitor = new Monitor();
|
||||
|
||||
private int putCount;
|
||||
|
||||
/**
|
||||
* Create a new PersistenceContext.
|
||||
*/
|
||||
public DefaultPersistenceContext() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create as a shallow copy with initial or types that have not been added to.
|
||||
*/
|
||||
private DefaultPersistenceContext(DefaultPersistenceContext parent, boolean initial) {
|
||||
for (Map.Entry<Class<?>, ClassContext> entry : parent.typeCache.entrySet()) {
|
||||
typeCache.put(entry.getKey(), entry.getValue().copy(initial));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the initial shallow copy with each ClassContext noting it's initialSize (to detect additions).
|
||||
*/
|
||||
@Override
|
||||
public PersistenceContext forIterate() {
|
||||
return new DefaultPersistenceContext(this, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a shallow copy including each ClassContext that has had no additions (still at initialSize).
|
||||
*/
|
||||
@Override
|
||||
public PersistenceContext forIterateReset() {
|
||||
return new DefaultPersistenceContext(this, false);
|
||||
}
|
||||
|
||||
public boolean resetLimit() {
|
||||
synchronized (monitor) {
|
||||
if (putCount < 100) {
|
||||
return false;
|
||||
}
|
||||
putCount = 0;
|
||||
for (ClassContext value : typeCache.values()) {
|
||||
if (value.resetLimit()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// checking after another 100 puts
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object into the PersistenceContext.
|
||||
*/
|
||||
@Override
|
||||
public void put(Class<?> rootType, Object id, Object bean) {
|
||||
synchronized (monitor) {
|
||||
putCount++;
|
||||
getClassContext(rootType).put(id, bean);
|
||||
}
|
||||
}
|
||||
@@ -53,6 +97,7 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
@Override
|
||||
public Object putIfAbsent(Class<?> rootType, Object id, Object bean) {
|
||||
synchronized (monitor) {
|
||||
putCount++;
|
||||
return getClassContext(rootType).putIfAbsent(id, bean);
|
||||
}
|
||||
}
|
||||
@@ -133,7 +178,6 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
}
|
||||
|
||||
private ClassContext getClassContext(Class<?> rootType) {
|
||||
|
||||
return typeCache.computeIfAbsent(rootType, k -> new ClassContext());
|
||||
}
|
||||
|
||||
@@ -143,29 +187,72 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
|
||||
private Set<Object> deleteSet;
|
||||
|
||||
private int initialSize;
|
||||
|
||||
private ClassContext parent;
|
||||
|
||||
private ClassContext() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create as a shallow copy.
|
||||
*/
|
||||
private ClassContext(ClassContext source, boolean initial) {
|
||||
if (initial || source.isTransfer()) {
|
||||
parent = source.transferParent();
|
||||
initialSize = parent.size();
|
||||
if (source.deleteSet != null) {
|
||||
deleteSet = new HashSet<>(source.deleteSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* True if this should be transferred to a new iterator persistence context.
|
||||
*/
|
||||
private boolean isTransfer() {
|
||||
// map not added to and has some original/parent beans
|
||||
return map.isEmpty() && initialSize > 0;
|
||||
}
|
||||
|
||||
private ClassContext transferParent() {
|
||||
return (parent != null) ? parent : this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a shallow copy if initial copy or it has not grown (still at initialSize).
|
||||
*/
|
||||
private ClassContext copy(boolean initial) {
|
||||
return new ClassContext(this, initial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if grown above the reset limit size of 1000.
|
||||
*/
|
||||
private boolean resetLimit() {
|
||||
return map.size() > 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "size:" + map.size();
|
||||
}
|
||||
|
||||
private Object get(Object id) {
|
||||
Object bean = (parent == null) ? null : parent.get(id);
|
||||
return bean != null ? bean : map.get(id);
|
||||
}
|
||||
|
||||
private WithOption getWithOption(Object id) {
|
||||
if (deleteSet != null && deleteSet.contains(id)) {
|
||||
return WithOption.DELETED;
|
||||
}
|
||||
Object bean = map.get(id);
|
||||
Object bean = get(id);
|
||||
return (bean == null) ? null : new WithOption(bean);
|
||||
}
|
||||
|
||||
private Object get(Object id) {
|
||||
return map.get(id);
|
||||
}
|
||||
|
||||
private Object putIfAbsent(Object id, Object bean) {
|
||||
|
||||
Object existingValue = map.get(id);
|
||||
Object existingValue = get(id);
|
||||
if (existingValue != null) {
|
||||
// it is not absent
|
||||
return existingValue;
|
||||
@@ -180,7 +267,7 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
}
|
||||
|
||||
private int size() {
|
||||
return map.size();
|
||||
return map.size() + initialSize;
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
|
||||
@@ -24,7 +24,7 @@ public class DocStoreTransactionManager extends TransactionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
|
||||
return new DocStoreOnlyTransaction("", false, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* This only works for Postgres and H2 (and doesn't work for Oracle).
|
||||
* <p>
|
||||
* Uses explicit begin statement to start the transactions.
|
||||
*/
|
||||
public class ExplicitJdbcTransaction extends JdbcTransaction {
|
||||
|
||||
public ExplicitJdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
|
||||
super(id, explicit, connection, manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkAutoCommit(Connection connection) throws SQLException {
|
||||
// begin the transaction explicitly
|
||||
executeStatement("begin");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performRollback() throws SQLException {
|
||||
// Postgres needs this explicit rollback statement when used with AutoCommit=true
|
||||
executeStatement("rollback");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performCommit() throws SQLException {
|
||||
// Postgres needs this explicit commit statement when used with AutoCommit=true
|
||||
executeStatement("commit");
|
||||
}
|
||||
|
||||
private void executeStatement(String statement) throws SQLException {
|
||||
try (PreparedStatement stmt = connection.prepareStatement(statement)) {
|
||||
stmt.execute();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* TransactionManager where the transactions start with explicit "begin" statement.
|
||||
*/
|
||||
public class ExplicitTransactionManager extends TransactionManager {
|
||||
|
||||
public ExplicitTransactionManager(TransactionManagerOptions options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ExplicitJdbcTransaction.
|
||||
*/
|
||||
@Override
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c) {
|
||||
return new ExplicitJdbcTransaction(nextTxnId(), explicit, c, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the initialise of OnQueryOnly with the intention not to use CLOSE with ExplicitJdbcTransaction.
|
||||
*/
|
||||
@Override
|
||||
protected DatabasePlatform.OnQueryOnly initOnQueryOnly(DatabasePlatform.OnQueryOnly dbPlatformOnQueryOnly) {
|
||||
|
||||
// first check for a system property 'override'
|
||||
String systemPropertyValue = System.getProperty("ebean.transaction.onqueryonly");
|
||||
if (systemPropertyValue != null) {
|
||||
return DatabasePlatform.OnQueryOnly.valueOf(systemPropertyValue.trim().toUpperCase());
|
||||
}
|
||||
|
||||
// default to rollback if not defined on the platform
|
||||
return dbPlatformOnQueryOnly == null ? DatabasePlatform.OnQueryOnly.ROLLBACK : dbPlatformOnQueryOnly;
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
|
||||
private final long startNanos;
|
||||
|
||||
private ProfileLocation profileLocation;
|
||||
|
||||
/**
|
||||
* Create without a tenantId.
|
||||
*/
|
||||
@@ -125,12 +127,12 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
|
||||
@Override
|
||||
public void setProfileLocation(ProfileLocation profileLocation) {
|
||||
throw new IllegalStateException(notExpectedMessage);
|
||||
this.profileLocation = profileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return null;
|
||||
return profileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -138,6 +140,11 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCacheExplicit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkipCache(boolean skipCache) {
|
||||
}
|
||||
|
||||
@@ -293,6 +293,11 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCacheExplicit() {
|
||||
return (skipCache != null && !skipCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCache() {
|
||||
if (skipCache != null) return skipCache;
|
||||
|
||||
@@ -200,6 +200,11 @@ class NoTransaction implements SpiTransaction {
|
||||
public void setSkipCache(boolean skipCache) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCacheExplicit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCache() {
|
||||
return false;
|
||||
|
||||
@@ -23,7 +23,7 @@ abstract class TransactionFactory {
|
||||
*
|
||||
* @param tenantId The tenantId for lazy loading queries.
|
||||
*/
|
||||
abstract SpiTransaction createQueryTransaction(Object tenantId);
|
||||
abstract SpiTransaction createReadOnlyTransaction(Object tenantId);
|
||||
|
||||
/**
|
||||
* Return a new transaction.
|
||||
|
||||
@@ -21,7 +21,7 @@ class TransactionFactoryBasic extends TransactionFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
|
||||
|
||||
Connection connection = null;
|
||||
try {
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ class TransactionFactoryBasicWithRead extends TransactionFactoryBasic {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
|
||||
|
||||
Connection connection = null;
|
||||
try {
|
||||
|
||||
@@ -24,7 +24,7 @@ class TransactionFactoryTenant extends TransactionFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
|
||||
return create(false, tenantId);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ class TransactionFactoryTenantWithRead extends TransactionFactoryTenant {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
|
||||
|
||||
Connection connection = null;
|
||||
try {
|
||||
|
||||
@@ -191,6 +191,7 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
this.txnMain = metricFactory.createTimedMetric("txn.main");
|
||||
this.txnReadOnly = metricFactory.createTimedMetric("txn.readonly");
|
||||
this.txnNamed = metricFactory.createTimedMetricMap("txn.named.");
|
||||
// Add gauges for db pool size
|
||||
|
||||
scopeManager.register(this);
|
||||
}
|
||||
@@ -356,6 +357,14 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
return t;
|
||||
}
|
||||
|
||||
private SpiTransaction createTransaction(TxScope txScope) {
|
||||
if (txScope.isReadonly()) {
|
||||
return createReadOnlyTransaction(null);
|
||||
} else {
|
||||
return createTransaction(true, txScope.getIsolationLevel());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Transaction.
|
||||
*/
|
||||
@@ -366,8 +375,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
/**
|
||||
* Create a new Transaction for query only purposes (can use read only datasource).
|
||||
*/
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
return transactionFactory.createQueryTransaction(tenantId);
|
||||
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
|
||||
return transactionFactory.createReadOnlyTransaction(tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -669,7 +678,7 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
transaction = NoTransaction.INSTANCE;
|
||||
break;
|
||||
default:
|
||||
transaction = createTransaction(true, txScope.getIsolationLevel());
|
||||
transaction = createTransaction(txScope);
|
||||
initNewTransaction(transaction, txScope);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.util.function.Predicate;
|
||||
public class NoneDocStore implements DocumentStore {
|
||||
|
||||
public static IllegalStateException implementationNotInClassPath() {
|
||||
throw new IllegalStateException("DocStore implementation not included in the classPath. You need to add the maven dependency for avaje-ebeanorm-elastic");
|
||||
throw new IllegalStateException("DocStore implementation not included in the classPath. You need to add the maven dependency for io.ebean:ebean-elastic");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user