Compare commits

...
Author SHA1 Message Date
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
rob bygrave d758590da4 [maven-release-plugin] prepare release ebean-12.3.2 2020-05-12 22:39:31 +12:00
rob bygrave 21a5efa9b9 Tidy tests - tidy up TestCsvReader and TestCsvReaderWithCallback 2020-05-12 21:28:31 +12:00
rob bygrave 55a2cb12bb #2007 - Encrypted fields unable to be mapped when using DtoQuery 2020-05-12 21:11:28 +12:00
rob bygrave 293700acc0 #2007 - Encrypted fields unable to be mapped when using DtoQuery 2020-05-12 20:25:56 +12:00
rob bygrave 8980a3b6fd No functional change - tidy DbEncrypt, AbstractDbEncrypt 2020-05-12 16:34:08 +12:00
rob bygrave 72efc5d7d6 #2005 - Mark Ebean as deprecated - migrate to io.ebean.Database (EbeanServer -> Database) 2020-05-11 23:15:02 +12:00
rob bygrave 84589fcce6 Bump the versions of ebean-maven-plugin and maven-surefire-plugin 2020-05-11 23:09:24 +12:00
rob bygrave a0e44aedf3 Fix IllegalStateException to use correct dependency io.ebean:ebean-elastic in error message 2020-05-11 20:29:58 +12:00
rob bygrave 41102aca35 Add clearBatch() on BatchedPstmt construction 2020-05-08 23:32:24 +12:00
rob bygrave 548cea432e Fix test UpdateQueryTest for PG 2020-05-08 23:22:23 +12:00
rob bygrave 592cb37693 Reset test default to H2 2020-05-08 23:01:59 +12:00
rob bygrave f2178bf0a6 #2003 - Bump ebean-datasource to 4.9.2 with optional driver 2020-05-04 12:41:14 +12:00
rob bygrave 5cae425b09 #2002 - Support maxRows with delete query 2020-05-04 12:28:52 +12:00
rob bygrave 94b78f1c2d [maven-release-plugin] prepare for next development iteration 2020-04-30 23:41:07 +12:00
101 changed files with 1532 additions and 643 deletions
+13 -6
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.3.1</version>
<version>12.3.4</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.1</tag>
<tag>ebean-12.3.4</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.1</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.2.4</version>
<version>12.3.4</version>
<executions>
<execution>
<id>test</id>
@@ -310,7 +317,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<version>3.0.0-M4</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<trimStackTrace>false</trimStackTrace>
@@ -31,15 +31,18 @@ public interface BackgroundExecutor {
* Execute a task periodically with a fixed delay between each execution.
* <p>
* For example, execute a runnable every minute.
* </p>
* <p>
* The delay is the time between executions no matter how long the task took.
* That is, this method has the same behaviour characteristics as
* {@link ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)}
* </p>
*/
void executePeriodically(Runnable r, long delay, TimeUnit unit);
/**
* Execute a task periodically additionally with an initial delay different from delay.
*/
void executePeriodically(Runnable r, long initialDelay, long delay, TimeUnit unit);
/**
* Schedules a Runnable for one-shot action that becomes enabled after the given delay.
*
+2 -1
View File
@@ -24,7 +24,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
/**
* Ebean will be Deprecated - please migrate to use DB.
* Deprecated - please migrate to use <code>io.ebean.DB</code>.
* <p>
* Ebean is a registry of {@link Database} by name. Ebean has now been renamed to {@link DB}.
* <p>
@@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap;
* <p>
* The preference is to use DB and Database rather than Ebean and EbeanServer.
*/
@Deprecated
public final class Ebean {
private static final Logger logger = LoggerFactory.getLogger(Ebean.class);
+2
View File
@@ -1,6 +1,7 @@
package io.ebean;
/**
* Deprecated - please migrate to <code>io.ebean.Database</code>.
* Provides the API for fetching and saving beans to a particular Database.
* <p>
* Effectively this is an alias for {@link Database} which is now the new
@@ -8,6 +9,7 @@ package io.ebean;
* <p>
* The preference is to use DB and Database rather than Ebean and EbeanServer.
*/
@Deprecated
public interface EbeanServer extends Database {
}
@@ -11,6 +11,8 @@ import java.util.Properties;
import java.util.ServiceLoader;
/**
* Deprecated - please migrate to DatabaseFactory.
* <p>
* Creates EbeanServer instances.
* <p>
* This uses either a ServerConfig or properties in the ebean.properties file to
@@ -28,6 +30,7 @@ import java.util.ServiceLoader;
* convenient way of using the 'default/primary' EbeanServer.
* </p>
*/
@Deprecated
public class EbeanServerFactory {
+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();
@@ -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 {};
}
@@ -44,6 +44,8 @@ import java.util.Properties;
import java.util.ServiceLoader;
/**
* Deprecated - please migrate to <code>io.ebean.DatabaseConfig</code>.
*
* The configuration used for creating a Database.
* <p>
* Used to programmatically construct a Database and optionally register it
@@ -80,6 +82,7 @@ import java.util.ServiceLoader;
* @author rbygrave
* @see DatabaseFactory
*/
@Deprecated
public class ServerConfig {
/**
@@ -43,11 +43,8 @@ public abstract class AbstractDbEncrypt implements DbEncrypt {
public DbEncryptFunction getDbEncryptFunction(int jdbcType) {
switch (jdbcType) {
case Types.VARCHAR:
return varcharEncryptFunction;
case Types.CLOB:
return varcharEncryptFunction;
case Types.CHAR:
return varcharEncryptFunction;
case Types.LONGVARCHAR:
return varcharEncryptFunction;
@@ -7,30 +7,18 @@ package io.ebean.config.dbplatform;
* As an alternative to using DB encryption you can encrypt/decrypt in java via
* a special ScalarType but this has the limitation that you can't include that
* property in query where clauses.
* </p>
*
* @author rbygrave
*/
public interface DbEncrypt {
// /**
// * Return the SQL for decrypting a column returning a VARCHAR.
// */
// String getDecryptSql(String columnWithTableAlias);
//
// /**
// * Return the DB function with bind variables used to encrypt a VARCHAR
// * value.
// */
// String getEncryptBindSql();
/**
* Return the DB encrypt function for the given JDBC type.
*/
DbEncryptFunction getDbEncryptFunction(int jdbcType);
/**
* Return the DB type that encrypted Strings are stored in.
* <p>
* This is VARCHAR for MySql and VARBINARY for most others.
* </p>
*/
int getEncryptDbType();
@@ -0,0 +1,38 @@
package io.ebean.config.dbplatform.mariadb;
import io.ebean.config.dbplatform.DbStandardHistorySupport;
/**
* History support for MariaDB.
*/
public class MariaDbHistorySupport extends DbStandardHistorySupport {
/**
* Return the ' as of timestamp ?' clause appended after the table name.
*/
@Override
public String getAsOfViewSuffix(String asOfViewSuffix) {
return " for system_time as of ?";
}
@Override
public String getVersionsBetweenSuffix(String asOfViewSuffix) {
return " for system_time between ? and ?";
}
/**
* Returns the SQL Server specific effective start column.
*/
@Override
public String getSysPeriodLower(String tableAlias, String sysPeriod) {
return tableAlias + "." + sysPeriod + "From";
}
/**
* Returns the SQL Server specific effective end column.
*/
@Override
public String getSysPeriodUpper(String tableAlias, String sysPeriod) {
return tableAlias + "." + sysPeriod + "To";
}
}
@@ -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";
}
}
@@ -458,7 +458,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
/**
* Set the query to be a delete query.
*/
void setupForDeleteOrUpdate(boolean deleteRequest);
void setupForDeleteOrUpdate();
/**
* Set the query to be delete by ids due to cascading delete.
@@ -788,7 +788,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
/**
* Set to true when we only include the Id property if it is explicitly included in the select().
*/
void setManualId(boolean manualId);
void setManualId();
/**
* Set default select clauses where none have been explicitly defined.
@@ -313,4 +313,9 @@ public interface SpiTransaction extends Transaction {
* Return true when nested transactions should create Savepoints.
*/
boolean isNestedUseSavepoint();
/**
* Return true if explicitly set to skip cache (ignores skipOnWrite).
*/
boolean isSkipCacheExplicit();
}
@@ -158,6 +158,11 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
transaction.setSkipCache(skipCache);
}
@Override
public boolean isSkipCacheExplicit() {
return transaction.isSkipCacheExplicit();
}
@Override
public boolean isSkipCache() {
return transaction.isSkipCache();
@@ -15,6 +15,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mariadb.MariaDbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
@@ -820,6 +821,8 @@ public class DefaultDbMigration implements DbMigration {
return new Postgres9Platform();
case POSTGRES:
return new PostgresPlatform();
case MARIADB:
return new MariaDbPlatform();
case MYSQL55:
return new MySql55Platform();
case MYSQL:
@@ -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,98 @@
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 {
private String systemPeriodStart;
private String systemPeriodEnd;
private PlatformDdl platformDdl;
@Override
public void configure(ServerConfig serverConfig, PlatformDdl platformDdl) {
this.systemPeriodStart = serverConfig.getAsOfSysPeriod() + "From";
this.systemPeriodEnd = serverConfig.getAsOfSysPeriod() + "To";
this.platformDdl = platformDdl;
}
@Override
public void createWithHistory(DdlWrite writer, MTable table) throws IOException {
String baseTable = table.getName();
enableSystemVersioning(writer, baseTable);
}
String getHistoryTable(String baseTable) {
String historyTable = baseTable + "_history";
if (baseTable.startsWith("[")) {
historyTable = historyTable.replace("]", "") + "]";
}
if (historyTable.indexOf('.') == -1) {
// history must contain schema, add the default schema if none was specified
historyTable = "dbo." + historyTable;
}
return historyTable;
}
private void enableSystemVersioning(DdlWrite writer, String baseTable) throws IOException {
DdlBuffer apply = writer.applyHistoryView();
final String systemTime = String.format("system_time(%s, %s),", systemPeriodStart, systemPeriodEnd);
apply.append("alter table ").append(baseTable).newLine()
.append(" add column ").append(systemPeriodStart).append(" timestamp(6) generated always as row start,").newLine()
.append(" add column ").append(systemPeriodEnd).append(" timestamp(6) generated always as row end,").newLine()
.append(" add period for ").append(systemTime).newLine()
.append(" add system versioning;").newLine();
DdlBuffer drop = writer.dropAll();
//drop.append("IF OBJECT_ID('").append(baseTable).append("', 'U') IS NOT NULL alter table ").append(baseTable).append(" set (system_versioning = off)").endOfStatement();
//drop.append("IF OBJECT_ID('").append(baseTable).append("_history', 'U') IS NOT NULL drop table ").append(baseTable).append("_history").endOfStatement();
}
@Override
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException {
String baseTable = dropHistoryTable.getBaseTable();
DdlBuffer apply = writer.applyHistoryView();
apply.append("-- dropping history support for ").append(baseTable).endOfStatement();
// drop default constraints
apply.append(platformDdl.alterColumnDefaultValue(baseTable, systemPeriodStart, DdlHelp.DROP_DEFAULT)).endOfStatement();
apply.append(platformDdl.alterColumnDefaultValue(baseTable, systemPeriodEnd, DdlHelp.DROP_DEFAULT)).endOfStatement();
// switch of versioning & period
apply.append("alter table ").append(baseTable).append(" set (system_versioning = off)").endOfStatement();
apply.append("alter table ").append(baseTable).append(" drop period for system_time").endOfStatement();
// now drop tables & columns
apply.append("alter table ").append(baseTable).append(" drop column ").append(systemPeriodStart).endOfStatement();
apply.append("alter table ").append(baseTable).append(" drop column ").append(systemPeriodEnd).endOfStatement();
//apply.append("IF OBJECT_ID('").append(baseTable).append("_history', 'U') IS NOT NULL drop table ").append(baseTable).append("_history").endOfStatement();
apply.end();
}
@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) throws IOException {
// SQL Server 2016 does not need triggers
DdlBuffer apply = writer.applyHistoryView();
String baseTableName = baseTable.getBaseTable();
apply.append("-- alter table ").append(baseTableName).append(" set (system_versioning = off (history_table=")
.append(getHistoryTable(baseTableName)).append("))").endOfStatement();
apply.append("-- history migration goes here").newLine();
apply.append("-- alter table ").append(baseTableName).append(" set (system_versioning = on (history_table=")
.append(getHistoryTable(baseTableName)).append("))").endOfStatement();
}
}
@@ -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 {
@@ -1507,19 +1507,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());
@@ -47,7 +47,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
SpiQuery<?> ormQuery = query.getOrmQuery();
if (ormQuery != null) {
ormQuery.setType(type);
ormQuery.setManualId(true);
ormQuery.setManualId();
// execute the underlying ORM query returning the ResultSet
SpiResultSet result = server.findResultSet(ormQuery, trans);
@@ -102,7 +102,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
@SuppressWarnings("unchecked")
public T readNextBean() throws SQLException {
dataReader.resetColumnPosition();
return (T)plan.readRow(dataReader);
return (T) plan.readRow(dataReader);
}
private DtoMappingRequest mappingRequest() throws SQLException {
@@ -110,19 +110,29 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
}
private DtoColumn[] readMeta() throws SQLException {
ResultSetMetaData metaData = resultSet.getMetaData();
int cols = metaData.getColumnCount();
DtoColumn[] meta = new DtoColumn[cols];
for (int i = 0; i < cols; i++) {
int pos = i+1;
int pos = i + 1;
String columnLabel = metaData.getColumnLabel(pos);
if (columnLabel == null) {
columnLabel = metaData.getColumnName(pos);
}
meta[i] = new DtoColumn(columnLabel);
meta[i] = new DtoColumn(parseColumn(columnLabel));
}
return meta;
}
static String parseColumn(String columnLabel) {
if (columnLabel.startsWith("_e_") || columnLabel.startsWith("_E_")) {
// encrypted column alias in the form _e_<tableAlias>_<column>
final int pos = columnLabel.indexOf("_", 3);
if (pos > -1) {
return columnLabel.substring(pos + 1);
}
}
return columnLabel;
}
}
@@ -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,10 +503,9 @@ public class InternalConfiguration {
* Create the DataTimeZone implementation to use.
*/
private DataTimeZone initDataTimeZone() {
String tz = serverConfig.getDataTimeZone();
if (tz == null) {
if (isMySql(getPlatform())) {
if (isMySqlOrMariaDB(getPlatform())) {
return new MySqlDataTimeZone();
}
return new NoDataTimeZone();
@@ -518,8 +517,8 @@ public class InternalConfiguration {
}
}
private boolean isMySql(Platform platform) {
return platform.base() == Platform.MYSQL;
private boolean isMySqlOrMariaDB(Platform platform) {
return platform.base() == Platform.MYSQL || platform.base() == Platform.MARIADB;
}
public DataTimeZone getDataTimeZone() {
@@ -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);
@@ -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();
}
@@ -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() {
@@ -542,12 +542,11 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
return dbEncryptFunction.getDecryptSql(propertyName);
}
public String getDecryptSql() {
return dbEncryptFunction.getDecryptSql(this.getDbColumn());
}
private String getDecryptSql(String tableAlias) {
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn());
/**
* Return the SQL for the column including decryption function and column alias.
*/
private String getDecryptSqlWithColumnAlias(String tableAlias) {
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()) + " _e_" + tableAlias + "_" + this.getDbColumn();
}
@Override
@@ -600,8 +599,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
}
if (dbEncrypted) {
String decryptSql = getDecryptSql(ctx.peekTableAlias());
ctx.appendRawColumn(decryptSql);
ctx.appendRawColumn(getDecryptSqlWithColumnAlias(ctx.peekTableAlias()));
ctx.addEncryptedProp(this);
} else {
@@ -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);
}
@@ -53,8 +53,9 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
/**
* Create with a given statement.
*/
public BatchedPstmt(PreparedStatement pstmt, boolean isGenKeys, String sql, SpiTransaction transaction) {
public BatchedPstmt(PreparedStatement pstmt, boolean isGenKeys, String sql, SpiTransaction transaction) throws SQLException {
this.pstmt = pstmt;
this.pstmt.clearBatch();
this.isGenKeys = isGenKeys;
this.sql = sql;
this.transaction = transaction;
@@ -92,7 +93,7 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
private void flushStatementBatch() throws SQLException {
final int[] rows = pstmt.executeBatch();
if (rows.length != list.size()) {
throw new IllegalStateException("Invalid state? rows:" + rows.length + " != " + list.size());
throw new IllegalStateException("Invalid state on executeBatch, rows:" + rows.length + " != " + list.size());
}
for (BatchPostExecute item : list) {
item.postExecute();
@@ -156,8 +157,7 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
try {
results = pstmt.executeBatch();
if (results.length != list.size()) {
String s = "results array error " + results.length + " " + list.size();
throw new SQLException(s);
throw new SQLException("Invalid state on executeBatch, rows:" + results.length + " != " + list.size());
}
// check for concurrency exceptions...
for (int i = 0; i < results.length; i++) {
@@ -13,6 +13,9 @@ import io.ebeaninternal.server.persist.dmlbind.BindableList;
import java.sql.SQLException;
import static io.ebean.annotation.Platform.MARIADB;
import static io.ebean.annotation.Platform.MYSQL;
/**
* Meta data for insert handler. The meta data is for a particular bean type. It
* is considered immutable and is thread safe.
@@ -166,12 +169,7 @@ final class InsertMeta {
request.append("insert into ").append(table);
if (nullId && noColumnsForInsert(draftTable)) {
if (this.platform.base() == Platform.MYSQL) {
request.append(" values (default)");
} else {
request.append(" default values");
}
return request.toString();
return request.append(defaultValues()).toString();
}
request.append(" (");
@@ -199,6 +197,10 @@ final class InsertMeta {
return request.toString();
}
private String defaultValues() {
return platform.base() == MYSQL || platform.base() == MARIADB ? " values (default)" : " default values";
}
/**
* Return true if the insert actually contains no columns.
*/
@@ -608,12 +608,10 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
}
QueryIterator<T> readIterate(int bufferSize, OrmQueryRequest<T> request) {
if (bufferSize > 0) {
return new CQueryIteratorWithBuffer<>(this, request, bufferSize);
} else {
if (bufferSize == 1) {
return new CQueryIteratorSimple<>(this, request);
} else {
return new CQueryIteratorWithBuffer<>(this, request, bufferSize);
}
}
@@ -95,7 +95,7 @@ class CQueryBuilder {
SpiQuery<T> query = request.getQuery();
String rootTableAlias = query.getAlias();
query.setupForDeleteOrUpdate(deleteRequest);
query.setupForDeleteOrUpdate();
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
@@ -125,7 +125,7 @@ class CQueryBuilder {
private <T> String buildDeleteSql(OrmQueryRequest<T> request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) {
String alias = alias(rootTableAlias);
if (sqlTree.noJoins()) {
if (sqlTree.noJoins() && !request.getQuery().hasMaxRowsOrFirstRow()) {
if (dbPlatform.isSupportsDeleteTableAlias()) {
// delete from table <alias> ...
return aliasReplace(buildSqlDelete("delete", request, predicates, sqlTree).getSql(), alias);
@@ -201,26 +201,20 @@ public class CQueryEngine {
* when you have finished).
*/
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
if (request.isIterateSingleContext()) {
// expected relatively small number of results, single persistence context
cquery.prepareBindExecuteQuery();
} else if (!cquery.prepareBindExecuteQueryForwardOnly(forwardOnlyHintOnFindIterate)) {
if (!cquery.prepareBindExecuteQueryForwardOnly(forwardOnlyHintOnFindIterate)) {
// query has been cancelled already
logger.trace("Future fetch already cancelled");
return null;
}
if (request.isLogSql()) {
logSql(cquery);
}
// first check batch sizes set on query joins
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize);
if (iterateBufferSize < 1) {
@@ -228,20 +222,19 @@ public class CQueryEngine {
int queryBatch = request.getQuery().getLazyLoadBatchSize();
if (queryBatch > 0) {
iterateBufferSize = queryBatch;
} else {
iterateBufferSize = 100;
}
}
QueryIterator<T> readIterate = cquery.readIterate(iterateBufferSize, request);
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
if (request.isAuditReads()) {
// indicates we need to audit as the iterator progresses
cquery.auditFindIterate();
}
return readIterate;
} catch (SQLException e) {
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.core.OrmQueryRequest;
import javax.persistence.PersistenceException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.NoSuchElementException;
/**
* A QueryIterator that uses a buffer to execute secondary queries periodically.
@@ -17,6 +18,7 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
private final OrmQueryRequest<T> request;
private final ArrayList<T> buffer;
private boolean closed;
private boolean moreToLoad = true;
CQueryIteratorWithBuffer(CQuery<T> cquery, OrmQueryRequest<T> request, int bufferSize) {
@@ -29,6 +31,7 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
@Override
@SuppressWarnings("unchecked")
public boolean hasNext() {
boolean ret = false;
try {
if (buffer.isEmpty() && moreToLoad) {
// load buffer
@@ -44,23 +47,33 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
}
request.executeSecondaryQueries(true);
}
return !buffer.isEmpty();
ret = !buffer.isEmpty();
return ret;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
if (!ret) {
close();
}
}
}
@Override
public T next() {
if (buffer.isEmpty()) {
throw new NoSuchElementException();
}
return buffer.remove(0);
}
@Override
public void close() {
cquery.updateExecutionStatisticsIterator();
cquery.close();
request.endTransIfRequired();
if (!closed) {
closed = true;
cquery.updateExecutionStatisticsIterator();
cquery.close();
request.endTransIfRequired();
}
}
@Override
@@ -328,11 +328,9 @@ class DefaultDbSqlContext implements DbSqlContext {
}
@Override
public void appendRawColumn(String rawcolumnWithTableAlias) {
public void appendRawColumn(String rawColumnWithAlias) {
sb.append(COMMA);
sb.append(rawcolumnWithTableAlias);
appendColumnAlias();
sb.append(rawColumnWithAlias);
}
@Override
@@ -133,7 +133,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
SpiQuery<T> query = request.getQuery();
if (request.isBeanCachePut()) {
if (request.isBeanCachePutMany()) {
// load the individual beans into the bean cache
BeanDescriptor<T> descriptor = request.getBeanDescriptor();
Collection<T> c = result.getActualDetails();
@@ -684,13 +684,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
* Setup to be a delete or update query.
*/
@Override
public void setupForDeleteOrUpdate(boolean deleteRequest) {
// unset any paging and select on the id in the case where the query
// includes joins and we use - delete ... where id in (...)
if (deleteRequest) {
maxRows = 0;
}
firstRow = 0;
public void setupForDeleteOrUpdate() {
forUpdate = null;
rootTableAlias = "${RTA}"; // alias we remove later
setSelectId();
@@ -1721,8 +1715,10 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public void setManualId(boolean manualId) {
this.manualId = manualId;
public void setManualId() {
if (detail != null && detail.hasSelectClause()) {
this.manualId = true;
}
}
/**
@@ -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,9 +187,45 @@ public final class DefaultPersistenceContext implements PersistenceContext {
private Set<Object> deleteSet;
private int initialSize;
private ClassContext() {
}
/**
* Create as a shallow copy.
*/
private ClassContext(ClassContext parent, boolean initial) {
if (initial || parent.isInitialSize()) {
this.map.putAll(parent.map);
this.initialSize = map.size();
if (parent.deleteSet != null) {
this.deleteSet = new HashSet<>(parent.deleteSet);
}
}
}
/**
* True if the map has not changed from it's initial size (no additions).
*/
private boolean isInitialSize() {
return map.size() == initialSize;
}
/**
* 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.
*/
private boolean resetLimit() {
return map.size() > initialSize + 1000;
}
@Override
public String toString() {
return "size:" + map.size();
@@ -164,7 +244,6 @@ public final class DefaultPersistenceContext implements PersistenceContext {
}
private Object putIfAbsent(Object id, Object bean) {
Object existingValue = map.get(id);
if (existingValue != null) {
// it is not absent
@@ -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);
}
@@ -19,7 +19,7 @@ import java.util.function.Predicate;
public class NoneDocStore implements DocumentStore {
public static IllegalStateException implementationNotInClassPath() {
throw new IllegalStateException("DocStore implementation not included in the classPath. You need to add the maven dependency for avaje-ebeanorm-elastic");
throw new IllegalStateException("DocStore implementation not included in the classPath. You need to add the maven dependency for io.ebean:ebean-elastic");
}
@Override
+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;
+8 -4
View File
@@ -116,7 +116,11 @@ public class UpdateQueryTest extends BaseTestCase {
assertThat(sql).hasSize(1);
assertThat(rows).isEqualTo(0);
assertSql(sql.get(0)).contains("update o_customer set status = ? where id in (?,?,?,?,?)"); // bind padding to 5
if (isPostgres()) {
assertSql(sql.get(0)).contains("update o_customer set status = ? where id = any(?)");
} else {
assertSql(sql.get(0)).contains("update o_customer set status = ? where id in (?,?,?,?,?)"); // bind padding to 5
}
}
@Test
@@ -164,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() {
@@ -210,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() {
@@ -226,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)");
@@ -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
@@ -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() {
@@ -0,0 +1,22 @@
package io.ebeaninternal.server.core;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class DtoQueryRequestTest {
@Test
public void testParse() {
assertEquals("foo", DtoQueryRequest.parseColumn("foo"));
assertEquals("bar", DtoQueryRequest.parseColumn("_e_t0_bar"));
assertEquals("BAR", DtoQueryRequest.parseColumn("_E_T0_BAR"));
assertEquals("baz", DtoQueryRequest.parseColumn("_e_t42_baz"));
assertEquals("BAZ", DtoQueryRequest.parseColumn("_E_T42_BAZ"));
assertEquals("e_t42_nope", DtoQueryRequest.parseColumn("e_t42_nope"));
assertEquals("_f_t42_nope", DtoQueryRequest.parseColumn("_f_t42_nope"));
}
}
@@ -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,12 +2,16 @@ 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 {
@@ -23,12 +27,12 @@ public class DefaultPersistenceContextTest {
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 +42,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 +52,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 +62,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 +71,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 +80,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 +102,7 @@ public class DefaultPersistenceContextTest {
}
@Test
public void getWithOption_when_there() throws Exception {
public void getWithOption_when_there() {
PersistenceContext pc = pcWith42();
@@ -107,7 +111,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 +122,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 +144,7 @@ public class DefaultPersistenceContextTest {
}
@Test
public void clearClass() throws Exception {
public void clearClass() {
PersistenceContext pc = pcWith42();
pc.clear(Customer.class);
@@ -148,7 +152,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 +163,94 @@ public class DefaultPersistenceContextTest {
pc.clear(Customer.class, 43);
assertThat(pc.size(Customer.class)).isEqualTo(0);
}
@Test
public void forIterate() {
final DefaultPersistenceContext pc = pcWith42();
// act
final PersistenceContext pcIterate = pc.forIterate();
assertThat(pc).isNotSameAs(pcIterate);
assertThat(pcIterate.size(Customer.class)).isEqualTo(1);
}
@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 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");
@@ -55,6 +55,49 @@ public class TestEncrypt extends BaseTestCase {
assertThat(loggedSql.get(1)).contains("left join e_basicenc t1 on t1.id = t0.other_id");
}
@Test
public void asDto() {
DB.find(EBasicEncrypt.class).delete();
EBasicEncrypt e = new EBasicEncrypt();
e.setName("name2");
e.setDescription("descEncrypted2");
e.setDob(new Date(System.currentTimeMillis() - 100000));
DB.save(e);
final EDto dto = DB.find(EBasicEncrypt.class)
.select("name, description")
.asDto(EDto.class)
.findOne();
assertThat(dto).isNotNull();
assertThat(dto.getName()).isEqualTo("name2");
assertThat(dto.getDescription()).isEqualTo("descEncrypted2");
}
public static class EDto {
String name;
String description;
public EDto(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
@Test
@ForPlatform(Platform.H2)
public void test() {
+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 (?)");
@@ -1,8 +1,8 @@
package org.tests.delete;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.Query;
import io.ebean.Transaction;
import io.ebean.annotation.IgnorePlatform;
@@ -21,15 +21,54 @@ import static org.assertj.core.api.Assertions.assertThat;
public class TestDeleteByQuery extends BaseTestCase {
private void createUser(String name) {
BBookmarkUser u1 = new BBookmarkUser(name);
DB.save(u1);
}
@Test
@IgnorePlatform({Platform.MYSQL, Platform.MARIADB})
public void deleteWithLimit() {
createUser("deleteWithLimit1");
createUser("deleteWithLimit2");
createUser("deleteWithLimit3");
createUser("deleteWithLimit4");
LoggedSqlCollector.start();
final int rows = DB.find(BBookmarkUser.class)
.where().startsWith("name", "deleteWithLimit")
.setMaxRows(3)
.delete();
assertThat(rows).isEqualTo(3);
List<String> sql = LoggedSqlCollector.stop();
if (isSqlServer()) {
assertSql(sql.get(0)).contains("delete from bbookmark_user where id in (select top 3 t0.id from bbookmark_user t0 where t0.name like ");
} else {
assertSql(sql.get(0)).contains("delete from bbookmark_user where id in (select t0.id from bbookmark_user t0 where t0.name like");
if (isH2() || isPostgres()) {
assertSql(sql.get(0)).contains("limit 3");
}
}
final int rowsAfter = DB.find(BBookmarkUser.class)
.where().startsWith("name", "deleteWithLimit")
.setMaxRows(3)
.delete();
assertThat(rowsAfter).isEqualTo(1);
}
@Test
@IgnorePlatform(Platform.MYSQL)
// FIXME: MySql does not the sub query selecting from the delete table
public void deleteWithSubquery() {
EbeanServer server = Ebean.getDefaultServer();
Database server = DB.getDefault();
BBookmarkUser u1 = new BBookmarkUser("u1");
Ebean.save(u1);
DB.save(u1);
Query<BBookmarkUser> query = server.find(BBookmarkUser.class)
.where().eq("org.name", "NahYeahMaybe")
@@ -71,7 +110,7 @@ public class TestDeleteByQuery extends BaseTestCase {
// FIXME: MySql does not the sub query selecting from the delete table
public void deleteWithSubquery_withEscalation() {
EbeanServer server = Ebean.getDefaultServer();
Database server = DB.getDefault();
Query<Contact> query = server.find(Contact.class).where().eq("group.name", "NahYeahMaybe").query();
@@ -104,8 +143,8 @@ public class TestDeleteByQuery extends BaseTestCase {
LoggedSqlCollector.start();
Ebean.find(BBookmarkUser.class).where().eq("id", 7000).delete();
Ebean.find(BBookmarkUser.class).setId(7000).delete();
DB.find(BBookmarkUser.class).where().eq("id", 7000).delete();
DB.find(BBookmarkUser.class).setId(7000).delete();
List<String> sql = LoggedSqlCollector.stop();
if (isPlatformSupportsDeleteTableAlias()) {
@@ -117,7 +156,7 @@ public class TestDeleteByQuery extends BaseTestCase {
}
// and note this is the easiest option
Ebean.delete(BBookmarkUser.class, 7000);
DB.delete(BBookmarkUser.class, 7000);
}
@Test
@@ -125,8 +164,8 @@ public class TestDeleteByQuery extends BaseTestCase {
LoggedSqlCollector.start();
Ebean.find(Contact.class).where().eq("id", 7000).delete();
Ebean.find(Contact.class).setId(7000).delete();
DB.find(Contact.class).where().eq("id", 7000).delete();
DB.find(Contact.class).setId(7000).delete();
List<String> sql = LoggedSqlCollector.stop();
// escalate to fetch ids then delete ... but no rows found
@@ -134,7 +173,7 @@ public class TestDeleteByQuery extends BaseTestCase {
assertSql(sql.get(1)).contains("select t0.id from contact t0 where t0.id = ?");
// and note this is the easiest option
Ebean.delete(Contact.class, 7000);
DB.delete(Contact.class, 7000);
}
@Test
@@ -142,10 +181,10 @@ public class TestDeleteByQuery extends BaseTestCase {
LoggedSqlCollector.start();
try (Transaction transaction = Ebean.beginTransaction()) {
try (Transaction transaction = DB.beginTransaction()) {
transaction.setPersistCascade(false);
Ebean.find(Contact.class).where().eq("id", 7001).delete();
DB.find(Contact.class).where().eq("id", 7001).delete();
transaction.commit();
}
@@ -163,7 +202,7 @@ public class TestDeleteByQuery extends BaseTestCase {
LoggedSqlCollector.start();
Ebean.find(Customer.class)
DB.find(Customer.class)
.where().eq("name", "Don Roberto")
.query().forUpdate()
.delete();
@@ -181,11 +220,11 @@ public class TestDeleteByQuery extends BaseTestCase {
public void deleteByPredicate() {
BBookmarkUser ud = new BBookmarkUser("deleteQueryByPredicate");
Ebean.save(ud);
DB.save(ud);
Ebean.find(BBookmarkUser.class).where().eq("name", "deleteQueryByPredicate").delete();
DB.find(BBookmarkUser.class).where().eq("name", "deleteQueryByPredicate").delete();
BBookmarkUser found = Ebean.find(BBookmarkUser.class, ud.getId());
BBookmarkUser found = DB.find(BBookmarkUser.class, ud.getId());
assertThat(found).isNull();
}
@@ -200,11 +239,11 @@ public class TestDeleteByQuery extends BaseTestCase {
contact.setLastName("deleteMe");
contact.setCustomer(all.get(0));
Ebean.save(contact);
DB.save(contact);
Ebean.find(Contact.class).where().eq("firstName", "DelByQueryFirstName").delete();
DB.find(Contact.class).where().eq("firstName", "DelByQueryFirstName").delete();
Contact contactFind = Ebean.find(Contact.class, contact.getId());
Contact contactFind = DB.find(Contact.class, contact.getId());
assertThat(contactFind).isNull();
}
@@ -214,13 +253,13 @@ public class TestDeleteByQuery extends BaseTestCase {
Country country = new Country();
country.setCode("XX");
country.setName("SecretName");
Ebean.save(country);
Query<Country> query = Ebean.find(Country.class).where().eq("name", "SecretName").setUseQueryCache(true);
DB.save(country);
Query<Country> query = DB.find(Country.class).where().eq("name", "SecretName").setUseQueryCache(true);
assertThat(query.findList()).hasSize(1);
assertThat(query.findCount()).isEqualTo(1);
Ebean.find(Country.class).where().eq("name", "SecretName").delete();
DB.find(Country.class).where().eq("name", "SecretName").delete();
//Ebean.getDefaultServer().getPluginApi().getBeanType(Country.class).clearQueryCache();
assertThat(query.findList()).hasSize(0);
assertThat(query.findCount()).isEqualTo(0);
@@ -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();
@@ -1,6 +1,6 @@
package org.tests.text.csv;
import io.ebean.Ebean;
import io.ebean.DB;
import io.ebean.TransactionalTestCase;
import io.ebean.text.csv.CsvReader;
import org.junit.Test;
@@ -10,7 +10,6 @@ import org.tests.model.basic.ResetBasicData;
import java.io.File;
import java.io.FileReader;
import java.net.URL;
import java.util.Locale;
public class TestCsvReader extends TransactionalTestCase {
@@ -25,7 +24,7 @@ public class TestCsvReader extends TransactionalTestCase {
FileReader reader = new FileReader(f);
CsvReader<Customer> csvReader = Ebean.createCsvReader(Customer.class);
CsvReader<Customer> csvReader = DB.getDefault().createCsvReader(Customer.class);
csvReader.setPersistBatchSize(2);
@@ -33,7 +32,7 @@ public class TestCsvReader extends TransactionalTestCase {
// csvReader.addProperty("id");
csvReader.addProperty("status");
csvReader.addProperty("name");
csvReader.addDateTime("anniversary", "dd-MMM-yyyy", Locale.GERMAN);
csvReader.addDateTime("anniversary", "dd-MMM-yyyy");
csvReader.addProperty("billingAddress.line1");
csvReader.addProperty("billingAddress.city");
csvReader.addProperty("billingAddress.country.code");
@@ -1,7 +1,6 @@
package org.tests.text.csv;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.DB;
import io.ebean.TransactionalTestCase;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.csv.DefaultCsvCallback;
@@ -11,7 +10,6 @@ import org.tests.model.basic.Customer;
import java.io.File;
import java.io.FileReader;
import java.net.URL;
import java.util.Locale;
import static org.assertj.core.api.Assertions.assertThat;
@@ -25,9 +23,7 @@ public class TestCsvReaderWithCallback extends TransactionalTestCase {
FileReader reader = new FileReader(f);
final EbeanServer server = Ebean.getServer(null);
CsvReader<Customer> csvReader = server.createCsvReader(Customer.class);
CsvReader<Customer> csvReader = DB.getDefault().createCsvReader(Customer.class);
csvReader.setPersistBatchSize(2);
csvReader.setLogInfoFrequency(3);
@@ -36,13 +32,13 @@ public class TestCsvReaderWithCallback extends TransactionalTestCase {
// csvReader.addProperty("id");
csvReader.addProperty("status");
csvReader.addProperty("name");
csvReader.addDateTime("anniversary", "dd-MMM-yyyy", Locale.GERMAN);
csvReader.addDateTime("anniversary", "dd-MMM-yyyy");
csvReader.addProperty("billingAddress.line1");
csvReader.addProperty("billingAddress.city");
// processor.addReference("billingAddress.country.code");
csvReader.addProperty("billingAddress.country.code");
int before = Ebean.find(Customer.class).findCount();
int before = DB.find(Customer.class).findCount();
csvReader.process(reader, new DefaultCsvCallback<Customer>() {
@@ -56,9 +52,8 @@ public class TestCsvReaderWithCallback extends TransactionalTestCase {
});
int after = Ebean.find(Customer.class).findCount();
int after = DB.find(Customer.class).findCount();
assertThat(after).isEqualTo(before + 9);
}
}
+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"));
+29 -37
View File
@@ -72,8 +72,8 @@ datasource.db.databaseDriver=org.h2.Driver
datasource.h2.username=sa
datasource.h2.password=
datasource.h2.databaseUrl=jdbc:h2:mem:tests
datasource.h2.databaseDriver=org.h2.Driver
datasource.h2.url=jdbc:h2:mem:tests
#datasource.h2.driver=org.h2.Driver
datasource.h2.minConnections=1
datasource.h2.maxConnections=25
#datasource.h2.heartbeatsql=select 1
@@ -87,61 +87,57 @@ datasource.h2.poolListener=org.tests.basic.MyTestDataSourcePoolListener
datasource.h2multitenant.username=sa
datasource.h2multitenant.password=
datasource.h2multitenant.url=jdbc:h2:mem:h2multitenant
datasource.h2multitenant.driver=org.h2.Driver
datasource.h2autocommit.autoCommit=true
datasource.h2autocommit.username=sa
datasource.h2autocommit.password=
datasource.h2autocommit.databaseUrl=jdbc:h2:mem:autocommittests
datasource.h2autocommit.databaseDriver=org.h2.Driver
datasource.h2autocommit2.autoCommit=true
datasource.h2autocommit2.username=sa
datasource.h2autocommit2.password=
datasource.h2autocommit2.databaseUrl=jdbc:h2:mem:autocommit2tests
datasource.h2autocommit2.databaseDriver=org.h2.Driver
datasource.h2autocommit2.url=jdbc:h2:mem:autocommit2tests
datasource.h2other.username=sa
datasource.h2other.password=
datasource.h2other.databaseUrl=jdbc:h2:mem:h2other
datasource.h2other.databaseDriver=org.h2.Driver
datasource.h2other.url=jdbc:h2:mem:h2other
datasource.h2otherfind.username=sa
datasource.h2otherfind.password=
datasource.h2otherfind.databaseUrl=jdbc:h2:mem:h2otherfind
datasource.h2otherfind.databaseDriver=org.h2.Driver
datasource.h2otherfind.url=jdbc:h2:mem:h2otherfind
datasource.h2ebasicver.username=sa
datasource.h2ebasicver.password=
datasource.h2ebasicver.databaseUrl=jdbc:h2:mem:h2ebasicver
datasource.h2ebasicver.databaseDriver=org.h2.Driver
datasource.h2ebasicver.url=jdbc:h2:mem:h2ebasicver
datasource.h2ebasicver2.username=sa
datasource.h2ebasicver2.password=
datasource.h2ebasicver2.databaseUrl=jdbc:h2:mem:h2ebasicver
datasource.h2ebasicver2.databaseDriver=org.h2.Driver
datasource.h2ebasicver2.url=jdbc:h2:mem:h2ebasicver
datasource.sqlite.username=
datasource.sqlite.password=
datasource.sqlite.databaseUrl=jdbc:sqlite:mydb.db
datasource.sqlite.databaseDriver=org.sqlite.JDBC
datasource.sqlite.url=jdbc:sqlite:mydb.db
datasource.sqlite.driver=org.sqlite.JDBC
datasource.sqlite.isolationlevel=read_uncommitted
datasource.hsqldb.username=sa
datasource.hsqldb.password=
datasource.hsqldb.databaseUrl=jdbc:hsqldb:mem:tests
datasource.hsqldb.databaseDriver=org.hsqldb.jdbcDriver
datasource.hsqldb.url=jdbc:hsqldb:mem:tests
# Set caseSensitiveCollation to false when using
# MySql with case insensitive collation
#ebean.mysql.caseSensitiveCollation=false
datasource.mysql.username=unit
datasource.mysql.password=unit
datasource.mysql.databaseUrl=jdbc:mysql://127.0.0.1:4306/unit
datasource.mysql.url=jdbc:mysql://127.0.0.1:4306/unit
#datasource.mysql.username=test_ebean
#datasource.mysql.password=test
#datasource.mysql.databaseUrl=jdbc:mysql://127.0.0.1:4306/test_ebean
datasource.mysql.databaseDriver=com.mysql.cj.jdbc.Driver
#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
datasource.cockroach.username=root
datasource.cockroach.password=
@@ -150,23 +146,21 @@ datasource.cockroach.driver=org.postgresql.Driver
datasource.oracle.username=test_ebean
datasource.oracle.password=test
datasource.oracle.databaseUrl=jdbc:oracle:thin:@localhost:1521:XE
datasource.oracle.databaseDriver=oracle.jdbc.driver.OracleDriver
datasource.oracle.url=jdbc:oracle:thin:@localhost:1521:XE
#datasource.oracle.driver=oracle.jdbc.driver.OracleDriver
## set this to use timestamp rather than timestamptz
#ebean.postgres.timestamp=timestamp
datasource.pg.username=unit
datasource.pg.password=unit
datasource.pg.databaseUrl=jdbc:postgresql://127.0.0.1:5432/unit
datasource.pg.databaseDriver=org.postgresql.Driver
datasource.pg.url=jdbc:postgresql://127.0.0.1:5432/unit
#datasource.pg.capturestacktrace=true
datasource.pg.maxStackTraceSize=50
datasource.pg9.username=unit
datasource.pg9.password=unit
datasource.pg9.databaseUrl=jdbc:postgresql://127.0.0.1:9432/unit
datasource.pg9.databaseDriver=org.postgresql.Driver
datasource.pg9.url=jdbc:postgresql://127.0.0.1:9432/unit
#ebean.sqlserver.idType=SEQUENCE
@@ -179,29 +173,28 @@ ebean.sqlserver.databasePlatformName=sqlserver17
datasource.sqlserver.username=test_ebean
datasource.sqlserver.password=SqlS3rv#r
datasource.sqlserver.url=jdbc:sqlserver://localhost:1433;databaseName=test_ebean;sendTimeAsDateTime=false
datasource.sqlserver.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
#datasource.sqlserver.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
datasource.nuodb.schema=test_user
datasource.nuodb.username=test_user
datasource.nuodb.password=test
datasource.nuodb.url=jdbc:com.nuodb://localhost/testdb
datasource.nuodb.driver=com.nuodb.jdbc.Driver
#datasource.nuodb.driver=com.nuodb.jdbc.Driver
datasource.db2.username=db2admin
datasource.db2.password=veryverysecret#1234
datasource.db2.databaseUrl=jdbc:db2://127.0.0.1:50000/SAMPLE
datasource.db2.databaseDriver=com.ibm.db2.jcc.DB2Driver
datasource.db2.url=jdbc:db2://127.0.0.1:50000/SAMPLE
#datasource.db2.driver=com.ibm.db2.jcc.DB2Driver
datasource.hana.username=EBEAN_TEST
datasource.hana.password=Eb3an_test
datasource.hana.databaseUrl=jdbc:sap://hxehost:39013/?databaseName=HXE
datasource.hana.databaseDriver=com.sap.db.jdbc.Driver
datasource.hana.url=jdbc:sap://hxehost:39013/?databaseName=HXE
#datasource.hana.driver=com.sap.db.jdbc.Driver
# parameters for migration test
datasource.migrationtest.username=SA
datasource.migrationtest.password=SA
datasource.migrationtest.databaseUrl=jdbc:h2:mem:migration
datasource.migrationtest.databaseDriver=org.h2.Driver
datasource.migrationtest.url=jdbc:h2:mem:migration
ebean.migrationtest.applyPrefix=V
ebean.migrationtest.ddl.generate=false
ebean.migrationtest.ddl.run=false
@@ -213,8 +206,7 @@ ebean.migrationtest.migration.strict=true
# parameters for migration test
datasource.migrationtest-history.username=SA
datasource.migrationtest-history.password=SA
datasource.migrationtest-history.databaseUrl=jdbc:h2:mem:migration
datasource.migrationtest-history.databaseDriver=org.h2.Driver
datasource.migrationtest-history.url=jdbc:h2:mem:migration
ebean.migrationtest-history.applyPrefix=V
ebean.migrationtest-history.ddl.generate=false
ebean.migrationtest-history.ddl.run=false
@@ -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>

Some files were not shown because too many files have changed in this diff Show More