From 1ee679c5aec8efdcbd3ef5afe33e4141e69af3e3 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 4 Dec 2014 01:20:35 +1300 Subject: [PATCH] javadoc update --- src/main/java/com/avaje/ebean/BeanState.java | 4 - src/main/java/com/avaje/ebean/Ebean.java | 6 +- .../java/com/avaje/ebean/EbeanServer.java | 784 +++++++++++++++--- src/main/java/com/avaje/ebean/Model.java | 105 ++- src/main/java/com/avaje/ebean/Page.java | 74 -- src/main/java/com/avaje/ebean/Query.java | 465 +++++++---- .../com/avaje/ebean/QueryEachConsumer.java | 16 +- .../com/avaje/ebean/annotation/Formula.java | 2 +- .../ebean/event/BeanPersistListener.java | 2 +- src/main/java/com/avaje/ebean/overview.html | 30 +- .../server/text/json/WriteJson.java | 2 +- .../ebean/elasticsearch/TestBasicPush.java | 119 ++- 12 files changed, 1218 insertions(+), 391 deletions(-) delete mode 100644 src/main/java/com/avaje/ebean/Page.java diff --git a/src/main/java/com/avaje/ebean/BeanState.java b/src/main/java/com/avaje/ebean/BeanState.java index b232ff130..ae290c37a 100644 --- a/src/main/java/com/avaje/ebean/BeanState.java +++ b/src/main/java/com/avaje/ebean/BeanState.java @@ -81,10 +81,6 @@ public interface BeanState { * {@link EbeanServer#createEntityBean(Class)}, then populate its properties * and then call this method specifying which properties where loaded or null * for a fully loaded entity bean. - * - * @param loadedProperties - * the properties that where loaded or null for a fully loaded entity - * bean. */ public void setLoaded(); } \ 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 3730980d0..9626761b2 100644 --- a/src/main/java/com/avaje/ebean/Ebean.java +++ b/src/main/java/com/avaje/ebean/Ebean.java @@ -671,7 +671,8 @@ public final class Ebean { /** * Refresh the values of a bean. *

- * Note that this does not refresh any OneToMany or ManyToMany properties. + * Note that this resets OneToMany and ManyToMany properties so that if they + * are accessed a lazy load will refresh the many property. *

*/ public static void refresh(Object bean) { @@ -1376,8 +1377,7 @@ public final class Ebean { /** * Return the BeanState for a given entity bean. *

- * This will return null if the bean is not an enhanced (or subclassed) entity - * bean. + * This will return null if the bean is not an enhanced entity bean. *

*/ public static BeanState getBeanState(Object bean) { diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java index 3d506826f..f78d82727 100644 --- a/src/main/java/com/avaje/ebean/EbeanServer.java +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -131,8 +131,7 @@ public interface EbeanServer { /** * Return the BeanState for a given entity bean. *

- * This will return null if the bean is not an enhanced (or subclassed) entity - * bean. + * This will return null if the bean is not an enhanced entity bean. *

*/ public BeanState getBeanState(Object bean); @@ -152,14 +151,10 @@ public interface EbeanServer { public Map diff(Object a, Object b); /** - * Create a new instance of T that is an EntityBean (for subclassing). + * Create a new instance of T that is an EntityBean. *

- * Note that if you are using enhancement (rather than subclassing) then you - * do not need to use this method and just new up a bean. - *

- *

- * Potentially useful when using subclassing and you wish to programmatically - * load a entity bean . Otherwise this method is generally not required. + * Generally not expected to be useful (now dynamic subclassing support was removed in + * favour of always using enhancement). *

*/ public T createEntityBean(Class type); @@ -170,13 +165,22 @@ public interface EbeanServer { public CsvReader createCsvReader(Class beanType); /** - * Create a named query for an entity bean (refer - * {@link Ebean#createQuery(Class, String)}) + * Return a named Query that will have defined fetch paths, predicates etc. *

- * The query statement will be defined in a deployment orm xml file. + * The query is created from a statement that will be defined in a deployment + * orm xml file or NamedQuery annotations. The query will typically already + * define fetch paths, predicates, order by clauses etc so often you will just + * need to bind required parameters and then execute the query. *

- * - * @see Ebean#createQuery(Class, String) + * + *
{@code
+   *
+   *   // example
+   *   Query query = ebeanServer.createNamedQuery(Order.class, "new.for.customer");
+   *   query.setParameter("customerId", 23);
+   *   List newOrders = query.findList();
+   *
+   * }
*/ public Query createNamedQuery(Class beanType, String namedQuery); @@ -207,14 +211,51 @@ public interface EbeanServer { public Query createQuery(Class beanType, String query); /** - * Create a query for an entity bean (refer {@link Ebean#createQuery(Class)}). - * - * @see Ebean#createQuery(Class) + * Create a query for an entity bean and synonym for {@link #find(Class)}. + * + * @see #find(Class) */ public Query createQuery(Class beanType); /** - * Create a query for a type of entity bean (the same as {@link EbeanServer#createQuery(Class)}). + * Create a query for a type of entity bean. + *

+ * You can use the methods on the Query object to specify fetch paths, + * predicates, order by, limits etc. + *

+ *

+ * You then use findList(), findSet(), findMap() and findUnique() to execute + * the query and return the collection or bean. + *

+ *

+ * Note that a query executed by {@link Query#findList()} + * {@link Query#findSet()} etc will execute against the same EbeanServer from + * which is was created. + *

+ * + *
{@code
+   *
+   *   // Find order 2 specifying explicitly the parts of the object graph to
+   *   // eagerly fetch. In this case eagerly fetch the associated customer,
+   *   // details and details.product.name
+   *
+   *   Order order = ebeanServer.find(Order.class)
+   *     .fetch("customer")
+   *     .fetch("details")
+   *     .fetch("detail.product", "name")
+   *     .setId(2)
+   *     .findUnique();
+   *
+   *   // find some new orders ... with firstRow/maxRows
+   *   List orders =
+   *     ebeanServer.find(Order.class)
+   *       .where().eq("status", Order.Status.NEW)
+   *       .setFirstRow(20)
+   *       .setMaxRows(10)
+   *       .findList();
+   *
+   * }
+ * */ public Query find(Class beanType); @@ -233,15 +274,54 @@ public interface EbeanServer { public Object nextId(Class beanType); /** - * Create a filter for filtering lists of entity beans. + * Create a filter for sorting and filtering lists of entities locally without + * going back to the database. + *

+ * This produces and returns a new list with the sort and filters applied. + *

+ *

+ * Refer to {@link Filter} for an example of its use. + *

*/ public Filter filter(Class beanType); /** - * Sort the list using the sortByClause. - * - * @see Ebean#sort(List, String) - * + * Sort the list in memory using the sortByClause which can contain a comma delimited + * list of property names and keywords asc, desc, nullsHigh and nullsLow. + *
    + *
  • asc - ascending order (which is the default)
  • + *
  • desc - Descending order
  • + *
  • nullsHigh - Treat null values as high/large values (which is the + * default)
  • + *
  • nullsLow- Treat null values as low/very small values
  • + *
+ *

+ * If you leave off any keywords the defaults are ascending order and treating + * nulls as high values. + *

+ *

+ * Note that the sorting uses a Comparator and Collections.sort(); and does + * not invoke a DB query. + *

+ * + *
{@code
+   *
+   *   // find orders and their customers
+   *   List list = ebeanServer.find(Order.class)
+   *     .fetch("customer")
+   *     .orderBy("id")
+   *     .findList();
+   *
+   *   // sort by customer name ascending, then by order shipDate
+   *   // ... then by the order status descending
+   *   ebeanServer.sort(list, "customer.name, shipDate, status desc");
+   *
+   *   // sort by customer name descending (with nulls low)
+   *   // ... then by the order id
+   *   ebeanServer.sort(list, "customer.name desc nullsLow, id");
+   *
+   * }
+ * * @param list * the list of entity beans * @param sortByClause @@ -250,39 +330,126 @@ public interface EbeanServer { public void sort(List list, String sortByClause); /** - * Create a named update for an entity bean (refer - * {@link Ebean#createNamedUpdate(Class, String)}). + * Create a named orm update. The update statement is specified via the + * NamedUpdate annotation. + *

+ * The orm update differs from the SqlUpdate in that it uses the bean name and + * bean property names rather than table and column names. + *

+ *

+ * Note that named update statements can be specified in raw sql (with column + * and table names) or using bean name and bean property names. This can be + * specified with the isSql flag. + *

+ *

+ * Example named updates: + *

+ * + *
{@code
+   *   package app.data;
+   *
+   *   import ...
+   *
+   *   @NamedUpdates(value = {
+   *    @NamedUpdate( name = "setTitle",
+   * 	    isSql = false,
+   * 		  notifyCache = false,
+   * 		  update = "update topic set title = :title, postCount = :postCount where id = :id"),
+   * 	  @NamedUpdate( name = "setPostCount",
+   * 		  notifyCache = false,
+   * 		  update = "update f_topic set post_count = :postCount where id = :id"),
+   * 	  @NamedUpdate( name = "incrementPostCount",
+   * 		  notifyCache = false,
+   * 		  isSql = false,
+   * 		  update = "update Topic set postCount = postCount + 1 where id = :id") })
+   *   @Entity
+   *   @Table(name = "f_topic")
+   *   public class Topic { ...
+   *
+   * }
+ * + *

+ * Example using a named update: + *

+ * + *
{@code
+   *
+   *   Update update = ebeanServer.createNamedUpdate(Topic.class, "setPostCount");
+   *   update.setParameter("postCount", 10);
+   *   update.setParameter("id", 3);
+   *
+   *   int rows = update.execute();
+   *   System.out.println("rows updated: " + rows);
+   *
+   * }
*/ public Update createNamedUpdate(Class beanType, String namedUpdate); /** - * Create a update for an entity bean where you will manually specify the - * insert update or delete statement. + * Create a orm update where you will supply the insert/update or delete + * statement (rather than using a named one that is already defined using the + * @NamedUpdates annotation). + *

+ * The orm update differs from the sql update in that it you can use the bean + * name and bean property names rather than table and column names. + *

+ *

+ * An example: + *

+ * + *
{@code
+   *
+   *   // The bean name and properties - "topic","postCount" and "id"
+   *
+   *   // will be converted into their associated table and column names
+   *   String updStatement = "update topic set postCount = :pc where id = :id";
+   *
+   *   Update update = ebeanServer.createUpdate(Topic.class, updStatement);
+   *
+   *   update.set("pc", 9);
+   *   update.set("id", 3);
+   *
+   *   int rows = update.execute();
+   *   System.out.println("rows updated:" + rows);
+   *
+   * }
*/ public Update createUpdate(Class beanType, String ormUpdate); /** - * Create a sql query for executing native sql query statements (refer - * {@link Ebean#createSqlQuery(String)}). - * - * @see Ebean#createSqlQuery(String) + * Create a SqlQuery for executing native sql + * query statements. + *

+ * Note that you can use raw SQL with entity beans, refer to the SqlSelect + * annotation for examples. + *

*/ public SqlQuery createSqlQuery(String sql); /** - * Create a named sql query (refer {@link Ebean#createNamedSqlQuery(String)}). + * Create a named sql query. *

* The query statement will be defined in a deployment orm xml file. *

- * - * @see Ebean#createNamedSqlQuery(String) + * + * @param namedQuery + * the name of the query */ public SqlQuery createNamedSqlQuery(String namedQuery); /** - * Create a sql update for executing native dml statements (refer {@link Ebean#createSqlUpdate(String)}). - * - * @see Ebean#createSqlUpdate(String) + * Create a sql update for executing native dml statements. + *

+ * Use this to execute a Insert Update or Delete statement. The statement will + * be native to the database and contain database table and column names. + *

+ *

+ * See {@link SqlUpdate} for example usage. + *

+ *

+ * Where possible it would be expected practice to put the statement in a orm + * xml file (named update) and use {@link #createNamedSqlUpdate(String)} . + *

*/ public SqlUpdate createSqlUpdate(String sql); @@ -292,13 +459,23 @@ public interface EbeanServer { public CallableSql createCallableSql(String callableSql); /** - * Create a named sql update (refer {@link Ebean#createNamedSqlUpdate(String)}). + * Create a named sql update. *

* The statement (an Insert Update or Delete statement) will be defined in a * deployment orm xml file. *

- * - * @see Ebean#createNamedSqlUpdate(String) + * + *
{@code
+   *
+   *   // Use a namedQuery
+   *   UpdateSql update = Ebean.createNamedSqlUpdate("update.topic.count");
+   *
+   *   update.setParameter("count", 1);
+   *   update.setParameter("topicId", 50);
+   *
+   *   int modifiedCount = update.execute();
+   *
+   * }
*/ public SqlUpdate createNamedSqlUpdate(String namedQuery); @@ -332,9 +509,42 @@ public interface EbeanServer { public Transaction createTransaction(TxIsolation isolation); /** - * Start a new transaction putting it into a ThreadLocal. - * - * @see Ebean#beginTransaction() + * Start a new explicit transaction putting it into a ThreadLocal. + *

+ * The transaction is stored in a ThreadLocal variable and typically you only + * need to use the returned Transaction IF you wish to do things like + * use batch mode, change the transaction isolation level, use savepoints or + * log comments to the transaction log. + *

+ *

+ * Example of using a transaction to span multiple calls to find(), save() + * etc. + *

+ * + *
{@code
+   *
+   *   // start a transaction (stored in a ThreadLocal)
+   *   ebeanServer.beginTransaction();
+   *   try {
+   * 	   Order order = ebeanServer.find(Order.class,10); ...
+   *
+   * 	   ebeanServer.save(order);
+   *
+   * 	   ebeanServer.commitTransaction();
+   *
+   *   } finally {
+   * 	   // rollback if we didn't commit
+   * 	   // i.e. an exception occurred before commitTransaction().
+   * 	   ebeanServer.endTransaction();
+   *   }
+   *
+   * }
+ * + *

+ * If you want to externalise the transaction management then you use + * createTransaction() and pass the transaction around to the various methods on + * EbeanServer yourself. + *

*/ public Transaction beginTransaction(); @@ -350,15 +560,11 @@ public interface EbeanServer { /** * Commit the current transaction. - * - * @see Ebean#commitTransaction() */ public void commitTransaction(); /** * Rollback the current transaction. - * - * @see Ebean#rollbackTransaction() */ public void rollbackTransaction(); @@ -373,7 +579,8 @@ public interface EbeanServer { * Code example: * *
{@code
-   *   ebeanServer.startTransaction();
+   *
+   *   ebeanServer.beginTransaction();
    *   try {
    *     // do some fetching and or persisting ...
    * 
@@ -384,21 +591,20 @@ public interface EbeanServer {
    *     // if commit didn't occur then rollback the transaction
    *     ebeanServer.endTransaction();
    *   }
+   *
    * }
* *

- * - * @see Ebean#endTransaction() + * */ public void endTransaction(); /** * Refresh the values of a bean. *

- * Note that this does not refresh any OneToMany or ManyToMany properties. + * Note that this resets OneToMany and ManyToMany properties so that if they + * are accessed a lazy load will refresh the many property. *

- * - * @see Ebean#refresh(Object) */ public void refresh(Object bean); @@ -409,34 +615,98 @@ public interface EbeanServer { * the entity bean containing the 'many' property * @param propertyName * the 'many' property to be refreshed - * - * @see Ebean#refreshMany(Object, String) + * */ public void refreshMany(Object bean, String propertyName); /** * Find a bean using its unique id. - * - * @see Ebean#find(Class, Object) + * + *
{@code
+   *   // Fetch order 1
+   *   Order order = ebeanServer.find(Order.class, 1);
+   * }
+ * + *

+ * If you want more control over the query then you can use createQuery() and + * Query.findUnique(); + *

+ * + *
{@code
+   *   // ... additionally fetching customer, customer shipping address,
+   *   // order details, and the product associated with each order detail.
+   *   // note: only product id and name is fetch (its a "partial object").
+   *   // note: all other objects use "*" and have all their properties fetched.
+   *
+   *   Query query = ebeanServer.find(Order.class)
+   *     .setId(1)
+   *     .fetch("customer")
+   *     .fetch("customer.shippingAddress")
+   *     .fetch("details")
+   *     .query();
+   *
+   *   // fetch associated products but only fetch their product id and name
+   *   query.fetch("details.product", "name");
+   *
+   *
+   *   Order order = query.findUnique();
+   *
+   *   // traverse the object graph...
+   *
+   *   Customer customer = order.getCustomer();
+   *   Address shippingAddress = customer.getShippingAddress();
+   *   List details = order.getDetails();
+   *   OrderDetail detail0 = details.get(0);
+   *   Product product = detail0.getProduct();
+   *   String productName = product.getName();
+   *
+   * }
+ * + * @param beanType + * the type of entity bean to fetch + * @param id + * the id value */ - public T find(Class beanType, Object uid); + public T find(Class beanType, Object id); /** - * Get a reference bean (see {@link Ebean#getReference(Class, Object)}. + * Get a reference object. *

- * This will not perform a query against the database. + * This will not perform a query against the database unless some property other + * that the id property is accessed. + *

+ *

+ * It is most commonly used to set a 'foreign key' on another bean like: *

*
{@code
-   * Product product = Ebean.getReference(Product.class, 1);
-   * 
-   * // You can get the id without causing a fetch/lazy load
-   * Integer productId = product.getId();
-   * 
-   * // If you try to get any other property a fetch/lazy loading will occur
-   * // This will cause a query to execute...
-   * String name = product.getName();
+   *
+   *   Product product = ebeanServer.getReference(Product.class, 1);
+   *
+   *   OrderDetail orderDetail = new OrderDetail();
+   *   // set the product 'foreign key'
+   *   orderDetail.setProduct(product);
+   *   orderDetail.setQuantity(42);
+   *   ...
+   *
+   *   ebeanServer.save(orderDetail);
+   *
+   *
    * }
- * + * + *

Lazy loading characteristics

+ *
{@code
+   *
+   *   Product product = ebeanServer.getReference(Product.class, 1);
+   *
+   *   // You can get the id without causing a fetch/lazy load
+   *   Long productId = product.getId();
+   *
+   *   // If you try to get any other property a fetch/lazy loading will occur
+   *   // This will cause a query to execute...
+   *   String name = product.getName();
+   *
+   * }
+ * * @param beanType * the type of entity bean * @param id @@ -447,35 +717,100 @@ public interface EbeanServer { /** * Return the number of 'top level' or 'root' entities this query should * return. + * + * @see Query#findRowCount() + * @see com.avaje.ebean.Query#findFutureRowCount() */ public int findRowCount(Query query, Transaction transaction); /** * Return the Id values of the query as a List. + * + * @see com.avaje.ebean.Query#findIds() */ public List findIds(Query query, Transaction transaction); /** - * Return a QueryIterator for the query. This is similar to findVisit in that - * not all the result beans need to be held in memory at the same time and as - * such is go for processing large queries. + * 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#findEach(QueryEachConsumer) + * @see Query#findEachWhile(QueryEachWhileConsumer) */ public QueryIterator findIterate(Query query, Transaction transaction); /** - * Execute the query visiting the results. This is similar to findIterate in - * that not all the result beans need to be held in memory at the same time - * and as such is go for processing large queries. + * Execute the query visiting the each bean one at a time. + *

+ * Unlike findList() this is suitable for processing a query that will return + * a very large resultSet. The reason is that not all the result beans need to be + * held in memory at the same time and instead processed one at a time. + *

+ *

+ * Internally this query using a PersistenceContext scoped to each bean (and the + * beans associated object graph). + *

+ * + *
{@code
+   *
+   *     ebeanServer.find(Order.class)
+   *       .where().eq("status", Order.Status.NEW)
+   *       .order().asc("id")
+   *       .findEach((Order order) -> {
+   *
+   *         // do something with the order bean
+   *         System.out.println(" -- processing order ... " + order);
+   *       });
+   *
+   * }
* * @see Query#findEach(QueryEachConsumer) + * @see Query#findEachWhile(QueryEachWhileConsumer) */ public void findEach(Query query, QueryEachConsumer consumer, Transaction transaction); /** - * Execute the query visiting the results. This is similar to findIterate in - * that not all the result beans need to be held in memory at the same time - * and as such is go for processing large queries. + * Execute the query visiting the each bean one at a time. + *

+ * Compared to findEach() this provides the ability to stop processing the query + * results early by returning false for the QueryEachWhileConsumer. + *

+ *

+ * Unlike findList() this is suitable for processing a query that will return + * a very large resultSet. The reason is that not all the result beans need to be + * held in memory at the same time and instead processed one at a time. + *

+ *

+ * Internally this query using a PersistenceContext scoped to each bean (and the + * beans associated object graph). + *

* + *
{@code
+   *
+   *     ebeanServer.find(Order.class)
+   *       .where().eq("status", Order.Status.NEW)
+   *       .order().asc("id")
+   *       .findEachWhile((Order order) -> {
+   *
+   *         // do something with the order bean
+   *         System.out.println(" -- processing order ... " + order);
+   *
+   *         boolean carryOnProcessing = ...
+   *         return carryOnProcessing;
+   *       });
+   *
+   * }
+ * + * @see Query#findEach(QueryEachConsumer) * @see Query#findEachWhile(QueryEachWhileConsumer) */ public void findEachWhile(Query query, QueryEachWhileConsumer consumer, Transaction transaction); @@ -500,7 +835,16 @@ public interface EbeanServer { * explicitly calling this method. You could use this method if you wish to * explicitly control the transaction used for the query. *

- * + * + *
{@code
+   *
+   * List customers =
+   *     ebeanServer.find(Customer.class)
+   *     .where().ilike("name", "rob%")
+   *     .findList();
+   *
+   * }
+ * * @param * the type of entity bean to fetch. * @param query @@ -508,6 +852,7 @@ public interface EbeanServer { * @param transaction * the transaction to use (can be null). * @return the list of fetched beans. + * * @see Query#findList() */ public List findList(Query query, Transaction transaction); @@ -525,6 +870,8 @@ public interface EbeanServer { * @param transaction * the transaction (can be null). * @return a Future object for the row count query + * + * @see com.avaje.ebean.Query#findFutureRowCount() */ public FutureRowCount findFutureRowCount(Query query, Transaction transaction); @@ -541,6 +888,8 @@ public interface EbeanServer { * @param transaction * the transaction (can be null). * @return a Future object for the list of Id's + * + * @see com.avaje.ebean.Query#findFutureIds() */ public FutureIds findFutureIds(Query query, Transaction transaction); @@ -559,6 +908,8 @@ public interface EbeanServer { * @param transaction * the transaction (can be null). * @return a Future object for the list result of the query + * + * @see Query#findFutureList() */ public FutureList findFutureList(Query query, Transaction transaction); @@ -596,6 +947,8 @@ public interface EbeanServer { * @param pageSize * The number of beans to return per page. * @return The PagedList + * + * @see Query#findPagedList(int, int) */ public PagedList findPagedList(Query query, Transaction transaction, int pageIndex, int pageSize); @@ -606,6 +959,15 @@ public interface EbeanServer { * explicitly calling this method. You could use this method if you wish to * explicitly control the transaction used for the query. *

+ * + *
{@code
+   *
+   * Set customers =
+   *     ebeanServer.find(Customer.class)
+   *     .where().ilike("name", "rob%")
+   *     .findSet();
+   *
+   * }
* * @param * the type of entity bean to fetch. @@ -614,6 +976,7 @@ public interface EbeanServer { * @param transaction * the transaction to use (can be null). * @return the set of fetched beans. + * * @see Query#findSet() */ public Set findSet(Query query, Transaction transaction); @@ -633,6 +996,7 @@ public interface EbeanServer { * @param transaction * the transaction to use (can be null). * @return the map of fetched beans. + * * @see Query#findMap() */ public Map findMap(Query query, Transaction transaction); @@ -653,6 +1017,7 @@ public interface EbeanServer { * @param transaction * the transaction to use (can be null). * @return the list of fetched beans. + * * @see Query#findUnique() */ public T findUnique(Query query, Transaction transaction); @@ -670,6 +1035,7 @@ public interface EbeanServer { * @param transaction * the transaction to use (can be null). * @return the list of fetched MapBean. + * * @see SqlQuery#findList() */ public List findList(SqlQuery query, Transaction transaction); @@ -687,6 +1053,7 @@ public interface EbeanServer { * @param transaction * the transaction to use (can be null). * @return the set of fetched MapBean. + * * @see SqlQuery#findSet() */ public Set findSet(SqlQuery query, Transaction transaction); @@ -704,6 +1071,7 @@ public interface EbeanServer { * @param transaction * the transaction to use (can be null). * @return the set of fetched MapBean. + * * @see SqlQuery#findMap() */ public Map findMap(SqlQuery query, Transaction transaction); @@ -725,14 +1093,43 @@ public interface EbeanServer { * @param transaction * the transaction to use (can be null). * @return the fetched MapBean or null if none was found. + * * @see SqlQuery#findUnique() */ public SqlRow findUnique(SqlQuery query, Transaction transaction); /** - * Persist the bean by either performing an insert or update. - * - * @see Ebean#save(Object) + * Either Insert or Update the bean depending on its state. + *

+ * If there is no current transaction one will be created and committed for + * you automatically. + *

+ *

+ * Save can cascade along relationships. For this to happen you need to + * specify a cascade of CascadeType.ALL or CascadeType.PERSIST on the + * OneToMany, OneToOne or ManyToMany annotation. + *

+ *

+ * In this example below the details property has a CascadeType.ALL set so + * saving an order will also save all its details. + *

+ * + *
{@code
+   *   public class Order { ...
+   *
+   * 	   @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
+   * 	   @JoinColumn(name="order_id")
+   * 	   List details;
+   * 	   ...
+   *   }
+   * }
+ * + *

+ * When a save cascades via a OneToMany or ManyToMany Ebean will automatically + * set the 'parent' object to the 'detail' object. In the example below in + * saving the order and cascade saving the order details the 'parent' order + * will be set against each order detail when it is saved. + *

*/ public void save(Object bean) throws OptimisticLockException; @@ -748,8 +1145,10 @@ public interface EbeanServer { /** * Delete the bean. - * - * @see Ebean#delete(Object) + *

+ * If there is no current transaction one will be created and committed for + * you automatically. + *

*/ public void delete(Object bean) throws OptimisticLockException; @@ -785,16 +1184,47 @@ public interface EbeanServer { public void delete(Class beanType, Collection ids, Transaction transaction); /** - * Execute a SQL Update Delete or Insert statement using the current - * transaction. This returns the number of rows that where updated, deleted or - * inserted. + * Execute a Sql Update Delete or Insert statement. This returns the number of + * rows that where updated, deleted or inserted. If is executed in batch then + * this returns -1. You can get the actual rowCount after commit() from + * updateSql.getRowCount(). *

- * Refer to Ebean.execute(UpdateSql) for full documentation. + * If you wish to execute a Sql Select natively then you should use the + * FindByNativeSql object. *

- * - * @see Ebean#execute(SqlUpdate) + *

+ * Note that the table modification information is automatically deduced and + * you do not need to call the Ebean.externalModification() method when you + * use this method. + *

+ *

+ * Example: + *

+ * + *
{@code
+   *
+   *   // example that uses 'named' parameters
+   *   String s = "UPDATE f_topic set post_count = :count where id = :id"
+   *
+   *   SqlUpdate update = ebeanServer.createSqlUpdate(s);
+   *
+   *   update.setParameter("id", 1);
+   *   update.setParameter("count", 50);
+   *
+   *   int modifiedCount = ebeanServer.execute(update);
+   *
+   *   String msg = "There where " + modifiedCount + "rows updated";
+   *
+   * }
+ * + * @param sqlUpdate + * the update sql potentially with bind values + * + * @return the number of rows updated or deleted. -1 if executed in batch. + * + * @see CallableSql */ - public int execute(SqlUpdate updSql); + public int execute(SqlUpdate sqlUpdate); /** * Execute a ORM insert update or delete statement using the current @@ -812,27 +1242,67 @@ public interface EbeanServer { public int execute(Update update, Transaction t); /** - * Call a stored procedure. + * For making calls to stored procedures. *

- * Refer to Ebean.execute(CallableSql) for full documentation. + * Example: *

- * - * @see Ebean#execute(CallableSql) + * + *
{@code
+   *
+   *   String sql = "{call sp_order_modify(?,?,?)}";
+   *
+   *   CallableSql cs = ebeanServer.createCallableSql(sql);
+   *   cs.setParameter(1, 27);
+   *   cs.setParameter(2, "SHIPPED");
+   *   cs.registerOut(3, Types.INTEGER);
+   *
+   *   ebeanServer.execute(cs);
+   *
+   *   // read the out parameter
+   *   Integer returnValue = (Integer) cs.getObject(3);
+   *
+   * }
+ * + * @see CallableSql + * @see Ebean#execute(SqlUpdate) */ public int execute(CallableSql callableSql); /** - * Process committed changes from another framework. + * Inform Ebean that tables have been modified externally. These could be the + * result of from calling a stored procedure, other JDBC calls or external + * programs including other frameworks. *

- * This notifies this instance of the framework that beans have been committed - * externally to it. Either by another framework or clustered server. It uses - * this to maintain its cache and text indexes appropriately. + * If you use ebeanServer.execute(UpdateSql) then the table modification information + * is automatically deduced and you do not need to call this method yourself. *

- * - * @see Ebean#externalModification(String, boolean, boolean, boolean) + *

+ * This information is used to invalidate objects out of the cache and + * potentially text indexes. This information is also automatically broadcast + * across the cluster. + *

+ *

+ * If there is a transaction then this information is placed into the current + * transactions event information. When the transaction is committed this + * information is registered (with the transaction manager). If this + * transaction is rolled back then none of the transaction event information + * registers including the information you put in via this method. + *

+ *

+ * If there is NO current transaction when you call this method then this + * information is registered immediately (with the transaction manager). + *

+ * + * @param tableName + * the name of the table that was modified + * @param inserted + * true if rows where inserted into the table + * @param updated + * true if rows on the table where updated + * @param deleted + * true if rows on the table where deleted */ - public void externalModification(String tableName, boolean inserted, boolean updated, - boolean deleted); + public void externalModification(String tableName, boolean inserted, boolean updated, boolean deleted); /** * Find a entity bean with an explicit transaction. @@ -955,6 +1425,11 @@ public interface EbeanServer { /** * Insert the bean. + *

+ * Compared to save() this forces bean to perform an insert rather than trying to decide + * based on the bean state. As such this is useful when you fetch beans from one database + * and want to insert them into another database (and you want to explicitly insert them). + *

*/ public void insert(Object bean); @@ -1086,6 +1561,20 @@ public interface EbeanServer { * The scope can control the transaction type, isolation and rollback * semantics. *

+ * + *
{@code
+   *
+   *   // set specific transactional scope settings
+   *   TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
+   *
+   *   ebeanServer.execute(scope, new TxRunnable() {
+   * 	   public void run() {
+   * 		   User u1 = Ebean.find(User.class, 1);
+   * 		   ...
+   * 	   }
+   *   });
+   *
+   * }
*/ public void execute(TxScope scope, TxRunnable r); @@ -1095,6 +1584,23 @@ public interface EbeanServer { * The default scope runs with REQUIRED and by default will rollback on any * exception (checked or runtime). *

+ * + *
{@code
+   *
+   *   ebeanServer.execute(new TxRunnable() {
+   *     public void run() {
+   *       User u1 = ebeanServer.find(User.class, 1);
+   *       User u2 = ebeanServer.find(User.class, 2);
+   *
+   *       u1.setName("u1 mod");
+   *       u2.setName("u2 mod");
+   *
+   *       ebeanServer.save(u1);
+   *       ebeanServer.save(u2);
+   *     }
+   *   });
+   *
+   * }
*/ public void execute(TxRunnable r); @@ -1104,6 +1610,21 @@ public interface EbeanServer { * The scope can control the transaction type, isolation and rollback * semantics. *

+ * + *
{@code
+   *
+   *   // set specific transactional scope settings
+   *   TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
+   *
+   *   ebeanServer.execute(scope, new TxCallable() {
+   * 	   public String call() {
+   * 		   User u1 = ebeanServer.find(User.class, 1);
+   * 		   ...
+   * 		   return u1.getEmail();
+   * 	   }
+   *   });
+   *
+   * }
*/ public T execute(TxScope scope, TxCallable c); @@ -1113,6 +1634,29 @@ public interface EbeanServer { * The default scope runs with REQUIRED and by default will rollback on any * exception (checked or runtime). *

+ *

+ * This is basically the same as TxRunnable except that it returns an Object + * (and you specify the return type via generics). + *

+ * + *
{@code
+   *
+   *   ebeanServer.execute(new TxCallable() {
+   *     public String call() {
+   *       User u1 = ebeanServer.find(User.class, 1);
+   *       User u2 = ebeanServer.find(User.class, 2);
+   *
+   *       u1.setName("u1 mod");
+   *       u2.setName("u2 mod");
+   *
+   *       ebeanServer.save(u1);
+   *       ebeanServer.save(u2);
+   *
+   *       return u1.getEmail();
+   *     }
+   *   });
+   *
+   * }
*/ public T execute(TxCallable c); @@ -1153,8 +1697,40 @@ public interface EbeanServer { /** * Return the JsonContext for reading/writing JSON. *

- * This instance is safe to be used concurrently by multiple threads and this method is cheap to call. + * This instance is safe to be used concurrently by multiple threads and this + * method is cheap to call. *

+ * + *

Simple example:

+ *
{@code
+   *
+   *     JsonContext json = ebeanServer.json();
+   *     String jsonOutput = json.toJson(list);
+   *     System.out.println(jsonOutput);
+   *
+   * }
+ * + *

Using PathProperties:

+ *
{@code
+   *
+   *     // specify just the properties we want
+   *     PathProperties paths = PathProperties.parse("name, status, anniversary");
+   *
+   *     List customers =
+   *       ebeanServer.find(Customer.class)
+   *         // apply those paths to the query (only fetch what we need)
+   *         .apply(paths)
+   *         .where().ilike("name", "rob%")
+   *         .findList();
+   *
+   *     // ... get the json
+   *     JsonContext jsonContext = ebeanServer.json();
+   *     String json = jsonContext.toJson(customers, paths);
+   *
+   * }
+ * + * @see com.avaje.ebean.text.PathProperties + * @see Query#apply(com.avaje.ebean.text.PathProperties) */ public JsonContext json(); diff --git a/src/main/java/com/avaje/ebean/Model.java b/src/main/java/com/avaje/ebean/Model.java index 73e291a4f..a09a98cf1 100644 --- a/src/main/java/com/avaje/ebean/Model.java +++ b/src/main/java/com/avaje/ebean/Model.java @@ -24,11 +24,94 @@ import javax.persistence.MappedSuperclass; *

* You may choose not use this Model mapped superclass if you don't like the 'Active Record' style * or if you believe it 'pollutes' your entity beans. - * + * + *

+ * You can use Dependency Injection like Guice or Spring to construct and wire a EbeanServer instance + * and have that same instance used with this Model and Finder. The way that works is that when the + * DI container creates the EbeanServer instance it can be registered with the Ebean singleton. In this + * way the EbeanServer instance can be injected as per normal Guice / Spring dependency injection and + * that same instance also used to support the Model and Finder active record style. + * *

* If you choose to use the Model mapped superclass you will probably also chose to additionally add * a {@link Finder} as a public static field to complete the active record pattern and provide a * relatively nice clean way to write queries. + * + *

Typical common @MappedSuperclass

+ *
{@code
+ *
+ *     // Typically there is a common base model that has some
+ *     // common properties like the ones below
+ *
+ *     @MappedSuperclass
+ *     public class BaseModel extends Model {
+ *
+ *       @Id Long id;
+ *
+ *       @Version Long version;
+ *
+ *       @CreatedTimestamp Timestamp whenCreated;
+ *
+ *       @UpdatedTimestamp Timestamp whenUpdated;
+ *
+ *       ...
+ *
+ * }
+ * + *

Extend the Model

+ *
{@code
+ *
+ *     // Extend the mappedSuperclass
+ *
+ *     @Entity @Table(name="oto_account")
+ *     public class Account extends BaseModel {
+ *
+ *       // add a static Finder
+ *       // ... with Long being the type of our ID property ...
+ *
+ *       public static final Finder find =
+ *             new Finder(Long.class, Account.class);
+ *
+ *       String name;
+ *
+ *       @OneToOne(mappedBy = "account",optional = true)
+ *       User user;
+ *
+ *       ...
+ *     }
+ *
+ * }
+ * + *

Modal: save()

+ *
{@code
+ *
+ *     // Active record style ... save(), delete() etc
+ *     Account account = new Account();
+ *     account.setName("AC234");
+ *
+ *     // save() method inherited from Model
+ *     account.save();
+ *
+ * }
+ * + *

Finder: find byId

+ *
{@code
+ *
+ *     // find byId
+ *     Account account = Account.find.byId(42);
+ *
+ * }
+ * + *

Finder: find where

+ *
{@code
+ *
+ *     // find where ...
+ *     List accounts =
+ *         Account.find
+ *         .where().gt("startDate", lastMonth)
+ *         .findList();
+ *
+ * }
*/ @MappedSuperclass public abstract class Model { @@ -105,7 +188,9 @@ public abstract class Model { * customer.markAsDirty(); * customer.save(); * - * + * + * + * @see EbeanServer#markAsDirty(Object) */ public void markAsDirty() { db().markAsDirty(this); @@ -117,6 +202,8 @@ public abstract class Model { *

* Ebean will detect if this is a new bean or a previously fetched bean and perform either an * insert or an update based on that. + * + * @see EbeanServer#save(Object) */ public void save() { db().save(this); @@ -124,6 +211,8 @@ public abstract class Model { /** * Update this entity. + * + * @see EbeanServer#update(Object) */ public void update() { db().update(this); @@ -131,6 +220,8 @@ public abstract class Model { /** * Insert this entity. + * + * @see EbeanServer#insert(Object) */ public void insert() { db().insert(this); @@ -138,6 +229,8 @@ public abstract class Model { /** * Delete this entity. + * + * @see EbeanServer#delete(Object) */ public void delete() { db().delete(this); @@ -166,6 +259,8 @@ public abstract class Model { /** * Refreshes this entity from the database. + * + * @see EbeanServer#refresh(Object) */ public void refresh() { db().refresh(this); @@ -250,6 +345,8 @@ public abstract class Model { /** * Delete a bean by Id. + *

+ * Equivalent to {@link EbeanServer#delete(Class, Object)} */ public void deleteById(I id) { db().delete(type, id); @@ -306,6 +403,8 @@ public abstract class Model { /** * Creates a query applying the path properties to set the select and fetch clauses. + *

+ * Equivalent to {@link Query#apply(com.avaje.ebean.text.PathProperties)} */ public Query apply(PathProperties pathProperties) { return db().find(type).apply(pathProperties); @@ -346,6 +445,8 @@ public abstract class Model { /** * Execute the query consuming each bean one at a time. *

+ * Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)} + *

* This is similar to #findEach except that you return boolean * true to continue processing beans and return false to stop * processing early. diff --git a/src/main/java/com/avaje/ebean/Page.java b/src/main/java/com/avaje/ebean/Page.java deleted file mode 100644 index 626e29dd9..000000000 --- a/src/main/java/com/avaje/ebean/Page.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.avaje.ebean; - -import java.util.List; - -/** - * Represents a Page of results that is part of a PagingList. - *

- * Typically a Page represents the data that is shown to the user at a single - * time - and the user 'pages' through a large list. - *

- * - * @author rbygrave - * - * @param - * the entity bean type - * - * @see Query#findPagingList(int) - * @see PagingList - */ -public interface Page { - - /** - * Return the list of entities for this page. - */ - public List getList(); - - /** - * Return the total row count for all pages. - */ - public int getTotalRowCount(); - - /** - * Return the total number of pages. - */ - public int getTotalPageCount(); - - /** - * Return the index position of this page. - */ - public int getPageIndex(); - - /** - * Return true if there is a next page. - */ - public boolean hasNext(); - - /** - * Return true if there is a previous page. - */ - public boolean hasPrev(); - - /** - * Return the next page. - */ - public Page next(); - - /** - * Return the previous page. - */ - public Page prev(); - - /** - * Helper method to return a "X to Y of Z" string for this page where X is the - * first row, Y the last row and Z the total row count. - * - * @param to - * String to put between the first and last row - * @param of - * String to put between the last row and the total row count - * - * @return String of the format XtoYofZ. - */ - public String getDisplayXtoYofZ(String to, String of); -} diff --git a/src/main/java/com/avaje/ebean/Query.java b/src/main/java/com/avaje/ebean/Query.java index f817fb67f..5ab23a0e9 100644 --- a/src/main/java/com/avaje/ebean/Query.java +++ b/src/main/java/com/avaje/ebean/Query.java @@ -13,54 +13,57 @@ import java.util.Set; * Example: Create the query using the API. *

* - *
- * List<Order> orderList = 
- *   Ebean.find(Order.class)
- *     .fetch("customer")
- *     .fetch("details")
+ * 
{@code
+ *
+ * List orderList = 
+ *   ebeanServer.find(Order.class)
+ *     .fetch("customer")
+ *     .fetch("details")
  *     .where()
- *       .like("customer.name","rob%")
- *       .gt("orderDate",lastWeek)
- *     .orderBy("customer.id, id desc")
+ *       .like("customer.name","rob%")
+ *       .gt("orderDate",lastWeek)
+ *     .orderBy("customer.id, id desc")
  *     .setMaxRows(50)
  *     .findList();
  *   
  * ...
- * 
+ * }
* *

* Example: The same query using the query language *

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

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

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

Autofetch

*

@@ -98,13 +101,13 @@ import java.util.Set; * Refer to "ALL Properties/Columns" mode of Optimistic Concurrency checking. *

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

* FIND {bean type} [ ( * | {fetch properties} ) ] @@ -160,17 +163,17 @@ import java.util.Set; * 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 @@ -178,30 +181,30 @@ import java.util.Set; * properties. *

* - *
+ * 
{@code
  * find order (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 @@ -212,13 +215,13 @@ import java.util.Set; * populated. *

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

Early parsing of the Query

*

@@ -352,19 +355,24 @@ public interface Query extends Serializable { /** * Explicitly set a comma delimited list of the properties to fetch on the - * 'main' entity bean (aka partial object). Note that '*' means all + * 'main' root level entity bean (aka partial object). Note that '*' means all * properties. + *

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

* - *
-   * Query<Customer> query = Ebean.createQuery(Customer.class);
+   * 
{@code
    *
-   * // Only fetch the customer id, name and status.
-   * // This is described as a "Partial Object"
-   * query.select("name, status");
-   * query.where("lower(name) like :custname").setParameter("custname", "rob%");
+   * List customers =
+   *     ebeanServer.find(Customer.class)
+   *     // Only fetch the customer id, name and status.
+   *     // This is described as a "Partial Object"
+   *     .select("name, status")
+   *     .where.ilike("name", "rob%")
+   *     .findList();
    *
-   * List<Customer> customerList = query.findList();
-   * 
+ * }
* * @param fetchProperties * the properties to fetch for this bean (* = all properties). @@ -383,31 +391,35 @@ public interface Query extends Serializable { * "Partial Object" - a bean that only has some of its properties populated. *

* - *
+   * 
{@code
+   *
    * // query orders...
-   * Query<Order> query = Ebean.createQuery(Order.class);
+   * List orders =
+   *     ebeanserver.find(Order.class)
+   *       // fetch the customer...
+   *       // ... getting the customers name and phone number
+   *       .fetch("customer", "name, phoneNumber")
    * 
-   * // fetch the customer...
-   * // ... getting the customer's name and phone number
-   * query.fetch("customer", "name, phNumber");
-   * 
-   * // ... also fetch the customers billing address (* = all properties)
-   * query.fetch("customer.billingAddress", "*");
-   * 
+ * // ... 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. *

* - *
+   * 
{@code
+   *
    * // fetch customers (their id, name and status)
-   * Query<Customer> query = Ebean.createQuery(Customer.class);
-   * 
-   * // only fetch some of the properties of the customers
-   * query.select("name, status");
-   * List<Customer> list = query.findList();
-   * 
+ * List customers = + * ebeanServer.find(Customer.class) + * .select("name, status") + * .fetch("contacts", "firstName,lastName,email") + * .findList(); + * + * }
* * @param path * the path of an associated (1-1,1-M,M-1,M-M) bean. @@ -420,6 +432,17 @@ public interface Query extends Serializable { /** * Additionally specify a FetchConfig to use a separate query or lazy loading * to load this path. + * + *
{@code
+   *
+   * // fetch customers (their id, name and status)
+   * List customers =
+   *     ebeanServer.find(Customer.class)
+   *     .select("name, status")
+   *     .fetch("contacts", "firstName,lastName,email", new FetchConfig().lazy(10))
+   *     .findList();
+   *
+   * }
*/ public Query fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig); @@ -428,7 +451,17 @@ public interface Query extends Serializable { *

* The same as {@link #fetch(String, String)} with the fetchProperties as "*". *

- * + *
{@code
+   *
+   * // fetch customers (their id, name and status)
+   * List customers =
+   *     ebeanServer.find(Customer.class)
+   *     // eager fetch the contacts
+   *     .fetch("contacts")
+   *     .findList();
+   *
+   * }
+ * * @param path * the property of an associated (1-1,1-M,M-1,M-M) bean. */ @@ -437,6 +470,18 @@ public interface Query extends Serializable { /** * Additionally specify a JoinConfig to specify a "query join" and or define * the lazy loading query. + * + * + *
{@code
+   *
+   * // fetch customers (their id, name and status)
+   * List customers =
+   *     ebeanServer.find(Customer.class)
+   *     // lazy fetch contacts with a batch size of 100
+   *     .fetch("contacts", new FetchConfig().lazy(100))
+   *     .findList();
+   *
+   * }
*/ public Query fetch(String path, FetchConfig joinConfig); @@ -466,6 +511,10 @@ public interface Query extends Serializable { * (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. *

*/ @@ -493,6 +542,12 @@ public interface Query extends Serializable { * (unlike #findList #findSet etc) *

*

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

+ *

* Compared with #findEachWhile this will always process all the beans where as * #findEachWhile provides a way to stop processing the query result early before * all the beans have been read. @@ -503,19 +558,18 @@ public interface Query extends Serializable { * with Java8 closures. *

* - *
+   * 
{@code
    *
-   * Query<Customer> query = server.find(Customer.class)
-   *     .where().gt("id", 0)
-   *     .orderBy("id")
-   *     .setMaxRows(2);
+   *  ebeanServer.find(Customer.class)
+   *     .where().eq("status", Status.NEW)
+   *     .order().asc("id")
+   *     .findEach((Customer customer) -> {
    *
-   * query.findVisit((Customer customer) -> {
+   *       // do something with customer
+   *       System.out.println("-- visit " + customer);
+   *     });
    *
-   *     // do something with customer
-   *     System.out.println("-- visit " + customer);
-   * });
-   * 
+ * }
* * @param consumer * the consumer used to process the queried beans. @@ -532,23 +586,23 @@ public interface Query extends Serializable { *

* - *
+   * 
{@code
    *
-   * Query<Customer> query = server.find(Customer.class)
-   *     .fetch("contacts", new FetchConfig().query(2))
-   *     .where().gt("id", 0)
-   *     .orderBy("id")
-   *     .setMaxRows(2);
+   *  ebeanServer.find(Customer.class)
+   *     .fetch("contacts", new FetchConfig().query(2))
+   *     .where().eq("status", Status.NEW)
+   *     .order().asc("id")
+   *     .setMaxRows(2000)
+   *     .findEachWhile((Customer customer) -> {
    *
-   * query.findEachWhile((Customer customer) -> {
+   *       // do something with customer
+   *       System.out.println("-- visit " + customer);
    *
-   *     // do something with customer
-   *     System.out.println("-- visit " + customer);
+   *       // return true to continue processing or false to stop
+   *       return (customer.getId() < 40);
+   *     });
    *
-   *     // return true to continue processing or false to stop
-   *     return (customer.getId() < 40);
-   * });
-   * 
+ * }
* * @param consumer * the consumer used to process the queried beans. @@ -560,7 +614,16 @@ public interface Query extends Serializable { *

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

- * + * + *
{@code
+   *
+   * List customers =
+   *     ebeanServer.find(Customer.class)
+   *     .where().ilike("name", "rob%")
+   *     .findList();
+   *
+   * }
+ * * @see EbeanServer#findList(Query, Transaction) */ public List findList(); @@ -570,7 +633,16 @@ public interface Query extends Serializable { *

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

- * + * + *
{@code
+   *
+   * Set customers =
+   *     ebeanServer.find(Customer.class)
+   *     .where().ilike("name", "rob%")
+   *     .findSet();
+   *
+   * }
+ * * @see EbeanServer#findSet(Query, Transaction) */ public Set findSet(); @@ -585,11 +657,14 @@ public interface Query extends Serializable { * on the map. If one is not specified then the id property is used. *

* - *
-   * Query<Product> query = Ebean.createQuery(Product.class);
-   * query.setMapKey("sku");
-   * Map<?, Product> map = query.findMap();
-   * 
+ *
{@code
+   *
+   * Map map =
+   *   ebeanServer.find(Product.class)
+   *     .setMapKey("sku")
+   *     .findMap();
+   *
+   * }
* * @see EbeanServer#findMap(Query, Transaction) */ @@ -612,32 +687,34 @@ public interface Query extends Serializable { * return 0 or 1 results. *

* - *
+   * 
{@code
+   *
    * // assuming the sku of products is unique...
    * Product product =
-   *     Ebean.find(Product.class)
-   *         .where("sku = ?")
-   *         .set(1, "aa113")
+   *     ebeanServer.find(Product.class)
+   *         .where().eq("sku", "aa113")
    *         .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 = 
-   *     Ebean.find(Order.class)
+   *     ebeanServer.find(Order.class)
    *       .setId(1)
-   *       .fetch("details")
+   *       .fetch("details")
    *       .findUnique();
-   *       
-   * List<OrderDetail> details = order.getDetails();
+   *
+   * // the order details were eagerly loaded
+   * List details = order.getDetails();
    * ...
-   * 
+ * }
*/ public T findUnique(); @@ -696,7 +773,32 @@ public interface Query extends Serializable { * the query. This translates into SQL that uses limit offset, rownum or row_number function to * limit the result set. *

- * + * + *

Example: typical use including total row count

+ *
{@code
+   *
+   *     // We want to find the first 100 new orders
+   *     //  ... 0 means first page
+   *     //  ... page size is 100
+   *
+   *     PagedList pagedList
+   *       = ebeanServer.find(Order.class)
+   *       .where().eq("status", Order.Status.NEW)
+   *       .order().asc("id")
+   *       .findPagedList(0, 100);
+   *
+   *     // Optional: initiate the loading of the total
+   *     // row count in a background thread
+   *     pagedList.loadRowCount();
+   *
+   *     // fetch and return the list in the foreground thread
+   *     List orders = pagedList.getList();
+   *
+   *     // get the total row count (from the future)
+   *     int totalRowCount = pagedList.getTotalRowCount();
+   *
+   * }
+ * * @param pageIndex * The zero based index of the page. * @param pageSize @@ -706,19 +808,20 @@ public interface Query extends Serializable { public PagedList findPagedList(int pageIndex, int pageSize); /** - * Set a named bind parameter. Named parameters have a colon to prefix the - * name. + * 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";
+   * String oql = "find order where status = :orderStatus";
    * 
-   * Query<Order> query = Ebean.createQuery(Order.class, oql);
+   * Query query = ebeanServer.find(Order.class, oql);
    * 
    * // bind the named parameter
-   * query.bind("orderStatus", OrderStatus.NEW);
-   * List<Order> list = query.findList();
-   * 
+ * query.bind("orderStatus", OrderStatus.NEW); + * List list = query.findList(); + * + * }
* * @param name * the parameter name @@ -732,17 +835,19 @@ public interface Query extends Serializable { * 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";
+   * String oql = "where status = ? order by id desc";
    * 
-   * Query<Order> query = Ebean.createQuery(Order.class, oql);
+   * Query query = ebeanServer.createQuery(Order.class, oql);
    * 
    * // bind the parameter
    * query.setParameter(1, OrderStatus.NEW);
    * 
-   * List<Order> list = query.findList();
-   * 
+ * List list = query.findList(); + * + * }
* * @param position * the parameter bind position starting from 1 (not 0) @@ -758,12 +863,18 @@ public interface Query extends Serializable { * fetch joins. *

* - *
-   * Query<Order> query = Ebean.createQuery(Order.class);
-   * Order order = query.setId(1).join("details").findUnique();
-   * List<OrderDetail> details = order.getDetails();
-   * ...
-   * 
+ *
{@code
+   *
+   * Order order =
+   *     ebeanServer.find(Order.class)
+   *     .setId(1)
+   *     .fetch("details")
+   *     .findUnique();
+   *
+   * // the order details were eagerly fetched
+   * List details = order.getDetails();
+   *
+   * }
*/ public Query setId(Object id); @@ -774,15 +885,17 @@ public interface Query extends Serializable { * {@link #setParameter(String, Object)}. *

* - *
-   * Query<Order> query = Ebean.createQuery(Order.class, "top");
+   * 
{@code
+   *
+   * Query query = ebeanServer.createQuery(Order.class, "top");
    * ...
    * if (...) {
-   *   query.where("status = :status and lower(customer.name) like :custName");
-   *   query.setParameter("status", Order.NEW);
-   *   query.setParameter("custName", "rob%");
+   *   query.where("status = :status and lower(customer.name) like :custName");
+   *   query.setParameter("status", Order.NEW);
+   *   query.setParameter("custName", "rob%");
    * }
-   * 
+ * + * }
* *

* Internally the addToWhereClause string is processed by removing named @@ -802,13 +915,15 @@ public interface Query extends Serializable { /** * Add a single Expression to the where clause returning the query. * - *

-   * List<Order> newOrders = 
-   *     Ebean.find(Order.class)
-   * 		.where().eq("status", Order.NEW)
+   * 
{@code
+   *
+   * List newOrders = 
+   *     ebeanServer.find(Order.class)
+   * 		.where().eq("status", Order.NEW)
    * 		.findList();
    * ...
-   * 
+ * + * }
*/ public Query where(Expression expression); @@ -817,15 +932,16 @@ public interface Query extends Serializable { * ExpressionList. You can use this for adding multiple expressions to the * where clause. * - *
-   * Query<Order> query = Ebean.createQuery(Order.class, "top");
-   * ...
-   * if (...) {
-   *   query.where()
-   *     .eq("status", Order.NEW)
-   *     .ilike("customer.name","rob%");
-   * }
-   * 
+ *
{@code
+   *
+   * List orders =
+   *     ebeanServer.find(Order.class)
+   *     .where()
+   *       .eq("status", Order.NEW)
+   *       .ilike("customer.name","rob%")
+   *     .findList();
+   *
+   * }
* * @see Expr * @return The ExpressionList for adding expressions to. @@ -843,17 +959,18 @@ public interface Query extends Serializable { * week. In this case you can use filterMany() to filter the orders. *

* - *
+   * 
{@code
    * 
-   * List<Customer> list = Ebean
-   *     .find(Customer.class)
-   *     // .fetch("orders", new FetchConfig().lazy())
-   *     // .fetch("orders", new FetchConfig().query())
-   *     .fetch("orders").where().ilike("name", "rob%").filterMany("orders")
-   *     .eq("status", Order.Status.NEW).gt(
-   *         "orderDate", lastWeek).findList();
+   * List list =
+   *     ebeanServer.find(Customer.class)
+   *     // .fetch("orders", new FetchConfig().lazy())
+   *     // .fetch("orders", new FetchConfig().query())
+   *     .fetch("orders")
+   *     .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 @@ -891,14 +1008,14 @@ public interface Query extends Serializable { * {@link #setParameter(String, Object)}. *

* - *
-   * Query<ReportOrder> query = Ebean.createQuery(ReportOrder.class);
-   * ...
-   * if (...) {
-   *   query.having("score > :min");
-   *   query.setParameter("min", 1);
-   * }
-   * 
+ *
{@code
+   *
+   * List query =
+   *     ebeanServer.find(ReportOrder.class)
+   *     .having("score > :min").setParameter("min", 1)
+   *     .findList();
+   *
+   * }
* * @param addToHavingClause * the clause to append to the having clause which typically contains @@ -1030,17 +1147,17 @@ public interface Query extends Serializable { * If no property is set then the id property is used. *

* - *
+   * 
{@code
+   *
    * // Assuming sku is unique for products...
    *    
-   * Query<Product> query = Ebean.createQuery(Product.class);
-   *   
-   * // use sku for keys...
-   * query.setMapKey("sku");
-   *   
-   * Map<?,Product> productMap = query.findMap();
-   * ...
-   * 
+ * Map productMap = + * ebeanServer.find(Product.class) + * // use sku for keys... + * .setMapKey("sku") + * .findMap(); + * + * }
* * @param mapKey * the property to use as keys for a map. diff --git a/src/main/java/com/avaje/ebean/QueryEachConsumer.java b/src/main/java/com/avaje/ebean/QueryEachConsumer.java index 7e9761426..3d015456c 100644 --- a/src/main/java/com/avaje/ebean/QueryEachConsumer.java +++ b/src/main/java/com/avaje/ebean/QueryEachConsumer.java @@ -12,19 +12,19 @@ package com.avaje.ebean; * QueryResultVisitor useful for processing large queries. *

* - *
+ * 
{@code
  *
- * Query<Customer> query = server.find(Customer.class)
- *     .where().gt("id", 0)
- *     .orderBy("id")
- *     .setMaxRows(2);
+ * Query query = server.find(Customer.class)
+ *     .where().eq("status", Status.NEW)
+ *     .order().asc("id");
  *
- * query.findVisit((Customer customer) -> {
+ * query.findEach((Customer customer) -> {
  *
  *     // do something with customer
- *     System.out.println("-- visit " + customer);
+ *     System.out.println("-- visit " + customer);
  * });
- * 
+ * + * }
* * @param * the type of entity bean being queried. diff --git a/src/main/java/com/avaje/ebean/annotation/Formula.java b/src/main/java/com/avaje/ebean/annotation/Formula.java index f33ca8e34..915cf0deb 100644 --- a/src/main/java/com/avaje/ebean/annotation/Formula.java +++ b/src/main/java/com/avaje/ebean/annotation/Formula.java @@ -20,7 +20,7 @@ import com.avaje.ebean.Query; * You may also put use the Transient annotation with the Formula annotation. * The effect of the Transient annotation in this case is that the formula will * NOT be included in queries by default - you have to explicitly include - * it via {@link Query#select(String)} or {@link Query#join(String, String)}. + * it via {@link Query#select(String)} or {@link Query#fetch(String, String, com.avaje.ebean.FetchConfig)}. * You may want to do this if the Formula is relatively expensive and only want * it included in the query when you explicitly state it. *

diff --git a/src/main/java/com/avaje/ebean/event/BeanPersistListener.java b/src/main/java/com/avaje/ebean/event/BeanPersistListener.java index 9b453f6dd..50b3c098f 100644 --- a/src/main/java/com/avaje/ebean/event/BeanPersistListener.java +++ b/src/main/java/com/avaje/ebean/event/BeanPersistListener.java @@ -33,7 +33,7 @@ import com.avaje.ebean.config.ServerConfig; *

*

* A BeanPersistListener is either found automatically via class path search or - * can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}}. + * can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}}. *

* @see ServerConfig#add(BeanPersistListener) */ diff --git a/src/main/java/com/avaje/ebean/overview.html b/src/main/java/com/avaje/ebean/overview.html index 85d597896..4ce74e843 100644 --- a/src/main/java/com/avaje/ebean/overview.html +++ b/src/main/java/com/avaje/ebean/overview.html @@ -3,8 +3,8 @@ Ebean API -Ebean Object Relational Mapping (start at Ebean -or EbeanServer). +Ebean Object Relational Mapping (start at +EbeanServer or Ebean).

Ebean

@@ -21,15 +21,15 @@ For a full description of the query language refer to +
{@code
 // fetch order 10
 Order order = Ebean.find(Order.class, 10);
-
+}

EXAMPLE 2: Fetch an Object with associations

-
+
{@code
 // fetch Customer 7 including their billing and shipping addresses
 Customer customer = Ebean.find(Customer.class)
     .fetch("billingAddress");
@@ -40,19 +40,19 @@ Customer customer = Ebean.find(Customer.class)
 		
 Address billAddr = customer.getBillingAddress();
 Address shipAddr = customer.getShippingAddress();
-
+}

EXAMPLE 3: Fetch a list of Objects with associations

-
+
{@code
 // Note: This example shows a "Partial Object".
 //       For the product objects associated with the 
 //       order details only the product id and name is
 //       fetched (the product objects are partially populated).
 		
 // fetch orders for customer.id = 2
-List<Order> orderList = Ebean.find(Order.class);
+List orderList = Ebean.find(Order.class);
     .fetch("customer")
     .fetch("customer.shippingAddress")
     .fetch("details")
@@ -72,17 +72,17 @@ Order order = orderList.get(0);
 Customer customer = order.getCustomer();	
 Address shipAddr = customer.getShippingAddress();
 
-List<OrderDetail> details = order.getDetails();
+List details = order.getDetails();
 OrderDetail detail = details.get(0);
 Product product = detail.getProduct();
 String productName = product.getName();
 
-
+}

EXAMPLE 4: Create and save an Order

-
+
{@code
 // get a Customer reference so we don't hit the database
 Customer custRef = Ebean.getReference(Customer.class, 7);
 
@@ -107,14 +107,14 @@ orderLines.add(line);
 // NB: assumes CascadeType.PERSIST is set on the order lines association
 Ebean.save(newOrder);
 
-
+}

EXAMPLE 5: Use another database

-
+
{@code
 // Get access to the Human Resources EbeanServer/Database
-EbeanServer hrServer = Ebean.getServer("HR");
+EbeanServer hrServer = Ebean.getServer("HR");
                                     
                               
 // fetch contact 3 from the HR database
@@ -125,7 +125,7 @@ contact.setStatus(Contact.Status.INACTIVE);
                                     
 // save the contact back to the HR database
 hrServer.save(contact); 	
-
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java index 8b1336bcf..3c3dc9914 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java @@ -73,7 +73,7 @@ public class WriteJson { return new WriteBean(desc, explicitAllProps, currentIncludeProps, bean); } - public class WriteBean { + public static class WriteBean { final boolean explicitAllProps; final Set currentIncludeProps; diff --git a/src/test/java/com/avaje/ebean/elasticsearch/TestBasicPush.java b/src/test/java/com/avaje/ebean/elasticsearch/TestBasicPush.java index 97efdb90b..fff74ba0b 100644 --- a/src/test/java/com/avaje/ebean/elasticsearch/TestBasicPush.java +++ b/src/test/java/com/avaje/ebean/elasticsearch/TestBasicPush.java @@ -1,7 +1,118 @@ package com.avaje.ebean.elasticsearch; -/** - * Created by rob on 2/12/14. - */ -public class TestBasicPush { +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.text.PathProperties; +import com.avaje.ebean.text.json.JsonContext; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.ResetBasicData; +import com.fasterxml.jackson.core.JsonGenerator; +import com.squareup.okhttp.*; +import org.junit.Ignore; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.List; + +public class TestBasicPush extends BaseTestCase { + + public static final MediaType JSON + = MediaType.parse("application/json; charset=utf-8"); + + OkHttpClient client = new OkHttpClient(); + + @Ignore + @Test + public void testBulkUpdate() throws IOException { + + ResetBasicData.reset(); + + EbeanServer server = Ebean.getServer(null); + JsonContext jsonContext = server.json(); + + StringWriter writer = new StringWriter(); + JsonGenerator generator = jsonContext.createGenerator(writer); + + List customers = Ebean.find(Customer.class).findList(); + + for (Customer customer : customers) { + + customer.setName(customer.getName()+"esMod"); + PathProperties updateProps = PathProperties.parse("name"); + + generator.writeStartObject(); + generator.writeFieldName("update"); + generator.writeStartObject(); + generator.writeStringField("_id", customer.getId().toString()); + generator.writeStringField("_type", "customer"); + generator.writeStringField("_index", "customer"); + generator.writeEndObject(); + generator.writeEndObject(); + generator.writeRaw("\n"); + + generator.writeStartObject(); + generator.writeFieldName("doc"); + jsonContext.toJson(customer, generator, updateProps); + generator.writeEndObject(); + generator.writeRaw("\n"); + } + + generator.close(); + String json = writer.toString(); + System.out.println(json); + + String response = post("http://localhost:9200/_bulk", json); + + System.out.println(response); + + //curl -s -XPOST localhost:9200/_bulk + +// { "update" : {"_id" : "1", "_type" : "type1", "_index" : "index1"} } +// { "doc" : {"field2" : "value2"} } + + + } + + @Ignore + @Test + public void test() throws IOException { + + ResetBasicData.reset(); + + EbeanServer server = Ebean.getServer(null); + JsonContext jsonContext = server.json(); + + List customers = Ebean.find(Customer.class).findList(); + + PathProperties paths = PathProperties.parse("name, status, anniversary"); + for (Customer customer : customers) { + + String json = jsonContext.toJson(customer, paths); + put("http://localhost:9200/customer/customer/"+customer.getId(), json); + } + + } + + String post(String url, String json) throws IOException { + RequestBody body = RequestBody.create(JSON, json); + Request request = new Request.Builder() + .url(url) + .put(body) + .build(); + Response response = client.newCall(request).execute(); + return response.body().string(); + } + + String put(String url, String json) throws IOException { + RequestBody body = RequestBody.create(JSON, json); + Request request = new Request.Builder() + .url(url) + .put(body) + .build(); + Response response = client.newCall(request).execute(); + return response.body().string(); + } + }