diff --git a/README.md b/README.md index 7f6f03932..022e7794c 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,12 @@ Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Web ## Maven cental links: -[Maven central - avaje-ebeanorm](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.avaje.ebeanorm%22%20AND%20a%3A%22avaje-ebeanorm%22 "maven central ebeanorm") +[Maven central - ebean](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.avaje.ebean%22%20AND%20a%3A%22ebean%22 "maven central ebean") -[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cavaje-ebeanorm "maven central ebeanorm") +[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects") ## Current versions -* [![Maven Central : avaje-ebeanorm](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm) - avaje-ebeanorm -* [![Maven Central : avaje-ebeanorm-agent](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-agent) - avaje-ebeanorm-agent -* [![Maven Central : avaje-ebeanorm-agent](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-mavenenhancer/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-mavenenhancer) - avaje-ebeanorm-mavenenhancer +* [![Maven Central : ebean](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean) - ebean +* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-agent) - ebean-agent +* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-maven-plugin) - ebean-maven-plugin diff --git a/pom.xml b/pom.xml index ec2540282..dbd0ea83d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,12 +7,12 @@ 1.1 - org.avaje.ebeanorm - avaje-ebeanorm - 7.20.2-SNAPSHOT + org.avaje.ebean + ebean + 8.2.2-SNAPSHOT jar - avaje-ebeanorm + ebean http://ebean-orm.github.io/ @@ -36,7 +36,7 @@ - scm:git:https://github.com/ebean-orm/avaje-ebeanorm.git + scm:git:https://github.com/ebean-orm/ebean.git HEAD @@ -172,9 +172,9 @@ - org.avaje.ebeanorm - avaje-ebeanorm-agent - 4.11.1 + org.avaje.ebean + ebean-agent + 8.1.1 test @@ -247,16 +247,15 @@ - org.avaje.ebeanorm - avaje-ebeanorm-mavenenhancer - 4.11.1 + org.avaje.ebean + ebean-maven-plugin + 8.1.1 test process-test-classes target/test-classes - debug=1 @@ -304,7 +303,7 @@ maven-javadoc-plugin 2.9.1 - Ebean 7 + Ebean 8 src/main/java/com/avaje/ebean/overview.html 1.8 org.avaje.doclet.PygmentsDoclet diff --git a/src/main/java/com/avaje/ebean/BeanState.java b/src/main/java/com/avaje/ebean/BeanState.java index 68cd12ab5..a6708a783 100644 --- a/src/main/java/com/avaje/ebean/BeanState.java +++ b/src/main/java/com/avaje/ebean/BeanState.java @@ -120,4 +120,9 @@ public interface BeanState { * for a fully loaded entity bean. */ void setLoaded(); + + /** + * Reset the bean putting it into NEW state such that a save() results in an insert. + */ + void resetForInsert(); } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/Ebean.java b/src/main/java/com/avaje/ebean/Ebean.java index 924c1c221..ffd330b1a 100644 --- a/src/main/java/com/avaje/ebean/Ebean.java +++ b/src/main/java/com/avaje/ebean/Ebean.java @@ -499,6 +499,13 @@ public final class Ebean { serverMgr.getDefaultServer().endTransaction(); } + /** + * Mark the current transaction as rollback only. + */ + public static void setRollbackOnly() { + serverMgr.getDefaultServer().currentTransaction().setRollbackOnly(); + } + /** * Return a map of the differences between two objects of the same type. *

diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java index 3392334a6..3490bd203 100644 --- a/src/main/java/com/avaje/ebean/EbeanServer.java +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -739,20 +739,46 @@ public interface EbeanServer { T getReference(Class beanType, Object id); /** - * Return the number of 'top level' or 'root' entities this query should - * return. + * Return the number of 'top level' or 'root' entities this query should return. * - * @see Query#findRowCount() - * @see com.avaje.ebean.Query#findFutureRowCount() + * @see Query#findCount() + * @see Query#findFutureCount() + */ + int findCount(Query query, Transaction transaction); + + /** + * Deprecated in favor of findCount() + * + * Return the number of 'top level' or 'root' entities this query should return. + * @deprecated */ int findRowCount(Query query, Transaction transaction); /** * Return the Id values of the query as a List. * - * @see com.avaje.ebean.Query#findIds() + * @see Query#findIds() */ - List findIds(Query query, Transaction transaction); + List findIds(Query query, Transaction transaction); + + /** + * Return a QueryIterator for the query. + *

+ * Generally using {@link #findEach(Query, QueryEachConsumer, Transaction)} or + * {@link #findEachWhile(Query, QueryEachWhileConsumer, 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). + *

+ *

+ * 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. + *

+ * + * @see Query#findIterate() + * @see Query#findEach(QueryEachConsumer) + * @see Query#findEachWhile(QueryEachWhileConsumer) + */ + QueryIterator findIterate(Query query, Transaction transaction); /** * Execute the query visiting the each bean one at a time. @@ -868,7 +894,15 @@ public interface EbeanServer { * @return a Future object for the row count query * @see com.avaje.ebean.Query#findFutureRowCount() */ - FutureRowCount findFutureRowCount(Query query, Transaction transaction); + FutureRowCount findFutureCount(Query query, Transaction transaction); + + /** + * Deprecated in favor of findFutureCount(). + * + * Execute find row count query in a background thread. + * @deprecated + */ + FutureRowCount findFutureRowCount(Query query, Transaction transaction); /** * Execute find Id's query in a background thread. @@ -971,7 +1005,41 @@ public interface EbeanServer { * @return the map of fetched beans. * @see Query#findMap() */ - Map findMap(Query query, Transaction transaction); + Map findMap(Query query, Transaction transaction); + + /** + * Execute the query returning a list of values for a single property. + * + *

Example 1:

+ *
{@code
+   *
+   *  List names =
+   *    Ebean.find(Customer.class)
+   *      .select("name")
+   *      .orderBy().asc("name")
+   *      .findSingleAttributeList();
+   *
+   * }
+ * + *

Example 2:

+ *
{@code
+   *
+   *  List names =
+   *    Ebean.find(Customer.class)
+   *      .setDistinct(true)
+   *      .select("name")
+   *      .where().eq("status", Customer.Status.NEW)
+   *      .orderBy().asc("name")
+   *      .setMaxRows(100)
+   *      .findSingleAttributeList();
+   *
+   * }
+ * + * @return the list of values for the selected property + * + * @see Query#findSingleAttributeList() + */ +
List findSingleAttributeList(Query query, Transaction transaction); /** * Execute the query returning at most one entity bean or null (if no matching diff --git a/src/main/java/com/avaje/ebean/ExpressionList.java b/src/main/java/com/avaje/ebean/ExpressionList.java index 34f4c441a..4f8d38d29 100644 --- a/src/main/java/com/avaje/ebean/ExpressionList.java +++ b/src/main/java/com/avaje/ebean/ExpressionList.java @@ -31,7 +31,7 @@ import java.util.Set; * more methods than you would initially expect (the ones duplicated from * Query). *

- * + * * @see Query#where() */ public interface ExpressionList { @@ -83,14 +83,14 @@ public interface ExpressionList { /** * Add an orderBy clause to the query. - * + * * @see Query#orderBy(String) */ Query orderBy(String orderBy); /** * Add an orderBy clause to the query. - * + * * @see Query#orderBy(String) */ Query setOrderBy(String orderBy); @@ -116,12 +116,6 @@ public interface ExpressionList { */ Query asDraft(); - /** - * Deprecated in favour of setIncludeSoftDeletes(). - */ - @Deprecated - Query includeSoftDeletes(); - /** * Execute the query including soft deleted rows. */ @@ -147,6 +141,13 @@ public interface ExpressionList { */ int update(); + /** + * Execute the query iterating over the results. + * + * @see Query#findIterate() + */ + QueryIterator findIterate(); + /** * Execute the query process the beans one at a time. * @@ -164,17 +165,17 @@ public interface ExpressionList { /** * Execute the query returning a list. - * + * * @see Query#findList() */ List findList(); /** * Execute the query returning the list of Id's. - * + * * @see Query#findIds() */ - List findIds(); + List findIds(); /** * Return the count of entities this query should return. @@ -182,26 +183,60 @@ public interface ExpressionList { * This is the number of 'top level' or 'root level' entities. *

*/ + int findCount(); + + /** + * Deprecated in favor of findCount(). + * + * @deprecated + */ int findRowCount(); /** * Execute the query returning a set. - * + * * @see Query#findSet() */ Set findSet(); /** * Execute the query returning a map. - * + * * @see Query#findMap() */ - Map findMap(); + Map findMap(); /** - * Return a typed map specifying the key property and type. + * Execute the query returning a list of values for a single property. + * + *

Example 1:

+ *
{@code
+   *
+   *  List names =
+   *    Ebean.find(Customer.class)
+   *      .select("name")
+   *      .orderBy().asc("name")
+   *      .findSingleAttributeList();
+   *
+   * }
+ * + *

Example 2:

+ *
{@code
+   *
+   *  List names =
+   *    Ebean.find(Customer.class)
+   *      .setDistinct(true)
+   *      .select("name")
+   *      .where().eq("status", Customer.Status.NEW)
+   *      .orderBy().asc("name")
+   *      .setMaxRows(100)
+   *      .findSingleAttributeList();
+   *
+   * }
+ * + * @return the list of values for the selected property */ - Map findMap(String keyProperty, Class keyType); +
List findSingleAttributeList(); /** * Execute the query returning a single bean or null (if no matching @@ -225,9 +260,16 @@ public interface ExpressionList { * execution status (isDone etc) and get the value (with or without a * timeout). *

- * + * * @return a Future object for the row count query */ + FutureRowCount findFutureCount(); + + /** + * Deprecated in favor of findFutureCount(). + * + * @deprecated + */ FutureRowCount findFutureRowCount(); /** @@ -237,7 +279,7 @@ public interface ExpressionList { * execution status (isDone etc) and get the value (with or without a * timeout). *

- * + * * @return a Future object for the list of Id's */ FutureIds findFutureIds(); @@ -249,7 +291,7 @@ public interface ExpressionList { * execution status (isDone etc) and get the value (with or without a * timeout). *

- * + * * @return a Future object for the list result of the query */ FutureList findFutureList(); @@ -312,7 +354,7 @@ public interface ExpressionList { /** * Specify specific properties to fetch on the main/root bean (aka partial * object). - * + * * @see Query#select(String) */ Query select(String properties); @@ -337,28 +379,28 @@ public interface ExpressionList { /** * Set the first row to fetch. - * + * * @see Query#setFirstRow(int) */ Query setFirstRow(int firstRow); /** * Set the maximum number of rows to fetch. - * + * * @see Query#setMaxRows(int) */ Query setMaxRows(int maxRows); /** * Set the name of the property which values become the key of a map. - * + * * @see Query#setMapKey(String) */ Query setMapKey(String mapKey); /** * Set to true to use the query for executing this query. - * + * * @see Query#setUseCache(boolean) */ Query setUseCache(boolean useCache); @@ -619,7 +661,7 @@ public interface ExpressionList { * To get control over the options you can create an ExampleExpression and set * those options such as case insensitive etc. *

- * + * *
{@code
    *
    * // create an example bean and set the properties
@@ -627,26 +669,26 @@ public interface ExpressionList {
    * Customer example = new Customer();
    * example.setName("Rob%");
    * example.setNotes("%something%");
-   * 
+   *
    * List<Customer> list = Ebean.find(Customer.class).where()
    *     // pass the bean into the where() clause
    *     .exampleLike(example)
    *     // you can add other expressions to the same query
    *     .gt("id", 2).findList();
-   * 
+   *
    * }
- * + * * Similarly you can create an ExampleExpression - * + * *
{@code
    *
    * Customer example = new Customer();
    * example.setName("Rob%");
    * example.setNotes("%something%");
-   * 
+   *
    * // create a ExampleExpression with more control
    * ExampleExpression qbe = new ExampleExpression(example, true, LikeType.EQUAL_TO).includeZeros();
-   * 
+   *
    * List list = Ebean.find(Customer.class).where().add(qbe).findList();
    *
    * }
@@ -748,7 +790,7 @@ public interface ExpressionList { * Exists expression */ ExpressionList exists(Query subQuery); - + /** * Not exists expression */ @@ -775,7 +817,7 @@ public interface ExpressionList { * Expression where all the property names in the map are equal to the * corresponding value. *

- * + * * @param propertyMap * a map keyed by property names. */ diff --git a/src/main/java/com/avaje/ebean/FetchConfig.java b/src/main/java/com/avaje/ebean/FetchConfig.java index bf2904531..fc4f3de11 100644 --- a/src/main/java/com/avaje/ebean/FetchConfig.java +++ b/src/main/java/com/avaje/ebean/FetchConfig.java @@ -29,8 +29,7 @@ import java.io.Serializable; * // Find Orders join details using a single SQL query * } *

- * Example: Using a "query join" instead of a "fetch join" we instead use 2 SQL - * queries + * Example: Using a "query join" instead of a "fetch join" we instead use 2 SQL queries *

* *
{@code
@@ -117,7 +116,7 @@ import java.io.Serializable;
  * 
{@code
  * List list = Ebean.find(Order.class)
  *   .fetch("customer","name", new FetchConfig().lazy(5))
- *   .fetch("customer.contacts","contactName, phone, email")
+ *   .fetch("customer.contacts","contactName, phone, email")
  *   .fetch("customer.shippingAddress")
  *   .where().eq("status",Order.Status.NEW)
  *   .findList();
@@ -125,12 +124,7 @@ import java.io.Serializable;
  * // query 1) find order where status = Order.Status.NEW
  * //  
  * // .. if lazy loading of customers is invoked 
- * // .. use a batch size of 5 to load the customers 
- *  
- *       find  customer (name) 
- *       fetch customer.contacts (contactName, phone, email) 
- *       fetch customer.shippingAddress (*) 
- *       where id in (?,?,?,?,?)
+ * // .. use a batch size of 5 to load the customers
  * 
  * }
* diff --git a/src/main/java/com/avaje/ebean/FutureIds.java b/src/main/java/com/avaje/ebean/FutureIds.java index ab8d80a5d..fb280b349 100644 --- a/src/main/java/com/avaje/ebean/FutureIds.java +++ b/src/main/java/com/avaje/ebean/FutureIds.java @@ -9,8 +9,6 @@ import java.util.concurrent.Future; * It extends the java.util.concurrent.Future with the ability to get the Id's * while the query is still executing in the background. *

- * - * @author rbygrave */ public interface FutureIds extends Future> { @@ -19,16 +17,4 @@ public interface FutureIds extends Future> { */ Query getQuery(); - /** - * Return the list of Id's which could be partially populated. - *

- * That is the query getting the id's could still be running and adding id's - * to this list. - *

- *

- * To get the list of Id's ensuring the query has finished use the - * {@link Future#get()} method instead of this one. - *

- */ - List getPartialIds(); } diff --git a/src/main/java/com/avaje/ebean/Model.java b/src/main/java/com/avaje/ebean/Model.java index ab87eb41d..f03e52706 100644 --- a/src/main/java/com/avaje/ebean/Model.java +++ b/src/main/java/com/avaje/ebean/Model.java @@ -495,7 +495,7 @@ public abstract class Model { /** * Return typically a different EbeanServer to the default. *

- * This is equivilent to {@link Ebean#getServer(String)} + * This is equivalent to {@link Ebean#getServer(String)} * * @param server * The name of the EbeanServer. If this is null then the default EbeanServer is @@ -598,7 +598,7 @@ public abstract class Model { *

* Equivalent to {@link Query#findIds()} */ - public List findIds() { + public List findIds() { return query().findIds(); } @@ -660,36 +660,43 @@ public abstract class Model { *

* Equivalent to {@link Query#findMap()} */ - public Map findMap() { + public Map findMap() { return query().findMap(); } - /** - * Executes the query and returns the results as a map of the objects specifying the map key - * property. - *

- * Equivalent to {@link Query#findMap(String, Class)} - */ - public Map findMap(String keyProperty, Class keyType) { - return query().findMap(keyProperty, keyType); - } - /** * Executes a find row count query in a background thread. *

- * Equivalent to {@link Query#findFutureRowCount()} + * Equivalent to {@link Query#findFutureCount()} + */ + public FutureRowCount findFutureCount() { + return query().findFutureCount(); + } + + /** + * Deprecated in favor of findFutureCount(). + *

+ * Equivalent to {@link Query#findFutureCount()} */ public FutureRowCount findFutureRowCount() { - return query().findFutureRowCount(); + return query().findFutureCount(); } /** * Returns the total number of entities for this type. * *

- * Equivalent to {@link Query#findRowCount()} + * Equivalent to {@link Query#findCount()} + */ + public int findCount() { + return query().findCount(); + } + + /** + * Deprecated in favor of findCount(). + * @deprecated */ public int findRowCount() { - return query().findRowCount(); + return query().findCount(); } /** diff --git a/src/main/java/com/avaje/ebean/PagedList.java b/src/main/java/com/avaje/ebean/PagedList.java index 22347967d..9efb6a830 100644 --- a/src/main/java/com/avaje/ebean/PagedList.java +++ b/src/main/java/com/avaje/ebean/PagedList.java @@ -110,6 +110,13 @@ public interface PagedList { * wrapped in the unchecked PersistenceException (which might be preferrable). *

*/ + void loadCount(); + + /** + * Deprecated in favor of loadCount(). + * + * @deprecated + */ void loadRowCount(); /** @@ -138,6 +145,13 @@ public interface PagedList { * * } */ + Future getFutureCount(); + + /** + * Deprecated in favor of getFutureCount(). + * + * @deprecated + */ Future getFutureRowCount(); /** @@ -170,6 +184,13 @@ public interface PagedList { * * } */ + int getTotalCount(); + + /** + * Deprecated in favor of getTotalCount(). + * + * @deprecated + */ int getTotalRowCount(); /** diff --git a/src/main/java/com/avaje/ebean/Query.java b/src/main/java/com/avaje/ebean/Query.java index 42bbd424c..47b4661cc 100644 --- a/src/main/java/com/avaje/ebean/Query.java +++ b/src/main/java/com/avaje/ebean/Query.java @@ -13,10 +13,10 @@ import java.util.Set; *

* Example: Create the query using the API. *

- * + *

*

{@code
  *
- * List orderList = 
+ * List orderList =
  *   ebeanServer.find(Order.class)
  *     .fetch("customer")
  *     .fetch("details")
@@ -26,46 +26,40 @@ import java.util.Set;
  *     .orderBy("customer.id, id desc")
  *     .setMaxRows(50)
  *     .findList();
- *   
+ *
  * ...
  * }
- * *

* Example: The same query using the query language *

- * *
{@code
  *
- * String oql = 
- *   	"  find  order "
+ * String oql =
  *   	+" fetch customer "
  *   	+" fetch details "
  *   	+" where customer.name like :custName and orderDate > :minOrderDate "
  *   	+" order by customer.id, id desc "
  *   	+" limit 50 ";
- *   
+ *
  * Query query = ebeanServer.createQuery(Order.class, oql);
  * query.setParameter("custName", "Rob%");
  * query.setParameter("minOrderDate", lastWeek);
- *   
+ *
  * List orderList = query.findList();
  * ...
  * }
- * *

* Example: Using a named query called "with.cust.and.details" *

- * *
{@code
  *
  * Query query = ebeanServer.createNamedQuery(Order.class,"with.cust.and.details");
  * query.setParameter("custName", "Rob%");
  * query.setParameter("minOrderDate", lastWeek);
- *   
+ *
  * List orderList = query.findList();
  * ...
  * }
- * *

AutoTune

*

* Ebean has built in support for "AutoTune". This is a mechanism where a query @@ -82,7 +76,6 @@ import java.util.Set; * to a remote client or where there is some requirement for "Read Consistency" * guarantees. *

- * *

Query Language

*

* Partial Objects @@ -101,36 +94,25 @@ import java.util.Set; * concurrency checking will occur but only include the fetched properties. * Refer to "ALL Properties/Columns" mode of Optimistic Concurrency checking. *

- * *
{@code
- * [ find  {bean type} [ ( * | {fetch properties} ) ] ]
- * [ fetch {associated bean} [ ( * | {fetch properties} ) ] ]
+ * [ select [ ( * | {fetch properties} ) ] ]
+ * [ fetch {path} [ ( * | {fetch properties} ) ] ]
  * [ where {predicates} ]
  * [ order by {order by properties} ]
  * [ limit {max rows} [ offset {first row} ] ]
  * }
- * *

- * FIND {bean type} [ ( * | {fetch properties} ) ] + * SELECT [ ( * | {fetch properties} ) ] *

*

- * With the find you specify the type of beans to fetch. You can optionally - * specify a list of properties to fetch. If you do not specify a list of - * properties ALL the properties for those beans are fetched. + * With the select you can specify a list of properties to fetch. *

*

- * In object graph terms the find clause specifies the type of bean at - * the root level and the fetch clauses specify the paths of the object - * graph to populate. - *

- *

- * FETCH {associated property} [ ( * | {fetch - * properties} ) ] + * FETCH {path} [ ( * | {fetch properties} ) ] *

*

* With the fetch you specify the associated property to fetch and populate. The - * associated property is a OneToOnem, ManyToOne, OneToMany or ManyToMany - * property. When the query is executed Ebean will fetch the associated data. + * path is a OneToOne, ManyToOne, OneToMany or ManyToMany property. *

*

* For fetch of a path we can optionally specify a list of properties to fetch. @@ -161,52 +143,34 @@ import java.util.Set; *

*

Examples of Ebean's Query Language

*

- * Find orders fetching all its properties - *

- * - *
{@code
- * find order
- * }
- * - *

- * Find orders fetching all its properties - *

- * - *
{@code
- * find order (*)
- * }
- * - *

* Find orders fetching its id, shipDate and status properties. Note that the id * property is always fetched even if it is not included in the list of fetch * properties. *

- * *
{@code
- * find order (shipDate, status)
+ *
+ * select (shipDate, status)
+ *
  * }
- * *

* Find orders with a named bind variable (that will need to be bound via * {@link Query#setParameter(String, Object)}). *

- * *
{@code
- * find order
+ *
  * where customer.name like :custLike
+ *
  * }
- * *

* Find orders and also fetch the customer with a named bind parameter. This * will fetch and populate both the order and customer objects. *

- * *
{@code
- * find  order
+ *
  * fetch customer
  * where customer.id = :custId
+ *
  * }
- * *

* Find orders and also fetch the customer, customer shippingAddress, order * details and related product. Note that customer and product objects will be @@ -215,63 +179,16 @@ import java.util.Set; * objects (associated with each order detail) will have their id, sku and name * populated. *

- * *
{@code
- * find  order
+ *
  * fetch customer (name)
  * fetch customer.shippingAddress
  * fetch details
  * fetch details.product (sku, name)
+ *
  * }
- * - *

Early parsing of the Query

- *

- * When you get a Query object from a named query, the query statement has - * already been parsed. You can then add to that query (add fetch paths, add to - * the where clause) or override some of its settings (override the order by - * clause, first rows, max rows). - *

- *

- * The thought is that you can use named queries as a 'starting point' and then - * modify the query to suit specific needs. - *

- *

Building the Where clause

- *

- * You can add to the where clause using Expression objects or a simple String. - * Note that the ExpressionList has methods to add most of the common - * expressions that you will need. - *

    - *
  • where(String addToWhereClause)
  • - *
  • where().add(Expression expression)
  • - *
  • where().eq(propertyName, value).like(propertyName , value)...
  • - *
- *

- *

- * The full WHERE clause is constructed by appending together - *

  • original query where clause (Named query or query.setQuery(String oql))
  • - *
  • clauses added via query.where(String addToWhereClause)
  • - *
  • clauses added by Expression objects
  • - *

    - *

    - * The above is the order that these are clauses are appended to give the full - * WHERE clause. - *

    - *

    Design Goal

    - *

    - * This query language is NOT designed to be a replacement for SQL. It is - * designed to be a simple way to describe the "Object Graph" you want Ebean to - * build for you. Each find/fetch represents a node in that "Object Graph" which - * makes it easy to define for each node which properties you want to fetch. - *

    - *

    - * Once you hit the limits of this language such as wanting aggregate functions - * (sum, average, min etc) or recursive queries etc you use SQL. Ebean's goal is - * to make it as easy as possible to use your own SQL to populate entity beans. - * Refer to {@link RawSql} . - *

    - * - * @param - * the type of Entity bean this query will fetch. + * + * @param the type of Entity bean this query will fetch. */ public interface Query { @@ -289,7 +206,7 @@ public interface Query { * Perform an 'As of' query using history tables to return the object graph * as of a time in the past. *

    - * To perform this query the DB must have underlying history tables. + * To perform this query the DB must have underlying history tables. *

    * * @param asOf the date time in the past at which you want to view the data @@ -325,7 +242,7 @@ public interface Query { * Specify the PersistenceContextScope to use for this query. *

    * When this is not set the 'default' configured on {@link com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)} - * is used - this value defaults to {@link com.avaje.ebean.PersistenceContextScope#TRANSACTION}. + * is used - this value defaults to {@link PersistenceContextScope#TRANSACTION}. *

    * Note that the same persistence Context is used for subsequent lazy loading and query join queries. *

    @@ -392,14 +309,15 @@ public interface Query { Query setDisableReadAuditing(); /** - * Explicitly set a comma delimited list of the properties to fetch on the - * 'main' root level entity bean (aka partial object). Note that '*' means all - * properties. + * Specify the properties to fetch on the root level entity bean in comma delimited format. *

    - * You use {@link #fetch(String, String)} to specify specific properties to fetch + * The Id property is automatically included in the properties to fetch unless setDistinct(true) + * is set on the query. + *

    + *

    + * Use {@link #fetch(String, String)} to specify specific properties to fetch * on other non-root level paths of the object graph. *

    - * *
    {@code
        *
        * List customers =
    @@ -412,42 +330,33 @@ public interface Query {
        *
        * }
    * - * @param fetchProperties - * the properties to fetch for this bean (* = all properties). + * @param fetchProperties the properties to fetch for this bean (* = all properties). */ Query select(String fetchProperties); /** - * Specify a path to fetch with its specific properties to include - * (aka partial object). + * Specify a path to fetch eagerly including specific properties. *

    - * When you specify a join this means that property (associated bean(s)) will - * be fetched and populated. If you specify "*" then all the properties of the - * associated bean will be fetched and populated. You can specify a comma - * delimited list of the properties of that associated bean which means that - * only those properties are fetched and populated resulting in a - * "Partial Object" - a bean that only has some of its properties populated. + * Ebean will endeavour to fetch this path using a SQL join. If Ebean determines that it can + * not use a SQL join (due to maxRows or because it would result in a cartesian product) Ebean + * will automatically convert this fetch query into a "query join" - i.e. use fetchQuery(). *

    - * *
    {@code
        *
        * // query orders...
        * List orders =
    -   *     ebeanserver.find(Order.class)
    +   *     ebeanServer.find(Order.class)
        *       // fetch the customer...
        *       // ... getting the customers name and phone number
        *       .fetch("customer", "name, phoneNumber")
    -   * 
    +   *
        *       // ... also fetch the customers billing address (* = all properties)
        *       .fetch("customer.billingAddress", "*")
        *       .findList();
        * }
    - * *

    - * If columns is null or "*" then all columns/properties for that path are - * fetched. + * If columns is null or "*" then all columns/properties for that path are fetched. *

    - * *
    {@code
        *
        * // fetch customers (their id, name and status)
    @@ -458,19 +367,66 @@ public interface Query {
        *     .findList();
        *
        * }
    - * - * @param path - * the path of an associated (1-1,1-M,M-1,M-M) bean. - * @param fetchProperties - * properties of the associated bean that you want to include in the - * fetch (* means all properties, null also means all properties). + * + * @param path the property path we wish to fetch eagerly. + * @param fetchProperties properties of the associated bean that you want to include in the + * fetch (* means all properties, null also means all properties). */ Query fetch(String path, String fetchProperties); + /** + * Fetch the path and properties using a "query join" (separate SQL query). + *

    + * This is the same as: + *

    + *
    {@code
    +   *
    +   *  fetch(path, fetchProperties, new FetchConfig().query())
    +   *
    +   * }
    + *

    + * This would be used instead of a fetch() when we use a separate SQL query to fetch this + * part of the object graph rather than a SQL join. + *

    + *

    + * We might typically get a performance benefit when the path to fetch is a OneToMany + * or ManyToMany, the 'width' of the 'root bean' is wide and the cardinality of the many + * is high. + *

    + * + * @param path the property path we wish to fetch eagerly. + * @param fetchProperties properties of the associated bean that you want to include in the + * fetch (* means all properties, null also means all properties). + */ + Query fetchQuery(String path, String fetchProperties); + + /** + * Fetch the path and properties lazily (via batch lazy loading). + *

    + * This is the same as: + *

    + *
    {@code
    +   *
    +   *  fetch(path, fetchProperties, new FetchConfig().lazy())
    +   *
    +   * }
    + *

    + * The reason for using fetchLazy() is to either: + *

    + *
      + *
    • Control/tune what is fetched as part of lazy loading
    • + *
    • Make use of the L2 cache, build this part of the graph from L2 cache
    • + *
    + * + * @param path the property path we wish to fetch lazily. + * @param fetchProperties properties of the associated bean that you want to include in the + * fetch (* means all properties, null also means all properties). + */ + Query fetchLazy(String path, String fetchProperties); + /** * Additionally specify a FetchConfig to use a separate query or lazy loading * to load this path. - * *
    {@code
        *
        * // fetch customers (their id, name and status)
    @@ -481,13 +437,17 @@ public interface Query {
        *     .findList();
        *
        * }
    + * + * @param path the property path we wish to fetch eagerly. */ - Query fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig); + Query fetch(String path, String fetchProperties, FetchConfig fetchConfig); /** - * Specify a path to load including all its properties. + * Specify a path to fetch eagerly including all its properties. *

    - * The same as {@link #fetch(String, String)} with the fetchProperties as "*". + * Ebean will endeavour to fetch this path using a SQL join. If Ebean determines that it can + * not use a SQL join (due to maxRows or because it would result in a cartesian product) Ebean + * will automatically convert this fetch query into a "query join" - i.e. use fetchQuery(). *

    *
    {@code
        *
    @@ -500,16 +460,59 @@ public interface Query {
        *
        * }
    * - * @param path - * the property of an associated (1-1,1-M,M-1,M-M) bean. + * @param path the property path we wish to fetch eagerly. */ Query fetch(String path); + /** + * Fetch the path eagerly using a "query join" (separate SQL query). + *

    + * This is the same as: + *

    + *
    {@code
    +   *
    +   *  fetch(path, new FetchConfig().query())
    +   *
    +   * }
    + *

    + * This would be used instead of a fetch() when we use a separate SQL query to fetch this + * part of the object graph rather than a SQL join. + *

    + *

    + * We might typically get a performance benefit when the path to fetch is a OneToMany + * or ManyToMany, the 'width' of the 'root bean' is wide and the cardinality of the many + * is high. + *

    + * + * @param path the property path we wish to fetch eagerly + */ + Query fetchQuery(String path); + + /** + * Fetch the path lazily (via batch lazy loading). + *

    + * This is the same as: + *

    + *
    {@code
    +   *
    +   *  fetch(path, new FetchConfig().lazy())
    +   *
    +   * }
    + *

    + * The reason for using fetchLazy() is to either: + *

    + *
      + *
    • Control/tune what is fetched as part of lazy loading
    • + *
    • Make use of the L2 cache, build this part of the graph from L2 cache
    • + *
    + * + * @param path the property path we wish to fetch lazily. + */ + Query fetchLazy(String path); + /** * Additionally specify a JoinConfig to specify a "query join" and or define * the lazy loading query. - * - * *
    {@code
        *
        * // fetch customers (their id, name and status)
    @@ -536,10 +539,50 @@ public interface Query {
        * 

    * This query will execute against the EbeanServer that was used to create it. *

    - * + * * @see EbeanServer#findIds(Query, Transaction) */ - List findIds(); + List findIds(); + + /** + * Execute the query iterating over the results. + *

    + * Note that findIterate (and findEach and findEachWhile) uses a "per graph" + * persistence context scope and adjusts jdbc fetch buffer size for large + * queries. As such it is better to use findList for small queries. + *

    + *

    + * Remember that with {@link QueryIterator} you must call {@link QueryIterator#close()} + * when you have finished iterating the results (typically in a finally block). + *

    + *

    + * findEach() and findEachWhile() are preferred to findIterate() as they ensure + * the jdbc statement and resultSet are closed at the end of the iteration. + *

    + *

    + * This query will execute against the EbeanServer that was used to create it. + *

    + *
    {@code
    +   *
    +   *  Query query =
    +   *    ebeanServer.find(Customer.class)
    +   *     .where().eq("status", Status.NEW)
    +   *     .order().asc("id");
    +   *
    +   *  QueryIterator it = query.findIterate();
    +   *  try {
    +   *    while (it.hasNext()) {
    +   *      Customer customer = it.next();
    +   *      // do something with customer ...
    +   *    }
    +   *  } finally {
    +   *    // close the underlying resources
    +   *    it.close();
    +   *  }
    +   *
    +   * }
    + */ + QueryIterator findIterate(); /** * Execute the query processing the beans one at a time. @@ -549,6 +592,11 @@ public interface Query { * (unlike #findList #findSet etc) *

    *

    + * Note that findEach (and findEachWhile and findIterate) uses a "per graph" + * persistence context scope and adjusts jdbc fetch buffer size for large + * queries. As such it is better to use findList for small queries. + *

    + *

    * Note that internally Ebean can inform the JDBC driver that it is expecting larger * resultSet and specifically for MySQL this hint is required to stop it's JDBC driver * from buffering the entire resultSet. As such, for smaller resultSets findList() is @@ -564,7 +612,6 @@ public interface Query { * iterator uses the QueryEachConsumer (SAM) interface which is better suited to use * with Java8 closures. *

    - * *
    {@code
        *
        *  ebeanServer.find(Customer.class)
    @@ -578,8 +625,7 @@ public interface Query {
        *
        * }
    * - * @param consumer - * the consumer used to process the queried beans. + * @param consumer the consumer used to process the queried beans. */ void findEach(QueryEachConsumer consumer); @@ -587,12 +633,15 @@ public interface Query { * Execute the query using callbacks to a visitor to process the resulting * beans one at a time. *

    + * Note that findEachWhile (and findEach and findIterate) uses a "per graph" + * persistence context scope and adjusts jdbc fetch buffer size for large + * queries. As such it is better to use findList for small queries. + *

    + *

    * This method is functionally equivalent to findIterate() but instead of using an * iterator uses the QueryEachWhileConsumer (SAM) interface which is better suited to use * with Java8 closures. *

    - - * *
    {@code
        *
        *  ebeanServer.find(Customer.class)
    @@ -611,8 +660,7 @@ public interface Query {
        *
        * }
    * - * @param consumer - * the consumer used to process the queried beans. + * @param consumer the consumer used to process the queried beans. */ void findEachWhile(QueryEachWhileConsumer consumer); @@ -621,7 +669,6 @@ public interface Query { *

    * This query will execute against the EbeanServer that was used to create it. *

    - * *
    {@code
        *
        * List customers =
    @@ -640,7 +687,6 @@ public interface Query {
        * 

    * This query will execute against the EbeanServer that was used to create it. *

    - * *
    {@code
        *
        * Set customers =
    @@ -663,24 +709,50 @@ public interface Query {
        * You can use setMapKey() so specify the property values to be used as keys
        * on the map. If one is not specified then the id property is used.
        * 

    - * *
    {@code
        *
    -   * Map map =
    +   * Map map =
        *   ebeanServer.find(Product.class)
        *     .setMapKey("sku")
        *     .findMap();
        *
        * }
    - * + * * @see EbeanServer#findMap(Query, Transaction) */ - Map findMap(); + Map findMap(); /** - * Return a typed map specifying the key property and type. + * Execute the query returning a list of values for a single property. + * + *

    Example 1:

    + *
    {@code
    +   *
    +   *  List names =
    +   *    Ebean.find(Customer.class)
    +   *      .select("name")
    +   *      .orderBy().asc("name")
    +   *      .findSingleAttributeList();
    +   *
    +   * }
    + * + *

    Example 2:

    + *
    {@code
    +   *
    +   *  List names =
    +   *    Ebean.find(Customer.class)
    +   *      .setDistinct(true)
    +   *      .select("name")
    +   *      .where().eq("status", Customer.Status.NEW)
    +   *      .orderBy().asc("name")
    +   *      .setMaxRows(100)
    +   *      .findSingleAttributeList();
    +   *
    +   * }
    + * + * @return the list of values for the selected property */ - Map findMap(String keyProperty, Class keyType); +
    List findSingleAttributeList(); /** * Execute the query returning either a single bean or null (if no matching @@ -693,7 +765,6 @@ public interface Query { * This is useful when your predicates dictate that your query should only * return 0 or 1 results. *

    - * *
    {@code
        *
        * // assuming the sku of products is unique...
    @@ -703,16 +774,14 @@ public interface Query {
        *         .findUnique();
        * ...
        * }
    - * *

    * It is also useful with finding objects by their id when you want to specify * further join information. *

    - * *
    {@code
        *
        * // Fetch order 1 and additionally fetch join its order details...
    -   * Order order = 
    +   * Order order =
        *     ebeanServer.find(Order.class)
        *       .setId(1)
        *       .fetch("details")
    @@ -731,13 +800,13 @@ public interface Query {
       /**
        * Return versions of a @History entity bean.
        * 

    - * Note that this query will work against view based history implementations - * but not sql2011 standards based implementations that require a start and - * end timestamp to be specified. + * Note that this query will work against view based history implementations + * but not sql2011 standards based implementations that require a start and + * end timestamp to be specified. *

    *

    - * Generally this query is expected to be a find by id or unique predicates query. - * It will execute the query against the history returning the versions of the bean. + * Generally this query is expected to be a find by id or unique predicates query. + * It will execute the query against the history returning the versions of the bean. *

    */ List> findVersions(); @@ -745,8 +814,8 @@ public interface Query { /** * Return versions of a @History entity bean between the 2 timestamps. *

    - * Generally this query is expected to be a find by id or unique predicates query. - * It will execute the query against the history returning the versions of the bean. + * Generally this query is expected to be a find by id or unique predicates query. + * It will execute the query against the history returning the versions of the bean. *

    */ List> findVersionsBetween(Timestamp start, Timestamp end); @@ -774,6 +843,15 @@ public interface Query { * This is the number of 'top level' or 'root level' entities. *

    */ + int findCount(); + + /** + * Deprecated in favor of findCount(). + *

    + * Return the count of entities this query should return. + * + * @deprecated + */ int findRowCount(); /** @@ -783,9 +861,24 @@ public interface Query { * execution status (isDone etc) and get the value (with or without a * timeout). *

    - * + * * @return a Future object for the row count query */ + FutureRowCount findFutureCount(); + + /** + * Deprecated in favor of findFutureCount(). + *

    + * Execute find row count query in a background thread. + *

    + * This returns a Future object which can be used to cancel, check the + * execution status (isDone etc) and get the value (with or without a + * timeout). + *

    + * + * @return a Future object for the row count query + * @deprecated + */ FutureRowCount findFutureRowCount(); /** @@ -795,7 +888,7 @@ public interface Query { * execution status (isDone etc) and get the value (with or without a * timeout). *

    - * + * * @return a Future object for the list of Id's */ FutureIds findFutureIds(); @@ -821,7 +914,6 @@ public interface Query { * If maxRows is not set on the query prior to calling findPagedList() then a * PersistenceException is thrown. *

    - * *
    {@code
        *
        *  PagedList pagedList = Ebean.find(Order.class)
    @@ -843,24 +935,21 @@ public interface Query {
     
       /**
        * Set a named bind parameter. Named parameters have a colon to prefix the name.
    -   * 
        * 
    {@code
        *
        * // a query with a named parameter
        * String oql = "find order where status = :orderStatus";
    -   * 
    +   *
        * Query query = ebeanServer.find(Order.class, oql);
    -   * 
    +   *
        * // bind the named parameter
        * query.bind("orderStatus", OrderStatus.NEW);
        * List list = query.findList();
        *
        * }
    - * - * @param name - * the parameter name - * @param value - * the parameter value + * + * @param name the parameter name + * @param value the parameter value */ Query setParameter(String name, Object value); @@ -868,25 +957,22 @@ public interface Query { * Set an ordered bind parameter according to its position. Note that the * position starts at 1 to be consistent with JDBC PreparedStatement. You need * to set a parameter value for each ? you have in the query. - * *
    {@code
        *
        * // a query with a positioned parameter
        * String oql = "where status = ? order by id desc";
    -   * 
    +   *
        * Query query = ebeanServer.createQuery(Order.class, oql);
    -   * 
    +   *
        * // bind the parameter
        * query.setParameter(1, OrderStatus.NEW);
    -   * 
    +   *
        * List list = query.findList();
        *
        * }
    - * - * @param position - * the parameter bind position starting from 1 (not 0) - * @param value - * the parameter bind value. + * + * @param position the parameter bind position starting from 1 (not 0) + * @param value the parameter bind value. */ Query setParameter(int position, Object value); @@ -896,7 +982,6 @@ public interface Query { * You can use this to have further control over the query. For example adding * fetch joins. *

    - * *
    {@code
        *
        * Order order =
    @@ -919,10 +1004,9 @@ public interface Query {
     
       /**
        * Add a single Expression to the where clause returning the query.
    -   * 
        * 
    {@code
        *
    -   * List newOrders = 
    +   * List newOrders =
        *     ebeanServer.find(Order.class)
        * 		.where().eq("status", Order.NEW)
        * 		.findList();
    @@ -936,7 +1020,6 @@ public interface Query {
        * Add Expressions to the where clause with the ability to chain on the
        * ExpressionList. You can use this for adding multiple expressions to the
        * where clause.
    -   * 
        * 
    {@code
        *
        * List orders =
    @@ -947,9 +1030,9 @@ public interface Query {
        *     .findList();
        *
        * }
    - * - * @see Expr + * * @return The ExpressionList for adding expressions to. + * @see Expr */ ExpressionList where(); @@ -984,9 +1067,8 @@ public interface Query { * each customer you only want to get the new orders they placed since last * week. In this case you can use filterMany() to filter the orders. *

    - * *
    {@code
    -   * 
    +   *
        * List list =
        *     ebeanServer.find(Customer.class)
        *     // .fetch("orders", new FetchConfig().lazy())
    @@ -995,20 +1077,17 @@ public interface Query {
        *     .where().ilike("name", "rob%")
        *     .filterMany("orders").eq("status", Order.Status.NEW).gt("orderDate", lastWeek)
        *     .findList();
    -   * 
    +   *
        * }
    - * *

    * Please note you have to be careful that you add expressions to the correct * expression list - as there is one for the 'root level' and one for each * filterMany that you have. *

    - * - * @param propertyName - * the name of the many property that you want to have a filter on. - * + * + * @param propertyName the name of the many property that you want to have a filter on. * @return the expression list that you add filter expressions for the many - * to. + * to. */ ExpressionList filterMany(String propertyName); @@ -1021,9 +1100,9 @@ public interface Query { * Note that this returns the ExpressionList (so you can add multiple * expressions to the query in a fluent API way). *

    - * - * @see Expr + * * @return The ExpressionList for adding more expressions to. + * @see Expr */ ExpressionList having(); @@ -1037,9 +1116,8 @@ public interface Query { * than the ExpressionList. This is useful when you want to further specify * something on the query. *

    - * - * @param addExpressionToHaving - * the expression to add to the having clause. + * + * @param addExpressionToHaving the expression to add to the having clause. * @return the Query object */ Query having(Expression addExpressionToHaving); @@ -1151,9 +1229,8 @@ public interface Query { /** * Set the maximum number of rows to return in the query. - * - * @param maxRows - * the maximum number of rows to return in the query. + * + * @param maxRows the maximum number of rows to return in the query. */ Query setMaxRows(int maxRows); @@ -1162,11 +1239,10 @@ public interface Query { *

    * If no property is set then the id property is used. *

    - * *
    {@code
        *
        * // Assuming sku is unique for products...
    -   *    
    +   *
        * Map productMap =
        *     ebeanServer.find(Product.class)
        *     // use sku for keys...
    @@ -1174,9 +1250,8 @@ public interface Query {
        *     .findMap();
        *
        * }
    - * - * @param mapKey - * the property to use as keys for a map. + * + * @param mapKey the property to use as keys for a map. */ Query setMapKey(String mapKey); @@ -1203,7 +1278,7 @@ public interface Query { /** * Set to true if this query should execute against the doc store. *

    - * When setting this you may also consider disabling lazy loading. + * When setting this you may also consider disabling lazy loading. *

    */ Query setUseDocStore(boolean useDocStore); @@ -1226,9 +1301,8 @@ public interface Query { * preparedStatement. If the timeout occurs an exception will be thrown - this * will be a SQLException wrapped up in a PersistenceException. *

    - * - * @param secs - * the query timeout limit in seconds. Zero means there is no limit. + * + * @param secs the query timeout limit in seconds. Zero means there is no limit. */ Query setTimeout(int secs); @@ -1238,6 +1312,16 @@ public interface Query { * Gives the JDBC driver a hint as to the number of rows that should be * fetched from the database when more rows are needed for ResultSet. *

    + *

    + * Note that internally findEach and findEachWhile will set the fetch size + * if it has not already as these queries expect to process a lot of rows. + * If we didn't then Postgres and MySql for example would eagerly pull back + * all the row data and potentially consume a lot of memory in the process. + *

    + *

    + * As findEach and findEachWhile automatically set the fetch size we don't have + * to do so generally but we might still wish to for tuning a specific use case. + *

    */ Query setBufferFetchSizeHint(int fetchSize); @@ -1260,7 +1344,7 @@ public interface Query { * Return true if this query has forUpdate set. */ boolean isForUpdate(); - + /** * Set root table alias. */ @@ -1274,7 +1358,7 @@ public interface Query { /** * Set true if you want to disable lazy loading. *

    - * That is, once the object graph is returned further lazy loading is disabled. + * That is, once the object graph is returned further lazy loading is disabled. *

    */ Query setDisableLazyLoading(boolean disableLazyLoading); diff --git a/src/main/java/com/avaje/ebean/QueryIterator.java b/src/main/java/com/avaje/ebean/QueryIterator.java new file mode 100644 index 000000000..23bfaea55 --- /dev/null +++ b/src/main/java/com/avaje/ebean/QueryIterator.java @@ -0,0 +1,67 @@ +package com.avaje.ebean; + +import java.util.Iterator; + +/** + * Used to provide iteration over query results. + *

    + * This can be used when you want to process a very large number of results and + * means that you don't have to hold all the results in memory at once (unlike + * findList(), findSet() etc where all the beans are held in the List or Set + * etc). + *

    + *

    + * Note that findIterate (and findEach and findEachWhile) uses a "per graph" + * persistence context scope and adjusts jdbc fetch buffer size for large + * queries. As such it is better to use findList for small queries. + *

    + *

    + * Remember that with {@link QueryIterator} you must call {@link QueryIterator#close()} + * when you have finished iterating the results (typically in a finally block). + *

    + * + *
    {@code
    + * 
    + *  Query query = server.find(Customer.class)
    + *     .where().gt("id", 0)
    + *     .orderBy("id")
    + *     .setMaxRows(2);
    + *
    + *  QueryIterator it = query.findIterate();
    + *  try {
    + *    while (it.hasNext()) {
    + *      Customer customer = it.next();
    + *      // do something with customer ...
    + *    }
    + *  } finally {
    + *    // close the underlying resources
    + *    it.close();
    + *  }
    + *
    + * }
    + * + * @param + * the type of entity bean in the iteration + */ +public interface QueryIterator extends Iterator, java.io.Closeable { + + /** + * Returns true if the iteration has more elements. + */ + boolean hasNext(); + + /** + * Returns the next element in the iteration. + */ + T next(); + + /** + * Remove is not allowed. + */ + void remove(); + + /** + * Close the underlying resources held by this iterator. + */ + void close(); +} diff --git a/src/main/java/com/avaje/ebean/SqlQuery.java b/src/main/java/com/avaje/ebean/SqlQuery.java index 9541f7e69..58e4b721d 100644 --- a/src/main/java/com/avaje/ebean/SqlQuery.java +++ b/src/main/java/com/avaje/ebean/SqlQuery.java @@ -2,8 +2,6 @@ package com.avaje.ebean; import java.io.Serializable; import java.util.List; -import java.util.Map; -import java.util.Set; /** * Query object for performing native SQL queries that return SqlRow's. diff --git a/src/main/java/com/avaje/ebean/SqlUpdate.java b/src/main/java/com/avaje/ebean/SqlUpdate.java index 5fe6022a9..c02e89485 100644 --- a/src/main/java/com/avaje/ebean/SqlUpdate.java +++ b/src/main/java/com/avaje/ebean/SqlUpdate.java @@ -117,8 +117,7 @@ public interface SqlUpdate { SqlUpdate setParameter(int position, Object value); /** - * Set a null parameter via its index position. Exactly the same as - * {@link #setNull(int, int)}. + * Set a null parameter via its index position. */ SqlUpdate setNull(int position, int jdbcType); diff --git a/src/main/java/com/avaje/ebean/Transaction.java b/src/main/java/com/avaje/ebean/Transaction.java index cd2db3004..c05434b5c 100644 --- a/src/main/java/com/avaje/ebean/Transaction.java +++ b/src/main/java/com/avaje/ebean/Transaction.java @@ -54,13 +54,52 @@ public interface Transaction extends Closeable { */ void setReadOnly(boolean readOnly); + /** + * Commits the transaction at this point with the expectation that another + * commit (or rollback or end) will occur later to complete the transaction. + *

    + * This is similar to commit() but leaves the transaction "Active". + *

    + *

    Functions/h3> + *
      + *
    • Flush the JDBC batch buffer
    • + *
    • Call commit on the underlying JDBC connection
    • + *
    • Trigger any registered TransactionCallbacks
    • + *
    • Perform post-commit processing updating L2 cache, ElasticSearch etc
    • + *
    + */ + void commitAndContinue() throws RollbackException; + /** * Commit the transaction. + *

    + * This performs commit and completes the transaction closing underlying resources and + * marking the transaction as "In active". + *

    + *

    Functions/h3> + *
      + *
    • Flush the JDBC batch buffer
    • + *
    • Call commit on the underlying JDBC connection
    • + *
    • Trigger any registered TransactionCallbacks
    • + *
    • Perform post-commit processing updating L2 cache, ElasticSearch etc
    • + *
    • Close any underlying resources, closing the underlying JDBC connection
    • + *
    • Mark the transaction as "Inactive"
    • + *
    */ void commit() throws RollbackException; /** * Rollback the transaction. + *

    + * This performs rollback, closes underlying resources and marks the transaction as "In active". + *

    + *

    Functions/h3> + *
      + *
    • Call rollback on the underlying JDBC connection
    • + *
    • Trigger any registered TransactionCallbacks
    • + *
    • Close any underlying resources, closing the underlying JDBC connection
    • + *
    • Mark the transaction as "Inactive"
    • + *
    */ void rollback() throws PersistenceException; @@ -74,6 +113,16 @@ public interface Transaction extends Closeable { */ void rollback(Throwable e) throws PersistenceException; + /** + * Mark the transaction for rollback only. + */ + void setRollbackOnly(); + + /** + * Return true if the transaction is marked as rollback only. + */ + boolean isRollbackOnly(); + /** * If the transaction is active then perform rollback. Otherwise do nothing. */ @@ -417,9 +466,8 @@ public interface Transaction extends Closeable { /** * Add an arbitrary user object to the transaction. The objects added have no - * impact on any internals of ebena and are solely meant as a convenient - * method push user information to e.g. the - * {@link com.avaje.ebean.event.TransactionEventListener}. + * impact on any internals of ebean and are solely meant as a convenient + * method push user information (although somewhat replaced by TransactionCallback). */ void putUserObject(String name, Object value); diff --git a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java index 24462e614..22d68e0e4 100644 --- a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java +++ b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java @@ -39,6 +39,8 @@ public final class EntityBeanIntercept implements Serializable { private transient BeanLoader beanLoader; + private transient PreGetterCallback preGetterCallback; + private String ebeanServerName; /** @@ -186,6 +188,21 @@ public final class EntityBeanIntercept implements Serializable { return embeddedOwnerIndex; } + /** + * Clear the getter callback. + */ + public void clearGetterCallback() { + this.preGetterCallback = null; + } + + /** + * Register the callback to be triggered when getter is called. + * This is used primarily to automatically flush the JDBC batch. + */ + public void registerGetterCallback(PreGetterCallback getterCallback) { + this.preGetterCallback = getterCallback; + } + /** * Set the embedded beans owning bean. */ @@ -325,6 +342,13 @@ public final class EntityBeanIntercept implements Serializable { return state == STATE_LOADED; } + /** + * Set the bean into NEW state. + */ + public void setNew() { + this.state = STATE_NEW; + } + /** * Set the loaded state to true. *

    @@ -832,19 +856,31 @@ public final class EntityBeanIntercept implements Serializable { public void initialisedMany(int propertyIndex) { loadedProps[propertyIndex] = true; } - + + private final void preGetterCallback() { + if (preGetterCallback != null) { + preGetterCallback.preGetterTrigger(); + } + } + + /** + * Called prior to Id property getter. + */ + public void preGetId() { + preGetterCallback(); + } + /** * Method that is called prior to a getter method on the actual entity. */ public void preGetter(int propertyIndex) { + preGetterCallback(); if (state == STATE_NEW || disableLazyLoad) { return; } - if (!isLoadedProperty(propertyIndex)) { loadBean(propertyIndex); } - if (nodeUsageCollector != null) { nodeUsageCollector.addUsed(getProperty(propertyIndex)); } diff --git a/src/main/java/com/avaje/ebean/bean/PreGetterCallback.java b/src/main/java/com/avaje/ebean/bean/PreGetterCallback.java new file mode 100644 index 000000000..8faecec22 --- /dev/null +++ b/src/main/java/com/avaje/ebean/bean/PreGetterCallback.java @@ -0,0 +1,13 @@ +package com.avaje.ebean.bean; + +/** + * A callback that can be registered to fire on getter method calls. + * It's primary purpose is to automatically flush JDBC batch buffer. + */ +public interface PreGetterCallback { + + /** + * Trigger the callback. + */ + void preGetterTrigger(); +} diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index 0db26cc5a..9dd0d05bb 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -14,7 +14,6 @@ import com.avaje.ebean.event.BeanPostLoad; import com.avaje.ebean.event.BeanQueryAdapter; import com.avaje.ebean.event.BulkTableEventListener; import com.avaje.ebean.event.ServerConfigStartup; -import com.avaje.ebean.event.TransactionEventListener; import com.avaje.ebean.event.changelog.ChangeLogListener; import com.avaje.ebean.event.changelog.ChangeLogPrepare; import com.avaje.ebean.event.changelog.ChangeLogRegister; @@ -331,7 +330,6 @@ public class ServerConfig { private List queryAdapters = new ArrayList(); private List bulkTableEventListeners = new ArrayList(); private List configStartupListeners = new ArrayList(); - private List transactionEventListeners = new ArrayList(); /** * By default inserts are included in the change log. @@ -2052,35 +2050,6 @@ public class ServerConfig { this.persistControllers = persistControllers; } - /** - * Register a TransactionEventListener instance - *

    - * Note alternatively you can use {@link #setTransactionEventListeners(List)} - * to set all the TransactionEventListener instances. - *

    - */ - public void add(TransactionEventListener listener) { - transactionEventListeners.add(listener); - } - - /** - * Return the TransactionEventListener instances. - */ - public List getTransactionEventListeners() { - return transactionEventListeners; - } - - /** - * Register all the TransactionEventListener instances. - *

    - * Note alternatively you can use {@link #add(TransactionEventListener)} to - * add TransactionEventListener instances one at a time. - *

    - */ - public void setTransactionEventListeners(List transactionEventListeners) { - this.transactionEventListeners = transactionEventListeners; - } - /** * Register a BeanPersistListener instance. *

    diff --git a/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java b/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java index edf934518..cd243e4e4 100644 --- a/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java +++ b/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java @@ -15,7 +15,6 @@ import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; -import java.util.Properties; /** * Database platform specific settings. @@ -34,12 +33,6 @@ public class DatabasePlatform { */ ROLLBACK, - /** - * Just close the transaction. Valid at READ_COMMITTED isolation and preferred on some Databases - * as a performance optimisation. - */ - CLOSE, - /** * Commit the transaction */ diff --git a/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java b/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java index e04a67737..18fff98cb 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java +++ b/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java @@ -202,10 +202,10 @@ public class DbMigration { // use this flag to stop other plugins like full DDL generation if (!online) { DbOffline.setGenerateMigration(); - if (databasePlatform == null || !platforms.isEmpty()) { + if (databasePlatform == null && !platforms.isEmpty()) { // for multiple platform generation set the general platform // to H2 so that it runs offline without DB connection - setPlatform(DbPlatformName.H2); + setPlatform(platforms.get(0).platform); } } setDefaults(); diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java index 8e8610751..e7e8c66f6 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -270,17 +270,15 @@ public class MTable { } // compare existing columns (look for dropped columns) - int columnPosition = 0; for (MColumn existingColumn : allColumns()) { MColumn newColumn = newColumnMap.get(existingColumn.getName()); if (newColumn == null) { - diffDropColumn(modelDiff, existingColumn, columnPosition, newTable); + diffDropColumn(modelDiff, existingColumn); } else if (newColumn.isDraftOnly() && !draft) { // effectively a drop column (draft only column on a non-draft table) logger.trace("... drop column {} from table {} as now draftOnly", newColumn.getName(), name); - diffDropColumn(modelDiff, existingColumn, columnPosition, newTable); + diffDropColumn(modelDiff, existingColumn); } - columnPosition++; } if (addColumn != null) { @@ -540,7 +538,7 @@ public class MTable { /** * Add a 'drop column' to the diff. */ - private void diffDropColumn(ModelDiff modelDiff, MColumn existingColumn, int columnPosition, MTable newTable) { + private void diffDropColumn(ModelDiff modelDiff, MColumn existingColumn) { DropColumn dropColumn = new DropColumn(); dropColumn.setTableName(name); diff --git a/src/main/java/com/avaje/ebean/event/BeanPersistAdapter.java b/src/main/java/com/avaje/ebean/event/BeanPersistAdapter.java index 44b814e92..7cdab42ba 100644 --- a/src/main/java/com/avaje/ebean/event/BeanPersistAdapter.java +++ b/src/main/java/com/avaje/ebean/event/BeanPersistAdapter.java @@ -1,7 +1,5 @@ package com.avaje.ebean.event; -import java.util.Set; - import com.avaje.ebean.config.ServerConfig; /** diff --git a/src/main/java/com/avaje/ebean/event/BeanPersistController.java b/src/main/java/com/avaje/ebean/event/BeanPersistController.java index d10fe07e3..84a53a79a 100644 --- a/src/main/java/com/avaje/ebean/event/BeanPersistController.java +++ b/src/main/java/com/avaje/ebean/event/BeanPersistController.java @@ -1,7 +1,5 @@ package com.avaje.ebean.event; -import java.util.Set; - /** * Used to enhance or override the default bean persistence mechanism. *

    diff --git a/src/main/java/com/avaje/ebean/event/TransactionEventListener.java b/src/main/java/com/avaje/ebean/event/TransactionEventListener.java deleted file mode 100644 index fb9ab50c7..000000000 --- a/src/main/java/com/avaje/ebean/event/TransactionEventListener.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.avaje.ebean.event; - -import com.avaje.ebean.Transaction; - -/** - * Used to get notified about commit or rollback of a transaction - */ -public interface TransactionEventListener { - /** - * Called after the transaction has been committed - */ - void postTransactionCommit(Transaction tx); - - /** - * Called after the transaction has been rolled back - */ - void postTransactionRollback(Transaction tx, Throwable cause); -} diff --git a/src/main/java/com/avaje/ebean/event/TransactionEventListenerAdapter.java b/src/main/java/com/avaje/ebean/event/TransactionEventListenerAdapter.java deleted file mode 100644 index 376dfdfbe..000000000 --- a/src/main/java/com/avaje/ebean/event/TransactionEventListenerAdapter.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.avaje.ebean.event; - -import com.avaje.ebean.Transaction; - -/** - * A no operation implementation of TransactionEventListener. Objects extending - * this need to only override the methods they want to. - */ -public abstract class TransactionEventListenerAdapter implements TransactionEventListener { - - public void postTransactionCommit(Transaction tx) { - // do nothing by default - } - - public void postTransactionRollback(Transaction tx, Throwable cause) { - // do nothing by default - } -} diff --git a/src/main/java/com/avaje/ebean/event/changelog/BeanChange.java b/src/main/java/com/avaje/ebean/event/changelog/BeanChange.java index 225e04010..4deb57d00 100644 --- a/src/main/java/com/avaje/ebean/event/changelog/BeanChange.java +++ b/src/main/java/com/avaje/ebean/event/changelog/BeanChange.java @@ -80,7 +80,7 @@ public class BeanChange { * Set the bean id (for JSON tools). */ public void setId(Object id) { - this.id = this.id; + this.id = id; } /** diff --git a/src/main/java/com/avaje/ebeaninternal/api/ManyWhereJoins.java b/src/main/java/com/avaje/ebeaninternal/api/ManyWhereJoins.java index ad5863311..0caab1800 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/ManyWhereJoins.java +++ b/src/main/java/com/avaje/ebeaninternal/api/ManyWhereJoins.java @@ -132,15 +132,11 @@ public class ManyWhereJoins implements Serializable { formulaProperties.append(propertyName); } - public boolean isHasMany() { - return formulaWithJoin || !joins.isEmpty(); - } - /** - * Return true if the findRowCount query just needs the id property in the select clause. + * Return true if the query select includes a formula with join. */ - public boolean isSelectId() { - return !formulaWithJoin; + public boolean isFormulaWithJoin() { + return formulaWithJoin; } /** diff --git a/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java b/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java index ecdda3085..c2eda8690 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java +++ b/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java @@ -181,6 +181,15 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler { return e; } + /** + * Mark the underlying transaction as rollback only. + */ + public void setRollbackOnly() { + if (transaction != null) { + transaction.setRollbackOnly(); + } + } + /** * An Exception was caught and may or may not cause a rollback to occur. * Returns the exception and this should be thrown by the calling code. diff --git a/src/main/java/com/avaje/ebeaninternal/api/ScopedTransaction.java b/src/main/java/com/avaje/ebeaninternal/api/ScopedTransaction.java index a8a98d5c2..2aa97a8ad 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/ScopedTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/api/ScopedTransaction.java @@ -29,9 +29,13 @@ public class ScopedTransaction implements SpiTransaction { public ScopedTransaction(ScopeTrans scopeTrans) { this.scopeTrans = scopeTrans; - this.transaction =scopeTrans.getTransaction(); + this.transaction = scopeTrans.getTransaction(); } + @Override + public void commitAndContinue() throws RollbackException { + transaction.commitAndContinue(); + } @Override public void commit() throws RollbackException { @@ -49,6 +53,21 @@ public class ScopedTransaction implements SpiTransaction { scopeTrans.rollback(e); } + @Override + public void rollbackIfActive() { + transaction.rollbackIfActive(); + } + + @Override + public void setRollbackOnly() { + scopeTrans.setRollbackOnly(); + } + + @Override + public boolean isRollbackOnly() { + return transaction.isRollbackOnly(); + } + @Override public void end() throws PersistenceException { try { diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java index 42b56053c..cc98af802 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java @@ -145,7 +145,7 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL * the query has finished (if executing in a background thread). *

    */ - List findIdsWithCopy(Query query, Transaction t); + List findIdsWithCopy(Query query, Transaction t); /** * Execute the findRowCount query but without copying the query. diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java index 8b4ebffa0..b0254617b 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java @@ -78,6 +78,11 @@ public interface SpiQuery extends Query { */ ID_LIST, + /** + * Find single attribute. + */ + ATTRIBUTE, + /** * Find rowCount. */ @@ -246,21 +251,6 @@ public interface SpiQuery extends Query { List getSoftDeletePredicates(); - /** - * Set the list of Id's that is being populated. - *

    - * This is a mutating list of id's and we are setting this so that other - * threads have access to the id's before the id query has finished. - *

    - */ - void setIdList(List ids); - - /** - * Return the list of Id's that is currently being fetched by a background - * thread. - */ - List getIdList(); - /** * Return a copy of the query. */ @@ -341,6 +331,24 @@ public interface SpiQuery extends Query { */ void setSelectId(); + /** + * Mark the query as selecting a single attribute. + */ + void setSingleAttribute(); + + /** + * Return true if this is singleAttribute query. + */ + boolean isSingleAttribute(); + + /** + * Return true if the query should include the Id property. + *

    + * distinct and single attribute queries exclude the Id property. + *

    + */ + boolean isWithId(); + /** * Set a filter to a join path. */ @@ -593,9 +601,15 @@ public interface SpiQuery extends Query { */ OrmQueryDetail getDetail(); - TableJoin getIncludeTableJoin(); + /** + * Return the extra join for a M2M lazy load. + */ + TableJoin getM2mIncludeJoin(); - void setIncludeTableJoin(TableJoin includeTableJoin); + /** + * Set the extra join for a M2M lazy load. + */ + void setM2MIncludeJoin(TableJoin includeTableJoin); /** * Return the property used to specify keys for a map. diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java b/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java index e51719579..0f3a6ae1e 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java @@ -220,6 +220,12 @@ public interface SpiTransaction extends Transaction { */ Connection getInternalConnection(); + /** + * Rollback if the transaction is active. This provides an internal + * mechanism for rollback failures occur on commit(). + */ + void rollbackIfActive(); + /** * Return true if the manyToMany intersection should be persisted for this particular relationship direction. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java index 069057a6f..b84c91194 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java @@ -4,7 +4,6 @@ import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; -import java.io.Serializable; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCache.java b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCache.java index d7db64a10..f6444b840 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCache.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCache.java @@ -1,7 +1,6 @@ package com.avaje.ebeaninternal.server.cache; import com.avaje.ebean.BackgroundExecutor; -import com.avaje.ebean.EbeanServer; import com.avaje.ebean.cache.ServerCache; import com.avaje.ebean.cache.ServerCacheOptions; import com.avaje.ebean.cache.ServerCacheStatistics; diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java index b5907bfe4..9ba4070e7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java @@ -68,11 +68,11 @@ public abstract class BeanRequest { public void rollbackTransIfRequired() { if (createdTransaction) { try { - transaction.rollback(); + transaction.rollbackIfActive(); } catch (Exception e) { // Just log this and carry on. A previous exception has been // thrown and if this rollback throws exception it likely means - // that the connection is broken (and the datasource and db will cleanup) + // that the connection is broken (and the dataSource and db will cleanup) log.error("Error trying to rollback a transaction (after a prior exception thrown)", e); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java index d31135827..1a20d6b04 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java @@ -84,4 +84,9 @@ public class DefaultBeanState implements BeanState { public boolean isDisableLazyLoad() { return intercept.isDisableLazyLoad(); } + + @Override + public void resetForInsert() { + intercept.setNew(); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java index 210fde590..0f25e5cc7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java @@ -195,7 +195,6 @@ public class DefaultContainer implements SpiContainer { bootup.addPersistControllers(serverConfig.getPersistControllers()); bootup.addPostLoaders(serverConfig.getPostLoaders()); bootup.addFindControllers(serverConfig.getFindControllers()); - bootup.addTransactionEventListeners(serverConfig.getTransactionEventListeners()); bootup.addPersistListeners(serverConfig.getPersistListeners()); bootup.addQueryAdapters(serverConfig.getQueryAdapters()); bootup.addServerConfigStartup(serverConfig.getServerConfigStartupListeners()); diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index 5bc59014d..a87e07960 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -71,10 +71,8 @@ import javax.persistence.NonUniqueResultException; import javax.persistence.OptimisticLockException; import javax.persistence.PersistenceException; import javax.sql.DataSource; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -1134,30 +1132,52 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } @SuppressWarnings({ "unchecked", "rawtypes" }) - public Map findMap(Query query, Transaction t) { + public Map findMap(Query query, Transaction t) { SpiOrmQueryRequest request = createQueryRequest(Type.MAP, query, t); Object result = request.getFromQueryCache(); if (result != null) { - return (Map) result; + return (Map) result; } try { request.initTransIfRequired(); - return (Map) request.findMap(); + return (Map) request.findMap(); } finally { request.endTransIfRequired(); } } - public int findRowCount(Query query, Transaction t) { + @Override + @SuppressWarnings("unchecked") + public List findSingleAttributeList(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.ATTRIBUTE, query, t); + Object result = request.getFromQueryCache(); + if (result != null) { + return (List) result; + } + try { + request.initTransIfRequired(); + return (List) request.findSingleAttributeList(); + + } finally { + request.endTransIfRequired(); + } + } + + public int findCount(Query query, Transaction t) { SpiQuery copy = ((SpiQuery) query).copy(); return findRowCountWithCopy(copy, t); } + public int findRowCount(Query query, Transaction t) { + return findCount(query, t); + } + public int findRowCountWithCopy(Query query, Transaction t) { SpiOrmQueryRequest request = createQueryRequest(Type.ROWCOUNT, query, t); @@ -1170,16 +1190,14 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } } - public List findIds(Query query, Transaction t) { + public List findIds(Query query, Transaction t) { - SpiQuery copy = ((SpiQuery) query).copy(); - - return findIdsWithCopy(copy, t); + return findIdsWithCopy(((SpiQuery) query).copy(), t); } - public List findIdsWithCopy(Query query, Transaction t) { + public List findIdsWithCopy(Query query, Transaction t) { - SpiOrmQueryRequest request = createQueryRequest(Type.ID_LIST, query, t); + SpiOrmQueryRequest request = createQueryRequest(Type.ID_LIST, query, t); try { request.initTransIfRequired(); return request.findIds(); @@ -1213,7 +1231,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } } - public FutureRowCount findFutureRowCount(Query q, Transaction t) { + public FutureRowCount findFutureCount(Query q, Transaction t) { SpiQuery copy = ((SpiQuery) q).copy(); copy.setFutureFetch(true); @@ -1228,17 +1246,15 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return queryFuture; } + public FutureRowCount findFutureRowCount(Query q, Transaction t) { + return findFutureCount(q, t); + } + public FutureIds findFutureIds(Query query, Transaction t) { SpiQuery copy = ((SpiQuery) query).copy(); copy.setFutureFetch(true); - // this is the list we will put the id's in ... create it now so - // it is available for other threads to read while the id query - // is still executing (we don't need to wait for it to finish) - List idList = Collections.synchronizedList(new ArrayList()); - copy.setIdList(idList); - Transaction newTxn = createTransaction(); CallableQueryIds call = new CallableQueryIds(this, copy, newTxn); @@ -1286,6 +1302,19 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return new LimitOffsetPagedList(this, spiQuery); } + public QueryIterator findIterate(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.ITERATE, query, t); + try { + request.initTransIfRequired(); + return request.findIterate(); + + } catch (RuntimeException ex) { + request.endTransIfRequired(); + throw ex; + } + } + public void findEach(Query query, QueryEachConsumer consumer, Transaction t) { SpiOrmQueryRequest request = createQueryRequest(Type.ITERATE, query, t); diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java index 219acce17..caf82e185 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -340,14 +340,14 @@ public class InternalConfiguration { boolean localL2 = cacheManager.isLocalL2Caching(); if (serverConfig.isExplicitTransactionBeginMode()) { - return new ExplicitTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses()); + return new ExplicitTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager); } if (isAutoCommitMode()) { - return new AutoCommitTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses()); + return new AutoCommitTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager); } - return new TransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses()); + return new TransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java index 02464b5bc..83acce984 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java @@ -1,8 +1,8 @@ package com.avaje.ebeaninternal.server.core; +import com.avaje.ebean.QueryIterator; import com.avaje.ebean.Version; import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebeaninternal.api.BeanIdList; import java.util.List; @@ -21,6 +21,14 @@ public interface OrmQueryEngine { */ BeanCollection findMany(OrmQueryRequest request); + /** + * Execute the findSingleAttributeList query. + */ + List findSingleAttributeList(OrmQueryRequest request); + + /** + * Execute the findVersions query. + */ List> findVersions(OrmQueryRequest request); /** @@ -36,7 +44,7 @@ public interface OrmQueryEngine { /** * Execute the find id's query. */ - BeanIdList findIds(OrmQueryRequest request); + List findIds(OrmQueryRequest request); /** * Execute the query as a delete statement. diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java index 0aa5a472b..5b1a74340 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java @@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.core; import com.avaje.ebean.PersistenceContextScope; import com.avaje.ebean.QueryEachConsumer; import com.avaje.ebean.QueryEachWhileConsumer; +import com.avaje.ebean.QueryIterator; import com.avaje.ebean.RawSql; import com.avaje.ebean.Version; import com.avaje.ebean.bean.BeanCollection; @@ -12,7 +13,6 @@ import com.avaje.ebean.event.BeanFindController; import com.avaje.ebean.event.BeanQueryAdapter; import com.avaje.ebean.event.BeanQueryRequest; import com.avaje.ebean.text.json.JsonReadOptions; -import com.avaje.ebeaninternal.api.BeanIdList; import com.avaje.ebeaninternal.api.CQueryPlanKey; import com.avaje.ebeaninternal.api.HashQuery; import com.avaje.ebeaninternal.api.LoadContext; @@ -304,9 +304,8 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe return queryEngine.findRowCount(this); } - public List findIds() { - BeanIdList idList = queryEngine.findIds(this); - return idList.getIdList(); + public List findIds() { + return queryEngine.findIds(this); } public void findEach(QueryEachConsumer consumer) { @@ -373,6 +372,14 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe return (Map) queryEngine.findMany(this); } + /** + * Execute the findSingleAttributeList query. + */ + @Override + public List findSingleAttributeList() { + return queryEngine.findSingleAttributeList(this); + } + /** * Return a bean specific finder if one has been set. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java index afd86bfa6..fa954bcff 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java @@ -1,6 +1,7 @@ package com.avaje.ebeaninternal.server.core; import com.avaje.ebean.ValuePair; +import com.avaje.ebean.bean.PreGetterCallback; import com.avaje.ebeaninternal.api.ConcurrencyMode; import com.avaje.ebean.annotation.DocStoreMode; import com.avaje.ebean.bean.EntityBean; @@ -36,7 +37,7 @@ import java.util.Set; /** * PersistRequest for insert update or delete of a bean. */ -public final class PersistRequestBean extends PersistRequest implements BeanPersistRequest, DocStoreUpdate { +public final class PersistRequestBean extends PersistRequest implements BeanPersistRequest, DocStoreUpdate, PreGetterCallback { private final BeanManager beanManager; @@ -137,6 +138,11 @@ public final class PersistRequestBean extends PersistRequest implements BeanP private long version; + /** + * Flag set when request is added to JDBC batch registered as a "getter callback" to automatically flush batch. + */ + private boolean getterCallback; + public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, SpiTransaction t, PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse, boolean publish) { @@ -239,8 +245,17 @@ public final class PersistRequestBean extends PersistRequest implements BeanP */ public void setBatched() { batched = true; + if (type == Type.INSERT || type == Type.UPDATE) { + // used to trigger automatic jdbc batch flush + intercept.registerGetterCallback(this); + getterCallback = true; + } } + @Override + public void preGetterTrigger() { + transaction.flushBatch(); + } public void setSkipBatchForTopLevel() { skipBatchForTopLevel = true; @@ -621,6 +636,9 @@ public final class PersistRequestBean extends PersistRequest implements BeanP @Override public int executeNow() { + if (getterCallback) { + intercept.clearGetterCallback(); + } switch (type) { case INSERT: persistExecute.executeInsertBean(this); @@ -803,7 +821,7 @@ public final class PersistRequestBean extends PersistRequest implements BeanP } /** - * Add the bean to the TransactionEvent. This will be used by TransactionManager to synch Cache, + * Add the bean to the TransactionEvent. This will be used by TransactionManager to sync Cache, * Cluster and text indexes. */ private void addEvent() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/QueryIterator.java b/src/main/java/com/avaje/ebeaninternal/server/core/QueryIterator.java deleted file mode 100644 index 148a7d8c1..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/core/QueryIterator.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.avaje.ebeaninternal.server.core; - -import java.util.Iterator; - -/** - * Used to provide iteration over query results. - *

    - * This can be used when you want to process a very large number of results and - * means that you don't have to hold all the results in memory at once (unlike - * findList(), findSet() etc where all the beans are held in the List or Set - * etc). - *

    - * - *
    - * 
    - * Query<Customer> query = server.find(Customer.class)
    - *     .fetch("contacts", new FetchConfig().query(2))
    - *     .where().gt("id", 0)
    - *     .orderBy("id")
    - *     .setMaxRows(2);
    - * 
    - * QueryIterator<Customer> it = query.findIterate();
    - * try {
    - *   while (it.hasNext()) {
    - *     Customer customer = it.next();
    - *     // do something with customer...
    - *   }
    - * } finally {
    - *   // close the associated resources
    - *   it.close();
    - * }
    - * 
    - * - * @author rbygrave - * - * @param - * the type of entity bean in the iteration - */ -public interface QueryIterator extends Iterator, java.io.Closeable { - - /** - * Returns true if the iteration has more elements. - */ - boolean hasNext(); - - /** - * Returns the next element in the iteration. - */ - T next(); - - /** - * Remove is not allowed. - */ - void remove(); - - /** - * Close the underlying resources held by this iterator. - */ - void close(); -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java index b5f22603a..7c68e63c8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java @@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.core; import com.avaje.ebean.QueryEachConsumer; import com.avaje.ebean.QueryEachWhileConsumer; +import com.avaje.ebean.QueryIterator; import com.avaje.ebean.Version; import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebeaninternal.api.SpiQuery; @@ -70,7 +71,7 @@ public interface SpiOrmQueryRequest extends DocQueryRequest { /** * Execute the find ids query. */ - List findIds(); + List findIds(); /** * Execute the find returning a QueryIterator and visitor pattern. @@ -107,6 +108,11 @@ public interface SpiOrmQueryRequest extends DocQueryRequest { */ Map findMap(); + /** + * Execute the findSingleAttributeList query. + */ + List findSingleAttributeList(); + /** * Try to get the query result from the query cache. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/TransWrapper.java b/src/main/java/com/avaje/ebeaninternal/server/core/TransWrapper.java index 3ad7b74bc..36fb3de0f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/TransWrapper.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/TransWrapper.java @@ -41,7 +41,7 @@ final class TransWrapper { void rollbackIfCreated() { if (wasCreated){ - transaction.rollback(); + transaction.rollbackIfActive(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/bootup/BootupClasses.java b/src/main/java/com/avaje/ebeaninternal/server/core/bootup/BootupClasses.java index 2413c6caa..d846536d8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/bootup/BootupClasses.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/bootup/BootupClasses.java @@ -10,7 +10,6 @@ import com.avaje.ebean.event.BeanPersistListener; import com.avaje.ebean.event.BeanPostLoad; import com.avaje.ebean.event.BeanQueryAdapter; import com.avaje.ebean.event.ServerConfigStartup; -import com.avaje.ebean.event.TransactionEventListener; import com.avaje.ebean.event.changelog.ChangeLogListener; import com.avaje.ebean.event.changelog.ChangeLogPrepare; import com.avaje.ebean.event.changelog.ChangeLogRegister; @@ -54,8 +53,6 @@ public class BootupClasses implements ClassFilter { private final List> beanPostLoadList = new ArrayList>(); - private final List> transactionEventListenerList = new ArrayList>(); - private final List> beanFindControllerList = new ArrayList>(); private final List> beanQueryAdapterList = new ArrayList>(); @@ -70,7 +67,6 @@ public class BootupClasses implements ClassFilter { private final List beanPostLoadInstances = new ArrayList(); private final List persistListenerInstances = new ArrayList(); private final List queryAdapterInstances = new ArrayList(); - private final List transactionEventListenerInstances = new ArrayList(); private Class changeLogPrepareClass; private Class changeLogListenerClass; @@ -181,19 +177,6 @@ public class BootupClasses implements ClassFilter { } } - /** - * Add TransactionEventListeners instances. - */ - public void addTransactionEventListeners(List transactionEventListeners) { - if (transactionEventListeners != null) { - for (TransactionEventListener c : transactionEventListeners) { - this.transactionEventListenerInstances.add(c); - // don't automatically instantiate - this.transactionEventListenerList.remove(c.getClass()); - } - } - } - public void addPersistListeners(List listenerInstances) { if (listenerInstances != null) { for (BeanPersistListener l : listenerInstances) { @@ -351,14 +334,6 @@ public class BootupClasses implements ClassFilter { return idGeneratorInstances; } - public List getTransactionEventListeners() { - // add class registered TransactionEventListener to the already created instances - for (Class cls : transactionEventListenerList) { - createAdd(cls, transactionEventListenerInstances); - } - return transactionEventListenerInstances; - } - /** * Return the list of Embeddable classes. */ @@ -440,11 +415,6 @@ public class BootupClasses implements ClassFilter { interesting = true; } - if (TransactionEventListener.class.isAssignableFrom(cls)) { - transactionEventListenerList.add(cls); - interesting = true; - } - if (ScalarType.class.isAssignableFrom(cls)) { scalarTypeList.add(cls); interesting = true; diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java index 93dd8210c..4fe99bdb9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java @@ -1,7 +1,6 @@ package com.avaje.ebeaninternal.server.deploy; import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; import com.avaje.ebean.bean.PersistenceContext; import com.avaje.ebeaninternal.server.query.SqlJoinType; diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java index ffb37a85d..4791cb641 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java @@ -1,7 +1,6 @@ package com.avaje.ebeaninternal.server.deploy; import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; import com.avaje.ebean.bean.PersistenceContext; import com.avaje.ebeaninternal.server.query.SqlJoinType; diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 4581ea350..a64586646 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -357,7 +357,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { String tableAlias = manyToMany ? "int_." : "t0."; if (manyToMany) { - query.setIncludeTableJoin(inverseJoin); + query.setM2MIncludeJoin(inverseJoin); } String rawWhere = deriveWhereParentIdSql(true, tableAlias); String expr = descriptor.getParentIdInExpr(parentIds.size(), rawWhere); diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java index 0790b87c8..466eba53a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java @@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.server.deploy; import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.BeanCollectionAdd; import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.api.ClassUtil; import com.avaje.ebeaninternal.server.text.json.ReadJson; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java index 0a3f05474..aa677d98b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java @@ -217,12 +217,10 @@ public final class IdBinderSimple implements IdBinder { if (!idValue.getClass().equals(expectedType)) { idValue = scalarType.toBeanType(idValue); } - if (bean != null) { // support PropertyChangeSupport idProperty.setValueIntercept(bean, idValue); } - return idValue; } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedId.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedId.java index a4c56587a..77b79de30 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedId.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedId.java @@ -22,11 +22,6 @@ public interface ImportedId { */ boolean isScalar(); - /** - * Return the logical property name. - */ - String getLogicalName(); - /** * For scalar id return the related single db column. *

    @@ -46,11 +41,6 @@ public interface ImportedId { */ void dmlAppend(GenerateDmlRequest request); - /** - * Append to the DML statement to the where clause. - */ - void dmlWhere(GenerateDmlRequest request, EntityBean bean); - /** * Bind the value from the bean. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedIdEmbedded.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedIdEmbedded.java index fff0d126c..6a75753c0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedIdEmbedded.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedIdEmbedded.java @@ -46,11 +46,6 @@ public class ImportedIdEmbedded implements ImportedId { return false; } - public String getLogicalName() { - return owner.getName() + "." + foreignAssocOne.getName(); - } - - public String getDbColumn() { return null; } @@ -68,34 +63,6 @@ public class ImportedIdEmbedded implements ImportedId { } } - public void dmlWhere(GenerateDmlRequest request, EntityBean bean) { - - Object embeddedId = null; - if (bean != null) { - embeddedId = foreignAssocOne.getValue(bean); - } - - if (embeddedId == null) { - for (int i = 0; i < imported.length; i++) { - if (imported[i].owner.isDbUpdatable()) { - request.appendColumnIsNull(imported[i].localDbColumn); - } - } - } else { - EntityBean embedded = (EntityBean) embeddedId; - for (int i = 0; i < imported.length; i++) { - if (imported[i].owner.isDbUpdatable()) { - Object value = imported[i].foreignProperty.getValue(embedded); - if (value == null) { - request.appendColumnIsNull(imported[i].localDbColumn); - } else { - request.appendColumn(imported[i].localDbColumn); - } - } - } - } - } - public Object bind(BindableRequest request, EntityBean bean) throws SQLException { Object embeddedId = null; diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedIdSimple.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedIdSimple.java index d706995fc..4241f37d7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedIdSimple.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/ImportedIdSimple.java @@ -82,10 +82,6 @@ public final class ImportedIdSimple implements ImportedId, Comparable implements SpiExpressionList { return query.asDraft(); } - @Override - public Query includeSoftDeletes() { - return setIncludeSoftDeletes(); - } - @Override public Query setIncludeSoftDeletes() { return query.setIncludeSoftDeletes(); @@ -323,9 +318,14 @@ public class DefaultExpressionList implements SpiExpressionList { return query.findFutureIds(); } + @Override + public FutureRowCount findFutureCount() { + return query.findFutureCount(); + } + @Override public FutureRowCount findFutureRowCount() { - return query.findFutureRowCount(); + return findFutureCount(); } @Override @@ -339,15 +339,25 @@ public class DefaultExpressionList implements SpiExpressionList { } @Override - public int findRowCount() { - return query.findRowCount(); + public int findCount() { + return query.findCount(); } @Override - public List findIds() { + public int findRowCount() { + return findCount(); + } + + @Override + public List findIds() { return query.findIds(); } + @Override + public QueryIterator findIterate() { + return query.findIterate(); + } + @Override public void findEach(QueryEachConsumer consumer) { query.findEach(consumer); @@ -369,13 +379,13 @@ public class DefaultExpressionList implements SpiExpressionList { } @Override - public Map findMap() { + public Map findMap() { return query.findMap(); } @Override - public Map findMap(String keyProperty, Class keyType) { - return query.findMap(keyProperty, keyType); + public List findSingleAttributeList() { + return query.findSingleAttributeList(); } @Override diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExpressionList.java b/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExpressionList.java index 91ffc2406..cea91d4bf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExpressionList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExpressionList.java @@ -48,9 +48,14 @@ public class FilterExpressionList extends DefaultExpressionList { return rootQuery.findFutureList(); } + @Override + public FutureRowCount findFutureCount() { + return rootQuery.findFutureCount(); + } + @Override public FutureRowCount findFutureRowCount() { - return rootQuery.findFutureRowCount(); + return findFutureCount(); } @Override @@ -59,13 +64,18 @@ public class FilterExpressionList extends DefaultExpressionList { } @Override - public Map findMap() { + public Map findMap() { return rootQuery.findMap(); } + @Override + public int findCount() { + return rootQuery.findCount(); + } + @Override public int findRowCount() { - return rootQuery.findRowCount(); + return findCount(); } @Override diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java index 6b49e2492..1788ce7cb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java @@ -12,6 +12,7 @@ import com.avaje.ebean.PagedList; import com.avaje.ebean.Query; import com.avaje.ebean.QueryEachConsumer; import com.avaje.ebean.QueryEachWhileConsumer; +import com.avaje.ebean.QueryIterator; import com.avaje.ebean.Version; import com.avaje.ebean.event.BeanQueryRequest; import com.avaje.ebean.search.Match; @@ -62,6 +63,7 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression * This is expected to only used after expressions are built via query language parsing. *

    */ + @SuppressWarnings("unchecked") public void simplify() { exprList.simplifyEntries(); @@ -325,11 +327,6 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression return exprList.asDraft(); } - @Override - public Query includeSoftDeletes() { - return setIncludeSoftDeletes(); - } - @Override public Query setIncludeSoftDeletes() { return exprList.setIncludeSoftDeletes(); @@ -361,15 +358,25 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression } @Override - public FutureRowCount findFutureRowCount() { - return exprList.findFutureRowCount(); + public FutureRowCount findFutureCount() { + return exprList.findFutureCount(); } @Override - public List findIds() { + public FutureRowCount findFutureRowCount() { + return findFutureCount(); + } + + @Override + public List findIds() { return exprList.findIds(); } + @Override + public QueryIterator findIterate() { + return exprList.findIterate(); + } + @Override public void findEach(QueryEachConsumer consumer) { exprList.findEach(consumer); @@ -386,13 +393,13 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression } @Override - public Map findMap() { + public Map findMap() { return exprList.findMap(); } @Override - public Map findMap(String keyProperty, Class keyType) { - return exprList.findMap(keyProperty, keyType); + public List findSingleAttributeList() { + return exprList.findSingleAttributeList(); } @Override @@ -401,10 +408,15 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression } @Override - public int findRowCount() { + public int findCount() { return exprList.findRowCount(); } + @Override + public int findRowCount() { + return findCount(); + } + @Override public Set findSet() { return exprList.findSet(); diff --git a/src/main/java/com/avaje/ebeaninternal/server/grammer/antlr/EQLLexer.java b/src/main/java/com/avaje/ebeaninternal/server/grammer/antlr/EQLLexer.java index 3e15448b1..d65b01136 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/grammer/antlr/EQLLexer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/grammer/antlr/EQLLexer.java @@ -1,13 +1,16 @@ // Generated from /home/rob/github/avaje-ebeanorm/src/test/resources/EQL.g4 by ANTLR 4.5.3 package com.avaje.ebeaninternal.server.grammer.antlr; -import org.antlr.v4.runtime.Lexer; + import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenStream; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.LexerATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) public class EQLLexer extends Lexer { diff --git a/src/main/java/com/avaje/ebeaninternal/server/grammer/antlr/EQLParser.java b/src/main/java/com/avaje/ebeaninternal/server/grammer/antlr/EQLParser.java index f4ede0ebc..7644c2e38 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/grammer/antlr/EQLParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/grammer/antlr/EQLParser.java @@ -1,13 +1,23 @@ // Generated from /home/rob/github/avaje-ebeanorm/src/test/resources/EQL.g4 by ANTLR 4.5.3 package com.avaje.ebeaninternal.server.grammer.antlr; -import org.antlr.v4.runtime.atn.*; + +import org.antlr.v4.runtime.NoViableAltException; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ParserATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.misc.*; -import org.antlr.v4.runtime.tree.*; +import org.antlr.v4.runtime.tree.ParseTreeListener; +import org.antlr.v4.runtime.tree.TerminalNode; + import java.util.List; -import java.util.Iterator; -import java.util.ArrayList; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) public class EQLParser extends Parser { diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/mimetypes.properties b/src/main/java/com/avaje/ebeaninternal/server/lib/util/mimetypes.properties deleted file mode 100644 index 8b77c2af1..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/mimetypes.properties +++ /dev/null @@ -1,186 +0,0 @@ - # - # Used by MimeTypeHelper to find the mime types of based on file extensions. - -abs=audio/x-mpeg -ai=application/postscript -aif=audio/x-aiff -aifc=audio/x-aiff -aiff=audio/x-aiff -aim=application/x-aim -art=image/x-jg -asc=text/plain -asf=video/x-ms-asf -asx=video/x-ms-asf -au=audio/basic -avi=video/x-msvideo -avx=video/x-rad-screenplay - -bcpio=application/x-bcpio -bin=application/octet-stream -bmp=image/bmp -body=text/html - -cdf=application/x-netcdf -cer=application/x-x509-ca-cert -class=application/java -cpio=application/x-cpio -csh=application/x-csh -css=text/css -csv=text/csv - -dib=image/bmp -doc=application/msword -dtd=application/xml-dtd -dv=video/x-dv -dvi=application/x-dvi -dms=application/octet-stream - -eps=application/postscript -etx=text/x-setext -exe=application/octet-stream - -gif=image/gif -gtar=application/x-gtar -gz=application/x-gzip - -hdf=application/x-hdf -htc=text/x-component -htm=text/html -html=text/html -hqx=application/mac-binhex40 - -ico=image/x-icon -ief=image/ief - -jad=text/vnd.sun.j2me.app-descriptor -jar=application/java-archive -java=text/plain -jnlp=application/x-java-jnlp-file -jpe=image/jpeg -jpeg=image/jpeg -jpg=image/jpeg -js=text/javascript -jsf=text/plain -jspf=text/plain - -kar=audio/midi - -latex=application/x-latex -lha=application/octet-stream -lzh=application/octet-stream - -m3u=audio/x-mpegurl -mac=image/x-macpaint -man=application/x-troff-man -mathml=application/mathml+xml -me=application/x-troff-me -mid=audio/midi -midi=audio/midi -mif=application/vnd.mif -mov=video/quicktime -movie=video/x-sgi-movie -mp1=audio/x-mpeg -mp2=audio/mpeg -mp3=audio/mpeg -mpa=audio/x-mpeg -mpe=video/mpeg -mpeg=video/mpeg -mpega=audio/x-mpeg -mpga=audio/mpeg -mpg=video/mpeg -mpv2=video/mpeg2 -ms=application/x-troff-ms - -nc=application/x-netcdf - -oda=application/oda -ogg=application/ogg - -pbm=image/x-portable-bitmap -pct=image/pict -pdf=application/pdf -pgm=image/x-portable-graymap -pic=image/pict -pict=image/pict -pls=audio/x-scpls -png=image/png -pnm=image/x-portable-anymap -pnt=image/x-macpaint -ppm=image/x-portable-pixmap -pps=application/vnd.ms-powerpoint -ppt=application/vnd.ms-powerpoint -ps=application/postscript -psd=image/x-photoshop - -qt=video/quicktime -qti=image/x-quicktime -qtif=image/x-quicktime - -ra=audio/x-realaudio -ram=audio/x-pn-realaudio -ras=image/x-cmu-raster -rdf=application/rdf+xml -rgb=image/x-rgb -rpm=audio/x-pn-realaudio-plugin -rm=application/vnd.rn-realmedia -roff=application/x-troff -rtf=text/rtf -rtx=text/richtext - -sh=application/x-sh -shar=application/x-shar -shtml=text/x-server-parsed-html -sgml=text/sgml -sgm=text/sgml -smf=audio/x-midi -sit=application/x-stuffit -snd=audio/basic -src=application/x-wais-source -sv4cpio=application/x-sv4cpio -sv4crc=application/x-sv4crc -svg=image/svg+xml -svgz=image/svg -swf=application/x-shockwave-flash - -t=application/x-troff -tar=application/x-tar -tcl=application/x-tcl -tex=application/x-tex -texi=application/x-texinfo -texinfo=application/x-texinfo -tif=image/tiff -tiff=image/tiff -tr=application/x-troff -tsv=text/tab-separated-values -txt=text/plain - -ulw=audio/basic -ustar=application/x-ustar - -vcd=application/x-cdlink -vrml=model/vrml -vsd=application/x-visio -vxml=application/voicexml+xml - -wav=audio/x-wav -wbmp=image/vnd.wap.wbmp -wml=text/vnd.wap.wml -wmlc=application/vnd.wap.wmlc -wmls=text/vnd.wap.wmlscript -wmlscriptc=application/vnd.wap.wmlscriptc -wrl=model/vrml - -xbm=image/x-xbitmap -xht=application/xhtml+xml -xhtml=application/xhtml+xml -xls=application/vnd.ms-excel -xml=application/xml -xpm=image/x-xpixmap -xsl=application/xml -xslt=application/xslt+xml -xul=application/vnd.mozilla.xul+xml -xwd=image/x-xwindowdump - -Z=application/x-compress -z=application/x-compress -zip=application/zip \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java index 249d9f998..72bc67626 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java @@ -56,11 +56,9 @@ public abstract class DLoadBaseContext { int queryBatchSize = queryProps.getQueryFetchBatch(); if (queryBatchSize == -1) { - // not eager query fetch, just lazy loading return batchSize; } else if (queryBatchSize == 0) { - // default query fetch batch size is 100 return 100; } else { diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java index 09cd0419a..3a73be4e3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java @@ -25,9 +25,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex private LoadBuffer currentBuffer; public DLoadBeanContext(DLoadContext parent, BeanDescriptor desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) { - super(parent, desc, path, defaultBatchSize, queryProps); - // bufferList only required when using query joins (queryFetch) this.bufferList = (!queryFetch) ? null : new ArrayList(); this.currentBuffer = createBuffer(firstBatchSize); diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java index fde0ebde4..2088e5b05 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java @@ -284,20 +284,23 @@ public class DLoadContext implements LoadContext { private void registerSecondaryNode(boolean many, OrmQueryProperties props) { - String path = props.getPath(); - int lazyJoinBatch = props.getLazyFetchBatch(); - int batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize; - - if (many) { - DLoadManyContext manyContext = createManyContext(path, batchSize, props); - manyMap.put(path, manyContext); + int batchSize; + if (props.isQueryFetch()) { + batchSize = 100; } else { - DLoadBeanContext beanContext = createBeanContext(path, batchSize, props); - beanMap.put(path, beanContext); + int lazyJoinBatch = props.getLazyFetchBatch(); + batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize; + } + + String path = props.getPath(); + if (many) { + manyMap.put(path, createManyContext(path, batchSize, props)); + } else { + beanMap.put(path, createBeanContext(path, batchSize, props)); } } - private DLoadManyContext getManyContext(String path) { + protected DLoadManyContext getManyContext(String path) { if (path == null) { throw new RuntimeException("path is null?"); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java index c76277461..846ed3dd2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java @@ -146,7 +146,7 @@ public final class BatchControl { /** * Entity Bean insert, update or delete. This will either execute the request - * immediately or queue it for batch processing later. The queue is flushed + * immediately or queue it for batch processing later. The queue is flushedIntercept * according to the depth (object graph depth). */ public int executeOrQueue(PersistRequestBean request, boolean batch) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DmlUtil.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DmlUtil.java index 92e99a3fe..df174cfbd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DmlUtil.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DmlUtil.java @@ -10,6 +10,6 @@ public class DmlUtil { * Return true if the value is null or a Numeric 0 (for primitive int's and long's) or Option empty. */ public static boolean isNullOrZero(Object value) { - return value == null || value instanceof Number && ((Number) value).longValue() == 0l; + return value == null || value instanceof Number && ((Number) value).longValue() == 0L; } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java index efd238f73..a53564a20 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java @@ -5,7 +5,6 @@ import com.avaje.ebeaninternal.api.SpiTransaction; import com.avaje.ebeaninternal.api.SpiUpdate; import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.type.DataBind; import com.avaje.ebeaninternal.server.util.BindParamsParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java index 67cb2ab4f..30e0ac473 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java @@ -5,7 +5,6 @@ import com.avaje.ebeaninternal.api.SpiSqlUpdate; import com.avaje.ebeaninternal.api.SpiTransaction; import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql; import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql.SqlType; -import com.avaje.ebeaninternal.server.type.DataBind; import com.avaje.ebeaninternal.server.util.BindParamsParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java index c034e5c78..a81496742 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java @@ -1,12 +1,11 @@ package com.avaje.ebeaninternal.server.persist.dmlbind; -import java.sql.SQLException; - -import com.avaje.ebean.bean.EntityBean; import com.avaje.ebeaninternal.api.DerivedRelationshipData; import com.avaje.ebeaninternal.server.core.PersistRequestBean; import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import java.sql.SQLException; + /** * Request object passed to bindables. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java index ca37d7976..d3e1121bd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java @@ -1,6 +1,6 @@ package com.avaje.ebeaninternal.server.query; -import com.avaje.ebeaninternal.server.core.QueryIterator; +import com.avaje.ebean.QueryIterator; import com.avaje.ebean.Version; import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.EntityBean; diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java index fafdb94bc..ce8efe1fa 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java @@ -123,7 +123,7 @@ public class CQueryBuilder { if (!sqlTree.isIncludeJoins()) { // simple - delete from table ... - return aliasStrip(buildSql("delete", request, predicates, sqlTree).getSql()); + return aliasStrip(buildSql("delete", request, predicates, sqlTree).getSql()); } // wrap as - delete from table where id in (select id ...) String sql = buildSql(null, request, predicates, sqlTree).getSql(); @@ -135,11 +135,11 @@ public class CQueryBuilder { private String buildUpdateSql(OrmQueryRequest request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) { - String updateClause = "update "+request.getBeanDescriptor().getBaseTable()+" set "+predicates.getDbUpdateClause(); + String updateClause = "update " + request.getBeanDescriptor().getBaseTable() + " set " + predicates.getDbUpdateClause(); if (!sqlTree.isIncludeJoins()) { // simple - update table set ... where ... - return aliasStrip(buildSqlUpdate(updateClause, request, predicates, sqlTree).getSql()); + return aliasStrip(buildSqlUpdate(updateClause, request, predicates, sqlTree).getSql()); } // wrap as - update table set ... where id in (select id ...) String sql = buildSqlUpdate(null, request, predicates, sqlTree).getSql(); @@ -161,26 +161,20 @@ public class CQueryBuilder { * Replace the root table alias. */ private String aliasReplace(String sql, String replaceWith) { - sql = StringHelper.replaceString(sql, "${RTA}.", replaceWith+"."); + sql = StringHelper.replaceString(sql, "${RTA}.", replaceWith + "."); return StringHelper.replaceString(sql, "${RTA}", replaceWith); } - /** - * Build the row count query. - */ - public CQueryFetchIds buildFetchIdsQuery(OrmQueryRequest request) { + public CQueryFetchSingleAttribute buildFetchAttributeQuery(OrmQueryRequest request) { - SpiQuery query = request.getQuery(); - - query.setSelectId(); + SpiQuery query = request.getQuery(); + query.setSingleAttribute(); CQueryPredicates predicates = new CQueryPredicates(binder, request); CQueryPlan queryPlan = request.getQueryPlan(); if (queryPlan != null) { - // skip building the SqlTree and Sql string predicates.prepare(false); - String sql = queryPlan.getSql(); - return new CQueryFetchIds(request, predicates, sql); + return new CQueryFetchSingleAttribute(request, predicates, queryPlan); } // use RawSql or generated Sql @@ -188,13 +182,19 @@ public class CQueryBuilder { SqlTree sqlTree = createSqlTree(request, predicates, getHistorySupport(query), getDraftSupport(query)); SqlLimitResponse s = buildSql(null, request, predicates, sqlTree); - String sql = s.getSql(); - - // cache the query plan - queryPlan = new CQueryPlan(request, sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql()); + queryPlan = new CQueryPlan(request, s.getSql(), sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql()); request.putQueryPlan(queryPlan); - return new CQueryFetchIds(request, predicates, sql); + return new CQueryFetchSingleAttribute(request, predicates, queryPlan); + } + + /** + * Build the find ids query. + */ + public CQueryFetchSingleAttribute buildFetchIdsQuery(OrmQueryRequest request) { + + request.getQuery().setSelectId(); + return buildFetchAttributeQuery(request); } /** @@ -225,20 +225,10 @@ public class CQueryBuilder { ManyWhereJoins manyWhereJoins = query.getManyWhereJoins(); - boolean hasMany = manyWhereJoins.isHasMany(); - if (manyWhereJoins.isSelectId()) { - // just select the id property - query.setSelectId(); - } else { - // select the id and the required formula properties + if (manyWhereJoins.isFormulaWithJoin()) { query.select(manyWhereJoins.getFormulaProperties()); - } - - String sqlSelect = "select count(*)"; - if (hasMany) { - // need to count distinct id's ... - query.setSqlDistinct(true); - sqlSelect = null; + } else { + query.setSelectId(); } CQueryPredicates predicates = new CQueryPredicates(binder, request); @@ -257,6 +247,14 @@ public class CQueryBuilder { sqlTree.addSoftDeletePredicate(query); } + boolean hasMany = sqlTree.hasMany(); + String sqlSelect = "select count(*)"; + if (hasMany) { + // need to count distinct id's ... + query.setSqlDistinct(true); + sqlSelect = null; + } + SqlLimitResponse s = buildSql(sqlSelect, request, predicates, sqlTree); String sql = s.getSql(); if (hasMany || query.isRawSql()) { @@ -452,8 +450,8 @@ public class CQueryBuilder { } sb.append(select.getSelectSql()); - if (query.isDistinctQuery() && dbOrderBy != null) { - // add the orderby columns to the select clause (due to distinct) + if (query.isDistinctQuery() && dbOrderBy != null && !query.isSingleAttribute()) { + // add the orderBy columns to the select clause (due to distinct) sb.append(", ").append(convertDbOrderByForSelect(dbOrderBy)); } } @@ -488,7 +486,7 @@ public class CQueryBuilder { } if (stripAlias) { // strip the table alias for use in update statement - idSql = StringHelper.replaceString(idSql, "t0.",""); + idSql = StringHelper.replaceString(idSql, "t0.", ""); } sb.append(idSql).append(" "); hasWhere = true; diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java index 425e488f8..49d8625f9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java @@ -1,17 +1,16 @@ package com.avaje.ebeaninternal.server.query; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.server.core.QueryIterator; import com.avaje.ebean.ValuePair; import com.avaje.ebean.Version; import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebeaninternal.api.BeanIdList; import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.server.core.DiffHelp; import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebean.QueryIterator; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.lib.util.Str; import com.avaje.ebeaninternal.server.persist.Binder; @@ -89,29 +88,24 @@ public class CQueryEngine { } /** - * Build and execute the find Id's query. + * Build and execute the findSingleAttributeList query. */ - public BeanIdList findIds(OrmQueryRequest request) { + public List findSingleAttributeList(OrmQueryRequest request) { - CQueryFetchIds rcQuery = queryBuilder.buildFetchIdsQuery(request); + CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchAttributeQuery(request); + return findAttributeList(request, rcQuery); + } + + @SuppressWarnings("unchecked") + private List findAttributeList(OrmQueryRequest request, CQueryFetchSingleAttribute rcQuery) { try { - - BeanIdList list = rcQuery.findIds(); - + List list = (List)rcQuery.findList(); if (request.isLogSql()) { logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog()); } - if (request.isLogSummary()) { request.getTransaction().logSummary(rcQuery.getSummary()); } - - if (request.getQuery().isFutureFetch()) { - // end the transaction for futureFindIds (it had it's own one) - logger.debug("Future findIds completed!"); - request.getTransaction().end(); - } - return list; } catch (SQLException e) { @@ -119,6 +113,15 @@ public class CQueryEngine { } } + /** + * Build and execute the find Id's query. + */ + public List findIds(OrmQueryRequest request) { + + CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchIdsQuery(request); + return findAttributeList(request, rcQuery); + } + private void logGeneratedSql(OrmQueryRequest request, String sql, String bindLog) { String logSql = sql; if (TransactionManager.SQL_LOGGER.isTraceEnabled()) { @@ -300,9 +303,10 @@ public class CQueryEngine { SpiQuery query = request.getQuery(); - if (query.getMaxRows() > 1 || query.getFirstRow() > 0) { + if (!query.isDistinct() && (query.getMaxRows() > 1 || query.getFirstRow() > 0)) { // deemed to be a be a paging query - check that the order by contains // the id property to ensure unique row ordering for predicable paging + // but only in case, this is not a distinct query request.getBeanDescriptor().appendOrderById(query); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java deleted file mode 100644 index 7000e6558..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java +++ /dev/null @@ -1,278 +0,0 @@ -package com.avaje.ebeaninternal.server.query; - -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.api.BeanIdList; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiQuery.Mode; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.DbReadContext; -import com.avaje.ebeaninternal.server.type.DataReader; -import com.avaje.ebeaninternal.server.type.RsetDataReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * Executes the select row count query. - */ -public class CQueryFetchIds { - - private static final Logger logger = LoggerFactory.getLogger(CQueryFetchIds.class); - - /** - * The overall find request wrapper object. - */ - private final OrmQueryRequest request; - - private final BeanDescriptor desc; - - private final SpiQuery query; - - /** - * Where clause predicates. - */ - private final CQueryPredicates predicates; - - /** - * The final sql that is generated. - */ - private final String sql; - - private RsetDataReader dataReader; - - /** - * The statement used to create the resultSet. - */ - private PreparedStatement pstmt; - - private String bindLog; - - private int executionTimeMicros; - - private int rowCount; - - private final int maxRows; - - /** - * Create the Sql select based on the request. - */ - public CQueryFetchIds(OrmQueryRequest request, CQueryPredicates predicates, String sql) { - - this.request = request; - this.query = request.getQuery(); - this.sql = sql; - this.maxRows = query.getMaxRows(); - - query.setGeneratedSql(sql); - - this.desc = request.getBeanDescriptor(); - this.predicates = predicates; - } - - /** - * Return a summary description of this query. - */ - public String getSummary() { - StringBuilder sb = new StringBuilder(80); - sb.append("FindIds exeMicros[").append(executionTimeMicros) - .append("] rows[").append(rowCount) - .append("] type[").append(desc.getName()) - .append("] predicates[").append(predicates.getLogWhereSql()) - .append("] bind[").append(bindLog).append("]"); - - return sb.toString(); - } - - /** - * Return the bind log. - */ - public String getBindLog() { - return bindLog; - } - - /** - * Return the generated sql. - */ - public String getGeneratedSql() { - return sql; - } - - /** - * Execute the query returning the row count. - */ - public BeanIdList findIds() throws SQLException { - - long startNano = System.nanoTime(); - - try { - // get the list that we are going to put the id's into. - // This was already set so that it is available to be - // read by other threads (it is a synchronised list) - List idList = query.getIdList(); - if (idList == null) { - // running in foreground thread (not FutureIds query) - idList = Collections.synchronizedList(new ArrayList()); - query.setIdList(idList); - } - - BeanIdList result = new BeanIdList(idList); - - SpiTransaction t = request.getTransaction(); - Connection conn = t.getInternalConnection(); - pstmt = conn.prepareStatement(sql); - - if (query.getBufferFetchSizeHint() > 0) { - pstmt.setFetchSize(query.getBufferFetchSizeHint()); - } - - if (query.getTimeout() > 0) { - pstmt.setQueryTimeout(query.getTimeout()); - } - - bindLog = predicates.bind(pstmt, conn); - - ResultSet rset = pstmt.executeQuery(); - dataReader = new RsetDataReader(request.getDataTimeZone(), rset); - - boolean hitMaxRows = false; - boolean hasMoreRows = false; - rowCount = 0; - - DbReadContext ctx = new DbContext(); - - while (rset.next()) { - Object idValue = desc.getIdBinder().read(ctx); - idList.add(idValue); - // reset back to 0 - dataReader.resetColumnPosition(); - rowCount++; - - if (maxRows > 0 && rowCount == maxRows) { - hitMaxRows = true; - hasMoreRows = rset.next(); - break; - - } - } - - if (hitMaxRows) { - result.setHasMore(hasMoreRows); - } - - long exeNano = System.nanoTime() - startNano; - executionTimeMicros = (int) exeNano / 1000; - - return result; - - } finally { - close(); - } - } - - /** - * Close the resources. - *

    - * The jdbc resultSet and statement need to be closed. Its important that - * this method is called. - *

    - */ - private void close() { - try { - if (dataReader != null) { - dataReader.close(); - dataReader = null; - } - } catch (SQLException e) { - logger.error("Error closing DataReader", e); - } - try { - if (pstmt != null) { - pstmt.close(); - pstmt = null; - } - } catch (SQLException e) { - logger.error("Error closing PreparedStatement", e); - } - } - - - class DbContext implements DbReadContext { - - public void propagateState(Object e) { - throw new RuntimeException("Not Called"); - } - - public Mode getQueryMode() { - return Mode.NORMAL; - } - - public DataReader getDataReader() { - return dataReader; - } - - public Boolean isReadOnly() { - return Boolean.FALSE; - } - - @Override - public boolean isDisableLazyLoading() { - return false; - } - - public boolean isRawSql() { - return false; - } - - public void register(String path, EntityBeanIntercept ebi) { - } - - public void register(String path, BeanCollection bc) { - } - - public BeanPropertyAssocMany getManyProperty() { - // always null - return null; - } - - public PersistenceContext getPersistenceContext() { - // always null - return null; - } - - public boolean isAutoTuneProfiling() { - return false; - } - - public void profileBean(EntityBeanIntercept ebi, String prefix) { - // no-op - } - - public void setCurrentPrefix(String currentPrefix, Map pathMap) { - // no-op - } - - public void setLazyLoadedChildBean(EntityBean loadedBean, Object lazyLoadParentId) { - // no-op - } - - @Override - public boolean isDraftQuery() { - return false; - } - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchSingleAttribute.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchSingleAttribute.java new file mode 100644 index 000000000..431d84edd --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchSingleAttribute.java @@ -0,0 +1,172 @@ +package com.avaje.ebeaninternal.server.query; + +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.type.RsetDataReader; +import com.avaje.ebeaninternal.server.type.ScalarType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Base compiled query request for single attribute queries. + */ +class CQueryFetchSingleAttribute { + + private static final Logger logger = LoggerFactory.getLogger(CQueryFetchSingleAttribute.class); + + /** + * The overall find request wrapper object. + */ + private final OrmQueryRequest request; + + private final BeanDescriptor desc; + + private final SpiQuery query; + + /** + * Where clause predicates. + */ + private final CQueryPredicates predicates; + + /** + * The final sql that is generated. + */ + private final String sql; + + private RsetDataReader dataReader; + + /** + * The statement used to create the resultSet. + */ + private PreparedStatement pstmt; + + private String bindLog; + + private int executionTimeMicros; + + private int rowCount; + + private final ScalarType scalarType; + + /** + * Create the Sql select based on the request. + */ + public CQueryFetchSingleAttribute(OrmQueryRequest request, CQueryPredicates predicates, CQueryPlan plan) { + this.request = request; + this.query = request.getQuery(); + this.sql = plan.getSql(); + this.desc = request.getBeanDescriptor(); + this.predicates = predicates; + this.scalarType = plan.getSingleProperty().getScalarType(); + + query.setGeneratedSql(sql); + } + + /** + * Return a summary description of this query. + */ + protected String getSummary() { + StringBuilder sb = new StringBuilder(80); + sb.append("FindAttr exeMicros[").append(executionTimeMicros) + .append("] rows[").append(rowCount) + .append("] type[").append(desc.getName()) + .append("] predicates[").append(predicates.getLogWhereSql()) + .append("] bind[").append(bindLog).append("]"); + + return sb.toString(); + } + + /** + * Execute the query returning the row count. + */ + protected List findList() throws SQLException { + + long startNano = System.nanoTime(); + try { + + prepareExecute(); + + List result = new ArrayList(); + + while (dataReader.next()) { + result.add(scalarType.read(dataReader)); + dataReader.resetColumnPosition(); + rowCount++; + } + + long exeNano = System.nanoTime() - startNano; + executionTimeMicros = (int) exeNano / 1000; + + return result; + + } finally { + close(); + } + } + + /** + * Return the bind log. + */ + protected String getBindLog() { + return bindLog; + } + + /** + * Return the generated sql. + */ + protected String getGeneratedSql() { + return sql; + } + + private void prepareExecute() throws SQLException { + + SpiTransaction t = request.getTransaction(); + Connection conn = t.getInternalConnection(); + pstmt = conn.prepareStatement(sql); + + if (query.getBufferFetchSizeHint() > 0) { + pstmt.setFetchSize(query.getBufferFetchSizeHint()); + } + if (query.getTimeout() > 0) { + pstmt.setQueryTimeout(query.getTimeout()); + } + + bindLog = predicates.bind(pstmt, conn); + dataReader = new RsetDataReader(request.getDataTimeZone(), pstmt.executeQuery()); + } + + /** + * Close the resources. + *

    + * The jdbc resultSet and statement need to be closed. Its important that + * this method is called. + *

    + */ + private void close() { + try { + if (dataReader != null) { + dataReader.close(); + dataReader = null; + } + } catch (SQLException e) { + logger.error("Error closing DataReader", e); + } + try { + if (pstmt != null) { + pstmt.close(); + pstmt = null; + } + } catch (SQLException e) { + logger.error("Error closing PreparedStatement", e); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java index 2fe591939..88dba323a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java @@ -4,7 +4,7 @@ import java.sql.SQLException; import javax.persistence.PersistenceException; -import com.avaje.ebeaninternal.server.core.QueryIterator; +import com.avaje.ebean.QueryIterator; import com.avaje.ebeaninternal.server.core.OrmQueryRequest; /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java index fc042d264..163fdfc2f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java @@ -5,7 +5,7 @@ import java.util.ArrayList; import javax.persistence.PersistenceException; -import com.avaje.ebeaninternal.server.core.QueryIterator; +import com.avaje.ebean.QueryIterator; import com.avaje.ebeaninternal.server.core.OrmQueryRequest; /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlan.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlan.java index f4f831cef..6c490bf17 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlan.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlan.java @@ -266,4 +266,7 @@ public class CQueryPlan { return stats.getLastQueryTime(); } + public BeanProperty getSingleProperty() { + return sqlTree.getRootNode().getSingleProperty(); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java index 3342637d4..493d7030b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java @@ -1,11 +1,10 @@ package com.avaje.ebeaninternal.server.query; -import com.avaje.ebeaninternal.server.core.QueryIterator; +import com.avaje.ebean.QueryIterator; import com.avaje.ebean.Version; import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.event.BeanFindController; -import com.avaje.ebeaninternal.api.BeanIdList; import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.api.SpiTransaction; import com.avaje.ebeaninternal.server.core.OrmQueryEngine; @@ -65,12 +64,18 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine { return queryEngine.findRowCount(request); } - public BeanIdList findIds(OrmQueryRequest request) { + public List findIds(OrmQueryRequest request) { flushJdbcBatchOnQuery(request); return queryEngine.findIds(request); } + @Override + public List findSingleAttributeList(OrmQueryRequest request) { + flushJdbcBatchOnQuery(request); + return queryEngine.findSingleAttributeList(request); + } + public QueryIterator findIterate(OrmQueryRequest request) { // LIMITATION: You can not use QueryIterator to load bean cache diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagedList.java b/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagedList.java index 9906ac223..eb5ef408c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagedList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagedList.java @@ -43,19 +43,27 @@ public class LimitOffsetPagedList implements PagedList { this.firstRow = query.getFirstRow(); } - public void loadRowCount() { - getFutureRowCount(); + public void loadCount() { + getFutureCount(); } - public Future getFutureRowCount() { + public void loadRowCount() { + loadCount(); + } + + public Future getFutureCount() { synchronized (monitor) { if (futureRowCount == null) { - futureRowCount = server.findFutureRowCount(query, null); + futureRowCount = server.findFutureCount(query, null); } return futureRowCount; } } + public Future getFutureRowCount() { + return getFutureCount(); + } + public List getList() { synchronized (monitor) { if (list == null) { @@ -67,7 +75,7 @@ public class LimitOffsetPagedList implements PagedList { public int getTotalPageCount() { - int rowCount = getTotalRowCount(); + int rowCount = getTotalCount(); if (rowCount == 0) { return 0; } else { @@ -75,7 +83,7 @@ public class LimitOffsetPagedList implements PagedList { } } - public int getTotalRowCount() { + public int getTotalCount() { synchronized (monitor) { if (futureRowCount != null) { try { @@ -89,13 +97,17 @@ public class LimitOffsetPagedList implements PagedList { if (foregroundTotalRowCount > -1) return foregroundTotalRowCount; // just using foreground thread - foregroundTotalRowCount = server.findRowCount(query, null); + foregroundTotalRowCount = server.findCount(query, null); return foregroundTotalRowCount; } } + public int getTotalRowCount() { + return getTotalCount(); + } + public boolean hasNext() { - return (firstRow + maxRows) < getTotalRowCount(); + return (firstRow + maxRows) < getTotalCount(); } public boolean hasPrev() { @@ -110,7 +122,7 @@ public class LimitOffsetPagedList implements PagedList { int first = firstRow + 1; int last = firstRow + getList().size(); - int total = getTotalRowCount(); + int total = getTotalCount(); return first + to + last + of + total; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureIds.java b/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureIds.java index 85ff80907..bf0d595cf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureIds.java @@ -1,12 +1,12 @@ package com.avaje.ebeaninternal.server.query; -import java.util.List; -import java.util.concurrent.FutureTask; - import com.avaje.ebean.FutureIds; import com.avaje.ebean.Query; import com.avaje.ebean.Transaction; +import java.util.List; +import java.util.concurrent.FutureTask; + /** * Default implementation of FutureIds. */ @@ -31,10 +31,6 @@ public class QueryFutureIds extends BaseFuture> implements Futur return call.query; } - public List getPartialIds() { - return call.query.getIdList(); - } - public boolean cancel(boolean mayInterruptIfRunning) { call.query.cancel(); return super.cancel(mayInterruptIfRunning); diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java index aaca0a912..8e7a3906e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java @@ -60,21 +60,6 @@ public class SqlTree { this.includeJoins = includeJoins; } - /** - * Construct for RawSql. - */ - public SqlTree(String summary, SqlTreeNode rootNode) { - this.summary = summary; - this.rootNode = rootNode; - this.selectSql = null; - this.fromSql = null; - this.inheritanceWhereSql = null; - this.encryptedProps = null; - this.manyProperty = null; - this.includes = null; - this.includeJoins = false; //not valid for rawSql - } - /** * Return true if the query includes joins (not valid for rawSql). */ @@ -153,4 +138,10 @@ public class SqlTree { return encryptedProps; } + /** + * Return true if the query has a many join. + */ + public boolean hasMany() { + return manyProperty != null || rootNode.hasMany(); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java index 2621b50b2..835fd97ed 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java @@ -106,7 +106,7 @@ public class SqlTreeBuilder { this.query = request.getQuery(); this.disableLazyLoad = query.isDisableLazyLoading(); this.subQuery = Type.SUBQUERY.equals(query.getType()) || Type.ID_LIST.equals(query.getType()); - this.includeJoin = query.getIncludeTableJoin(); + this.includeJoin = query.getM2mIncludeJoin(); this.manyWhereJoins = query.getManyWhereJoins(); this.queryDetail = query.getDetail(); @@ -260,7 +260,7 @@ public class SqlTreeBuilder { // Optional many property for lazy loading query BeanPropertyAssocMany lazyLoadMany = (query == null) ? null : query.getLazyLoadMany(); - boolean withId = !rawNoId && !subQuery && (query == null || !query.isDistinct()); + boolean withId = !rawNoId && !subQuery && (query == null || query.isWithId()); return new SqlTreeNodeRoot(desc, props, myList, withId, includeJoin, lazyLoadMany, SpiQuery.TemporalMode.of(query), disableLazyLoad); } else if (prop instanceof BeanPropertyAssocMany) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java index 6700f8e16..0313c351c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java @@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.query; import com.avaje.ebean.Version; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; import com.avaje.ebeaninternal.server.deploy.DbReadContext; import com.avaje.ebeaninternal.server.deploy.DbSqlContext; @@ -57,4 +58,15 @@ public interface SqlTreeNode { * Load a version of a @History bean with effective dates. */ Version loadVersion(DbReadContext ctx) throws SQLException; + + /** + * Return true if the query has a many join. + */ + boolean hasMany(); + + /** + * Return the property for singleAttribute query. + */ + BeanProperty getSingleProperty(); + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java index 60d1e882f..c0ac1e85d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -1,11 +1,5 @@ package com.avaje.ebeaninternal.server.query; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import com.avaje.ebean.Version; import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.EntityBean; @@ -24,6 +18,12 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin; import com.avaje.ebeaninternal.server.deploy.id.IdBinder; import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Normal bean included in the query. */ @@ -83,13 +83,6 @@ public class SqlTreeNodeBean implements SqlTreeNode { */ private boolean intersectionAsOfTableAlias; - /** - * Construct for Raw SQL. - */ - public SqlTreeNodeBean(BeanDescriptor desc, SqlTreeProperties props, boolean withId, boolean disableLazyLoad) { - this(null, null, desc, props, null, withId, null, null, disableLazyLoad); - } - /** * Construct for leaf node. */ @@ -137,6 +130,11 @@ public class SqlTreeNodeBean implements SqlTreeNode { pathMap = createPathMap(prefix, desc); } + @Override + public BeanProperty getSingleProperty() { + return properties[0]; + } + private Map createPathMap(String prefix, BeanDescriptor desc) { BeanPropertyAssocMany[] manys = desc.propertiesMany(); @@ -570,4 +568,14 @@ public class SqlTreeNodeBean implements SqlTreeNode { return true; } + @Override + public boolean hasMany() { + + for (SqlTreeNode child : children) { + if (child.hasMany()) { + return true; + } + } + return false; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java index 97d3a92a4..2a7fd0cfb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java @@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.query; import com.avaje.ebean.Version; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; import com.avaje.ebeaninternal.server.deploy.DbReadContext; @@ -55,6 +56,11 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode { // nothing to do here } + @Override + public BeanProperty getSingleProperty() { + throw new IllegalStateException("No expected"); + } + /** * Return true if the extra join is a many join. *

    @@ -146,4 +152,9 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode { public Version loadVersion(DbReadContext ctx) throws SQLException { return null; } + + @Override + public boolean hasMany() { + return manyJoin; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java index d3813c7e0..defe4263d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java @@ -1,13 +1,13 @@ package com.avaje.ebeaninternal.server.query; -import java.sql.SQLException; -import java.util.List; - import com.avaje.ebean.bean.EntityBean; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; import com.avaje.ebeaninternal.server.deploy.DbReadContext; import com.avaje.ebeaninternal.server.deploy.DbSqlContext; +import java.sql.SQLException; +import java.util.List; + public final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { private final BeanPropertyAssocMany manyProp; @@ -38,4 +38,8 @@ public final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { super.appendFrom(ctx, joinType.autoToOuter()); } + @Override + public boolean hasMany() { + return true; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java index eaa0996c0..b3c390761 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java @@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.query; import com.avaje.ebean.Version; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; @@ -39,6 +40,11 @@ public class SqlTreeNodeManyWhereJoin implements SqlTreeNode { this.parentPrefix = split[0]; } + @Override + public BeanProperty getSingleProperty() { + throw new IllegalStateException("No expected"); + } + @Override public void addAsOfTableAlias(SpiQuery query) { // do nothing here ... @@ -109,4 +115,9 @@ public class SqlTreeNodeManyWhereJoin implements SqlTreeNode { // nothing to do here return null; } + + @Override + public boolean hasMany() { + return true; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeRoot.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeRoot.java index 75cd58059..f3f027bce 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeRoot.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeRoot.java @@ -25,14 +25,6 @@ public final class SqlTreeNodeRoot extends SqlTreeNodeBean { this.includeJoin = includeJoin; } - /** - * Construct for raw sql named queries. - */ - public SqlTreeNodeRoot(BeanDescriptor desc, SqlTreeProperties props, boolean withId) { - super(desc, props, withId, false); - this.includeJoin = null; - } - /** * Set AsOf support (at root level). */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java index befb221ce..370fd8bc8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -43,6 +43,10 @@ public class DefaultOrmQuery implements SpiQuery { public static final String DEFAULT_QUERY_NAME = "default"; + private static final FetchConfig FETCH_QUERY = new FetchConfig().query(); + + private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy(); + private final Class beanType; private final BeanDescriptor beanDescriptor; @@ -55,7 +59,7 @@ public class DefaultOrmQuery implements SpiQuery { * For lazy loading of ManyToMany we need to add a join to the intersection table. This is that * join to the intersection table. */ - private TableJoin includeTableJoin; + private TableJoin m2mIncludeJoin; private ProfilingListener profilingListener; @@ -119,8 +123,6 @@ public class DefaultOrmQuery implements SpiQuery { */ private ReadEvent futureFetchAudit; - private List partialIds; - private int timeout; /** @@ -188,6 +190,8 @@ public class DefaultOrmQuery implements SpiQuery { */ private boolean forUpdate; + private boolean singleAttribute; + /** * Set to true if this query has been tuned by autoTune. */ @@ -530,6 +534,26 @@ public class DefaultOrmQuery implements SpiQuery { select(beanDescriptor.getIdBinder().getIdProperty()); } + @Override + public void setSingleAttribute() { + this.singleAttribute = true; + } + + /** + * Return true if this is a single attribute query. + */ + public boolean isSingleAttribute() { + return singleAttribute; + } + + /** + * Return true if the Id should be included in the query. + */ + @Override + public boolean isWithId() { + return !distinct && !singleAttribute; + } + @Override public NaturalKeyBindParam getNaturalKeyBindParam() { NaturalKeyBindParam namedBind = null; @@ -571,7 +595,7 @@ public class DefaultOrmQuery implements SpiQuery { public DefaultOrmQuery copy(EbeanServer server) { DefaultOrmQuery copy = new DefaultOrmQuery(beanDescriptor, server, expressionFactory); - copy.includeTableJoin = includeTableJoin; + copy.m2mIncludeJoin = m2mIncludeJoin; copy.profilingListener = profilingListener; // copy.query = query; @@ -846,7 +870,7 @@ public class DefaultOrmQuery implements SpiQuery { */ CQueryPlanKey createQueryPlanKey() { - queryPlanKey = new OrmQueryPlanKey(includeTableJoin, type, detail, maxRows, firstRow, + queryPlanKey = new OrmQueryPlanKey(m2mIncludeJoin, type, detail, maxRows, firstRow, disableLazyLoading, orderBy, distinct, sqlDistinct, mapKey, id, bindParams, whereExpressions, havingExpressions, temporalMode, forUpdate, rootTableAlias, rawSql, updateProperties); @@ -1013,6 +1037,16 @@ public class DefaultOrmQuery implements SpiQuery { return fetch(property, null, null); } + @Override + public Query fetchQuery(String property) { + return fetch(property, null, FETCH_QUERY); + } + + @Override + public Query fetchLazy(String property) { + return fetch(property, null, FETCH_LAZY); + } + @Override public DefaultOrmQuery fetch(String property, FetchConfig joinConfig) { return fetch(property, null, joinConfig); @@ -1023,6 +1057,16 @@ public class DefaultOrmQuery implements SpiQuery { return fetch(property, columns, null); } + @Override + public Query fetchQuery(String property, String columns) { + return fetch(property, columns, FETCH_QUERY); + } + + @Override + public Query fetchLazy(String property, String columns) { + return fetch(property, columns, FETCH_LAZY); + } + @Override public DefaultOrmQuery fetch(String property, String columns, FetchConfig config) { detail.fetch(property, columns, config); @@ -1048,11 +1092,16 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public int findRowCount() { + public int findCount() { // a copy of this query is made in the server // as the query needs to modified (so we modify // the copy rather than this query instance) - return server.findRowCount(this, null); + return server.findCount(this, null); + } + + @Override + public int findRowCount() { + return findCount(); } @Override @@ -1065,6 +1114,11 @@ public class DefaultOrmQuery implements SpiQuery { server.findEach(this, consumer, null); } + @Override + public QueryIterator findIterate() { + return server.findIterate(this, null); + } + @Override public List> findVersions() { this.temporalMode = TemporalMode.VERSIONS; @@ -1093,15 +1147,14 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public Map findMap() { + public Map findMap() { return server.findMap(this, null); } @Override @SuppressWarnings("unchecked") - public Map findMap(String keyProperty, Class keyType) { - setMapKey(keyProperty); - return (Map) findMap(); + public List findSingleAttributeList() { + return (List)server.findSingleAttributeList(this, null); } @Override @@ -1119,9 +1172,14 @@ public class DefaultOrmQuery implements SpiQuery { return server.findFutureList(this, null); } + @Override + public FutureRowCount findFutureCount() { + return server.findFutureCount(this, null); + } + @Override public FutureRowCount findFutureRowCount() { - return server.findFutureRowCount(this, null); + return findFutureCount(); } @Override @@ -1254,14 +1312,13 @@ public class DefaultOrmQuery implements SpiQuery { return "Query [" + whereExpressions + "]"; } - @Override - public TableJoin getIncludeTableJoin() { - return includeTableJoin; + public TableJoin getM2mIncludeJoin() { + return m2mIncludeJoin; } @Override - public void setIncludeTableJoin(TableJoin includeTableJoin) { - this.includeTableJoin = includeTableJoin; + public void setM2MIncludeJoin(TableJoin m2mIncludeJoin) { + this.m2mIncludeJoin = m2mIncludeJoin; } @Override @@ -1449,16 +1506,6 @@ public class DefaultOrmQuery implements SpiQuery { return disableReadAudit; } - @Override - public List getIdList() { - return partialIds; - } - - @Override - public void setIdList(List partialIds) { - this.partialIds = partialIds; - } - @Override public boolean isFutureFetch() { return futureFetch; diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPlanKey.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPlanKey.java index 6023754b1..60b15fe5d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPlanKey.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPlanKey.java @@ -14,9 +14,8 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin; */ public class OrmQueryPlanKey implements CQueryPlanKey { - private final TableJoin includeTableJoin; + private final String m2mIncludeTable; private final String orderByAsSting; - private final OrmQueryDetail detail; private final SpiExpression where; private final SpiExpression having; private final RawSql.Key rawSqlKey; @@ -36,11 +35,10 @@ public class OrmQueryPlanKey implements CQueryPlanKey { private final int planHash; private final int bindCount; - public OrmQueryPlanKey(TableJoin includeTableJoin, SpiQuery.Type type, OrmQueryDetail detail, int maxRows, int firstRow, boolean disableLazyLoading, OrderBy orderBy, boolean distinct, boolean sqlDistinct, String mapKey, Object id, BindParams bindParams, SpiExpression whereExpressions, SpiExpression havingExpressions, SpiQuery.TemporalMode temporalMode, boolean forUpdate, String rootTableAlias, RawSql rawSql, OrmUpdateProperties updateProperties) { + public OrmQueryPlanKey(TableJoin m2mIncludeTable, SpiQuery.Type type, OrmQueryDetail detail, int maxRows, int firstRow, boolean disableLazyLoading, OrderBy orderBy, boolean distinct, boolean sqlDistinct, String mapKey, Object id, BindParams bindParams, SpiExpression whereExpressions, SpiExpression havingExpressions, SpiQuery.TemporalMode temporalMode, boolean forUpdate, String rootTableAlias, RawSql rawSql, OrmUpdateProperties updateProperties) { - this.includeTableJoin = includeTableJoin; + this.m2mIncludeTable = m2mIncludeTable == null ? null : m2mIncludeTable.getTable(); this.type = type; - this.detail = detail; this.maxRows = maxRows; this.firstRow = firstRow; this.disableLazyLoading = disableLazyLoading; @@ -69,7 +67,7 @@ public class OrmQueryPlanKey implements CQueryPlanKey { builder.add(hasIdValue); builder.add(temporalMode); builder.add(rawSqlKey == null ? 0 : rawSqlKey.hashCode()); - builder.add(includeTableJoin != null ? includeTableJoin.queryHash() : 0); + builder.add(this.m2mIncludeTable); builder.add(rootTableAlias); if (detail != null) { @@ -120,15 +118,12 @@ public class OrmQueryPlanKey implements CQueryPlanKey { if (hasIdValue != that.hasIdValue) return false; if (type != that.type) return false; if (temporalMode != that.temporalMode) return false; - if (includeTableJoin != null ? !includeTableJoin.equals(that.includeTableJoin) : that.includeTableJoin != null) return false; + if (m2mIncludeTable != null ? !m2mIncludeTable.equals(that.m2mIncludeTable) : that.m2mIncludeTable != null) return false; if (orderByAsSting != null ? !orderByAsSting.equals(that.orderByAsSting) : that.orderByAsSting != null) return false; if (where != null ? !where.isSameByPlan(that.where) : that.where != null) return false; if (having != null ? !having.isSameByPlan(that.having) : that.having != null) return false; if (updateProperties != null ? !updateProperties.isSameByPlan(that.updateProperties) : that.updateProperties != null) return false; if (rawSqlKey != null ? !rawSqlKey.equals(that.rawSqlKey) : that.rawSqlKey != null) return false; - -// if (detail != null ? !detail.equals(that.detail) : that.detail != null) return false; - if (mapKey != null ? !mapKey.equals(that.mapKey) : that.mapKey != null) return false; return rootTableAlias != null ? rootTableAlias.equals(that.rootTableAlias) : that.rootTableAlias == null; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/AutoCommitTransactionManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/AutoCommitTransactionManager.java index af04c6ad5..c2c40ebe4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/AutoCommitTransactionManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/AutoCommitTransactionManager.java @@ -3,10 +3,9 @@ package com.avaje.ebeaninternal.server.transaction; import com.avaje.ebean.BackgroundExecutor; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor; import com.avaje.ebeaninternal.server.cluster.ClusterManager; -import com.avaje.ebeaninternal.server.core.bootup.BootupClasses; import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor; import java.sql.Connection; @@ -18,9 +17,9 @@ import java.sql.Connection; public class AutoCommitTransactionManager extends TransactionManager { public AutoCommitTransactionManager(boolean localL2Caching, ServerConfig serverConfig, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, - DocStoreUpdateProcessor indexUpdateProcessor, BeanDescriptorManager descMgr, BootupClasses bootupClasses) { + DocStoreUpdateProcessor indexUpdateProcessor, BeanDescriptorManager descMgr) { - super(localL2Caching, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, descMgr, bootupClasses); + super(localL2Caching, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, descMgr); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java index b989605df..fa7970edf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java @@ -1,7 +1,6 @@ package com.avaje.ebeaninternal.server.transaction; import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebean.bean.PersistenceContextUtil; import com.avaje.ebeaninternal.api.Monitor; import java.util.HashMap; diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java index b6834ec41..2b255d106 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java @@ -4,8 +4,7 @@ import com.avaje.ebeaninternal.api.SpiTransaction; import com.avaje.ebeaninternal.server.transaction.TransactionMap.State; /** - * Used by EbeanMgr to store its Transactions in a ThreadLocal. This way the - * transaction objects don't have to passed around. + * Used to store Transactions in a ThreadLocal. */ public final class DefaultTransactionThreadLocal { @@ -99,7 +98,7 @@ public final class DefaultTransactionThreadLocal { * block. *

    *

    -   * Ebean.beingTransaction();
    +   * Ebean.beginTransaction();
        * try {
        *   // ... perform some actions in a single transaction
        *
    @@ -110,8 +109,6 @@ public final class DefaultTransactionThreadLocal {
        *   Ebean.endTransaction();
        * }
        * 
    - *

    - *

    */ public static void end(String serverName) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java index c93d4a592..70b4ee7b1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java @@ -4,10 +4,9 @@ import com.avaje.ebean.BackgroundExecutor; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.config.dbplatform.DatabasePlatform; import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor; import com.avaje.ebeaninternal.server.cluster.ClusterManager; -import com.avaje.ebeaninternal.server.core.bootup.BootupClasses; import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor; import javax.sql.DataSource; import java.sql.Connection; @@ -18,9 +17,9 @@ import java.sql.Connection; public class ExplicitTransactionManager extends TransactionManager { public ExplicitTransactionManager(boolean localL2Caching, ServerConfig serverConfig, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, - DocStoreUpdateProcessor indexUpdateProcessor, BeanDescriptorManager descMgr, BootupClasses bootupClasses) { + DocStoreUpdateProcessor indexUpdateProcessor, BeanDescriptorManager descMgr) { - super(localL2Caching, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, descMgr, bootupClasses); + super(localL2Caching, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, descMgr); } /** @@ -44,11 +43,7 @@ public class ExplicitTransactionManager extends TransactionManager { return DatabasePlatform.OnQueryOnly.valueOf(systemPropertyValue.trim().toUpperCase()); } - if (DatabasePlatform.OnQueryOnly.CLOSE.equals(dbPlatformOnQueryOnly)) { - // Not using OnQueryOnly.CLOSE with ExplicitJdbcTransaction - return DatabasePlatform.OnQueryOnly.COMMIT; - } - // default to commit if not defined on the platform - return dbPlatformOnQueryOnly == null ? DatabasePlatform.OnQueryOnly.COMMIT : dbPlatformOnQueryOnly; + // default to rollback if not defined on the platform + return dbPlatformOnQueryOnly == null ? DatabasePlatform.OnQueryOnly.ROLLBACK : dbPlatformOnQueryOnly; } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java index 9edd8b395..3185b4bfc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java @@ -66,6 +66,8 @@ public class JdbcTransaction implements SpiTransaction { */ protected boolean active; + protected boolean rollbackOnly; + /** * The underlying Connection. */ @@ -851,33 +853,15 @@ public class JdbcTransaction implements SpiTransaction { } } - protected void notifyQueryOnly() { - if (manager != null) { - manager.notifyOfQueryOnly(this); - } - } - /** - * Rollback, Commit or Close for query only transaction. - *

    - * For a transaction that was used for queries only we can choose to either - * rollback or just close the connection for performance. - *

    + * Rollback or Commit for query only transaction. */ protected void connectionEndForQueryOnly() { try { - switch (onQueryOnly) { - case ROLLBACK: - performRollback(); - break; - case COMMIT: - performCommit(); - break; - case CLOSE: - // valid at READ COMMITTED Isolation - break; - default: - performRollback(); + if (onQueryOnly == OnQueryOnly.COMMIT) { + performCommit(); + } else { + performRollback(); } } catch (SQLException e) { logger.error("Error when ending a query only transaction via " + onQueryOnly, e); @@ -898,37 +882,73 @@ public class JdbcTransaction implements SpiTransaction { connection.commit(); } + /** + * Batch flush, jdbc commit, trigger registered TransactionCallbacks, notify l2 cache etc. + */ + private void flushCommitAndNotify() throws SQLException { + if (batchControl != null && !batchControl.isEmpty()) { + batchControl.flush(); + } + firePreCommit(); + // only performCommit can throw an exception + performCommit(); + firePostCommit(); + notifyCommit(); + } + + /** + * Perform a commit, fire callbacks and notify l2 cache etc. + *

    + * This leaves the transaction active and expects another commit + * to occur later (which closes the underlying connection etc). + *

    + */ + @Override + public void commitAndContinue() throws RollbackException { + if (rollbackOnly) { + return; + } + if (!isActive()) { + throw new IllegalStateException(illegalStateMessage); + } + try { + flushCommitAndNotify(); + // the event has been sent to the transaction manager + // for postCommit processing (l2 cache updates etc) + // start a new transaction event + event = new TransactionEvent(); + + } catch (Exception e) { + doRollback(e); + throw new RollbackException(e); + } + } + /** * Commit the transaction. */ @Override public void commit() throws RollbackException { + if (rollbackOnly) { + rollback(); + return; + } if (!isActive()) { throw new IllegalStateException(illegalStateMessage); } - - firePreCommit(); - try { if (queryOnly) { - // can rollback or just close for performance connectionEndForQueryOnly(); } else { - // commit - if (batchControl != null && !batchControl.isEmpty()) { - batchControl.flush(); - } - performCommit(); + flushCommitAndNotify(); } } catch (Exception e) { + doRollback(e); throw new RollbackException(e); } finally { - // these will not throw an exception - firePostCommit(); deactivate(); - notifyCommit(); } } @@ -945,6 +965,32 @@ public class JdbcTransaction implements SpiTransaction { } } + /** + * Return true if the transaction is marked as rollback only. + */ + @Override + public boolean isRollbackOnly() { + return rollbackOnly; + } + + /** + * Mark the transaction as rollback only. + */ + @Override + public void setRollbackOnly() { + this.rollbackOnly = true; + } + + /** + * Perform rollback is the transaction is still active. + */ + @Override + public void rollbackIfActive() { + if (isActive()) { + rollback(null); + } + } + /** * Rollback the transaction. */ @@ -962,17 +1008,26 @@ public class JdbcTransaction implements SpiTransaction { if (!isActive()) { throw new IllegalStateException(illegalStateMessage); } + try { + doRollback(cause); + } finally { + deactivate(); + } + } + + /** + * Perform the jdbc rollback and fire any registered callbacks. + */ + private void doRollback(Throwable cause) { firePreRollback(); try { performRollback(); - - } catch (Exception ex) { + } catch (SQLException ex) { throw new PersistenceException(ex); } finally { // these will not throw an exception firePostRollback(); - deactivate(); notifyRollback(cause); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java index 954dc4b75..4d42916e4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java @@ -4,8 +4,6 @@ import com.avaje.ebean.BackgroundExecutor; import com.avaje.ebean.config.PersistBatch; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly; -import com.avaje.ebean.dbmigration.DbOffline; -import com.avaje.ebean.event.TransactionEventListener; import com.avaje.ebean.event.changelog.ChangeLogListener; import com.avaje.ebean.event.changelog.ChangeLogPrepare; import com.avaje.ebean.event.changelog.ChangeSet; @@ -14,11 +12,10 @@ import com.avaje.ebeaninternal.api.TransactionEvent; import com.avaje.ebeaninternal.api.TransactionEventTable; import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; import com.avaje.ebeaninternal.server.cluster.ClusterManager; -import com.avaje.ebeaninternal.server.core.bootup.BootupClasses; import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; -import org.avaje.datasource.DataSourcePool; import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor; import com.avaje.ebeanservice.docstore.api.DocStoreUpdates; +import org.avaje.datasource.DataSourcePool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,8 +89,6 @@ public class TransactionManager { protected final BulkEventListenerMap bulkEventListenerMap; - protected final TransactionEventListener[] transactionEventListeners; - /** * Used to prepare the change set setting user context information in the * foreground thread before logging. @@ -115,7 +110,7 @@ public class TransactionManager { * Create the TransactionManager */ public TransactionManager(boolean localL2Caching, ServerConfig config, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, - DocStoreUpdateProcessor docStoreUpdateProcessor, BeanDescriptorManager descMgr, BootupClasses bootupClasses) { + DocStoreUpdateProcessor docStoreUpdateProcessor, BeanDescriptorManager descMgr) { this.skipCacheAfterWrite = config.isSkipCacheAfterWrite(); this.localL2Caching = localL2Caching; @@ -133,9 +128,6 @@ public class TransactionManager { this.docStoreUpdateProcessor = docStoreUpdateProcessor; this.bulkEventListenerMap = new BulkEventListenerMap(config.getBulkTableEventListeners()); - List transactionEventListeners = bootupClasses.getTransactionEventListeners(); - this.transactionEventListeners = transactionEventListeners.toArray(new TransactionEventListener[transactionEventListeners.size()]); - this.prefix = ""; this.externalTransPrefix = "e"; @@ -192,51 +184,10 @@ public class TransactionManager { return OnQueryOnly.valueOf(systemPropertyValue.trim().toUpperCase()); } - if (OnQueryOnly.CLOSE.equals(dbPlatformOnQueryOnly)) { - // check for read committed isolation level - if (!isReadCommittedIsolation(ds)) { - logger.warn("Ignoring DatabasePlatform.OnQueryOnly.CLOSE as the transaction Isolation Level is not READ_COMMITTED"); - // we will just use ROLLBACK and ignore the desired optimisation - return OnQueryOnly.ROLLBACK; - } else { - // will use the OnQueryOnly.CLOSE optimisation - return OnQueryOnly.CLOSE; - } - } // default to rollback if not defined on the platform return dbPlatformOnQueryOnly == null ? OnQueryOnly.ROLLBACK : dbPlatformOnQueryOnly; } - /** - * Return true if the isolation level is read committed. - */ - protected boolean isReadCommittedIsolation(DataSource ds) { - - if (DbOffline.isSet()) { - return true; - } - Connection c = null; - try { - c = ds.getConnection(); - - int isolationLevel = c.getTransactionIsolation(); - return (isolationLevel == Connection.TRANSACTION_READ_COMMITTED); - - } catch (SQLException ex) { - String m = "Errored trying to determine the default Isolation Level"; - throw new PersistenceException(m, ex); - - } finally { - try { - if (c != null) { - c.close(); - } - } catch (SQLException ex) { - logger.error("closing connection", ex); - } - } - } - public String getServerName() { return serverName; } @@ -344,16 +295,12 @@ public class TransactionManager { public void notifyOfRollback(SpiTransaction transaction, Throwable cause) { try { - if (TXN_LOGGER.isInfoEnabled()) { + if (TXN_LOGGER.isDebugEnabled()) { String msg = transaction.getLogPrefix() + "Rollback"; if (cause != null) { msg += " error: " + formatThrowable(cause); } - TXN_LOGGER.info(msg); - } - - for (TransactionEventListener listener : transactionEventListeners) { - listener.postTransactionRollback(transaction, cause); + TXN_LOGGER.debug(msg); } } catch (Exception ex) { @@ -410,10 +357,6 @@ public class TransactionManager { postCommit.notifyLocalCache(); backgroundExecutor.execute(postCommit.backgroundNotify()); - for (TransactionEventListener listener : transactionEventListeners) { - listener.postTransactionCommit(transaction); - } - } catch (Exception ex) { logger.error("NotifyOfCommit failed. L2 Cache potentially not notified.", ex); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java index 9ced06aaf..9b4ad0f40 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java @@ -209,7 +209,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { initialiseJacksonTypes(config); if (bootupClasses != null) { - initialiseCustomScalarTypes(jsonDateTime, bootupClasses, config); + initialiseCustomScalarTypes(jsonDateTime, bootupClasses); initialiseScalarConverters(bootupClasses); initialiseCompoundTypes(bootupClasses); } @@ -682,7 +682,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { * interface and register it with this TypeManager. *

    */ - protected void initialiseCustomScalarTypes(JsonConfig.DateTime mode, BootupClasses bootupClasses, ServerConfig serverConfig) { + protected void initialiseCustomScalarTypes(JsonConfig.DateTime mode, BootupClasses bootupClasses) { ScalarTypeLongToTimestamp longToTimestamp = new ScalarTypeLongToTimestamp(mode); diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/reflect/ImmutableMetaFactory.java b/src/main/java/com/avaje/ebeaninternal/server/type/reflect/ImmutableMetaFactory.java index c40004348..096e5341f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/reflect/ImmutableMetaFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/reflect/ImmutableMetaFactory.java @@ -165,11 +165,7 @@ public class ImmutableMetaFactory { if (methods[i].getParameterTypes().length == 0) { // could be a getter String methName = methods[i].getName(); - if (methName.equals("hashCode")) { - - } else if (methName.equals("toString")) { - - } else { + if (!methName.equals("hashCode") && !methName.equals("toString")) { Class returnType = methods[i].getReturnType(); if (paramType.equals(returnType)) { return methods[i]; diff --git a/src/main/java/com/avaje/ebeanservice/docstore/api/support/DocStructure.java b/src/main/java/com/avaje/ebeanservice/docstore/api/support/DocStructure.java index adb658759..eb4866bb9 100644 --- a/src/main/java/com/avaje/ebeanservice/docstore/api/support/DocStructure.java +++ b/src/main/java/com/avaje/ebeanservice/docstore/api/support/DocStructure.java @@ -1,9 +1,8 @@ package com.avaje.ebeanservice.docstore.api.support; -import com.avaje.ebean.text.PathProperties; import com.avaje.ebean.FetchPath; +import com.avaje.ebean.text.PathProperties; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; import java.util.HashMap; diff --git a/src/test/java/com/avaje/ebean/BaseTestCase.java b/src/test/java/com/avaje/ebean/BaseTestCase.java index ab5700445..614a8824e 100644 --- a/src/test/java/com/avaje/ebean/BaseTestCase.java +++ b/src/test/java/com/avaje/ebean/BaseTestCase.java @@ -13,7 +13,7 @@ public class BaseTestCase { static { logger.debug("... preStart"); - if (!AgentLoader.loadAgentFromClasspath("avaje-ebeanorm-agent","debug=0;packages=com.avaje.tests.**,org.avaje.test.**")) { + if (!AgentLoader.loadAgentFromClasspath("ebean-agent","debug=1;packages=com.avaje.tests,org.avaje.test")) { logger.info("avaje-ebeanorm-agent not found in classpath - not dynamically loaded"); } } diff --git a/src/test/java/com/avaje/ebean/EbeanServer_deleteAllByIdTest.java b/src/test/java/com/avaje/ebean/EbeanServer_deleteAllByIdTest.java index 9e5ce361c..0e3f48ebd 100644 --- a/src/test/java/com/avaje/ebean/EbeanServer_deleteAllByIdTest.java +++ b/src/test/java/com/avaje/ebean/EbeanServer_deleteAllByIdTest.java @@ -114,8 +114,6 @@ public class EbeanServer_deleteAllByIdTest { } private EBasicVer bean(String name) { - EBasicVer bean = new EBasicVer(); - bean.setName(name); - return bean; + return new EBasicVer(name); } } \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/EbeanServer_deleteByIdTest.java b/src/test/java/com/avaje/ebean/EbeanServer_deleteByIdTest.java index a0b3bebf6..987c2c61a 100644 --- a/src/test/java/com/avaje/ebean/EbeanServer_deleteByIdTest.java +++ b/src/test/java/com/avaje/ebean/EbeanServer_deleteByIdTest.java @@ -85,8 +85,6 @@ public class EbeanServer_deleteByIdTest { } private EBasicVer bean(String name) { - EBasicVer bean = new EBasicVer(); - bean.setName(name); - return bean; + return new EBasicVer(name); } } \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/EbeanServer_deleteTest.java b/src/test/java/com/avaje/ebean/EbeanServer_deleteTest.java index 82d377dc6..48d3ce3ba 100644 --- a/src/test/java/com/avaje/ebean/EbeanServer_deleteTest.java +++ b/src/test/java/com/avaje/ebean/EbeanServer_deleteTest.java @@ -48,8 +48,6 @@ public class EbeanServer_deleteTest { } private EBasicVer bean(String name) { - EBasicVer bean = new EBasicVer(); - bean.setName(name); - return bean; + return new EBasicVer(name); } } \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/EbeanServer_saveAllTest.java b/src/test/java/com/avaje/ebean/EbeanServer_saveAllTest.java index 850a420a0..4cdedd009 100644 --- a/src/test/java/com/avaje/ebean/EbeanServer_saveAllTest.java +++ b/src/test/java/com/avaje/ebean/EbeanServer_saveAllTest.java @@ -123,8 +123,6 @@ public class EbeanServer_saveAllTest extends BaseTestCase { } private EBasicVer bean(String name) { - EBasicVer bean = new EBasicVer(); - bean.setName(name); - return bean; + return new EBasicVer(name); } } \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java b/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java index c889510e0..5aba15b8a 100644 --- a/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java +++ b/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java @@ -24,8 +24,7 @@ public class BeanPersistControllerTest { EbeanServer ebeanServer = getEbeanServer(continuePersistingAdapter); - EBasicVer bean = new EBasicVer(); - bean.setName("testController"); + EBasicVer bean = new EBasicVer("testController"); ebeanServer.save(bean); assertThat(continuePersistingAdapter.methodsCalled).hasSize(2); @@ -49,8 +48,7 @@ public class BeanPersistControllerTest { EbeanServer ebeanServer = getEbeanServer(stopPersistingAdapter); - EBasicVer bean = new EBasicVer(); - bean.setName("testController"); + EBasicVer bean = new EBasicVer("testController"); ebeanServer.save(bean); assertThat(stopPersistingAdapter.methodsCalled).hasSize(1); diff --git a/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java b/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java index 2372e3c1e..1e782cc67 100644 --- a/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java +++ b/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java @@ -24,8 +24,7 @@ public class BeanPostLoadTest extends BaseTestCase { EbeanServer ebeanServer = getEbeanServer(); - EBasicVer bean = new EBasicVer(); - bean.setName("testPostLoad"); + EBasicVer bean = new EBasicVer("testPostLoad"); bean.setDescription("someDescription"); bean.setOther("other"); diff --git a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java index 97c93f13f..a05d5fc98 100644 --- a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java +++ b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java @@ -186,7 +186,7 @@ public class TDSpiEbeanServer implements SpiEbeanServer { } @Override - public List findIdsWithCopy(Query query, Transaction t) { + public List findIdsWithCopy(Query query, Transaction t) { return null; } @@ -455,13 +455,23 @@ public class TDSpiEbeanServer implements SpiEbeanServer { return null; } + @Override + public int findCount(Query query, Transaction transaction) { + return 0; + } + @Override public int findRowCount(Query query, Transaction transaction) { return 0; } @Override - public List findIds(Query query, Transaction transaction) { + public List findIds(Query query, Transaction transaction) { + return null; + } + + @Override + public QueryIterator findIterate(Query query, Transaction transaction) { return null; } @@ -480,6 +490,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer { return null; } + @Override + public FutureRowCount findFutureCount(Query query, Transaction transaction) { + return null; + } + @Override public FutureRowCount findFutureRowCount(Query query, Transaction transaction) { return null; @@ -506,7 +521,12 @@ public class TDSpiEbeanServer implements SpiEbeanServer { } @Override - public Map findMap(Query query, Transaction transaction) { + public Map findMap(Query query, Transaction transaction) { + return null; + } + + @Override + public List findSingleAttributeList(Query query, Transaction transaction) { return null; } diff --git a/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java b/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java index d61015f1e..d45ee0f5e 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java @@ -153,6 +153,20 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase { assertThat(detail.getFetchPaths()).containsExactly("customer"); } + @Test + public void testJoinOrder_when_queryFetch_expect_getFetchPaths_doesNotIncludeQueryJoin_via_fetchQuery() { + + Query query = Ebean.find(Order.class) + .select("status, orderDate") + .fetch("customer", "name") + .fetchQuery("details"); + + OrmQueryRequest queryRequest = queryRequest(query); + OrmQueryDetail detail = queryRequest.getQuery().getDetail(); + + assertThat(detail.getFetchPaths()).containsExactly("customer"); + } + @Test public void testJoinOrder_when_lazyFetch_expect_getFetchPaths_doesNotIncludeQueryJoin() { @@ -167,6 +181,20 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase { assertThat(detail.getFetchPaths()).containsExactly("customer"); } + @Test + public void testJoinOrder_when_lazyFetch_expect_getFetchPaths_doesNotIncludeQueryJoin_via_fetchLazy() { + + Query query = Ebean.find(Order.class) + .select("status, orderDate") + .fetch("customer", "name") + .fetchLazy("details"); + + OrmQueryRequest queryRequest = queryRequest(query); + OrmQueryDetail detail = queryRequest.getQuery().getDetail(); + + assertThat(detail.getFetchPaths()).containsExactly("customer"); + } + @Test public void testJoinOrder_when_lazyFetchAndHasChildren_expect_getFetchPaths_doesNotIncludeJoinOrChild() { diff --git a/src/test/java/com/avaje/ebeaninternal/server/lib/sql/TestDataSourceMaxWithEntity.java b/src/test/java/com/avaje/ebeaninternal/server/lib/sql/TestDataSourceMaxWithEntity.java index 8abda7611..ad97b3b5e 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/lib/sql/TestDataSourceMaxWithEntity.java +++ b/src/test/java/com/avaje/ebeaninternal/server/lib/sql/TestDataSourceMaxWithEntity.java @@ -54,7 +54,7 @@ public class TestDataSourceMaxWithEntity extends BaseTestCase { public void run() { - server.find(Customer.class).findRowCount(); + server.find(Customer.class).findCount(); try { System.out.println(position+" sleep " + sleepMillis); Thread.sleep(sleepMillis); diff --git a/src/test/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContextTest.java b/src/test/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContextTest.java index 006544217..151d6a5da 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContextTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContextTest.java @@ -49,6 +49,20 @@ public class DLoadContextTest extends BaseTestCase { assertThat(customer.secondaryBatchSize).isEqualTo(100); } + @Test + public void construct_when_fetchQuery_expect_100_batchSize_viaFetchQuery() { + + OrmQueryRequest queryRequest = queryRequest(query().fetchQuery("customer")); + queryRequest.initTransIfRequired(); + queryRequest.endTransIfRequired(); + + DLoadContext graphContext = (DLoadContext)queryRequest.getGraphContext(); + DLoadBeanContext customer = graphContext.getBeanContext("customer"); + + assertThat(customer.firstBatchSize).isEqualTo(100); + assertThat(customer.secondaryBatchSize).isEqualTo(100); + } + @Test public void construct_when_fetchQuery50_expect_50_batchSize() { @@ -77,4 +91,19 @@ public class DLoadContextTest extends BaseTestCase { assertThat(customer.secondaryBatchSize).isEqualTo(5); } + @Test + public void construct_when_fetch_expect_100_100_batchSize() { + + // the fetch is converted to a query join due to the maxRows + OrmQueryRequest queryRequest = queryRequest(query().fetch("details").setMaxRows(100)) ; + queryRequest.initTransIfRequired(); + queryRequest.endTransIfRequired(); + + DLoadContext graphContext = (DLoadContext)queryRequest.getGraphContext(); + DLoadManyContext details = graphContext.getManyContext("details"); + + assertThat(details.firstBatchSize).isEqualTo(100); + assertThat(details.secondaryBatchSize).isEqualTo(100); + } + } \ No newline at end of file diff --git a/src/test/java/com/avaje/ebeaninternal/server/query/TestFutureRowCountErrorHandling.java b/src/test/java/com/avaje/ebeaninternal/server/query/TestFutureRowCountErrorHandling.java index 278466bec..233bd873b 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/query/TestFutureRowCountErrorHandling.java +++ b/src/test/java/com/avaje/ebeaninternal/server/query/TestFutureRowCountErrorHandling.java @@ -29,7 +29,7 @@ public class TestFutureRowCountErrorHandling extends BaseTestCase { .where().eq("doesNotExist", "this will fail") .query(); - FutureRowCount futureRowCount = server.findFutureRowCount(query, null); + FutureRowCount futureRowCount = server.findFutureCount(query, null); QueryFutureRowCount internalRowCount = (QueryFutureRowCount)futureRowCount; Transaction t = internalRowCount.getTransaction(); diff --git a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPlanKeyTest.java b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPlanKeyTest.java index f9f4fdb3e..7af9dfd88 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPlanKeyTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPlanKeyTest.java @@ -30,7 +30,7 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest { @Test public void equals_when_diffTableJoinNull() { - TableJoin tableJoin = tableJoin("id", "customer_id"); + TableJoin tableJoin = tableJoin("table", "id", "customer_id"); OrmQueryPlanKey key1 = new OrmQueryPlanKey(tableJoin, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null); OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null); @@ -41,8 +41,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest { @Test public void equals_when_diffTableJoin() { - TableJoin tableJoin1 = tableJoin("id", "customer_id"); - TableJoin tableJoin2 = tableJoin("id", "other_customer_id"); + TableJoin tableJoin1 = tableJoin("one", "id", "customer_id"); + TableJoin tableJoin2 = tableJoin("two", "id", "customer_id"); OrmQueryPlanKey key1 = new OrmQueryPlanKey(tableJoin1, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null); OrmQueryPlanKey key2 = new OrmQueryPlanKey(tableJoin2, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null); @@ -53,8 +53,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest { @Test public void equals_when_sameTableJoin() { - TableJoin tableJoin1 = tableJoin("id", "customer_id"); - TableJoin tableJoin2 = tableJoin("id", "customer_id"); + TableJoin tableJoin1 = tableJoin("one", "id", "customer_id"); + TableJoin tableJoin2 = tableJoin("one", "id", "customer_id"); OrmQueryPlanKey key1 = new OrmQueryPlanKey(tableJoin1, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null); OrmQueryPlanKey key2 = new OrmQueryPlanKey(tableJoin2, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null); @@ -63,9 +63,9 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest { } @NotNull - private TableJoin tableJoin(String col1, String col2) { + private TableJoin tableJoin(String table, String col1, String col2) { DeployTableJoin deploy = new DeployTableJoin(); - deploy.setTable("myTable"); + deploy.setTable(table); deploy.addJoinColumn(new DeployTableJoinColumn(col1, col2)); return new TableJoin(deploy); } diff --git a/src/test/java/com/avaje/tests/basic/MainDbBoolean.java b/src/test/java/com/avaje/tests/basic/MainDbBoolean.java index a4929a481..e3c2ab758 100644 --- a/src/test/java/com/avaje/tests/basic/MainDbBoolean.java +++ b/src/test/java/com/avaje/tests/basic/MainDbBoolean.java @@ -147,7 +147,7 @@ public class MainDbBoolean { .setAutoTune(false) .order("id"); - int rc = query.findRowCount(); + int rc = query.findCount(); Assert.assertTrue(rc > 0); diff --git a/src/test/java/com/avaje/tests/basic/TestFetchId.java b/src/test/java/com/avaje/tests/basic/TestFetchId.java index 06b046972..2e0908bfb 100644 --- a/src/test/java/com/avaje/tests/basic/TestFetchId.java +++ b/src/test/java/com/avaje/tests/basic/TestFetchId.java @@ -1,17 +1,17 @@ package com.avaje.tests.basic; -import java.util.List; -import java.util.concurrent.ExecutionException; - -import org.junit.Assert; -import org.junit.Test; - import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.FutureIds; import com.avaje.ebean.Query; import com.avaje.tests.model.basic.Order; import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; public class TestFetchId extends BaseTestCase { @@ -28,19 +28,12 @@ public class TestFetchId extends BaseTestCase { .query(); List ids = Ebean.getServer(null).findIds(query, null); + assertThat(ids).isNotEmpty(); FutureIds futureIds = Ebean.getServer(null).findFutureIds(query,null); - // this list is likely empty at this point and - // will get populated in the background - List partial = futureIds.getPartialIds(); - - // this is likely 0 or a small number - // wait for all the id's to be fetched List idList = futureIds.get(); - Assert.assertTrue("same instance", partial == idList); - - Assert.assertTrue("sz > 0", !ids.isEmpty()); + assertThat(idList).isNotEmpty(); } } diff --git a/src/test/java/com/avaje/tests/basic/TestIUDVanilla.java b/src/test/java/com/avaje/tests/basic/TestIUDVanilla.java index 0cb0b2b7d..2b9d1e3d8 100644 --- a/src/test/java/com/avaje/tests/basic/TestIUDVanilla.java +++ b/src/test/java/com/avaje/tests/basic/TestIUDVanilla.java @@ -14,8 +14,7 @@ public class TestIUDVanilla extends BaseTestCase { @Test public void test() { - EBasicVer e0 = new EBasicVer(); - e0.setName("vanilla"); + EBasicVer e0 = new EBasicVer("vanilla"); Ebean.save(e0); @@ -39,9 +38,8 @@ public class TestIUDVanilla extends BaseTestCase { e2.setName("forcedUpdate"); Ebean.update(e2); - EBasicVer e3 = new EBasicVer(); + EBasicVer e3 = new EBasicVer("ModNoOCC"); e3.setId(e0.getId()); - e3.setName("ModNoOCC"); Ebean.update(e3); diff --git a/src/test/java/com/avaje/tests/basic/TestLazyLoadInCache.java b/src/test/java/com/avaje/tests/basic/TestLazyLoadInCache.java index 47c9d5a04..f1cb4a1c5 100644 --- a/src/test/java/com/avaje/tests/basic/TestLazyLoadInCache.java +++ b/src/test/java/com/avaje/tests/basic/TestLazyLoadInCache.java @@ -22,7 +22,7 @@ public class TestLazyLoadInCache extends BaseTestCase { ResetBasicData.reset(); - Map map = Ebean.find(Customer.class) + Map map = Ebean.find(Customer.class) .select("id, name") .setLoadBeanCache(true) .setReadOnly(true) diff --git a/src/test/java/com/avaje/tests/basic/TestLoadBeanCache.java b/src/test/java/com/avaje/tests/basic/TestLoadBeanCache.java index e688df5b1..6e950ee62 100644 --- a/src/test/java/com/avaje/tests/basic/TestLoadBeanCache.java +++ b/src/test/java/com/avaje/tests/basic/TestLoadBeanCache.java @@ -17,7 +17,7 @@ public class TestLoadBeanCache extends BaseTestCase { ResetBasicData.reset(); - Map map = Ebean.find(Country.class) + Map map = Ebean.find(Country.class) .setLoadBeanCache(true) .setUseQueryCache(true) .setReadOnly(true) diff --git a/src/test/java/com/avaje/tests/basic/TestLogTransLogOnError.java b/src/test/java/com/avaje/tests/basic/TestLogTransLogOnError.java index 9f53c0f65..58a8368d5 100644 --- a/src/test/java/com/avaje/tests/basic/TestLogTransLogOnError.java +++ b/src/test/java/com/avaje/tests/basic/TestLogTransLogOnError.java @@ -25,9 +25,8 @@ public class TestLogTransLogOnError extends BaseTestCase { Ebean.find(Customer.class).findList(); Ebean.find(Order.class).where().gt("id", 1).findList(); - EBasicVer newBean = new EBasicVer(); + EBasicVer newBean = new EBasicVer("aName"); newBean.setDescription("something"); - newBean.setName("aName"); // Ebean.save(newBean); @@ -55,13 +54,12 @@ public class TestLogTransLogOnError extends BaseTestCase { try { Ebean.find(Customer.class).findList(); - EBasicVer newBean = new EBasicVer(); + EBasicVer newBean = new EBasicVer("aName"); newBean .setDescription("something sdfjksdjflsjdflsjdflksjdfkjd fsjdfkjsdkfjsdkfjskdjfskjdf" + " sjdf sdjflksjdfkjsdlfkjsdkfjs ksjdfksjdlfjsldf something sdfjksdjflsjdflsjdflksjdfkjd" + "fsjdfkjsdkfjsdkfjskdjfskjdf sjdf sdjflksjdfkjsdlfkjsdkfjs ksjdfksjdlfjsldf something s" + "dfjksdjflsjdflsjdflksjdfkjd fsjdfkjsdkfjsdkfjskdjfskjdf sjdf sdjflksjdfkjsdlfkjsdkfjs "); - newBean.setName("aName"); // t.log("--- next insert should error"); Ebean.save(newBean); diff --git a/src/test/java/com/avaje/tests/basic/event/MyTestTransactionEventListener.java b/src/test/java/com/avaje/tests/basic/event/MyTestTransactionEventListener.java deleted file mode 100644 index abe27803c..000000000 --- a/src/test/java/com/avaje/tests/basic/event/MyTestTransactionEventListener.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.avaje.tests.basic.event; - -import com.avaje.ebean.Transaction; -import com.avaje.ebean.event.TransactionEventListener; - -public class MyTestTransactionEventListener implements TransactionEventListener { - private volatile static boolean doTest = false; - - private static Transaction lastCommitted; - private static Transaction lastRollbacked; - - public void postTransactionCommit(Transaction tx) { - if (!doTest) { - return; - } - - lastCommitted = tx; - } - - public void postTransactionRollback(Transaction tx, Throwable cause) { - if (!doTest) { - return; - } - - lastRollbacked = tx; - } - - public static void setDoTest(boolean doTest) { - MyTestTransactionEventListener.doTest = doTest; - - // reset what we've recorded so far - lastCommitted = null; - lastRollbacked = null; - } - - public static Transaction getLastCommitted() { - return lastCommitted; - } - - public static Transaction getLastRollbacked() { - return lastRollbacked; - } -} diff --git a/src/test/java/com/avaje/tests/basic/event/TestTransactionEvent.java b/src/test/java/com/avaje/tests/basic/event/TestTransactionEvent.java deleted file mode 100644 index 6a36c862e..000000000 --- a/src/test/java/com/avaje/tests/basic/event/TestTransactionEvent.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.avaje.tests.basic.event; - -import junit.framework.TestCase; - -import com.avaje.ebean.Ebean; -import com.avaje.ebean.Transaction; -import com.avaje.tests.model.basic.TWithPreInsert; - -public class TestTransactionEvent extends TestCase { - - @Override - protected void tearDown() throws Exception { - MyTestTransactionEventListener.setDoTest(false); - } - - @Override - protected void setUp() throws Exception { - MyTestTransactionEventListener.setDoTest(true); - } - - public void test() { - - assertNull(MyTestTransactionEventListener.getLastCommitted()); - assertNull(MyTestTransactionEventListener.getLastRollbacked()); - - final Object myUserObject = new Object(); - - Transaction tx = Ebean.beginTransaction(); - tx.putUserObject("myUserObject", myUserObject); - - TWithPreInsert e = new TWithPreInsert(); - e.setTitle("Mister Transaction1"); - Ebean.save(e); - - tx.commit(); - - assertNotNull(MyTestTransactionEventListener.getLastCommitted()); - assertNotNull(MyTestTransactionEventListener.getLastCommitted().getUserObject("myUserObject")); - assertSame(MyTestTransactionEventListener.getLastCommitted().getUserObject("myUserObject"), myUserObject); - assertNull(MyTestTransactionEventListener.getLastRollbacked()); - - Transaction tx2 = Ebean.beginTransaction(); - tx2.putUserObject("myUserObject2", myUserObject); - - TWithPreInsert e2 = new TWithPreInsert(); - e2.setTitle("Mister Transaction2"); - Ebean.save(e2); - - tx2.rollback(); - - assertNotNull(MyTestTransactionEventListener.getLastCommitted()); - assertNotNull(MyTestTransactionEventListener.getLastRollbacked()); - - assertNotSame(MyTestTransactionEventListener.getLastCommitted(), MyTestTransactionEventListener.getLastRollbacked()); - - assertNotNull(MyTestTransactionEventListener.getLastCommitted().getUserObject("myUserObject")); - assertSame(MyTestTransactionEventListener.getLastCommitted().getUserObject("myUserObject"), myUserObject); - - assertNotNull(MyTestTransactionEventListener.getLastRollbacked().getUserObject("myUserObject2")); - assertSame(MyTestTransactionEventListener.getLastRollbacked().getUserObject("myUserObject2"), myUserObject); - } -} diff --git a/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertFlush.java b/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertFlush.java new file mode 100644 index 000000000..8cbd0f517 --- /dev/null +++ b/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertFlush.java @@ -0,0 +1,117 @@ +package com.avaje.tests.batchinsert; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.annotation.Transactional; +import com.avaje.ebean.config.PersistBatch; +import com.avaje.tests.model.basic.EBasicVer; +import org.junit.Test; + +import java.sql.Timestamp; + +import static org.junit.Assert.assertNotNull; + +public class TestBatchInsertFlush extends BaseTestCase { + + @Test + @Transactional(batch = PersistBatch.ALL) + public void transactional_flushOnGetId() { + + EbeanServer server = Ebean.getDefaultServer(); + + EBasicVer b1 = new EBasicVer("b1"); + server.save(b1); + + EBasicVer b2 = new EBasicVer("b2"); + server.save(b2); + + Integer id = b1.getId(); + assertNotNull(id); + EBasicVer b3 = new EBasicVer("b3"); + server.save(b3); + } + + @Test + public void testFlushOnGetId() { + + EbeanServer server = Ebean.getDefaultServer(); + Transaction txn = server.beginTransaction(); + try { + txn.setBatch(PersistBatch.ALL); + + EBasicVer b1 = new EBasicVer("b1"); + server.save(b1, txn); + + EBasicVer b2 = new EBasicVer("b2"); + server.save(b2, txn); + + Integer id = b1.getId(); + assertNotNull(id); + + EBasicVer b3 = new EBasicVer("b3"); + server.save(b3, txn); + + txn.commit(); + + } finally { + txn.end(); + } + } + + @Test + public void testFlushOnGetProperty() { + + EbeanServer server = Ebean.getDefaultServer(); + Transaction txn = server.beginTransaction(); + try { + txn.setBatch(PersistBatch.ALL); + + EBasicVer b1 = new EBasicVer("b1"); + server.save(b1, txn); + + EBasicVer b2 = new EBasicVer("b2"); + server.save(b2, txn); + + // flush here + Timestamp lastUpdate = b1.getLastUpdate(); + assertNotNull(lastUpdate); + + EBasicVer b3 = new EBasicVer("b3"); + server.save(b3, txn); + + txn.commit(); + + } finally { + txn.end(); + } + } + + @Test + public void testFlushOnSetProperty() { + + EbeanServer server = Ebean.getDefaultServer(); + Transaction txn = server.beginTransaction(); + try { + txn.setBatch(PersistBatch.ALL); + + EBasicVer b1 = new EBasicVer("b1"); + server.save(b1, txn); + + EBasicVer b2 = new EBasicVer("b2"); + server.save(b2, txn); + + // flush here + b1.setDescription("modify"); + + EBasicVer b3 = new EBasicVer("b3"); + server.save(b3, txn); + + txn.commit(); + + } finally { + txn.end(); + } + } +} diff --git a/src/test/java/com/avaje/tests/batchload/TestLazyLoadEmptyCollection.java b/src/test/java/com/avaje/tests/batchload/TestLazyLoadEmptyCollection.java index 9e72be21a..6a5196c50 100644 --- a/src/test/java/com/avaje/tests/batchload/TestLazyLoadEmptyCollection.java +++ b/src/test/java/com/avaje/tests/batchload/TestLazyLoadEmptyCollection.java @@ -26,8 +26,10 @@ public class TestLazyLoadEmptyCollection extends BaseTestCase { Ebean.save(c); - List list = Ebean.find(Customer.class).fetch("contacts", new FetchConfig().query(0)) - .fetch("contacts.notes", new FetchConfig().query(100)).findList(); + List list = Ebean.find(Customer.class) + .fetch("contacts", new FetchConfig().query(0)) + .fetch("contacts.notes", new FetchConfig().query(100)) + .findList(); for (Customer customer : list) { List contacts = customer.getContacts(); diff --git a/src/test/java/com/avaje/tests/batchload/TestSecondaryQueries.java b/src/test/java/com/avaje/tests/batchload/TestSecondaryQueries.java index 577a290a3..5651f484d 100644 --- a/src/test/java/com/avaje/tests/batchload/TestSecondaryQueries.java +++ b/src/test/java/com/avaje/tests/batchload/TestSecondaryQueries.java @@ -2,6 +2,7 @@ package com.avaje.tests.batchload; import java.util.List; +import org.avaje.ebeantest.LoggedSqlCollector; import org.junit.Assert; import org.junit.Test; @@ -14,8 +15,60 @@ import com.avaje.tests.model.basic.Customer; import com.avaje.tests.model.basic.Order; import com.avaje.tests.model.basic.ResetBasicData; +import static org.assertj.core.api.Assertions.assertThat; + public class TestSecondaryQueries extends BaseTestCase { + @Test + public void fetchQuery() { + + ResetBasicData.reset(); + + LoggedSqlCollector.start(); + + Ebean.find(Order.class) + .select("status") + .fetchQuery("customer", "name") + .findList(); + + List sql = LoggedSqlCollector.stop(); + + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("select t0.id c0, t0.status c1, t0.kcustomer_id c2 from o_order t0"); + assertThat(sql.get(1)).contains("select t0.id c0, t0.name c1 from o_customer t0 where t0.id in"); + } + + @Test + public void fetchLazy() { + + ResetBasicData.reset(); + + LoggedSqlCollector.start(); + + List orders = Ebean.find(Order.class) + .select("status") + .fetchLazy("customer", "name") + .setMaxRows(10) + .setUseCache(false) + .findList(); + + List sql = LoggedSqlCollector.stop(); + + assertThat(sql).hasSize(1); + assertThat(sql.get(0)).contains("select t0.id c0, t0.status c1, t0.kcustomer_id c2 from o_order t0"); + + LoggedSqlCollector.start(); + + // invoke lazy loading + for (Order order : orders) { + order.getCustomer().getName(); + } + + sql = LoggedSqlCollector.stop(); + assertThat(sql).hasSize(1); + assertThat(sql.get(0)).contains("select t0.id c0, t0.name c1 from o_customer t0 where t0.id in"); + } + @Test public void testSecQueryOneToMany() { diff --git a/src/test/java/com/avaje/tests/cache/TestQueryCacheInsert.java b/src/test/java/com/avaje/tests/cache/TestQueryCacheInsert.java index 82ad9f308..966effe01 100644 --- a/src/test/java/com/avaje/tests/cache/TestQueryCacheInsert.java +++ b/src/test/java/com/avaje/tests/cache/TestQueryCacheInsert.java @@ -17,12 +17,12 @@ public class TestQueryCacheInsert extends BaseTestCase { EbeanServer server = Ebean.getServer(null); - EBasicVer account = new EBasicVer(); + EBasicVer account = new EBasicVer("junk"); server.save(account); List alist0 = server.find(EBasicVer.class).setUseQueryCache(true).findList(); - EBasicVer a2 = new EBasicVer(); + EBasicVer a2 = new EBasicVer("junk2"); server.save(a2); awaitL2Cache(); diff --git a/src/test/java/com/avaje/tests/delete/TestDeleteWithoutOptimisticLocking.java b/src/test/java/com/avaje/tests/delete/TestDeleteWithoutOptimisticLocking.java index 8487789db..51d0981b4 100644 --- a/src/test/java/com/avaje/tests/delete/TestDeleteWithoutOptimisticLocking.java +++ b/src/test/java/com/avaje/tests/delete/TestDeleteWithoutOptimisticLocking.java @@ -31,8 +31,7 @@ public class TestDeleteWithoutOptimisticLocking extends BaseTestCase { @Test public void testSimpleBeanDelete_existingBean_returnsTrue() { - EBasicVer basic = new EBasicVer(); - basic.setName("DelTest"); + EBasicVer basic = new EBasicVer("DelTest"); Ebean.save(basic); EBasicVer basicRef = Ebean.getReference(EBasicVer.class, basic.getId()); @@ -43,8 +42,7 @@ public class TestDeleteWithoutOptimisticLocking extends BaseTestCase { @Test public void testSimpleBeanDelete_existingBeanWithJdbcBatch_returnsTrue() { - EBasicVer basic = new EBasicVer(); - basic.setName("DelTestBatch"); + EBasicVer basic = new EBasicVer("DelTestBatch"); Ebean.save(basic); EbeanServer server = Ebean.getDefaultServer(); diff --git a/src/test/java/com/avaje/tests/draftable/LinkQueryPublishTest.java b/src/test/java/com/avaje/tests/draftable/LinkQueryPublishTest.java index 03d885b35..762068b91 100644 --- a/src/test/java/com/avaje/tests/draftable/LinkQueryPublishTest.java +++ b/src/test/java/com/avaje/tests/draftable/LinkQueryPublishTest.java @@ -39,7 +39,7 @@ public class LinkQueryPublishTest { .setMaxRows(10) .findPagedList(); - assertThat(pagedList.getTotalRowCount()).isEqualTo(3); + assertThat(pagedList.getTotalCount()).isEqualTo(3); assertThat(pagedList.getList()).hasSize(3); diff --git a/src/test/java/com/avaje/tests/iud/TestInsertQueryUpdate.java b/src/test/java/com/avaje/tests/iud/TestInsertQueryUpdate.java index 58fa4375c..e4eaf5894 100644 --- a/src/test/java/com/avaje/tests/iud/TestInsertQueryUpdate.java +++ b/src/test/java/com/avaje/tests/iud/TestInsertQueryUpdate.java @@ -15,8 +15,7 @@ public class TestInsertQueryUpdate extends BaseTestCase { @Test public void test() { - EBasicVer e0 = new EBasicVer(); - e0.setName("name0"); + EBasicVer e0 = new EBasicVer("name0"); e0.setDescription("desc0"); Ebean.save(e0); diff --git a/src/test/java/com/avaje/tests/iud/TestInsertUpdateTrans.java b/src/test/java/com/avaje/tests/iud/TestInsertUpdateTrans.java index 8d50beba1..6c71a44ab 100644 --- a/src/test/java/com/avaje/tests/iud/TestInsertUpdateTrans.java +++ b/src/test/java/com/avaje/tests/iud/TestInsertUpdateTrans.java @@ -15,8 +15,7 @@ public class TestInsertUpdateTrans extends BaseTestCase { Ebean.beginTransaction(); try { - EBasicVer e0 = new EBasicVer(); - e0.setName("onInsert"); + EBasicVer e0 = new EBasicVer("onInsert"); e0.setDescription("something"); Ebean.save(e0); diff --git a/src/test/java/com/avaje/tests/m2m/TestM2MDeleteWithCascade.java b/src/test/java/com/avaje/tests/m2m/TestM2MDeleteWithCascade.java index ca13a9710..6525886ba 100644 --- a/src/test/java/com/avaje/tests/m2m/TestM2MDeleteWithCascade.java +++ b/src/test/java/com/avaje/tests/m2m/TestM2MDeleteWithCascade.java @@ -39,13 +39,13 @@ public class TestM2MDeleteWithCascade extends BaseTestCase { List roleIds = new ArrayList(); Collections.addAll(roleIds, r0.getRoleid(), r1.getRoleid()); - int rc = Ebean.find(MRole.class).where().idIn(roleIds).findRowCount(); + int rc = Ebean.find(MRole.class).where().idIn(roleIds).findCount(); Assert.assertEquals("roles not deleted", 2, rc); Ebean.deleteAll(roles); - rc = Ebean.find(MRole.class).where().idIn(roleIds).findRowCount(); + rc = Ebean.find(MRole.class).where().idIn(roleIds).findCount(); Assert.assertEquals("roles deleted now", 0, rc); } diff --git a/src/test/java/com/avaje/tests/model/basic/EBasicVer.java b/src/test/java/com/avaje/tests/model/basic/EBasicVer.java index 95975f337..7c63183ef 100644 --- a/src/test/java/com/avaje/tests/model/basic/EBasicVer.java +++ b/src/test/java/com/avaje/tests/model/basic/EBasicVer.java @@ -25,6 +25,10 @@ public class EBasicVer { @Version Timestamp lastUpdate; + public EBasicVer(String name) { + this.name = name; + } + public Integer getId() { return id; } diff --git a/src/test/java/com/avaje/tests/model/basic/ResetBasicData.java b/src/test/java/com/avaje/tests/model/basic/ResetBasicData.java index db13c5fee..b4d3bd35d 100644 --- a/src/test/java/com/avaje/tests/model/basic/ResetBasicData.java +++ b/src/test/java/com/avaje/tests/model/basic/ResetBasicData.java @@ -25,7 +25,7 @@ public class ResetBasicData { server.execute(new TxRunnable() { public void run() { - if (server.find(Product.class).findRowCount() > 0) { + if (server.find(Product.class).findCount() > 0) { // we can't really delete this base data as // the test rely on the products being in there return; @@ -72,7 +72,7 @@ public class ResetBasicData { public void insertCountries() { - if (server.find(Country.class).findRowCount() > 0) { + if (server.find(Country.class).findCount() > 0) { return; } @@ -94,7 +94,7 @@ public class ResetBasicData { public void insertProducts() { - if (server.find(Product.class).findRowCount() > 0) { + if (server.find(Product.class).findCount() > 0) { return; } server.execute(new TxRunnable() { diff --git a/src/test/java/com/avaje/tests/model/basic/xtra/TestInsertBatchThenUpdate.java b/src/test/java/com/avaje/tests/model/basic/xtra/TestInsertBatchThenUpdate.java index 617cc1e95..a9c22e64c 100644 --- a/src/test/java/com/avaje/tests/model/basic/xtra/TestInsertBatchThenUpdate.java +++ b/src/test/java/com/avaje/tests/model/basic/xtra/TestInsertBatchThenUpdate.java @@ -10,6 +10,7 @@ import org.junit.Test; import java.util.ArrayList; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; public class TestInsertBatchThenUpdate extends BaseTestCase { @@ -24,6 +25,8 @@ public class TestInsertBatchThenUpdate extends BaseTestCase { try { txn.setBatch(PersistBatch.ALL); + LoggedSqlCollector.start(); + EdParent parent = new EdParent(); parent.setName("MyComputer"); @@ -36,21 +39,17 @@ public class TestInsertBatchThenUpdate extends BaseTestCase { Ebean.save(parent); - // nothing flushed yet - List loggedSql0 = LoggedSqlCollector.start(); - assertEquals(0, loggedSql0.size()); - parent.setName("MyDesk"); Ebean.save(parent); - // nothing flushed yet - assertEquals(0, LoggedSqlCollector.start().size()); - Ebean.commitTransaction(); // insert statements for EdExtendedParent - List loggedSql2 = LoggedSqlCollector.start(); - assertEquals(2, loggedSql2.size()); + List loggedSql = LoggedSqlCollector.stop(); + assertEquals(3, loggedSql.size()); + assertThat(loggedSql.get(0)).contains("insert into td_parent"); + assertThat(loggedSql.get(1)).contains("insert into td_child "); + assertThat(loggedSql.get(2)).contains("update td_parent set parent_name=? where parent_id=?"); } finally { Ebean.endTransaction(); diff --git a/src/test/java/com/avaje/tests/model/m2m/MnyB.java b/src/test/java/com/avaje/tests/model/m2m/MnyB.java index 6009cb81d..371d2f994 100644 --- a/src/test/java/com/avaje/tests/model/m2m/MnyB.java +++ b/src/test/java/com/avaje/tests/model/m2m/MnyB.java @@ -19,6 +19,13 @@ public class MnyB extends BaseModel { @ManyToMany(cascade = CascadeType.REMOVE) List cs; + public MnyB(String name) { + this.name = name; + } + + public MnyB() { + } + public String getName() { return name; } diff --git a/src/test/java/com/avaje/tests/model/noid/TestInsertNoIdBean.java b/src/test/java/com/avaje/tests/model/noid/TestInsertNoIdBean.java index 18ff9e86d..af954a29e 100644 --- a/src/test/java/com/avaje/tests/model/noid/TestInsertNoIdBean.java +++ b/src/test/java/com/avaje/tests/model/noid/TestInsertNoIdBean.java @@ -18,7 +18,7 @@ public class TestInsertNoIdBean extends BaseTestCase { Ebean.save(bean); - int rowCount = Ebean.find(NoIdBean.class).findRowCount(); + int rowCount = Ebean.find(NoIdBean.class).findCount(); assertTrue("rowCount:"+rowCount, rowCount > 0); diff --git a/src/test/java/com/avaje/tests/model/selfref/TestTextJsonSelfRef.java b/src/test/java/com/avaje/tests/model/selfref/TestTextJsonSelfRef.java index 2e33211b7..4c928bbf7 100644 --- a/src/test/java/com/avaje/tests/model/selfref/TestTextJsonSelfRef.java +++ b/src/test/java/com/avaje/tests/model/selfref/TestTextJsonSelfRef.java @@ -18,7 +18,7 @@ public class TestTextJsonSelfRef extends BaseTestCase { Ebean.execute(new TxRunnable() { public void run() { - if (Ebean.find(SelfRefCustomer.class).findRowCount() == 0) { + if (Ebean.find(SelfRefCustomer.class).findCount() == 0) { SelfRefCustomer c1 = new SelfRefCustomer(); c1.setName("Foo"); c1.setReferredBy(c1); diff --git a/src/test/java/com/avaje/tests/persistencecontext/TestPersistenceContextQueryScope.java b/src/test/java/com/avaje/tests/persistencecontext/TestPersistenceContextQueryScope.java index ee036322b..1a2cd85ce 100644 --- a/src/test/java/com/avaje/tests/persistencecontext/TestPersistenceContextQueryScope.java +++ b/src/test/java/com/avaje/tests/persistencecontext/TestPersistenceContextQueryScope.java @@ -16,8 +16,7 @@ public class TestPersistenceContextQueryScope extends BaseTestCase { @Test public void test() { - EBasicVer bean = new EBasicVer(); - bean.setName("first"); + EBasicVer bean = new EBasicVer("first"); Ebean.save(bean); //Ebean.getServerCacheManager().setCaching(EBasicVer.class, true); diff --git a/src/test/java/com/avaje/tests/query/TestQueryFindFutureList.java b/src/test/java/com/avaje/tests/query/TestQueryFindFutureList.java index 51d92f922..b540a731b 100644 --- a/src/test/java/com/avaje/tests/query/TestQueryFindFutureList.java +++ b/src/test/java/com/avaje/tests/query/TestQueryFindFutureList.java @@ -48,7 +48,7 @@ public class TestQueryFindFutureList extends BaseTestCase { // wait for it to complete List orders = futureList.getUnchecked(); - assertEquals(Ebean.find(Order.class).findRowCount(), orders.size()); + assertEquals(Ebean.find(Order.class).findCount(), orders.size()); } @Test @@ -61,7 +61,7 @@ public class TestQueryFindFutureList extends BaseTestCase { // wait for it to complete List orders = futureList.getUnchecked(1, TimeUnit.SECONDS); - assertEquals(Ebean.find(Order.class).findRowCount(), orders.size()); + assertEquals(Ebean.find(Order.class).findCount(), orders.size()); } } diff --git a/src/test/java/com/avaje/tests/query/TestQueryFindIterate.java b/src/test/java/com/avaje/tests/query/TestQueryFindIterate.java index f03b6da6c..67b6bc466 100644 --- a/src/test/java/com/avaje/tests/query/TestQueryFindIterate.java +++ b/src/test/java/com/avaje/tests/query/TestQueryFindIterate.java @@ -27,6 +27,32 @@ public class TestQueryFindIterate extends BaseTestCase { EbeanServer server = Ebean.getServer(null); + Query query = server.find(Customer.class) + .setMaxRows(2); + + final AtomicInteger count = new AtomicInteger(); + + QueryIterator it = query.findIterate(); + try { + while (it.hasNext()) { + Customer customer = it.next(); + customer.getName(); + count.incrementAndGet(); + } + } finally { + it.close(); + } + + assertEquals(2, count.get()); + } + + @Test + public void findEach() { + + ResetBasicData.reset(); + + EbeanServer server = Ebean.getServer(null); + Query query = server.find(Customer.class) .setAutoTune(false) //.fetch("contacts", new FetchConfig().query(2)).where().gt("id", 0).orderBy("id") diff --git a/src/test/java/com/avaje/tests/query/TestQueryFindMapTypedKey.java b/src/test/java/com/avaje/tests/query/TestQueryFindMapTypedKey.java index 8aaa9c12d..6648eb5f1 100644 --- a/src/test/java/com/avaje/tests/query/TestQueryFindMapTypedKey.java +++ b/src/test/java/com/avaje/tests/query/TestQueryFindMapTypedKey.java @@ -1,14 +1,16 @@ package com.avaje.tests.query; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.Product; import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNotNull; public class TestQueryFindMapTypedKey extends BaseTestCase { @@ -17,9 +19,21 @@ public class TestQueryFindMapTypedKey extends BaseTestCase { ResetBasicData.reset(); - Map map = Ebean.find(Customer.class).select("id, name") - .findMap("name", String.class); + Map productsBySku = Ebean.find(Product.class) + .setMapKey("sku") + .findMap(); + + assertThat(productsBySku).isNotEmpty(); + + Product desk = productsBySku.get("DSK1"); + assertNotNull(desk); + + Map map = Ebean.find(Customer.class) + .select("id, name") + .setMapKey("name") + .findMap(); + + assertNotNull(map); - Assert.assertNotNull(map); } } diff --git a/src/test/java/com/avaje/tests/query/TestQueryFindPagedList.java b/src/test/java/com/avaje/tests/query/TestQueryFindPagedList.java index 501136f8c..3581fb475 100644 --- a/src/test/java/com/avaje/tests/query/TestQueryFindPagedList.java +++ b/src/test/java/com/avaje/tests/query/TestQueryFindPagedList.java @@ -83,7 +83,7 @@ public class TestQueryFindPagedList extends BaseTestCase { pagedList.loadRowCount(); List orders = pagedList.getList(); - int totalRowCount = pagedList.getTotalRowCount(); + int totalRowCount = pagedList.getTotalCount(); assertThat(orders.size()).isLessThan(totalRowCount); assertTrue(pagedList.hasNext()); @@ -96,9 +96,9 @@ public class TestQueryFindPagedList extends BaseTestCase { .setMaxRows(3) .findPagedList(); - pagedList2.loadRowCount(); + pagedList2.loadCount(); List orders2 = pagedList2.getList(); - int totalRowCount2 = pagedList2.getTotalRowCount(); + int totalRowCount2 = pagedList2.getTotalCount(); assertTrue(pagedList2.hasNext()); assertTrue(pagedList2.hasPrev()); @@ -161,11 +161,11 @@ public class TestQueryFindPagedList extends BaseTestCase { PagedList pagedList = Ebean.find(Order.class).setMaxRows(3).findPagedList(); - Future rowCount = pagedList.getFutureRowCount(); + Future rowCount = pagedList.getFutureCount(); List orders = pagedList.getList(); // these are each getting the total row count - int totalRowCount = pagedList.getTotalRowCount(); + int totalRowCount = pagedList.getTotalCount(); Integer totalRowCountWithTimeout = rowCount.get(30, TimeUnit.SECONDS); Integer totalRowCountViaFuture = rowCount.get(); @@ -183,9 +183,9 @@ public class TestQueryFindPagedList extends BaseTestCase { // fetch less that total orders (page size 3) PagedList pagedList = Ebean.find(Order.class).setMaxRows(3).findPagedList(); - pagedList.loadRowCount(); + pagedList.loadCount(); List orders = pagedList.getList(); - int totalRowCount = pagedList.getTotalRowCount(); + int totalRowCount = pagedList.getTotalCount(); assertThat(orders.size()).isLessThan(totalRowCount); } @@ -207,10 +207,10 @@ public class TestQueryFindPagedList extends BaseTestCase { try { List orders = pagedList.getList(); - int totalRowCount = pagedList.getTotalRowCount(); + int totalRowCount = pagedList.getTotalCount(); // invoke it again but cached... - int totalRowCountAgain = pagedList.getTotalRowCount(); + int totalRowCountAgain = pagedList.getTotalCount(); List loggedSql = LoggedSqlCollector.stop(); @@ -242,7 +242,7 @@ public class TestQueryFindPagedList extends BaseTestCase { LoggedSqlCollector.start(); - pagedList.getTotalRowCount(); + pagedList.getTotalCount(); pagedList.getList(); List loggedSql = LoggedSqlCollector.stop(); diff --git a/src/test/java/com/avaje/tests/query/TestQueryPlanCacheRowCount.java b/src/test/java/com/avaje/tests/query/TestQueryPlanCacheRowCount.java index 1ee498639..475fc4cfc 100644 --- a/src/test/java/com/avaje/tests/query/TestQueryPlanCacheRowCount.java +++ b/src/test/java/com/avaje/tests/query/TestQueryPlanCacheRowCount.java @@ -23,16 +23,16 @@ public class TestQueryPlanCacheRowCount extends BaseTestCase { int rc0 = query.findRowCount(); - List ids = query.findIds(); + List ids = query.findIds(); Assert.assertEquals(rc0, ids.size()); List list0 = query.findList(); Assert.assertEquals(rc0, list0.size()); - int rc1 = query.findRowCount(); + int rc1 = query.findCount(); Assert.assertEquals(rc0, rc1); - List ids1 = query.findIds(); + List ids1 = query.findIds(); Assert.assertEquals(rc0, ids1.size()); List list1 = query.findList(); @@ -48,12 +48,12 @@ public class TestQueryPlanCacheRowCount extends BaseTestCase { Query query2 = Ebean.find(Order.class).where().eq("status", Order.Status.NEW) .ge("id", idGt).order().desc("id"); - int rc2 = query2.findRowCount(); + int rc2 = query2.findCount(); System.out.println("Expection Not same " + rc0 + " != " + rc2); Assert.assertNotSame(rc0, rc2); - List ids2 = query2.findIds(); + List ids2 = query2.findIds(); Assert.assertEquals(rc2, ids2.size()); List list2 = query2.findList(); diff --git a/src/test/java/com/avaje/tests/query/TestRowCount.java b/src/test/java/com/avaje/tests/query/TestRowCount.java index b8b47c3b0..ae7196f04 100644 --- a/src/test/java/com/avaje/tests/query/TestRowCount.java +++ b/src/test/java/com/avaje/tests/query/TestRowCount.java @@ -21,7 +21,7 @@ public class TestRowCount extends BaseTestCase { Query query = Ebean.find(Order.class).fetch("details").where().gt("id", 1) .gt("details.id", 1).order("id desc"); - int rc = query.findRowCount(); + int rc = query.findCount(); List ids = query.findIds(); diff --git a/src/test/java/com/avaje/tests/query/joins/TestDisjunctWhereOuterOnMany.java b/src/test/java/com/avaje/tests/query/joins/TestDisjunctWhereOuterOnMany.java index 5f41d95cc..3b9f2dab4 100644 --- a/src/test/java/com/avaje/tests/query/joins/TestDisjunctWhereOuterOnMany.java +++ b/src/test/java/com/avaje/tests/query/joins/TestDisjunctWhereOuterOnMany.java @@ -44,7 +44,7 @@ public class TestDisjunctWhereOuterOnMany extends BaseTestCase { .query(); List list = query.findList(); - int rowCount = query.findRowCount(); + int rowCount = query.findCount(); // select distinct t0.id c0, t0.name c1 // from uuone t0 diff --git a/src/test/java/com/avaje/tests/query/other/TestFindIdsWithInheritance.java b/src/test/java/com/avaje/tests/query/other/TestFindIdsWithInheritance.java index 511456106..fb65a46ee 100644 --- a/src/test/java/com/avaje/tests/query/other/TestFindIdsWithInheritance.java +++ b/src/test/java/com/avaje/tests/query/other/TestFindIdsWithInheritance.java @@ -20,8 +20,7 @@ public class TestFindIdsWithInheritance extends BaseTestCase { Ebean.save(truck); - List ids = Ebean.find(Vehicle.class).findIds(); - + List ids = Ebean.find(Vehicle.class).findIds(); Assert.assertNotNull(ids); Ebean.delete(truck); diff --git a/src/test/java/com/avaje/tests/query/other/TestFormulaWithFindCount.java b/src/test/java/com/avaje/tests/query/other/TestFormulaWithFindCount.java index 105028ee7..f4cc122ef 100644 --- a/src/test/java/com/avaje/tests/query/other/TestFormulaWithFindCount.java +++ b/src/test/java/com/avaje/tests/query/other/TestFormulaWithFindCount.java @@ -31,7 +31,7 @@ public class TestFormulaWithFindCount extends BaseTestCase { } ExpressionList expressionList = server.find(Order.class).where().gt("totalAmount", 1d); - int rowCount = expressionList.findRowCount(); + int rowCount = expressionList.findCount(); Assert.assertEquals(list.size(), rowCount); } diff --git a/src/test/java/com/avaje/tests/query/other/TestObjectGraphNodeStatsCollection.java b/src/test/java/com/avaje/tests/query/other/TestObjectGraphNodeStatsCollection.java index c9efc59f5..9decc1dff 100644 --- a/src/test/java/com/avaje/tests/query/other/TestObjectGraphNodeStatsCollection.java +++ b/src/test/java/com/avaje/tests/query/other/TestObjectGraphNodeStatsCollection.java @@ -28,7 +28,7 @@ public class TestObjectGraphNodeStatsCollection extends BaseTestCase { MetaInfoManager infoManager = server.getMetaInfoManager(); - server.find(Order.class).findRowCount(); + server.find(Order.class).findCount(); infoManager.collectNodeStatistics(true); infoManager.collectQueryPlanStatistics(true); @@ -95,7 +95,7 @@ public class TestObjectGraphNodeStatsCollection extends BaseTestCase { ResetBasicData.reset(); - List ids = Ebean.find(Order.class).findIds(); + List ids = Ebean.find(Order.class).findIds(); Assert.assertTrue(!ids.isEmpty()); } diff --git a/src/test/java/com/avaje/tests/query/other/TestQueryConversationRowCount.java b/src/test/java/com/avaje/tests/query/other/TestQueryConversationRowCount.java index f76397580..01d64fc36 100644 --- a/src/test/java/com/avaje/tests/query/other/TestQueryConversationRowCount.java +++ b/src/test/java/com/avaje/tests/query/other/TestQueryConversationRowCount.java @@ -47,7 +47,7 @@ public class TestQueryConversationRowCount extends BaseTestCase { LoggedSqlCollector.start(); - query.findRowCount(); + query.findCount(); // select count(*) from ( // select distinct t0.id c0 diff --git a/src/test/java/com/avaje/tests/query/other/TestQueryDistinct.java b/src/test/java/com/avaje/tests/query/other/TestQueryDistinct.java index 7a7e6040f..751bbc73c 100644 --- a/src/test/java/com/avaje/tests/query/other/TestQueryDistinct.java +++ b/src/test/java/com/avaje/tests/query/other/TestQueryDistinct.java @@ -1,10 +1,5 @@ package com.avaje.tests.query.other; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.Query; @@ -12,6 +7,14 @@ import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.bean.EntityBeanIntercept; import com.avaje.tests.model.basic.Customer; import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Test; + +import java.util.List; + +import static junit.framework.TestCase.assertNull; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class TestQueryDistinct extends BaseTestCase { @@ -27,17 +30,17 @@ public class TestQueryDistinct extends BaseTestCase { List customers = query.findList(); String generatedSql = query.getGeneratedSql(); - Assert.assertTrue(generatedSql.contains("select distinct t0.name c0 from o_customer t0")); + assertThat(generatedSql).contains("select distinct t0.name c0 from o_customer t0"); for (Customer customer : customers) { EntityBeanIntercept ebi = ((EntityBean)customer)._ebean_getIntercept(); - Assert.assertTrue(ebi.isDisableLazyLoad()); - Assert.assertNull(ebi.getPersistenceContext()); + assertTrue(ebi.isDisableLazyLoad()); + assertNull(ebi.getPersistenceContext()); // lazy loading disabled - Assert.assertNull(customer.getId()); - Assert.assertNull(customer.getAnniversary()); + assertNull(customer.getId()); + assertNull(customer.getAnniversary()); } } @@ -53,7 +56,7 @@ public class TestQueryDistinct extends BaseTestCase { query.findList(); String generatedSql = query.getGeneratedSql(); - Assert.assertTrue(generatedSql.contains("select distinct t0.name c0 from o_customer t0")); + assertThat(generatedSql).contains("select distinct t0.name c0 from o_customer t0"); } @Test @@ -69,15 +72,33 @@ public class TestQueryDistinct extends BaseTestCase { List customers = query.findList(); String generatedSql = query.getGeneratedSql(); - Assert.assertTrue(generatedSql.contains("select distinct t0.status c0 from o_customer t0")); + assertThat(generatedSql).contains("select distinct t0.status c0 from o_customer t0"); for (Customer customer : customers) { - Assert.assertNotNull(customer.getStatus()); + assertNotNull(customer.getStatus()); // lazy loading disabled - Assert.assertNull(customer.getId()); - Assert.assertNull(customer.getAnniversary()); + assertNull(customer.getId()); + assertNull(customer.getAnniversary()); + } + } + + @Test + public void testPagingQuery_expect_doesNotAddOrderBy() { + + ResetBasicData.reset(); + + Query query = Ebean.find(Customer.class) + .setMaxRows(10) + .setDistinct(true) + .select("name"); + + query.findList(); + + if (isH2() || isPostgres()) { + String generatedSql = query.getGeneratedSql(); + assertThat(generatedSql).contains("select distinct t0.name c0 from o_customer t0 limit 10"); } } diff --git a/src/test/java/com/avaje/tests/query/other/TestQueryRawExpressionMany.java b/src/test/java/com/avaje/tests/query/other/TestQueryRawExpressionMany.java new file mode 100644 index 000000000..f21ea9212 --- /dev/null +++ b/src/test/java/com/avaje/tests/query/other/TestQueryRawExpressionMany.java @@ -0,0 +1,35 @@ +package com.avaje.tests.query.other; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.Query; +import com.avaje.tests.model.basic.Order; +import com.avaje.tests.model.basic.ResetBasicData; +import org.avaje.ebeantest.LoggedSqlCollector; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestQueryRawExpressionMany extends BaseTestCase { + + @Test + public void test() { + + ResetBasicData.reset(); + + Integer quantity = 1; + + Query query = Ebean.find(Order.class) + .where().raw("details.orderQty = ?", quantity) + .query(); + + LoggedSqlCollector.start(); + + query.findCount(); + List sql = LoggedSqlCollector.stop(); + + assertThat(sql.get(0)).contains("select count(*) from ( select distinct t0.id c0 from o_order t0 left outer join o_order_detail t1 on t1.order_id = t0.id where t1.order_qty = ?)"); + } +} diff --git a/src/test/java/com/avaje/tests/query/other/TestQueryRowCountWithMany.java b/src/test/java/com/avaje/tests/query/other/TestQueryRowCountWithMany.java index 240039bea..bf472df71 100644 --- a/src/test/java/com/avaje/tests/query/other/TestQueryRowCountWithMany.java +++ b/src/test/java/com/avaje/tests/query/other/TestQueryRowCountWithMany.java @@ -47,7 +47,7 @@ public class TestQueryRowCountWithMany extends BaseTestCase { Assert.assertTrue(generatedSql.contains(" order by t0.cretime")); - int rowCount = query.findRowCount(); + int rowCount = query.findCount(); // select count(*) from o_order t0 // left outer join o_order_detail t1 on t1.order_id = t0.id @@ -84,7 +84,7 @@ public class TestQueryRowCountWithMany extends BaseTestCase { .orderBy("cretime asc"); LoggedSqlCollector.start(); - query.findRowCount(); + query.findCount(); List sqlLogged = LoggedSqlCollector.stop(); diff --git a/src/test/java/com/avaje/tests/query/other/TestQuerySingleAttribute.java b/src/test/java/com/avaje/tests/query/other/TestQuerySingleAttribute.java new file mode 100644 index 000000000..7fddbceb3 --- /dev/null +++ b/src/test/java/com/avaje/tests/query/other/TestQuerySingleAttribute.java @@ -0,0 +1,128 @@ +package com.avaje.tests.query.other; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.Query; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Test; + +import java.sql.Date; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestQuerySingleAttribute extends BaseTestCase { + + + @Test + public void exampleUsage() { + + ResetBasicData.reset(); + + List names = + Ebean.find(Customer.class) + .setDistinct(true) + .select("name") + .where().eq("status", Customer.Status.NEW) + .orderBy().asc("name") + .setMaxRows(100) + .findSingleAttributeList(); + + assertThat(names).isNotNull(); + } + + @Test + public void exampleUsage_otherType() { + + ResetBasicData.reset(); + + List dates = + Ebean.find(Customer.class) + .setDistinct(true) + .select("anniversary") + .where().isNotNull("anniversary") + .orderBy().asc("anniversary") + .findSingleAttributeList(); + + assertThat(dates).isNotNull(); + } + + @Test + public void withOrderBy() { + + Query query = + Ebean.find(Customer.class) + .setDistinct(true) + .select("name") + .where().eq("status", Customer.Status.NEW) + .orderBy().asc("name") + .setMaxRows(100); + + query.findSingleAttributeList(); + assertThat(query.getGeneratedSql()).contains("select distinct t0.name c0 from o_customer t0 where t0.status = ? order by t0.name "); + } + + @Test + public void basic() { + + ResetBasicData.reset(); + + Query query = Ebean.find(Customer.class).select("name"); + + List names = query.findSingleAttributeList();//String.class); + + assertThat(query.getGeneratedSql()).contains("select t0.name c0 from o_customer t0"); + assertThat(names).isNotNull(); + } + + @Test + public void distinctAndWhere() { + + ResetBasicData.reset(); + + Query query = Ebean.find(Customer.class) + .setDistinct(true) + .select("name") + .where().eq("status", Customer.Status.NEW) + .query(); + + List names = query.findSingleAttributeList(); + + assertThat(query.getGeneratedSql()).contains("select distinct t0.name c0 from o_customer t0 where t0.status = ? "); + assertThat(names).isNotNull(); + } + + @Test + public void distinctWhereWithJoin() { + + ResetBasicData.reset(); + + Query query = Ebean.find(Customer.class) + .setDistinct(true) + .select("name") + .where().eq("status", Customer.Status.NEW) + .istartsWith("billingAddress.city", "auck") + .query(); + + List names = query.findSingleAttributeList(); + + assertThat(query.getGeneratedSql()).contains("select distinct t0.name c0 from o_customer t0 left outer join o_address t1 on t1.id = t0.billing_address_id where t0.status = ? and lower(t1.city) like ?"); + assertThat(names).isNotNull(); + } + + + @Test + public void queryPlan_expect_differentPlans() { + + ResetBasicData.reset(); + + Query query = Ebean.find(Customer.class).select("name"); + query.findSingleAttributeList(); + assertThat(query.getGeneratedSql()).contains("select t0.name c0 from o_customer t0"); + + Query query2 = Ebean.find(Customer.class).select("name"); + query2.findList(); + assertThat(query2.getGeneratedSql()).contains("select t0.id c0, t0.name c1 from o_customer t0"); + } +} diff --git a/src/test/java/com/avaje/tests/query/other/TestSelfParent.java b/src/test/java/com/avaje/tests/query/other/TestSelfParent.java index 9d42bf022..7e8e35306 100644 --- a/src/test/java/com/avaje/tests/query/other/TestSelfParent.java +++ b/src/test/java/com/avaje/tests/query/other/TestSelfParent.java @@ -14,7 +14,7 @@ public class TestSelfParent extends BaseTestCase { @Test public void test() { - if (Ebean.find(SelfParent.class).findRowCount() > 0) { + if (Ebean.find(SelfParent.class).findCount() > 0) { // only run once return; } diff --git a/src/test/java/com/avaje/tests/query/softdelete/TestSoftDeletePagingList.java b/src/test/java/com/avaje/tests/query/softdelete/TestSoftDeletePagingList.java index 430facf91..caa3312da 100644 --- a/src/test/java/com/avaje/tests/query/softdelete/TestSoftDeletePagingList.java +++ b/src/test/java/com/avaje/tests/query/softdelete/TestSoftDeletePagingList.java @@ -31,7 +31,7 @@ public class TestSoftDeletePagingList { .setMaxRows(10) .findPagedList(); - int totalRowCount = pagedList.getTotalRowCount(); + int totalRowCount = pagedList.getTotalCount(); List resultList = pagedList.getList(); List sql = LoggedSqlCollector.stop(); diff --git a/src/test/java/com/avaje/tests/query/sqlquery/SqlQueryTests.java b/src/test/java/com/avaje/tests/query/sqlquery/SqlQueryTests.java index c5e507287..4e7cefb0c 100644 --- a/src/test/java/com/avaje/tests/query/sqlquery/SqlQueryTests.java +++ b/src/test/java/com/avaje/tests/query/sqlquery/SqlQueryTests.java @@ -111,7 +111,7 @@ public class SqlQueryTests extends BaseTestCase { ResetBasicData.reset(); - int expectedRows = Ebean.find(Order.class).findRowCount(); + int expectedRows = Ebean.find(Order.class).findCount(); final AtomicInteger count = new AtomicInteger(); diff --git a/src/test/java/com/avaje/tests/rawsql/TestOrderReportTotal.java b/src/test/java/com/avaje/tests/rawsql/TestOrderReportTotal.java index 5f7b53bef..d114541df 100644 --- a/src/test/java/com/avaje/tests/rawsql/TestOrderReportTotal.java +++ b/src/test/java/com/avaje/tests/rawsql/TestOrderReportTotal.java @@ -56,7 +56,7 @@ public class TestOrderReportTotal extends BaseTestCase { .where() .gt("order.id", 2) .istartsWith("order.customer.name","rob") - .findRowCount(); + .findCount(); assertThat(detailsCount).isGreaterThan(0); } diff --git a/src/test/java/com/avaje/tests/rawsql/TestRawSqlOrmQuery.java b/src/test/java/com/avaje/tests/rawsql/TestRawSqlOrmQuery.java index 3ae69818f..c8424ac7f 100644 --- a/src/test/java/com/avaje/tests/rawsql/TestRawSqlOrmQuery.java +++ b/src/test/java/com/avaje/tests/rawsql/TestRawSqlOrmQuery.java @@ -78,14 +78,14 @@ public class TestRawSqlOrmQuery extends BaseTestCase { Query query = Ebean.find(Customer.class); query.setRawSql(rawSql); - int initialRowCount = query.findRowCount(); + int initialRowCount = query.findCount(); query.setFirstRow(1); query.setMaxRows(2); List list = query.findList(); - int rowCount = query.findRowCount(); - FutureRowCount futureRowCount = query.findFutureRowCount(); + int rowCount = query.findCount(); + FutureRowCount futureRowCount = query.findFutureCount(); Assert.assertEquals(initialRowCount, rowCount); Assert.assertEquals(initialRowCount, futureRowCount.get().intValue()); @@ -110,12 +110,12 @@ public class TestRawSqlOrmQuery extends BaseTestCase { Query query = Ebean.find(Customer.class); query.setRawSql(rawSql); - int initialRowCount = query.findRowCount(); + int initialRowCount = query.findCount(); PagedList page = query.setMaxRows(2).findPagedList(); List list = page.getList(); - int rowCount = page.getTotalRowCount(); + int rowCount = page.getTotalCount(); Assert.assertEquals(2, list.size()); Assert.assertEquals(initialRowCount, rowCount); @@ -186,7 +186,7 @@ public class TestRawSqlOrmQuery extends BaseTestCase { query.order("id desc"); PagedList pagedList = query.findPagedList(); pagedList.getList(); - pagedList.getTotalRowCount(); + pagedList.getTotalCount(); assertThat(query.getGeneratedSql()).contains("order by o.id desc limit 100"); } diff --git a/src/test/java/com/avaje/tests/readaudit/TestReadAudit.java b/src/test/java/com/avaje/tests/readaudit/TestReadAudit.java index 578039966..927e75bba 100644 --- a/src/test/java/com/avaje/tests/readaudit/TestReadAudit.java +++ b/src/test/java/com/avaje/tests/readaudit/TestReadAudit.java @@ -257,7 +257,7 @@ public class TestReadAudit extends BaseTestCase { resetCounters(); - Map list = server.find(EBasicChangeLog.class) + Map list = server.find(EBasicChangeLog.class) .where().startsWith("shortDescription", "readAudit") .findMap(); diff --git a/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteBasic.java b/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteBasic.java index 30b01c627..91ac41dda 100644 --- a/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteBasic.java +++ b/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteBasic.java @@ -71,7 +71,7 @@ public class TestSoftDeleteBasic extends BaseTestCase { bean.setName("two"); Ebean.save(bean); - int rowCountBefore = Ebean.find(EBasicSoftDelete.class).findRowCount(); + int rowCountBefore = Ebean.find(EBasicSoftDelete.class).findCount(); Ebean.delete(EBasicSoftDelete.class, bean.getId()); @@ -79,7 +79,7 @@ public class TestSoftDeleteBasic extends BaseTestCase { // -- test .findRowCount() LoggedSqlCollector.start(); - int rowCountAfter = Ebean.find(EBasicSoftDelete.class).findRowCount(); + int rowCountAfter = Ebean.find(EBasicSoftDelete.class).findCount(); List loggedSql = LoggedSqlCollector.stop(); assertThat(loggedSql).hasSize(1); @@ -91,7 +91,7 @@ public class TestSoftDeleteBasic extends BaseTestCase { // -- test includeSoftDeletes().findRowCount() LoggedSqlCollector.start(); - int rowCountFull = Ebean.find(EBasicSoftDelete.class).setIncludeSoftDeletes().findRowCount(); + int rowCountFull = Ebean.find(EBasicSoftDelete.class).setIncludeSoftDeletes().findCount(); assertThat(rowCountFull).isGreaterThan(rowCountAfter); loggedSql = LoggedSqlCollector.stop(); @@ -197,7 +197,7 @@ public class TestSoftDeleteBasic extends BaseTestCase { Ebean.find(EBasicSoftDelete.class) .setId(bean.getId()) .where() - .includeSoftDeletes() + .setIncludeSoftDeletes() .findUnique(); assertThat(fetchAllWithLazy.getChildren()).hasSize(3); diff --git a/src/test/java/com/avaje/tests/text/json/TestJsonMap.java b/src/test/java/com/avaje/tests/text/json/TestJsonMap.java index 9caa18da8..ecf582b50 100644 --- a/src/test/java/com/avaje/tests/text/json/TestJsonMap.java +++ b/src/test/java/com/avaje/tests/text/json/TestJsonMap.java @@ -41,7 +41,7 @@ public class TestJsonMap extends BaseTestCase { ResetBasicData.reset(); - Map map = Ebean.find(Customer.class).findMap("id", String.class); + Map map = Ebean.find(Customer.class).findMap(); JsonContext jsonContext = Ebean.json(); JsonWriteOptions options = JsonWriteOptions.parsePath("(id,status,name)"); diff --git a/src/test/java/com/avaje/tests/transaction/TestBeanStateReset.java b/src/test/java/com/avaje/tests/transaction/TestBeanStateReset.java new file mode 100644 index 000000000..faeb64a13 --- /dev/null +++ b/src/test/java/com/avaje/tests/transaction/TestBeanStateReset.java @@ -0,0 +1,99 @@ +package com.avaje.tests.transaction; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.Transaction; +import com.avaje.tests.model.m2m.MnyB; +import com.avaje.tests.model.m2m.MnyC; +import org.avaje.ebeantest.LoggedSqlCollector; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.PersistenceException; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNotNull; + +public class TestBeanStateReset extends BaseTestCase { + + private static final Logger logger = LoggerFactory.getLogger(TestBeanStateReset.class); + + @Test + public void resetForInsert() { + + // setup to fail foreign key constraint + MnyC c = new MnyC(); + c.setId(Long.MAX_VALUE); + + MnyB b = new MnyB(); + b.getCs().add(c); + + try { + // inserts of b succeeds but intersection insert fails FK check on c + b.save(); + + } catch (PersistenceException e) { + logger.info("expected error " + e.getMessage()); + + Ebean.getBeanState(b).resetForInsert(); + b.getCs().clear(); + + LoggedSqlCollector.start(); + b.setName("mod"); + b.save(); + + List sql = LoggedSqlCollector.stop(); + assertThat(sql.get(0)).contains("insert into mny_b (id, name, version, when_created, when_modified, a_id) values ("); + } + + } + + @Test + public void alternativeVia_persistCascadeOff_with_commitAndContinue() { + + // setup to fail foreign key constraint when using cascade save + MnyC c = new MnyC(); + c.setId(Long.MAX_VALUE); + + MnyB b = new MnyB(); + b.getCs().add(c); + + Transaction transaction = Ebean.beginTransaction(); + + try { + // turn off cascade ... + transaction.setPersistCascade(false); + b.save(); + + // commit at this point + transaction.commitAndContinue(); + try { + + // turn on cascade ... such that the ManyToMany is persisted + transaction.setPersistCascade(true); + + // save b again which this time cascades to the ManyToMany + // but this fails due to FK on ManyToMany + b.save(); + + // we actually don't get here due to the FK error + transaction.commit(); + + } catch (PersistenceException e) { + // so we failed the second save but that is ok'ish + // we handle this exception knowing b got inserted and committed + // and that the inserts into the intersection table failed + logger.info("The ManyToMany intersection error: " + e.getMessage()); + } + } finally { + // performs a rollback as the commit at line:80 does not happen + transaction.end(); + } + + // assert our insert prior to the commitAndContinue succeeded + MnyB madeIt = Ebean.find(MnyB.class, b.getId()); + assertNotNull(madeIt); + } +} diff --git a/src/test/java/com/avaje/tests/transaction/TestCommitAndContinue.java b/src/test/java/com/avaje/tests/transaction/TestCommitAndContinue.java new file mode 100644 index 000000000..74f7e5b94 --- /dev/null +++ b/src/test/java/com/avaje/tests/transaction/TestCommitAndContinue.java @@ -0,0 +1,183 @@ +package com.avaje.tests.transaction; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.annotation.Transactional; +import com.avaje.tests.model.m2m.MnyB; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +public class TestCommitAndContinue extends BaseTestCase { + + private static final Logger logger = LoggerFactory.getLogger("org.avaje.ebean.TXN"); + + @Test + @Transactional + public void transactional_partialSuccess() { + + MnyB a = new MnyB("a100"); + MnyB b = new MnyB("b200"); + + a.save(); + + // commit at this point + Ebean.currentTransaction().commitAndContinue(); + + try { + b.save(); + + // some error occurs + throw new IllegalStateException(); + + } catch (IllegalStateException e) { + // mark the transaction as rollback + Ebean.currentTransaction().setRollbackOnly(); + + // use a different transaction to assert + EbeanServer server = Ebean.getDefaultServer(); + Transaction anotherTxn = server.createTransaction(); + + // success prior to commitAndContinue + assertNotNull(server.find(MnyB.class, a.getId(), anotherTxn)); + + // insert failed after commitAndContinue + assertNull(server.find(MnyB.class, b.getId(), anotherTxn)); + } + } + + /** + * The @Transactional is nicer to me. + */ + @Test + public void tryFinally_partialSuccess() { + + MnyB a = new MnyB("a100"); + MnyB b = new MnyB("b200"); + + EbeanServer server = Ebean.getDefaultServer(); + Transaction txn = server.beginTransaction(); + try { + a.save(); + // commit at this point + txn.commitAndContinue(); + + try { + b.save(); + + // some error occurs + throw new IllegalStateException(); + + } catch (IllegalStateException e) { + // mark the transaction as rollback + txn.setRollbackOnly(); + + // use a different transaction to assert + Transaction anotherTxn = server.createTransaction(); + // success prior to commitAndContinue + assertNotNull(server.find(MnyB.class, a.getId(), anotherTxn)); + // insert failed after commitAndContinue + assertNull(server.find(MnyB.class, b.getId(), anotherTxn)); + } + + // does not commit due to the txn.setRollbackOnly(); + txn.commit(); + + } finally { + server.endTransaction(); + } + } + + @Test + @Transactional + public void transactional_partialSuccess_secondTransactionInsert() { + + MnyB a = new MnyB("a100"); + MnyB b = new MnyB("b200"); + MnyB c = new MnyB("c300"); + + a.save(); + + // commit at this point + Ebean.currentTransaction().commitAndContinue(); + + try { + b.save(); + + // some error occurs + throw new IllegalStateException(); + + } catch (IllegalStateException e) { + // mark the transaction as rollback + Ebean.currentTransaction().setRollbackOnly(); + + // use a different transaction to do something useful + EbeanServer server = Ebean.getDefaultServer(); + Transaction txn2 = server.createTransaction(); + try { + server.save(c, txn2); + txn2.commit(); + } finally { + txn2.end(); + } + } + + // asserts + + EbeanServer server = Ebean.getDefaultServer(); + Transaction txnForAssert = server.createTransaction(); + + // success prior to commitAndContinue + assertNotNull(server.find(MnyB.class, a.getId(), txnForAssert)); + + // insert failed after commitAndContinue + assertNull(server.find(MnyB.class, b.getId(), txnForAssert)); + + // successful insert using txn2 + assertNotNull(server.find(MnyB.class, c.getId(), txnForAssert)); + } + + @Test + public void basic() { + + MnyB a = new MnyB("a"); + MnyB b = new MnyB("b"); + MnyB c = new MnyB("c"); + + Transaction txn = Ebean.beginTransaction(); + try { + a.save(); + txn.commitAndContinue(); + + txn.setBatchMode(true); + b.save(); + logger.info("... pre commitAndContinue"); + txn.commitAndContinue(); + + c.save(); + txn.commit(); + + } finally { + txn.end(); + } + } + + @Test + @Transactional + public void runTransactional() { + + new MnyB("a100").save(); + new MnyB("a101").save(); + + Ebean.currentTransaction().commitAndContinue(); + + new MnyB("a200").save(); + new MnyB("a201").save(); + } + +} diff --git a/src/test/java/com/avaje/tests/transaction/TestDeleteFromPersistenceContext.java b/src/test/java/com/avaje/tests/transaction/TestDeleteFromPersistenceContext.java index 15050e22c..79d26407b 100644 --- a/src/test/java/com/avaje/tests/transaction/TestDeleteFromPersistenceContext.java +++ b/src/test/java/com/avaje/tests/transaction/TestDeleteFromPersistenceContext.java @@ -18,9 +18,7 @@ public class TestDeleteFromPersistenceContext extends BaseTestCase { ResetBasicData.reset(); - EBasicVer bean = new EBasicVer(); - bean.setName("Please Delete Me"); - + EBasicVer bean = new EBasicVer("Please Delete Me"); Ebean.save(bean); SpiTransaction transaction = (SpiTransaction)Ebean.beginTransaction(); diff --git a/src/test/java/com/avaje/tests/transaction/TestExplicitTransactionMode.java b/src/test/java/com/avaje/tests/transaction/TestExplicitTransactionMode.java index b33455217..f1edf9e53 100644 --- a/src/test/java/com/avaje/tests/transaction/TestExplicitTransactionMode.java +++ b/src/test/java/com/avaje/tests/transaction/TestExplicitTransactionMode.java @@ -70,7 +70,7 @@ public class TestExplicitTransactionMode extends BaseTestCase { } // rollback as expected - assertEquals(0, ebeanServer.find(UTMaster.class).findRowCount()); + assertEquals(0, ebeanServer.find(UTMaster.class).findCount()); UTMaster bean1 = new UTMaster("one1"); UTMaster bean2 = new UTMaster("two2"); diff --git a/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequired.java b/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequired.java index 7782712f7..163463ed5 100644 --- a/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequired.java +++ b/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequired.java @@ -25,11 +25,11 @@ public class TestNestedBeginRequired extends BaseTestCase { Transaction txn = server.beginTransaction(TxScope.required()); try { - server.find(Country.class).findRowCount(); + server.find(Country.class).findCount(); someInnerMethod(); - server.find(Product.class).findRowCount(); + server.find(Product.class).findCount(); txn.commit(); @@ -45,7 +45,7 @@ public class TestNestedBeginRequired extends BaseTestCase { Transaction txn = server.beginTransaction(TxScope.required()); try { - server.find(Customer.class).findRowCount(); + server.find(Customer.class).findCount(); txn.commit(); diff --git a/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiredWithFailure.java b/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiredWithFailure.java index 7b3945ed8..1f52a9c03 100644 --- a/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiredWithFailure.java +++ b/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiredWithFailure.java @@ -23,12 +23,12 @@ public class TestNestedBeginRequiredWithFailure extends BaseTestCase { Transaction txn = server.beginTransaction(TxScope.required()); try { - server.find(Country.class).findRowCount(); + server.find(Country.class).findCount(); try { someInnerMethodWithFailure(); - server.find(Product.class).findRowCount(); + server.find(Product.class).findCount(); txn.commit(); } catch (RuntimeException e) { @@ -50,7 +50,7 @@ public class TestNestedBeginRequiredWithFailure extends BaseTestCase { Transaction txn = server.beginTransaction(TxScope.required()); try { - server.find(Customer.class).findRowCount(); + server.find(Customer.class).findCount(); EBasic basic = new EBasic(); basic.setName("ignore"); diff --git a/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiresNew.java b/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiresNew.java index 11a231d09..21d85d4b0 100644 --- a/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiresNew.java +++ b/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiresNew.java @@ -25,11 +25,11 @@ public class TestNestedBeginRequiresNew extends BaseTestCase { Transaction txn = server.beginTransaction(TxScope.requiresNew()); try { - server.find(Country.class).findRowCount(); + server.find(Country.class).findCount(); someInnerMethod(); - server.find(Product.class).findRowCount(); + server.find(Product.class).findCount(); txn.commit(); @@ -45,7 +45,7 @@ public class TestNestedBeginRequiresNew extends BaseTestCase { Transaction txn = server.beginTransaction(TxScope.requiresNew()); try { - server.find(Customer.class).findRowCount(); + server.find(Customer.class).findCount(); txn.commit(); } finally { diff --git a/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiresNewWithFailure.java b/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiresNewWithFailure.java index eea7e3175..a99324c69 100644 --- a/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiresNewWithFailure.java +++ b/src/test/java/com/avaje/tests/transaction/TestNestedBeginRequiresNewWithFailure.java @@ -25,14 +25,14 @@ public class TestNestedBeginRequiresNewWithFailure extends BaseTestCase { Transaction txn = server.beginTransaction(TxScope.requiresNew()); try { - server.find(Country.class).findRowCount(); + server.find(Country.class).findCount(); try { someInnerMethodWithFailure(); } catch (RuntimeException e) { logger.info("Inner method failed with " + e.getMessage()); } - server.find(Product.class).findRowCount(); + server.find(Product.class).findCount(); txn.commit(); @@ -48,7 +48,7 @@ public class TestNestedBeginRequiresNewWithFailure extends BaseTestCase { Transaction txn = server.beginTransaction(TxScope.requiresNew()); try { - server.find(Customer.class).findRowCount(); + server.find(Customer.class).findCount(); if (server != null) { throw new RuntimeException("barf"); diff --git a/src/test/java/com/avaje/tests/transaction/TestTransactionCallback.java b/src/test/java/com/avaje/tests/transaction/TestTransactionCallback.java index e8f3ba85c..f4e7f3370 100644 --- a/src/test/java/com/avaje/tests/transaction/TestTransactionCallback.java +++ b/src/test/java/com/avaje/tests/transaction/TestTransactionCallback.java @@ -3,6 +3,7 @@ package com.avaje.tests.transaction; import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Transaction; import com.avaje.ebean.TransactionCallbackAdapter; import org.junit.Test; @@ -27,8 +28,9 @@ public class TestTransactionCallback extends BaseTestCase { public void test_commitAndRollback() { - Ebean.beginTransaction(); + Transaction txn = Ebean.beginTransaction(); Ebean.register(new MyCallback()); + txn.getConnection(); Ebean.commitTransaction(); assertEquals(1, countPreCommit); diff --git a/src/test/java/com/avaje/tests/transaction/TestTransactionRollbackOnly.java b/src/test/java/com/avaje/tests/transaction/TestTransactionRollbackOnly.java new file mode 100644 index 000000000..5cfd3806d --- /dev/null +++ b/src/test/java/com/avaje/tests/transaction/TestTransactionRollbackOnly.java @@ -0,0 +1,58 @@ +package com.avaje.tests.transaction; + +import com.avaje.ebean.Ebean; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.annotation.Transactional; +import com.avaje.tests.model.basic.EBasic; +import org.junit.Test; + +import static org.assertj.core.api.StrictAssertions.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TestTransactionRollbackOnly { + + private EBasic one; + + private EBasic two; + + @Test + public void transaction_setRollbackOnly() { + + doVia_currentTransaction(); + + assertThat(one.getId()).isNotNull(); + assertThat(Ebean.find(EBasic.class, one.getId())).isNull(); + } + + @Transactional + protected void doVia_currentTransaction() { + + one = new EBasic("WillNotSave"); + Ebean.save(one); + + Transaction transaction = Ebean.currentTransaction(); + assertFalse(transaction.isRollbackOnly()); + + transaction.setRollbackOnly(); + assertTrue(transaction.isRollbackOnly()); + } + + @Test + public void test_Ebean_setRollbackOnly() { + + do_Ebean_setRollbackOnly(); + + assertThat(two.getId()).isNotNull(); + assertThat(Ebean.find(EBasic.class, two.getId())).isNull(); + } + + @Transactional + protected void do_Ebean_setRollbackOnly() { + + two = new EBasic("WillNotSave"); + Ebean.save(two); + + Ebean.setRollbackOnly(); + } +} diff --git a/src/test/java/com/avaje/tests/unitinternal/TestTxTypeOnTransactional.java b/src/test/java/com/avaje/tests/unitinternal/TestTxTypeOnTransactional.java index ddecf3197..63a76739a 100644 --- a/src/test/java/com/avaje/tests/unitinternal/TestTxTypeOnTransactional.java +++ b/src/test/java/com/avaje/tests/unitinternal/TestTxTypeOnTransactional.java @@ -39,8 +39,7 @@ public class TestTxTypeOnTransactional extends BaseTestCase { public void testOptimisticException() { logger.info("-- testOptimisticException"); - EBasicVer v = new EBasicVer(); - v.setName("occ"); + EBasicVer v = new EBasicVer("occ"); v.setDescription("blah"); Ebean.save(v); diff --git a/src/test/java/com/avaje/tests/update/TestMarkAsDirty.java b/src/test/java/com/avaje/tests/update/TestMarkAsDirty.java index dbca56a69..789ec6e1d 100644 --- a/src/test/java/com/avaje/tests/update/TestMarkAsDirty.java +++ b/src/test/java/com/avaje/tests/update/TestMarkAsDirty.java @@ -14,9 +14,7 @@ public class TestMarkAsDirty extends BaseTestCase { @Test public void test() throws InterruptedException { - EBasicVer bean = new EBasicVer(); - bean.setName("markAsDirty"); - + EBasicVer bean = new EBasicVer("markAsDirty"); Ebean.save(bean); Timestamp lastUpdate = bean.getLastUpdate(); diff --git a/src/test/java/com/avaje/tests/update/TestUpdateAllLoadedProperties.java b/src/test/java/com/avaje/tests/update/TestUpdateAllLoadedProperties.java index 8e83930aa..a02782991 100644 --- a/src/test/java/com/avaje/tests/update/TestUpdateAllLoadedProperties.java +++ b/src/test/java/com/avaje/tests/update/TestUpdateAllLoadedProperties.java @@ -20,13 +20,11 @@ public class TestUpdateAllLoadedProperties extends BaseTestCase { @Test public void test() { - EBasicVer basic1 = new EBasicVer(); - basic1.setName("basic1"); + EBasicVer basic1 = new EBasicVer("basic1"); basic1.setDescription("aaa"); Ebean.save(basic1); - EBasicVer basic2 = new EBasicVer(); - basic1.setName("basic2"); + EBasicVer basic2 = new EBasicVer("basic2"); basic1.setDescription("bbb"); Ebean.save(basic2);