mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
143
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ac3a8d1ad | ||
|
|
5a8f8b3a83 | ||
|
|
7c01cec466 | ||
|
|
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 | ||
|
|
0a1704e6a9 | ||
|
|
854c208f92 | ||
|
|
e6f604764c | ||
|
|
9b501b3dc8 | ||
|
|
c2fb23a25c | ||
|
|
1ad7ace9bd | ||
|
|
6a3a7eafea | ||
|
|
896aed0b38 | ||
|
|
f7359aacd7 | ||
|
|
d77fb9265b | ||
|
|
db27844eca | ||
|
|
61be1683b2 | ||
|
|
27a1f35356 | ||
|
|
f0c0b7ebcc | ||
|
|
f4c646cdc1 | ||
|
|
48abc73e50 | ||
|
|
91a327a5e7 | ||
|
|
a861e8bdcc | ||
|
|
7692d9782a | ||
|
|
524e3e31f0 | ||
|
|
d69d96a400 |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>12.1.14-SNAPSHOT</version>
|
||||
<version>12.3.6</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
<tag>ebean-12.3.6</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -64,7 +64,7 @@
|
||||
<dependency>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
@@ -72,22 +72,22 @@
|
||||
to assist with IDE auto-completion with Ebean API
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-jsr305</artifactId>
|
||||
<version>1.1</version>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>avaje-jsr305</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>persistence-api</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<version>2.2.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>6.9</version>
|
||||
<version>6.13</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.1.12</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();
|
||||
|
||||
@@ -313,7 +313,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* <h3>getGeneratedKeys</h3>
|
||||
* <p>
|
||||
* Often with large batch inserts we want to turn off getGeneratedKeys. We do
|
||||
* this via {@link #setBatchGetGeneratedKeys(boolean)}.
|
||||
* this via {@link #setGetGeneratedKeys(boolean)}.
|
||||
* Also note that some JDBC drivers do not support getGeneratedKeys in JDBC batch mode.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
@@ -342,7 +342,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* </p>
|
||||
* <p>
|
||||
* We use {@link #flush()} to explicitly flush the batch and we can use
|
||||
* {@link #setBatchFlushOnQuery(boolean)} and {@link #setBatchFlushOnMixed(boolean)}
|
||||
* {@link #setFlushOnQuery(boolean)} and {@link #setFlushOnMixed(boolean)}
|
||||
* to control the automatic flushing behaviour.
|
||||
* </p>
|
||||
* <p>
|
||||
@@ -432,7 +432,15 @@ public interface Transaction extends AutoCloseable {
|
||||
* number of objects and you don't care about getting back the ids.
|
||||
* </p>
|
||||
*/
|
||||
void setBatchGetGeneratedKeys(boolean getGeneratedKeys);
|
||||
void setGetGeneratedKeys(boolean getGeneratedKeys);
|
||||
|
||||
/**
|
||||
* Deprecated renamed to setGetGeneratedKeys().
|
||||
*/
|
||||
@Deprecated
|
||||
default void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
|
||||
setGetGeneratedKeys(getGeneratedKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* By default when mixing UpdateSql (or CallableSql) with Beans the batch is
|
||||
@@ -449,7 +457,15 @@ public interface Transaction extends AutoCloseable {
|
||||
* have a 2 step process (delayed binding).
|
||||
* </p>
|
||||
*/
|
||||
void setBatchFlushOnMixed(boolean batchFlushOnMixed);
|
||||
void setFlushOnMixed(boolean batchFlushOnMixed);
|
||||
|
||||
/**
|
||||
* Deprecated renamed to setFlushOnMixed().
|
||||
*/
|
||||
@Deprecated
|
||||
default void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
|
||||
setFlushOnMixed(batchFlushOnMixed);
|
||||
}
|
||||
|
||||
/**
|
||||
* By default executing a query will automatically flush any batched
|
||||
@@ -459,7 +475,15 @@ public interface Transaction extends AutoCloseable {
|
||||
* execute a query and the batch will not be automatically flushed.
|
||||
* </p>
|
||||
*/
|
||||
void setBatchFlushOnQuery(boolean batchFlushOnQuery);
|
||||
void setFlushOnQuery(boolean batchFlushOnQuery);
|
||||
|
||||
/**
|
||||
* Deprecated renamed to setFlushOnQuery().
|
||||
*/
|
||||
@Deprecated
|
||||
default void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
|
||||
setFlushOnQuery(batchFlushOnQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the batch (of persisted beans or executed UpdateSql etc)
|
||||
@@ -468,7 +492,15 @@ public interface Transaction extends AutoCloseable {
|
||||
* The default is for this to be true.
|
||||
* </p>
|
||||
*/
|
||||
boolean isBatchFlushOnQuery();
|
||||
boolean isFlushOnQuery();
|
||||
|
||||
/**
|
||||
* Deprecated renamed to isFlushOnQuery().
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean isBatchFlushOnQuery() {
|
||||
return isFlushOnQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* The batch will be flushing automatically but you can use this to explicitly
|
||||
|
||||
@@ -40,6 +40,8 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
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);
|
||||
|
||||
@@ -222,8 +222,8 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
* Return true if this class is part of entity inheritance.
|
||||
*/
|
||||
protected boolean hasInheritance(Class<?> supCls) {
|
||||
return AnnotationUtil.findAnnotationRecursive(supCls, Inheritance.class) != null
|
||||
|| AnnotationUtil.findAnnotation(supCls, DiscriminatorValue.class) != null;
|
||||
return AnnotationUtil.typeHas(supCls, Inheritance.class)
|
||||
|| AnnotationUtil.has(supCls, DiscriminatorValue.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -255,16 +255,12 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
* Gets the table name from annotation.
|
||||
*/
|
||||
protected TableName getTableNameFromAnnotation(Class<?> beanClass) {
|
||||
|
||||
final Table t = AnnotationUtil.findAnnotationRecursive(beanClass, Table.class);
|
||||
|
||||
// Take the annotation if defined
|
||||
if (t != null && !isEmpty(t.name())) {
|
||||
final Table table = AnnotationUtil.typeGet(beanClass, Table.class);
|
||||
if (table != null && !isEmpty(table.name())) {
|
||||
// Note: empty catalog and schema are converted to null
|
||||
// Only need to convert quoted identifiers from annotations
|
||||
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()), quoteIdentifiers(t.name()));
|
||||
return new TableName(quoteIdentifiers(table.catalog()), quoteIdentifiers(table.schema()), quoteIdentifiers(table.name()));
|
||||
}
|
||||
|
||||
// No annotation
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -34,6 +34,11 @@ public class PlatformConfig {
|
||||
*/
|
||||
private String databaseBooleanFalse;
|
||||
|
||||
/**
|
||||
* For DB's using sequences this is the number of sequence values prefetched.
|
||||
*/
|
||||
private int databaseSequenceBatchSize = 20;
|
||||
|
||||
/**
|
||||
* Set for DB's that support both Sequence and Identity (and the default choice is not desired).
|
||||
*/
|
||||
@@ -74,6 +79,7 @@ public class PlatformConfig {
|
||||
public PlatformConfig(PlatformConfig platformConfig) {
|
||||
this.databaseBooleanFalse = platformConfig.databaseBooleanFalse;
|
||||
this.databaseBooleanTrue = platformConfig.databaseBooleanTrue;
|
||||
this.databaseSequenceBatchSize = platformConfig.databaseSequenceBatchSize;
|
||||
this.idType = platformConfig.idType;
|
||||
this.geometrySRID = platformConfig.geometrySRID;
|
||||
this.dbUuid = platformConfig.dbUuid;
|
||||
@@ -167,6 +173,20 @@ public class PlatformConfig {
|
||||
this.databaseBooleanFalse = databaseBooleanFalse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of DB sequence values that should be preallocated.
|
||||
*/
|
||||
public int getDatabaseSequenceBatchSize() {
|
||||
return databaseSequenceBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of DB sequence values that should be preallocated.
|
||||
*/
|
||||
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
|
||||
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Geometry SRID.
|
||||
*/
|
||||
@@ -274,6 +294,7 @@ public class PlatformConfig {
|
||||
public void loadSettings(PropertiesWrapper p) {
|
||||
|
||||
idType = p.getEnum(IdType.class, "idType", idType);
|
||||
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
|
||||
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
|
||||
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
|
||||
databaseInetAddressVarchar = p.getBoolean("databaseInetAddressVarchar", databaseInetAddressVarchar);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
@@ -139,7 +142,7 @@ public class ServerConfig {
|
||||
* Having this on and registering entity classes means we don't need to manually
|
||||
* write that code or use classpath scanning to find entity classes.
|
||||
*/
|
||||
private boolean loadModuleInfo;
|
||||
private boolean loadModuleInfo = true;
|
||||
|
||||
/**
|
||||
* List of interesting classes such as entities, embedded, ScalarTypes,
|
||||
@@ -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).
|
||||
@@ -1050,6 +1046,18 @@ public class ServerConfig {
|
||||
this.lazyLoadBatchSize = lazyLoadBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of sequences to fetch/preallocate when using DB sequences.
|
||||
* <p>
|
||||
* This is a performance optimisation to reduce the number times Ebean
|
||||
* requests a sequence to be used as an Id for a bean (aka reduce network
|
||||
* chatter).
|
||||
* </p>
|
||||
*/
|
||||
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
|
||||
platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default JDBC fetchSize hint for findList queries.
|
||||
*/
|
||||
@@ -1728,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>
|
||||
@@ -1816,6 +1788,32 @@ public class ServerConfig {
|
||||
this.platformConfig.setDatabaseBooleanFalse(databaseFalse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of DB sequence values that should be preallocated.
|
||||
*/
|
||||
public int getDatabaseSequenceBatchSize() {
|
||||
return platformConfig.getDatabaseSequenceBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of DB sequence values that should be preallocated and cached
|
||||
* by Ebean.
|
||||
* <p>
|
||||
* This is only used for DB's that use sequences and is a performance
|
||||
* optimisation. This reduces the number of times Ebean needs to get a
|
||||
* sequence value from the Database reducing network chatter.
|
||||
* </p>
|
||||
* <p>
|
||||
* By default this value is 10 so when we need another Id (and don't have one
|
||||
* in our cache) Ebean will fetch 10 id's from the database. Note that when
|
||||
* the cache drops to have full (which is 5 by default) Ebean will fetch
|
||||
* another batch of Id's in a background thread.
|
||||
* </p>
|
||||
*/
|
||||
public void setDatabaseSequenceBatch(int databaseSequenceBatchSize) {
|
||||
this.platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the database platform name (can be null).
|
||||
* <p>
|
||||
@@ -2761,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");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2822,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);
|
||||
@@ -3062,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
|
||||
*/
|
||||
@@ -3260,6 +3271,17 @@ public class ServerConfig {
|
||||
return loadModuleInfo && classes.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set false to turn off automatic registration of entity beans.
|
||||
* <p>
|
||||
* When using query beans that also generates a module info class that
|
||||
* can register the entity bean classes (to avoid classpath scanning).
|
||||
* This is on by default and setting this to false turns it off.
|
||||
*/
|
||||
public void setLoadModuleInfo(boolean loadModuleInfo) {
|
||||
this.loadModuleInfo = loadModuleInfo;
|
||||
}
|
||||
|
||||
public enum UuidVersion {
|
||||
VERSION4,
|
||||
VERSION1,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -110,6 +110,10 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected DbIdentity dbIdentity = new DbIdentity();
|
||||
|
||||
protected boolean sequenceBatchMode = true;
|
||||
|
||||
protected int sequenceBatchSize = 20;
|
||||
|
||||
/**
|
||||
* The history support for this database platform.
|
||||
*/
|
||||
@@ -142,7 +146,7 @@ public class DatabasePlatform {
|
||||
|
||||
protected String truncateTable = "truncate table %s";
|
||||
|
||||
protected String columnAliasPrefix = "c";
|
||||
protected String columnAliasPrefix;
|
||||
|
||||
/**
|
||||
* Use a BackTick ` at the beginning and end of table or column names that you
|
||||
@@ -231,6 +235,7 @@ public class DatabasePlatform {
|
||||
* Configure the platform given the server configuration.
|
||||
*/
|
||||
public void configure(PlatformConfig config) {
|
||||
this.sequenceBatchSize = config.getDatabaseSequenceBatchSize();
|
||||
this.caseSensitiveCollation = config.isCaseSensitiveCollation();
|
||||
configureIdType(config.getIdType());
|
||||
configure(config, config.isAllQuotedIdentifiers());
|
||||
@@ -293,6 +298,20 @@ public class DatabasePlatform {
|
||||
return platform.name().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we are using Sequence batch mode rather than STEP.
|
||||
*/
|
||||
public boolean isSequenceBatchMode() {
|
||||
return sequenceBatchMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false to not use sequence batch mode but instead STEP mode.
|
||||
*/
|
||||
public void setSequenceBatchMode(boolean sequenceBatchMode) {
|
||||
this.sequenceBatchMode = sequenceBatchMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this database platform supports native ILIKE expression.
|
||||
*/
|
||||
@@ -362,13 +381,13 @@ public class DatabasePlatform {
|
||||
/**
|
||||
* Return a DB Sequence based IdGenerator.
|
||||
*
|
||||
* @param be the BackgroundExecutor that can be used to load the sequence if
|
||||
* desired
|
||||
* @param ds the DataSource
|
||||
* @param increment the sequence allocation size as defined by mapping (defaults to 50)
|
||||
* @param seqName the name of the sequence
|
||||
* @param be the BackgroundExecutor that can be used to load the sequence if
|
||||
* desired
|
||||
* @param ds the DataSource
|
||||
* @param stepSize the sequence allocation size as defined by mapping (defaults to 50)
|
||||
* @param seqName the name of the sequence
|
||||
*/
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ public class DbPlatformTypeMapping {
|
||||
|
||||
} else {
|
||||
put(DbType.VARCHAR, new DbPlatformType("varchar", 255));
|
||||
put(DbType.DECIMAL, new DbPlatformType("decimal", 38));
|
||||
put(DbType.DECIMAL, new DbPlatformType("decimal", 16, 3));
|
||||
put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
|
||||
put(DbType.BINARY, new DbPlatformType("binary", 255));
|
||||
put(DbType.CHAR, new DbPlatformType("char", 1));
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package io.ebean.config.dbplatform;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Database sequence based IdGenerator using Sequence Step 1 but batch fetch many sequence values.
|
||||
*/
|
||||
public abstract class SequenceBatchIdGenerator extends SequenceIdGenerator {
|
||||
|
||||
/**
|
||||
* Construct where batchSize is the sequence step size.
|
||||
*
|
||||
*/
|
||||
public SequenceBatchIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
|
||||
super(be, ds, seqName, batchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* If allocateSize is large load some sequences in a background thread.
|
||||
* <p>
|
||||
* For example, when inserting a bean with a cascade on a OneToMany with many
|
||||
* beans Ebean can call this to ensure .
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void preAllocateIds(int requestSize) {
|
||||
if (allocationSize > 1 && requestSize > allocationSize) {
|
||||
// only bother if allocateSize is bigger than
|
||||
// the normal loading batchSize
|
||||
if (requestSize > 100) {
|
||||
// max out at 100 for now
|
||||
requestSize = 100;
|
||||
}
|
||||
loadInBackground(requestSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the next set of Ids as the next value plus all the following numbers up to the step size.
|
||||
*/
|
||||
@Override
|
||||
protected List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException {
|
||||
|
||||
List<Long> newIds = new ArrayList<>(loadSize);
|
||||
while (resultSet.next()) {
|
||||
newIds.add(resultSet.getLong(1));
|
||||
}
|
||||
return newIds;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
|
||||
this.allocationSize = allocationSize;
|
||||
}
|
||||
|
||||
public abstract String getSql();
|
||||
public abstract String getSql(int batchSize);
|
||||
|
||||
/**
|
||||
* Returns the sequence name.
|
||||
@@ -102,7 +102,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
|
||||
if (size > 0) {
|
||||
maybeLoadMoreInBackground(size);
|
||||
} else {
|
||||
loadMore();
|
||||
loadMore(allocationSize);
|
||||
}
|
||||
return idList.pollFirst();
|
||||
}
|
||||
@@ -116,8 +116,8 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadMore() {
|
||||
List<Long> newIds = getMoreIds();
|
||||
private void loadMore(int requestSize) {
|
||||
List<Long> newIds = getMoreIds(requestSize);
|
||||
synchronized (monitor) {
|
||||
idList.addAll(newIds);
|
||||
}
|
||||
@@ -139,7 +139,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
|
||||
currentlyBackgroundLoading = true;
|
||||
|
||||
backgroundExecutor.execute(() -> {
|
||||
loadMore();
|
||||
loadMore(requestSize);
|
||||
synchronized (backgroundLoadMonitor) {
|
||||
currentlyBackgroundLoading = false;
|
||||
}
|
||||
@@ -150,14 +150,14 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
|
||||
/**
|
||||
* Read the resultSet returning the list of Id values.
|
||||
*/
|
||||
protected abstract List<Long> readIds(ResultSet resultSet) throws SQLException;
|
||||
protected abstract List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException;
|
||||
|
||||
/**
|
||||
* Get more Id's by executing a query and reading the Id's returned.
|
||||
*/
|
||||
protected List<Long> getMoreIds() {
|
||||
protected List<Long> getMoreIds(int requestSize) {
|
||||
|
||||
String sql = getSql();
|
||||
String sql = getSql(requestSize);
|
||||
|
||||
Connection connection = null;
|
||||
PreparedStatement statement = null;
|
||||
@@ -168,7 +168,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
|
||||
statement = connection.prepareStatement(sql);
|
||||
resultSet = statement.executeQuery();
|
||||
|
||||
List<Long> newIds = readIds(resultSet);
|
||||
List<Long> newIds = readIds(resultSet, requestSize);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("seq:{} loaded:{} sql:{}", seqName, newIds.size(), sql);
|
||||
}
|
||||
|
||||
@@ -13,25 +13,19 @@ import java.util.List;
|
||||
*/
|
||||
public abstract class SequenceStepIdGenerator extends SequenceIdGenerator {
|
||||
|
||||
private final String nextSql;
|
||||
/**
|
||||
* Construct with stepSize (typically 50).
|
||||
*/
|
||||
protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize, String nextSql) {
|
||||
protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
|
||||
super(be, ds, seqName, stepSize);
|
||||
this.nextSql = nextSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
return nextSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the next set of Ids as the next value plus all the following numbers up to the step size.
|
||||
*/
|
||||
@Override
|
||||
protected List<Long> readIds(ResultSet resultSet) throws SQLException {
|
||||
protected List<Long> readIds(ResultSet resultSet, int ignoreRequestSize) throws SQLException {
|
||||
|
||||
List<Long> newIds = new ArrayList<>(allocationSize);
|
||||
if (resultSet.next()) {
|
||||
long start = resultSet.getLong(1);
|
||||
|
||||
@@ -16,10 +16,8 @@ public class ClickHousePlatform extends DatabasePlatform {
|
||||
//this.dbEncrypt =
|
||||
//this.historySupport =
|
||||
//this.exceptionTranslator =
|
||||
|
||||
this.nativeUuidType = true;
|
||||
this.dbDefaultValue.setNow("now()");
|
||||
this.columnAliasPrefix = null;
|
||||
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
@@ -34,7 +32,7 @@ public class ClickHousePlatform extends DatabasePlatform {
|
||||
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("UInt16", false));
|
||||
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("UInt32", false));
|
||||
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("UInt64", false));
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("Decimal", 16,4));
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("Decimal", 16, 3));
|
||||
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("Float64", false));
|
||||
|
||||
dbTypeMap.put(DbType.DATE, new DbPlatformType("Date", false));
|
||||
|
||||
@@ -42,8 +42,7 @@ public class DB2Platform extends DatabasePlatform {
|
||||
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
|
||||
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("bigint", false));
|
||||
dbTypeMap.put(DbType.REAL, new DbPlatformType("real"));
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 15));
|
||||
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 16, 3));
|
||||
persistBatchOnCascade = PersistBatch.NONE;
|
||||
}
|
||||
|
||||
@@ -52,8 +51,9 @@ public class DB2Platform extends DatabasePlatform {
|
||||
* sequence values.
|
||||
*/
|
||||
@Override
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
|
||||
return new DB2Sequence(be, ds, seqName, increment);
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
|
||||
|
||||
return new DB2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package io.ebean.config.dbplatform.db2;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* DB2 specific sequence Id Generator.
|
||||
*/
|
||||
class DB2Sequence extends SequenceStepIdGenerator {
|
||||
|
||||
DB2Sequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
|
||||
super(be, ds, seqName, stepSize, "values nextval for " + seqName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package io.ebean.config.dbplatform.db2;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* DB2 specific sequence Id Generator.
|
||||
*/
|
||||
public class DB2SequenceIdGenerator extends SequenceBatchIdGenerator {
|
||||
|
||||
private final String baseSql;
|
||||
private final String unionBaseSql;
|
||||
|
||||
/**
|
||||
* Construct given a dataSource and sql to return the next sequence value.
|
||||
*/
|
||||
public DB2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
|
||||
super(be, ds, seqName, batchSize);
|
||||
this.baseSql = "values nextval for " + seqName;
|
||||
this.unionBaseSql = " union " + baseSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql(int batchSize) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(baseSql);
|
||||
for (int i = 1; i < batchSize; i++) {
|
||||
sb.append(unionBaseSql);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package io.ebean.config.dbplatform.h2;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
class H2DbSequence extends SequenceStepIdGenerator {
|
||||
|
||||
H2DbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
|
||||
super(be, ds, seqName, stepSize, "select " + seqName + ".nextval");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,6 @@ public class H2Platform extends DatabasePlatform {
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.inlineSqlUpdateLimit = true;
|
||||
this.dbDefaultValue.setNow("now()");
|
||||
this.columnAliasPrefix = null;
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
.addAcquireLock("50200","HYT00")
|
||||
@@ -42,9 +41,13 @@ public class H2Platform extends DatabasePlatform {
|
||||
dbTypeMap.put(DbType.UUID, new DbPlatformType("uuid", false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a H2 specific sequence IdGenerator that supports batch fetching
|
||||
* sequence values.
|
||||
*/
|
||||
@Override
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
|
||||
return new H2DbSequence(be, ds, seqName, increment);
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
|
||||
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package io.ebean.config.dbplatform.h2;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* H2 specific sequence Id Generator.
|
||||
*/
|
||||
public class H2SequenceIdGenerator extends SequenceBatchIdGenerator {
|
||||
|
||||
private final String baseSql;
|
||||
private final String unionBaseSql;
|
||||
|
||||
/**
|
||||
* Construct given a dataSource and sql to return the next sequence value.
|
||||
*/
|
||||
public H2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
|
||||
super(be, ds, seqName, batchSize);
|
||||
this.baseSql = "select " + seqName + ".nextval";
|
||||
this.unionBaseSql = " union " + baseSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql(int batchSize) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(baseSql);
|
||||
for (int i = 1; i < batchSize; i++) {
|
||||
sb.append(unionBaseSql);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ public class HanaPlatform extends DatabasePlatform {
|
||||
this.sqlLimiter = new HanaSqlLimiter();
|
||||
this.persistBatchOnCascade = PersistBatch.NONE;
|
||||
this.supportsResultSetConcurrencyModeUpdatable = false;
|
||||
this.columnAliasPrefix = null;
|
||||
this.historySupport = new HanaHistorySupport();
|
||||
this.basicSqlLimiter = new HanaBasicSqlLimiter();
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.ebean.config.dbplatform.hsqldb;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
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.PlatformIdGenerator;
|
||||
import io.ebean.config.dbplatform.h2.H2DbEncrypt;
|
||||
import io.ebean.config.dbplatform.h2.H2SequenceIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* H2 specific platform.
|
||||
*/
|
||||
public class HsqldbPlatform extends DatabasePlatform {
|
||||
|
||||
public HsqldbPlatform() {
|
||||
super();
|
||||
this.platform = Platform.HSQLDB;
|
||||
this.dbEncrypt = new H2DbEncrypt();
|
||||
this.truncateTable = "delete from %s";
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
|
||||
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
|
||||
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Hsqldb specific support.
|
||||
*/
|
||||
package io.ebean.config.dbplatform.hsqldb;
|
||||
@@ -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,78 +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.columnAliasPrefix = null;
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ public class NuoDbPlatform extends DatabasePlatform {
|
||||
this.idInExpandedForm = true;
|
||||
this.supportsResultSetConcurrencyModeUpdatable = false;
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.columnAliasPrefix = null;
|
||||
this.likeClauseRaw = "like ?";
|
||||
this.historySupport = new NuoDbHistorySupport();
|
||||
//this.dbEncrypt = ...
|
||||
@@ -44,8 +43,8 @@ public class NuoDbPlatform extends DatabasePlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
|
||||
return new NuoDbSequence(be, ds, seqName, increment);
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
|
||||
return new NuoDbSequence(be, ds, seqName, stepSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,10 +5,20 @@ import io.ebean.config.dbplatform.SequenceStepIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
class NuoDbSequence extends SequenceStepIdGenerator {
|
||||
public class NuoDbSequence extends SequenceStepIdGenerator {
|
||||
|
||||
NuoDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
|
||||
super(be, ds, seqName, stepSize, "select next value for " + seqName + " from dual");
|
||||
private final String nextSql;
|
||||
|
||||
/**
|
||||
* Construct where batchSize is the sequence step size.
|
||||
*/
|
||||
public NuoDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
|
||||
super(be, ds, seqName, stepSize);
|
||||
this.nextSql = "select next value for " + seqName + " from dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql(int batchSize) {
|
||||
return nextSql;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package io.ebean.config.dbplatform.oracle;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
class OracleDbSequence extends SequenceStepIdGenerator {
|
||||
|
||||
OracleDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
|
||||
super(be, ds, seqName, stepSize, "select " + seqName + ".nextval from dual");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,6 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
public OraclePlatform() {
|
||||
super();
|
||||
this.platform = Platform.ORACLE;
|
||||
this.columnAliasPrefix = null;
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.maxTableNameLength = 30;
|
||||
this.maxConstraintNameLength = 30;
|
||||
@@ -31,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);
|
||||
@@ -63,8 +62,7 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("number", 19, 4));
|
||||
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("number", 5));
|
||||
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("number", 3));
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("number", 38));
|
||||
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("number", 16, 3));
|
||||
dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("varchar2", 255));
|
||||
|
||||
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("blob"));
|
||||
@@ -76,8 +74,8 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
|
||||
return new OracleDbSequence(be, ds, seqName, increment);
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
|
||||
return new OracleSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.ebean.config.dbplatform.oracle;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* Oracle specific sequence Id Generator.
|
||||
*/
|
||||
public class OracleSequenceIdGenerator extends SequenceBatchIdGenerator {
|
||||
|
||||
private final String baseSql;
|
||||
|
||||
/**
|
||||
* Construct given a dataSource and sql to return the next sequence value.
|
||||
*/
|
||||
public OracleSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
|
||||
super(be, ds, seqName, batchSize);
|
||||
this.baseSql = "select " + seqName + ".nextval, a from (select level as a FROM dual CONNECT BY level <= ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql(int batchSize) {
|
||||
return baseSql + batchSize + ")";
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package io.ebean.config.dbplatform.postgres;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
class PostgresDbSequence extends SequenceStepIdGenerator {
|
||||
|
||||
PostgresDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
|
||||
super(be, ds, seqName, stepSize, "select nextval('" + seqName + "')");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,7 +34,6 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
this.clobDbType = Types.VARCHAR;
|
||||
this.nativeUuidType = true;
|
||||
this.columnAliasPrefix = null;
|
||||
this.truncateTable = "truncate table %s cascade";
|
||||
this.dbEncrypt = new PostgresDbEncrypt();
|
||||
this.historySupport = new PostgresHistorySupport();
|
||||
@@ -71,7 +70,6 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
|
||||
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float"));
|
||||
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 38));
|
||||
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("timestamptz"));
|
||||
|
||||
dbTypeMap.put(DbType.BINARY, dbBytea);
|
||||
@@ -109,8 +107,9 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
* Create a Postgres specific sequence IdGenerator.
|
||||
*/
|
||||
@Override
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
|
||||
return new PostgresDbSequence(be, ds, seqName, increment);
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
|
||||
|
||||
return new PostgresSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.ebean.config.dbplatform.postgres;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* Postgres specific sequence Id Generator.
|
||||
*/
|
||||
public class PostgresSequenceIdGenerator extends SequenceBatchIdGenerator {
|
||||
|
||||
private final String baseSql;
|
||||
|
||||
/**
|
||||
* Construct given a dataSource and sql to return the next sequence value.
|
||||
*/
|
||||
public PostgresSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
|
||||
super(be, ds, seqName, batchSize);
|
||||
this.baseSql = "select nextval('" + seqName + "'), s.generate_series from (select generate_series from generate_series(1,";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql(int batchSize) {
|
||||
return baseSql + batchSize + ") ) as s";
|
||||
}
|
||||
}
|
||||
@@ -32,8 +32,7 @@ public class SqlAnywherePlatform extends DatabasePlatform {
|
||||
dbTypeMap.put(DbType.REAL, new DbPlatformType("float(16)"));
|
||||
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float(32)"));
|
||||
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 28));
|
||||
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 16, 3));
|
||||
dbTypeMap.put(DbType.BLOB, new DbPlatformType("binary(4500)"));
|
||||
dbTypeMap.put(DbType.CLOB, new DbPlatformType("long varchar"));
|
||||
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("long binary"));
|
||||
|
||||
@@ -12,7 +12,6 @@ public class SqlServer17Platform extends SqlServerBasePlatform {
|
||||
public SqlServer17Platform() {
|
||||
super();
|
||||
this.platform = Platform.SQLSERVER17;
|
||||
this.columnAliasPrefix = null;
|
||||
this.nativeUuidType = true;
|
||||
this.dbTypeMap.put(DbType.UUID, new DbPlatformType("uniqueidentifier", false));
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
this.sequenceBatchMode = false;
|
||||
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
@@ -61,9 +62,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
|
||||
dbTypeMap.put(DbType.REAL, new DbPlatformType("float(16)"));
|
||||
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float(32)"));
|
||||
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 28));
|
||||
|
||||
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 16, 3));
|
||||
dbTypeMap.put(DbType.DATE, new DbPlatformType("date"));
|
||||
dbTypeMap.put(DbType.TIME, new DbPlatformType("time"));
|
||||
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime2"));
|
||||
@@ -94,9 +93,12 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
|
||||
sb.append('[').append(ch).append(']');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Postgres specific sequence IdGenerator.
|
||||
*/
|
||||
@Override
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
|
||||
return new SqlServerDbSequence(be, ds, seqName, increment);
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
|
||||
return new SqlServerStepSequence(be, ds, seqName, stepSize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package io.ebean.config.dbplatform.sqlserver;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
class SqlServerDbSequence extends SequenceStepIdGenerator {
|
||||
|
||||
SqlServerDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
|
||||
super(be, ds, seqName, stepSize, "select next value for " + seqName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.ebean.config.dbplatform.sqlserver;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public class SqlServerStepSequence extends SequenceStepIdGenerator {
|
||||
|
||||
private final String nextSql;
|
||||
|
||||
/**
|
||||
* Construct where batchSize is the sequence step size.
|
||||
*/
|
||||
public SqlServerStepSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
|
||||
super(be, ds, seqName, stepSize);
|
||||
this.nextSql = "select next value for "+seqName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql(int batchSize) {
|
||||
return nextSql;
|
||||
}
|
||||
}
|
||||
@@ -171,7 +171,7 @@ public class DefaultCsvCallback<T> implements CsvCallback<T> {
|
||||
logger.info("Creating transaction, batchSize[" + persistBatchSize + "]");
|
||||
transaction.setBatchMode(true);
|
||||
transaction.setBatchSize(persistBatchSize);
|
||||
transaction.setBatchGetGeneratedKeys(false);
|
||||
transaction.setGetGeneratedKeys(false);
|
||||
|
||||
} else {
|
||||
// explicitly turn off JDBC batching in case
|
||||
|
||||
@@ -1,304 +1,98 @@
|
||||
package io.ebean.util;
|
||||
|
||||
import io.ebean.annotation.Formula;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.annotation.Where;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Annotation utility methods to find annotations recursively - taken from the spring framework.
|
||||
* Annotation utility methods to find annotations.
|
||||
*/
|
||||
public class AnnotationUtil {
|
||||
|
||||
/**
|
||||
* Determine if the supplied {@link Annotation} is defined in the core JDK {@code java.lang.annotation} package.
|
||||
*/
|
||||
public static boolean isInJavaLangAnnotationPackage(Annotation annotation) {
|
||||
return annotation.annotationType().getName().startsWith("java.lang.annotation");
|
||||
public static boolean notJavaLang(Annotation annotation) {
|
||||
return !annotation.annotationType().getName().startsWith("java.lang.annotation");
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a single {@link Annotation} of {@code annotationType} on the supplied {@link AnnotatedElement}.
|
||||
* <p>
|
||||
* Meta-annotations will be searched if the annotation is not <em>directly present</em> on the supplied element.
|
||||
* <p>
|
||||
* <strong>Warning</strong>: this method operates generically on annotated elements. In other words, this method
|
||||
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
|
||||
* It also does not filter out platform dependent annotations!
|
||||
* Simple get on field or method with no meta-annotations or platform filtering.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
if (annotationType == null) {
|
||||
return null;
|
||||
}
|
||||
// check if directly present, if not, start search for meta-annotations.
|
||||
Annotation[] anns = annotatedElement.getAnnotations();
|
||||
if (anns.length == 0) {
|
||||
return null; // no annotations present, so searching for meta annotations not required
|
||||
}
|
||||
|
||||
// As we need the anns array anyway, we iterate over this instead
|
||||
// of using annotatedElement.getAnnotation(...) which is synchronized internally
|
||||
for (Annotation ann : anns) {
|
||||
if (ann.annotationType() == annotationType) {
|
||||
return (A) ann;
|
||||
}
|
||||
}
|
||||
|
||||
return findAnnotation(anns, annotationType, new HashSet<>());
|
||||
|
||||
public static <A extends Annotation> A get(AnnotatedElement element, Class<A> annotation) {
|
||||
return element.getAnnotation(annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a single {@link Annotation} of {@code annotationType} on the supplied class.
|
||||
* <p>Meta-annotations will be searched if the annotation is not directly present on
|
||||
* the supplied element.
|
||||
* <p><strong>Note</strong>: this method searches for annotations at class & superClass(es)!
|
||||
* Simple has with no meta-annotations or platform filtering.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <A extends Annotation> A findAnnotationRecursive(Class<?> clazz, Class<A> annotationType) {
|
||||
if (annotationType == null) {
|
||||
return null;
|
||||
}
|
||||
public static <A extends Annotation> boolean has(AnnotatedElement element, Class<A> annotation) {
|
||||
return get(element, annotation) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* On class get the annotation - includes inheritance.
|
||||
*/
|
||||
public static <A extends Annotation> A typeGet(Class<?> clazz, Class<A> annotationType) {
|
||||
while (clazz != null && clazz != Object.class) {
|
||||
// check if directly present, if not, start search for meta-annotations.
|
||||
Annotation[] anns = clazz.getAnnotations();
|
||||
if (anns.length != 0) {
|
||||
for (Annotation ann : anns) {
|
||||
if (ann.annotationType() == annotationType) {
|
||||
return (A) ann;
|
||||
}
|
||||
}
|
||||
|
||||
A ann = findAnnotation(anns, annotationType, new HashSet<>());
|
||||
if (ann != null) {
|
||||
return ann;
|
||||
}
|
||||
final A val = clazz.getAnnotation(annotationType);
|
||||
if (val != null) {
|
||||
return val;
|
||||
}
|
||||
// no meta-annotation present at this class - traverse to superclass
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first annotation of a type for this platform. (if annotation is platform specific, otherwise first
|
||||
* found annotation is returned)
|
||||
* On class get all the annotations - includes inheritance.
|
||||
*/
|
||||
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType, Platform platform) {
|
||||
if (annotationType == null) {
|
||||
return null;
|
||||
public static <A extends Annotation> Set<A> typeGetAll(Class<?> clazz, Class<A> annotationType) {
|
||||
Set<A> result = new LinkedHashSet<>();
|
||||
typeGetAllCollect(clazz, annotationType, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static <A extends Annotation> void typeGetAllCollect(Class<?> clazz, Class<A> annotationType, Set<A> result) {
|
||||
while (clazz != null && clazz != Object.class) {
|
||||
final A val = clazz.getAnnotation(annotationType);
|
||||
if (val != null) {
|
||||
result.add(val);
|
||||
}
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
Set<A> anns = findAnnotations(annotatedElement, annotationType);
|
||||
return getPlatformMatchingAnnotation(anns, platform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all annotations recusively for a class and its superclasses or interfaces.
|
||||
* On class simple check for annotation - includes inheritance.
|
||||
*/
|
||||
public static <A extends Annotation> Set<A> findAnnotationsRecursive(Class<?> clazz, Class<A> annotationType) {
|
||||
Objects.requireNonNull(annotationType);
|
||||
Set<A> ret = new LinkedHashSet<>();
|
||||
public static <A extends Annotation> boolean typeHas(Class<?> clazz, Class<A> annotation) {
|
||||
return typeGet(clazz, annotation) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the annotations for the filter searching meta-annotations.
|
||||
*/
|
||||
public static Set<Annotation> metaFindAllFor(AnnotatedElement element, Set<Class<?>> filter) {
|
||||
Set<Annotation> visited = new HashSet<>();
|
||||
Set<Class<?>> visitedInterfaces = new HashSet<>();
|
||||
while (clazz != null && !clazz.getName().startsWith("java.lang.")) {
|
||||
findMetaAnnotationsRecursive(clazz, annotationType, ret, visited, visitedInterfaces);
|
||||
clazz = clazz.getSuperclass();
|
||||
Set<Annotation> result = new LinkedHashSet<>();
|
||||
for (Annotation ann : element.getAnnotations()) {
|
||||
metaAdd(ann, filter, visited, result);
|
||||
}
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the interfaces for annotations.
|
||||
*/
|
||||
private static <A extends Annotation> void findMetaAnnotationsRecursive(Class<?> clazz,
|
||||
Class<A> annotationType, Set<A> ret,
|
||||
Set<Annotation> visited, Set<Class<?>> visitedInterfaces) {
|
||||
findMetaAnnotations(clazz, annotationType, ret, visited);
|
||||
for (Class<?> iface : clazz.getInterfaces()) {
|
||||
if (!iface.getName().startsWith("java.lang.") && visitedInterfaces.add(iface)) {
|
||||
findMetaAnnotationsRecursive(iface, annotationType, ret, visited, visitedInterfaces);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the search algorithm avoiding endless recursion by tracking which
|
||||
* annotations have already been visited.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <A extends Annotation> A findAnnotation(Annotation[] anns, Class<A> annotationType, Set<Annotation> visited) {
|
||||
|
||||
|
||||
for (Annotation ann : anns) {
|
||||
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
|
||||
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
||||
for (Annotation metaAnn : metaAnns) {
|
||||
if (metaAnn.annotationType() == annotationType) {
|
||||
return (A) metaAnn;
|
||||
}
|
||||
}
|
||||
if (metaAnns.length > 0) {
|
||||
A annotation = findAnnotation(metaAnns, annotationType, visited);
|
||||
if (annotation != null) {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all {@link Annotation}s of {@code annotationType} on the supplied {@link AnnotatedElement}.
|
||||
* <p>
|
||||
* Meta-annotations will be searched if the annotation is not <em>directly present</em> on the supplied element.
|
||||
* <p>
|
||||
* <strong>Warning</strong>: this method operates generically on annotated elements. In other words, this method
|
||||
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
|
||||
*/
|
||||
public static <A extends Annotation> Set<A> findAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
if (annotationType == null) {
|
||||
return null;
|
||||
}
|
||||
Set<A> ret = new LinkedHashSet<>();
|
||||
findMetaAnnotations(annotatedElement, annotationType, ret, new HashSet<>());
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the search algorithm avoiding endless recursion by tracking which
|
||||
* annotations have already been visited.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <A extends Annotation> void findMetaAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType, Set<A> ret, Set<Annotation> visited) {
|
||||
|
||||
Annotation[] anns = annotatedElement.getAnnotations();
|
||||
for (Annotation ann : anns) {
|
||||
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
|
||||
if (ann.annotationType() == annotationType) {
|
||||
ret.add((A) ann);
|
||||
} else {
|
||||
Method repeatableValueMethod = getRepeatableValueMethod(ann, annotationType);
|
||||
if (repeatableValueMethod != null) {
|
||||
try {
|
||||
A[] repeatedAnns = (A[]) repeatableValueMethod.invoke(ann);
|
||||
for (Annotation repeatedAnn : repeatedAnns) {
|
||||
ret.add((A) repeatedAnn);
|
||||
findMetaAnnotations(repeatedAnn.annotationType(), annotationType, ret, visited);
|
||||
}
|
||||
} catch (Exception e) { // catch all exceptions (thrown by invoke)
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
findMetaAnnotations(ann.annotationType(), annotationType, ret, visited);
|
||||
}
|
||||
private static void metaAdd(Annotation ann, Set<Class<?>> filter, Set<Annotation> visited, Set<Annotation> result) {
|
||||
if (notJavaLang(ann) && visited.add(ann)) {
|
||||
if (filter.contains(ann.annotationType())) {
|
||||
result.add(ann);
|
||||
} else {
|
||||
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
|
||||
metaAdd(metaAnn, filter, visited, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// caches for getRepeatableValueMethod
|
||||
private static Method getNullMethod() {
|
||||
try {
|
||||
return AnnotationUtil.class.getDeclaredMethod("getNullMethod");
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final ConcurrentMap<Annotation, Method> valueMethods = new ConcurrentHashMap<>();
|
||||
// only a non-null-marker the valueMethods - Cache
|
||||
private static final Method nullMethod = getNullMethod();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the <code>value()</code> method for a possible containerAnnotation.
|
||||
* Method is retuned only, if its signature is <code>array of containingType</code>.
|
||||
*/
|
||||
private static <A extends Annotation> Method getRepeatableValueMethod(
|
||||
Annotation containerAnnotation, Class<A> containingType) {
|
||||
|
||||
Method method = valueMethods.get(containerAnnotation);
|
||||
if (method == null) {
|
||||
try {
|
||||
method = containerAnnotation.annotationType().getMethod("value");
|
||||
} catch (NoSuchMethodException e) {
|
||||
method = nullMethod;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Method prev = valueMethods.putIfAbsent(containerAnnotation, method);
|
||||
method = prev == null ? method : prev;
|
||||
}
|
||||
if (method != nullMethod) {
|
||||
Class<?> retType = method.getReturnType();
|
||||
if (retType.isArray() && retType.getComponentType() == containingType) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds a suitable annotation from <code>Set<T> anns</code> for this platform.
|
||||
* To distinguish between platforms, annotation type <code>T</code> must define
|
||||
* a method withthis signature:
|
||||
* <p>
|
||||
* <code>Class<? extends DatabasePlatform>[] platforms() default {};</code>
|
||||
* </p>
|
||||
* The finding rules are:
|
||||
* <ol>
|
||||
* <li>Check if T has method "platforms" if not, return <code>ann[0]</code></code>
|
||||
* <li>find the annotation that is defined for <code>databasePlatform</code></li>
|
||||
* <li>otherwise return the annotation for default platform (platforms = {})</li>
|
||||
* <li>return null
|
||||
* </ol>
|
||||
* (This mechanism is currently used by {@link Where} and {@link Formula})
|
||||
*/
|
||||
public static <T extends Annotation> T getPlatformMatchingAnnotation(Set<T> anns, Platform matchPlatform) {
|
||||
if (anns.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Method getPlatformsMethod = null;
|
||||
T fallback = null;
|
||||
for (T ann : anns) {
|
||||
try {
|
||||
if (getPlatformsMethod == null) {
|
||||
getPlatformsMethod = ann.getClass().getMethod("platforms");
|
||||
}
|
||||
if (!Platform[].class.isAssignableFrom(getPlatformsMethod.getReturnType())) {
|
||||
return ann;
|
||||
}
|
||||
Platform[] platforms = (Platform[]) getPlatformsMethod.invoke(ann);
|
||||
if (platforms.length == 0) {
|
||||
fallback = ann;
|
||||
} else {
|
||||
for (Platform platform : platforms) {
|
||||
if (matchPlatform == platform) {
|
||||
return ann;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
return ann; // not platform specific - return first one
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -71,13 +71,13 @@ public class ScopeTrans {
|
||||
restoreBatchOnCascade = transaction.isBatchOnCascade();
|
||||
restoreBatchSize = transaction.getBatchSize();
|
||||
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
|
||||
restoreBatchFlushOnQuery = transaction.isBatchFlushOnQuery();
|
||||
restoreBatchFlushOnQuery = transaction.isFlushOnQuery();
|
||||
}
|
||||
if (txScope.isBatchSet()) {
|
||||
transaction.setBatchMode(txScope.isBatchMode());
|
||||
}
|
||||
if (!txScope.isFlushOnQuery()) {
|
||||
transaction.setBatchFlushOnQuery(false);
|
||||
transaction.setFlushOnQuery(false);
|
||||
}
|
||||
if (txScope.isBatchOnCascadeSet()) {
|
||||
transaction.setBatchOnCascade(txScope.isBatchOnCascade());
|
||||
@@ -86,7 +86,7 @@ public class ScopeTrans {
|
||||
transaction.setBatchSize(txScope.getBatchSize());
|
||||
}
|
||||
if (txScope.isSkipGeneratedKeys()) {
|
||||
transaction.setBatchGetGeneratedKeys(false);
|
||||
transaction.setGetGeneratedKeys(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ public class ScopeTrans {
|
||||
transaction.commit();
|
||||
} else {
|
||||
nestedCommit = true;
|
||||
transaction.setBatchFlushOnQuery(restoreBatchFlushOnQuery);
|
||||
transaction.setFlushOnQuery(restoreBatchFlushOnQuery);
|
||||
if (restoreBatch != null) {
|
||||
transaction.setBatchMode(restoreBatch);
|
||||
}
|
||||
@@ -147,7 +147,7 @@ public class ScopeTrans {
|
||||
transaction.setBatchSize(restoreBatchSize);
|
||||
}
|
||||
if (restoreBatchGeneratedKeys != null) {
|
||||
transaction.setBatchGetGeneratedKeys(restoreBatchGeneratedKeys);
|
||||
transaction.setGetGeneratedKeys(restoreBatchGeneratedKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
@@ -269,8 +274,8 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
|
||||
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
|
||||
public void setGetGeneratedKeys(boolean getGeneratedKeys) {
|
||||
transaction.setGetGeneratedKeys(getGeneratedKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -279,18 +284,18 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
|
||||
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
|
||||
public void setFlushOnMixed(boolean batchFlushOnMixed) {
|
||||
transaction.setFlushOnMixed(batchFlushOnMixed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
|
||||
transaction.setBatchFlushOnQuery(batchFlushOnQuery);
|
||||
public void setFlushOnQuery(boolean batchFlushOnQuery) {
|
||||
transaction.setFlushOnQuery(batchFlushOnQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchFlushOnQuery() {
|
||||
return transaction.isBatchFlushOnQuery();
|
||||
public boolean isFlushOnQuery() {
|
||||
return transaction.isFlushOnQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,6 +14,8 @@ import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
|
||||
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;
|
||||
@@ -813,10 +815,14 @@ public class DefaultDbMigration implements DbMigration {
|
||||
switch (platform) {
|
||||
case H2:
|
||||
return new H2Platform();
|
||||
case HSQLDB:
|
||||
return new HsqldbPlatform();
|
||||
case POSTGRES9:
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ public class PlatformTypeConverter {
|
||||
} else {
|
||||
// scale - varchar(10)
|
||||
int scale = Integer.parseInt(columnDefinition.substring(open + 1, close));
|
||||
return dbType.renderType(scale, 0) + suffix;
|
||||
return dbType.renderType(scale, -1) + suffix;
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
@@ -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).
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package io.ebeaninternal.server.cache;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
/**
|
||||
* Bean Id value plus discriminator type.
|
||||
* <p>
|
||||
* Put into L2 cache such that we know the type of a bean with inheritance.
|
||||
*/
|
||||
public class CachedBeanId implements Externalizable {
|
||||
|
||||
private String discValue;
|
||||
private Object id;
|
||||
|
||||
public CachedBeanId(String discValue, Object id) {
|
||||
this.discValue = discValue;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct from serialisation.
|
||||
*/
|
||||
public CachedBeanId() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeUTF(discValue);
|
||||
out.writeObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
discValue = in.readUTF();
|
||||
id = in.readObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return discValue + ":" + id;
|
||||
}
|
||||
|
||||
public String getDiscValue() {
|
||||
return discValue;
|
||||
}
|
||||
|
||||
public Object getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -134,7 +134,7 @@ class DefaultCacheHolder {
|
||||
}
|
||||
|
||||
private ServerCacheOptions getQueryOptions(Class<?> cls) {
|
||||
CacheQueryTuning tuning = AnnotationUtil.findAnnotation(cls, CacheQueryTuning.class);
|
||||
CacheQueryTuning tuning = AnnotationUtil.get(cls, CacheQueryTuning.class);
|
||||
if (tuning != null) {
|
||||
return new ServerCacheOptions(tuning).applyDefaults(queryDefault);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class DefaultChangeLogRegister implements ChangeLogRegister {
|
||||
* Find and return the ChangeLog annotation in the inheritance hierarchy.
|
||||
*/
|
||||
private ChangeLog getChangeLog(Class<?> beanType) {
|
||||
return AnnotationUtil.findAnnotationRecursive(beanType, ChangeLog.class);
|
||||
return AnnotationUtil.typeGet(beanType, ChangeLog.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
|
||||
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;
|
||||
@@ -75,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();
|
||||
}
|
||||
@@ -122,7 +126,6 @@ public class DatabasePlatformFactory {
|
||||
if (dbName.equals("hana")) {
|
||||
return new HanaPlatform();
|
||||
}
|
||||
|
||||
throw new RuntimeException("database platform " + dbName + " is not known?");
|
||||
}
|
||||
|
||||
@@ -159,12 +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:
|
||||
}
|
||||
}
|
||||
@@ -699,7 +687,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Create and return a new reference bean matching this beans Id value.
|
||||
*/
|
||||
public T createReference() {
|
||||
return beanDescriptor.createReference(getBeanId(), null);
|
||||
return beanDescriptor.createRef(getBeanId(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -473,6 +473,6 @@ public class BootupClasses implements ClassFilter {
|
||||
* Returns true if this class has the annotation (or meta annotation). Does not search recursively.
|
||||
*/
|
||||
private boolean has(Class<?> cls, Class<? extends Annotation> ann) {
|
||||
return AnnotationUtil.findAnnotation(cls, ann) != null;
|
||||
return AnnotationUtil.has(cls, ann);
|
||||
}
|
||||
}
|
||||
|
||||
+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);
|
||||
}
|
||||
@@ -2053,14 +1923,16 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Create a non read only reference bean without checking cacheSharableBeans.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T createReference(Object id, PersistenceContext pc) {
|
||||
if (inheritInfo != null && !inheritInfo.isConcrete()) {
|
||||
return findReferenceBean(id, pc);
|
||||
}
|
||||
return createRef(id, pc);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T createRef(Object id, PersistenceContext pc) {
|
||||
try {
|
||||
if (inheritInfo != null && !inheritInfo.isConcrete()) {
|
||||
return findReferenceBean(id, pc);
|
||||
}
|
||||
|
||||
EntityBean eb = createEntityBean();
|
||||
id = convertSetId(id, eb);
|
||||
EntityBeanIntercept ebi = eb._ebean_getIntercept();
|
||||
@@ -2071,7 +1943,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
ebi.setPersistenceContext(pc);
|
||||
}
|
||||
return (T) eb;
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new PersistenceException(ex);
|
||||
}
|
||||
@@ -2085,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;
|
||||
}
|
||||
@@ -2113,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());
|
||||
}
|
||||
@@ -2146,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;
|
||||
@@ -2167,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;
|
||||
@@ -2194,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
|
||||
@@ -2314,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);
|
||||
@@ -2324,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);
|
||||
@@ -2358,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);
|
||||
@@ -2384,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) {
|
||||
@@ -2397,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) {
|
||||
@@ -2419,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);
|
||||
}
|
||||
@@ -2442,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;
|
||||
@@ -2467,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
|
||||
@@ -2476,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<>();
|
||||
@@ -2538,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);
|
||||
@@ -2559,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) {
|
||||
@@ -2567,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);
|
||||
}
|
||||
@@ -2638,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) {
|
||||
@@ -2665,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2679,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;
|
||||
@@ -2780,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) {
|
||||
@@ -2797,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) {
|
||||
@@ -2814,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;
|
||||
@@ -2836,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);
|
||||
}
|
||||
}
|
||||
@@ -2900,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() {
|
||||
@@ -3011,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();
|
||||
@@ -3149,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;
|
||||
@@ -3197,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;
|
||||
@@ -3251,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();
|
||||
@@ -3328,7 +3150,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
|
||||
public ConcurrencyMode getConcurrencyMode(EntityBeanIntercept ebi) {
|
||||
|
||||
if (!hasVersionProperty(ebi)) {
|
||||
return ConcurrencyMode.NONE;
|
||||
} else {
|
||||
@@ -3349,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3367,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());
|
||||
}
|
||||
@@ -3408,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;
|
||||
@@ -3482,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.
|
||||
*/
|
||||
@@ -1002,6 +986,15 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
orderProperty.setDbInsertable(orderColumn.isInsertable());
|
||||
orderProperty.setDbUpdateable(orderColumn.isUpdatable());
|
||||
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);
|
||||
}
|
||||
@@ -1401,13 +1394,13 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
String primaryKeyColumn = desc.getSinglePrimaryKeyColumn();
|
||||
seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn);
|
||||
}
|
||||
int increment = desc.setIdentitySequenceDefaultIncrement();
|
||||
desc.setIdGenerator(createSequenceIdGenerator(seqName, increment));
|
||||
int stepSize = desc.setIdentitySequenceBatchMode(databasePlatform.isSequenceBatchMode());
|
||||
desc.setIdGenerator(createSequenceIdGenerator(seqName, stepSize));
|
||||
}
|
||||
}
|
||||
|
||||
private PlatformIdGenerator createSequenceIdGenerator(String seqName, int increment) {
|
||||
return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, increment, seqName);
|
||||
private PlatformIdGenerator createSequenceIdGenerator(String seqName, int stepSize) {
|
||||
return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, stepSize, seqName);
|
||||
}
|
||||
|
||||
private void createByteCode(DeployBeanDescriptor<?> deploy) {
|
||||
@@ -1591,7 +1584,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
private boolean isMappedSuperWithNoProperties(Class<?> beanClass) {
|
||||
// do not search recursive here
|
||||
MappedSuperclass annotation = AnnotationUtil.findAnnotation(beanClass, MappedSuperclass.class);
|
||||
MappedSuperclass annotation = AnnotationUtil.get(beanClass, MappedSuperclass.class);
|
||||
if (annotation == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -13,6 +13,7 @@ import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.cache.CachedBeanData;
|
||||
import io.ebeaninternal.server.cache.CachedBeanId;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
@@ -433,11 +434,19 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
if (embedded) {
|
||||
return targetDescriptor.cacheEmbeddedBeanExtract((EntityBean) ap);
|
||||
} else if (targetInheritInfo != null) {
|
||||
return createCacheBeanId(ap);
|
||||
} else {
|
||||
return targetDescriptor.getIdProperty().getCacheDataValue((EntityBean) ap);
|
||||
}
|
||||
}
|
||||
|
||||
private Object createCacheBeanId(Object bean) {
|
||||
final BeanDescriptor<?> desc = targetDescriptor.descOf(bean.getClass());
|
||||
final Object id = desc.getIdProperty().getCacheDataValue((EntityBean) bean);
|
||||
return new CachedBeanId(desc.getDiscValue(), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(Object value) {
|
||||
return targetDescriptor.getIdBinder().cacheKey(value);
|
||||
@@ -451,19 +460,31 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
if (embedded) {
|
||||
setValue(bean, targetDescriptor.cacheEmbeddedBeanLoad((CachedBeanData) cacheData, context));
|
||||
} else {
|
||||
if (cacheData instanceof String) {
|
||||
cacheData = targetDescriptor.getIdProperty().scalarType.parse((String) cacheData);
|
||||
if (cacheData instanceof CachedBeanId) {
|
||||
setValue(bean, refInheritBean((CachedBeanId) cacheData, context));
|
||||
} else {
|
||||
setValue(bean, refBean(targetDescriptor, cacheData, context));
|
||||
}
|
||||
// cacheData is the id value, maybe already in persistence context
|
||||
Object assocBean = targetDescriptor.contextGet(context, cacheData);
|
||||
if (assocBean == null) {
|
||||
assocBean = targetDescriptor.createReference(cacheData, context);
|
||||
}
|
||||
setValue(bean, assocBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object refInheritBean(CachedBeanId cacheId, PersistenceContext context) {
|
||||
final InheritInfo rowInheritInfo = targetInheritInfo.readType(cacheId.getDiscValue());
|
||||
return refBean(rowInheritInfo.desc(), cacheId.getId(), context);
|
||||
}
|
||||
|
||||
private Object refBean(BeanDescriptor<?> desc, Object id, PersistenceContext context) {
|
||||
if (id instanceof String) {
|
||||
id = desc.getIdProperty().scalarType.parse((String) id);
|
||||
}
|
||||
Object bean = desc.contextGet(context, id);
|
||||
if (bean == null) {
|
||||
bean = desc.createRef(id, context);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarDataReader<?> getIdReader() {
|
||||
return targetDescriptor.getIdProperty();
|
||||
@@ -640,14 +661,12 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
|
||||
@Override
|
||||
public void addTenant(SpiQuery<?> query, Object tenantId) {
|
||||
T refBean = targetDescriptor.createReference(tenantId, null);
|
||||
query.where().eq(name, refBean);
|
||||
query.where().eq(name, targetDescriptor.createRef(tenantId, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTenantValue(EntityBean entityBean, Object tenantId) {
|
||||
T refBean = targetDescriptor.createReference(tenantId, null);
|
||||
setValue(entityBean, refBean);
|
||||
setValue(entityBean, targetDescriptor.createRef(tenantId, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user