Compare commits

..
Author SHA1 Message Date
rob bygrave 30ad4260f0 [maven-release-plugin] prepare release ebean-12.3.5 2020-06-29 17:21:08 +12:00
rob bygrave ab421d6f2b No effective change - trim whitespace in BaseTableDdl 2020-06-29 17:15:35 +12:00
rob bygrave 0b807addc7 No effective change - test tidy 2020-06-29 17:09:02 +12:00
rob bygrave bb03ef5693 No effective change - test tidy 2020-06-29 15:51:34 +12:00
rob bygrave 248a7b1062 #2004 - MariaDB DDL review 2020-06-29 15:50:08 +12:00
rob bygrave 9da28262b4 #2004 - MariaDB History useLegacyDatetimeCode=false required to properly handle server timezone 2020-06-29 14:51:24 +12:00
rob bygrave fc8dd18fd1 #2004 - MariaDB History DDL using add/drop system versioning 2020-06-29 14:03:21 +12:00
rob bygrave 70ba658442 #2026 - Postgres DDL - modify type generates wrong alter sql. Needs explicit cast / using clause. 2020-06-29 12:12:10 +12:00
rob bygrave 70b730657f #2028 - Refactor internals of smart persistence context to reference "parent" rather than copy beans (adjustment of #2021) 2020-06-29 08:48:24 +12:00
rob bygrave bf3cb7e2e2 No effective change - tidy test BeanDescriptor_registerTest 2020-06-26 15:49:35 +12:00
rob bygrave c2708ecc76 No effective change - Refactor tidy BeanDescriptor internals - trimQueryPlans() etc 2020-06-23 22:14:11 +12:00
rob bygrave 896aba3053 No effective change - trim whitespace 2020-06-23 22:03:21 +12:00
rob bygrave f78b602a90 No effective change - trim whitespace 2020-06-23 21:08:30 +12:00
rob bygrave 306c1b5cf6 No effective change - EntityBeanIntercept tidy javadoc 2020-06-23 20:57:31 +12:00
rob bygrave cdf2718ca9 No effective change - refactor EntityBeanIntercept reverse internal notEqual() method 2020-06-23 20:43:08 +12:00
rob bygrave 967ca0c9f3 No effective change - add some more tests for EntityBeanIntercept 2020-06-23 17:09:15 +12:00
rob bygrave 607acb1b4e No effective change - tidy DefaultPersistenceContext 2020-06-23 16:35:53 +12:00
rob bygrave 5f923945e9 No effective change - trim whitespace 2020-06-23 16:35:36 +12:00
rob bygrave 11fbe8bf5b [maven-release-plugin] prepare for next development iteration 2020-06-19 23:23:32 +12:00
rob bygrave 0544fa7ab4 [maven-release-plugin] prepare release ebean-12.3.4 2020-06-19 23:23:21 +12:00
rob bygrave 51eed9bfe4 Testing - default to h2 2020-06-19 23:20:46 +12:00
Rob BygraveandGitHub 32ce1f1d59 #2004 - MariaDb specific platform with @History support (initial) (#2023)
#2004 - MariaDb specific platform with @History support (initial)
2020-06-19 23:20:07 +12:00
rob bygrave f445c118a0 #2025 - Make findStream & findLargeStream the same - deprecate findLargeStream 2020-06-19 23:17:47 +12:00
rob bygrave a8ff918bb3 #2024 - Change default "fetchBuffer" for findEach/iterate/stream queries to 100 2020-06-19 22:36:24 +12:00
rob bygrave 729aeae0d7 No effective change - trim whitespace 2020-06-19 22:13:50 +12:00
rob bygrave 17a9341287 #2006 - Auto register AttributeConverter with Ebean (using @Converter) 2020-06-19 20:33:58 +12:00
rob bygrave c863014fec Javadoc improvements for #2022 - use of index params like ?1, ?2, ?3 etc 2020-06-18 16:04:13 +12:00
rob bygrave 8170db4101 Bump ebean-annotation which has MariaDB platform enum
As a step for #2004
Means we can add a specific platform for MariaDB separate from MySql
2020-06-18 11:30:43 +12:00
rob bygrave 434a5117a2 Bump to ebean-datasource 5.1
This will expose pool size which can be exposed as a gauge metric (the current size of main connection pool and readonly connection pool)
2020-06-18 11:10:01 +12:00
rob bygrave 85ed8cd784 Test update only - TestSqlUpdateBindMultipleLists 2020-06-18 11:07:45 +12:00
rob bygrave 9b70900267 #2021 - Use smarter persistence context for findEach/iterate/stream queries - improve performance of un-tuned findEach/iterate/stream queries that invoke lazy loading 2020-06-17 21:50:11 +12:00
rob bygrave 0e33d58bb2 Bump ebean-maven-plugin to 12.3.4 2020-06-17 21:47:14 +12:00
rob bygrave 9d9d13060c #2020 - Fix potential connection leak in query-iterator WITH lazyLoadBufferSize (Refer #1164) 2020-06-17 17:10:16 +12:00
rob bygrave e071eecd59 Tidy test only - TestQueryFindEachWhile 2020-06-17 15:37:58 +12:00
rob bygrave bc8e254a65 Tidy tests - delete duplicate test TestQueryFindVisit.java 2020-06-17 13:31:18 +12:00
rob bygrave e0e369904c Tidy test only - TestQueryFindIterate 2020-06-17 13:30:34 +12:00
rob bygrave bd85ba17a1 No effective change - tidy TestQueryFindEach 2020-06-16 17:00:23 +12:00
rob bygrave 8801b8d8ca No effective change - tidy TestQueryFindEach 2020-06-16 16:55:49 +12:00
rob bygrave ebd6c535ab No effective change - tidy TestRawSqlParsing 2020-06-12 15:04:36 +12:00
rob bygrave 5c771a7219 Add tests for RawSql with ${where} and ${andWhere} placeholders 2020-06-12 15:03:05 +12:00
rob bygrave eb6f722385 Put back javadoc maven plugin 2020-06-09 20:03:47 +12:00
rob bygrave cef4f59b2c [maven-release-plugin] prepare for next development iteration 2020-06-08 21:58:31 +12:00
rob bygrave 67352ab3b2 [maven-release-plugin] prepare release ebean-12.3.3 2020-06-08 21:58:19 +12:00
rob bygrave 4a21395cda Comment out javadoc maven plugin temporarily 2020-06-08 21:56:07 +12:00
rob bygrave fcd7f8b6cf Bump ebean-maven-plugin 2020-06-08 20:00:22 +12:00
rob bygrave 4565719728 #2017 - ENH: Add option for different initialDelay to BackgroundExecutor.executePeriodically() 2020-06-05 18:31:07 +12:00
rob bygrave cbdf5609b2 #2015 - Dto instance id fields are not populated when queried using DtoQuery (and no select clause is specified) 2020-05-27 22:05:39 +12:00
rob bygrave f97359502b Refactor internals - DtoMeta DtoMappingRequest
Refactor move most of the code mapping DB columns to bean setters from DtoMeta to DtoMappingRequest
2020-05-27 22:04:03 +12:00
rob bygrave ffaf00c064 Improve error message throw on DTO unmapped DB column 2020-05-27 21:39:16 +12:00
rob bygrave f6f9511d2a #2014 - IllegalArgumentException: No rule for io.ebeaninternal.dbmigration.migration.RenameColumn 2020-05-27 21:00:42 +12:00
rob bygrave 5e94c26094 No effective change - update TestQueryInIdTypeConversion 2020-05-27 15:53:01 +12:00
rob bygrave 5aaed4e12b #2016 - L2 Cache - missing puts with explicit bean cache + findByIds/keys + after transaction writes 2020-05-26 23:42:44 +12:00
rob bygrave c29a57b5ec No effective change - update TestBeanCache Ebean -> DB 2020-05-26 23:19:23 +12:00
rob bygrave 998b8cc192 #2011 - Change query.orderBy() to not be deprecated 2020-05-15 14:06:28 +12:00
rob bygrave 9c1d930fb8 #2012 - Change ExpressionList.orderBy(String) to return ExpressionList<T>
Plus #2011 remove @Deprecated for orderBy
2020-05-15 14:06:06 +12:00
rob bygrave d6d7537a9c #2012 - Change ExpressionList.orderBy(String) to return ExpressionList<T> 2020-05-15 14:05:15 +12:00
rob bygrave 92e83c49d9 [maven-release-plugin] prepare for next development iteration 2020-05-12 22:39:42 +12:00
99 changed files with 8270 additions and 1042 deletions
+12 -5
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.3.2</version>
<version>12.3.5</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-12.3.2</tag>
<tag>ebean-12.3.5</tag>
</scm>
<profiles>
@@ -87,7 +87,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>6.11</version>
<version>6.12</version>
</dependency>
<dependency>
@@ -99,7 +99,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-datasource</artifactId>
<version>4.9.2</version>
<version>5.1</version>
</dependency>
<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.3.2</version>
<version>12.3.4</version>
<executions>
<execution>
<id>test</id>
@@ -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.
*
+50 -18
View File
@@ -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);
+11 -5
View File
@@ -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);
/**
+22 -9
View File
@@ -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);
/**
+37
View File
@@ -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);
+44 -22
View File
@@ -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();
@@ -82,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.
*/
@@ -371,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;
@@ -391,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;
@@ -423,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;
@@ -445,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
@@ -453,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;
@@ -471,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();
@@ -495,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;
@@ -517,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;
@@ -605,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;
@@ -660,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);
@@ -674,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;
}
@@ -712,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);
@@ -734,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);
@@ -758,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) {
@@ -818,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')
@@ -844,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
}
@@ -879,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);
}
/**
@@ -945,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();
@@ -988,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);
}
}
@@ -1001,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) {
@@ -1013,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) {
@@ -1025,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) {
@@ -1037,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) {
@@ -1049,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) {
@@ -1061,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) {
@@ -1073,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) {
@@ -1085,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) {
@@ -1097,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)) {
@@ -1109,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)) {
@@ -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>
@@ -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 {};
}
@@ -0,0 +1,38 @@
package io.ebean.config.dbplatform.mariadb;
import io.ebean.config.dbplatform.DbStandardHistorySupport;
/**
* History support for MariaDB.
*/
public class MariaDbHistorySupport extends DbStandardHistorySupport {
/**
* Return the ' as of timestamp ?' clause appended after the table name.
*/
@Override
public String getAsOfViewSuffix(String asOfViewSuffix) {
return " for system_time as of ?";
}
@Override
public String getVersionsBetweenSuffix(String asOfViewSuffix) {
return " for system_time between ? and ?";
}
/**
* Returns the SQL Server specific effective start column.
*/
@Override
public String getSysPeriodLower(String tableAlias, String sysPeriod) {
return tableAlias + ".row_start";
}
/**
* Returns the SQL Server specific effective end column.
*/
@Override
public String getSysPeriodUpper(String tableAlias, String sysPeriod) {
return tableAlias + ".row_end";
}
}
@@ -0,0 +1,16 @@
package io.ebean.config.dbplatform.mariadb;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.mysql.BaseMySqlPlatform;
/**
* MariaDB platform.
*/
public class MariaDbPlatform extends BaseMySqlPlatform {
public MariaDbPlatform() {
super();
this.platform = Platform.MARIADB;
this.historySupport = new MariaDbHistorySupport();
}
}
@@ -0,0 +1,68 @@
package io.ebean.config.dbplatform.mysql;
import io.ebean.Query;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.SqlErrorCodes;
import java.sql.Types;
/**
* Base platform for both MySql and MariaDB.
*/
public abstract class BaseMySqlPlatform extends DatabasePlatform {
public BaseMySqlPlatform() {
super();
this.useExtraTransactionOnIterateSecondaryQueries = true;
this.selectCountWithAlias = true;
this.supportsSavepointId = false;
this.inlineSqlUpdateLimit = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.historySupport = new MySqlHistorySupport();
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
this.dbIdentity.setSupportsSequence(false);
this.dbDefaultValue.setNow("now(6)"); // must have same precision as TIMESTAMP
this.dbDefaultValue.setFalse("0");
this.dbDefaultValue.setTrue("1");
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("1205")
.addDuplicateKey("1062", "1169")
.addDataIntegrity("630", "839", "840", "893", "1215", "1216", "1217", "1364", "1451", "1452", "1557")
.build();
this.openQuote = "`";
this.closeQuote = "`";
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
this.likeClauseRaw = "like ? escape''";
this.likeClauseEscaped = "like ? escape'|'";
this.forwardOnlyHintOnFindIterate = true;
this.booleanDbType = Types.BIT;
dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1)"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1)"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime(6)"));
dbTypeMap.put(DbType.CLOB, new MySqlClob());
dbTypeMap.put(DbType.BLOB, new MySqlBlob());
dbTypeMap.put(DbType.BINARY, new DbPlatformType("binary", 255));
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false));
dbTypeMap.put(DbType.JSONB, new DbPlatformType("json", false));
}
@Override
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
// NOWAIT and SKIP LOCKED currently not supported with MySQL
return sql + " for update";
}
}
@@ -4,7 +4,7 @@ import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
public class MySql55Platform extends MySqlPlatform {
public class MySql55Platform extends BaseMySqlPlatform {
public MySql55Platform() {
super();
@@ -1,77 +1,15 @@
package io.ebean.config.dbplatform.mysql;
import io.ebean.Query;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.SqlErrorCodes;
import java.sql.Types;
/**
* MySQL specific platform.
* <p>
* <ul>
* <li>supportsGetGeneratedKeys = true</li>
* <li>Uses LIMIT OFFSET clause</li>
* <li>Uses ` for quoted identifiers</li>
* </ul>
* </p>
*/
public class MySqlPlatform extends DatabasePlatform {
public class MySqlPlatform extends BaseMySqlPlatform {
public MySqlPlatform() {
super();
this.platform = Platform.MYSQL;
this.useExtraTransactionOnIterateSecondaryQueries = true;
this.selectCountWithAlias = true;
this.supportsSavepointId = false;
this.inlineSqlUpdateLimit = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.historySupport = new MySqlHistorySupport();
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
this.dbIdentity.setSupportsSequence(false);
this.dbDefaultValue.setNow("now(6)"); // must have same precision as TIMESTAMP
this.dbDefaultValue.setFalse("0");
this.dbDefaultValue.setTrue("1");
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("1205")
.addDuplicateKey("1062", "1169")
.addDataIntegrity("630", "839", "840", "893", "1215", "1216", "1217", "1364", "1451", "1452", "1557")
.build();
this.openQuote = "`";
this.closeQuote = "`";
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
this.likeClauseRaw = "like ? escape''";
this.likeClauseEscaped = "like ? escape'|'";
this.forwardOnlyHintOnFindIterate = true;
this.booleanDbType = Types.BIT;
dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1)"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1)"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime(6)"));
dbTypeMap.put(DbType.CLOB, new MySqlClob());
dbTypeMap.put(DbType.BLOB, new MySqlBlob());
dbTypeMap.put(DbType.BINARY, new DbPlatformType("binary", 255));
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false));
dbTypeMap.put(DbType.JSONB, new DbPlatformType("json", false));
}
@Override
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
// NOWAIT and SKIP LOCKED currently not supported with MySQL
return sql + " for update";
}
}
@@ -788,7 +788,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
/**
* Set to true when we only include the Id property if it is explicitly included in the select().
*/
void setManualId(boolean manualId);
void setManualId();
/**
* Set default select clauses where none have been explicitly defined.
@@ -313,4 +313,9 @@ public interface SpiTransaction extends Transaction {
* Return true when nested transactions should create Savepoints.
*/
boolean isNestedUseSavepoint();
/**
* Return true if explicitly set to skip cache (ignores skipOnWrite).
*/
boolean isSkipCacheExplicit();
}
@@ -158,6 +158,11 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
transaction.setSkipCache(skipCache);
}
@Override
public boolean isSkipCacheExplicit() {
return transaction.isSkipCacheExplicit();
}
@Override
public boolean isSkipCache() {
return transaction.isSkipCache();
@@ -15,6 +15,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mariadb.MariaDbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
@@ -820,6 +821,8 @@ public class DefaultDbMigration implements DbMigration {
return new Postgres9Platform();
case POSTGRES:
return new PostgresPlatform();
case MARIADB:
return new MariaDbPlatform();
case MYSQL55:
return new MySql55Platform();
case MYSQL:
@@ -111,11 +111,9 @@ public class BaseTableDdl implements TableDdl {
this.columnName = column.getName();
this.defaultValue = platformDdl.convertDefaultValue(column.getDefaultValue());
boolean alterNotNull = Boolean.TRUE.equals(column.isNotnull());
if (column.getBefore().isEmpty() && alterNotNull && defaultValue == null) {
handleStrictError(tableName, columnName);
}
before = getScriptsForPlatform(column.getBefore());
after = getScriptsForPlatform(column.getAfter());
this.withHistory = withHistory;
@@ -127,10 +125,8 @@ public class BaseTableDdl implements TableDdl {
DdlMigrationHelp(AlterColumn alter) {
this.tableName = alter.getTableName();
this.columnName = alter.getColumnName();
String tmp = alter.getDefaultValue() != null ? alter.getDefaultValue() : alter.getCurrentDefaultValue();
this.defaultValue = platformDdl.convertDefaultValue(tmp);
boolean alterNotNull = Boolean.TRUE.equals(alter.isNotnull());
// here we add the platform's default update script
withHistory = isTrue(alter.isWithHistory());
@@ -201,7 +197,6 @@ public class BaseTableDdl implements TableDdl {
public String getDefaultValue() {
return defaultValue;
}
}
/**
@@ -241,7 +236,6 @@ public class BaseTableDdl implements TableDdl {
*/
@Override
public void generate(DdlWrite writer, CreateTable createTable) throws IOException {
reset();
String tableName = lowerTableName(createTable.getName());
@@ -275,7 +269,6 @@ public class BaseTableDdl implements TableDdl {
if (platformDdl.isInlineForeignKeys()) {
writeInlineForeignKeys(writer, createTable);
}
apply.newLine().append(")");
addTableStorageEngine(apply, createTable);
addTableCommentInline(apply, createTable);
@@ -285,9 +278,7 @@ public class BaseTableDdl implements TableDdl {
apply.endOfStatement();
addComments(apply, createTable);
writeUniqueOneToOneConstraints(writer, createTable);
if (isTrue(createTable.isWithHistory())) {
// create history with rollback before the
// associated drop table is written to rollback
@@ -305,7 +296,6 @@ public class BaseTableDdl implements TableDdl {
// add blank line for a bit of whitespace between tables
apply.end();
writer.dropAll().end();
if (!platformDdl.isInlineForeignKeys()) {
writeAddForeignKeys(writer, createTable);
}
@@ -324,7 +314,6 @@ public class BaseTableDdl implements TableDdl {
if (hasValue(tableComment)) {
platformDdl.addTableComment(apply, createTable.getName(), tableComment);
}
for (Column column : createTable.getColumn()) {
if (!StringHelper.isNull(column.getComment())) {
platformDdl.addColumnComment(apply, createTable.getName(), column.getName(), column.getComment());
@@ -427,7 +416,6 @@ public class BaseTableDdl implements TableDdl {
writeForeignKey(write, createTable.getName(), column);
}
}
writeAddCompoundForeignKeys(write, createTable);
}
@@ -448,7 +436,6 @@ public class BaseTableDdl implements TableDdl {
// no matching unique constraint so add the index
fkeyBuffer.appendStatement(platformDdl.createIndex(new WriteCreateIndex(request.indexName(), tableName, request.cols(), false)));
}
alterTableAddForeignKey(write.getOptions(), fkeyBuffer, request);
fkeyBuffer.end();
@@ -456,7 +443,6 @@ public class BaseTableDdl implements TableDdl {
if (hasValue(request.indexName())) {
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(request.indexName(), tableName));
}
write.dropAllForeignKeys().end();
}
@@ -513,9 +499,7 @@ public class BaseTableDdl implements TableDdl {
* Write the unique constraints inline with the create table statement.
*/
protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
boolean inlineUniqueWhenNullable = platformDdl.isInlineUniqueWhenNullable();
List<Column> columns = new WriteUniqueConstraint(createTable.getColumn()).uniqueKeys();
for (Column column : columns) {
if (Boolean.TRUE.equals(column.isNotnull()) || inlineUniqueWhenNullable) {
@@ -532,12 +516,10 @@ public class BaseTableDdl implements TableDdl {
* Write the unique constraint inline with the create table statement.
*/
protected void inlineUniqueConstraintSingle(DdlBuffer buffer, Column column) throws IOException {
String uqName = column.getUnique();
if (uqName == null) {
uqName = column.getUniqueOneToOne();
}
buffer.append(",").newLine();
buffer.append(" constraint ").append(uqName).append(" unique ");
buffer.append("(");
@@ -549,7 +531,6 @@ public class BaseTableDdl implements TableDdl {
* Write the primary key constraint inline with the create table statement.
*/
protected void writePrimaryKeyConstraint(DdlBuffer buffer, String pkName, String[] pkColumns) throws IOException {
buffer.append(",").newLine();
buffer.append(" constraint ").append(pkName).append(" primary key");
appendColumns(pkColumns, buffer);
@@ -559,7 +540,6 @@ public class BaseTableDdl implements TableDdl {
* Return as an array of string column names.
*/
protected String[] toColumnNames(List<Column> columns) {
String[] cols = new String[columns.size()];
for (int i = 0; i < cols.length; i++) {
cols[i] = columns.get(i).getName();
@@ -682,13 +662,11 @@ public class BaseTableDdl implements TableDdl {
*/
@Override
public void generate(DdlWrite writer, AddColumn addColumn) throws IOException {
String tableName = addColumn.getTableName();
List<Column> columns = addColumn.getColumn();
for (Column column : columns) {
alterTableAddColumn(writer.apply(), tableName, column, false, isTrue(addColumn.isWithHistory()));
}
if (isTrue(addColumn.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
// make same changes to the history table
String historyTable = historyTable(tableName);
@@ -697,14 +675,11 @@ public class BaseTableDdl implements TableDdl {
alterTableAddColumn(writer.apply(), historyTable, column, true, true);
}
}
for (Column column : columns) {
if (hasValue(column.getReferences())) {
writeForeignKey(writer, tableName, column);
}
}
// add a bit of whitespace
writer.apply().end();
}
@@ -713,9 +688,7 @@ public class BaseTableDdl implements TableDdl {
*/
@Override
public void generate(DdlWrite writer, DropTable dropTable) throws IOException {
dropTable(writer.apply(), dropTable.getName());
if (hasValue(dropTable.getSequenceCol())
&& platformDdl.getPlatform().getDbIdentity().isSupportsSequence()) {
String sequenceName = dropTable.getSequenceName();
@@ -731,9 +704,7 @@ public class BaseTableDdl implements TableDdl {
*/
@Override
public void generate(DdlWrite writer, DropColumn dropColumn) throws IOException {
String tableName = dropColumn.getTableName();
alterTableDropColumn(writer.apply(), tableName, dropColumn.getColumnName());
if (isTrue(dropColumn.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
@@ -741,7 +712,6 @@ public class BaseTableDdl implements TableDdl {
regenerateHistoryTriggers(tableName, HistoryTableUpdate.Change.DROP, dropColumn.getColumnName());
alterTableDropColumn(writer.apply(), historyTable(tableName), dropColumn.getColumnName());
}
writer.apply().end();
}
@@ -826,7 +796,6 @@ public class BaseTableDdl implements TableDdl {
* Register the base table that we need to regenerate the history triggers on.
*/
protected void regenerateHistoryTriggers(String baseTableName, HistoryTableUpdate.Change change, String column) {
HistoryTableUpdate update = regenerateHistoryTriggers.computeIfAbsent(baseTableName, HistoryTableUpdate::new);
update.add(change, column);
}
@@ -836,7 +805,6 @@ public class BaseTableDdl implements TableDdl {
* Will be called, if there is a type, dbdefault or notnull change.
*/
protected void alterColumnBaseAttributes(DdlWrite writer, AlterColumn alter) throws IOException {
String ddl = platformDdl.alterColumnBaseAttributes(alter);
if (hasValue(ddl)) {
writer.apply().appendStatement(ddl);
@@ -857,27 +825,22 @@ public class BaseTableDdl implements TableDdl {
}
protected void alterColumnDefaultValue(DdlWrite writer, AlterColumn alter) throws IOException {
writer.apply().appendStatement(platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue()));
}
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter, String constraintName) throws IOException {
writer.apply().appendStatement(platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName));
}
protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
writer.apply().appendStatement(platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint()));
}
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) throws IOException {
writer.apply().appendStatement(platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull()));
}
protected void alterColumnType(DdlWrite writer, AlterColumn alter) throws IOException {
String ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType());
if (hasValue(ddl)) {
writer.apply().appendStatement(ddl);
@@ -891,33 +854,26 @@ public class BaseTableDdl implements TableDdl {
}
protected void alterColumnAddForeignKey(DdlWrite writer, AlterColumn alterColumn) throws IOException {
alterTableAddForeignKey(writer.getOptions(), writer.apply(), new WriteForeignKey(alterColumn));
}
protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()));
}
protected void alterColumnDropUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()));
}
protected void alterColumnAddUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
addUniqueConstraint(writer, alter, alter.getUniqueOneToOne());
}
protected void alterColumnAddUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
addUniqueConstraint(writer, alter, alter.getUnique());
}
protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter, String uqName) throws IOException {
String[] cols = {alter.getColumnName()};
boolean notNull = alter.isNotnull() != null ? alter.isNotnull() : Boolean.TRUE.equals(alter.isNotnull());
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols));
@@ -965,11 +921,4 @@ public class BaseTableDdl implements TableDdl {
return Boolean.TRUE.equals(value);
}
/**
* Return as an int value with 0 when it is null.
*/
protected int toInt(BigInteger value) {
return (value == null) ? 0 : value.intValue();
}
}
@@ -0,0 +1,14 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
/**
* MariaDB platform DDL.
*/
public class MariaDbDdl extends MySqlDdl {
public MariaDbDdl(DatabasePlatform platform) {
super(platform);
this.historyDdl = new MariaDbHistoryDdl();
}
}
@@ -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
}
}
@@ -54,4 +54,9 @@ public class PostgresDdl extends PlatformDdl {
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
return asIdentityStandardOptions(columnDefn, identity);
}
@Override
public String alterColumnType(String tableName, String columnName, String type) {
return super.alterColumnType(tableName, columnName, type) + " using " + columnName + "::" + convert(type);
}
}
@@ -15,7 +15,7 @@ import java.util.Objects;
*/
public class MColumn {
private final String name;
private String name;
private String type;
private String checkConstraint;
private String checkConstraintName;
@@ -88,7 +88,6 @@ public class MColumn {
* Return a copy of this column used for creating the associated draft table.
*/
public MColumn copyForDraft() {
MColumn copy = new MColumn(name, type);
copy.draftOnly = draftOnly;
copy.checkConstraint = checkConstraint;
@@ -470,6 +469,14 @@ public class MColumn {
this.dbMigrationInfos = dbMigrationInfos;
}
/**
* Rename the column.
*/
public MColumn rename(String newName) {
this.name = newName;
return this;
}
/**
* Apply changes based on the AlterColumn request.
*/
@@ -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).
*/
@@ -8,6 +8,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mariadb.MariaDbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
@@ -76,11 +77,13 @@ public class DatabasePlatformFactory {
* Lookup the platform by name.
*/
private DatabasePlatform byDatabaseName(String dbName) {
dbName = dbName.toLowerCase();
if (dbName.equals("h2")) {
return new H2Platform();
}
if (dbName.equals("mariadb")) {
return new MariaDbPlatform();
}
if (dbName.equals("mysql")) {
return new MySqlPlatform();
}
@@ -123,7 +126,6 @@ public class DatabasePlatformFactory {
if (dbName.equals("hana")) {
return new HanaPlatform();
}
throw new RuntimeException("database platform " + dbName + " is not known?");
}
@@ -160,14 +162,16 @@ public class DatabasePlatformFactory {
return new OraclePlatform();
} else if (dbProductName.contains("microsoft")) {
throw new IllegalArgumentException("For SqlServer please explicitly choose either sqlserver16 or sqlserver17 as the platform via ServerConfig.setDatabasePlatformName. Refer to issue #1340 for more details");
} else if (dbProductName.contains("mysql")) {
return mysqlVersion(majorVersion, minorVersion);
} else if (dbProductName.contains("h2")) {
return new H2Platform();
} else if (dbProductName.contains("hsql database engine")) {
return new HsqldbPlatform();
} else if (dbProductName.contains("postgres")) {
return readPostgres(connection, majorVersion);
} else if (dbProductName.contains("mariadb")) {
return new MariaDbPlatform();
} else if (dbProductName.contains("mysql")) {
return mysqlVersion(majorVersion, minorVersion);
} else if (dbProductName.contains("nuo")) {
return new NuoDbPlatform();
} else if (dbProductName.contains("sqlite")) {
@@ -33,7 +33,6 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
@Override
public void execute(Runnable r) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
pool.execute(r);
} else {
@@ -50,10 +49,14 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
@Override
public void executePeriodically(Runnable r, long delay, TimeUnit unit) {
final Map<String, String> map = MDC.getCopyOfContextMap();
executePeriodically(r, delay, delay, unit);
}
@Override
public void executePeriodically(Runnable r, long initialDelay, long delay, TimeUnit unit) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
schedulePool.scheduleWithFixedDelay(r, delay, delay, unit);
schedulePool.scheduleWithFixedDelay(r, initialDelay, delay, unit);
} else {
schedulePool.scheduleWithFixedDelay(() -> {
MDC.setContextMap(map);
@@ -62,14 +65,13 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
} finally {
MDC.clear();
}
}, delay, delay, unit);
}, initialDelay, delay, unit);
}
}
@Override
public ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit unit) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
return schedulePool.schedule(r, delay, unit);
} else {
@@ -87,7 +89,6 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
return schedulePool.schedule(c, delay, unit);
} else {
@@ -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);
}
@@ -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);
@@ -117,12 +117,8 @@ class InitDataSource {
}
private DataSource create(DataSourceConfig dsConfig, boolean readOnly) {
DataSourceFactory factory = DataSourceFactory.get();
if (factory == null) {
throw new IllegalStateException("No DataSourceFactory service implementation found in class path. Missing dependency to ebean-datasource?");
}
String poolName = config.getName() + (readOnly ? "-ro" : "");
return factory.createPool(poolName, dsConfig);
return DataSourceFactory.create(poolName, dsConfig);
}
/**
@@ -503,7 +503,6 @@ public class InternalConfiguration {
* Create the DataTimeZone implementation to use.
*/
private DataTimeZone initDataTimeZone() {
String tz = serverConfig.getDataTimeZone();
if (tz == null) {
if (isMySql(getPlatform())) {
@@ -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()) {
@@ -316,7 +305,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
*/
@Override
public JsonReadOptions createJsonReadOptions() {
persistenceContext = getPersistenceContext(query, transaction);
if (query.getPersistenceContext() == null) {
query.setPersistenceContext(persistenceContext);
@@ -327,7 +315,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
loadContext = new DLoadContext(this, secondaryQueries);
jsonRead.setLoadContext(loadContext);
}
return jsonRead;
}
@@ -335,8 +322,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
* For iterate queries reset the persistenceContext and loadContext.
*/
public void flushPersistenceContextOnIterate() {
if (!iterateSingleContext) {
persistenceContext = new DefaultPersistenceContext();
if (persistenceContext.resetLimit()) {
persistenceContext = persistenceContext.forIterateReset();
loadContext.resetPersistenceContext(persistenceContext);
if (jsonRead != null) {
jsonRead.setPersistenceContext(persistenceContext);
@@ -350,7 +337,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
* transaction scoped.
*/
private PersistenceContext getPersistenceContext(SpiQuery<?> query, SpiTransaction t) {
// check if there is already a persistence context set which is the case
// when lazy loading or query joins are executed
PersistenceContext ctx = query.getPersistenceContext();
@@ -358,7 +344,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
// determine the scope (from the query and then server)
PersistenceContextScope scope = ebeanServer.getPersistenceContextScope(query);
return (scope == PersistenceContextScope.QUERY || t == null) ? new DefaultPersistenceContext() : t.getPersistenceContext();
if (scope == PersistenceContextScope.QUERY || t == null) {
return new DefaultPersistenceContext();
}
if (Type.ITERATE == query.getType()) {
return t.getPersistenceContext().forIterate();
} else {
return t.getPersistenceContext();
}
}
/**
@@ -578,6 +571,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
return cacheKey != null && query.getUseQueryCache().isPut();
}
public boolean isBeanCachePutMany() {
return !transaction.isSkipCacheExplicit() && query.isBeanCachePut();
}
public boolean isBeanCachePut() {
return !transaction.isSkipCache() && query.isBeanCachePut();
}
@@ -586,7 +583,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
* Merge in prior L2 bean cache hits with the query result.
*/
public void mergeCacheHits(BeanCollection<T> result) {
if (cacheBeans != null && !cacheBeans.isEmpty()) {
if (query.getType() == Type.MAP) {
mergeCacheHitsToMap(result);
@@ -188,7 +188,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
super(server, t, persistExecute);
this.entityBean = (EntityBean) bean;
this.intercept = entityBean._ebean_getIntercept();
@@ -204,7 +203,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
if (Flags.isRecurse(flags)) {
this.persistCascade = t.isPersistCascade();
}
if (this.type == Type.UPDATE) {
if (intercept.isNew()) {
// 'stateless update' - set loaded properties as dirty
@@ -279,7 +277,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Init the transaction and also check for batch on cascade escalation.
*/
public void initTransIfRequiredWithBatchCascade() {
if (createImplicitTransIfRequired()) {
docStoreMode = calcDocStoreMode(transaction, type);
}
@@ -317,9 +314,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
private void onUpdateGeneratedProperties() {
for (BeanProperty prop : beanDescriptor.propertiesGenUpdate()) {
GeneratedProperty generatedProperty = prop.getGeneratedProperty();
if (prop.isVersion()) {
if (isLoadedProperty(prop)) {
@@ -451,7 +446,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Return true if any of the given properties are dirty.
*/
public boolean hasDirtyProperty(int[] propertyPositions) {
for (int propertyPosition : propertyPositions) {
if (dirtyProperties[propertyPosition]) {
return true;
@@ -517,7 +511,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
@Override
public void docStoreUpdate(DocStoreUpdateContext txn) throws IOException {
switch (type) {
case INSERT:
beanDescriptor.docStoreInsert(idValue, this, txn);
@@ -554,7 +547,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
beanPersistMap.add(beanDescriptor, type, idValue);
}
@@ -564,19 +556,15 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
case INSERT:
beanPersistListener.inserted(bean);
break;
case UPDATE:
beanPersistListener.updated(bean, updatedProperties);
break;
case DELETE:
beanPersistListener.deleted(bean);
break;
case DELETE_SOFT:
beanPersistListener.softDeleted(bean);
break;
default:
}
}
@@ -785,7 +773,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
case INSERT:
executeInsert();
return -1;
case UPDATE:
if (beanPersistListener != null) {
// store the updated properties for sending later
@@ -793,15 +780,12 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
executeUpdate();
return -1;
case DELETE_SOFT:
prepareForSoftDelete();
executeSoftDelete();
return -1;
case DELETE:
return executeDelete();
default:
throw new RuntimeException("Invalid type " + type);
}
@@ -811,13 +795,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Soft delete is executed as update so we want to set deleted=true property.
*/
private void prepareForSoftDelete() {
beanDescriptor.setSoftDeleteValue(entityBean);
}
@Override
public int executeOrQueue() {
boolean batch = isBatchThisRequest();
try {
BatchControl control = transaction.getBatchControl();
@@ -827,7 +809,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
if (batch) {
control = persistExecute.createBatchControl(transaction);
return control.executeOrQueue(this, true);
} else {
return executeNoBatch();
}
@@ -926,7 +907,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
controllerPost();
}
setNotifyCache();
boolean isChangeLog = beanDescriptor.isChangeLog();
if (type == Type.UPDATE && (isChangeLog || notifyCache || docStoreMode == DocStoreMode.UPDATE)) {
// get the dirty properties for update notification to the doc store
@@ -935,16 +915,13 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
if (isChangeLog) {
changeLog();
}
// if bean persisted again then should result in an update
intercept.setLoaded();
if (isInsert()) {
postInsert();
}
addPostCommitListeners();
notifyCacheOnPostExecute();
if (isLogSummary()) {
logSummary();
}
@@ -994,7 +971,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
private void logSummary() {
String draft = (beanDescriptor.isDraftable() && !publish) ? " draft[true]" : "";
String name = beanDescriptor.getName();
switch (type) {
@@ -1136,7 +1112,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* of queue events or list of update events.
*/
public void addDocStoreUpdates(DocStoreUpdates docStoreUpdates) {
if (type == Type.UPDATE) {
beanDescriptor.docStoreUpdateEmbedded(this, docStoreUpdates);
}
@@ -1165,7 +1140,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* </p>
*/
private boolean determineUpdateAllLoadedProperties() {
Boolean txnUpdateAll = transaction.isUpdateAllLoadedProperties();
if (txnUpdateAll != null) {
// use the setting explicitly set on the transaction
@@ -1174,7 +1148,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
// if using batch use the server default setting
requestUpdateAllLoadedProps = isBatchThisRequest() && ebeanServer.isUpdateAllPropertiesInBatch();
}
return requestUpdateAllLoadedProps;
}
@@ -1196,25 +1169,21 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Return the key for an update persist request.
*/
public String getUpdatePlanHash() {
StringBuilder key;
if (determineUpdateAllLoadedProperties()) {
key = intercept.getLoadedPropertyKey();
} else {
key = intercept.getDirtyPropertyKey();
}
BeanProperty versionProperty = beanDescriptor.getVersionProperty();
if (versionProperty != null) {
if (intercept.isLoadedProperty(versionProperty.getPropertyIndex())) {
key.append('v');
}
}
if (publish) {
key.append('p');
}
return key.toString();
}
@@ -1380,13 +1349,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Build the bean update for the L2 cache.
*/
public void addBeanUpdate(CacheChangeSet changeSet) {
if (!updatedManysOnly || collectionChanges != null) {
boolean updateNaturalKey = false;
String key = beanDescriptor.cacheKey(idValue);
Map<String, Object> changes = new LinkedHashMap<>();
EntityBean bean = getEntityBean();
boolean[] dirtyProperties = getDirtyProperties();
@@ -1410,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,6 +7,7 @@ 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.OracleDdl;
@@ -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);
@@ -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();
}
@@ -105,7 +105,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -219,9 +218,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
private final BeanProperty draftDirty;
/**
* Map of BeanProperty Linked so as to preserve order.
*/
private final LinkedHashMap<String, BeanProperty> propMap;
/**
@@ -234,44 +230,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
*/
private final Map<String, BeanPropertyAssoc<?>> tablePath = new HashMap<>();
/**
* The type of bean this describes.
*/
final Class<T> beanType;
final Class<?> rootBeanType;
/**
* This is not sent to a remote client.
*/
private final BeanDescriptorMap owner;
final String[] properties;
/**
* Intercept pre post on insert,update, and delete .
*/
private volatile BeanPersistController persistController;
private final BeanPostLoad beanPostLoad;
private final BeanPostConstructListener beanPostConstructListener;
/**
* Listens for post commit insert update and delete events.
*/
private volatile BeanPersistController persistController;
private volatile BeanPersistListener persistListener;
private final BeanQueryAdapter queryAdapter;
/**
* If set overrides the find implementation. Server side only.
*/
private final BeanFindController beanFinder;
/**
* Used for fine grain filtering for the change log.
*/
private final ChangeLogFilter changeLogFilter;
/**
@@ -281,22 +250,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
private final boolean abstractType;
/**
* Derived list of properties that make up the unique id.
*/
private final BeanProperty idProperty;
private final int idPropertyIndex;
/**
* Derived list of properties that are used for version concurrency checking.
*/
private final BeanProperty versionProperty;
private final int versionPropertyIndex;
private final BeanProperty whenModifiedProperty;
private final BeanProperty whenCreatedProperty;
/**
@@ -313,14 +272,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Scalar mutable properties (need to dirty check on update).
*/
private final BeanProperty[] propertiesMutable;
private final BeanPropertyAssocOne<?> unidirectional;
private final BeanProperty orderColumn;
/**
* list of properties that are Lists/Sets/Maps (Derived).
*/
private final BeanProperty[] propertiesNonMany;
private final BeanProperty[] propertiesAggregate;
private final BeanPropertyAssocMany<?>[] propertiesMany;
@@ -332,25 +286,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* list of properties that are associated beans and not embedded (Derived).
*/
private final BeanPropertyAssocOne<?>[] propertiesOne;
private final BeanPropertyAssocOne<?>[] propertiesOneImported;
private final BeanPropertyAssocOne<?>[] propertiesOneImportedSave;
private final BeanPropertyAssocOne<?>[] propertiesOneImportedDelete;
//private final BeanPropertyAssocOne<?>[] propertiesOneExported;
private final BeanPropertyAssocOne<?>[] propertiesOneExportedSave;
private final BeanPropertyAssocOne<?>[] propertiesOneExportedDelete;
/**
* list of properties that are embedded beans.
*/
private final BeanPropertyAssocOne<?>[] propertiesEmbedded;
/**
* List of the scalar properties excluding id and secondary table properties.
*/
private final BeanProperty[] propertiesBaseScalar;
private final BeanProperty[] propertiesTransient;
/**
@@ -382,11 +325,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
private final EntityBean prototypeEntityBean;
private final IdBinder idBinder;
private String idBinderInLHSSql;
private String idBinderIdSql;
private String deleteByIdSql;
private String deleteByIdInSql;
private String whereIdInSql;
@@ -394,29 +334,19 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
private String softDeleteByIdInSql;
private final String name;
private final String baseTableAlias;
private final boolean cacheSharableBeans;
private final String docStoreQueueId;
private final BeanDescriptorDraftHelp<T> draftHelp;
private final BeanDescriptorCacheHelp<T> cacheHelp;
private final BeanDescriptorJsonHelp<T> jsonHelp;
private DocStoreBeanAdapter<T> docStoreAdapter;
private DocumentMapping docMapping;
private boolean docStoreEmbeddedInvalidation;
private final String defaultSelectClause;
private SpiEbeanServer ebeanServer;
/**
* Construct the BeanDescriptor.
*/
public BeanDescriptor(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy) {
this.owner = owner;
this.multiValueSupported = owner.isMultiValueSupported();
this.entityType = deploy.getEntityType();
@@ -431,7 +361,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.namedQuery = deploy.getNamedQuery();
this.namedRawSql = deploy.getNamedRawSql();
this.inheritInfo = deploy.getInheritInfo();
this.beanFinder = deploy.getBeanFinder();
this.persistController = deploy.getPersistController();
this.persistListener = deploy.getPersistListener();
@@ -439,7 +368,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.beanPostLoad = deploy.getPostLoad();
this.queryAdapter = deploy.getQueryAdapter();
this.changeLogFilter = deploy.getChangeLogFilter();
this.defaultSelectClause = deploy.getDefaultSelectClause();
this.identityMode = deploy.buildIdentityMode();
this.idType = identityMode.getIdType();
@@ -449,7 +377,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.selectLastInsertedIdDraft = deploy.getSelectLastInsertedIdDraft();
this.concurrencyMode = deploy.getConcurrencyMode();
this.indexDefinitions = deploy.getIndexDefinitions();
this.readAuditing = deploy.isReadAuditing();
this.draftable = deploy.isDraftable();
this.draftableElement = deploy.isDraftableElement();
@@ -464,10 +391,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.partitionMeta = deploy.getPartitionMeta();
this.storageEngine = deploy.getStorageEngine();
this.autoTunable = beanFinder == null && (entityType == EntityType.ORM || entityType == EntityType.VIEW);
// helper object used to derive lists of properties
DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy);
this.softDeleteProperty = listHelper.getSoftDeleteProperty();
// if formula is set, the property is virtual only (there is no column in db) the formula must evaluate to true,
// if there is a join to a deleted bean. Example: '@Formula(select = "${ta}.user_id is null")'
@@ -503,22 +428,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.propertiesGenInsert = listHelper.getGeneratedInsert();
this.propertiesGenUpdate = listHelper.getGeneratedUpdate();
this.idOnlyReference = isIdOnlyReference(propertiesBaseScalar);
boolean noRelationships = propertiesOne.length + propertiesMany.length == 0;
this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly();
this.cacheHelp = new BeanDescriptorCacheHelp<>(this, owner.getCacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported);
this.jsonHelp = new BeanDescriptorJsonHelp<>(this);
this.draftHelp = new BeanDescriptorDraftHelp<>(this);
this.docStoreAdapter = owner.createDocStoreBeanAdapter(this, deploy);
this.docStoreQueueId = docStoreAdapter.getQueueId();
// Check if there are no cascade save associated beans ( subject to change
// in initialiseOther()). Note that if we are in an inheritance hierarchy
// then we also need to check every BeanDescriptors in the InheritInfo as
// well. We do that later in initialiseOther().
saveRecurseSkippable = (0 == (propertiesOneExportedSave.length + propertiesOneImportedSave.length + propertiesManySave.length));
// Check if there are no cascade delete associated beans (also subject to
@@ -529,7 +449,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.idBinder = owner.createIdBinder(idProperty);
this.whenModifiedProperty = findWhenModifiedProperty();
this.whenCreatedProperty = findWhenCreatedProperty();
// derive the index position of the Id and Version properties
this.abstractType = Modifier.isAbstract(beanType.getModifiers());
if (abstractType) {
@@ -537,7 +456,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.versionPropertyIndex = -1;
this.unloadProperties = new int[0];
this.propertiesIndex = new BeanProperty[0];
} else {
EntityBeanIntercept ebi = prototypeEntityBean._ebean_getIntercept();
this.idPropertyIndex = (idProperty == null) ? -1 : ebi.findProperty(idProperty.getName());
@@ -568,23 +486,19 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* These properties need to be unloaded when populating beans for queries.
*/
private int[] derivePropertiesToUnload(EntityBean prototypeEntityBean) {
boolean[] loaded = prototypeEntityBean._ebean_getIntercept().getLoaded();
int[] props = new int[loaded.length];
int pos = 0;
// collect the positions of the properties initialised in the default constructor.
for (int i = 0; i < loaded.length; i++) {
if (loaded[i]) {
props[pos++] = i;
}
}
if (pos == 0) {
// nothing set in the constructor
return new int[0];
}
// populate a smaller/minimal array
int[] unload = new int[pos];
System.arraycopy(props, 0, unload, 0, pos);
@@ -665,14 +579,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* <p>
* These properties need to be initialised prior to the association properties
* as they are used to get the imported and exported properties.
* </p>
*/
void initialiseId(BeanDescriptorInitContext initContext) {
if (logger.isTraceEnabled()) {
logger.trace("BeanDescriptor initialise " + fullName);
}
if (draftable) {
initContext.addDraft(baseTable, draftTable);
}
@@ -680,7 +591,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
// add mapping (used to swap out baseTable for asOf queries)
initContext.addHistory(baseTable, baseTableAsOf);
}
if (inheritInfo != null) {
inheritInfo.setDescriptor(this);
}
@@ -695,19 +605,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Initialise the exported and imported parts for associated properties.
*/
public void initialiseOther(BeanDescriptorInitContext initContext) {
for (BeanPropertyAssocMany<?> many : propertiesManyToMany) {
// register associated draft table for M2M intersection
many.registerDraftIntersectionTable(initContext);
}
if (historySupport) {
// history support on this bean so check all associated intersection tables
// and if they are not excluded register the associated 'with history' table
for (BeanPropertyAssocMany<?> aPropertiesManyToMany : propertiesManyToMany) {
for (BeanPropertyAssocMany<?> manyToMany : propertiesManyToMany) {
// register associated history table for M2M intersection
if (!aPropertiesManyToMany.isExcludedFromHistory()) {
TableJoin intersectionTableJoin = aPropertiesManyToMany.getIntersectionTableJoin();
if (!manyToMany.isExcludedFromHistory()) {
TableJoin intersectionTableJoin = manyToMany.getIntersectionTableJoin();
initContext.addHistoryIntersection(intersectionTableJoin.getTable());
}
}
@@ -730,11 +638,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
idBinderIdSql = idBinder.getBindIdSql(baseTableAlias);
String idBinderInLHSSqlNoAlias = idBinder.getBindIdInSql(null);
String idEqualsSql = idBinder.getBindIdSql(null);
deleteByIdSql = "delete from " + baseTable + " where " + idEqualsSql;
whereIdInSql = " where " + idBinderInLHSSqlNoAlias + " ";
deleteByIdInSql = "delete from " + baseTable + whereIdInSql;
if (softDelete) {
softDeleteByIdSql = "update " + baseTable + " set " + getSoftDeleteDbSet() + " where " + idEqualsSql;
softDeleteByIdInSql = "update " + baseTable + " set " + getSoftDeleteDbSet() + " where " + idBinderInLHSSqlNoAlias + " ";
@@ -765,7 +671,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return false;
}
boolean hasCircularImportedIdTo(BeanDescriptor sourceDesc) {
boolean hasCircularImportedIdTo(BeanDescriptor<?> sourceDesc) {
for (BeanPropertyAssocOne<?> assocOne : propertiesOneImportedSave) {
if (assocOne.getTargetDescriptor() == sourceDesc) {
return true;
@@ -792,7 +698,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Perform last initialisation for the descriptor.
*/
void initLast() {
for (BeanProperty prop : propertiesNonTransient) {
if (prop.isUnique()) {
propertiesUnique.add(new BeanProperty[]{prop});
@@ -895,13 +800,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
public void merge(EntityBean bean, EntityBean existing) {
EntityBeanIntercept fromEbi = bean._ebean_getIntercept();
EntityBeanIntercept toEbi = existing._ebean_getIntercept();
int propertyLength = toEbi.getPropertyLength();
String[] names = getProperties();
for (int i = 0; i < propertyLength; i++) {
if (fromEbi.isLoadedProperty(i)) {
@@ -977,14 +879,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Return the bean change for an update generating 'new values' and 'old values' in JSON form.
*/
private BeanChange updateBeanChange(PersistRequestBean<T> request) {
try {
BeanChangeJson changeJson = new BeanChangeJson(this, request.isStatelessUpdate());
request.getEntityBeanIntercept().addDirtyPropertyValues(changeJson);
changeJson.flush();
return beanChange(ChangeType.UPDATE, request.getBeanId(), changeJson.newJson(), changeJson.oldJson());
} catch (RuntimeException e) {
logger.error("Failed to write ChangeLog entry for update", e);
return null;
@@ -995,16 +894,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Return the bean change for an insert.
*/
private BeanChange insertBeanChange(PersistRequestBean<T> request) {
try {
StringWriter writer = new StringWriter(200);
SpiJsonWriter jsonWriter = createJsonWriter(writer);
jsonWriteForInsert(jsonWriter, request.getEntityBean());
jsonWriter.flush();
return beanChange(ChangeType.INSERT, request.getBeanId(), writer.toString(), null);
} catch (IOException e) {
logger.error("Failed to write ChangeLog entry for insert", e);
return null;
@@ -1063,12 +958,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* concurrency checking.
*/
private SqlUpdate deleteByIdList(List<Object> idList, DeleteMode mode) {
String baseSql = mode.isHard() ? deleteByIdInSql : softDeleteByIdInSql;
StringBuilder sb = new StringBuilder(baseSql);
String inClause = idBinder.getIdInValueExprDelete(idList.size());
sb.append(inClause);
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
idBinder.addIdInBindValues(delete, idList);
return delete;
@@ -1079,15 +972,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* concurrency checking.
*/
private SqlUpdate deleteById(Object id, DeleteMode mode) {
String baseSql = mode.isHard() ? deleteByIdSql : softDeleteByIdSql;
DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(baseSql);
Object[] bindValues = idBinder.getBindValues(id);
for (Object bindValue : bindValues) {
sqlDelete.setParameter(bindValue);
}
return sqlDelete;
}
@@ -1100,9 +990,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
void initialiseFkeys() {
for (BeanPropertyAssocOne<?> aPropertiesOneImported : propertiesOneImported) {
if (!aPropertiesOneImported.isFormula()) {
aPropertiesOneImported.addFkey();
for (BeanPropertyAssocOne<?> oneImported : propertiesOneImported) {
if (!oneImported.isFormula()) {
oneImported.addFkey();
}
}
}
@@ -1210,12 +1100,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Build the Document mapping recursively with the given prefix relative to the root of the document.
*/
public void docStoreMapping(final DocMappingBuilder mapping, final String prefix) {
if (prefix != null && idProperty != null) {
// id property not included in the
idProperty.docStoreMapping(mapping, prefix);
}
if (inheritInfo != null) {
String discCol = inheritInfo.getDiscriminatorColumn();
if (Types.VARCHAR == inheritInfo.getDiscriminatorType()) {
@@ -1702,13 +1590,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Trim query plans not used since the passed in epoch time.
*/
void trimQueryPlans(long unusedSince) {
Iterator<CQueryPlan> it = queryPlanCache.values().iterator();
while (it.hasNext()) {
CQueryPlan queryPlan = it.next();
if (queryPlan.getLastQueryTime() < unusedSince) {
it.remove();
}
}
queryPlanCache.values().removeIf(queryPlan -> queryPlan.getLastQueryTime() < unusedSince);
}
/**
@@ -1804,9 +1686,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Find a property annotated with @WhenCreated or @CreatedTimestamp.
*/
private BeanProperty findWhenCreatedProperty() {
for (BeanProperty aPropertiesBaseScalar : propertiesBaseScalar) {
if (aPropertiesBaseScalar.isGeneratedWhenCreated()) {
return aPropertiesBaseScalar;
for (BeanProperty baseScalar : propertiesBaseScalar) {
if (baseScalar.isGeneratedWhenCreated()) {
return baseScalar;
}
}
return null;
@@ -1816,10 +1698,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Find a property annotated with @WhenModified or @UpdatedTimestamp.
*/
private BeanProperty findWhenModifiedProperty() {
for (BeanProperty aPropertiesBaseScalar : propertiesBaseScalar) {
if (aPropertiesBaseScalar.isGeneratedWhenModified()) {
return aPropertiesBaseScalar;
for (BeanProperty baseScalar : propertiesBaseScalar) {
if (baseScalar.isGeneratedWhenModified()) {
return baseScalar;
}
}
return null;
@@ -1829,14 +1710,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Return the many property included in the query or null if one is not.
*/
public BeanPropertyAssocMany<?> getManyProperty(SpiQuery<?> query) {
OrmQueryDetail detail = query.getDetail();
for (BeanPropertyAssocMany<?> aPropertiesMany : propertiesMany) {
if (detail.includesPath(aPropertiesMany.getName())) {
return aPropertiesMany;
for (BeanPropertyAssocMany<?> many : propertiesMany) {
if (detail.includesPath(many.getName())) {
return many;
}
}
return null;
}
@@ -1905,7 +1784,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Bind the idValue to the preparedStatement.
* <p>
* This takes care of the various id types such as embedded beans etc.
* </p>
*/
public void bindId(DataBind dataBind, Object idValue) throws SQLException {
idBinder.bindId(dataBind, idValue);
@@ -1915,7 +1793,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Return the id as an array of scalar bindable values.
* <p>
* This 'flattens' any EmbeddedId or multiple Id property cases.
* </p>
*/
public Object[] getBindIdValues(Object idValue) {
return idBinder.getBindValues(idValue);
@@ -1940,7 +1817,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
try {
EntityBean bean = (EntityBean) prototypeEntityBean._ebean_newInstance();
if (beanPostConstructListener != null) {
beanPostConstructListener.autowire(bean); // calls all registered listeners
beanPostConstructListener.postConstruct(bean); // calls first the @PostConstruct method and then the listeners
@@ -1959,7 +1835,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
}
return bean;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
@@ -1999,7 +1874,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
*/
@SuppressWarnings("unchecked")
public T createReference(Boolean readOnly, boolean disableLazyLoad, Object id, PersistenceContext pc) {
if (cacheSharableBeans && !disableLazyLoad && !Boolean.FALSE.equals(readOnly)) {
CachedBeanData d = cacheHelp.beanCacheGetData(cacheKey(id));
if (d != null) {
@@ -2016,10 +1890,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
if (inheritInfo != null && !inheritInfo.isConcrete()) {
return findReferenceBean(id, pc);
}
EntityBean eb = createEntityBean();
id = convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
if (disableLazyLoad) {
ebi.setDisableLazyLoad(true);
@@ -2034,9 +1906,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
contextPut(pc, id, eb);
ebi.setPersistenceContext(pc);
}
return (T) eb;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
@@ -2086,14 +1956,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
public BeanProperty findPropertyFromPath(String path) {
BeanDescriptor<?> other = this;
while (true) {
String[] split = SplitName.splitBegin(path);
if (split[1] == null) {
return other._findBeanProperty(split[0]);
}
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) other._findBeanProperty(split[0]);
BeanDescriptor<?> targetDesc = assocProp.getTargetDescriptor();
path = split[1];
other = targetDesc;
}
@@ -2114,13 +1982,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return result;
}
String[] splitBegin = SplitName.splitBegin(path);
BeanProperty beanProperty = result.findProperty(splitBegin[0]);
if (beanProperty instanceof BeanPropertyAssoc<?>) {
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProperty;
path = splitBegin[1];
result = assocProp.getTargetDescriptor();
} else {
throw new PersistenceException("Invalid path " + path + " from " + result.getFullName());
}
@@ -2147,7 +2013,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* For bidirectional this is a real property on the bean. For unidirectional
* relationships we have this 'shadow' property which is not externally
* visible.
* </p>
*/
public BeanPropertyAssocOne<?> getUnidirectional() {
BeanDescriptor<?> other = this;
@@ -2168,7 +2033,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* <p>
* If this is false and the Id is null it is assumed that a database auto
* increment feature is being used to populate the id.
* </p>
*/
public boolean isUseIdGenerator() {
return idGenerator != null;
@@ -2195,7 +2059,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* <p>
* If this BeanDescriptor is for a table then this returns the table name
* instead.
* </p>
*/
@Override
@Nonnull
@@ -2315,7 +2178,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Return the Id value for the bean with embeddedId beans converted into maps.
* <p>
* The usage is to provide simple id types for JSON processing (for embeddedId's).
* </p>
*/
public Object getIdForJson(Object bean) {
return idBinder.getIdForJson((EntityBean) bean);
@@ -2325,7 +2187,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Convert the idValue assuming embeddedId values are Maps.
* <p>
* The usage is to provide simple id types for JSON processing (for embeddedId's).
* </p>
*/
Object convertIdFromJson(Object idValue) {
return idBinder.convertIdFromJson(idValue);
@@ -2359,7 +2220,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* <p>
* If the bean is not null, the id value is set to the id property of the bean
* after it has been converted to the correct type.
* </p>
*/
public Object convertSetId(Object idValue, EntityBean bean) {
return idBinder.convertSetId(idValue, bean);
@@ -2385,9 +2245,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
public void sort(List<T> list, String sortByClause) {
ElComparator<T> comparator = getElComparator(sortByClause);
list.sort(comparator);
list.sort(getElComparator(sortByClause));
}
public ElComparator<T> getElComparator(String propNameOrSortBy) {
@@ -2398,10 +2256,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Register all the assoc many properties on this bean that are not populated with the load context.
* <p>
* This provides further lazy loading via the load context.
* </p>
*/
public void lazyLoadRegister(String prefix, EntityBeanIntercept ebi, EntityBean bean, LoadContext loadContext) {
// load the List/Set/Map proxy objects (deferred fetching of lists)
BeanPropertyAssocMany<?>[] manys = propertiesMany();
for (BeanPropertyAssocMany<?> many : manys) {
@@ -2420,12 +2276,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* define a Reference for the collection and not invoke a query.
*/
public boolean lazyLoadMany(EntityBeanIntercept ebi) {
int lazyLoadProperty = ebi.getLazyLoadPropertyIndex();
if (lazyLoadProperty == -1) {
return false;
}
if (inheritInfo != null) {
return descOf(ebi.getOwner().getClass()).lazyLoadMany(ebi, lazyLoadProperty);
}
@@ -2443,7 +2297,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Check for lazy loading of many property.
*/
private boolean lazyLoadMany(EntityBeanIntercept ebi, int lazyLoadProperty) {
BeanProperty lazyLoadBeanProp = propertiesIndex[lazyLoadProperty];
if (lazyLoadBeanProp instanceof BeanPropertyAssocMany<?>) {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) lazyLoadBeanProp;
@@ -2468,7 +2321,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
*/
@SuppressWarnings("unchecked")
private ElComparator<T> createComparator(String sortByClause) {
SortByClause sortBy = SortByClauseParser.parse(sortByClause);
if (sortBy.size() == 1) {
// simple comparator for a single property
@@ -2477,24 +2329,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
// create a compound comparator based on the list of properties
ElComparator<T>[] comparators = new ElComparator[sortBy.size()];
List<SortByClause.Property> sortProps = sortBy.getProperties();
for (int i = 0; i < sortProps.size(); i++) {
SortByClause.Property sortProperty = sortProps.get(i);
comparators[i] = createPropertyComparator(sortProperty);
}
return new ElComparatorCompound<>(comparators);
}
private ElComparator<T> createPropertyComparator(SortByClause.Property sortProp) {
ElPropertyValue elGetValue = getElGetValue(sortProp.getName());
if (elGetValue == null) {
logger.error("Sort property [" + sortProp + "] not found in " + beanType + ". Cannot sort.");
return new ElComparatorNoop<>();
}
if (elGetValue.isAssocMany()) {
logger.error("Sort property [" + sortProp + "] in " + beanType + " is a many-property. Cannot sort.");
return new ElComparatorNoop<>();
@@ -2539,7 +2387,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Similar to ElPropertyValue but also uses foreign key shortcuts.
* <p>
* The foreign key shortcuts means we can avoid unnecessary joins.
* </p>
*/
public ElPropertyDeploy getElPropertyDeploy(String propName) {
ElPropertyDeploy elProp = elDeployCache.get(propName);
@@ -2560,7 +2407,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
ElPropertyValue buildElGetValue(String propName, ElPropertyChainBuilder chain, boolean propertyDeploy) {
if (propertyDeploy && chain != null) {
ElPropertyDeploy fk = elDeployCache.get(propName);
if (fk instanceof BeanFkeyProperty) {
@@ -2568,17 +2414,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return ((BeanFkeyProperty) fk).create(chain.getExpression(), chain.isContainsMany());
}
}
int basePos = propName.indexOf('.');
if (basePos > -1) {
// nested or embedded property
String baseName = propName.substring(0, basePos);
BeanProperty assocProp = _findBeanProperty(baseName);
if (assocProp == null) {
return null;
}
String remainder = propName.substring(basePos + 1);
return assocProp.buildElPropertyValue(propName, remainder, chain, propertyDeploy);
}
@@ -2639,7 +2482,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* <p>
* This searches this BeanDescriptor and then searches further down the
* inheritance tree (not up).
* </p>
*/
@Override
public BeanProperty findProperty(String propName) {
@@ -2666,11 +2508,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Reset the many properties to empty state ready for reloading.
*/
public void resetManyProperties(Object dbBean) {
EntityBean bean = (EntityBean) dbBean;
for (BeanPropertyAssocMany<?> aPropertiesMany : propertiesMany) {
if (aPropertiesMany.isCascadeRefresh()) {
aPropertiesMany.resetMany(bean);
for (BeanPropertyAssocMany<?> many : propertiesMany) {
if (many.isCascadeRefresh()) {
many.resetMany(bean);
}
}
}
@@ -2680,7 +2521,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* <p>
* This means is has no relationships and has readOnly=true in its cache
* options.
* </p>
*/
public boolean isCacheSharableBeans() {
return cacheSharableBeans;
@@ -2781,8 +2621,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* De-register the BeanPersistListener.
*/
public void deregister(BeanPersistListener listener) {
// volatile read...
BeanPersistListener currentListener = persistListener;
if (currentListener != null) {
if (currentListener instanceof ChainedBeanPersistListener) {
@@ -2798,8 +2636,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* De-register the BeanPersistController.
*/
public void deregister(BeanPersistController controller) {
// volatile read...
BeanPersistController currentController = persistController;
if (currentController != null) {
if (currentController instanceof ChainedBeanPersistController) {
@@ -2815,9 +2651,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Register the new BeanPersistController.
*/
public void register(BeanPersistListener newPersistListener) {
if (newPersistListener.isRegisterFor(beanType)) {
// volatile read...
BeanPersistListener currentListener = persistListener;
if (currentListener == null) {
persistListener = newPersistListener;
@@ -2837,18 +2671,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Register the new BeanPersistController.
*/
public void register(BeanPersistController newController) {
if (newController.isRegisterFor(beanType)) {
// volatile read...
BeanPersistController currentController = persistController;
if (currentController == null) {
persistController = newController;
} else {
if (currentController instanceof ChainedBeanPersistController) {
// add it to the existing chain
persistController = ((ChainedBeanPersistController) currentController).register(newController);
} else {
// build new chain of the 2
persistController = new ChainedBeanPersistController(currentController, newController);
}
}
@@ -2901,17 +2731,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Return the dependent tables for a view based entity.
* <p>
* These tables
* </p>
*/
public String[] getDependentTables() {
return dependentTables;
}
/**
* Return the base table. Only properties mapped to the base table are by
* default persisted.
* Return the base table. Only properties mapped to the base table are by default persisted.
*/
@Override
public String getBaseTable() {
@@ -3012,7 +2838,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
@Override
public ExtraJoin extraJoin(String propertyPath) {
ElPropertyValue elGetValue = getElGetValue(propertyPath);
if (elGetValue != null) {
BeanProperty beanProperty = elGetValue.getBeanProperty();
@@ -3150,7 +2975,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* <p>
* This is only used with Identity columns and getGeneratedKeys is not
* supported.
* </p>
*/
public String getSelectLastInsertedId(boolean publish) {
return publish ? selectLastInsertedId : selectLastInsertedIdDraft;
@@ -3198,8 +3022,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
/**
* Return the beans that are embedded. These share the base table with the
* owner bean.
* Return the beans that are embedded. These share the base table with the owner bean.
*/
public BeanPropertyAssocOne<?>[] propertiesEmbedded() {
return propertiesEmbedded;
@@ -3252,12 +3075,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* @param insertMode true if the 'root request' was an insert rather than an update
*/
public boolean isInsertMode(EntityBeanIntercept ebi, boolean insertMode) {
if (ebi.isLoaded()) {
// must be an update as the bean is loaded
return false;
}
if (idProperty.isEmbedded()) {
// not using Id generator so just base on isLoaded()
return !ebi.isLoaded();
@@ -3329,7 +3150,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
public ConcurrencyMode getConcurrencyMode(EntityBeanIntercept ebi) {
if (!hasVersionProperty(ebi)) {
return ConcurrencyMode.NONE;
} else {
@@ -3350,15 +3170,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Populate the diff for updates with flattened non-null property values.
*/
public void diff(String prefix, Map<String, ValuePair> map, EntityBean newBean, EntityBean oldBean) {
for (BeanProperty aPropertiesBaseScalar : propertiesBaseScalar) {
aPropertiesBaseScalar.diff(prefix, map, newBean, oldBean);
for (BeanProperty baseScalar : propertiesBaseScalar) {
baseScalar.diff(prefix, map, newBean, oldBean);
}
for (BeanPropertyAssocOne<?> aPropertiesOne : propertiesOne) {
aPropertiesOne.diff(prefix, map, newBean, oldBean);
for (BeanPropertyAssocOne<?> one : propertiesOne) {
one.diff(prefix, map, newBean, oldBean);
}
for (BeanPropertyAssocOne<?> aPropertiesEmbedded : propertiesEmbedded) {
aPropertiesEmbedded.diff(prefix, map, newBean, oldBean);
for (BeanPropertyAssocOne<?> embedded : propertiesEmbedded) {
embedded.diff(prefix, map, newBean, oldBean);
}
}
@@ -3368,10 +3187,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* <p>
* This is primarily used for paging queries to ensure that an order by clause is provided and that the order by
* provides unique ordering of the rows (so that the paging is predicable).
* </p>
*/
public void appendOrderById(SpiQuery<T> query) {
if (idProperty != null && !idProperty.isEmbedded() && !query.order().containsProperty(idProperty.getName())) {
query.order().asc(idProperty.getName());
}
@@ -3409,7 +3226,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Returns ManyToOnes and OneToOnes on the imported owning side.
* <p>
* Excludes OneToOnes on the exported side.
* </p>
*/
public BeanPropertyAssocOne<?>[] propertiesOneImported() {
return propertiesOneImported;
@@ -3483,7 +3299,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* no version property exists on the bean.
* <p>
* Note that this DOES NOT find a version property on an embedded bean.
* </p>
*/
public BeanProperty getVersionProperty() {
return versionProperty;
@@ -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() {
@@ -50,4 +50,45 @@ public class DtoMappingRequest {
public QueryPlanMetric createMetric() {
return MetricFactory.get().createQueryPlanMetric(type, label, profileLocation, sql);
}
/**
* Map all DB columns to setters.
*/
DtoReadSet[] mapSetters(DtoMeta meta) {
DtoReadSet[] setterProps = new DtoReadSet[columnMeta.length];
for (int i = 0; i < columnMeta.length; i++) {
setterProps[i] = mapColumn(i, meta);
}
return setterProps;
}
/**
* Map DB columns after constructor to setters.
*/
DtoReadSet[] mapArgPlusSetters(DtoMeta meta, int firstOnes) {
DtoReadSet[] setterProps = new DtoReadSet[columnMeta.length - firstOnes];
int pos = 0;
for (int i = firstOnes; i < columnMeta.length; i++) {
setterProps[pos++] = mapColumn(i, meta);
}
return setterProps;
}
private DtoReadSet mapColumn(int pos, DtoMeta meta) {
String label = columnMeta[pos].getLabel();
DtoReadSet property = meta.findProperty(label);
if (property == null || property.isReadOnly()) {
if (isRelaxedMode()) {
property = DtoReadSetColumnSkip.INSTANCE;
} else {
throw new IllegalStateException(unableToMapColumnMessage(columnMeta[pos], meta));
}
}
return property;
}
private String unableToMapColumnMessage(DtoColumn col, DtoMeta meta) {
return "Unable to map DB column " + col + " to a property with a setter method on " + meta.dtoType()+". Consider query.setRelaxedMode() to skip mapping this column.";
}
}
@@ -72,55 +72,16 @@ class DtoMeta {
}
private DtoQueryPlanConPlus matchMaxArgPlusSetters(DtoMappingRequest request) {
int firstOnes = maxArgConstructor.getArgCount();
DtoColumn[] cols = request.getColumnMeta();
DtoReadSet[] setterProps = new DtoReadSet[cols.length - firstOnes];
int pos = 0;
for (int i = firstOnes; i < cols.length; i++) {
String label = cols[i].getLabel();
DtoReadSet property = findProperty(label);
if (property == null || property.isReadOnly()) {
if (request.isRelaxedMode()) {
property = DtoReadSetColumnSkip.INSTANCE;
} else {
throw new IllegalStateException("Unable to map DB column " + cols[i] + " to a property with a setter method on " + dtoType);
}
}
setterProps[pos++] = property;
}
DtoReadSet[] setterProps = request.mapArgPlusSetters(this, maxArgConstructor.getArgCount());
return new DtoQueryPlanConPlus(request, maxArgConstructor, setterProps);
}
private DtoQueryPlan matchSetters(DtoMappingRequest request) {
DtoColumn[] cols = request.getColumnMeta();
DtoReadSet[] setterProps = new DtoReadSet[cols.length];
for (int i = 0; i < cols.length; i++) {
String label = cols[i].getLabel();
DtoReadSet property = findProperty(label);
if (property == null || property.isReadOnly()) {
if (request.isRelaxedMode()) {
property = DtoReadSetColumnSkip.INSTANCE;
} else {
throw new IllegalStateException("Unable to map DB column " + cols[i] + " to a property with a setter method on " + dtoType);
}
}
setterProps[i] = property;
}
DtoReadSet[] setterProps = request.mapSetters(this);
return new DtoQueryPlanConSetter(request, defaultConstructor, setterProps);
}
private DtoReadSet findProperty(String label) {
DtoReadSet findProperty(String label) {
String upperLabel = label.toUpperCase();
DtoMetaProperty property = propMap.get(upperLabel);
if (property == null && upperLabel.startsWith("IS_")) {
@@ -131,4 +92,8 @@ class DtoMeta {
}
return property;
}
Class<?> dtoType() {
return dtoType;
}
}
@@ -346,8 +346,9 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
@Override
public Query<T> orderBy(String orderBy) {
return query.order(orderBy);
public ExpressionList<T> orderBy(String orderBy) {
query.order(orderBy);
return this;
}
@Override
@@ -145,8 +145,9 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
}
@Override
public Query<T> orderBy(String orderBy) {
return rootQuery.orderBy(orderBy);
public ExpressionList<T> orderBy(String orderByClause) {
this.orderByClause = orderByClause;
return this;
}
@Override
@@ -809,7 +809,7 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
}
@Override
public Query<T> orderBy(String orderBy) {
public ExpressionList<T> orderBy(String orderBy) {
return exprList.orderBy(orderBy);
}
@@ -18,6 +18,8 @@ public class DbExpressionHandlerFactory {
case POSTGRES:
case POSTGRES9:
return new PostgresDbExpression();
case MARIADB:
return new MariaDbExpression();
case MYSQL55:
case MYSQL:
return new MySqlDbExpression();
@@ -0,0 +1,16 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.Op;
/**
* MariaDB specific handling of platform specific expressions.
*/
class MariaDbExpression extends BasicDbExpression {
@Override
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
request.append("(").append(propName).append(" ->> '$.").append(path).append("')");
request.append(operator.bind());
}
}
@@ -175,7 +175,6 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
@Override
public void loadBean(EntityBeanIntercept ebi) {
// A synchronized (this) is effectively held by EntityBeanIntercept.loadBean()
if (context.desc.lazyLoadMany(ebi)) {
// lazy load property was a Many
return;
@@ -183,7 +182,6 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
if (context.hitCache) {
Set<EntityBeanIntercept> hits = context.desc.cacheBeanLoadAll(list, persistenceContext, ebi.getLazyLoadPropertyIndex(), ebi.getLazyLoadProperty());
list.removeAll(hits);
if (list.isEmpty() || hits.contains(ebi)) {
// successfully hit the L2 cache so don't invoke DB lazy loading
@@ -193,6 +191,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
LoadBeanRequest req = new LoadBeanRequest(this, ebi, context.hitCache);
context.desc.getEbeanServer().loadBean(req);
list.clear();
}
}
@@ -73,7 +73,6 @@ public class DLoadContext implements LoadContext {
* Construct for use with JSON marshalling (doc store).
*/
public DLoadContext(BeanDescriptor<?> rootDescriptor, PersistenceContext persistenceContext) {
this.useDocStore = true;
this.rootDescriptor = rootDescriptor;
this.ebeanServer = rootDescriptor.getEbeanServer();
@@ -100,7 +99,6 @@ public class DLoadContext implements LoadContext {
}
public DLoadContext(OrmQueryRequest<?> request, SpiQuerySecondary secondaryQueries) {
this.tenantId = request.getTenantId();
this.persistenceContext = request.getPersistenceContext();
this.ebeanServer = request.getServer();
@@ -131,7 +129,6 @@ public class DLoadContext implements LoadContext {
// initialise rootBeanContext after origin and relativePath have been set
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
registerSecondaryQueries(secondaryQueries);
}
@@ -153,14 +150,12 @@ public class DLoadContext implements LoadContext {
* Register the +query and +lazy secondary queries with their appropriate LoadBeanContext or LoadManyContext.
*/
private void registerSecondaryQueries(SpiQuerySecondary secondaryQueries) {
this.secQuery = secondaryQueries.getQueryJoins();
if (secQuery != null) {
for (OrmQueryProperties pathProperties : secQuery) {
registerSecondaryQuery(pathProperties);
}
}
List<OrmQueryProperties> lazyJoins = secondaryQueries.getLazyJoins();
if (lazyJoins != null) {
for (OrmQueryProperties lazyJoin : lazyJoins) {
@@ -174,9 +169,7 @@ public class DLoadContext implements LoadContext {
* used to build the appropriate query for +query or +lazy loading.
*/
private void registerSecondaryQuery(OrmQueryProperties props) {
ElPropertyValue elGetValue = rootDescriptor.getElGetValue(props.getPath());
boolean many = elGetValue.getBeanProperty().containsMany();
registerSecondaryNode(many, props);
}
@@ -190,11 +183,9 @@ public class DLoadContext implements LoadContext {
*/
@Override
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
if (secQuery == null) {
return -1;
}
int maxBatch = 0;
for (OrmQueryProperties aSecQuery : secQuery) {
int batchSize = aSecQuery.getQueryFetchBatch();
@@ -211,7 +202,6 @@ public class DLoadContext implements LoadContext {
*/
@Override
public void executeSecondaryQueries(OrmQueryRequest<?> parentRequest, boolean forEach) {
if (secQuery != null) {
for (OrmQueryProperties aSecQuery : secQuery) {
LoadSecondaryQuery load = getLoadSecondaryQuery(aSecQuery.getPath());
@@ -234,7 +224,6 @@ public class DLoadContext implements LoadContext {
}
private ObjectGraphNode createObjectGraphNode(String path) {
if (relativePath != null) {
if (path == null) {
path = relativePath;
@@ -311,7 +300,6 @@ public class DLoadContext implements LoadContext {
}
private void registerSecondaryNode(boolean many, OrmQueryProperties props) {
int batchSize;
if (props.isQueryFetch()) {
batchSize = 100;
@@ -319,7 +307,6 @@ public class DLoadContext implements LoadContext {
int lazyJoinBatch = props.getLazyFetchBatch();
batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize;
}
String path = props.getPath();
if (many) {
manyMap.put(path, createManyContext(path, batchSize, props));
@@ -336,7 +323,6 @@ public class DLoadContext implements LoadContext {
}
private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) {
BeanPropertyAssocMany<?> p = (BeanPropertyAssocMany<?>) getBeanProperty(rootDescriptor, path);
return new DLoadManyContext(this, p, path, batchSize, queryProps);
}
@@ -13,6 +13,9 @@ import io.ebeaninternal.server.persist.dmlbind.BindableList;
import java.sql.SQLException;
import static io.ebean.annotation.Platform.MARIADB;
import static io.ebean.annotation.Platform.MYSQL;
/**
* Meta data for insert handler. The meta data is for a particular bean type. It
* is considered immutable and is thread safe.
@@ -166,12 +169,7 @@ final class InsertMeta {
request.append("insert into ").append(table);
if (nullId && noColumnsForInsert(draftTable)) {
if (this.platform.base() == Platform.MYSQL) {
request.append(" values (default)");
} else {
request.append(" default values");
}
return request.toString();
return request.append(defaultValues()).toString();
}
request.append(" (");
@@ -199,6 +197,10 @@ final class InsertMeta {
return request.toString();
}
private String defaultValues() {
return platform.base() == MYSQL || platform.base() == MARIADB ? " values (default)" : " default values";
}
/**
* Return true if the insert actually contains no columns.
*/
@@ -608,12 +608,10 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
}
QueryIterator<T> readIterate(int bufferSize, OrmQueryRequest<T> request) {
if (bufferSize > 0) {
return new CQueryIteratorWithBuffer<>(this, request, bufferSize);
} else {
if (bufferSize == 1) {
return new CQueryIteratorSimple<>(this, request);
} else {
return new CQueryIteratorWithBuffer<>(this, request, bufferSize);
}
}
@@ -201,26 +201,20 @@ public class CQueryEngine {
* when you have finished).
*/
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
if (request.isIterateSingleContext()) {
// expected relatively small number of results, single persistence context
cquery.prepareBindExecuteQuery();
} else if (!cquery.prepareBindExecuteQueryForwardOnly(forwardOnlyHintOnFindIterate)) {
if (!cquery.prepareBindExecuteQueryForwardOnly(forwardOnlyHintOnFindIterate)) {
// query has been cancelled already
logger.trace("Future fetch already cancelled");
return null;
}
if (request.isLogSql()) {
logSql(cquery);
}
// first check batch sizes set on query joins
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize);
if (iterateBufferSize < 1) {
@@ -228,20 +222,19 @@ public class CQueryEngine {
int queryBatch = request.getQuery().getLazyLoadBatchSize();
if (queryBatch > 0) {
iterateBufferSize = queryBatch;
} else {
iterateBufferSize = 100;
}
}
QueryIterator<T> readIterate = cquery.readIterate(iterateBufferSize, request);
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
if (request.isAuditReads()) {
// indicates we need to audit as the iterator progresses
cquery.auditFindIterate();
}
return readIterate;
} catch (SQLException e) {
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.core.OrmQueryRequest;
import javax.persistence.PersistenceException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.NoSuchElementException;
/**
* A QueryIterator that uses a buffer to execute secondary queries periodically.
@@ -17,6 +18,7 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
private final OrmQueryRequest<T> request;
private final ArrayList<T> buffer;
private boolean closed;
private boolean moreToLoad = true;
CQueryIteratorWithBuffer(CQuery<T> cquery, OrmQueryRequest<T> request, int bufferSize) {
@@ -29,6 +31,7 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
@Override
@SuppressWarnings("unchecked")
public boolean hasNext() {
boolean ret = false;
try {
if (buffer.isEmpty() && moreToLoad) {
// load buffer
@@ -44,23 +47,33 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
}
request.executeSecondaryQueries(true);
}
return !buffer.isEmpty();
ret = !buffer.isEmpty();
return ret;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
if (!ret) {
close();
}
}
}
@Override
public T next() {
if (buffer.isEmpty()) {
throw new NoSuchElementException();
}
return buffer.remove(0);
}
@Override
public void close() {
cquery.updateExecutionStatisticsIterator();
cquery.close();
request.endTransIfRequired();
if (!closed) {
closed = true;
cquery.updateExecutionStatisticsIterator();
cquery.close();
request.endTransIfRequired();
}
}
@Override
@@ -133,7 +133,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
SpiQuery<T> query = request.getQuery();
if (request.isBeanCachePut()) {
if (request.isBeanCachePutMany()) {
// load the individual beans into the bean cache
BeanDescriptor<T> descriptor = request.getBeanDescriptor();
Collection<T> c = result.getActualDetails();
@@ -1715,8 +1715,10 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public void setManualId(boolean manualId) {
this.manualId = manualId;
public void setManualId() {
if (detail != null && detail.hasSelectClause()) {
this.manualId = true;
}
}
/**
@@ -34,18 +34,62 @@ public final class DefaultPersistenceContext implements PersistenceContext {
private final Monitor monitor = new Monitor();
private int putCount;
/**
* Create a new PersistenceContext.
*/
public DefaultPersistenceContext() {
}
/**
* Create as a shallow copy with initial or types that have not been added to.
*/
private DefaultPersistenceContext(DefaultPersistenceContext parent, boolean initial) {
for (Map.Entry<Class<?>, ClassContext> entry : parent.typeCache.entrySet()) {
typeCache.put(entry.getKey(), entry.getValue().copy(initial));
}
}
/**
* Return the initial shallow copy with each ClassContext noting it's initialSize (to detect additions).
*/
@Override
public PersistenceContext forIterate() {
return new DefaultPersistenceContext(this, true);
}
/**
* Return a shallow copy including each ClassContext that has had no additions (still at initialSize).
*/
@Override
public PersistenceContext forIterateReset() {
return new DefaultPersistenceContext(this, false);
}
public boolean resetLimit() {
synchronized (monitor) {
if (putCount < 100) {
return false;
}
putCount = 0;
for (ClassContext value : typeCache.values()) {
if (value.resetLimit()) {
return true;
}
}
// checking after another 100 puts
return false;
}
}
/**
* Set an object into the PersistenceContext.
*/
@Override
public void put(Class<?> rootType, Object id, Object bean) {
synchronized (monitor) {
putCount++;
getClassContext(rootType).put(id, bean);
}
}
@@ -53,6 +97,7 @@ public final class DefaultPersistenceContext implements PersistenceContext {
@Override
public Object putIfAbsent(Class<?> rootType, Object id, Object bean) {
synchronized (monitor) {
putCount++;
return getClassContext(rootType).putIfAbsent(id, bean);
}
}
@@ -133,7 +178,6 @@ public final class DefaultPersistenceContext implements PersistenceContext {
}
private ClassContext getClassContext(Class<?> rootType) {
return typeCache.computeIfAbsent(rootType, k -> new ClassContext());
}
@@ -143,29 +187,72 @@ public final class DefaultPersistenceContext implements PersistenceContext {
private Set<Object> deleteSet;
private int initialSize;
private ClassContext parent;
private ClassContext() {
}
/**
* Create as a shallow copy.
*/
private ClassContext(ClassContext source, boolean initial) {
if (initial || source.isTransfer()) {
parent = source.transferParent();
initialSize = parent.size();
if (source.deleteSet != null) {
deleteSet = new HashSet<>(source.deleteSet);
}
}
}
/**
* True if this should be transferred to a new iterator persistence context.
*/
private boolean isTransfer() {
// map not added to and has some original/parent beans
return map.isEmpty() && initialSize > 0;
}
private ClassContext transferParent() {
return (parent != null) ? parent : this;
}
/**
* Return a shallow copy if initial copy or it has not grown (still at initialSize).
*/
private ClassContext copy(boolean initial) {
return new ClassContext(this, initial);
}
/**
* Return true if grown above the reset limit size of 1000.
*/
private boolean resetLimit() {
return map.size() > 1000;
}
@Override
public String toString() {
return "size:" + map.size();
}
private Object get(Object id) {
Object bean = (parent == null) ? null : parent.get(id);
return bean != null ? bean : map.get(id);
}
private WithOption getWithOption(Object id) {
if (deleteSet != null && deleteSet.contains(id)) {
return WithOption.DELETED;
}
Object bean = map.get(id);
Object bean = get(id);
return (bean == null) ? null : new WithOption(bean);
}
private Object get(Object id) {
return map.get(id);
}
private Object putIfAbsent(Object id, Object bean) {
Object existingValue = map.get(id);
Object existingValue = get(id);
if (existingValue != null) {
// it is not absent
return existingValue;
@@ -180,7 +267,7 @@ public final class DefaultPersistenceContext implements PersistenceContext {
}
private int size() {
return map.size();
return map.size() + initialSize;
}
private void clear() {
@@ -140,6 +140,11 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
return false;
}
@Override
public boolean isSkipCacheExplicit() {
return false;
}
@Override
public void setSkipCache(boolean skipCache) {
}
@@ -293,6 +293,11 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
return sb.toString();
}
@Override
public boolean isSkipCacheExplicit() {
return (skipCache != null && !skipCache);
}
@Override
public boolean isSkipCache() {
if (skipCache != null) return skipCache;
@@ -200,6 +200,11 @@ class NoTransaction implements SpiTransaction {
public void setSkipCache(boolean skipCache) {
}
@Override
public boolean isSkipCacheExplicit() {
return false;
}
@Override
public boolean isSkipCache() {
return false;
@@ -191,6 +191,7 @@ public class TransactionManager implements SpiTransactionManager {
this.txnMain = metricFactory.createTimedMetric("txn.main");
this.txnReadOnly = metricFactory.createTimedMetric("txn.readonly");
this.txnNamed = metricFactory.createTimedMetricMap("txn.named.");
// Add gauges for db pool size
scopeManager.register(this);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4
View File
@@ -206,6 +206,10 @@ public abstract class BaseTestCase {
return Platform.MYSQL == platform();
}
public boolean isMariaDB() {
return Platform.MARIADB == platform();
}
public boolean isHana() {
return Platform.HANA == platform();
}
@@ -140,6 +140,32 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
}
@Test
public void asDto_withoutSelectClause() {
ResetBasicData.reset();
LoggedSqlCollector.start();
DtoQuery<ContactDto2> query = DB.find(Contact.class)
.where().isNotNull("email").order().asc("lastName")
.asDto(ContactDto2.class)
.setRelaxedMode();
List<ContactDto2> dtos = query.findList();
assertThat(dtos).isNotEmpty();
for (ContactDto2 dto : dtos) {
assertThat(dto.getEmail()).isNotNull();
assertThat(dto.getFirstName()).isNotNull();
assertThat(dto.getLastName()).isNotNull();
assertThat(dto.getId()).isGreaterThan(0);
}
List<String> sql = LoggedSqlCollector.stop();
assertSql(sql.get(0)).contains("select t0.id, t0.first_name, t0.last_name");
}
@Test
public void asDto_withoutExplicitId() {
@@ -326,6 +352,46 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
}
}
public static class ContactDto2 {
int id;
String firstName;
String lastName;
String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
public static class ContactDto {
String email;
+3 -3
View File
@@ -168,7 +168,7 @@ public class UpdateQueryTest extends BaseTestCase {
assertSql(query).contains("update o_customer cust set status=?, updtime=? where id > ?");
}
@IgnorePlatform(Platform.MYSQL)
@IgnorePlatform({Platform.MYSQL, Platform.MARIADB})
@Test
public void withJoin() {
@@ -214,7 +214,7 @@ public class UpdateQueryTest extends BaseTestCase {
assertSql(sql.get(0)).contains("update o_customer set status=? where id in (select t0.id from o_customer t0 left join o_address t1 on t1.id = t0.billing_address_id where t1.country_code = ? and t0.id > ? limit 100)");
}
@ForPlatform({Platform.H2, Platform.POSTGRES, Platform.MYSQL})
@ForPlatform({Platform.H2, Platform.POSTGRES, Platform.MYSQL, Platform.MARIADB})
@Test
public void simpleWithLimit() {
@@ -230,7 +230,7 @@ public class UpdateQueryTest extends BaseTestCase {
.update();
final List<String> sql = LoggedSqlCollector.stop();
if (isMySql() || isH2()) {
if (isMySql() || isH2() || isMariaDB()) {
assertSql(sql.get(0)).contains("update o_customer set status=? where id > ? limit 100");
} else {
assertSql(sql.get(0)).contains("update o_customer set status=? where id in (select t0.id from o_customer t0 where t0.id > ? limit 100)");
@@ -1,7 +1,8 @@
package io.ebean.bean;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.DB;
import org.tests.compositekeys.db.AuditInfo;
import org.tests.model.basic.Customer;
import org.tests.model.basic.EBasic;
import org.tests.model.basic.ResetBasicData;
@@ -19,12 +20,11 @@ import static org.junit.Assert.assertTrue;
public class EntityBeanInterceptTest extends BaseTestCase {
@Test
public void testHasDirtyProperty() throws Exception {
public void testHasDirtyProperty() {
ResetBasicData.reset();
List<Customer> list = Ebean.find(Customer.class).findList();
List<Customer> list = DB.find(Customer.class).findList();
Set<String> propertyNames = new HashSet<>();
propertyNames.add("name");
@@ -32,7 +32,7 @@ public class EntityBeanInterceptTest extends BaseTestCase {
Customer customer = list.get(0);
EntityBeanIntercept ebi = ((EntityBean) customer)._ebean_getIntercept();
EntityBeanIntercept ebi = ebi(customer);
assertFalse(ebi.hasDirtyProperty(propertyNames));
@@ -48,7 +48,7 @@ public class EntityBeanInterceptTest extends BaseTestCase {
public void isPartial_when_new() {
EBasic basic = new EBasic();
EntityBeanIntercept ebi = ((EntityBean) basic)._ebean_getIntercept();
EntityBeanIntercept ebi = ebi(basic);
assertThat(ebi.isPartial()).isTrue();
}
@@ -58,7 +58,7 @@ public class EntityBeanInterceptTest extends BaseTestCase {
EBasic basic = new EBasic();
basic.setId(42);
basic.setName("some");
EntityBeanIntercept ebi = ((EntityBean) basic)._ebean_getIntercept();
EntityBeanIntercept ebi = ebi(basic);
assertThat(ebi.isPartial()).isTrue();
}
@@ -72,8 +72,64 @@ public class EntityBeanInterceptTest extends BaseTestCase {
basic.setSomeDate(null);
basic.setStatus(EBasic.Status.ACTIVE);
EntityBeanIntercept ebi = ((EntityBean) basic)._ebean_getIntercept();
EntityBeanIntercept ebi = ebi(basic);
assertThat(ebi.isPartial()).isFalse();
}
@Test
public void isEmbeddedNewOrDirty() {
AuditInfo auditInfo = new AuditInfo();
EntityBeanIntercept ebi = ebi(auditInfo);
assertTrue(ebi.isNew());
assertTrue(ebi.isEmbeddedNewOrDirty(auditInfo));
auditInfo.setUpdatedBy("initial");
ebi.setLoaded();
assertTrue(ebi.isLoaded());
assertFalse(ebi.isEmbeddedNewOrDirty(auditInfo));
auditInfo.setUpdatedBy("nowDirty");
assertTrue(ebi.isDirty());
assertTrue(ebi.isEmbeddedNewOrDirty(auditInfo));
assertFalse(ebi.isEmbeddedNewOrDirty(null));
}
@Test
public void setEmbeddedLoaded() {
AuditInfo auditInfo = new AuditInfo();
EntityBeanIntercept ebi = ebi(auditInfo);
assertFalse(ebi.isLoaded());
ebi.setEmbeddedLoaded(auditInfo);
assertTrue(ebi.isLoaded());
}
@Test
public void initialisedMany() {
Customer customer = new Customer();
EntityBeanIntercept ebi = ebi(customer);
final int contactsPos = findProperty("contacts", ebi);
assertFalse(ebi.isLoadedProperty(contactsPos));
ebi.initialisedMany(contactsPos);
assertTrue(ebi.isLoadedProperty(contactsPos));
}
private int findProperty(String name, EntityBeanIntercept eb) {
final String[] names = eb.getOwner()._ebean_getPropertyNames();
for (int i = 0; i < names.length; i++) {
if (names[i].equals(name)) {
return i;
}
}
throw new RuntimeException("property not found");
}
@SuppressWarnings("unchecked")
private EntityBeanIntercept ebi(Object bean) {
return ((EntityBean)bean)._ebean_getIntercept();
}
}
@@ -24,7 +24,7 @@ public class DbMigrationTest extends BaseTestCase {
server().script().runScript(scriptName, content, useAutoCommit);
}
@IgnorePlatform({Platform.ORACLE, Platform.NUODB})
@IgnorePlatform({Platform.ORACLE, Platform.NUODB, Platform.MARIADB})
@Test
public void testRunMigration() throws IOException {
// first clean up previously created objects
@@ -20,12 +20,12 @@ import static org.junit.Assert.assertTrue;
public class PlatformDdl_AlterColumnTest {
private PlatformDdl h2Ddl = PlatformDdlBuilder.create(new H2Platform());
private PlatformDdl pgDdl = PlatformDdlBuilder.create(new PostgresPlatform());
private PlatformDdl mysqlDdl = PlatformDdlBuilder.create(new MySqlPlatform());
private PlatformDdl oraDdl = PlatformDdlBuilder.create(new OraclePlatform());
private PlatformDdl sqlServerDdl = PlatformDdlBuilder.create(new SqlServer17Platform());
private PlatformDdl hanaDdl = PlatformDdlBuilder.create(new HanaPlatform());
private final PlatformDdl h2Ddl = PlatformDdlBuilder.create(new H2Platform());
private final PlatformDdl pgDdl = PlatformDdlBuilder.create(new PostgresPlatform());
private final PlatformDdl mysqlDdl = PlatformDdlBuilder.create(new MySqlPlatform());
private final PlatformDdl oraDdl = PlatformDdlBuilder.create(new OraclePlatform());
private final PlatformDdl sqlServerDdl = PlatformDdlBuilder.create(new SqlServer17Platform());
private final PlatformDdl hanaDdl = PlatformDdlBuilder.create(new HanaPlatform());
{
ServerConfig serverConfig = Ebean.getDefaultServer().getPluginApi().getServerConfig();
@@ -122,7 +122,9 @@ public class PlatformDdl_AlterColumnTest {
assertEquals("alter table mytab alter column acol varchar(20)", sql);
sql = pgDdl.alterColumnType("mytab", "acol", "varchar(20)");
assertEquals("alter table mytab alter column acol type varchar(20)", sql);
assertEquals("alter table mytab alter column acol type varchar(20) using acol::varchar(20)", sql);
sql = pgDdl.alterColumnType("mytab", "acol", "bigint");
assertEquals("alter table mytab alter column acol type bigint using acol::bigint", sql);
sql = oraDdl.alterColumnType("mytab", "acol", "varchar(20)");
assertEquals("alter table mytab modify acol varchar2(20)", sql);
@@ -154,6 +154,16 @@ public class ModelContainerTest {
container.applyChange(dropHistoryTable);
}
@Test
public void apply_renameColumn() {
ModelContainer container = new ModelContainer();
container.apply(mig("6.0__renameColumn.model.xml"), ver("6.0"));
final MTable table = container.getTable("document");
assertThat(table.getColumn("title")).isNotNull();
assertThat(table.getColumn("short_title")).isNull();
}
@Test
public void getSchemas() {
@@ -1,7 +1,7 @@
package io.ebeaninternal.server.deploy;
import io.ebean.EbeanServerFactory;
import io.ebean.config.ServerConfig;
import io.ebean.DatabaseFactory;
import io.ebean.config.DatabaseConfig;
import io.ebean.event.AbstractBeanPersistListener;
import io.ebean.event.BeanPersistAdapter;
import io.ebean.event.BeanPersistListener;
@@ -14,9 +14,9 @@ import static org.junit.Assert.*;
public class BeanDescriptor_registerTest {
@Test
public void testRegisterDeregister() throws Exception {
public void testRegisterDeregister() {
ServerConfig config = new ServerConfig();
DatabaseConfig config = new DatabaseConfig();
config.setName("h2other");
config.loadFromProperties();
@@ -25,7 +25,7 @@ public class BeanDescriptor_registerTest {
config.setDefaultServer(false);
config.getClasses().add(EBasic.class);
SpiEbeanServer ebeanServer = (SpiEbeanServer) EbeanServerFactory.create(config);
SpiEbeanServer ebeanServer = (SpiEbeanServer)DatabaseFactory.create(config);
BeanDescriptor<EBasic> desc = ebeanServer.getBeanDescriptor(EBasic.class);
persistListenerRegistrationTests(desc);
@@ -308,12 +308,19 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
}
@Test
public void equals_when_manualId() {
public void equals_when_manualId_andSelectClause() {
DefaultOrmQuery<Customer> q1 = query().select("name");
q1.setManualId();
assertDifferent(q1, query().select("name"));
}
@Test
public void equals_when_manualId_andNoSelectClause() {
DefaultOrmQuery<Customer> q1 = query();
q1.setManualId(true);
q1.setManualId();
assertDifferent(q1, query());
assertSame(q1, query());
}
private CQueryPlanKey planKey(ExpressionList<Customer> id) {
@@ -1,7 +1,7 @@
package io.ebeaninternal.server.rawsql;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.DB;
import io.ebean.Query;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
@@ -52,7 +52,7 @@ public class TestRawSqlParsing extends BaseTestCase {
.parse(sql)
.create();
List<Customer> list = Ebean.createQuery(Customer.class)
List<Customer> list = DB.find(Customer.class)
.setRawSql(rawSql)
.setParameter("name", "Rob")
.findList();
@@ -67,7 +67,7 @@ public class TestRawSqlParsing extends BaseTestCase {
RawSql sql = RawSqlBuilder.parse("SELECT id, name FROM o_customer ${where}").create();
List<Customer> customers = Ebean.createQuery(Customer.class)
List<Customer> customers = DB.find(Customer.class)
.setRawSql(sql)
.where().gt("id", 1)
.findList();
@@ -91,7 +91,7 @@ public class TestRawSqlParsing extends BaseTestCase {
RawSql rawSql = RawSqlBuilder.parse(sql).create();
Query<Customer> query = Ebean.createQuery(Customer.class)
Query<Customer> query = DB.find(Customer.class)
.setRawSql(rawSql)
.setFirstRow(1)
.setMaxRows(5);
@@ -103,6 +103,70 @@ public class TestRawSqlParsing extends BaseTestCase {
}
@ForPlatform({Platform.H2, Platform.POSTGRES})
@Test
public void testUnion_explicitWhere() {
ResetBasicData.reset();
String sql =
"select id, name from (" +
" select id, name from o_customer where name <= 'f' " +
" union all " +
" select id, name from o_customer where name > 'f' " +
") all_split ${where}";
RawSql rawSql = RawSqlBuilder.parse(sql).create();
Query<Customer> query = DB.find(Customer.class)
.setRawSql(rawSql)
.setFirstRow(1)
.setMaxRows(5);
query.findList();
assertThat(sqlOf(query)).contains(") all_split limit 5 offset 1");
Query<Customer> query1 = DB.find(Customer.class)
.setRawSql(rawSql)
.where().gt("id", 2)
.setFirstRow(1)
.setMaxRows(5)
.query();
query1.findList();
assertThat(sqlOf(query1)).contains("all_split where id > ? limit 5 offset 1");
}
@ForPlatform({Platform.H2, Platform.POSTGRES})
@Test
public void testUnion_explicitAndhere() {
ResetBasicData.reset();
String sql =
"select id, name from (" +
" select id, name from o_customer where name <= 'f' " +
" union all " +
" select id, name from o_customer where name > 'f' " +
") all_split where 1=1 ${andWhere}";
RawSql rawSql = RawSqlBuilder.parse(sql).create();
Query<Customer> query = DB.find(Customer.class)
.setRawSql(rawSql)
.setFirstRow(1)
.setMaxRows(5);
query.findList();
assertThat(sqlOf(query)).contains(") all_split where 1=1 limit 5 offset 1");
Query<Customer> query1 = DB.find(Customer.class)
.setRawSql(rawSql)
.where().gt("id", 2)
.setFirstRow(1)
.setMaxRows(5)
.query();
query1.findList();
assertThat(sqlOf(query1)).contains("all_split where 1=1 and id > ? limit 5 offset 1");
}
@ForPlatform({Platform.H2, Platform.POSTGRES})
@Test
public void testColumnName2() {
@@ -113,7 +177,7 @@ public class TestRawSqlParsing extends BaseTestCase {
RawSql rawSql = RawSqlBuilder.parse(sql).create();
Query<A2Customer> query = Ebean.createQuery(A2Customer.class)
Query<A2Customer> query = DB.find(A2Customer.class)
.setRawSql(rawSql);
final List<A2Customer> list = query.findList();
@@ -135,7 +199,7 @@ public class TestRawSqlParsing extends BaseTestCase {
RawSql rawSql = RawSqlBuilder.parse(sql).create();
Query<ACustomer> query = Ebean.createQuery(ACustomer.class)
Query<ACustomer> query = DB.find(ACustomer.class)
.setRawSql(rawSql);
final List<ACustomer> list = query.findList();
@@ -159,7 +223,7 @@ public class TestRawSqlParsing extends BaseTestCase {
.columnMapping("v.customerName", "custName")
.create();
Query<ACustomer> query = Ebean.createQuery(ACustomer.class)
Query<ACustomer> query = DB.find(ACustomer.class)
.setRawSql(rawSql);
final List<ACustomer> list = query.findList();
@@ -2,33 +2,36 @@ package io.ebeaninternal.server.transaction;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.server.deploy.PersistenceContextUtil;
import org.tests.model.basic.Car;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Vehicle;
import org.junit.Test;
import org.tests.model.basic.Car;
import org.tests.model.basic.Contact;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Product;
import org.tests.model.basic.Vehicle;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class DefaultPersistenceContextTest {
Customer customer42;
private final Customer customer42;
Car car1;
private final Car car1;
public DefaultPersistenceContextTest() {
customer42 = new Customer();
customer42.setId(42);
car1 = new Car();
car1.setId(1);
}
PersistenceContext pc() {
private DefaultPersistenceContext pc() {
return new DefaultPersistenceContext();
}
PersistenceContext pcWith42() {
PersistenceContext pc = pc();
private DefaultPersistenceContext pcWith42() {
DefaultPersistenceContext pc = pc();
pc.put(Customer.class, 42, customer42);
return pc;
}
@@ -38,7 +41,7 @@ public class DefaultPersistenceContextTest {
}
@Test
public void put_get_withInheritance() throws Exception {
public void put_get_withInheritance() {
PersistenceContext pc = pc();
pc.put(root(Vehicle.class), 1, car1);
@@ -48,7 +51,7 @@ public class DefaultPersistenceContextTest {
}
@Test
public void put_get() throws Exception {
public void put_get() {
PersistenceContext pc = pc();
pc.put(Customer.class, customer42.getId(), customer42);
@@ -58,7 +61,7 @@ public class DefaultPersistenceContextTest {
}
@Test
public void putIfAbsent_when_absent() throws Exception {
public void putIfAbsent_when_absent() {
PersistenceContext pc = pc();
Object existing = pc.putIfAbsent(Customer.class, customer42.getId(), customer42);
@@ -67,7 +70,7 @@ public class DefaultPersistenceContextTest {
}
@Test
public void putIfAbsent_when_notAbsent() throws Exception {
public void putIfAbsent_when_notAbsent() {
PersistenceContext pc = pcWith42();
Object existing = pc.putIfAbsent(Customer.class, customer42.getId(), new Customer());
@@ -76,21 +79,21 @@ public class DefaultPersistenceContextTest {
}
@Test
public void get_when_empty() throws Exception {
public void get_when_empty() {
PersistenceContext pc = pc();
Object found = pc.get(Customer.class, 42);
assertThat(found).isNull();
}
@Test
public void get_when_there() throws Exception {
public void get_when_there() {
PersistenceContext pc = pcWith42();
Object found = pc.get(Customer.class, 42);
assertThat(found).isSameAs(customer42);
}
@Test
public void getWithOption_when_empty() throws Exception {
public void getWithOption_when_empty() {
PersistenceContext pc = pc();
PersistenceContext.WithOption withOption = pc.getWithOption(Customer.class, 42);
@@ -98,7 +101,7 @@ public class DefaultPersistenceContextTest {
}
@Test
public void getWithOption_when_there() throws Exception {
public void getWithOption_when_there() {
PersistenceContext pc = pcWith42();
@@ -107,7 +110,7 @@ public class DefaultPersistenceContextTest {
}
@Test
public void getWithOption_when_deleted() throws Exception {
public void getWithOption_when_deleted() {
PersistenceContext pc = pcWith42();
pc.deleted(Customer.class, 42);
@@ -118,21 +121,21 @@ public class DefaultPersistenceContextTest {
}
@Test
public void size_when_empty() throws Exception {
public void size_when_empty() {
PersistenceContext pc = pc();
assertThat(pc.size(Customer.class)).isEqualTo(0);
}
@Test
public void size_when_some() throws Exception {
public void size_when_some() {
PersistenceContext pc = pcWith42();
assertThat(pc.size(Customer.class)).isEqualTo(1);
}
@Test
public void clear() throws Exception {
public void clear() {
PersistenceContext pc = pcWith42();
pc.clear();
@@ -140,7 +143,7 @@ public class DefaultPersistenceContextTest {
}
@Test
public void clearClass() throws Exception {
public void clearClass() {
PersistenceContext pc = pcWith42();
pc.clear(Customer.class);
@@ -148,7 +151,7 @@ public class DefaultPersistenceContextTest {
}
@Test
public void clearClassAndId() throws Exception {
public void clearClassAndId() {
PersistenceContext pc = pcWith42();
pc.put(Customer.class, 43, new Customer());
@@ -159,4 +162,102 @@ public class DefaultPersistenceContextTest {
pc.clear(Customer.class, 43);
assertThat(pc.size(Customer.class)).isEqualTo(0);
}
@Test
public void forIterate() {
final DefaultPersistenceContext pc = pcWith42();
final Object origCustomer42 = pc.get(Customer.class, 42);
// act
final PersistenceContext pcIterate = pc.forIterate();
assertThat(pc).isNotSameAs(pcIterate);
assertThat(pcIterate.size(Customer.class)).isEqualTo(1);
// assert same instance (bean effectively transferred to iterator persistence context
final Object customer42 = pcIterate.get(Customer.class, 42);
assertThat(customer42).isSameAs(origCustomer42);
final PersistenceContext.WithOption option = pcIterate.getWithOption(Customer.class, 42);
assertThat(option.getBean()).isSameAs(origCustomer42);
}
@Test
public void forIterate_many() {
DefaultPersistenceContext pc = new DefaultPersistenceContext();
addCustomers(pc, 1, 100);
addContacts(pc, 1, 1010);
assertThat(pc.size(Customer.class)).isEqualTo(100);
assertThat(pc.size(Contact.class)).isEqualTo(1010);
// act
final PersistenceContext pcIterate = pc.forIterate();
assertThat(pcIterate.size(Customer.class)).isEqualTo(100);
assertThat(pcIterate.size(Contact.class)).isEqualTo(1010);
}
@Test
public void forIterate_resetLimit_forIterateReset() {
DefaultPersistenceContext initialPc = new DefaultPersistenceContext();
addCustomers(initialPc, 1, 100);
addContacts(initialPc, 1, 1010);
final PersistenceContext pcIterate = initialPc.forIterate();
assertFalse(pcIterate.resetLimit());
// added 900 NEW contact beans
addContacts(pcIterate, 2000, 900);
assertThat(pcIterate.size(Contact.class)).isEqualTo(1910);
assertFalse(pcIterate.resetLimit());
// boundary, added 1000 NEW contact beans (still false)
addContacts(pcIterate, 3000, 100);
assertFalse(pcIterate.resetLimit());
addContacts(pcIterate, 4000, 1);
addProducts(pcIterate, 1, 100);
// ACT - over 1000 added beans boundary for contacts so returns true
assertTrue(pcIterate.resetLimit());
assertThat(pcIterate.size(Contact.class)).isEqualTo(2011);
assertThat(pcIterate.size(Customer.class)).isEqualTo(100);
assertThat(pcIterate.size(Product.class)).isEqualTo(100);
// ACT - obtain new PC forIterateReset
PersistenceContext pcReset = pcIterate.forIterateReset();
// keeps original customer beans as no new added beans there
assertThat(pcReset.size(Customer.class)).isEqualTo(100); // customers didn't change
// added beans to contacts and products so those where reset
assertThat(pcReset.size(Contact.class)).isEqualTo(0);
assertThat(pcReset.size(Product.class)).isEqualTo(0);
}
@Test
public void toString_sillyTest() {
DefaultPersistenceContext pc = pcWith42();
assertThat(pc.toString()).contains("org.tests.model.basic.Customer");
}
private void addCustomers(PersistenceContext pc, int start, int loop) {
for (int i = start; i < start + loop; i++) {
Customer bean = new Customer();
bean.setId(i);
pc.put(Customer.class, i, bean);
}
}
private void addContacts(PersistenceContext pc, int start, int loop) {
for (int i = start; i < start + loop; i++) {
Contact bean = new Contact();
bean.setId(i);
pc.put(Contact.class, i, bean);
}
}
private void addProducts(PersistenceContext pc, int start, int loop) {
for (int i = start; i < start + loop; i++) {
Product bean = new Product();
bean.setId(i);
pc.put(Product.class, i, bean);
}
}
}
+18
View File
@@ -0,0 +1,18 @@
package main;
import io.ebean.docker.commands.MariaDBConfig;
import io.ebean.docker.commands.MariaDBContainer;
public class StartMariaDb {
public static void main(String[] args) {
MariaDBConfig config = new MariaDBConfig("10.5");
config.setDbName("unit");
config.setUser("unit");
config.setPassword("unit");
MariaDBContainer container = new MariaDBContainer(config);
container.startWithDropCreate();
}
}
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertNotNull;
public class TestAggregateFormula extends BaseTestCase {
@IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.NUODB})
@IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB, Platform.NUODB})
@Test
public void minDistinctOrderByNulls() {
@@ -49,7 +49,7 @@ public class TestMetaAnnotation extends BaseTestCase {
* This test writes 101 spaces to "line1" which is annotated with &#64;Size(max=100).
*/
@Test
@IgnorePlatform({Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL}) // pg & mssql does not fail if string is too long.
@IgnorePlatform({Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB}) // pg & mssql does not fail if string is too long.
public void testWrite101SpacesToLine1() {
Address address = new Address();
@@ -66,7 +66,7 @@ public class TestMetaAnnotation extends BaseTestCase {
* This test writes 101 spaces to "line1" which is meta-annotated with {@link SizeMedium}.
*/
@Test
@IgnorePlatform({Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL})
@IgnorePlatform({Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB})
public void testWrite101SpacesToLine2() {
Address address = new Address();
@@ -23,7 +23,7 @@ public class TestQueryForUpdate extends BaseTestCase {
@Test
@ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL})
@ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB})
public void testForUpdate() {
ResetBasicData.reset();
@@ -41,7 +41,7 @@ public class TestQueryForUpdate extends BaseTestCase {
}
@Test
@ForPlatform({ Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL})
@ForPlatform({ Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB})
public void testForUpdate_when_alreadyInPC() {
EBasic basic = new EBasic("test PC cache");
+45 -17
View File
@@ -2,7 +2,7 @@ package org.tests.cache;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import io.ebean.Ebean;
import io.ebean.Transaction;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheStatistics;
import org.ebeantest.LoggedSqlCollector;
@@ -27,37 +27,66 @@ public class TestBeanCache extends BaseTestCase {
private static final Logger log = LoggerFactory.getLogger(TestBeanCache.class);
private ServerCache beanCache = DB.getDefault().getServerCacheManager().getBeanCache(OCachedBean.class);
private final ServerCache beanCache = DB.getDefault().getServerCacheManager().getBeanCache(OCachedBean.class);
@Test
public void findById_when_idTypeConverted() {
OCachedBean bean = new OCachedBean();
bean.setName("findById");
Ebean.save(bean);
DB.save(bean);
OCachedBean bean0 = Ebean.find(OCachedBean.class, bean.getId());
OCachedBean bean0 = DB.find(OCachedBean.class, bean.getId());
assertNotNull(bean0);
// expect to hit the cache, no SQL
LoggedSqlCollector.start();
OCachedBean bean1 = Ebean.find(OCachedBean.class, bean.getId());
OCachedBean bean1 = DB.find(OCachedBean.class, bean.getId());
List<String> sql = LoggedSqlCollector.stop();
assertNotNull(bean1);
assertThat(sql).isEmpty();
// expect to hit the cache, no SQL
LoggedSqlCollector.start();
OCachedBean bean2 = Ebean.find(OCachedBean.class).setReadOnly(true).setId(String.valueOf(bean.getId())).findOne();
OCachedBean bean2 = DB.find(OCachedBean.class).setReadOnly(true).setId(String.valueOf(bean.getId())).findOne();
sql = LoggedSqlCollector.stop();
assertNotNull(bean2);
assertThat(sql).isEmpty();
}
@Test
public void idsIn_explicitCache_expect_cachePut() {
List<OCachedBean> beans = createBeans(Arrays.asList("k0","k1"));
List<Long> ids = beans.stream().map(OCachedBean::getId).collect(Collectors.toList());
beanCache.clear();
beanCache.getStatistics(true);
try (Transaction transaction = DB.beginTransaction()) {
// skipCacheAfterWrite set after this write ...
final OCachedBean junk = createBean("junk");
DB.save(junk);
List<OCachedBean> list = DB.find(OCachedBean.class)
.where().idIn(ids)
.setUseCache(true)
.findList();
assertThat(list).hasSize(2);
transaction.commit();
}
ServerCacheStatistics statistics = beanCache.getStatistics(true);
assertThat(statistics.getHitCount()).isEqualTo(0);
assertThat(statistics.getMissCount()).isEqualTo(2);
assertThat(statistics.getPutCount()).isEqualTo(2);
}
@Test
public void idsInExpression() {
List<OCachedBean> beans = createBeans();
List<OCachedBean> beans = createBeans(Arrays.asList("z0", "z1", "z2"));
List<Long> ids = beans.stream().map(OCachedBean::getId).collect(Collectors.toList());
beanCache.clear();
@@ -134,26 +163,25 @@ public class TestBeanCache extends BaseTestCase {
assertThat(statistics.getMissCount()).isEqualTo(missCount);
}
private List<OCachedBean> createBeans() {
List<String> names = Arrays.asList("z0", "z1", "z2");
private List<OCachedBean> createBeans(List<String> names) {
List<OCachedBean> beans = new ArrayList<>();
for (String name : names) {
OCachedBean bean = new OCachedBean();
bean.setName(name);
beans.add(bean);
beans.add(createBean(name));
}
DB.saveAll(beans);
return beans;
}
private OCachedBean createBean(String name) {
OCachedBean bean = new OCachedBean();
bean.setName(name);
return bean;
}
@Test
public void find_whenNotExits() {
Country country = Ebean.find(Country.class)
Country country = DB.find(Country.class)
.where()
.eq("name","NotValid")
.findOne();
@@ -26,7 +26,7 @@ public class TestDefaults extends BaseTestCase {
final List<String> current = LoggedSqlCollector.current();
assertThat(current).isNotEmpty();
if (isMySql()) {
if (isMySql() || isMariaDB()) {
assertThat(current.get(0)).contains("insert into defaults_model_draft values (default);");
} else if (isSqlServer()) {
assertThat(current.get(0)).contains("insert into defaults_model_draft (id) values (?)");
@@ -27,7 +27,7 @@ public class TestDeleteByQuery extends BaseTestCase {
}
@Test
@IgnorePlatform(Platform.MYSQL)
@IgnorePlatform({Platform.MYSQL, Platform.MARIADB})
public void deleteWithLimit() {
createUser("deleteWithLimit1");
createUser("deleteWithLimit2");
@@ -23,6 +23,61 @@ public class TestHistoryInsert extends BaseTestCase {
private final Logger logger = LoggerFactory.getLogger(TestHistoryInsert.class);
/**
* Looks like we MUST use useLegacyDatetimeCode=false ... in order for
* the correct server timezone to be honored by the MariaDB JDBC driver.
*/
@Test
@ForPlatform({Platform.MARIADB})
public void mariadb_simple_history() {
Timestamp t0 = new Timestamp(System.currentTimeMillis());
littleSleep();
User user = new User();
user.setName("Jim");
user.setEmail("one@email.com");
user.setPasswordHash("someHash");
DB.save(user);
Timestamp t1 = new Timestamp(System.currentTimeMillis());
littleSleep();
user.setName("NotJim");
user.save();
Timestamp t2 = new Timestamp(System.currentTimeMillis());
littleSleep();
user.setName("NotJimV2");
user.setEmail("two@email.com");
user.save();
Timestamp t3 = new Timestamp(System.currentTimeMillis());
List<Version<User>> versions = DB.find(User.class).setId(user.getId()).findVersionsBetween(t0, t3);
assertThat(versions).hasSize(3);
// use useLegacyDatetimeCode=false with MariaDB JDBC driver
final User user0 = DB.find(User.class).setId(user.getId()).asOf(t0).findOne();
final User user1 = DB.find(User.class).setId(user.getId()).asOf(t1).findOne();
final User user2 = DB.find(User.class).setId(user.getId()).asOf(t2).findOne();
final User user3 = DB.find(User.class).setId(user.getId()).asOf(t3).findOne();
assertThat(user1.getName()).isEqualTo("Jim");
assertThat(user1.getEmail()).isEqualTo("one@email.com");
assertThat(user2.getName()).isEqualTo("NotJim");
assertThat(user2.getEmail()).isEqualTo("one@email.com");
assertThat(user3.getName()).isEqualTo("NotJimV2");
assertThat(user3.getEmail()).isEqualTo("two@email.com");
assertThat(user0).isNull();
}
private void littleSleep() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Test
@ForPlatform({Platform.H2, Platform.POSTGRES})
public void test() throws InterruptedException {
@@ -1,40 +1,80 @@
package org.tests.query;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.DB;
import io.ebean.FetchConfig;
import io.ebean.Query;
import io.ebean.Transaction;
import io.ebean.annotation.Transactional;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.api.SpiTransaction;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import org.tests.model.basic.Contact;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import org.junit.Assert;
import org.junit.Test;
import org.tests.o2m.OmBasicChild;
import org.tests.o2m.OmBasicParent;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
public class TestQueryFindEach extends BaseTestCase {
private final Random random = new Random();
@Test
public void test() {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
Query<Customer> query
= server.find(Customer.class)
.setAutoTune(false)
.fetch("contacts", new FetchConfig().query(2)).where().gt("id", 0).order("id")
Query<Customer> query = DB.find(Customer.class)
.fetch("contacts", new FetchConfig().query(2))
.where().gt("id", 0).order("id")
.setMaxRows(2).query();
final AtomicInteger counter = new AtomicInteger(0);
query.findEach(customer -> {
counter.incrementAndGet();
customer.getName();
assertNotNull(customer.getName());
});
Assert.assertEquals(2, counter.get());
assertEquals(2, counter.get());
}
@Test
public void persistenceContext_scope() {
ResetBasicData.reset();
Query<Contact> query = DB.find(Contact.class);
try (final Transaction transaction = DB.beginTransaction()) {
// effectively loads customers into persistence context
final List<Customer> customerList = DB.find(Customer.class)
.select("name")
.findList();
SpiTransaction spiTxn = (SpiTransaction) transaction;
PersistenceContext pc = spiTxn.getPersistenceContext();
assertThat(pc.size(Customer.class)).isEqualTo(customerList.size());
LoggedSqlCollector.start();
query.findEach(contact -> {
// use customer from persistence context (otherwise would invoke lazy loading)
assertNotNull(contact.getCustomer().getName());
});
final List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(1);
}
}
/**
@@ -45,10 +85,9 @@ public class TestQueryFindEach extends BaseTestCase {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
Query<Customer> query = server.find(Customer.class).setAutoTune(false)
.fetch("contacts", new FetchConfig().query(2)).where().gt("id", 0).order("id")
Query<Customer> query = DB.find(Customer.class)
.fetch("contacts", new FetchConfig().query(2))
.where().gt("id", 0).order("id")
.setMaxRows(2).query();
final AtomicInteger counter = new AtomicInteger(0);
@@ -60,6 +99,47 @@ public class TestQueryFindEach extends BaseTestCase {
}
});
Assert.assertFalse("Never get here - exception thrown", true);
fail("Never get here - exception thrown");
}
@Test
public void iterateResetLimit() {
DB.find(OmBasicChild.class).delete();
DB.find(OmBasicParent.class).delete();
insertData();
LoggedSqlCollector.start();
try (final Transaction transaction = DB.beginTransaction()) {
// DB.find(OmBasicParent.class).findList();
DB.find(OmBasicChild.class)
.setLazyLoadBatchSize(100)
//.fetchQuery("parent","name")
//.fetch("parent","name")
.findEach(child -> {
assertNotNull(child.getParent().getName());
});
}
final List<String> sql = LoggedSqlCollector.stop();
assertThat(sql.size()).isLessThan(50);
}
@Transactional(batchSize = 40)
private void insertData() {
for (int i = 0; i < 150; i++) {
insertData2(i);
}
}
private void insertData2(int i) {
OmBasicParent p = new OmBasicParent("bl_" + i);
DB.save(p);
int children = 20 + random.nextInt(10);
for (int j = 0; j < children; j++) {
OmBasicChild c = new OmBasicChild("bl_" + i + "_" + j, p);
DB.save(c);
}
}
}
@@ -1,17 +1,19 @@
package org.tests.query;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.DB;
import io.ebean.FetchConfig;
import io.ebean.Query;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
public class TestQueryFindEachWhile extends BaseTestCase {
@Test
@@ -19,10 +21,8 @@ public class TestQueryFindEachWhile extends BaseTestCase {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
Query<Customer> query
= server.find(Customer.class)
= DB.find(Customer.class)
.setAutoTune(false)
.fetch("contacts", new FetchConfig().query(2)).where().gt("id", 0).order("id")
.setMaxRows(2).query();
@@ -31,11 +31,11 @@ public class TestQueryFindEachWhile extends BaseTestCase {
query.findEachWhile(customer -> {
counter.incrementAndGet();
customer.getName();
assertNotNull(customer.getName());
return true;
});
Assert.assertEquals(2, counter.get());
assertEquals(2, counter.get());
}
/**
@@ -46,9 +46,7 @@ public class TestQueryFindEachWhile extends BaseTestCase {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
Query<Customer> query = server.find(Customer.class).setAutoTune(false)
Query<Customer> query = DB.find(Customer.class).setAutoTune(false)
.fetch("contacts", new FetchConfig().query(2)).where().gt("id", 0).order("id")
.setMaxRows(2).query();
@@ -62,6 +60,6 @@ public class TestQueryFindEachWhile extends BaseTestCase {
return true;
});
Assert.assertFalse("Never get here - exception thrown", true);
fail("Never get here - exception thrown");
}
}
@@ -1,8 +1,7 @@
package org.tests.query;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.DB;
import io.ebean.Query;
import io.ebean.QueryIterator;
import io.ebean.datasource.DataSourcePool;
@@ -32,9 +31,7 @@ public class TestQueryFindIterate extends BaseTestCase {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
Query<Customer> query = server.find(Customer.class)
Query<Customer> query = DB.find(Customer.class)
.setMaxRows(2);
final AtomicInteger count = new AtomicInteger();
@@ -54,9 +51,7 @@ public class TestQueryFindIterate extends BaseTestCase {
public void test_hasNext_hasNext() {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
QueryIterator<Customer> queryIterator = server.find(Customer.class)
QueryIterator<Customer> queryIterator = DB.find(Customer.class)
.where()
.isNotNull("name")
.setMaxRows(3)
@@ -89,9 +84,7 @@ public class TestQueryFindIterate extends BaseTestCase {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
Query<Customer> query = server.find(Customer.class)
Query<Customer> query = DB.find(Customer.class)
.setAutoTune(false)
//.fetch("contacts", new FetchConfig().query(2)).where().gt("id", 0).orderBy("id")
.setMaxRows(2);
@@ -108,7 +101,7 @@ public class TestQueryFindIterate extends BaseTestCase {
ResetBasicData.reset();
Ebean.find(Order.class)
DB.find(Order.class)
//.select("orderDate")
.where().gt("id", 0).le("id", 10)
.findEach(order -> {
@@ -129,7 +122,7 @@ public class TestQueryFindIterate extends BaseTestCase {
LoggedSqlCollector.start();
Ebean.find(Order.class)
DB.find(Order.class)
.setLazyLoadBatchSize(10)
.select("status, orderDate")
.fetch("customer", "name")
@@ -159,10 +152,10 @@ public class TestQueryFindIterate extends BaseTestCase {
LoggedSqlCollector.start();
// make sure we don't hit the L2 cache for order shipments
Ebean.getServerCacheManager().clear(Order.class);
Ebean.getServerCacheManager().clear(OrderShipment.class);
DB.getServerCacheManager().clear(Order.class);
DB.getServerCacheManager().clear(OrderShipment.class);
Ebean.find(Order.class)
DB.find(Order.class)
.setLazyLoadBatchSize(10)
.setUseCache(false)
.select("status, orderDate")
@@ -190,10 +183,8 @@ public class TestQueryFindIterate extends BaseTestCase {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
// intentionally a query with incorrect type binding
Query<Customer> query = server.find(Customer.class)
Query<Customer> query = DB.find(Customer.class)
.setAutoTune(false)
.where().gt("id", "JUNK_NOT_A_LONG")
.setMaxRows(2).query();
@@ -203,11 +194,11 @@ public class TestQueryFindIterate extends BaseTestCase {
});
if (!server.getName().equals("h2")) {
if (!DB.getDefault().getName().equals("h2")) {
// MySql allows the query with type conversion?
throw new PersistenceException("H2 does expected thing but MySql does not");
}
assertTrue("Never get here as exception thrown", false);
fail("Never get here as exception thrown");
}
@@ -216,9 +207,7 @@ public class TestQueryFindIterate extends BaseTestCase {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
Query<Customer> query = server.find(Customer.class)
Query<Customer> query = DB.find(Customer.class)
.setAutoTune(false)
.where().gt("id", 0)
.setMaxRows(2).query();
@@ -231,7 +220,16 @@ public class TestQueryFindIterate extends BaseTestCase {
}
@Test
public void testCloseConnection() {
public void testCloseConnection_findIterate() {
findIterateCloseConnection(false);
}
@Test
public void testCloseConnection_findIterate_withBatchLoad() {
findIterateCloseConnection(true);
}
public void findIterateCloseConnection(boolean withLoadBatch) {
ResetBasicData.reset();
SpiServer pluginApi = server().getPluginApi();
@@ -241,12 +239,17 @@ public class TestQueryFindIterate extends BaseTestCase {
}
int startConns = dsPool.getStatus(false).getBusy();
QueryIterator<Customer> queryIterator = server().find(Customer.class)
.where()
.isNotNull("name")
.setMaxRows(3)
.order().asc("id")
.findIterate();
final Query<Customer> query = server().find(Customer.class)
.where()
.isNotNull("name")
.setMaxRows(3)
.order().asc("id");
if (withLoadBatch) {
query.setLazyLoadBatchSize(1);
}
QueryIterator<Customer> queryIterator = query.findIterate();
assertThat(dsPool.getStatus(false).getBusy()).isEqualTo(startConns + 1);
@@ -267,7 +270,8 @@ public class TestQueryFindIterate extends BaseTestCase {
try {
queryIterator.next();
fail("noSuchElementException expected");
} catch (NoSuchElementException e) {}
} catch (NoSuchElementException e) {
logger.debug("Expected NoSuchElementException");
}
}
}
@@ -1,61 +0,0 @@
package org.tests.query;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.FetchConfig;
import io.ebean.Query;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicInteger;
public class TestQueryFindVisit extends BaseTestCase {
@Test
public void test() {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
Query<Customer> query = server.find(Customer.class).setAutoTune(false)
.fetch("contacts", new FetchConfig().query(2)).where().gt("id", 0)
.order("id")
.setMaxRows(2).query();
final AtomicInteger counter = new AtomicInteger(0);
query.findEach(bean -> counter.incrementAndGet());
Assert.assertEquals(2, counter.get());
}
/**
* Test the behaviour when an exception is thrown inside the findVisit().
*/
@Test(expected = IllegalStateException.class)
public void testVisitThrowingException() {
ResetBasicData.reset();
EbeanServer server = Ebean.getServer(null);
Query<Customer> query = server.find(Customer.class).setAutoTune(false)
.fetch("contacts", new FetchConfig().query(2)).where().gt("id", 0).order("id")
.setMaxRows(2).query();
final AtomicInteger counter = new AtomicInteger(0);
query.findEach(bean -> {
counter.incrementAndGet();
if (counter.intValue() > 0) {
throw new IllegalStateException("cause a failure");
}
});
Assert.assertFalse("Never get here - exception thrown", true);
}
}
@@ -1,7 +1,7 @@
package org.tests.query;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.DB;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
@@ -17,7 +17,7 @@ public class TestQueryInIdTypeConversion extends BaseTestCase {
ResetBasicData.reset();
List<Customer> list = Ebean.find(Customer.class).where().idIn("1", "2").findList();
List<Customer> list = DB.find(Customer.class).where().idIn("1", "2").findList();
assertThat(list).isNotEmpty();
}
@@ -26,7 +26,13 @@ public class TestQueryInIdTypeConversion extends BaseTestCase {
ResetBasicData.reset();
List<Customer> list = Ebean.find(Customer.class).where().idIn().findList();
List<Customer> list = DB.find(Customer.class).where().idIn().findList();
assertThat(list).isEmpty();
}
@Test
public void test_emptyList_idIn() {
List<Customer> list = DB.find(Customer.class).where().idIn(new Object[] {}).findList();
assertThat(list).isEmpty();
}
}
@@ -1,6 +1,7 @@
package org.tests.query;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import io.ebean.Ebean;
import io.ebean.Expr;
import io.ebean.Query;
@@ -57,7 +58,7 @@ public class TestWhereRawClause extends BaseTestCase {
ResetBasicData.reset();
Query<Customer> query = Ebean.find(Customer.class)
Query<Customer> query = DB.find(Customer.class)
.where()
.raw("id in (select c.id from o_customer c where c.name in (?1))", asList("Rob", "Fiona", "Jack"))
.query();
@@ -41,7 +41,7 @@ public class TestOrderByWithDistinct extends BaseTestCase {
}
@Test
@IgnorePlatform({Platform.MYSQL, Platform.SQLSERVER, Platform.NUODB}) // do not support nulls first/last
@IgnorePlatform({Platform.MYSQL, Platform.MARIADB, Platform.SQLSERVER, Platform.NUODB}) // do not support nulls first/last
public void testDistinctOn() {
MRole role = Ebean.getReference(MRole.class, 1);
@@ -1,10 +1,9 @@
package org.tests.query.other;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.DB;
import io.ebean.Query;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Assert;
import org.junit.Test;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
@@ -12,6 +11,7 @@ import org.tests.model.basic.ResetBasicData;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
public class TestQueryRowCountWithMany extends BaseTestCase {
@@ -24,7 +24,7 @@ public class TestQueryRowCountWithMany extends BaseTestCase {
Long productId = 1L;
Query<Order> query = Ebean.find(Order.class)
Query<Order> query = DB.find(Order.class)
.fetch("details")
.where().eq("details.product.id", productId)
.order("cretime asc").query();
@@ -66,8 +66,8 @@ public class TestQueryRowCountWithMany extends BaseTestCase {
List<String> sqlLogged = LoggedSqlCollector.stop();
Assert.assertEquals(list.size(), rowCount);
Assert.assertEquals(2, sqlLogged.size());
assertEquals(list.size(), rowCount);
assertEquals(2, sqlLogged.size());
assertThat(trimSql(sqlLogged.get(1), 1)).contains(
"select count(*) from ( select distinct t0.id from o_order t0 join o_order_detail u1 on u1.order_id = t0.id where u1.product_id = ?)");
@@ -81,19 +81,20 @@ public class TestQueryRowCountWithMany extends BaseTestCase {
Long productId = 1L;
Query<Order> query = Ebean.find(Order.class)
Query<Order> query = DB.find(Order.class)
.fetch("details")
.where().eq("details.product.id", productId)
.setFirstRow(2)
.setMaxRows(20)
.orderBy("cretime asc");
.orderBy("cretime asc")
.query();
LoggedSqlCollector.start();
query.findCount();
List<String> sqlLogged = LoggedSqlCollector.stop();
Assert.assertEquals(1, sqlLogged.size());
assertEquals(1, sqlLogged.size());
assertThat(trimSql(sqlLogged.get(0), 1)).contains("select count(*) from ( select distinct t0.id from o_order t0 join o_order_detail u1 on u1.order_id = t0.id where u1.product_id = ?)");
query.findList();
@@ -26,7 +26,7 @@ public class TestWhereLikeWithSlash extends BaseTestCase {
Query<EBasic> query1 = Ebean.find(EBasic.class).where().like("name", "slash\\mon%").query();
List<EBasic> list1 = query1.findList();
if (!isMySql() && !isNuoDb()) {
if (!isMySql() && !isNuoDb() && !isMariaDB()) {
// For mysql this assert depends on no_backslash_escapes setting so we won't assert here
// Still good on Postgres which was the original issue
assertEquals(1, list1.size());
@@ -117,7 +117,7 @@ public class TestRawSqlOrmQuery extends BaseTestCase {
}
@Test
@IgnorePlatform({Platform.MYSQL, Platform.SQLSERVER})
@IgnorePlatform({Platform.MYSQL, Platform.MARIADB, Platform.SQLSERVER})
public void test_upperCaseSql() {
ResetBasicData.reset();
+20 -17
View File
@@ -36,6 +36,9 @@ public class TestNewTypes extends BaseTestCase {
@Test
public void testInsertUpdate() throws InterruptedException {
DB.find(SomeNewTypesBean.class).delete();
SomeNewTypesBean bean = new SomeNewTypesBean();
bean.setLocalDate(LocalDate.now());
bean.setLocalDateTime(LocalDateTime.now());
@@ -65,55 +68,55 @@ public class TestNewTypes extends BaseTestCase {
DB.save(bean);
Thread.sleep(DB_CLOCK_DELTA); // wait, to ensure that instant < Instant.now()
List<SomeNewTypesBean> list = DB.find(SomeNewTypesBean.class).where().lt("instant", Instant.now()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().le("localDate", LocalDate.now()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().lt("localDateTime", LocalDateTime.now()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().lt("offsetDateTime", OffsetDateTime.now()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().lt("zonedDateTime", ZonedDateTime.now()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().eq("sqlDate", bean.getSqlDate()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().eq("sqlTime", bean.getSqlTime()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().le("localTime", LocalTime.now()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().eq("zoneId", ZoneId.systemDefault().getId()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().eq("zoneOffset", ZonedDateTime.now().getOffset()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().le("yearMonth", YearMonth.of(2014, 9)).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().le("monthDay", MonthDay.of(9,22)).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().le("year", Year.now()).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().le("month", Month.SEPTEMBER).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().eq("path", Paths.get(TEMP_PATH)).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().eq("period", Period.of(4,3,2)).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
list = DB.find(SomeNewTypesBean.class).where().eq("duration", Duration.ofMinutes(5)).findList();
assertTrue(!list.isEmpty());
assertThat(list).isNotEmpty();
SomeNewTypesBean fetched = DB.find(SomeNewTypesBean.class, bean.getId());
@@ -2,7 +2,6 @@ package org.tests.update;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import io.ebean.Ebean;
import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import org.ebeantest.LoggedSqlCollector;
@@ -21,7 +20,7 @@ public class TestSqlUpdateBindMultipleLists extends BaseTestCase {
LoggedSqlCollector.start();
SqlUpdate sqlUpdate = Ebean.createSqlUpdate("delete from o_customer where id in (:ids)");
SqlUpdate sqlUpdate = DB.sqlUpdate("delete from o_customer where id in (:ids)");
sqlUpdate.setParameter("ids", asList(9991, 9992, 9993));
sqlUpdate.execute();
@@ -39,7 +38,7 @@ public class TestSqlUpdateBindMultipleLists extends BaseTestCase {
@Test
public void positionParamsExpansion() {
SqlUpdate sqlUpdate = Ebean.createSqlUpdate("delete from o_customer where id in (?1)");
SqlUpdate sqlUpdate = DB.sqlUpdate("delete from o_customer where id in (?1)");
sqlUpdate.setParameter(1, asList(9991, 9992, 9993));
sqlUpdate.execute();
@@ -53,7 +52,7 @@ public class TestSqlUpdateBindMultipleLists extends BaseTestCase {
assertEquals("delete from o_customer where id in (?,?)", sqlUpdate.getGeneratedSql());
sqlUpdate = Ebean.createSqlUpdate("delete from o_customer where id in (?1)");
sqlUpdate = DB.sqlUpdate("delete from o_customer where id in (?1)");
sqlUpdate.setParameter(1, asList(9993));
sqlUpdate.execute();
@@ -64,30 +63,41 @@ public class TestSqlUpdateBindMultipleLists extends BaseTestCase {
@Test
public void positionParamsExpansion_withPrePost() {
SqlUpdate sqlUpdate = Ebean.createSqlUpdate("delete from o_customer where id > ? and id in (?2) and id < ?");
SqlUpdate sqlUpdate = DB.sqlUpdate("delete from o_customer where id > ? and id in (?2) and id < ?")
.setParameter(1, 90)
.setParameter(2, asList(9991, 9992, 9993))
.setParameter(3, 91);
sqlUpdate.setParameter(1, 90);
sqlUpdate.setParameter(2, asList(9991, 9992, 9993));
sqlUpdate.setParameter(3, 91);
sqlUpdate.execute();
assertEquals("delete from o_customer where id > ? and id in (?,?,?) and id < ?", sqlUpdate.getGeneratedSql());
}
@Test
public void positionParamsExpansion_withPrePost_2() {
SqlUpdate sqlUpdate = DB.sqlUpdate("delete from o_customer where id > ? and id in (?2) and id < ?")
.setParameter(90)
.setParameter(asList(9991, 9992, 9993))
.setParameter(91);
sqlUpdate.execute();
assertEquals("delete from o_customer where id > ? and id in (?,?,?) and id < ?", sqlUpdate.getGeneratedSql());
}
@Test
public void positionParamsExpansion_withPrePost_usingParam() {
SqlUpdate sqlUpdate = DB.sqlUpdate("delete from o_customer where id > ? and id in (?2) and id < ?");
sqlUpdate.setParameters(90, asList(9991, 9992, 9993), 91);
sqlUpdate.execute();
SqlUpdate sqlUpdate = DB.sqlUpdate("delete from o_customer where id > ? and id in (?2) and id < ?")
.setParameters(90, asList(9991, 9992, 9993), 91);
sqlUpdate.execute();
assertEquals("delete from o_customer where id > ? and id in (?,?,?) and id < ?", sqlUpdate.getGeneratedSql());
}
@Test
public void positionParamsExpansion_withPre() {
SqlUpdate sqlUpdate = Ebean.createSqlUpdate("delete from o_customer where name = ? and id in (?2)");
SqlUpdate sqlUpdate = DB.sqlUpdate("delete from o_customer where name = ? and id in (?2)");
sqlUpdate.setParameter(1, "Foo");
sqlUpdate.setParameter(2, asList(9991, 9992, 9993));
@@ -120,7 +130,7 @@ public class TestSqlUpdateBindMultipleLists extends BaseTestCase {
@Test
public void positionParamsExpansion_withPost() {
SqlUpdate sqlUpdate = Ebean.createSqlUpdate("delete from o_customer where id in (?1) and name = ?");
SqlUpdate sqlUpdate = DB.sqlUpdate("delete from o_customer where id in (?1) and name = ?");
sqlUpdate.setParameter(1, asList(9991, 9992, 9993));
sqlUpdate.setParameter(2, "Foo");
@@ -253,7 +263,7 @@ public class TestSqlUpdateBindMultipleLists extends BaseTestCase {
@Test
public void test_multipleLists() {
SqlUpdate sqlUpdate = Ebean.createSqlUpdate("delete from o_customer where id in (:ids) and name in (:names)");
SqlUpdate sqlUpdate = DB.sqlUpdate("delete from o_customer where id in (:ids) and name in (:names)");
sqlUpdate.setParameter("ids", asList(9991, 9992, 9993));
sqlUpdate.setParameter("names", asList("rob", "jim"));
@@ -42,7 +42,7 @@ alter table migtest_e_basic alter column status set default 'A';
alter table migtest_e_basic alter column status set not null;
alter table migtest_e_basic add constraint ck_migtest_e_basic_status check ( status in ('N','A','I','?'));
alter table migtest_e_basic drop constraint if exists ck_migtest_e_basic_status2;
alter table migtest_e_basic alter column status2 type varchar(127);
alter table migtest_e_basic alter column status2 type varchar(127) using status2::varchar(127);
alter table migtest_e_basic alter column status2 drop default;
alter table migtest_e_basic alter column status2 drop not null;
@@ -71,7 +71,7 @@ alter table migtest_e_enum drop constraint if exists ck_migtest_e_enum_test_stat
alter table migtest_e_history alter column test_string TYPE bigint USING (test_string::integer);
comment on column migtest_e_history.test_string is 'Column altered to long now';
alter table migtest_e_history alter column test_string type bigint;
alter table migtest_e_history alter column test_string type bigint using test_string::bigint;
comment on table migtest_e_history is 'We have history now';
-- NOTE: table has @History - special migration may be necessary
@@ -85,8 +85,8 @@ alter table migtest_e_history2_history add column test_string2 varchar(255);
alter table migtest_e_history2_history add column test_string3 varchar(255) default 'unknown';
alter table migtest_e_history2_history add column new_column varchar(20);
alter table migtest_e_history4 alter column test_number type bigint;
alter table migtest_e_history4_history alter column test_number type bigint;
alter table migtest_e_history4 alter column test_number type bigint using test_number::bigint;
alter table migtest_e_history4_history alter column test_number type bigint using test_number::bigint;
alter table migtest_e_history5 add column test_boolean boolean default false not null;
alter table migtest_e_history5_history add column test_boolean boolean default false;
@@ -26,7 +26,7 @@ alter table migtest_e_basic add constraint ck_migtest_e_basic_status check ( sta
update migtest_e_basic set status2 = 'N' where status2 is null;
alter table migtest_e_basic drop constraint if exists ck_migtest_e_basic_status2;
alter table migtest_e_basic alter column status2 type varchar(1);
alter table migtest_e_basic alter column status2 type varchar(1) using status2::varchar(1);
alter table migtest_e_basic alter column status2 set default 'N';
alter table migtest_e_basic alter column status2 set not null;
alter table migtest_e_basic add constraint ck_migtest_e_basic_status2 check ( status2 in ('N','A','I'));
@@ -57,8 +57,8 @@ alter table migtest_e_history2 add column obsolete_string2 varchar(255);
alter table migtest_e_history2_history add column obsolete_string1 varchar(255);
alter table migtest_e_history2_history add column obsolete_string2 varchar(255);
alter table migtest_e_history4 alter column test_number type integer;
alter table migtest_e_history4_history alter column test_number type integer;
alter table migtest_e_history4 alter column test_number type integer using test_number::integer;
alter table migtest_e_history4_history alter column test_number type integer using test_number::integer;
alter table migtest_e_history6 alter column test_number1 drop default;
alter table migtest_e_history6 alter column test_number1 drop not null;
+4
View File
@@ -135,6 +135,10 @@ datasource.mysql.url=jdbc:mysql://127.0.0.1:4306/unit
#datasource.mysql.url=jdbc:mysql://127.0.0.1:4306/test_ebean
#datasource.mysql.driver=com.mysql.cj.jdbc.Driver
datasource.mariadb.username=unit
datasource.mariadb.password=unit
datasource.mariadb.url=jdbc:mariadb://localhost:4306/unit?useLegacyDatetimeCode=false
datasource.cockroach.username=root
datasource.cockroach.password=
datasource.cockroach.url=jdbc:postgresql://localhost:26257/unit?sslmode=disable
+1 -1
View File
@@ -5,7 +5,7 @@
drop view order_agg_vw if exists;
</ddl-script>
<ddl-script name="order views" platforms="db2,h2,postgres,oracle,mysql,nuodb">
<ddl-script name="order views" platforms="db2,h2,postgres,oracle,mysql,mariadb,nuodb">
create or replace view order_agg_vw as
select d.order_id, sum(d.order_qty * d.unit_price) as order_total,
sum(d.ship_qty * d.unit_price) as ship_total
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<createTable name="document" withHistory="true" pkName="pk_document">
<column name="id" type="bigint" primaryKey="true" references="document_draft.id" foreignKeyName="fk_document_id"/>
<column name="short_title" type="varchar(255)"/>
</createTable>
<renameColumn tableName="document" oldName="short_title" newName="title" />
</changeSet>
</migration>